mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 12:19:20 +00:00
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user