Migrate users from shell -> profile settings (#124615)
This commit is contained in:
parent
d2a0bfb286
commit
9224159b00
|
@ -10,6 +10,13 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
|||
import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export const enum TerminalSettingPrefix {
|
||||
Shell = 'terminal.integrated.shell.',
|
||||
ShellArgs = 'terminal.integrated.shellArgs.',
|
||||
DefaultProfile = 'terminal.integrated.defaultProfile.',
|
||||
Profiles = 'terminal.integrated.profiles.'
|
||||
}
|
||||
|
||||
export const enum TerminalSettingId {
|
||||
ShellLinux = 'terminal.integrated.shell.linux',
|
||||
ShellMacOs = 'terminal.integrated.shell.osx',
|
||||
|
|
|
@ -18,7 +18,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { INotificationService, IPromptChoice, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -46,7 +46,7 @@ import { TypeAheadAddon } from 'vs/workbench/contrib/terminal/browser/terminalTy
|
|||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId, TitleEventSource, TerminalIcon } from 'vs/platform/terminal/common/terminal';
|
||||
import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType, TerminalSettingId, TitleEventSource, TerminalIcon, TerminalSettingPrefix } 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';
|
||||
|
@ -66,6 +66,8 @@ import { getColorClass } from 'vs/workbench/contrib/terminal/browser/terminalIco
|
|||
const SLOW_CANVAS_RENDER_THRESHOLD = 50;
|
||||
const NUMBER_OF_FRAMES_TO_MEASURE = 20;
|
||||
|
||||
const SHOULD_PROMPT_FOR_PROFILE_MIGRATION_KEY = 'terminals.integrated.profile-migration';
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
* The maximum amount of milliseconds to wait for a container before starting to create the
|
||||
|
@ -326,6 +328,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
window.clearTimeout(initialDataEventsTimeout);
|
||||
}
|
||||
}));
|
||||
this.showProfileMigrationNotification();
|
||||
}
|
||||
|
||||
private _getIcon(): TerminalIcon | undefined {
|
||||
|
@ -353,6 +356,44 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
this._register(disposable);
|
||||
}
|
||||
|
||||
async showProfileMigrationNotification(): Promise<void> {
|
||||
const platform = this._getPlatformKey();
|
||||
const shouldMigrateToProfile = (!!this._configurationService.getValue(TerminalSettingPrefix.Shell + platform) ||
|
||||
!!this._configurationService.getValue(TerminalSettingPrefix.ShellArgs + platform)) &&
|
||||
!!this._configurationService.getValue(TerminalSettingPrefix.DefaultProfile + platform);
|
||||
if (shouldMigrateToProfile && this._storageService.getBoolean(SHOULD_PROMPT_FOR_PROFILE_MIGRATION_KEY, StorageScope.WORKSPACE, true)) {
|
||||
this._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('terminalProfileMigration', "The terminal is using deprecated shell/shellArgs settings, do you want to migrate it to a profile?"),
|
||||
[
|
||||
{
|
||||
label: nls.localize('migrateToProfile', "Migrate"),
|
||||
run: async () => {
|
||||
const shell = this._configurationService.getValue(TerminalSettingPrefix.Shell + platform);
|
||||
const shellArgs = this._configurationService.getValue(TerminalSettingPrefix.ShellArgs + platform);
|
||||
const profile = await this._terminalProfileResolverService.createProfileFromShellAndShellArgs(shell, shellArgs);
|
||||
if (profile) {
|
||||
this._configurationService.updateValue(TerminalSettingPrefix.DefaultProfile + platform, profile.profileName);
|
||||
this._configurationService.updateValue(TerminalSettingPrefix.Shell + platform, null);
|
||||
this._configurationService.updateValue(TerminalSettingPrefix.ShellArgs + platform, null);
|
||||
this._logService.trace(`migrated from shell/shellArgs, ${shell} ${shellArgs} to profile ${JSON.stringify(profile)}`);
|
||||
} else {
|
||||
this._logService.trace('migration from shell/shellArgs to profile did not occur bc created profile was an exact match for existing one', shell, shellArgs);
|
||||
}
|
||||
}
|
||||
} as IPromptChoice,
|
||||
],
|
||||
{
|
||||
neverShowAgain: { id: SHOULD_PROMPT_FOR_PROFILE_MIGRATION_KEY, scope: NeverShowAgainScope.WORKSPACE }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _getPlatformKey(): string {
|
||||
return isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux');
|
||||
}
|
||||
|
||||
private _initDimensions(): void {
|
||||
// The terminal panel needs to have been created
|
||||
if (!this._container) {
|
||||
|
|
|
@ -410,6 +410,45 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async createProfileFromShellAndShellArgs(shell?: unknown, shellArgs?: unknown): Promise<ITerminalProfile | undefined> {
|
||||
const detectedProfile = this._terminalService.availableProfiles?.find(p => p.path === shell);
|
||||
const fallbackProfile = (await this.getDefaultProfile({
|
||||
remoteAuthority: this._remoteAgentService.getConnection()?.remoteAuthority,
|
||||
os: this._primaryBackendOs!
|
||||
}));
|
||||
const profile = detectedProfile || fallbackProfile;
|
||||
const args = this._isValidShellArgs(shellArgs, this._primaryBackendOs!) ? shellArgs : profile.args;
|
||||
const createdProfile = {
|
||||
profileName: profile.profileName,
|
||||
path: profile.path,
|
||||
args,
|
||||
isDefault: true
|
||||
};
|
||||
if (detectedProfile && detectedProfile.profileName === createdProfile.profileName && detectedProfile.path === createdProfile.path && this._argsMatch(detectedProfile.args, createdProfile.args)) {
|
||||
return undefined;
|
||||
}
|
||||
return createdProfile;
|
||||
}
|
||||
|
||||
private _argsMatch(args1: string | string[] | undefined, args2: string | string[] | undefined): boolean {
|
||||
if (!args1 && !args2) {
|
||||
return true;
|
||||
} else if (typeof args1 === 'string' && typeof args2 === 'string') {
|
||||
return args1 === args2;
|
||||
} else if (Array.isArray(args1) && Array.isArray(args2)) {
|
||||
if (args1.length !== args2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < args1.length; i++) {
|
||||
if (args1[i] !== args2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserTerminalProfileResolverService extends BaseTerminalProfileResolverService {
|
||||
|
|
|
@ -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, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
|
||||
import { ILocalTerminalService, IOffProcessTerminalService, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalProfileObject, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalSettingId, TerminalSettingPrefix } from 'vs/platform/terminal/common/terminal';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditableData, IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IRemoteTerminalService, ITerminalExternalLinkProvider, ITerminalInstance, ITerminalService, ITerminalGroup, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
|
@ -177,12 +177,8 @@ export class TerminalService implements ITerminalService {
|
|||
lifecycleService.onWillShutdown(e => this._onWillShutdown(e));
|
||||
|
||||
this._configurationService.onDidChangeConfiguration(async e => {
|
||||
if (e.affectsConfiguration(TerminalSettingId.ProfilesWindows) ||
|
||||
e.affectsConfiguration(TerminalSettingId.ProfilesMacOs) ||
|
||||
e.affectsConfiguration(TerminalSettingId.ProfilesLinux) ||
|
||||
e.affectsConfiguration(TerminalSettingId.DefaultProfileWindows) ||
|
||||
e.affectsConfiguration(TerminalSettingId.DefaultProfileMacOs) ||
|
||||
e.affectsConfiguration(TerminalSettingId.DefaultProfileLinux) ||
|
||||
if (e.affectsConfiguration(TerminalSettingPrefix.DefaultProfile + this._getPlatformKey()) ||
|
||||
e.affectsConfiguration(TerminalSettingPrefix.Profiles + this._getPlatformKey()) ||
|
||||
e.affectsConfiguration(TerminalSettingId.UseWslProfiles)) {
|
||||
this._refreshAvailableProfiles();
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ export interface ITerminalProfileResolverService {
|
|||
// TODO: Remove when workspace trust is enabled
|
||||
getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined;
|
||||
getSafeConfigValueFullKey(key: string): unknown | undefined;
|
||||
createProfileFromShellAndShellArgs(shell?: unknown, shellArgs?: unknown): Promise<ITerminalProfile | undefined>;
|
||||
}
|
||||
|
||||
export interface IShellLaunchConfigResolveOptions {
|
||||
|
|
|
@ -1591,6 +1591,7 @@ export class TestTerminalProfileResolverService implements ITerminalProfileResol
|
|||
async getEnvironment(): Promise<IProcessEnvironment> { return process.env; }
|
||||
getSafeConfigValue(key: string, os: OperatingSystem): unknown | undefined { return undefined; }
|
||||
getSafeConfigValueFullKey(key: string): unknown | undefined { return undefined; }
|
||||
createProfileFromShellAndShellArgs(shell?: unknown, shellArgs?: unknown): Promise<ITerminalProfile | undefined> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestLocalTerminalService implements ILocalTerminalService {
|
||||
|
|
Loading…
Reference in a new issue