mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-24 01:25:37 -05:00
Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)
* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f * fix various icon issues * fix preview features
This commit is contained in:
@@ -6,9 +6,10 @@
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
|
||||
import { IRemoteConsoleLog, log } from 'vs/base/common/console';
|
||||
import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil';
|
||||
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadConsole)
|
||||
@@ -20,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
|
||||
) {
|
||||
const devOpts = parseExtensionDevOptions(this._environmentService);
|
||||
@@ -40,7 +41,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
|
||||
|
||||
// Log on main side if running tests from cli
|
||||
if (this._isExtensionDevTestFromCli) {
|
||||
this._windowsService.log(entry.severity, parse(entry).args);
|
||||
logRemoteEntry(this._logService, entry);
|
||||
}
|
||||
|
||||
// Broadcast to other windows if we are in development mode
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadKeytar)
|
||||
export class MainThreadKeytar implements MainThreadKeytarShape {
|
||||
|
||||
@@ -422,6 +422,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution()));
|
||||
}
|
||||
});
|
||||
this._taskService.setJsonTasksSupported(Promise.resolve(this._proxy.$jsonTasksSupported()));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
@@ -8,39 +8,42 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
|
||||
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
|
||||
|
||||
/**
|
||||
* Bi-directional map between webview handles and inputs.
|
||||
*/
|
||||
class WebviewHandleStore {
|
||||
private readonly _handlesToInputs = new Map<string, WebviewEditorInput>();
|
||||
private readonly _inputsToHandles = new Map<WebviewEditorInput, string>();
|
||||
private readonly _handlesToInputs = new Map<string, WebviewInput>();
|
||||
private readonly _inputsToHandles = new Map<WebviewInput, string>();
|
||||
|
||||
public add(handle: string, input: WebviewEditorInput): void {
|
||||
public add(handle: string, input: WebviewInput): void {
|
||||
this._handlesToInputs.set(handle, input);
|
||||
this._inputsToHandles.set(input, handle);
|
||||
}
|
||||
|
||||
public getHandleForInput(input: WebviewEditorInput): string | undefined {
|
||||
public getHandleForInput(input: WebviewInput): string | undefined {
|
||||
return this._inputsToHandles.get(input);
|
||||
}
|
||||
|
||||
public getInputForHandle(handle: string): WebviewEditorInput | undefined {
|
||||
public getInputForHandle(handle: string): WebviewInput | undefined {
|
||||
return this._handlesToInputs.get(handle);
|
||||
}
|
||||
|
||||
@@ -68,8 +71,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
'vscode-insider',
|
||||
]);
|
||||
|
||||
private static revivalPool = 0;
|
||||
|
||||
private readonly _proxy: ExtHostWebviewsShape;
|
||||
private readonly _webviewEditorInputs = new WebviewHandleStore();
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
@@ -94,8 +95,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
// This reviver's only job is to activate webview panel extensions
|
||||
// This should trigger the real reviver to be registered from the extension host side.
|
||||
this._register(_webviewEditorService.registerResolver({
|
||||
canResolve: (webview: WebviewEditorInput) => {
|
||||
if (!webview.webview.state && webview.getTypeId() === WebviewEditorInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case
|
||||
canResolve: (webview: WebviewInput) => {
|
||||
if (!webview.webview.state && webview.getTypeId() === WebviewInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -206,7 +207,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return;
|
||||
}
|
||||
|
||||
const handle = `revival-${MainThreadWebviews.revivalPool++}`;
|
||||
const handle = generateUuid();
|
||||
this._webviewEditorInputs.add(handle, webviewEditorInput);
|
||||
this.hookupWebviewEventDelegate(handle, webviewEditorInput);
|
||||
|
||||
@@ -246,13 +247,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
|
||||
this._editorProviders.set(viewType, this._webviewEditorService.registerResolver({
|
||||
canResolve: (webviewEditorInput) => {
|
||||
return webviewEditorInput.getTypeId() !== WebviewEditorInput.typeId && webviewEditorInput.viewType === viewType;
|
||||
return webviewEditorInput.getTypeId() !== WebviewInput.typeId && webviewEditorInput.viewType === viewType;
|
||||
},
|
||||
resolveWebview: async (webview) => {
|
||||
const handle = `resolved-${MainThreadWebviews.revivalPool++}`;
|
||||
const handle = generateUuid();
|
||||
this._webviewEditorInputs.add(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview);
|
||||
|
||||
if (webview instanceof CustomFileEditorInput) {
|
||||
webview.onWillSave(e => {
|
||||
e.waitUntil(this._proxy.$save(handle));
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxy.$resolveWebviewEditor(
|
||||
webview.getResource(),
|
||||
@@ -292,7 +299,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return viewType.replace(/^mainThreadWebview-/, '');
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewEditorInput) {
|
||||
private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewInput) {
|
||||
input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri));
|
||||
input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message));
|
||||
input.onDispose(() => {
|
||||
@@ -317,21 +324,31 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
|
||||
const activeInput = this._editorService.activeControl && this._editorService.activeControl.input;
|
||||
const viewStates: WebviewPanelViewStateData = {};
|
||||
|
||||
const updateViewStatesForInput = (group: IEditorGroup, topLevelInput: IEditorInput, editorInput: IEditorInput) => {
|
||||
if (!(editorInput instanceof WebviewInput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
editorInput.updateGroup(group.id);
|
||||
|
||||
const handle = this._webviewEditorInputs.getHandleForInput(editorInput);
|
||||
if (handle) {
|
||||
viewStates[handle] = {
|
||||
visible: topLevelInput.matches(group.activeEditor),
|
||||
active: topLevelInput.matches(activeInput),
|
||||
position: editorGroupToViewColumn(this._editorGroupService, group.id),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
for (const group of this._editorGroupService.groups) {
|
||||
for (const input of group.editors) {
|
||||
if (!(input instanceof WebviewEditorInput)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
input.updateGroup(group.id);
|
||||
|
||||
const handle = this._webviewEditorInputs.getHandleForInput(input);
|
||||
if (handle) {
|
||||
viewStates[handle] = {
|
||||
visible: input === group.activeEditor,
|
||||
active: input === activeInput,
|
||||
position: editorGroupToViewColumn(this._editorGroupService, group.id),
|
||||
};
|
||||
if (input instanceof DiffEditorInput) {
|
||||
updateViewStatesForInput(group, input, input.master);
|
||||
updateViewStatesForInput(group, input, input.details);
|
||||
} else {
|
||||
updateViewStatesForInput(group, input, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +365,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
}
|
||||
}
|
||||
|
||||
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
|
||||
private isSupportedLink(webview: WebviewInput, link: URI): boolean {
|
||||
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
|
||||
return true;
|
||||
}
|
||||
@@ -358,7 +375,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command';
|
||||
}
|
||||
|
||||
private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput {
|
||||
private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput {
|
||||
const webview = this.tryGetWebviewEditorInput(handle);
|
||||
if (!webview) {
|
||||
throw new Error('Unknown webview handle:' + handle);
|
||||
@@ -366,7 +383,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
return webview;
|
||||
}
|
||||
|
||||
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined {
|
||||
private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput | undefined {
|
||||
return this._webviewEditorInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,14 @@ import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isNative } from 'vs/base/common/platform';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
|
||||
@@ -25,6 +23,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@@ -44,10 +43,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
// The workspace file is provided be a unknown file system provider. It might come
|
||||
// from the extension host. So initialize now knowing that `rootPath` is undefined.
|
||||
if (workspace.configuration && !isNative && !fileService.canHandleResource(workspace.configuration)) {
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace));
|
||||
} else {
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
}
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
}
|
||||
@@ -214,19 +221,3 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
return this._windowService.resolveProxy(url);
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) {
|
||||
const workspaceEditingService = accessor.get(IWorkspaceEditingService);
|
||||
const extensionService = accessor.get(IExtensionService);
|
||||
const windowService = accessor.get(IWindowService);
|
||||
|
||||
if (disableExtensions && disableExtensions.length) {
|
||||
const runningExtensions = await extensionService.getExtensions();
|
||||
// If requested extension to disable is running, then reload window with given workspace
|
||||
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => ExtensionIdentifier.equals(runningExtension.identifier, id)))) {
|
||||
return windowService.openWindow([{ workspaceUri: workspace }], { args: { _: [], 'disable-extension': disableExtensions } });
|
||||
}
|
||||
}
|
||||
|
||||
return workspaceEditingService.enterWorkspace(workspace);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
@@ -252,6 +252,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
get remoteName() {
|
||||
return getRemoteName(initData.remote.authority);
|
||||
},
|
||||
get uiKind() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return initData.uiKind;
|
||||
}
|
||||
};
|
||||
if (!initData.environment.extensionTestsLocationURI) {
|
||||
@@ -905,6 +909,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CallHierarchyItem: extHostTypes.CallHierarchyItem,
|
||||
Decoration: extHostTypes.Decoration,
|
||||
WebviewEditorState: extHostTypes.WebviewEditorState,
|
||||
UIKind: UIKind
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ export interface IInitData {
|
||||
logsLocation: URI;
|
||||
autoStart: boolean;
|
||||
remote: { isRemote: boolean; authority: string | undefined; };
|
||||
uiKind: UIKind;
|
||||
}
|
||||
|
||||
export interface IConfigurationInitData extends IConfigurationData {
|
||||
@@ -109,6 +110,11 @@ export interface IExtHostContext extends IRPCProtocol {
|
||||
export interface IMainContext extends IRPCProtocol {
|
||||
}
|
||||
|
||||
export enum UIKind {
|
||||
Desktop = 1,
|
||||
Web = 2
|
||||
}
|
||||
|
||||
// --- main thread
|
||||
|
||||
export interface MainThreadClipboardShape extends IDisposable {
|
||||
@@ -571,6 +577,7 @@ export interface ExtHostWebviewsShape {
|
||||
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$save(handle: WebviewPanelHandle): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -1191,6 +1198,7 @@ export interface ExtHostTaskShape {
|
||||
$OnDidEndTask(execution: tasks.TaskExecutionDTO): void;
|
||||
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>;
|
||||
$getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined }>;
|
||||
$jsonTasksSupported(): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface IBreakpointDto {
|
||||
|
||||
@@ -3,12 +3,27 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import { TaskSystemInfoDTO } from '../common/shared/tasks';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
|
||||
export interface IExtHostTask extends ExtHostTaskShape {
|
||||
|
||||
@@ -21,10 +36,703 @@ export interface IExtHostTask extends ExtHostTaskShape {
|
||||
onDidEndTaskProcess: Event<vscode.TaskProcessEndEvent>;
|
||||
|
||||
registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable;
|
||||
registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
|
||||
registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void;
|
||||
fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]>;
|
||||
executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution>;
|
||||
terminateTask(execution: vscode.TaskExecution): Promise<void>;
|
||||
}
|
||||
|
||||
export namespace TaskDefinitionDTO {
|
||||
export function from(value: vscode.TaskDefinition): tasks.TaskDefinitionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.TaskDefinitionDTO): vscode.TaskDefinition | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: vscode.TaskPresentationOptions): tasks.TaskPresentationOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: vscode.ProcessExecutionOptions): tasks.ProcessExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ProcessExecutionDTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ProcessExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as tasks.ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ProcessExecution): tasks.ProcessExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: tasks.ProcessExecutionDTO = {
|
||||
process: value.process,
|
||||
args: value.args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: tasks.ProcessExecutionDTO): types.ProcessExecution | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return new types.ProcessExecution(value.process, value.args, value.options);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: vscode.ShellExecutionOptions): tasks.ShellExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: tasks.ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ShellExecutionDTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ShellExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as tasks.ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ShellExecution): tasks.ShellExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: tasks.ShellExecutionDTO = {
|
||||
};
|
||||
if (value.commandLine !== undefined) {
|
||||
result.commandLine = value.commandLine;
|
||||
} else {
|
||||
result.command = value.command;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: tasks.ShellExecutionDTO): types.ShellExecution | undefined {
|
||||
if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
if (value.commandLine) {
|
||||
return new types.ShellExecution(value.commandLine, value.options);
|
||||
} else {
|
||||
return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CustomExecution2DTO {
|
||||
export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.CustomExecution2DTO {
|
||||
if (value) {
|
||||
let candidate = value as tasks.CustomExecution2DTO;
|
||||
return candidate && candidate.customExecution === 'customExecution2';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function from(value: vscode.CustomExecution2): tasks.CustomExecution2DTO {
|
||||
return {
|
||||
customExecution: 'customExecution2'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TaskHandleDTO {
|
||||
export function from(value: types.Task): tasks.TaskHandleDTO {
|
||||
let folder: UriComponents | undefined;
|
||||
if (value.scope !== undefined && typeof value.scope !== 'number') {
|
||||
folder = value.scope.uri;
|
||||
}
|
||||
return {
|
||||
id: value._id!,
|
||||
workspaceFolder: folder!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskDTO {
|
||||
export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): tasks.TaskDTO[] {
|
||||
if (tasks === undefined || tasks === null) {
|
||||
return [];
|
||||
}
|
||||
const result: tasks.TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
const converted = from(task, extension);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function from(value: vscode.Task, extension: IExtensionDescription): tasks.TaskDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined;
|
||||
if (value.execution instanceof types.ProcessExecution) {
|
||||
execution = ProcessExecutionDTO.from(value.execution);
|
||||
} else if (value.execution instanceof types.ShellExecution) {
|
||||
execution = ShellExecutionDTO.from(value.execution);
|
||||
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
|
||||
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
|
||||
}
|
||||
|
||||
const definition: tasks.TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
|
||||
let scope: number | UriComponents;
|
||||
if (value.scope) {
|
||||
if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = value.scope.uri;
|
||||
}
|
||||
} else {
|
||||
// To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here:
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
|
||||
const result: tasks.TaskDTO = {
|
||||
_id: (value as types.Task)._id!,
|
||||
definition,
|
||||
name: value.name,
|
||||
source: {
|
||||
extensionId: extension.identifier.value,
|
||||
label: value.source,
|
||||
scope: scope
|
||||
},
|
||||
execution: execution!,
|
||||
isBackground: value.isBackground,
|
||||
group: group,
|
||||
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
|
||||
problemMatchers: value.problemMatchers,
|
||||
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
|
||||
runOptions: (<vscode.Task>value).runOptions ? (<vscode.Task>value).runOptions : { reevaluateOnRerun: true },
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: types.ShellExecution | types.ProcessExecution | undefined;
|
||||
if (ProcessExecutionDTO.is(value.execution)) {
|
||||
execution = ProcessExecutionDTO.to(value.execution);
|
||||
} else if (ShellExecutionDTO.is(value.execution)) {
|
||||
execution = ShellExecutionDTO.to(value.execution);
|
||||
}
|
||||
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
|
||||
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
if (value.source) {
|
||||
if (value.source.scope !== undefined) {
|
||||
if (typeof value.source.scope === 'number') {
|
||||
scope = value.source.scope;
|
||||
} else {
|
||||
scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
|
||||
}
|
||||
} else {
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers);
|
||||
if (value.isBackground !== undefined) {
|
||||
result.isBackground = value.isBackground;
|
||||
}
|
||||
if (value.group !== undefined) {
|
||||
result.group = types.TaskGroup.from(value.group);
|
||||
}
|
||||
if (value.presentationOptions) {
|
||||
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!;
|
||||
}
|
||||
if (value._id) {
|
||||
result._id = value._id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskFilterDTO {
|
||||
export function from(value: vscode.TaskFilter | undefined): tasks.TaskFilterDTO | undefined {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function to(value: tasks.TaskFilterDTO): vscode.TaskFilter | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
|
||||
constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
}
|
||||
|
||||
public get task(): vscode.Task {
|
||||
return this._task;
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this._tasks.terminateTask(this);
|
||||
}
|
||||
|
||||
public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void {
|
||||
}
|
||||
|
||||
public fireDidEndProcess(value: tasks.TaskProcessEndedDTO): void {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TaskExecutionDTO {
|
||||
export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
|
||||
const task = await TaskDTO.to(value.task, workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be created.');
|
||||
}
|
||||
return new TaskExecutionImpl(tasks, value.id, task);
|
||||
}
|
||||
export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO {
|
||||
return {
|
||||
id: (value as TaskExecutionImpl)._id,
|
||||
task: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface HandlerData {
|
||||
type: string;
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
export abstract class ExtHostTaskBase implements ExtHostTaskShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
protected readonly _proxy: MainThreadTaskShape;
|
||||
protected readonly _workspaceProvider: IExtHostWorkspaceProvider;
|
||||
protected readonly _editorService: IExtHostDocumentsAndEditors;
|
||||
protected readonly _configurationService: IExtHostConfiguration;
|
||||
protected readonly _terminalService: IExtHostTerminalService;
|
||||
protected _handleCounter: number;
|
||||
protected _handlers: Map<number, HandlerData>;
|
||||
protected _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
protected _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _notProvidedCustomExecutions: Set<string>; // Used for custom executions tasks that are created and run through executeTask.
|
||||
protected _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _lastStartedTask: string | undefined;
|
||||
protected readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
|
||||
protected readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
|
||||
|
||||
protected readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
|
||||
protected readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceProvider = workspaceService;
|
||||
this._editorService = editorService;
|
||||
this._configurationService = configurationService;
|
||||
this._terminalService = extHostTerminalService;
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
this._notProvidedCustomExecutions = new Set<string>();
|
||||
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
const handle = this.nextHandle();
|
||||
this._handlers.set(handle, { type, provider, extension });
|
||||
this._proxy.$registerTaskProvider(handle, type);
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._proxy.$unregisterTaskProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void {
|
||||
this._proxy.$registerTaskSystem(scheme, info);
|
||||
}
|
||||
|
||||
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
|
||||
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
|
||||
const result: vscode.Task[] = [];
|
||||
for (let value of values) {
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider);
|
||||
if (task) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public abstract async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution>;
|
||||
|
||||
public get taskExecutions(): vscode.TaskExecution[] {
|
||||
const result: vscode.TaskExecution[] = [];
|
||||
this._taskExecutions.forEach(value => result.push(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
public terminateTask(execution: vscode.TaskExecution): Promise<void> {
|
||||
if (!(execution instanceof TaskExecutionImpl)) {
|
||||
throw new Error('No valid task execution provided');
|
||||
}
|
||||
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
|
||||
}
|
||||
|
||||
public get onDidStartTask(): Event<vscode.TaskStartEvent> {
|
||||
return this._onDidExecuteTask.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise<void> {
|
||||
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
|
||||
if (execution2) {
|
||||
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
|
||||
throw new Error('We should not be trying to start the same custom task executions twice.');
|
||||
}
|
||||
|
||||
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
|
||||
this._activeCustomExecutions2.set(execution.id, execution2);
|
||||
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
|
||||
}
|
||||
this._lastStartedTask = execution.id;
|
||||
|
||||
this._onDidExecuteTask.fire({
|
||||
execution: await this.getTaskExecution(execution)
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidEndTask(): Event<vscode.TaskEndEvent> {
|
||||
return this._onDidTerminateTask.event;
|
||||
}
|
||||
|
||||
public async $OnDidEndTask(execution: tasks.TaskExecutionDTO): Promise<void> {
|
||||
const _execution = await this.getTaskExecution(execution);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.customExecutionComplete(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
execution: _execution
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidStartTaskProcess(): Event<vscode.TaskProcessStartEvent> {
|
||||
return this._onDidTaskProcessStarted.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessStarted.fire({
|
||||
execution: execution,
|
||||
processId: value.processId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
|
||||
return this._onDidTaskProcessEnded.event;
|
||||
}
|
||||
|
||||
public async $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessEnded.fire({
|
||||
execution: execution,
|
||||
exitCode: value.exitCode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription };
|
||||
|
||||
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<tasks.TaskSetDTO> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
// Set up a list of task ID promises that we can wait on
|
||||
// before returning the provided tasks. The ensures that
|
||||
// our task IDs are calculated for any custom execution tasks.
|
||||
// Knowing this ID ahead of time is needed because when a task
|
||||
// start event is fired this is when the custom execution is called.
|
||||
// The task start event is also the first time we see the ID from the main
|
||||
// thread, which is too late for us because we need to save an map
|
||||
// from an ID to the custom execution function. (Kind of a cart before the horse problem).
|
||||
const taskIdPromises: Promise<void>[] = [];
|
||||
const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
return this.provideTasksInternal(validTypes, taskIdPromises, handler, value);
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetchPromise.then((result) => {
|
||||
Promise.all(taskIdPromises).then(() => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined>;
|
||||
|
||||
public async $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
if (taskDTO.definition.type !== handler.type) {
|
||||
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
|
||||
}
|
||||
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None);
|
||||
if (!resolvedTask) {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
const resolvedTaskDTO: tasks.TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
|
||||
if (!resolvedTaskDTO) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
if (resolvedTask.definition !== task.definition) {
|
||||
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
|
||||
}
|
||||
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask, true);
|
||||
}
|
||||
|
||||
return await this.resolveTaskInternal(resolvedTaskDTO);
|
||||
}
|
||||
|
||||
public abstract async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>;
|
||||
|
||||
public abstract $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>;
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
|
||||
protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise<void> {
|
||||
const taskId = await this._proxy.$createTaskId(taskDTO);
|
||||
if (!isProvided && !this._providedCustomExecutions2.has(taskId)) {
|
||||
this._notProvidedCustomExecutions.add(taskId);
|
||||
}
|
||||
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
|
||||
}
|
||||
|
||||
protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
|
||||
if (typeof execution === 'string') {
|
||||
const taskExecution = this._taskExecutions.get(execution);
|
||||
if (!taskExecution) {
|
||||
throw new Error('Unexpected: The specified task is missing an execution');
|
||||
}
|
||||
return taskExecution;
|
||||
}
|
||||
|
||||
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
throw new Error('Unexpected: Task does not exist.');
|
||||
}
|
||||
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
|
||||
this._taskExecutions.set(execution.id, createdResult);
|
||||
return createdResult;
|
||||
}
|
||||
|
||||
private customExecutionComplete(execution: tasks.TaskExecutionDTO): void {
|
||||
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
|
||||
if (extensionCallback2) {
|
||||
this._activeCustomExecutions2.delete(execution.id);
|
||||
}
|
||||
|
||||
// Technically we don't really need to do this, however, if an extension
|
||||
// is executing a task through "executeTask" over and over again
|
||||
// with different properties in the task definition, then the map of executions
|
||||
// could grow indefinitely, something we don't want.
|
||||
if (this._notProvidedCustomExecutions.has(execution.id) && (this._lastStartedTask !== execution.id)) {
|
||||
this._providedCustomExecutions2.delete(execution.id);
|
||||
this._notProvidedCustomExecutions.delete(execution.id);
|
||||
}
|
||||
let iterator = this._notProvidedCustomExecutions.values();
|
||||
let iteratorResult = iterator.next();
|
||||
while (!iteratorResult.done) {
|
||||
if (!this._activeCustomExecutions2.has(iteratorResult.value) && (this._lastStartedTask !== iteratorResult.value)) {
|
||||
this._providedCustomExecutions2.delete(iteratorResult.value);
|
||||
this._notProvidedCustomExecutions.delete(iteratorResult.value);
|
||||
}
|
||||
iteratorResult = iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract async $jsonTasksSupported(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
authority: initData.remote.authority,
|
||||
platform: Platform.PlatformToString(Platform.Platform.Web)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
const dto = TaskDTO.from(task, extension);
|
||||
if (dto === undefined) {
|
||||
return Promise.reject(new Error('Task is not valid'));
|
||||
}
|
||||
|
||||
// If this task is a custom execution, then we need to save it away
|
||||
// in the provided custom execution map that is cleaned up after the
|
||||
// task is executed.
|
||||
if (CustomExecution2DTO.is(dto.execution)) {
|
||||
await this.addCustomExecution2(dto, <vscode.Task2>task, false);
|
||||
} else {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
|
||||
}
|
||||
|
||||
protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO && CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
taskDTOs.push(taskDTO);
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task, true));
|
||||
} else {
|
||||
console.warn('Only custom execution tasks supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
}
|
||||
|
||||
protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
return resolvedTaskDTO;
|
||||
} else {
|
||||
console.warn('Only custom execution tasks supported.');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
const result = {
|
||||
process: <unknown>undefined as string,
|
||||
variables: Object.create(null)
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async $jsonTasksSupported(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostTask = createDecorator<IExtHostTask>('IExtHostTask');
|
||||
|
||||
@@ -41,8 +41,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
public get cspSource(): string {
|
||||
return this._initData.webviewCspSource
|
||||
.replace('{{uuid}}', this._handle)
|
||||
.replace('{{commit}}', this._initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44');
|
||||
.replace('{{uuid}}', this._handle);
|
||||
}
|
||||
|
||||
public get html(): string {
|
||||
@@ -224,6 +223,18 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor {
|
||||
this._proxy.$setState(this._handle, typeConverters.WebviewEditorState.from(newState));
|
||||
}
|
||||
|
||||
private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable<boolean>) => void }>();
|
||||
public readonly onWillSave = this._onWillSave.event;
|
||||
|
||||
async _save(): Promise<boolean> {
|
||||
const waitingOn: Thenable<boolean>[] = [];
|
||||
this._onWillSave.fire({
|
||||
waitUntil: (thenable: Thenable<boolean>): void => { waitingOn.push(thenable); },
|
||||
});
|
||||
const result = await Promise.all(waitingOn);
|
||||
return result.every(x => x);
|
||||
}
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
@@ -422,6 +433,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel));
|
||||
}
|
||||
|
||||
async $save(handle: WebviewPanelHandle): Promise<boolean> {
|
||||
const panel = this.getWebviewPanel(handle);
|
||||
if (panel) {
|
||||
return panel._save();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function convertWebviewOptions(
|
||||
|
||||
@@ -7,7 +7,6 @@ import { URI } from 'vs/base/common/uri';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface WebviewInitData {
|
||||
readonly commit?: string;
|
||||
readonly webviewResourceRoot: string;
|
||||
readonly webviewCspSource: string;
|
||||
}
|
||||
@@ -18,7 +17,6 @@ export function asWebviewUri(
|
||||
resource: vscode.Uri,
|
||||
): vscode.Uri {
|
||||
const uri = initData.webviewResourceRoot
|
||||
.replace('{{commit}}', initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44')
|
||||
.replace('{{resource}}', resource.toString().replace(/^\S+?:/, ''))
|
||||
.replace('{{uuid}}', uuid);
|
||||
return URI.parse(uri);
|
||||
|
||||
@@ -6,375 +6,23 @@
|
||||
import * as path from 'vs/base/common/path';
|
||||
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { win32 } from 'vs/base/node/processes';
|
||||
|
||||
|
||||
import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
|
||||
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
|
||||
ShellExecutionOptionsDTO, ShellExecutionDTO,
|
||||
CustomExecution2DTO,
|
||||
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
|
||||
} from '../common/shared/tasks';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecution2DTO, HandlerData } from 'vs/workbench/api/common/extHostTask';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: ProcessExecutionDTO = {
|
||||
process: value.process,
|
||||
args: value.args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ProcessExecutionDTO): types.ProcessExecution | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return new types.ProcessExecution(value.process, value.args, value.options);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO {
|
||||
if (value) {
|
||||
const candidate = value as ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function from(value: vscode.ShellExecution): ShellExecutionDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: ShellExecutionDTO = {
|
||||
};
|
||||
if (value.commandLine !== undefined) {
|
||||
result.commandLine = value.commandLine;
|
||||
} else {
|
||||
result.command = value.command;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionDTO): types.ShellExecution | undefined {
|
||||
if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
if (value.commandLine) {
|
||||
return new types.ShellExecution(value.commandLine, value.options);
|
||||
} else {
|
||||
return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace CustomExecution2DTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO {
|
||||
if (value) {
|
||||
let candidate = value as CustomExecution2DTO;
|
||||
return candidate && candidate.customExecution === 'customExecution2';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function from(value: vscode.CustomExecution2): CustomExecution2DTO {
|
||||
return {
|
||||
customExecution: 'customExecution2'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskHandleDTO {
|
||||
export function from(value: types.Task): TaskHandleDTO {
|
||||
let folder: UriComponents | undefined;
|
||||
if (value.scope !== undefined && typeof value.scope !== 'number') {
|
||||
folder = value.scope.uri;
|
||||
}
|
||||
return {
|
||||
id: value._id!,
|
||||
workspaceFolder: folder!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDTO {
|
||||
|
||||
export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): TaskDTO[] {
|
||||
if (tasks === undefined || tasks === null) {
|
||||
return [];
|
||||
}
|
||||
const result: TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
const converted = from(task, extension);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined;
|
||||
if (value.execution instanceof types.ProcessExecution) {
|
||||
execution = ProcessExecutionDTO.from(value.execution);
|
||||
} else if (value.execution instanceof types.ShellExecution) {
|
||||
execution = ShellExecutionDTO.from(value.execution);
|
||||
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution2) {
|
||||
execution = CustomExecution2DTO.from(<types.CustomExecution2>(<vscode.Task2>value).execution2);
|
||||
}
|
||||
|
||||
const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
|
||||
let scope: number | UriComponents;
|
||||
if (value.scope) {
|
||||
if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = value.scope.uri;
|
||||
}
|
||||
} else {
|
||||
// To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here:
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
|
||||
const result: TaskDTO = {
|
||||
_id: (value as types.Task)._id!,
|
||||
definition,
|
||||
name: value.name,
|
||||
source: {
|
||||
extensionId: extension.identifier.value,
|
||||
label: value.source,
|
||||
scope: scope
|
||||
},
|
||||
execution: execution!,
|
||||
isBackground: value.isBackground,
|
||||
group: group,
|
||||
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
|
||||
problemMatchers: value.problemMatchers,
|
||||
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
|
||||
runOptions: (<vscode.Task>value).runOptions ? (<vscode.Task>value).runOptions : { reevaluateOnRerun: true },
|
||||
};
|
||||
return result;
|
||||
}
|
||||
export async function to(value: TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let execution: types.ShellExecution | types.ProcessExecution | undefined;
|
||||
if (ProcessExecutionDTO.is(value.execution)) {
|
||||
execution = ProcessExecutionDTO.to(value.execution);
|
||||
} else if (ShellExecutionDTO.is(value.execution)) {
|
||||
execution = ShellExecutionDTO.to(value.execution);
|
||||
}
|
||||
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
|
||||
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
|
||||
if (value.source) {
|
||||
if (value.source.scope !== undefined) {
|
||||
if (typeof value.source.scope === 'number') {
|
||||
scope = value.source.scope;
|
||||
} else {
|
||||
scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
|
||||
}
|
||||
} else {
|
||||
scope = types.TaskScope.Workspace;
|
||||
}
|
||||
}
|
||||
if (!definition || !scope) {
|
||||
return undefined;
|
||||
}
|
||||
const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers);
|
||||
if (value.isBackground !== undefined) {
|
||||
result.isBackground = value.isBackground;
|
||||
}
|
||||
if (value.group !== undefined) {
|
||||
result.group = types.TaskGroup.from(value.group);
|
||||
}
|
||||
if (value.presentationOptions) {
|
||||
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!;
|
||||
}
|
||||
if (value._id) {
|
||||
result._id = value._id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskFilterDTO {
|
||||
export function from(value: vscode.TaskFilter | undefined): TaskFilterDTO | undefined {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function to(value: TaskFilterDTO): vscode.TaskFilter | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskExecutionImpl implements vscode.TaskExecution {
|
||||
|
||||
constructor(private readonly _tasks: ExtHostTask, readonly _id: string, private readonly _task: vscode.Task) {
|
||||
}
|
||||
|
||||
public get task(): vscode.Task {
|
||||
return this._task;
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
this._tasks.terminateTask(this);
|
||||
}
|
||||
|
||||
public fireDidStartProcess(value: TaskProcessStartedDTO): void {
|
||||
}
|
||||
|
||||
public fireDidEndProcess(value: TaskProcessEndedDTO): void {
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskExecutionDTO {
|
||||
export async function to(value: TaskExecutionDTO, tasks: ExtHostTask, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
|
||||
const task = await TaskDTO.to(value.task, workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be created.');
|
||||
}
|
||||
return new TaskExecutionImpl(tasks, value.id, task);
|
||||
}
|
||||
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
|
||||
return {
|
||||
id: (value as TaskExecutionImpl)._id,
|
||||
task: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface HandlerData {
|
||||
type: string;
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
export class ExtHostTask implements ExtHostTaskShape {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _proxy: MainThreadTaskShape;
|
||||
private readonly _workspaceProvider: IExtHostWorkspaceProvider;
|
||||
private readonly _editorService: IExtHostDocumentsAndEditors;
|
||||
private readonly _configurationService: IExtHostConfiguration;
|
||||
private readonly _terminalService: IExtHostTerminalService;
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, HandlerData>;
|
||||
private _taskExecutions: Map<string, TaskExecutionImpl>;
|
||||
private _providedCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
private _activeCustomExecutions2: Map<string, vscode.CustomExecution2>;
|
||||
|
||||
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
|
||||
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
|
||||
|
||||
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
|
||||
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -384,17 +32,7 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask);
|
||||
this._workspaceProvider = workspaceService;
|
||||
this._editorService = editorService;
|
||||
this._configurationService = configurationService;
|
||||
this._terminalService = extHostTerminalService;
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
this._taskExecutions = new Map<string, TaskExecutionImpl>();
|
||||
this._providedCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
this._activeCustomExecutions2 = new Map<string, vscode.CustomExecution2>();
|
||||
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
@@ -404,36 +42,6 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
}
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
const handle = this.nextHandle();
|
||||
this._handlers.set(handle, { type, provider, extension });
|
||||
this._proxy.$registerTaskProvider(handle, type);
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._proxy.$unregisterTaskProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void {
|
||||
this._proxy.$registerTaskSystem(scheme, info);
|
||||
}
|
||||
|
||||
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
|
||||
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
|
||||
const result: vscode.Task[] = [];
|
||||
for (let value of values) {
|
||||
const task = await TaskDTO.to(value, this._workspaceProvider);
|
||||
if (task) {
|
||||
result.push(task);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
const tTask = (task as types.Task);
|
||||
// We have a preserved ID. So the task didn't change.
|
||||
@@ -449,175 +57,42 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
// in the provided custom execution map that is cleaned up after the
|
||||
// task is executed.
|
||||
if (CustomExecution2DTO.is(dto.execution)) {
|
||||
await this.addCustomExecution2(dto, <vscode.Task2>task);
|
||||
await this.addCustomExecution2(dto, <vscode.Task2>task, false);
|
||||
}
|
||||
|
||||
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
|
||||
}
|
||||
}
|
||||
|
||||
public get taskExecutions(): vscode.TaskExecution[] {
|
||||
const result: vscode.TaskExecution[] = [];
|
||||
this._taskExecutions.forEach(value => result.push(value));
|
||||
return result;
|
||||
}
|
||||
protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } {
|
||||
const taskDTOs: tasks.TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
public terminateTask(execution: vscode.TaskExecution): Promise<void> {
|
||||
if (!(execution instanceof TaskExecutionImpl)) {
|
||||
throw new Error('No valid task execution provided');
|
||||
}
|
||||
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
|
||||
}
|
||||
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO) {
|
||||
taskDTOs.push(taskDTO);
|
||||
|
||||
public get onDidStartTask(): Event<vscode.TaskStartEvent> {
|
||||
return this._onDidExecuteTask.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise<void> {
|
||||
const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id);
|
||||
if (execution2) {
|
||||
if (this._activeCustomExecutions2.get(execution.id) !== undefined) {
|
||||
throw new Error('We should not be trying to start the same custom task executions twice.');
|
||||
}
|
||||
|
||||
// Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task.
|
||||
this._activeCustomExecutions2.set(execution.id, execution2);
|
||||
this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback());
|
||||
}
|
||||
|
||||
this._onDidExecuteTask.fire({
|
||||
execution: await this.getTaskExecution(execution)
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidEndTask(): Event<vscode.TaskEndEvent> {
|
||||
return this._onDidTerminateTask.event;
|
||||
}
|
||||
|
||||
public async $OnDidEndTask(execution: TaskExecutionDTO): Promise<void> {
|
||||
const _execution = await this.getTaskExecution(execution);
|
||||
this._taskExecutions.delete(execution.id);
|
||||
this.customExecutionComplete(execution);
|
||||
this._onDidTerminateTask.fire({
|
||||
execution: _execution
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidStartTaskProcess(): Event<vscode.TaskProcessStartEvent> {
|
||||
return this._onDidTaskProcessStarted.event;
|
||||
}
|
||||
|
||||
public async $onDidStartTaskProcess(value: TaskProcessStartedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessStarted.fire({
|
||||
execution: execution,
|
||||
processId: value.processId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
|
||||
return this._onDidTaskProcessEnded.event;
|
||||
}
|
||||
|
||||
public async $onDidEndTaskProcess(value: TaskProcessEndedDTO): Promise<void> {
|
||||
const execution = await this.getTaskExecution(value.id);
|
||||
if (execution) {
|
||||
this._onDidTaskProcessEnded.fire({
|
||||
execution: execution,
|
||||
exitCode: value.exitCode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO> {
|
||||
const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
// Set up a list of task ID promises that we can wait on
|
||||
// before returning the provided tasks. The ensures that
|
||||
// our task IDs are calculated for any custom execution tasks.
|
||||
// Knowing this ID ahead of time is needed because when a task
|
||||
// start event is fired this is when the custom execution is called.
|
||||
// The task start event is also the first time we see the ID from the main
|
||||
// thread, which is too late for us because we need to save an map
|
||||
// from an ID to the custom execution function. (Kind of a cart before the horse problem).
|
||||
const taskIdPromises: Promise<void>[] = [];
|
||||
const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
|
||||
const taskDTOs: TaskDTO[] = [];
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
if (taskDTO) {
|
||||
taskDTOs.push(taskDTO);
|
||||
|
||||
if (CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task));
|
||||
}
|
||||
if (CustomExecution2DTO.is(taskDTO.execution)) {
|
||||
// The ID is calculated on the main thread task side, so, let's call into it here.
|
||||
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
|
||||
// is invoked, we have to be able to map it back to our data.
|
||||
taskIdPromises.push(this.addCustomExecution2(taskDTO, <vscode.Task2>task, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fetchPromise.then((result) => {
|
||||
Promise.all(taskIdPromises).then(() => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
tasks: taskDTOs,
|
||||
extension: handler.extension
|
||||
};
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} disable debug related method
|
||||
public async $resolveTask(handle: number, taskDTO: TaskDTO): Promise<TaskDTO | undefined> {
|
||||
/*const handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return Promise.reject(new Error('no handler found'));
|
||||
}
|
||||
|
||||
if (taskDTO.definition.type !== handler.type) {
|
||||
throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`);
|
||||
}
|
||||
|
||||
const task = await TaskDTO.to(taskDTO, this._workspaceProvider);
|
||||
if (!task) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None);
|
||||
if (!resolvedTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedTaskDTO: TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension);
|
||||
if (!resolvedTaskDTO) {
|
||||
throw new Error('Unexpected: Task cannot be resolved.');
|
||||
}
|
||||
|
||||
if (resolvedTask.definition !== task.definition) {
|
||||
throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.');
|
||||
}
|
||||
|
||||
if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) {
|
||||
await this.addCustomExecution2(resolvedTaskDTO, <vscode.Task2>resolvedTask);
|
||||
}
|
||||
|
||||
return resolvedTaskDTO;*/
|
||||
return undefined;
|
||||
protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise<tasks.TaskDTO | undefined> {
|
||||
return resolvedTaskDTO;
|
||||
}
|
||||
|
||||
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
|
||||
@@ -666,53 +141,7 @@ export class ExtHostTask implements ExtHostTaskShape {
|
||||
return this._terminalService.$requestDefaultShellAndArgs(true);
|
||||
}
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
|
||||
private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise<void> {
|
||||
const taskId = await this._proxy.$createTaskId(taskDTO);
|
||||
this._providedCustomExecutions2.set(taskId, <vscode.CustomExecution2>(<vscode.Task2>task).execution2);
|
||||
}
|
||||
|
||||
private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
|
||||
if (typeof execution === 'string') {
|
||||
const taskExecution = this._taskExecutions.get(execution);
|
||||
if (!taskExecution) {
|
||||
throw new Error('Unexpected: The specified task is missing an execution');
|
||||
}
|
||||
return taskExecution;
|
||||
}
|
||||
|
||||
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
|
||||
if (!taskToCreate) {
|
||||
throw new Error('Unexpected: Task does not exist.');
|
||||
}
|
||||
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
|
||||
this._taskExecutions.set(execution.id, createdResult);
|
||||
return createdResult;
|
||||
}
|
||||
|
||||
private customExecutionComplete(execution: TaskExecutionDTO): void {
|
||||
const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id);
|
||||
if (extensionCallback2) {
|
||||
this._activeCustomExecutions2.delete(execution.id);
|
||||
}
|
||||
|
||||
const lastCustomExecution = this._providedCustomExecutions2.get(execution.id);
|
||||
// Technically we don't really need to do this, however, if an extension
|
||||
// is executing a task through "executeTask" over and over again
|
||||
// with different properties in the task definition, then this list
|
||||
// could grow indefinitely, something we don't want.
|
||||
this._providedCustomExecutions2.clear();
|
||||
// We do still need to hang on to the last custom execution so that the
|
||||
// Rerun Task command doesn't choke when it tries to rerun a custom execution
|
||||
if (lastCustomExecution) {
|
||||
this._providedCustomExecutions2.set(execution.id, lastCustomExecution);
|
||||
}
|
||||
public async $jsonTasksSupported(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import * as os from 'os';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
@@ -181,7 +181,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
envFromConfig,
|
||||
this._variableResolver,
|
||||
isWorkspaceShellAllowed,
|
||||
pkg.version,
|
||||
product.version,
|
||||
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
|
||||
baseEnv
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user