Merge pull request #123729 from microsoft/tyriar/getprofilesexthost

Move profile resolving to pty service
This commit is contained in:
Daniel Imms 2021-05-13 07:19:41 -07:00 committed by GitHub
commit c185bff400
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 738 additions and 740 deletions

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.57.0",
"distro": "32e7f991e227138d9f08ed5bf19f34e6a5192dfd",
"distro": "d4088d7fa3ad4446065d4cec45d90d02d04fe745",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -272,7 +272,7 @@ class SharedProcessMain extends Disposable {
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
// Terminal
services.set(ILocalPtyService, this._register(new PtyHostService(logService, telemetryService)));
services.set(ILocalPtyService, this._register(new PtyHostService(configurationService, logService, telemetryService)));
return new InstantiationService(services);
}

View file

@ -9,6 +9,78 @@ 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';
export const enum TerminalSettingId {
ShellLinux = 'terminal.integrated.shell.linux',
ShellMacOs = 'terminal.integrated.shell.osx',
ShellWindows = 'terminal.integrated.shell.windows',
SendKeybindingsToShell = 'terminal.integrated.sendKeybindingsToShell',
AutomationShellLinux = 'terminal.integrated.automationShell.linux',
AutomationShellMacOs = 'terminal.integrated.automationShell.osx',
AutomationShellWindows = 'terminal.integrated.automationShell.windows',
ShellArgsLinux = 'terminal.integrated.shellArgs.linux',
ShellArgsMacOs = 'terminal.integrated.shellArgs.osx',
ShellArgsWindows = 'terminal.integrated.shellArgs.windows',
ProfilesWindows = 'terminal.integrated.profiles.windows',
ProfilesMacOs = 'terminal.integrated.profiles.osx',
ProfilesLinux = 'terminal.integrated.profiles.linux',
DefaultProfileLinux = 'terminal.integrated.defaultProfile.linux',
DefaultProfileMacOs = 'terminal.integrated.defaultProfile.osx',
DefaultProfileWindows = 'terminal.integrated.defaultProfile.windows',
UseWslProfiles = 'terminal.integrated.useWslProfiles',
TabsEnabled = 'terminal.integrated.tabs.enabled',
TabsHideCondition = 'terminal.integrated.tabs.hideCondition',
TabsShowActiveTerminal = 'terminal.integrated.tabs.showActiveTerminal',
TabsLocation = 'terminal.integrated.tabs.location',
TabsFocusMode = 'terminal.integrated.tabs.focusMode',
MacOptionIsMeta = 'terminal.integrated.macOptionIsMeta',
MacOptionClickForcesSelection = 'terminal.integrated.macOptionClickForcesSelection',
AltClickMovesCursor = 'terminal.integrated.altClickMovesCursor',
CopyOnSelection = 'terminal.integrated.copyOnSelection',
DrawBoldTextInBrightColors = 'terminal.integrated.drawBoldTextInBrightColors',
FontFamily = 'terminal.integrated.fontFamily',
FontSize = 'terminal.integrated.fontSize',
LetterSpacing = 'terminal.integrated.letterSpacing',
LineHeight = 'terminal.integrated.lineHeight',
MinimumContrastRatio = 'terminal.integrated.minimumContrastRatio',
FastScrollSensitivity = 'terminal.integrated.fastScrollSensitivity',
MouseWheelScrollSensitivity = 'terminal.integrated.mouseWheelScrollSensitivity',
BellDuration = 'terminal.integrated.bellDuration',
FontWeight = 'terminal.integrated.fontWeight',
FontWeightBold = 'terminal.integrated.fontWeightBold',
CursorBlinking = 'terminal.integrated.cursorBlinking',
CursorStyle = 'terminal.integrated.cursorStyle',
CursorWidth = 'terminal.integrated.cursorWidth',
Scrollback = 'terminal.integrated.scrollback',
DetectLocale = 'terminal.integrated.detectLocale',
GpuAcceleration = 'terminal.integrated.gpuAcceleration',
RightClickBehavior = 'terminal.integrated.rightClickBehavior',
Cwd = 'terminal.integrated.cwd',
ConfirmOnExit = 'terminal.integrated.confirmOnExit',
EnableBell = 'terminal.integrated.enableBell',
CommandsToSkipShell = 'terminal.integrated.commandsToSkipShell',
AllowChords = 'terminal.integrated.allowChords',
AllowMnemonics = 'terminal.integrated.allowMnemonics',
EnvMacOs = 'terminal.integrated.env.osx',
EnvLinux = 'terminal.integrated.env.linux',
EnvWindows = 'terminal.integrated.env.windows',
EnvironmentChangesIndicator = 'terminal.integrated.environmentChangesIndicator',
EnvironmentChangesRelaunch = 'terminal.integrated.environmentChangesRelaunch',
ShowExitAlert = 'terminal.integrated.showExitAlert',
SplitCwd = 'terminal.integrated.splitCwd',
WindowsEnableConpty = 'terminal.integrated.windowsEnableConpty',
WordSeparators = 'terminal.integrated.wordSeparators',
ExperimentalUseTitleEvent = 'terminal.integrated.experimentalUseTitleEvent',
EnableFileLinks = 'terminal.integrated.enableFileLinks',
UnicodeVersion = 'terminal.integrated.unicodeVersion',
ExperimentalLinkProvider = 'terminal.integrated.experimentalLinkProvider',
LocalEchoLatencyThreshold = 'terminal.integrated.localEchoLatencyThreshold',
LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms',
LocalEchoStyle = 'terminal.integrated.localEchoStyle',
EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions',
AllowWorkspaceConfiguration = 'terminal.integrated.allowWorkspaceConfiguration',
InheritEnv = 'terminal.integrated.inheritEnv'
}
export enum WindowsShellType {
CommandPrompt = 'cmd',
PowerShell = 'pwsh',
@ -96,6 +168,7 @@ export interface IOffProcessTerminalService {
attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
listProcesses(): Promise<IProcessDetails[]>;
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string>;
getProfiles(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]>;
getWslPath(original: string): Promise<string>;
getEnvironment(): Promise<IProcessEnvironment>;
getShellEnvironment(): Promise<IProcessEnvironment | undefined>;
@ -119,6 +192,8 @@ export interface IPtyService {
readonly onPtyHostStart?: Event<void>;
readonly onPtyHostUnresponsive?: Event<void>;
readonly onPtyHostResponsive?: Event<void>;
readonly onPtyHostRequestResolveVariables?: Event<IRequestResolveVariablesEvent>;
readonly onProcessData: Event<{ id: number, event: IProcessDataEvent | string }>;
readonly onProcessExit: Event<{ id: number, event: number | undefined }>;
readonly onProcessReady: Event<{ id: number, event: { pid: number, cwd: string } }>;
@ -131,6 +206,7 @@ export interface IPtyService {
restartPtyHost?(): Promise<void>;
shutdownAll?(): Promise<void>;
acceptPtyHostResolvedVariables?(id: number, resolved: string[]): Promise<void>;
createProcess(
shellLaunchConfig: IShellLaunchConfig,
@ -163,9 +239,10 @@ export interface IPtyService {
processBinary(id: number, data: string): Promise<void>;
/** Confirm the process is _not_ an orphan. */
orphanQuestionReply(id: number): Promise<void>;
updateTitle(id: number, title: string): Promise<void>
updateIcon(id: number, icon: string, color?: string): Promise<void>
updateTitle(id: number, title: string): Promise<void>;
updateIcon(id: number, icon: string, color?: string): Promise<void>;
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string>;
getProfiles?(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]>;
getEnvironment(): Promise<IProcessEnvironment>;
getWslPath(original: string): Promise<string>;
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void>;
@ -173,6 +250,11 @@ export interface IPtyService {
reduceConnectionGraceTime(): Promise<void>;
}
export interface IRequestResolveVariablesEvent {
id: number;
originalText: string[];
}
export enum HeartbeatConstants {
/**
* The duration between heartbeats
@ -453,6 +535,17 @@ export interface ITerminalDimensions {
rows: number;
}
export interface ITerminalProfile {
profileName: string;
path: string;
isDefault: boolean;
isAutoDetected?: boolean;
args?: string | string[] | undefined;
env?: ITerminalEnvironment;
overrideName?: boolean;
icon?: string;
}
export interface ITerminalDimensionsOverride extends Readonly<ITerminalDimensions> {
/**
* indicate that xterm must receive these exact dimensions, even if they overflow the ui!
@ -461,3 +554,26 @@ export interface ITerminalDimensionsOverride extends Readonly<ITerminalDimension
}
export type SafeConfigProvider = <T>(key: string) => T | undefined;
export const enum ProfileSource {
GitBash = 'Git Bash',
Pwsh = 'PowerShell'
}
export interface IBaseUnresolvedTerminalProfile {
args?: string | string[] | undefined;
isAutoDetected?: boolean;
overrideName?: boolean;
icon?: string;
env?: ITerminalEnvironment;
}
export interface ITerminalExecutable extends IBaseUnresolvedTerminalProfile {
path: string | string[];
}
export interface ITerminalProfileSource extends IBaseUnresolvedTerminalProfile {
source: ProfileSource;
}
export type ITerminalProfileObject = ITerminalExecutable | ITerminalProfileSource | null;

View file

@ -0,0 +1,326 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { localize } from 'vs/nls';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Registry } from 'vs/platform/registry/common/platform';
const terminalProfileSchema: IJSONSchema = {
type: 'object',
required: ['path'],
properties: {
path: {
description: localize('terminalProfile.path', 'A single path to a shell executable or an array of paths that will be used as fallbacks when one fails.'),
type: ['string', 'array'],
items: {
type: 'string'
}
},
args: {
description: localize('terminalProfile.args', 'An optional set of arguments to run the shell executable with.'),
type: 'array',
items: {
type: 'string'
}
},
overrideName: {
description: localize('terminalProfile.overrideName', 'Controls whether or not the profile name overrides the auto detected one.'),
type: 'boolean'
},
icon: {
description: localize('terminalProfile.icon', 'A codicon ID to associate with this terminal.'),
type: 'string'
},
env: {
markdownDescription: localize('terminalProfile.env', "An object with environment variables that will be added to the terminal profile process. Set to `null` to delete environment variables from the base environment."),
type: 'object',
additionalProperties: {
type: ['string', 'null']
},
default: {}
}
}
};
const shellDeprecationMessageLinux = localize('terminal.integrated.shell.linux.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.linux#`', '`#terminal.integrated.defaultProfile.linux#`');
const shellDeprecationMessageOsx = localize('terminal.integrated.shell.osx.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.osx#`', '`#terminal.integrated.defaultProfile.osx#`');
const shellDeprecationMessageWindows = localize('terminal.integrated.shell.windows.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.windows#`', '`#terminal.integrated.defaultProfile.windows#`');
const terminalPlatformConfiguration: IConfigurationNode = {
id: 'terminal',
order: 100,
title: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"),
type: 'object',
properties: {
[TerminalSettingId.AutomationShellLinux]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.linux',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.AutomationShellMacOs]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.osx',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.AutomationShellWindows]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.windows',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.ShellLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageLinux
},
[TerminalSettingId.ShellMacOs]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageOsx
},
[TerminalSettingId.ShellWindows]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageWindows
},
[TerminalSettingId.ShellArgsLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'array',
items: {
type: 'string'
},
default: [],
markdownDeprecationMessage: shellDeprecationMessageLinux
},
[TerminalSettingId.ShellArgsMacOs]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'array',
items: {
type: 'string'
},
// Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This
// is the reason terminals on macOS typically run login shells by default which set up
// the environment. See http://unix.stackexchange.com/a/119675/115410
default: ['-l'],
markdownDeprecationMessage: shellDeprecationMessageOsx
},
[TerminalSettingId.ShellArgsWindows]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
'anyOf': [
{
type: 'array',
items: {
type: 'string',
markdownDescription: localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).")
},
},
{
type: 'string',
markdownDescription: localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).")
}
],
default: [],
markdownDeprecationMessage: shellDeprecationMessageWindows
},
[TerminalSettingId.ProfilesWindows]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profiles.windows',
comment: ['{0}, {1}, and {2} are the `source`, `path` and optional `args` settings keys']
},
"The Windows profiles to present when creating a new terminal via the terminal dropdown. Set to null to exclude them, use the {0} property to use the default detected configuration. Or, set the {1} and optional {2}", '`source`', '`path`', '`args`.'
),
type: 'object',
default: {
'PowerShell': {
source: 'PowerShell',
icon: 'terminal-powershell'
},
'Command Prompt': {
path: [
'${env:windir}\\Sysnative\\cmd.exe',
'${env:windir}\\System32\\cmd.exe'
],
args: [],
icon: 'terminal-cmd'
},
'Git Bash': {
source: 'Git Bash'
}
},
additionalProperties: {
'anyOf': [
{
type: 'object',
required: ['source'],
properties: {
source: {
description: localize('terminalProfile.windowsSource', 'A profile source that will auto detect the paths to the shell.'),
enum: ['PowerShell', 'Git Bash']
},
overrideName: {
description: localize('terminalProfile.overrideName', 'Controls whether or not the profile name overrides the auto detected one.'),
type: 'boolean'
},
icon: {
description: localize('terminalProfile.icon', 'A codicon ID to associate with this terminal.'),
type: 'string'
},
env: {
markdownDescription: localize('terminalProfile.env', "An object with environment variables that will be added to the terminal profile process. Set to `null` to delete environment variables from the base environment."),
type: 'object',
additionalProperties: {
type: ['string', 'null']
},
default: {}
}
}
},
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.ProfilesMacOs]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profile.osx',
comment: ['{0} and {1} are the `path` and optional `args` settings keys']
},
"The macOS profiles to present when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0} and optional {1}", '`path`', '`args`.'
),
type: 'object',
default: {
'bash': {
path: 'bash',
icon: 'terminal-bash'
},
'zsh': {
path: 'zsh'
},
'fish': {
path: 'fish'
},
'tmux': {
path: 'tmux',
icon: 'terminal-tmux'
},
'pwsh': {
path: 'pwsh',
icon: 'terminal-powershell'
}
},
additionalProperties: {
'anyOf': [
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.ProfilesLinux]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profile.linux',
comment: ['{0} and {1} are the `path` and optional `args` settings keys']
},
"The Linux profiles to present when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0} and optional {1}", '`path`', '`args`.'
),
type: 'object',
default: {
'bash': {
path: 'bash'
},
'zsh': {
path: 'zsh'
},
'fish': {
path: 'fish'
},
'tmux': {
path: 'tmux',
icon: 'terminal-tmux'
},
'pwsh': {
path: 'pwsh',
icon: 'terminal-powershell'
}
},
additionalProperties: {
'anyOf': [
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.DefaultProfileLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.defaultProfile.linux', "The default profile used on Linux. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.linux#`', '`#terminal.integrated.shellArgs.linux#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.DefaultProfileMacOs]: {
restricted: true,
description: localize('terminal.integrated.defaultProfile.osx', "The default profile used on macOS. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.osx#`', '`#terminal.integrated.shellArgs.osx#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.DefaultProfileWindows]: {
restricted: true,
description: localize('terminal.integrated.defaultProfile.windows', "The default profile used on Windows. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.windows#`', '`#terminal.integrated.shellArgs.windows#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.UseWslProfiles]: {
description: localize('terminal.integrated.useWslProfiles', 'Controls whether or not WSL distros are shown in the terminal dropdown'),
type: 'boolean',
default: true
},
[TerminalSettingId.AllowWorkspaceConfiguration]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.allowWorkspaceConfiguration', "Allows shell and profile settings to be pick up from a workspace."),
type: 'boolean',
default: false
},
[TerminalSettingId.InheritEnv]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from VS Code which may source a login shell to ensure $PATH and other development variables are initialized. This has no effect on Windows."),
type: 'boolean',
default: true
},
}
};
/**
* Registers terminal configurations required by shared process and remote server.
*/
export function registerTerminalPlatformConfiguration() {
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration(terminalPlatformConfiguration);
}

View file

@ -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 } from 'vs/platform/terminal/common/terminal';
import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ITerminalsLayoutInfo, TerminalIpcChannels, IHeartbeatService, HeartbeatConstants, TerminalShellType, ITerminalProfile, IRequestResolveVariablesEvent, SafeConfigProvider, TerminalSettingId } 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';
@ -14,6 +14,9 @@ import { Emitter } from 'vs/base/common/event';
import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { detectAvailableProfiles } from 'vs/platform/terminal/node/terminalProfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration';
enum Constants {
MaxRestarts = 5
@ -25,6 +28,8 @@ enum Constants {
*/
let lastPtyId = 0;
let lastResolveVariablesRequestId = 0;
/**
* This service implements IPtyService by launching a pty host process, forwarding messages to and
* from the pty host process and manages the connection.
@ -51,6 +56,9 @@ export class PtyHostService extends Disposable implements IPtyService {
readonly onPtyHostUnresponsive = this._onPtyHostUnresponsive.event;
private readonly _onPtyHostResponsive = this._register(new Emitter<void>());
readonly onPtyHostResponsive = this._onPtyHostResponsive.event;
private readonly _onPtyHostRequestResolveVariables = this._register(new Emitter<IRequestResolveVariablesEvent>());
readonly onPtyHostRequestResolveVariables = this._onPtyHostRequestResolveVariables.event;
private readonly _onProcessData = this._register(new Emitter<{ id: number, event: IProcessDataEvent | string }>());
readonly onProcessData = this._onProcessData.event;
private readonly _onProcessExit = this._register(new Emitter<{ id: number, event: number | undefined }>());
@ -71,11 +79,16 @@ export class PtyHostService extends Disposable implements IPtyService {
readonly onProcessOrphanQuestion = this._onProcessOrphanQuestion.event;
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILogService private readonly _logService: ILogService,
@ITelemetryService private readonly _telemetryService: ITelemetryService
) {
super();
// Platform configuration is required on the process running the pty host (shared process or
// remote server).
registerTerminalPlatformConfiguration();
this._register(toDisposable(() => this._disposePtyHost()));
[this._client, this._proxy] = this._startPtyHost();
@ -206,6 +219,9 @@ export class PtyHostService extends Disposable implements IPtyService {
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string> {
return this._proxy.getDefaultSystemShell(osOverride);
}
async getProfiles(includeDetectedProfiles: boolean = false): Promise<ITerminalProfile[]> {
return detectAvailableProfiles(includeDetectedProfiles, this._buildSafeConfigProvider(), undefined, this._logService, this._resolveVariables.bind(this));
}
getEnvironment(): Promise<IProcessEnvironment> {
return this._proxy.getEnvironment();
}
@ -289,4 +305,33 @@ export class PtyHostService extends Disposable implements IPtyService {
this._heartbeatSecondTimeout = undefined;
}
}
private _pendingResolveVariablesRequests: Map<number, (resolved: string[]) => void> = new Map();
private _resolveVariables(text: string[]): Promise<string[]> {
return new Promise<string[]>(resolve => {
const id = ++lastResolveVariablesRequestId;
this._pendingResolveVariablesRequests.set(id, resolve);
this._onPtyHostRequestResolveVariables.fire({ id, originalText: text });
});
}
async acceptPtyHostResolvedVariables(id: number, resolved: string[]) {
const request = this._pendingResolveVariablesRequests.get(id);
if (request) {
request(resolved);
this._pendingResolveVariablesRequests.delete(id);
} else {
this._logService.warn(`Resolved variables received without matching request ${id}`);
}
}
private _buildSafeConfigProvider(): SafeConfigProvider {
return (key: string) => {
const isWorkspaceConfigAllowed = this._configurationService.getValue(TerminalSettingId.AllowWorkspaceConfiguration);
if (isWorkspaceConfigAllowed) {
return this._configurationService.getValue(key) as any;
}
const inspected = this._configurationService.inspect(key);
return inspected?.userValue || inspected?.defaultValue;
};
}
}

View file

@ -7,48 +7,58 @@ import * as fs from 'fs';
import { normalize, basename, delimiter } from 'vs/base/common/path';
import { enumeratePowerShellInstallations } from 'vs/base/node/powershell';
import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment';
import { ITerminalProfile, ITerminalProfileObject, ProfileSource, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import * as cp from 'child_process';
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ILogService } from 'vs/platform/log/common/log';
import * as pfs from 'vs/base/node/pfs';
import { ITerminalEnvironment, SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
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';
let profileSources: Map<string, IPotentialTerminalProfile> | undefined;
export function detectAvailableProfiles(configuredProfilesOnly: boolean, safeConfigProvider: SafeConfigProvider, fsProvider?: IFsProvider, logService?: ILogService, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder, testPaths?: string[]): Promise<ITerminalProfile[]> {
export function detectAvailableProfiles(
includeDetectedProfiles: boolean,
safeConfigProvider: SafeConfigProvider,
fsProvider?: IFsProvider,
logService?: ILogService,
variableResolver?: (text: string[]) => Promise<string[]>,
testPaths?: string[]
): Promise<ITerminalProfile[]> {
fsProvider = fsProvider || {
existsFile: pfs.SymlinkSupport.existsFile,
readFile: fs.promises.readFile
};
if (isWindows) {
return detectAvailableWindowsProfiles(
configuredProfilesOnly,
includeDetectedProfiles,
fsProvider,
logService,
safeConfigProvider(TerminalSettingId.UseWslProfiles) || true,
safeConfigProvider(TerminalSettingId.ProfilesWindows),
safeConfigProvider(TerminalSettingId.DefaultProfileWindows),
variableResolver,
workspaceFolder
variableResolver
);
}
return detectAvailableUnixProfiles(
fsProvider,
logService,
configuredProfilesOnly,
includeDetectedProfiles,
safeConfigProvider(isMacintosh ? TerminalSettingId.ProfilesMacOs : TerminalSettingId.ProfilesLinux),
safeConfigProvider(isMacintosh ? TerminalSettingId.DefaultProfileMacOs : TerminalSettingId.DefaultProfileLinux),
testPaths,
variableResolver,
workspaceFolder
variableResolver
);
}
async function detectAvailableWindowsProfiles(configuredProfilesOnly: boolean, fsProvider: IFsProvider, logService?: ILogService, useWslProfiles?: boolean, configProfiles?: { [key: string]: ITerminalProfileObject }, defaultProfileName?: string, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder): Promise<ITerminalProfile[]> {
async function detectAvailableWindowsProfiles(
includeDetectedProfiles: boolean,
fsProvider: IFsProvider,
logService?: ILogService,
useWslProfiles?: boolean,
configProfiles?: { [key: string]: ITerminalProfileObject },
defaultProfileName?: string,
variableResolver?: (text: string[]) => Promise<string[]>
): Promise<ITerminalProfile[]> {
// Determine the correct System32 path. We want to point to Sysnative
// when the 32-bit version of VS Code is running on a 64-bit machine.
// The reason for this is because PowerShell's important PSReadline
@ -67,7 +77,7 @@ async function detectAvailableWindowsProfiles(configuredProfilesOnly: boolean, f
const detectedProfiles: Map<string, ITerminalProfileObject> = new Map();
// Add auto detected profiles
if (!configuredProfilesOnly) {
if (includeDetectedProfiles) {
detectedProfiles.set('PowerShell', {
source: ProfileSource.Pwsh,
icon: Codicon.terminalPowershell.id,
@ -99,9 +109,9 @@ async function detectAvailableWindowsProfiles(configuredProfilesOnly: boolean, f
applyConfigProfilesToMap(configProfiles, detectedProfiles);
const resultProfiles: ITerminalProfile[] = await transformToTerminalProfiles(detectedProfiles.entries(), defaultProfileName, fsProvider, logService, variableResolver, workspaceFolder);
const resultProfiles: ITerminalProfile[] = await transformToTerminalProfiles(detectedProfiles.entries(), defaultProfileName, fsProvider, logService, variableResolver);
if (!configuredProfilesOnly || (configuredProfilesOnly && useWslProfiles)) {
if (includeDetectedProfiles || (!includeDetectedProfiles && useWslProfiles)) {
try {
const result = await getWslProfiles(`${system32Path}\\${useWSLexe ? 'wsl' : 'bash'}.exe`, defaultProfileName);
if (result) {
@ -115,7 +125,13 @@ async function detectAvailableWindowsProfiles(configuredProfilesOnly: boolean, f
return resultProfiles;
}
async function transformToTerminalProfiles(entries: IterableIterator<[string, ITerminalProfileObject]>, defaultProfileName: string | undefined, fsProvider: IFsProvider, logService?: ILogService, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder): Promise<ITerminalProfile[]> {
async function transformToTerminalProfiles(
entries: IterableIterator<[string, ITerminalProfileObject]>,
defaultProfileName: string | undefined,
fsProvider: IFsProvider,
logService?: ILogService,
variableResolver?: (text: string[]) => Promise<string[]>
): Promise<ITerminalProfile[]> {
const resultProfiles: ITerminalProfile[] = [];
for (const [profileName, profile] of entries) {
if (profile === null) { continue; }
@ -138,11 +154,7 @@ async function transformToTerminalProfiles(entries: IterableIterator<[string, IT
icon = profile.icon;
}
const paths = originalPaths.slice();
for (let i = 0; i < paths.length; i++) {
paths[i] = await variableResolver?.resolveAsync(workspaceFolder, paths[i]) || paths[i];
}
const paths = (await variableResolver?.(originalPaths)) || originalPaths.slice();
const validatedProfile = await validateProfilePaths(profileName, defaultProfileName, paths, fsProvider, args, profile.env, profile.overrideName, profile.isAutoDetected, logService);
if (validatedProfile) {
validatedProfile.isAutoDetected = profile.isAutoDetected;
@ -242,11 +254,19 @@ async function getWslProfiles(wslPath: string, defaultProfileName: string | unde
return profiles;
}
async function detectAvailableUnixProfiles(fsProvider: IFsProvider, logService?: ILogService, configuredProfilesOnly?: boolean, configProfiles?: { [key: string]: ITerminalProfileObject }, defaultProfileName?: string, testPaths?: string[], variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder): Promise<ITerminalProfile[]> {
async function detectAvailableUnixProfiles(
fsProvider: IFsProvider,
logService?: ILogService,
includeDetectedProfiles?: boolean,
configProfiles?: { [key: string]: ITerminalProfileObject },
defaultProfileName?: string,
testPaths?: string[],
variableResolver?: (text: string[]) => Promise<string[]>
): Promise<ITerminalProfile[]> {
const detectedProfiles: Map<string, ITerminalProfileObject> = new Map();
// Add non-quick launch profiles
if (!configuredProfilesOnly) {
if (includeDetectedProfiles) {
const contents = await fsProvider.readFile('/etc/shells', 'utf8');
const profiles = testPaths || contents.split('\n').filter(e => e.trim().indexOf('#') !== 0 && e.trim().length > 0);
const counts: Map<string, number> = new Map();
@ -264,7 +284,7 @@ async function detectAvailableUnixProfiles(fsProvider: IFsProvider, logService?:
applyConfigProfilesToMap(configProfiles, detectedProfiles);
return await transformToTerminalProfiles(detectedProfiles.entries(), defaultProfileName, fsProvider, logService, variableResolver, workspaceFolder);
return await transformToTerminalProfiles(detectedProfiles.entries(), defaultProfileName, fsProvider, logService, variableResolver);
}
function applyConfigProfilesToMap(configProfiles: { [key: string]: ITerminalProfileObject } | undefined, profilesMap: Map<string, ITerminalProfileObject>) {
@ -315,6 +335,10 @@ export interface IFsProvider {
readFile(path: string, options: { encoding: BufferEncoding, flag?: string | number } | BufferEncoding): Promise<string>;
}
export interface IProfileVariableResolver {
resolve(text: string[]): Promise<string[]>;
}
interface IPotentialTerminalProfile {
profileName: string;
paths: string[];

View file

@ -702,9 +702,6 @@ export class MainThreadTask implements MainThreadTaskShape {
});
});
},
getDefaultShellAndArgs: (): Promise<{ shell: string, args: string[] | string | undefined }> => {
return Promise.resolve(this._proxy.$getDefaultShellAndArgs());
},
findExecutable: (command: string, cwd?: string, paths?: string[]): Promise<string | undefined> => {
return this._proxy.$findExecutable(command, cwd, paths);
}

View file

@ -16,9 +16,10 @@ import { ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceServ
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
import { IAvailableProfilesRequest as IAvailableProfilesRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy, ITerminalProfileResolverService, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { OperatingSystem, OS } from 'vs/base/common/platform';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
@ -30,11 +31,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
* This comes in play only when dealing with terminals created on the extension host side
*/
private _extHostTerminalIds = new Map<string, number>();
private _remoteAuthority: string | null;
private readonly _toDispose = new DisposableStore();
private readonly _terminalProcessProxies = new Map<number, ITerminalProcessExtHostProxy>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
private _extHostKind: ExtensionHostKind;
/**
* 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
@ -43,17 +42,19 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
*/
private _linkProvider: IDisposable | undefined;
private _os: OperatingSystem = OS;
constructor(
extHostContext: IExtHostContext,
private readonly _extHostContext: IExtHostContext,
@ITerminalService private readonly _terminalService: ITerminalService,
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
@ILogService private readonly _logService: ILogService,
@ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService,
@IRemoteAgentService remoteAgentService: IRemoteAgentService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._remoteAuthority = extHostContext.remoteAuthority;
this._proxy = _extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
// ITerminalService listeners
this._toDispose.add(_terminalService.onInstanceCreated((instance) => {
@ -61,8 +62,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._onInstanceDimensionsChanged(instance);
}));
this._extHostKind = extHostContext.extensionHostKind;
this._toDispose.add(_terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
this._toDispose.add(_terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
this._toDispose.add(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
@ -70,7 +69,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e)));
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null)));
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.instanceId, instance.title)));
this._toDispose.add(_terminalService.onRequestAvailableProfiles(e => this._onRequestAvailableProfiles(e)));
// Set initial ext host state
this._terminalService.terminalInstances.forEach(t => {
@ -89,7 +87,11 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._proxy.$initEnvironmentVariableCollections(serializedCollections);
}
this._terminalService.extHostReady(extHostContext.remoteAuthority!); // TODO@Tyriar: remove null assertion
remoteAgentService.getEnvironment().then(async env => {
this._os = env?.os || OS;
this._updateDefaultProfile();
});
this._terminalService.onDidChangeAvailableProfiles(() => this._updateDefaultProfile());
}
public dispose(): void {
@ -97,6 +99,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._linkProvider?.dispose();
}
private async _updateDefaultProfile() {
const remoteAuthority = withNullAsUndefined(this._extHostContext.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 _getTerminalId(id: TerminalIdentifier): number | undefined {
if (typeof id === 'number') {
return id;
@ -311,21 +320,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._getTerminalProcess(terminalId)?.emitLatency(sum / COUNT);
}
private _isPrimaryExtHost(): boolean {
// The "primary" ext host is the remote ext host if there is one, otherwise the local
const conn = this._remoteAgentService.getConnection();
if (conn) {
return this._remoteAuthority === conn.remoteAuthority;
}
return this._extHostKind !== ExtensionHostKind.LocalWebWorker;
}
private async _onRequestAvailableProfiles(req: IAvailableProfilesRequest): Promise<void> {
if (this._isPrimaryExtHost()) {
req.callback(await this._proxy.$getAvailableProfiles(req.configuredProfilesOnly));
}
}
private _getTerminalProcess(terminalId: number): ITerminalProcessExtHostProxy | undefined {
const terminal = this._terminalProcessProxies.get(terminalId);
if (!terminal) {

View file

@ -296,7 +296,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get uriScheme() { return initData.environment.appUriScheme; },
get clipboard(): vscode.Clipboard { return extHostClipboard.value; },
get shell() {
return extHostTerminalService.getDefaultShell(false, configProvider);
return extHostTerminalService.getDefaultShell(false);
},
get isTelemetryEnabled() {
return extHostTelemetry.getTelemetryEnabled();

View file

@ -39,7 +39,7 @@ import { IRemoteConnectionData, RemoteAuthorityResolverErrorCode, ResolverResult
import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel';
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile } from 'vs/platform/terminal/common/terminal';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust';
@ -56,7 +56,6 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionRunTestsRequest, InternalTestItem, ISerializedTestResults, ITestItem, ITestMessage, ITestRunTask, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTimelineOptions, Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
import { ActivationKind, ExtensionHostKind, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
@ -1668,11 +1667,6 @@ export interface ExtHostTelemetryShape {
$onDidChangeTelemetryEnabled(enabled: boolean): void;
}
export interface IShellAndArgsDto {
shell: string;
args: string[] | string | undefined;
}
export interface ITerminalLinkDto {
/** The ID of the link to enable activation and disposal. */
id: number;
@ -1706,10 +1700,10 @@ export interface ExtHostTerminalServiceShape {
$acceptProcessRequestInitialCwd(id: number): void;
$acceptProcessRequestCwd(id: number): void;
$acceptProcessRequestLatency(id: number): number;
$getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
$activateLink(id: number, linkId: number): void;
$initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
$acceptDefaultProfile(profile: ITerminalProfile, automationProfile: ITerminalProfile): void;
}
export interface ExtHostSCMShape {
@ -1728,7 +1722,6 @@ export interface ExtHostTaskShape {
$onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): void;
$OnDidEndTask(execution: tasks.TaskExecutionDTO): void;
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string; }, variables: string[]; }): Promise<{ process?: string; variables: { [key: string]: string; }; }>;
$getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined; }>;
$jsonTasksSupported(): Thenable<boolean>;
$findExecutable(command: string, cwd?: string, paths?: string[]): Promise<string | undefined>;
}

View file

@ -605,8 +605,6 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask
public abstract $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>;
public abstract $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>;
private nextHandle(): number {
return this._handleCounter++;
}
@ -775,10 +773,6 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
return result;
}
public $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> {
throw new Error('Not implemented');
}
public async $jsonTasksSupported(): Promise<boolean> {
return false;
}

View file

@ -5,8 +5,7 @@
import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
@ -19,9 +18,8 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
@ -40,11 +38,10 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
createTerminalFromOptions(options: vscode.TerminalOptions, isFeatureTerminal?: boolean): vscode.Terminal;
createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal;
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
getDefaultShell(useAutomationShell: boolean): string;
getDefaultShellArgs(useAutomationShell: boolean): string[] | string;
registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable;
getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
}
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
@ -293,6 +290,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal | undefined> } = {};
protected _environmentVariableCollections: Map<string, EnvironmentVariableCollection> = new Map();
private _defaultProfile: ITerminalProfile | undefined;
private _defaultAutomationProfile: ITerminalProfile | undefined;
private readonly _bufferer: TerminalDataBufferer;
private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
@ -336,10 +335,15 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public abstract createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal;
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
public abstract getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
public abstract $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
public getDefaultShell(useAutomationShell: boolean): string {
const profile = useAutomationShell ? this._defaultAutomationProfile : this._defaultProfile;
return profile?.path || '';
}
public getDefaultShellArgs(useAutomationShell: boolean): string[] | string {
const profile = useAutomationShell ? this._defaultAutomationProfile : this._defaultProfile;
return profile?.args || [''];
}
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
@ -690,6 +694,11 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
});
}
public $acceptDefaultProfile(profile: ITerminalProfile, automationProfile: ITerminalProfile): void {
this._defaultProfile = profile;
this._defaultAutomationProfile = automationProfile;
}
private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
this._environmentVariableCollections.set(extensionIdentifier, collection);
collection.onDidChangeCollection(() => {
@ -778,20 +787,4 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
throw new NotSupportedError();
}
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
throw new NotSupportedError();
}
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
throw new NotSupportedError();
}
public async getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
throw new NotSupportedError();
}
public $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
throw new NotSupportedError();
}
}

View file

@ -81,8 +81,8 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
}
const configProvider = await this._configurationService.getConfigProvider();
const shell = this._terminalService.getDefaultShell(true, configProvider);
const shellArgs = this._terminalService.getDefaultShellArgs(true, configProvider);
const shell = this._terminalService.getDefaultShell(true);
const shellArgs = this._terminalService.getDefaultShellArgs(true);
const shellConfig = JSON.stringify({ shell, shellArgs });
let terminal = await this._integratedTerminalInstances.checkout(shellConfig);

View file

@ -174,10 +174,6 @@ export class ExtHostTask extends ExtHostTaskBase {
return result;
}
public $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> {
return this._terminalService.getDefaultShellAndArgs(true);
}
public async $jsonTasksSupported(): Promise<boolean> {
return true;
}

View file

@ -3,52 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as platform from 'vs/base/common/platform';
import { withNullAsUndefined } from 'vs/base/common/types';
import { generateUuid } from 'vs/base/common/uuid';
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
import { ILogService } from 'vs/platform/log/common/log';
import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ITerminalProfile, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles';
import type * as vscode from 'vscode';
export class ExtHostTerminalService extends BaseExtHostTerminalService {
private _variableResolver: ExtHostVariableResolverService | undefined;
private _variableResolverPromise: Promise<ExtHostVariableResolverService>;
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
private _defaultShell: string | undefined;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostConfiguration private _extHostConfiguration: ExtHostConfiguration,
@IExtHostWorkspace private _extHostWorkspace: ExtHostWorkspace,
@IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
@ILogService private _logService: ILogService,
@IExtHostEditorTabs private _extHostEditorTabs: IExtHostEditorTabs
@IExtHostRpcService extHostRpc: IExtHostRpcService
) {
super(true, extHostRpc);
// Getting the SystemShell is an async operation, however, the ExtHost terminal service is mostly synchronous
// and the API `vscode.env.shell` is also synchronous. The default shell _should_ be set when extensions are
// starting up but if not, we run getSystemShellSync below which gets a sane default.
getSystemShell(platform.OS, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
this._updateLastActiveWorkspace();
this._variableResolverPromise = this._updateVariableResolver();
this._registerListeners();
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
@ -76,72 +42,4 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
);
return terminal.value;
}
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
return terminalEnvironment.getDefaultShell(
this._buildSafeConfigProvider(configProvider),
this._defaultShell ?? getSystemShellSync(platform.OS, process.env as platform.IProcessEnvironment),
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
process.env.windir,
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
this._logService,
useAutomationShell
);
}
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
return terminalEnvironment.getDefaultShellArgs(
this._buildSafeConfigProvider(configProvider),
useAutomationShell,
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
this._logService
);
}
private _registerListeners(): void {
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(() => this._updateLastActiveWorkspace());
this._extHostWorkspace.onDidChangeWorkspace(() => {
this._variableResolverPromise = this._updateVariableResolver();
});
}
private _updateLastActiveWorkspace(): void {
const activeEditor = this._extHostDocumentsAndEditors.activeEditor();
if (activeEditor) {
this._lastActiveWorkspace = this._extHostWorkspace.getWorkspaceFolder(activeEditor.document.uri) as IWorkspaceFolder;
}
}
private async _updateVariableResolver(): Promise<ExtHostVariableResolverService> {
const configProvider = await this._extHostConfiguration.getConfigProvider();
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, this._extHostEditorTabs);
return this._variableResolver;
}
public async $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
const safeConfigProvider = this._buildSafeConfigProvider(await this._extHostConfiguration.getConfigProvider());
return detectAvailableProfiles(configuredProfilesOnly, safeConfigProvider, undefined, this._logService, await this._variableResolverPromise, this._lastActiveWorkspace);
}
public async getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
const configProvider = await this._extHostConfiguration.getConfigProvider();
return {
shell: this.getDefaultShell(useAutomationShell, configProvider),
args: this.getDefaultShellArgs(useAutomationShell, configProvider)
};
}
// TODO: Remove when workspace trust is enabled
private _buildSafeConfigProvider(configProvider: ExtHostConfigProvider): SafeConfigProvider {
const config = configProvider.getConfiguration();
return (key: string) => {
const isWorkspaceConfigAllowed = config.get(TerminalSettingId.AllowWorkspaceConfiguration);
if (isWorkspaceConfigAllowed) {
return config.get(key) as any;
}
const inspected = config.inspect(key);
return inspected?.globalValue || inspected?.defaultValue;
};
}
}

View file

@ -27,7 +27,7 @@ import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ITerminalProfileResolverService, TerminalSettingId, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalProfileResolverService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors';
@ -47,7 +47,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
import { ILogService } from 'vs/platform/log/common/log';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
import { TaskTerminalStatus } from 'vs/workbench/contrib/tasks/browser/taskTerminalStatus';
import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService';
@ -1019,20 +1019,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
const description = nls.localize('TerminalTaskSystem.terminalDescription', 'Task');
let originalCommand = task.command.name;
if (isShellCommand) {
let defaultConfig: { shell: string, args: string[] | string | undefined };
if (variableResolver.taskSystemInfo) {
defaultConfig = await variableResolver.taskSystemInfo.getDefaultShellAndArgs();
} else {
const defaultProfile = await this.terminalProfileResolverService.getDefaultProfile({
allowAutomationShell: true,
os: Platform.OS,
remoteAuthority: this.environmentService.remoteAuthority
});
defaultConfig = {
shell: defaultProfile.path,
args: defaultProfile.args
};
}
const defaultProfile = await this.terminalProfileResolverService.getDefaultProfile({
allowAutomationShell: true,
os: Platform.OS,
remoteAuthority: this.environmentService.remoteAuthority
});
const defaultConfig = {
shell: defaultProfile.path,
args: defaultProfile.args
};
shellLaunchConfig = { name: terminalName, description, executable: defaultConfig.shell, args: defaultConfig.args, waitOnExit };
let shellSpecified: boolean = false;
let shellOptions: ShellConfiguration | undefined = task.command.options && task.command.options.shell;
@ -1043,10 +1038,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
if (shellOptions.args) {
shellLaunchConfig.args = await this.resolveVariables(variableResolver, shellOptions.args.slice());
} else {
shellLaunchConfig.args = [];
}
}
if (shellLaunchConfig.args === undefined) {
shellLaunchConfig.args = [];
}
let shellArgs = Array.isArray(shellLaunchConfig.args!) ? <string[]>shellLaunchConfig.args!.slice(0) : [shellLaunchConfig.args!];
let toAdd: string[] = [];
let commandLine = this.buildShellCommandLine(platform, shellLaunchConfig.executable!, shellOptions, command, originalCommand, args);

View file

@ -120,7 +120,6 @@ export interface TaskSystemInfo {
context: any;
uriProvider: (this: void, path: string) => URI;
resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables | undefined>;
getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>;
findExecutable(command: string, cwd?: string, paths?: string[]): Promise<string | undefined>;
}

View file

@ -6,7 +6,9 @@
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { revive } from 'vs/base/common/marshalling';
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 { ICommandService } from 'vs/platform/commands/common/commands';
@ -14,11 +16,14 @@ 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 { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } 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';
import { ICompleteTerminalConfiguration, RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class RemoteTerminalService extends Disposable implements IRemoteTerminalService {
@ -34,6 +39,8 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
readonly onPtyHostResponsive = this._onPtyHostResponsive.event;
private readonly _onPtyHostRestart = this._register(new Emitter<void>());
readonly onPtyHostRestart = this._onPtyHostRestart.event;
private readonly _onPtyHostRequestResolveVariables = this._register(new Emitter<IRequestResolveVariablesEvent>());
readonly onPtyHostRequestResolveVariables = this._onPtyHostRequestResolveVariables.event;
constructor(
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@ -41,7 +48,10 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ICommandService private readonly _commandService: ICommandService,
@INotificationService notificationService: INotificationService,
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService,
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
@IHistoryService historyService: IHistoryService
) {
super();
@ -122,6 +132,15 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
this._onPtyHostResponsive.fire();
}));
}
this._register(channel.onPtyHostRequestResolveVariables(async e => {
const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file);
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
const resolveCalls: Promise<string>[] = e.originalText.map(t => {
return configurationResolverService.resolveAsync(lastActiveWorkspaceRoot, t);
});
const result = await Promise.all(resolveCalls);
channel.acceptPtyHostResolvedVariables(e.id, result);
}));
} else {
this._remoteTerminalChannel = null;
}
@ -204,6 +223,10 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
return this._remoteTerminalChannel?.getDefaultSystemShell(osOverride) || '';
}
async getProfiles(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]> {
return this._remoteTerminalChannel?.getProfiles(includeDetectedProfiles) || [];
}
async getEnvironment(): Promise<IProcessEnvironment> {
return this._remoteTerminalChannel?.getEnvironment() || {};
}

View file

@ -21,7 +21,6 @@ import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminal
import { KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, TERMINAL_VIEW_ID, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IRemoteTerminalService, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
@ -29,7 +28,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { terminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
import { registerTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
import { terminalViewIcon } from 'vs/workbench/contrib/terminal/browser/terminalIcons';
import { RemoteTerminalService } from 'vs/workbench/contrib/terminal/browser/remoteTerminalService';
@ -37,6 +36,7 @@ import { WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { isIOS, isWindows } from 'vs/base/common/platform';
import { setupTerminalMenus } from 'vs/workbench/contrib/terminal/browser/terminalMenus';
import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration';
// Register services
registerSingleton(ITerminalService, TerminalService, true);
@ -59,8 +59,8 @@ const quickAccessNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpe
CommandsRegistry.registerCommand({ id: quickAccessNavigatePreviousInTerminalPickerId, handler: getQuickNavigateHandler(quickAccessNavigatePreviousInTerminalPickerId, false) });
// Register configurations
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration(terminalConfiguration);
registerTerminalPlatformConfiguration();
registerTerminalConfiguration();
// Register views
const VIEW_CONTAINER = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({

View file

@ -8,8 +8,8 @@ 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, ITerminalTabLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IAvailableProfilesRequest, ICommandTracker, INavigationMode, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { IOffProcessTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, 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';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
@ -94,6 +94,7 @@ export interface ITerminalService {
isProcessSupportRegistered: boolean;
readonly connectionState: TerminalConnectionState;
readonly availableProfiles: ITerminalProfile[];
readonly profilesReady: Promise<void>;
initializeTerminals(): Promise<void>;
onActiveGroupChanged: Event<void>;
@ -112,7 +113,6 @@ export interface ITerminalService {
onInstanceIconChanged: Event<ITerminalInstance | undefined>;
onInstancePrimaryStatusChanged: Event<ITerminalInstance>;
onActiveInstanceChanged: Event<ITerminalInstance | undefined>;
onRequestAvailableProfiles: Event<IAvailableProfilesRequest>;
onDidRegisterProcessSupport: Event<void>;
onDidChangeConnectionState: Event<void>;
onDidChangeAvailableProfiles: Event<ITerminalProfile[]>;
@ -187,7 +187,6 @@ export interface ITerminalService {
setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;
extHostReady(remoteAuthority: string): void;
requestStartExtensionTerminal(proxy: ITerminalProcessExtHostProxy, cols: number, rows: number): Promise<ITerminalLaunchError | undefined>;
isAttachedToTerminal(remoteTerm: IRemoteTerminalAttachTarget): boolean;
}

View file

@ -26,13 +26,13 @@ import { IListService } from 'vs/platform/list/browser/listService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ILocalTerminalService } from 'vs/platform/terminal/common/terminal';
import { ILocalTerminalService, ITerminalProfile, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions';
import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, ITerminalProfile, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId, TerminalSettingId, TERMINAL_VIEW_ID, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId, TERMINAL_VIEW_ID, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history';

View file

@ -24,7 +24,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } 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, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
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';
import { ansiColorIdentifiers, ansiColorMap, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
import { TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
@ -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 } from 'vs/platform/terminal/common/terminal';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId } 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';

View file

@ -6,7 +6,8 @@
import { localize } from 'vs/nls';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { ContextKeyAndExpr, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, TerminalCommandId, TerminalSettingId, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, TerminalCommandId, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal';
const enum ContextMenuGroup {
Create = '1_create',

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { ProcessState, ITerminalProcessManager, ITerminalConfigHelper, IBeforeProcessDataEvent, ITerminalProfileResolverService, ITerminalConfiguration, TERMINAL_CONFIG_SECTION, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { ProcessState, ITerminalProcessManager, ITerminalConfigHelper, IBeforeProcessDataEvent, ITerminalProfileResolverService, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
@ -22,7 +22,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, TerminalShellType, ILocalTerminalService, IOffProcessTerminalService, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, TerminalShellType, ILocalTerminalService, IOffProcessTerminalService, ITerminalDimensions, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
import { localize } from 'vs/nls';
import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings';
@ -172,9 +172,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
detachFromProcess(): void {
if (this._process?.detach) {
this._process.detach();
}
this._process?.detach?.();
}
async createProcess(

View file

@ -13,8 +13,8 @@ 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 } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfigResolveOptions, ITerminalProfile, ITerminalProfileResolverService, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalProfile, 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';
@ -153,7 +153,9 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro
return shellSettingProfile;
}
// Return the real default profile if it exists and is valid
// Return the real default profile if it exists and is valid, wait for profiles to be ready
// if the window just opened
await this._terminalService.profilesReady;
const defaultProfile = this._getUnresolvedRealDefaultProfile(options.os);
if (defaultProfile) {
return defaultProfile;

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { timeout } from 'vs/base/common/async';
import { AutoOpenBarrier, timeout } from 'vs/base/common/async';
import { debounce, throttle } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
@ -17,7 +17,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILocalTerminalService, IOffProcessTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById } from 'vs/platform/terminal/common/terminal';
import { ILocalTerminalService, IOffProcessTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
@ -25,9 +25,8 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { TerminalGroup } from 'vs/workbench/contrib/terminal/browser/terminalGroup';
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView';
import { IAvailableProfilesRequest, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, TERMINAL_VIEW_ID, ITerminalProfileObject, KEYBINDING_CONTEXT_TERMINAL_COUNT, TerminalSettingId, ITerminalTypeContribution, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, TERMINAL_VIEW_ID, KEYBINDING_CONTEXT_TERMINAL_COUNT, ITerminalTypeContribution, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE } from 'vs/workbench/contrib/terminal/common/terminal';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { ILifecycleService, ShutdownReason, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
@ -53,7 +52,6 @@ export class TerminalService implements ITerminalService {
private _terminalGroups: ITerminalGroup[] = [];
private _backgroundedTerminalInstances: ITerminalInstance[] = [];
private _findState: FindReplaceState;
private _extHostsReady: { [authority: string]: IExtHostReadyEntry | undefined } = {};
private _activeGroupIndex: number;
private _activeInstanceIndex: number;
private _linkProviders: Set<ITerminalExternalLinkProvider> = new Set();
@ -61,6 +59,7 @@ export class TerminalService implements ITerminalService {
private _processSupportContextKey: IContextKey<boolean>;
private readonly _localTerminalService?: ILocalTerminalService;
private readonly _offProcessTerminalService?: IOffProcessTerminalService;
private _profilesReadyBarrier: AutoOpenBarrier;
private _availableProfiles: ITerminalProfile[] | undefined;
private _configHelper: TerminalConfigHelper;
private _terminalContainer: HTMLElement | undefined;
@ -72,6 +71,7 @@ export class TerminalService implements ITerminalService {
public get terminalGroups(): ITerminalGroup[] { return this._terminalGroups; }
public get isProcessSupportRegistered(): boolean { return !!this._processSupportContextKey.get(); }
get connectionState(): TerminalConnectionState { return this._connectionState; }
get profilesReady(): Promise<void> { return this._profilesReadyBarrier.wait().then(() => { }); }
get availableProfiles(): ITerminalProfile[] {
this._refreshAvailableProfiles();
return this._availableProfiles || [];
@ -114,8 +114,6 @@ export class TerminalService implements ITerminalService {
public get onInstancePrimaryStatusChanged(): Event<ITerminalInstance> { return this._onInstancePrimaryStatusChanged.event; }
private readonly _onGroupDisposed = new Emitter<ITerminalGroup>();
public get onGroupDisposed(): Event<ITerminalGroup> { return this._onGroupDisposed.event; }
private readonly _onRequestAvailableProfiles = new Emitter<IAvailableProfilesRequest>();
get onRequestAvailableProfiles(): Event<IAvailableProfilesRequest> { return this._onRequestAvailableProfiles.event; }
private readonly _onDidRegisterProcessSupport = new Emitter<void>();
get onDidRegisterProcessSupport(): Event<void> { return this._onDidRegisterProcessSupport.event; }
private readonly _onDidChangeConnectionState = new Emitter<void>();
@ -140,7 +138,6 @@ export class TerminalService implements ITerminalService {
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IExtensionService private readonly _extensionService: IExtensionService,
@ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService,
@ICommandService private readonly _commandService: ICommandService,
@optional(ILocalTerminalService) localTerminalService: ILocalTerminalService
@ -194,23 +191,24 @@ export class TerminalService implements ITerminalService {
const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions;
const conn = this._remoteAgentService.getConnection();
const remoteAuthority = conn ? conn.remoteAuthority : 'null';
this._whenExtHostReady(remoteAuthority).then(() => {
this._refreshAvailableProfiles();
});
// 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;
let initPromise: Promise<any> = isPersistentRemote ? this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals() :
enableTerminalReconnection ? this._localTerminalsInitPromise = this._reconnectToLocalTerminals() :
Promise.resolve();
this._offProcessTerminalService = isPersistentRemote ? this._remoteTerminalService :
enableTerminalReconnection ? this._localTerminalService : undefined;
let initPromise: Promise<any> = isPersistentRemote
? this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals()
: enableTerminalReconnection
? this._localTerminalsInitPromise = this._reconnectToLocalTerminals()
: Promise.resolve();
this._offProcessTerminalService = !!this._environmentService.remoteAuthority ? this._remoteTerminalService : this._localTerminalService;
initPromise.then(() => this._setConnected());
// Wait up to 5 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(5000);
this._refreshAvailableProfiles();
}
private _setConnected() {
@ -323,43 +321,22 @@ export class TerminalService implements ITerminalService {
});
}
async extHostReady(remoteAuthority: string): Promise<void> {
this._createExtHostReadyEntry(remoteAuthority);
this._extHostsReady[remoteAuthority]!.resolve();
}
@throttle(10000)
@throttle(2000)
private async _refreshAvailableProfiles(): Promise<void> {
const result = await this._detectProfiles(true);
const result = await this._detectProfiles();
if (!equals(result, this._availableProfiles)) {
this._availableProfiles = result;
this._onDidChangeAvailableProfiles.fire(this._availableProfiles);
this._profilesReadyBarrier.open();
}
}
private async _detectProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
await this._extensionService.whenInstalledExtensionsRegistered();
// Wait for the remoteAuthority to be ready (and listening for events) before firing
// the event to spawn the ext host process
const conn = this._remoteAgentService.getConnection();
const remoteAuthority = conn ? conn.remoteAuthority : 'null';
await this._whenExtHostReady(remoteAuthority);
return new Promise(r => this._onRequestAvailableProfiles.fire({ callback: r, configuredProfilesOnly: configuredProfilesOnly }));
}
private async _whenExtHostReady(remoteAuthority: string): Promise<void> {
this._createExtHostReadyEntry(remoteAuthority);
return this._extHostsReady[remoteAuthority]!.promise;
}
private _createExtHostReadyEntry(remoteAuthority: string): void {
if (this._extHostsReady[remoteAuthority]) {
return;
private async _detectProfiles(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]> {
const offProcService = this._offProcessTerminalService;
if (!offProcService) {
return this._availableProfiles || [];
}
let resolve!: () => void;
const promise = new Promise<void>(r => resolve = r);
this._extHostsReady[remoteAuthority] = { promise, resolve };
return offProcService?.getProfiles(includeDetectedProfiles);
}
private _onBeforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> {
@ -413,6 +390,9 @@ export class TerminalService implements ITerminalService {
@debounce(500)
private _saveState(): void {
if (!this.configHelper.config.enablePersistentSessions) {
return;
}
const state: ITerminalsLayoutInfoById = {
tabs: this.terminalGroups.map(g => g.getLayoutInfo(g === this.getActiveGroup()))
};
@ -421,7 +401,7 @@ export class TerminalService implements ITerminalService {
@debounce(500)
private _updateTitle(instance?: ITerminalInstance): void {
if (!instance || !instance.persistentProcessId || !instance.title) {
if (!this.configHelper.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.title) {
return;
}
this._offProcessTerminalService?.updateTitle(instance.persistentProcessId, instance.title);
@ -429,7 +409,7 @@ export class TerminalService implements ITerminalService {
@debounce(500)
private _updateIcon(instance?: ITerminalInstance): void {
if (!instance || !instance.persistentProcessId || !instance.icon) {
if (!this.configHelper.config.enablePersistentSessions || !instance || !instance.persistentProcessId || !instance.icon) {
return;
}
this._offProcessTerminalService?.updateIcon(instance.persistentProcessId, instance.icon.id, instance.color);
@ -765,7 +745,7 @@ export class TerminalService implements ITerminalService {
async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise<ITerminalInstance | undefined> {
let keyMods: IKeyMods | undefined;
const profiles = await this._detectProfiles(false);
const profiles = await this._detectProfiles(true);
const platformKey = await this._getPlatformKey();
const options: IPickOptions<IProfileQuickPickItem> = {
@ -1043,11 +1023,6 @@ interface IProfileQuickPickItem extends IQuickPickItem {
profile: ITerminalProfile | ITerminalTypeContribution;
}
interface IExtHostReadyEntry {
promise: Promise<void>;
resolve: () => void;
}
interface IInstanceLocation {
group: ITerminalGroup,
groupIndex: number,

View file

@ -23,7 +23,8 @@ import { Action, IAction, Separator } from 'vs/base/common/actions';
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_TABS_NARROW_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_TABS_NARROW_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { localize } from 'vs/nls';

View file

@ -16,7 +16,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { MenuItemAction } from 'vs/platform/actions/common/actions';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, TerminalCommandId, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { Codicon } from 'vs/base/common/codicons';
import { Action } from 'vs/base/common/actions';
import { MarkdownString } from 'vs/base/common/htmlContent';

View file

@ -22,7 +22,8 @@ import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IMenu, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { ITerminalProfile, ITerminalProfileResolverService, TerminalCommandId, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalProfileResolverService, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalSettingId, ITerminalProfile } from 'vs/platform/terminal/common/terminal';
import { ActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';

View file

@ -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, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IProcessDataEvent, IRequestResolveVariablesEvent, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, 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';
@ -84,6 +84,9 @@ export class RemoteTerminalChannelClient {
get onPtyHostResponsive(): Event<void> {
return this._channel.listen<void>('$onPtyHostResponsiveEvent');
}
get onPtyHostRequestResolveVariables(): Event<IRequestResolveVariablesEvent> {
return this._channel.listen<IRequestResolveVariablesEvent>('$onPtyHostRequestResolveVariablesEvent');
}
get onProcessData(): Event<{ id: number, event: IProcessDataEvent | string }> {
return this._channel.listen<{ id: number, event: IProcessDataEvent | string }>('$onProcessDataEvent');
}
@ -235,6 +238,12 @@ export class RemoteTerminalChannelClient {
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string> {
return this._channel.call('$getDefaultSystemShell', [osOverride]);
}
getProfiles(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]> {
return this._channel.call('$getProfiles', [includeDetectedProfiles]);
}
acceptPtyHostResolvedVariables(id: number, resolved: string[]) {
return this._channel.call('$acceptPtyHostResolvedVariables', [id, resolved]);
}
getEnvironment(): Promise<IProcessEnvironment> {
return this._channel.call('$getEnvironment');

View file

@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform';
import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensions, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IProcessDataEvent, 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';
@ -261,44 +261,6 @@ export interface IBeforeProcessDataEvent {
data: string;
}
export interface ITerminalProfile {
profileName: string;
path: string;
isDefault: boolean;
isAutoDetected?: boolean;
args?: string | string[] | undefined;
env?: ITerminalEnvironment;
overrideName?: boolean;
icon?: string;
}
export const enum ProfileSource {
GitBash = 'Git Bash',
Pwsh = 'PowerShell'
}
export interface IBaseUnresolvedTerminalProfile {
args?: string | string[] | undefined;
isAutoDetected?: boolean;
overrideName?: boolean;
icon?: string;
env?: ITerminalEnvironment;
}
export interface ITerminalExecutable extends IBaseUnresolvedTerminalProfile {
path: string | string[];
}
export interface ITerminalProfileSource extends IBaseUnresolvedTerminalProfile {
source: ProfileSource;
}
export type ITerminalProfileObject = ITerminalExecutable | ITerminalProfileSource | null;
export interface IAvailableProfilesRequest {
callback: (shells: ITerminalProfile[]) => void;
configuredProfilesOnly: boolean;
}
export interface IDefaultShellAndArgsRequest {
useAutomationShell: boolean;
callback: (shell: string, args: string[] | string | undefined) => void;
@ -412,78 +374,6 @@ export enum TitleEventSource {
export const QUICK_LAUNCH_PROFILE_CHOICE = 'workbench.action.terminal.profile.choice';
export const enum TerminalSettingId {
ShellLinux = 'terminal.integrated.shell.linux',
ShellMacOs = 'terminal.integrated.shell.osx',
ShellWindows = 'terminal.integrated.shell.windows',
SendKeybindingsToShell = 'terminal.integrated.sendKeybindingsToShell',
AutomationShellLinux = 'terminal.integrated.automationShell.linux',
AutomationShellMacOs = 'terminal.integrated.automationShell.osx',
AutomationShellWindows = 'terminal.integrated.automationShell.windows',
ShellArgsLinux = 'terminal.integrated.shellArgs.linux',
ShellArgsMacOs = 'terminal.integrated.shellArgs.osx',
ShellArgsWindows = 'terminal.integrated.shellArgs.windows',
ProfilesWindows = 'terminal.integrated.profiles.windows',
ProfilesMacOs = 'terminal.integrated.profiles.osx',
ProfilesLinux = 'terminal.integrated.profiles.linux',
DefaultProfileLinux = 'terminal.integrated.defaultProfile.linux',
DefaultProfileMacOs = 'terminal.integrated.defaultProfile.osx',
DefaultProfileWindows = 'terminal.integrated.defaultProfile.windows',
UseWslProfiles = 'terminal.integrated.useWslProfiles',
TabsEnabled = 'terminal.integrated.tabs.enabled',
TabsHideCondition = 'terminal.integrated.tabs.hideCondition',
TabsShowActiveTerminal = 'terminal.integrated.tabs.showActiveTerminal',
TabsLocation = 'terminal.integrated.tabs.location',
TabsFocusMode = 'terminal.integrated.tabs.focusMode',
MacOptionIsMeta = 'terminal.integrated.macOptionIsMeta',
MacOptionClickForcesSelection = 'terminal.integrated.macOptionClickForcesSelection',
AltClickMovesCursor = 'terminal.integrated.altClickMovesCursor',
CopyOnSelection = 'terminal.integrated.copyOnSelection',
DrawBoldTextInBrightColors = 'terminal.integrated.drawBoldTextInBrightColors',
FontFamily = 'terminal.integrated.fontFamily',
FontSize = 'terminal.integrated.fontSize',
LetterSpacing = 'terminal.integrated.letterSpacing',
LineHeight = 'terminal.integrated.lineHeight',
MinimumContrastRatio = 'terminal.integrated.minimumContrastRatio',
FastScrollSensitivity = 'terminal.integrated.fastScrollSensitivity',
MouseWheelScrollSensitivity = 'terminal.integrated.mouseWheelScrollSensitivity',
BellDuration = 'terminal.integrated.bellDuration',
FontWeight = 'terminal.integrated.fontWeight',
FontWeightBold = 'terminal.integrated.fontWeightBold',
CursorBlinking = 'terminal.integrated.cursorBlinking',
CursorStyle = 'terminal.integrated.cursorStyle',
CursorWidth = 'terminal.integrated.cursorWidth',
Scrollback = 'terminal.integrated.scrollback',
DetectLocale = 'terminal.integrated.detectLocale',
GpuAcceleration = 'terminal.integrated.gpuAcceleration',
RightClickBehavior = 'terminal.integrated.rightClickBehavior',
Cwd = 'terminal.integrated.cwd',
ConfirmOnExit = 'terminal.integrated.confirmOnExit',
EnableBell = 'terminal.integrated.enableBell',
CommandsToSkipShell = 'terminal.integrated.commandsToSkipShell',
AllowChords = 'terminal.integrated.allowChords',
AllowMnemonics = 'terminal.integrated.allowMnemonics',
EnvMacOs = 'terminal.integrated.env.osx',
EnvLinux = 'terminal.integrated.env.linux',
EnvWindows = 'terminal.integrated.env.windows',
EnvironmentChangesIndicator = 'terminal.integrated.environmentChangesIndicator',
EnvironmentChangesRelaunch = 'terminal.integrated.environmentChangesRelaunch',
ShowExitAlert = 'terminal.integrated.showExitAlert',
SplitCwd = 'terminal.integrated.splitCwd',
WindowsEnableConpty = 'terminal.integrated.windowsEnableConpty',
WordSeparators = 'terminal.integrated.wordSeparators',
ExperimentalUseTitleEvent = 'terminal.integrated.experimentalUseTitleEvent',
EnableFileLinks = 'terminal.integrated.enableFileLinks',
UnicodeVersion = 'terminal.integrated.unicodeVersion',
ExperimentalLinkProvider = 'terminal.integrated.experimentalLinkProvider',
LocalEchoLatencyThreshold = 'terminal.integrated.localEchoLatencyThreshold',
LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms',
LocalEchoStyle = 'terminal.integrated.localEchoStyle',
EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions',
AllowWorkspaceConfiguration = 'terminal.integrated.allowWorkspaceConfiguration',
InheritEnv = 'terminal.integrated.inheritEnv'
}
export const enum TerminalCommandId {
FindNext = 'workbench.action.terminal.findNext',
FindPrevious = 'workbench.action.terminal.findPrevious',

View file

@ -3,55 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConfigurationScope, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
import { Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { localize } from 'vs/nls';
import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE, TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Registry } from 'vs/platform/registry/common/platform';
const terminalProfileSchema: IJSONSchema = {
type: 'object',
required: ['path'],
properties: {
path: {
description: localize('terminalProfile.path', 'A single path to a shell executable or an array of paths that will be used as fallbacks when one fails.'),
type: ['string', 'array'],
items: {
type: 'string'
}
},
args: {
description: localize('terminalProfile.args', 'An optional set of arguments to run the shell executable with.'),
type: 'array',
items: {
type: 'string'
}
},
overrideName: {
description: localize('terminalProfile.overrideName', 'Controls whether or not the profile name overrides the auto detected one.'),
type: 'boolean'
},
icon: {
description: localize('terminalProfile.icon', 'A codicon ID to associate with this terminal.'),
type: 'string'
},
env: {
markdownDescription: localize('terminalProfile.env', "An object with environment variables that will be added to the terminal profile process. Set to `null` to delete environment variables from the base environment."),
type: 'object',
additionalProperties: {
type: ['string', 'null']
},
default: {}
}
}
};
const shellDeprecationMessageLinux = localize('terminal.integrated.shell.linux.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.linux#`', '`#terminal.integrated.defaultProfile.linux#`');
const shellDeprecationMessageOsx = localize('terminal.integrated.shell.osx.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.osx#`', '`#terminal.integrated.defaultProfile.osx#`');
const shellDeprecationMessageWindows = localize('terminal.integrated.shell.windows.deprecation', "This is deprecated, the new recommended way to configure your default shell is by creating a terminal profile in {0} and setting its profile name as the default in {1}. This will currently take priority over the new profiles settings but that will change in the future.", '`#terminal.integrated.profiles.windows#`', '`#terminal.integrated.defaultProfile.windows#`');
export const terminalConfiguration: IConfigurationNode = {
const terminalConfiguration: IConfigurationNode = {
id: 'terminal',
order: 100,
title: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"),
@ -62,252 +21,6 @@ export const terminalConfiguration: IConfigurationNode = {
type: 'boolean',
default: false
},
[TerminalSettingId.AutomationShellLinux]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.linux',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.AutomationShellMacOs]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.osx',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.AutomationShellWindows]: {
restricted: true,
markdownDescription: localize({
key: 'terminal.integrated.automationShell.windows',
comment: ['{0} and {1} are the `shell` and `shellArgs` settings keys']
}, "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.ShellLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageLinux
},
[TerminalSettingId.ShellMacOs]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageOsx
},
[TerminalSettingId.ShellWindows]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: ['string', 'null'],
default: null,
markdownDeprecationMessage: shellDeprecationMessageWindows
},
[TerminalSettingId.ShellArgsLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'array',
items: {
type: 'string'
},
default: [],
markdownDeprecationMessage: shellDeprecationMessageLinux
},
[TerminalSettingId.ShellArgsMacOs]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
type: 'array',
items: {
type: 'string'
},
// Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This
// is the reason terminals on macOS typically run login shells by default which set up
// the environment. See http://unix.stackexchange.com/a/119675/115410
default: ['-l'],
markdownDeprecationMessage: shellDeprecationMessageOsx
},
[TerminalSettingId.ShellArgsWindows]: {
restricted: true,
markdownDescription: localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."),
'anyOf': [
{
type: 'array',
items: {
type: 'string',
markdownDescription: localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).")
},
},
{
type: 'string',
markdownDescription: localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).")
}
],
default: [],
markdownDeprecationMessage: shellDeprecationMessageWindows
},
[TerminalSettingId.ProfilesWindows]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profiles.windows',
comment: ['{0}, {1}, and {2} are the `source`, `path` and optional `args` settings keys']
},
"The Windows profiles to present when creating a new terminal via the terminal dropdown. Set to null to exclude them, use the {0} property to use the default detected configuration. Or, set the {1} and optional {2}", '`source`', '`path`', '`args`.'
),
type: 'object',
default: {
'PowerShell': {
source: 'PowerShell',
icon: 'terminal-powershell'
},
'Command Prompt': {
path: [
'${env:windir}\\Sysnative\\cmd.exe',
'${env:windir}\\System32\\cmd.exe'
],
args: [],
icon: 'terminal-cmd'
},
'Git Bash': {
source: 'Git Bash'
}
},
additionalProperties: {
'anyOf': [
{
type: 'object',
required: ['source'],
properties: {
source: {
description: localize('terminalProfile.windowsSource', 'A profile source that will auto detect the paths to the shell.'),
enum: ['PowerShell', 'Git Bash']
},
overrideName: {
description: localize('terminalProfile.overrideName', 'Controls whether or not the profile name overrides the auto detected one.'),
type: 'boolean'
},
icon: {
description: localize('terminalProfile.icon', 'A codicon ID to associate with this terminal.'),
type: 'string'
},
env: {
markdownDescription: localize('terminalProfile.env', "An object with environment variables that will be added to the terminal profile process. Set to `null` to delete environment variables from the base environment."),
type: 'object',
additionalProperties: {
type: ['string', 'null']
},
default: {}
}
}
},
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.ProfilesMacOs]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profile.osx',
comment: ['{0} and {1} are the `path` and optional `args` settings keys']
},
"The macOS profiles to present when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0} and optional {1}", '`path`', '`args`.'
),
type: 'object',
default: {
'bash': {
path: 'bash',
icon: 'terminal-bash'
},
'zsh': {
path: 'zsh'
},
'fish': {
path: 'fish'
},
'tmux': {
path: 'tmux',
icon: 'terminal-tmux'
},
'pwsh': {
path: 'pwsh',
icon: 'terminal-powershell'
}
},
additionalProperties: {
'anyOf': [
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.ProfilesLinux]: {
restricted: true,
markdownDescription: localize(
{
key: 'terminal.integrated.profile.linux',
comment: ['{0} and {1} are the `path` and optional `args` settings keys']
},
"The Linux profiles to present when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0} and optional {1}", '`path`', '`args`.'
),
type: 'object',
default: {
'bash': {
path: 'bash'
},
'zsh': {
path: 'zsh'
},
'fish': {
path: 'fish'
},
'tmux': {
path: 'tmux',
icon: 'terminal-tmux'
},
'pwsh': {
path: 'pwsh',
icon: 'terminal-powershell'
}
},
additionalProperties: {
'anyOf': [
{ type: 'null' },
terminalProfileSchema
]
}
},
[TerminalSettingId.DefaultProfileLinux]: {
restricted: true,
markdownDescription: localize('terminal.integrated.defaultProfile.linux', "The default profile used on Linux. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.linux#`', '`#terminal.integrated.shellArgs.linux#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.DefaultProfileMacOs]: {
restricted: true,
description: localize('terminal.integrated.defaultProfile.osx', "The default profile used on macOS. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.osx#`', '`#terminal.integrated.shellArgs.osx#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.DefaultProfileWindows]: {
restricted: true,
description: localize('terminal.integrated.defaultProfile.windows', "The default profile used on Windows. This setting will currently be ignored if either {0} or {1} are set.", '`#terminal.integrated.shell.windows#`', '`#terminal.integrated.shellArgs.windows#`'),
type: ['string', 'null'],
default: null
},
[TerminalSettingId.UseWslProfiles]: {
description: localize('terminal.integrated.useWslProfiles', 'Controls whether or not WSL distros are shown in the terminal dropdown'),
type: 'boolean',
default: true
},
[TerminalSettingId.TabsEnabled]: {
description: localize('terminal.integrated.tabs.enabled', 'Controls whether terminal tabs display as a list to the side of the terminal. When this is disabled a dropdown will display instead.'),
type: 'boolean',
@ -393,7 +106,7 @@ export const terminalConfiguration: IConfigurationNode = {
[TerminalSettingId.FontSize]: {
description: localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."),
type: 'number',
default: EDITOR_FONT_DEFAULTS.fontSize
default: isMacintosh ? 12 : 14
},
[TerminalSettingId.LetterSpacing]: {
description: localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."),
@ -681,17 +394,10 @@ export const terminalConfiguration: IConfigurationNode = {
type: 'boolean',
default: true
},
[TerminalSettingId.AllowWorkspaceConfiguration]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.allowWorkspaceConfiguration', "Allows shell and profile settings to be pick up from a workspace."),
type: 'boolean',
default: false
},
[TerminalSettingId.InheritEnv]: {
scope: ConfigurationScope.APPLICATION,
description: localize('terminal.integrated.inheritEnv', "Whether new shells should inherit their environment from VS Code which may source a login shell to ensure $PATH and other development variables are initialized. This has no effect on Windows."),
type: 'boolean',
default: true
},
}
};
export function registerTerminalConfiguration() {
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration(terminalConfiguration);
}

View file

@ -9,9 +9,8 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
import { ILogService } from 'vs/platform/log/common/log';
import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalEnvironment, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { IProcessEnvironment, isWindows, locale, OperatingSystem, OS, platform, Platform } from 'vs/base/common/platform';
import { TerminalSettingId } from 'vs/workbench/contrib/terminal/common/terminal';
/**
* This module contains utility functions related to the environment, cwd and paths.

View file

@ -5,7 +5,9 @@
import { Emitter } from 'vs/base/common/event';
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 { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
@ -16,7 +18,9 @@ import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { LocalPty } from 'vs/workbench/contrib/terminal/electron-sandbox/localPty';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
export class LocalTerminalService extends Disposable implements ILocalTerminalService {
declare _serviceBrand: undefined;
@ -38,7 +42,9 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe
@ILocalPtyService private readonly _localPtyService: ILocalPtyService,
@ILabelService private readonly _labelService: ILabelService,
@INotificationService notificationService: INotificationService,
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService,
@IConfigurationResolverService configurationResolverService: IConfigurationResolverService,
@IHistoryService historyService: IHistoryService
) {
super();
@ -96,6 +102,17 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe
this._onPtyHostResponsive.fire();
}));
}
if (this._localPtyService.onPtyHostRequestResolveVariables) {
this._register(this._localPtyService.onPtyHostRequestResolveVariables(async e => {
const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file);
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
const resolveCalls: Promise<string>[] = e.originalText.map(t => {
return configurationResolverService.resolveAsync(lastActiveWorkspaceRoot, t);
});
const result = await Promise.all(resolveCalls);
this._localPtyService.acceptPtyHostResolvedVariables?.(e.id, result);
}));
}
}
async updateTitle(id: number, title: string): Promise<void> {
await this._localPtyService.updateTitle(id, title);
@ -137,6 +154,10 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe
return this._localPtyService.getDefaultSystemShell(osOverride);
}
async getProfiles(includeDetectedProfiles?: boolean) {
return this._localPtyService.getProfiles?.(includeDetectedProfiles) || [];
}
async getEnvironment(): Promise<IProcessEnvironment> {
return this._localPtyService.getEnvironment();
}

View file

@ -5,16 +5,16 @@
import { deepStrictEqual, fail, ok, strictEqual } from 'assert';
import { isWindows } from 'vs/base/common/platform';
import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
import { ITerminalConfiguration, ITerminalProfile, ITerminalProfiles, ProfileSource } from 'vs/workbench/contrib/terminal/common/terminal';
import { detectAvailableProfiles, IFsProvider } from 'vs/workbench/contrib/terminal/node/terminalProfiles';
import { ITerminalProfile, ProfileSource, SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
import { ITerminalConfiguration, ITerminalProfiles } from 'vs/workbench/contrib/terminal/common/terminal';
import { detectAvailableProfiles, IFsProvider } from 'vs/platform/terminal/node/terminalProfiles';
/**
* Assets that two profiles objects are equal, this will treat explicit undefined and unset
* properties the same. Order of the profiles is ignored.
*/
function profilesEqual(actualProfiles: ITerminalProfile[], expectedProfiles: ITerminalProfile[]) {
strictEqual(actualProfiles.length, expectedProfiles.length);
strictEqual(actualProfiles.length, expectedProfiles.length, `Actual: ${actualProfiles.map(e => e.profileName).join(',')}\nExpected: ${expectedProfiles.map(e => e.profileName).join(',')}`);
for (const expected of expectedProfiles) {
const actual = actualProfiles.find(e => e.profileName === expected.profileName);
ok(actual, `Expected profile ${expected.profileName} not found`);
@ -71,7 +71,7 @@ suite('Workbench - TerminalProfiles', () => {
const config: ITestTerminalConfig = {
profiles: {
windows: {
'PowerShell NoProfile': { source: ProfileSource.Pwsh, args: ['-NoProfile'], overrideName: true }
'PowerShell': { source: ProfileSource.Pwsh, args: ['-NoProfile'], overrideName: true }
},
linux: {},
osx: {},
@ -80,7 +80,7 @@ suite('Workbench - TerminalProfiles', () => {
};
const profiles = await detectAvailableProfiles(true, buildTestSafeConfigProvider(config), fsProvider, undefined, undefined, undefined);
const expected = [
{ profileName: 'PowerShell NoProfile', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', overrideName: true, args: ['-NoProfile'], isDefault: true }
{ profileName: 'PowerShell', path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', overrideName: true, args: ['-NoProfile'], isDefault: true }
];
profilesEqual(profiles, expected);
});

View file

@ -125,11 +125,11 @@ import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEd
import { IEnterWorkspaceResult, IRecent, IRecentlyOpened, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
import { ILocalTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { ILocalTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { isArray } from 'vs/base/common/types';
import { IShellLaunchConfigResolveOptions, ITerminalProfile, ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfigResolveOptions, ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal';
import { EditorOverrideService } from 'vs/workbench/services/editor/browser/editorOverrideService';
import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files';
import { IEditorOverrideService } from 'vs/workbench/services/editor/common/editorOverrideService';
@ -1597,6 +1597,7 @@ export class TestLocalTerminalService implements ILocalTerminalService {
async attachToProcess(id: number): Promise<ITerminalChildProcess | undefined> { throw new Error('Method not implemented.'); }
async listProcesses(): Promise<IProcessDetails[]> { throw new Error('Method not implemented.'); }
getDefaultSystemShell(osOverride?: OperatingSystem): Promise<string> { throw new Error('Method not implemented.'); }
getProfiles(includeDetectedProfiles?: boolean): Promise<ITerminalProfile[]> { throw new Error('Method not implemented.'); }
getEnvironment(): Promise<IProcessEnvironment> { throw new Error('Method not implemented.'); }
getShellEnvironment(): Promise<IProcessEnvironment | undefined> { throw new Error('Method not implemented.'); }
getWslPath(original: string): Promise<string> { throw new Error('Method not implemented.'); }