Remove in operator from contrib/terminal/browser

Part of #276071
This commit is contained in:
Daniel Imms
2025-11-07 11:06:40 -08:00
parent 4d5154f8be
commit 2a730d7365
20 changed files with 135 additions and 112 deletions

View File

@@ -338,24 +338,24 @@ export default tseslint.config(
'src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts',
'src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts',
'src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts',
'src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts',
'src/vs/workbench/contrib/terminal/browser/remotePty.ts',
'src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts',
'src/vs/workbench/contrib/terminal/browser/terminalActions.ts',
'src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts',
'src/vs/workbench/contrib/terminal/browser/terminalGroup.ts',
'src/vs/workbench/contrib/terminal/browser/terminalIcon.ts',
'src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts',
'src/vs/workbench/contrib/terminal/browser/terminalInstance.ts',
'src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts',
'src/vs/workbench/contrib/terminal/browser/terminalMenus.ts',
'src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts',
'src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts',
'src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts',
'src/vs/workbench/contrib/terminal/browser/terminalService.ts',
'src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts',
'src/vs/workbench/contrib/terminal/browser/terminalView.ts',
'src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts',
// 'src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts',
// 'src/vs/workbench/contrib/terminal/browser/remotePty.ts',
// 'src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalActions.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalGroup.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalIcon.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalInstance.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalMenus.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalProfileQuickpick.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalProfileService.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalService.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts',
// 'src/vs/workbench/contrib/terminal/browser/terminalView.ts',
// 'src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts',
'src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts',
'src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts',
'src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts',

View File

@@ -6,6 +6,7 @@
import { Emitter } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import { isObject } from '../../../../base/common/types.js';
import { localize } from '../../../../nls.js';
import { ICrossVersionSerializedTerminalState, IPtyHostController, ISerializedTerminalState, ITerminalLogService } from '../../../../platform/terminal/common/terminal.js';
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
@@ -105,20 +106,27 @@ export abstract class BaseTerminalBackend extends Disposable {
if (serializedState === undefined) {
return undefined;
}
const parsedUnknown = JSON.parse(serializedState);
if (!('version' in parsedUnknown) || !('state' in parsedUnknown) || !Array.isArray(parsedUnknown.state)) {
this._logService.warn('Could not revive serialized processes, wrong format', parsedUnknown);
const crossVersionState = JSON.parse(serializedState) as unknown;
if (!isCrossVersionSerializedTerminalState(crossVersionState)) {
this._logService.warn('Could not revive serialized processes, wrong format', crossVersionState);
return undefined;
}
const parsedCrossVersion = parsedUnknown as ICrossVersionSerializedTerminalState;
if (parsedCrossVersion.version !== 1) {
this._logService.warn(`Could not revive serialized processes, wrong version "${parsedCrossVersion.version}"`, parsedCrossVersion);
if (crossVersionState.version !== 1) {
this._logService.warn(`Could not revive serialized processes, wrong version "${crossVersionState.version}"`, crossVersionState);
return undefined;
}
return parsedCrossVersion.state as ISerializedTerminalState[];
return crossVersionState.state as ISerializedTerminalState[];
}
protected _getWorkspaceId(): string {
return this._workspaceContextService.getWorkspace().id;
}
}
function isCrossVersionSerializedTerminalState(obj: unknown): obj is ICrossVersionSerializedTerminalState {
return (
isObject(obj) &&
'version' in obj && typeof obj.version === 'number' &&
'state' in obj && Array.isArray(obj.state)
);
}

View File

@@ -8,6 +8,7 @@ import { ITerminalLaunchResult, IProcessPropertyMap, ITerminalChildProcess, ITer
import { BasePty } from '../common/basePty.js';
import { RemoteTerminalChannelClient } from '../common/remote/remoteTerminalChannel.js';
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
import { hasKey } from '../../../../base/common/types.js';
export class RemotePty extends BasePty implements ITerminalChildProcess {
private readonly _startBarrier: Barrier;
@@ -35,7 +36,7 @@ export class RemotePty extends BasePty implements ITerminalChildProcess {
const startResult = await this._remoteTerminalChannel.start(this.id);
if (startResult && 'message' in startResult) {
if (startResult && hasKey(startResult, { message: true })) {
// An error occurred
return startResult;
}

View File

@@ -349,7 +349,7 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack
this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE);
}
} catch (e: unknown) {
this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e);
this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', (<{ message?: string }>e).message ?? e);
}
}

View File

@@ -14,7 +14,7 @@ import { Schemas } from '../../../../base/common/network.js';
import { isAbsolute } from '../../../../base/common/path.js';
import { isWindows } from '../../../../base/common/platform.js';
import { dirname } from '../../../../base/common/resources.js';
import { isObject, isString } from '../../../../base/common/types.js';
import { hasKey, isObject, isString } from '../../../../base/common/types.js';
import { URI } from '../../../../base/common/uri.js';
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
import { ILanguageService } from '../../../../editor/common/languages/language.js';
@@ -316,7 +316,10 @@ export function registerTerminalActions() {
id: TerminalCommandId.CreateTerminalEditor,
title: localize2('workbench.action.terminal.createTerminalEditor', 'Create New Terminal in Editor Area'),
run: async (c, _, args) => {
const options = (isObject(args) && 'location' in args) ? args as ICreateTerminalOptions : { location: TerminalLocation.Editor };
function isCreateTerminalOptions(obj: unknown): obj is ICreateTerminalOptions {
return isObject(obj) && 'location' in obj;
}
const options = isCreateTerminalOptions(args) ? args : { location: TerminalLocation.Editor };
const instance = await c.service.createTerminal(options);
await instance.focusWhenReady();
}
@@ -998,7 +1001,7 @@ export function registerTerminalActions() {
}]
},
run: async (c, _, args) => {
const cwd = isObject(args) && 'cwd' in args ? toOptionalString(args.cwd) : undefined;
const cwd = args ? toOptionalString((<{ cwd?: string }>args).cwd) : undefined;
const instance = await c.service.createTerminal({ cwd });
if (!instance) {
return;
@@ -1032,7 +1035,7 @@ export function registerTerminalActions() {
f1: false,
run: async (activeInstance, c, accessor, args) => {
const notificationService = accessor.get(INotificationService);
const name = isObject(args) && 'name' in args ? toOptionalString(args.name) : undefined;
const name = args ? toOptionalString((<{ name?: string }>args).name) : undefined;
if (!name) {
notificationService.warn(localize('workbench.action.terminal.renameWithArg.noName', "No name argument provided"));
return;
@@ -1511,9 +1514,13 @@ export function validateTerminalName(name: string): { content: string; severity:
return null;
}
function isTerminalProfile(obj: unknown): obj is ITerminalProfile {
return isObject(obj) && 'profileName' in obj;
}
function convertOptionsOrProfileToOptions(optionsOrProfile?: ICreateTerminalOptions | ITerminalProfile): ICreateTerminalOptions | undefined {
if (isObject(optionsOrProfile) && 'profileName' in optionsOrProfile) {
return { config: optionsOrProfile as ITerminalProfile, location: (optionsOrProfile as ICreateTerminalOptions).location };
if (isTerminalProfile(optionsOrProfile)) {
return { config: optionsOrProfile, location: (optionsOrProfile as ICreateTerminalOptions).location };
}
return optionsOrProfile;
}
@@ -1574,13 +1581,16 @@ export function refreshTerminalActions(detectedProfiles: ITerminalProfile[]): ID
let instance: ITerminalInstance | undefined;
let cwd: string | URI | undefined;
if (isObject(eventOrOptionsOrProfile) && eventOrOptionsOrProfile && 'profileName' in eventOrOptionsOrProfile) {
if (isObject(eventOrOptionsOrProfile) && eventOrOptionsOrProfile && hasKey(eventOrOptionsOrProfile, { profileName: true })) {
const config = c.profileService.availableProfiles.find(profile => profile.profileName === eventOrOptionsOrProfile.profileName);
if (!config) {
throw new Error(`Could not find terminal profile "${eventOrOptionsOrProfile.profileName}"`);
}
options = { config };
if ('location' in eventOrOptionsOrProfile) {
function isSimpleArgs(obj: unknown): obj is { profileName: string; location?: 'view' | 'editor' | unknown } {
return isObject(obj) && 'location' in obj;
}
if (isSimpleArgs(eventOrOptionsOrProfile)) {
switch (eventOrOptionsOrProfile.location) {
case 'editor': options.location = TerminalLocation.Editor; break;
case 'view': options.location = TerminalLocation.Panel; break;

View File

@@ -3,10 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isObject } from '../../../../base/common/types.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { IEditorSerializer } from '../../../common/editor.js';
import { EditorInput } from '../../../common/editor/editorInput.js';
import { ISerializedTerminalEditorInput, ITerminalEditorService, ITerminalInstance } from './terminal.js';
import { ISerializedTerminalEditorInput, ITerminalEditorService, ITerminalInstance, type IDeserializedTerminalEditorInput } from './terminal.js';
import { TerminalEditorInput } from './terminalEditorInput.js';
export class TerminalInputSerializer implements IEditorSerializer {
@@ -26,8 +27,11 @@ export class TerminalInputSerializer implements IEditorSerializer {
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined {
const terminalInstance = JSON.parse(serializedEditorInput);
return this._terminalEditorService.reviveInput(terminalInstance);
const editorInput = JSON.parse(serializedEditorInput) as unknown;
if (!isDeserializedTerminalEditorInput(editorInput)) {
throw new Error(`Could not revive terminal editor input, ${editorInput}`);
}
return this._terminalEditorService.reviveInput(editorInput);
}
private _toJson(instance: ITerminalInstance): ISerializedTerminalEditorInput {
@@ -47,3 +51,7 @@ export class TerminalInputSerializer implements IEditorSerializer {
};
}
}
function isDeserializedTerminalEditorInput(obj: unknown): obj is IDeserializedTerminalEditorInput {
return isObject(obj) && 'id' in obj && 'pid' in obj;
}

View File

@@ -231,15 +231,11 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor
}
reviveInput(deserializedInput: IDeserializedTerminalEditorInput): EditorInput {
if ('pid' in deserializedInput) {
const newDeserializedInput = { ...deserializedInput, findRevivedId: true };
const instance = this._terminalInstanceService.createInstance({ attachPersistentProcess: newDeserializedInput }, TerminalLocation.Editor);
const input = this._instantiationService.createInstance(TerminalEditorInput, instance.resource, instance);
this._registerInstance(instance.resource.path, input, instance);
return input;
} else {
throw new Error(`Could not revive terminal editor input, ${deserializedInput}`);
}
const newDeserializedInput = { ...deserializedInput, findRevivedId: true };
const instance = this._terminalInstanceService.createInstance({ attachPersistentProcess: newDeserializedInput }, TerminalLocation.Editor);
const input = this._instantiationService.createInstance(TerminalEditorInput, instance.resource, instance);
this._registerInstance(instance.resource.path, input, instance);
return input;
}
detachInstance(instance: ITerminalInstance) {

View File

@@ -16,7 +16,7 @@ import { TerminalStatus } from './terminalStatusList.js';
import { getWindow } from '../../../../base/browser/dom.js';
import { getPartByLocation } from '../../../services/views/browser/viewsService.js';
import { asArray } from '../../../../base/common/arrays.js';
import type { SingleOrMany } from '../../../../base/common/types.js';
import { hasKey, type SingleOrMany } from '../../../../base/common/types.js';
const enum Constants {
/**
@@ -303,7 +303,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup {
// if a parent terminal is provided, find it
// otherwise, parent is the active terminal
const parentIndex = parentTerminalId ? this._terminalInstances.findIndex(t => t.instanceId === parentTerminalId) : this._activeInstanceIndex;
if ('instanceId' in shellLaunchConfigOrInstance) {
if (hasKey(shellLaunchConfigOrInstance, { instanceId: true })) {
instance = shellLaunchConfigOrInstance;
} else {
instance = this._terminalInstanceService.createInstance(shellLaunchConfigOrInstance, TerminalLocation.Panel);

View File

@@ -16,6 +16,7 @@ import { ITerminalProfileResolverService } from '../common/terminal.js';
import { ansiColorMap } from '../common/terminalColorRegistry.js';
import { createStyleSheet } from '../../../../base/browser/domStylesheets.js';
import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
import { isString } from '../../../../base/common/types.js';
export function getColorClass(colorKey: string): string;
@@ -108,9 +109,9 @@ export function getUriClasses(terminal: ITerminalInstance | IExtensionTerminalPr
}
}
if (icon instanceof URI) {
if (URI.isUri(icon)) {
uri = icon;
} else if (icon instanceof Object && 'light' in icon && 'dark' in icon) {
} else if (!ThemeIcon.isThemeIcon(icon) && !isString(icon)) {
uri = isDark(colorScheme) ? icon.dark : icon.light;
}
if (uri instanceof URI) {
@@ -123,8 +124,11 @@ export function getUriClasses(terminal: ITerminalInstance | IExtensionTerminalPr
}
export function getIconId(accessor: ServicesAccessor, terminal: ITerminalInstance | IExtensionTerminalProfile | ITerminalProfile): string {
if (!terminal.icon || (terminal.icon instanceof Object && !('id' in terminal.icon))) {
return accessor.get(ITerminalProfileResolverService).getDefaultIcon().id;
if (isString(terminal.icon)) {
return terminal.icon;
}
return typeof terminal.icon === 'string' ? terminal.icon : terminal.icon.id;
if (ThemeIcon.isThemeIcon(terminal.icon)) {
return terminal.icon.id;
}
return accessor.get(ITerminalProfileResolverService).getDefaultIcon().id;
}

View File

@@ -8,7 +8,7 @@ import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'
import { codiconsLibrary } from '../../../../base/common/codiconsLibrary.js';
import { Lazy } from '../../../../base/common/lazy.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import type { ThemeIcon } from '../../../../base/common/themables.js';
import { ThemeIcon } from '../../../../base/common/themables.js';
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';
@@ -23,7 +23,7 @@ const icons = new Lazy<IconContribution[]>(() => {
if (e.id === codiconsLibrary.blank.id) {
return false;
}
if (!('fontCharacter' in e.defaults)) {
if (ThemeIcon.isThemeIcon(e.defaults)) {
return false;
}
if (includedChars.has(e.defaults.fontCharacter)) {

View File

@@ -93,6 +93,7 @@ import type { IProgressState } from '@xterm/addon-progress';
import { refreshShellIntegrationInfoStatus } from './terminalTooltip.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import { PromptInputState } from '../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js';
import { hasKey } from '../../../../base/common/types.js';
const enum Constants {
/**
@@ -645,13 +646,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._register(this.onDisposed(() => {
contribution.dispose();
this._contributions.delete(desc.id);
// Just in case to prevent potential future memory leaks due to cyclic dependency.
if ('instance' in contribution) {
delete contribution.instance;
}
if ('_instance' in contribution) {
delete contribution._instance;
}
}));
}
}
@@ -1556,9 +1550,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
const originalIcon = this.shellLaunchConfig.icon;
await this._processManager.createProcess(this._shellLaunchConfig, this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows).then(result => {
if (result) {
if ('message' in result) {
if (hasKey(result, { message: true })) {
this._onProcessExit(result);
} else if ('injectedArgs' in result) {
} else if (hasKey(result, { injectedArgs: true })) {
this._injectedArgs = result.injectedArgs;
}
}
@@ -1832,9 +1826,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._shellLaunchConfig = shell; // Must be done before calling _createProcess()
await this._processManager.relaunch(this._shellLaunchConfig, this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows, reset).then(result => {
if (result) {
if ('message' in result) {
if (hasKey(result, { message: true })) {
this._onProcessExit(result);
} else if ('injectedArgs' in result) {
} else if (hasKey(result, { injectedArgs: true })) {
this._injectedArgs = result.injectedArgs;
}
}

View File

@@ -16,6 +16,7 @@ import { TerminalContextKeys } from '../common/terminalContextKey.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { promiseWithResolvers } from '../../../../base/common/async.js';
import { hasKey } from '../../../../base/common/types.js';
export class TerminalInstanceService extends Disposable implements ITerminalInstanceService {
declare _serviceBrand: undefined;
@@ -54,7 +55,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
convertProfileToShellLaunchConfig(shellLaunchConfigOrProfile?: IShellLaunchConfig | ITerminalProfile, cwd?: string | URI): IShellLaunchConfig {
// Profile was provided
if (shellLaunchConfigOrProfile && 'profileName' in shellLaunchConfigOrProfile) {
if (shellLaunchConfigOrProfile && hasKey(shellLaunchConfigOrProfile, { profileName: true })) {
const profile = shellLaunchConfigOrProfile;
if (!profile.path) {
return shellLaunchConfigOrProfile;

View File

@@ -19,6 +19,7 @@ import { terminalStrings } from '../common/terminalStrings.js';
import { ACTIVE_GROUP, AUX_WINDOW_GROUP, SIDE_GROUP } from '../../../services/editor/common/editorService.js';
import { DisposableStore } from '../../../../base/common/lifecycle.js';
import { HasSpeechProvider } from '../../speech/common/speechService.js';
import { hasKey } from '../../../../base/common/types.js';
export const enum TerminalContextMenuGroup {
Chat = '0_chat',
@@ -787,7 +788,7 @@ export function getTerminalActionBarArgs(location: ITerminalLocationOptions, pro
} {
const dropdownActions: IAction[] = [];
const submenuActions: IAction[] = [];
const splitLocation = (location === TerminalLocation.Editor || (typeof location === 'object' && 'viewColumn' in location && location.viewColumn === ACTIVE_GROUP)) ? { viewColumn: SIDE_GROUP } : { splitActiveTerminal: true };
const splitLocation = (location === TerminalLocation.Editor || (typeof location === 'object' && hasKey(location, { viewColumn: true }) && location.viewColumn === ACTIVE_GROUP)) ? { viewColumn: SIDE_GROUP } : { splitActiveTerminal: true };
if (location === TerminalLocation.Editor) {
location = { viewColumn: ACTIVE_GROUP };

View File

@@ -18,6 +18,7 @@ import { IPickerQuickAccessItem } from '../../../../platform/quickinput/browser/
import { getIconRegistry } from '../../../../platform/theme/common/iconRegistry.js';
import { basename } from '../../../../base/common/path.js';
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
import { hasKey } from '../../../../base/common/types.js';
type DefaultProfileName = string;
@@ -40,9 +41,7 @@ export class TerminalProfileQuickpick {
return;
}
if (type === 'setDefault') {
if ('command' in result.profile) {
return; // Should never happen
} else if ('id' in result.profile) {
if (hasKey(result.profile, { id: true })) {
// extension contributed profile
await this._configurationService.updateValue(defaultProfileKey, result.profile.title, ConfigurationTarget.USER);
return {
@@ -60,7 +59,7 @@ export class TerminalProfileQuickpick {
}
// Add the profile to settings if necessary
if ('isAutoDetected' in result.profile) {
if (hasKey(result.profile, { profileName: true })) {
const profilesConfig = await this._configurationService.getValue(profilesKey);
if (typeof profilesConfig === 'object') {
const newProfile: ITerminalProfileObject = {
@@ -76,7 +75,7 @@ export class TerminalProfileQuickpick {
// Set the default profile
await this._configurationService.updateValue(defaultProfileKey, result.profileName, ConfigurationTarget.USER);
} else if (type === 'createInstance') {
if ('id' in result.profile) {
if (hasKey(result.profile, { id: true })) {
return {
config: {
extensionIdentifier: result.profile.extensionIdentifier,
@@ -94,7 +93,7 @@ export class TerminalProfileQuickpick {
}
}
// for tests
return 'profileName' in result.profile ? result.profile.profileName : result.profile.title;
return hasKey(result.profile, { profileName: true }) ? result.profile.profileName : result.profile.title;
}
private async _createAndShow(type: 'setDefault' | 'createInstance'): Promise<IProfileQuickPickItem | undefined> {
@@ -110,10 +109,7 @@ export class TerminalProfileQuickpick {
if (!await this._isProfileSafe(context.item.profile)) {
return;
}
if ('command' in context.item.profile) {
return;
}
if ('id' in context.item.profile) {
if (hasKey(context.item.profile, { id: true })) {
return;
}
const configProfiles: { [key: string]: any } = this._configurationService.getValue(TerminalSettingPrefix.Profiles + platformKey);
@@ -223,8 +219,8 @@ export class TerminalProfileQuickpick {
}
private async _isProfileSafe(profile: ITerminalProfile | IExtensionTerminalProfile): Promise<boolean> {
const isUnsafePath = 'isUnsafePath' in profile && profile.isUnsafePath;
const requiresUnsafePath = 'requiresUnsafePath' in profile && profile.requiresUnsafePath;
const isUnsafePath = hasKey(profile, { profileName: true }) && profile.isUnsafePath;
const requiresUnsafePath = hasKey(profile, { profileName: true }) && profile.requiresUnsafePath;
if (!isUnsafePath && !requiresUnsafePath) {
return true;
}

View File

@@ -169,7 +169,7 @@ export abstract class BaseTerminalProfileResolverService extends Disposable impl
return this._context.getEnvironment(remoteAuthority);
}
private _getCustomIcon(icon?: unknown): TerminalIcon | undefined {
private _getCustomIcon(icon?: TerminalIcon): TerminalIcon | undefined {
if (!icon) {
return undefined;
}
@@ -182,11 +182,8 @@ export abstract class BaseTerminalProfileResolverService extends Disposable impl
if (URI.isUri(icon) || isUriComponents(icon)) {
return URI.revive(icon);
}
if (typeof icon === 'object' && 'light' in icon && 'dark' in icon) {
const castedIcon = (icon as { light: unknown; dark: unknown });
if ((URI.isUri(castedIcon.light) || isUriComponents(castedIcon.light)) && (URI.isUri(castedIcon.dark) || isUriComponents(castedIcon.dark))) {
return { light: URI.revive(castedIcon.light), dark: URI.revive(castedIcon.dark) };
}
if ((URI.isUri(icon.light) || isUriComponents(icon.light)) && (URI.isUri(icon.dark) || isUriComponents(icon.dark))) {
return { light: URI.revive(icon.light), dark: URI.revive(icon.dark) };
}
return undefined;
}

View File

@@ -23,6 +23,7 @@ import { ITerminalContributionService } from '../common/terminalExtensionPoints.
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js';
import { hasKey } from '../../../../base/common/types.js';
/*
* Links TerminalService with TerminalProfileResolverService
@@ -244,7 +245,7 @@ export class TerminalProfileService extends Disposable implements ITerminalProfi
async getContributedDefaultProfile(shellLaunchConfig: IShellLaunchConfig): Promise<IExtensionTerminalProfile | undefined> {
// prevents recursion with the MainThreadTerminalService call to create terminal
// and defers to the provided launch config when an executable is provided
if (shellLaunchConfig && !shellLaunchConfig.extHostTerminalId && !('executable' in shellLaunchConfig)) {
if (shellLaunchConfig && !shellLaunchConfig.extHostTerminalId && !hasKey(shellLaunchConfig, { executable: true })) {
const key = await this.getPlatformKey();
const defaultProfileName = this._configurationService.getValue(`${TerminalSettingPrefix.DefaultProfile}${key}`);
const contributedDefaultProfile = this.contributedProfiles.find(p => p.title === defaultProfileName);

View File

@@ -56,6 +56,7 @@ import { createInstanceCapabilityEventMultiplexer } from './terminalEvents.js';
import { isAuxiliaryWindow, mainWindow } from '../../../../base/browser/window.js';
import { GroupIdentifier } from '../../../common/editor.js';
import { getActiveWindow } from '../../../../base/browser/dom.js';
import { hasKey } from '../../../../base/common/types.js';
interface IBackgroundTerminal {
instance: ITerminalInstance;
@@ -251,14 +252,14 @@ export class TerminalService extends Disposable implements ITerminalService {
const defaultLocation = this._terminalConfigurationService.defaultLocation;
let instance;
if (result.config && 'id' in result?.config) {
if (result.config && hasKey(result.config, { id: true })) {
await this.createContributedTerminalProfile(result.config.extensionIdentifier, result.config.id, {
icon: result.config.options?.icon,
color: result.config.options?.color,
location: !!(keyMods?.alt && activeInstance) ? { splitActiveTerminal: true } : defaultLocation
});
return;
} else if (result.config && 'profileName' in result.config) {
} else if (result.config && hasKey(result.config, { profileName: true })) {
if (keyMods?.alt && activeInstance) {
// create split, only valid if there's an active instance
instance = await this.createTerminal({ location: { parentTerminal: activeInstance }, config: result.config, cwd });
@@ -951,9 +952,9 @@ export class TerminalService extends Disposable implements ITerminalService {
if (location === TerminalLocation.Editor) {
return this._terminalEditorService;
} else if (typeof location === 'object') {
if ('viewColumn' in location) {
if (hasKey(location, { viewColumn: true })) {
return this._terminalEditorService;
} else if ('parentTerminal' in location) {
} else if (hasKey(location, { parentTerminal: true })) {
return (await location.parentTerminal).target === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService;
}
} else {
@@ -968,7 +969,7 @@ export class TerminalService extends Disposable implements ITerminalService {
// local terminal in a remote workspace as profile won't be used in those cases and these
// terminals need to be launched before remote connections are established.
if (this._terminalProfileService.availableProfiles.length === 0) {
const isPtyTerminal = options?.config && 'customPtyImplementation' in options.config;
const isPtyTerminal = options?.config && hasKey(options.config, { customPtyImplementation: true });
const isLocalInRemoteTerminal = this._remoteAgentService.getConnection() && URI.isUri(options?.cwd) && options?.cwd.scheme === Schemas.vscodeFileResource;
if (!isPtyTerminal && !isLocalInRemoteTerminal) {
if (this._connectionState === TerminalConnectionState.Connecting) {
@@ -982,12 +983,14 @@ export class TerminalService extends Disposable implements ITerminalService {
}
const config = options?.config || this._terminalProfileService.getDefaultProfile();
const shellLaunchConfig = config && 'extensionIdentifier' in config ? {} : this._terminalInstanceService.convertProfileToShellLaunchConfig(config || {});
const shellLaunchConfig = config && hasKey(config, { extensionIdentifier: true }) ? {} : this._terminalInstanceService.convertProfileToShellLaunchConfig(config || {});
// Get the contributed profile if it was provided
const contributedProfile = options?.skipContributedProfileCheck ? undefined : await this._getContributedProfile(shellLaunchConfig, options);
const splitActiveTerminal = typeof options?.location === 'object' && 'splitActiveTerminal' in options.location ? options.location.splitActiveTerminal : typeof options?.location === 'object' ? 'parentTerminal' in options.location : false;
const splitActiveTerminal = typeof options?.location === 'object' && hasKey(options.location, { splitActiveTerminal: true })
? options.location.splitActiveTerminal
: typeof options?.location === 'object' ? hasKey(options.location, { parentTerminal: true }) : false;
await this._resolveCwd(shellLaunchConfig, splitActiveTerminal, options);
@@ -1000,7 +1003,7 @@ export class TerminalService extends Disposable implements ITerminalService {
if (splitActiveTerminal) {
location = resolvedLocation === TerminalLocation.Editor ? { viewColumn: SIDE_GROUP } : { splitActiveTerminal: true };
} else {
location = typeof options?.location === 'object' && 'viewColumn' in options.location ? options.location : resolvedLocation;
location = typeof options?.location === 'object' && hasKey(options.location, { viewColumn: true }) ? options.location : resolvedLocation;
}
await this.createContributedTerminalProfile(contributedProfile.extensionIdentifier, contributedProfile.id, {
icon: contributedProfile.icon,
@@ -1063,7 +1066,7 @@ export class TerminalService extends Disposable implements ITerminalService {
}
private async _getContributedProfile(shellLaunchConfig: IShellLaunchConfig, options?: ICreateTerminalOptions): Promise<IExtensionTerminalProfile | undefined> {
if (options?.config && 'extensionIdentifier' in options.config) {
if (options?.config && hasKey(options.config, { extensionIdentifier: true })) {
return options.config;
}
@@ -1100,7 +1103,7 @@ export class TerminalService extends Disposable implements ITerminalService {
shellLaunchConfig.cwd = options.cwd;
} else if (splitActiveTerminal && options?.location) {
let parent = this.activeInstance;
if (typeof options.location === 'object' && 'parentTerminal' in options.location) {
if (typeof options.location === 'object' && hasKey(options.location, { parentTerminal: true })) {
parent = await options.location.parentTerminal;
}
if (!parent) {
@@ -1152,13 +1155,13 @@ export class TerminalService extends Disposable implements ITerminalService {
async resolveLocation(location?: ITerminalLocationOptions): Promise<TerminalLocation | undefined> {
if (location && typeof location === 'object') {
if ('parentTerminal' in location) {
if (hasKey(location, { parentTerminal: true })) {
// since we don't set the target unless it's an editor terminal, this is necessary
const parentTerminal = await location.parentTerminal;
return !parentTerminal.target ? TerminalLocation.Panel : parentTerminal.target;
} else if ('viewColumn' in location) {
} else if (hasKey(location, { viewColumn: true })) {
return TerminalLocation.Editor;
} else if ('splitActiveTerminal' in location) {
} else if (hasKey(location, { splitActiveTerminal: true })) {
// since we don't set the target unless it's an editor terminal, this is necessary
return !this._activeInstance?.target ? TerminalLocation.Panel : this._activeInstance?.target;
}
@@ -1167,16 +1170,16 @@ export class TerminalService extends Disposable implements ITerminalService {
}
private async _getSplitParent(location?: ITerminalLocationOptions): Promise<ITerminalInstance | undefined> {
if (location && typeof location === 'object' && 'parentTerminal' in location) {
if (location && typeof location === 'object' && hasKey(location, { parentTerminal: true })) {
return location.parentTerminal;
} else if (location && typeof location === 'object' && 'splitActiveTerminal' in location) {
} else if (location && typeof location === 'object' && hasKey(location, { splitActiveTerminal: true })) {
return this.activeInstance;
}
return undefined;
}
private _getEditorOptions(location?: ITerminalLocationOptions): TerminalEditorLocation | undefined {
if (location && typeof location === 'object' && 'viewColumn' in location) {
if (location && typeof location === 'object' && hasKey(location, { viewColumn: true })) {
// Terminal-specific workaround to resolve the active group in auxiliary windows to
// override the locked editor behavior.
if (location.viewColumn === ACTIVE_GROUP && isAuxiliaryWindow(getActiveWindow())) {
@@ -1297,7 +1300,7 @@ class TerminalEditorStyle extends Themable {
let uri = undefined;
if (icon instanceof URI) {
uri = icon;
} else if (icon instanceof Object && 'light' in icon && 'dark' in icon) {
} else if (icon instanceof Object && hasKey(icon, { light: true, dark: true })) {
uri = isDark(colorTheme.type) ? icon.dark : icon.light;
}
const iconClasses = getUriClasses(instance, colorTheme.type);

View File

@@ -644,9 +644,7 @@ class TerminalTabsDragAndDrop extends Disposable implements IListDragAndDrop<ITe
return;
}
// Attach terminals type to event
const terminals: ITerminalInstance[] = (dndData as unknown[]).filter(e => (
isObject(e) && 'instanceId' in e
)) as ITerminalInstance[];
const terminals = (dndData as unknown[]).filter(isTerminalInstance);
if (terminals.length > 0) {
originalEvent.dataTransfer.setData(TerminalDataTransfers.Terminals, JSON.stringify(terminals.map(e => e.resource.toString())));
}
@@ -735,7 +733,7 @@ class TerminalTabsDragAndDrop extends Disposable implements IListDragAndDrop<ITe
sourceInstances = [];
for (const e of draggedElement) {
if ('instanceId' in e) {
if (isTerminalInstance(e)) {
sourceInstances.push(e as ITerminalInstance);
}
}
@@ -829,3 +827,7 @@ class TabDecorationsProvider extends Disposable implements IDecorationsProvider
};
}
}
function isTerminalInstance(obj: unknown): obj is ITerminalInstance {
return isObject(obj) && 'instanceId' in obj;
}

View File

@@ -52,6 +52,7 @@ import { InstanceContext, TerminalContextActionRunner } from './terminalContextM
import { MicrotaskDelay } from '../../../../base/common/symbols.js';
import { IStorageService } from '../../../../platform/storage/common/storage.js';
import { hasNativeContextMenu } from '../../../../platform/window/common/window.js';
import { hasKey } from '../../../../base/common/types.js';
export class TerminalViewPane extends ViewPane {
private _parentDomElement: HTMLElement | undefined;
@@ -624,7 +625,7 @@ class TerminalThemeIconStyle extends Themable {
let uri = undefined;
if (icon instanceof URI) {
uri = icon;
} else if (icon instanceof Object && 'light' in icon && 'dark' in icon) {
} else if (icon instanceof Object && hasKey(icon, { light: true, dark: true })) {
uri = isDark(colorTheme.type) ? icon.dark : icon.light;
}
const iconClasses = getUriClasses(instance, colorTheme.type);

View File

@@ -12,7 +12,7 @@ import { timeout } from '../../../../../base/common/async.js';
import { IThemeService } from '../../../../../platform/theme/common/themeService.js';
import { TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR } from '../../common/terminalColorRegistry.js';
import { getWindow } from '../../../../../base/browser/dom.js';
import { ICurrentPartialCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { ICurrentPartialCommand, isFullTerminalCommand } from '../../../../../platform/terminal/common/capabilities/commandDetection/terminalCommand.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { TerminalContribSettingId } from '../../terminalContribExports.js';
@@ -260,7 +260,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe
}
revealCommand(command: ITerminalCommand | ICurrentPartialCommand, position: ScrollPosition = ScrollPosition.Middle): void {
const marker = 'getOutput' in command ? command.marker : command.commandStartMarker;
const marker = isFullTerminalCommand(command) ? command.marker : command.commandStartMarker;
if (!this._terminal || !marker) {
return;
}