move mainThread[M-Q], #70319

This commit is contained in:
Johannes Rieken
2019-03-19 17:35:51 +01:00
parent 384773d4b3
commit 8237956caa
7 changed files with 21 additions and 21 deletions

View File

@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { Action, IAction } from 'vs/base/common/actions';
import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { dispose } from 'vs/base/common/lifecycle';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadMessageService)
export class MainThreadMessageService implements MainThreadMessageServiceShape {
constructor(
extHostContext: IExtHostContext,
@INotificationService private readonly _notificationService: INotificationService,
@ICommandService private readonly _commandService: ICommandService,
@IDialogService private readonly _dialogService: IDialogService
) {
//
}
dispose(): void {
//
}
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number | undefined> {
if (options.modal) {
return this._showModalMessage(severity, message, commands);
} else {
return this._showMessage(severity, message, commands, options.extension);
}
}
private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription | undefined): Promise<number> {
return new Promise<number>(resolve => {
const primaryActions: MessageItemAction[] = [];
class MessageItemAction extends Action {
constructor(id: string, label: string, handle: number) {
super(id, label, undefined, true, () => {
resolve(handle);
return Promise.resolve();
});
}
}
class ManageExtensionAction extends Action {
constructor(id: ExtensionIdentifier, label: string, commandService: ICommandService) {
super(id.value, label, undefined, true, () => {
return commandService.executeCommand('_extensions.manage', id.value);
});
}
}
commands.forEach(command => {
primaryActions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
});
let source: string | undefined;
if (extension) {
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
}
if (!source) {
source = nls.localize('defaultSource', "Extension");
}
const secondaryActions: IAction[] = [];
if (extension && !extension.isUnderDevelopment) {
secondaryActions.push(new ManageExtensionAction(extension.identifier, nls.localize('manageExtension', "Manage Extension"), this._commandService));
}
const messageHandle = this._notificationService.notify({
severity,
message,
actions: { primary: primaryActions, secondary: secondaryActions },
source
});
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
Event.once(messageHandle.onDidClose)(() => {
dispose(...primaryActions, ...secondaryActions);
resolve(undefined);
});
});
}
private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number | undefined> {
let cancelId: number | undefined = undefined;
const buttons = commands.map((command, index) => {
if (command.isCloseAffordance === true) {
cancelId = index;
}
return command.title;
});
if (cancelId === undefined) {
if (buttons.length > 0) {
buttons.push(nls.localize('cancel', "Cancel"));
} else {
buttons.push(nls.localize('ok', "OK"));
}
cancelId = buttons.length - 1;
}
return this._dialogService.show(severity, message, buttons, { cancelId })
.then(result => result === commands.length ? undefined : commands[result].handle);
}
}

View File

@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/contrib/output/common/output';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { UriComponents, URI } from 'vs/base/common/uri';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
@extHostNamedCustomer(MainContext.MainThreadOutputService)
export class MainThreadOutputService extends Disposable implements MainThreadOutputServiceShape {
private static _idPool = 1;
private readonly _proxy: ExtHostOutputServiceShape;
private readonly _outputService: IOutputService;
private readonly _layoutService: IWorkbenchLayoutService;
private readonly _panelService: IPanelService;
constructor(
extHostContext: IExtHostContext,
@IOutputService outputService: IOutputService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IPanelService panelService: IPanelService
) {
super();
this._outputService = outputService;
this._layoutService = layoutService;
this._panelService = panelService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostOutputService);
const setVisibleChannel = () => {
const panel = this._panelService.getActivePanel();
const visibleChannel: IOutputChannel | null = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null;
this._proxy.$setVisibleChannel(visibleChannel ? visibleChannel.id : null);
};
this._register(Event.any<any>(this._outputService.onActiveOutputChannel, this._panelService.onDidPanelOpen, this._panelService.onDidPanelClose)(() => setVisibleChannel()));
setVisibleChannel();
}
public $register(label: string, log: boolean, file?: UriComponents): Promise<string> {
const id = 'extension-output-#' + (MainThreadOutputService._idPool++);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : undefined, log });
this._register(toDisposable(() => this.$dispose(id)));
return Promise.resolve(id);
}
public $append(channelId: string, value: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.append(value);
}
return undefined;
}
public $update(channelId: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.update();
}
return undefined;
}
public $clear(channelId: string, till: number): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.clear(till);
}
return undefined;
}
public $reveal(channelId: string, preserveFocus: boolean): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
this._outputService.showChannel(channel.id, preserveFocus);
}
return undefined;
}
public $close(channelId: string): Promise<void> | undefined {
const panel = this._panelService.getActivePanel();
if (panel && panel.getId() === OUTPUT_PANEL_ID) {
const activeChannel = this._outputService.getActiveChannel();
if (activeChannel && channelId === activeChannel.id) {
this._layoutService.setPanelHidden(true);
}
}
return undefined;
}
public $dispose(channelId: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.dispose();
}
return undefined;
}
private _getChannel(channelId: string): IOutputChannel | null {
return this._outputService.getChannel(channelId);
}
}

View File

@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IProgress, IProgressService2, IProgressStep, IProgressOptions } from 'vs/platform/progress/common/progress';
import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadProgress)
export class MainThreadProgress implements MainThreadProgressShape {
private readonly _progressService: IProgressService2;
private _progress = new Map<number, { resolve: () => void, progress: IProgress<IProgressStep> }>();
private readonly _proxy: ExtHostProgressShape;
constructor(
extHostContext: IExtHostContext,
@IProgressService2 progressService: IProgressService2
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostProgress);
this._progressService = progressService;
}
dispose(): void {
this._progress.forEach(handle => handle.resolve());
this._progress.clear();
}
$startProgress(handle: number, options: IProgressOptions): void {
const task = this._createTask(handle);
this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle));
}
$progressReport(handle: number, message: IProgressStep): void {
const entry = this._progress.get(handle);
if (entry) {
entry.progress.report(message);
}
}
$progressEnd(handle: number): void {
const entry = this._progress.get(handle);
if (entry) {
entry.resolve();
this._progress.delete(handle);
}
}
private _createTask(handle: number) {
return (progress: IProgress<IProgressStep>) => {
return new Promise<any>(resolve => {
this._progress.set(handle, { resolve, progress });
});
};
}
}

View File

@@ -0,0 +1,212 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput } from 'vs/platform/quickinput/common/quickInput';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { CancellationToken } from 'vs/base/common/cancellation';
interface QuickInputSession {
input: IQuickInput;
handlesToItems: Map<number, TransferQuickPickItems>;
}
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private readonly _proxy: ExtHostQuickOpenShape;
private readonly _quickInputService: IQuickInputService;
private readonly _items: Record<number, {
resolve(items: TransferQuickPickItems[]): void;
reject(error: Error): void;
}> = {};
constructor(
extHostContext: IExtHostContext,
@IQuickInputService quickInputService: IQuickInputService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen);
this._quickInputService = quickInputService;
}
public dispose(): void {
}
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined> {
const contents = new Promise<TransferQuickPickItems[]>((resolve, reject) => {
this._items[instance] = { resolve, reject };
});
options = {
...options,
onDidFocus: el => {
if (el) {
this._proxy.$onItemSelected((<TransferQuickPickItems>el).handle);
}
}
};
if (options.canPickMany) {
return this._quickInputService.pick(contents, options as { canPickMany: true }, token).then(items => {
if (items) {
return items.map(item => item.handle);
}
return undefined;
});
} else {
return this._quickInputService.pick(contents, options, token).then(item => {
if (item) {
return item.handle;
}
return undefined;
});
}
}
$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void> {
if (this._items[instance]) {
this._items[instance].resolve(items);
delete this._items[instance];
}
return Promise.resolve();
}
$setError(instance: number, error: Error): Promise<void> {
if (this._items[instance]) {
this._items[instance].reject(error);
delete this._items[instance];
}
return Promise.resolve();
}
// ---- input
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string> {
const inputOptions: IInputOptions = Object.create(null);
if (options) {
inputOptions.password = options.password;
inputOptions.placeHolder = options.placeHolder;
inputOptions.valueSelection = options.valueSelection;
inputOptions.prompt = options.prompt;
inputOptions.value = options.value;
inputOptions.ignoreFocusLost = options.ignoreFocusOut;
}
if (validateInput) {
inputOptions.validateInput = (value) => {
return this._proxy.$validateInput(value);
};
}
return this._quickInputService.input(inputOptions, token);
}
// ---- QuickInput
private sessions = new Map<number, QuickInputSession>();
$createOrUpdate(params: TransferQuickInput): Promise<void> {
const sessionId = params.id;
let session = this.sessions.get(sessionId);
if (!session) {
if (params.type === 'quickPick') {
const input = this._quickInputService.createQuickPick();
input.onDidAccept(() => {
this._proxy.$onDidAccept(sessionId);
});
input.onDidChangeActive(items => {
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
});
input.onDidChangeSelection(items => {
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
});
input.onDidTriggerButton(button => {
this._proxy.$onDidTriggerButton(sessionId, (button as TransferQuickInputButton).handle);
});
input.onDidChangeValue(value => {
this._proxy.$onDidChangeValue(sessionId, value);
});
input.onDidHide(() => {
this._proxy.$onDidHide(sessionId);
});
session = {
input,
handlesToItems: new Map()
};
} else {
const input = this._quickInputService.createInputBox();
input.onDidAccept(() => {
this._proxy.$onDidAccept(sessionId);
});
input.onDidTriggerButton(button => {
this._proxy.$onDidTriggerButton(sessionId, (button as TransferQuickInputButton).handle);
});
input.onDidChangeValue(value => {
this._proxy.$onDidChangeValue(sessionId, value);
});
input.onDidHide(() => {
this._proxy.$onDidHide(sessionId);
});
session = {
input,
handlesToItems: new Map()
};
}
this.sessions.set(sessionId, session);
}
const { input, handlesToItems } = session;
for (const param in params) {
if (param === 'id' || param === 'type') {
continue;
}
if (param === 'visible') {
if (params.visible) {
input.show();
} else {
input.hide();
}
} else if (param === 'items') {
handlesToItems.clear();
params[param].forEach(item => {
handlesToItems.set(item.handle, item);
});
input[param] = params[param];
} else if (param === 'activeItems' || param === 'selectedItems') {
input[param] = params[param]
.filter(handle => handlesToItems.has(handle))
.map(handle => handlesToItems.get(handle));
} else if (param === 'buttons') {
input[param] = params.buttons!.map(button => {
if (button.handle === -1) {
return this._quickInputService.backButton;
}
const { iconPath, tooltip, handle } = button;
return {
iconPath: iconPath && {
dark: URI.revive(iconPath.dark),
light: iconPath.light && URI.revive(iconPath.light)
},
tooltip,
handle
};
});
} else {
input[param] = params[param];
}
}
return Promise.resolve(undefined);
}
$dispose(sessionId: number): Promise<void> {
const session = this.sessions.get(sessionId);
if (session) {
session.input.dispose();
this.sessions.delete(sessionId);
}
return Promise.resolve(undefined);
}
}