Merge pull request #125849 from microsoft/tyriar/120369

Finalize terminal profile contributions
This commit is contained in:
Daniel Imms 2021-06-11 07:00:57 -07:00 committed by GitHub
commit ae1f358300
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 84 additions and 114 deletions

31
src/vs/vscode.d.ts vendored
View file

@ -5813,6 +5813,31 @@ declare module 'vscode' {
tooltip?: string;
}
/**
* Provides a terminal profile for the contributed terminal profile when launched via the UI or
* command.
*/
export interface TerminalProfileProvider {
/**
* Provide the terminal profile.
* @param token A cancellation token that indicates the result is no longer needed.
* @returns The terminal profile.
*/
provideTerminalProfile(token: CancellationToken): ProviderResult<TerminalProfile>;
}
/**
* A terminal profile defines how a terminal will be launched.
*/
export class TerminalProfile {
/**
* The options that the terminal will launch with.
*/
options: TerminalOptions | ExtensionTerminalOptions;
constructor(options: TerminalOptions | ExtensionTerminalOptions);
}
/**
* A file decoration represents metadata that can be rendered with a file.
*/
@ -8994,6 +9019,12 @@ declare module 'vscode' {
*/
export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable;
/**
* Registers a provider for a contributed terminal profile.
* @param id The ID of the contributed terminal profile.
* @param provider The terminal profile provider.
*/
export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable;
/**
* Register a file decoration provider.
*

View file

@ -892,27 +892,6 @@ declare module 'vscode' {
//#endregion
//#region Terminal profile provider https://github.com/microsoft/vscode/issues/120369
export namespace window {
/**
* Registers a provider for a contributed terminal profile.
* @param id The ID of the contributed terminal profile.
* @param provider The terminal profile provider.
*/
export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable;
}
export interface TerminalProfileProvider {
/**
* Provide terminal profile options for the requested terminal.
* @param token A cancellation token that indicates the result is no longer needed.
*/
provideProfileOptions(token: CancellationToken): ProviderResult<TerminalOptions | ExtensionTerminalOptions>;
}
//#endregion
// eslint-disable-next-line vscode-dts-region-comments
//#region @jrieken -> exclusive document filters

View file

@ -211,10 +211,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
this._terminalService.registerProcessSupport(isSupported);
}
public $registerProfileProvider(id: string): void {
public $registerProfileProvider(id: string, extensionIdentifier: string): void {
// Proxy profile provider requests through the extension host
this._profileProviders.set(id, this._terminalService.registerTerminalProfileProvider(id, {
createContributedTerminalProfile: async (isSplitTerminal) => this._proxy.$createContributedProfileTerminal(id, isSplitTerminal)
this._profileProviders.set(id, this._terminalService.registerTerminalProfileProvider(extensionIdentifier, id, {
createContributedTerminalProfile: async (isSplitTerminal) => {
return this._proxy.$createContributedProfileTerminal(id, isSplitTerminal);
}
}));
}

View file

@ -675,7 +675,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostTerminalService.registerLinkProvider(provider);
},
registerTerminalProfileProvider(id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable {
return extHostTerminalService.registerProfileProvider(id, provider);
return extHostTerminalService.registerProfileProvider(extension, id, provider);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
@ -1252,6 +1252,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskScope: extHostTypes.TaskScope,
TerminalProfile: extHostTypes.TerminalProfile,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
TextEdit: extHostTypes.TextEdit,
TextEditorCursorStyle: TextEditorCursorStyle,

View file

@ -486,7 +486,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$startLinkProvider(): void;
$stopLinkProvider(): void;
$registerProcessSupport(isSupported: boolean): void;
$registerProfileProvider(id: string): void;
$registerProfileProvider(id: string, extensionIdentifier: string): void;
$unregisterProfileProvider(id: string): void;
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void;

View file

@ -43,7 +43,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID
getDefaultShell(useAutomationShell: boolean): string;
getDefaultShellArgs(useAutomationShell: boolean): string[] | string;
registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable;
registerProfileProvider(id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable;
registerProfileProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable;
getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
}
@ -584,12 +584,12 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
});
}
public registerProfileProvider(id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable {
public registerProfileProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable {
if (this._profileProviders.has(id)) {
throw new Error(`Terminal profile provider "${id}" already registered`);
}
this._profileProviders.set(id, provider);
this._proxy.$registerProfileProvider(id);
this._proxy.$registerProfileProvider(id, extension.identifier.value);
return new VSCodeDisposable(() => {
this._profileProviders.delete(id);
this._proxy.$unregisterProfileProvider(id);
@ -598,18 +598,18 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public async $createContributedProfileTerminal(id: string, isSplitTerminal: boolean): Promise<void> {
const token = new CancellationTokenSource().token;
const options = await this._profileProviders.get(id)?.provideProfileOptions(token);
const profile = await this._profileProviders.get(id)?.provideTerminalProfile(token);
if (token.isCancellationRequested) {
return;
}
if (!options) {
if (!profile || !('options' in profile)) {
throw new Error(`No terminal profile options provided for id "${id}"`);
}
if ('pty' in options) {
this.createExtensionTerminal(options, { isSplitTerminal });
if ('pty' in profile.options) {
this.createExtensionTerminal(profile.options, { isSplitTerminal });
return;
}
this.createTerminalFromOptions(options, { isSplitTerminal });
this.createTerminalFromOptions(profile.options, { isSplitTerminal });
}
public async $provideLinks(terminalId: number, line: string): Promise<ITerminalLinkDto[]> {

View file

@ -1707,6 +1707,13 @@ export enum SourceControlInputBoxValidationType {
Information = 2
}
export class TerminalProfile implements vscode.TerminalProfile {
constructor(
public options: vscode.TerminalOptions | vscode.ExtensionTerminalOptions
) {
}
}
export enum TaskRevealKind {
Always = 1,

View file

@ -141,7 +141,7 @@ export interface ITerminalService {
*/
createTerminal(profile: ITerminalProfile): ITerminalInstance;
createContributedTerminalProfile(id: string, isSplitTerminal: boolean): Promise<void>;
createContributedTerminalProfile(extensionIdentifier: string, id: string, isSplitTerminal: boolean): Promise<void>;
/**
* Creates a raw terminal instance, this should not be used outside of the terminal part.
@ -201,7 +201,7 @@ export interface ITerminalService {
*/
registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable;
registerTerminalProfileProvider(id: string, profileProvider: ITerminalProfileProvider): IDisposable;
registerTerminalProfileProvider(extensionIdenfifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable;
showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise<ITerminalInstance | undefined>;

View file

@ -33,7 +33,6 @@ import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/searc
import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess';
import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TABS_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TABS_SINGULAR_SELECTION, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
@ -1741,8 +1740,6 @@ export function registerTerminalActions() {
}
async run(accessor: ServicesAccessor, item?: string) {
const terminalService = accessor.get(ITerminalService);
const terminalContributionService = accessor.get(ITerminalContributionService);
const commandService = accessor.get(ICommandService);
if (!item || !item.split) {
return Promise.resolve(null);
}
@ -1759,10 +1756,6 @@ export function registerTerminalActions() {
terminalService.setActiveGroupByIndex(Number(indexMatches[1]) - 1);
return terminalService.showPanel(true);
}
const customType = terminalContributionService.terminalTypes.find(t => t.title === item);
if (customType) {
return commandService.executeCommand(customType.command);
}
const quickSelectProfiles = terminalService.availableProfiles;

View file

@ -25,7 +25,7 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term
import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance';
import { TerminalGroup } from 'vs/workbench/contrib/terminal/browser/terminalGroup';
import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView';
import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, TERMINAL_VIEW_ID, KEYBINDING_CONTEXT_TERMINAL_COUNT, ITerminalTypeContribution, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE, KEYBINDING_CONTEXT_TERMINAL_GROUP_COUNT, ITerminalProfileContribution } from 'vs/workbench/contrib/terminal/common/terminal';
import { IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, TERMINAL_VIEW_ID, KEYBINDING_CONTEXT_TERMINAL_COUNT, KEYBINDING_CONTEXT_TERMINAL_TABS_MOUSE, KEYBINDING_CONTEXT_TERMINAL_GROUP_COUNT, ITerminalProfileContribution } from 'vs/workbench/contrib/terminal/common/terminal';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { ILifecycleService, ShutdownReason, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
@ -34,7 +34,6 @@ import { configureTerminalProfileIcon } from 'vs/workbench/contrib/terminal/brow
import { equals } from 'vs/base/common/objects';
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ILabelService } from 'vs/platform/label/common/label';
import { Schemas } from 'vs/base/common/network';
import { VirtualWorkspaceContext } from 'vs/workbench/browser/contextkeys';
@ -58,7 +57,7 @@ export class TerminalService implements ITerminalService {
private _findState: FindReplaceState;
private _activeGroupIndex: number;
private _activeInstanceIndex: number;
private readonly _profileProviders: Map<string, ITerminalProfileProvider> = new Map();
private readonly _profileProviders: Map</*ext id*/string, Map</*provider id*/string, ITerminalProfileProvider>> = new Map();
private _linkProviders: Set<ITerminalExternalLinkProvider> = new Set();
private _linkProviderDisposables: Map<ITerminalExternalLinkProvider, IDisposable[]> = new Map();
private _processSupportContextKey: IContextKey<boolean>;
@ -148,7 +147,6 @@ export class TerminalService implements ITerminalService {
@IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ITerminalContributionService private readonly _terminalContributionService: ITerminalContributionService,
@ICommandService private readonly _commandService: ICommandService,
@IExtensionService private readonly _extensionService: IExtensionService,
@INotificationService private readonly _notificationService: INotificationService,
@optional(ILocalTerminalService) localTerminalService: ILocalTerminalService
@ -813,8 +811,13 @@ export class TerminalService implements ITerminalService {
};
}
registerTerminalProfileProvider(id: string, profileProvider: ITerminalProfileProvider): IDisposable {
this._profileProviders.set(id, profileProvider);
registerTerminalProfileProvider(extensionIdenfifier: string, id: string, profileProvider: ITerminalProfileProvider): IDisposable {
let extMap = this._profileProviders.get(extensionIdenfifier);
if (!extMap) {
extMap = new Map();
this._profileProviders.set(extensionIdenfifier, extMap);
}
extMap.set(id, profileProvider);
return toDisposable(() => this._profileProviders.delete(id));
}
@ -955,9 +958,6 @@ export class TerminalService implements ITerminalService {
// Add contributed profiles
if (type === 'createInstance') {
if (this._terminalContributionService.terminalProfiles.length > 0 || this._terminalContributionService.terminalTypes.length > 0) {
quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.contributed', "contributed") });
}
for (const contributed of this._terminalContributionService.terminalProfiles) {
const icon = contributed.icon ? (iconRegistry.get(contributed.icon) || Codicon.terminal) : Codicon.terminal;
quickPickItems.push({
@ -965,17 +965,6 @@ export class TerminalService implements ITerminalService {
profile: contributed
});
}
// Add contributed types (legacy), these cannot be defaults
if (type === 'createInstance') {
for (const contributed of this._terminalContributionService.terminalTypes) {
const icon = contributed.icon ? (iconRegistry.get(contributed.icon) || Codicon.terminal) : Codicon.terminal;
quickPickItems.push({
label: `$(${icon.id}) ${contributed.title}`,
profile: contributed
});
}
}
}
if (autoDetectedProfiles.length > 0) {
@ -988,16 +977,11 @@ export class TerminalService implements ITerminalService {
return;
}
if (type === 'createInstance') {
// Legacy implementation - remove when js-debug adopts new
if ('command' in value.profile) {
return this._commandService.executeCommand(value.profile.command);
}
const activeInstance = this.getActiveInstance();
let instance;
if ('id' in value.profile) {
await this.createContributedTerminalProfile(value.profile.id, !!(keyMods?.alt && activeInstance));
await this.createContributedTerminalProfile(value.profile.extensionIdentifier, value.profile.id, !!(keyMods?.alt && activeInstance));
return;
} else {
if (keyMods?.alt && activeInstance) {
@ -1038,16 +1022,21 @@ export class TerminalService implements ITerminalService {
return undefined;
}
async createContributedTerminalProfile(id: string, isSplitTerminal: boolean): Promise<void> {
async createContributedTerminalProfile(extensionIdentifier: string, id: string, isSplitTerminal: boolean): Promise<void> {
await this._extensionService.activateByEvent(`onTerminalProfile:${id}`);
const profileProvider = this._profileProviders.get(id);
const extMap = this._profileProviders.get(extensionIdentifier);
const profileProvider = extMap?.get(id);
if (!profileProvider) {
this._notificationService.error(`No terminal profile provider registered for id "${id}"`);
return;
}
await profileProvider.createContributedTerminalProfile(isSplitTerminal);
this.setActiveInstanceByIndex(this._terminalInstances.length - 1);
await this.getActiveInstance()?.focusWhenReady();
try {
await profileProvider.createContributedTerminalProfile(isSplitTerminal);
this.setActiveInstanceByIndex(this._terminalInstances.length - 1);
await this.getActiveInstance()?.focusWhenReady();
} catch (e) {
this._notificationService.error(e.message);
}
}
private _createProfileQuickPickItem(profile: ITerminalProfile): IProfileQuickPickItem {
@ -1224,7 +1213,7 @@ export class TerminalService implements ITerminalService {
}
interface IProfileQuickPickItem extends IQuickPickItem {
profile: ITerminalProfile | ITerminalTypeContribution | ITerminalProfileContribution;
profile: ITerminalProfile | (ITerminalProfileContribution & { extensionIdentifier: string });
}
interface IInstanceLocation {

View file

@ -226,13 +226,9 @@ export class TerminalViewPane extends ViewPane {
}
}
for (const contributed of this._terminalContributionService.terminalTypes) {
dropdownActions.push(new MenuItemAction({ id: contributed.command, title: contributed.title, category: TerminalTabContextMenuGroup.Profile }, undefined, undefined, this._contextKeyService, this._commandService));
}
for (const contributed of this._terminalContributionService.terminalProfiles) {
dropdownActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title, undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.id, false)));
submenuActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title, undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.id, true)));
dropdownActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title, undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.extensionIdentifier, contributed.id, false)));
submenuActions.push(new Action(TerminalCommandId.NewWithProfile, contributed.title, undefined, true, () => this._terminalService.createContributedTerminalProfile(contributed.extensionIdentifier, contributed.id, true)));
}
if (dropdownActions.length > 0) {

View file

@ -573,17 +573,9 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [
];
export interface ITerminalContributions {
/** @deprecated */
types?: ITerminalTypeContribution[];
profiles?: ITerminalProfileContribution[];
}
export interface ITerminalTypeContribution {
title: string;
command: string;
icon?: string;
}
export interface ITerminalProfileContribution {
title: string;
id: string;

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ITerminalTypeContribution, ITerminalContributions, terminalContributionsDescriptor, ITerminalProfileContribution } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalContributions, terminalContributionsDescriptor, ITerminalProfileContribution } from 'vs/workbench/contrib/terminal/common/terminal';
import { flatten } from 'vs/base/common/arrays';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@ -14,8 +14,7 @@ export const terminalsExtPoint = extensionsRegistry.ExtensionsRegistry.registerE
export interface ITerminalContributionService {
readonly _serviceBrand: undefined;
readonly terminalTypes: ReadonlyArray<ITerminalTypeContribution>;
readonly terminalProfiles: ReadonlyArray<ITerminalProfileContribution>;
readonly terminalProfiles: ReadonlyArray<ITerminalProfileContribution & { extensionIdentifier: string }>;
}
export const ITerminalContributionService = createDecorator<ITerminalContributionService>('terminalContributionsService');
@ -23,31 +22,12 @@ export const ITerminalContributionService = createDecorator<ITerminalContributio
export class TerminalContributionService implements ITerminalContributionService {
declare _serviceBrand: undefined;
private _terminalTypes: ReadonlyArray<ITerminalTypeContribution> = [];
get terminalTypes() { return this._terminalTypes; }
private _terminalProfiles: ReadonlyArray<ITerminalProfileContribution> = [];
private _terminalProfiles: ReadonlyArray<ITerminalProfileContribution & { extensionIdentifier: string }> = [];
get terminalProfiles() { return this._terminalProfiles; }
constructor() {
terminalsExtPoint.setHandler(contributions => {
this._terminalTypes = flatten(contributions.filter(c => c.description.enableProposedApi).map(c => {
return c.value?.types?.map(e => {
// TODO: Remove after adoption in js-debug
if (!e.icon && c.description.identifier.value === 'ms-vscode.js-debug') {
e.icon = '$(debug)';
}
// Only support $(id) for now, without that it should point to a path to be
// consistent with other icon APIs
if (e.icon && e.icon.startsWith('$(') && e.icon.endsWith(')')) {
e.icon = e.icon.substr(2, e.icon.length - 3);
} else {
e.icon = undefined;
}
return e;
}) || [];
}));
this._terminalProfiles = flatten(contributions.filter(c => c.description.enableProposedApi).map(c => {
this._terminalProfiles = flatten(contributions.map(c => {
return c.value?.profiles?.map(e => {
// Only support $(id) for now, without that it should point to a path to be
// consistent with other icon APIs
@ -56,7 +36,7 @@ export class TerminalContributionService implements ITerminalContributionService
} else {
e.icon = undefined;
}
return e;
return { ...e, extensionIdentifier: c.description.identifier.value };
}) || [];
}));
});