mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-03 17:23:42 -05:00
Merge from vscode 966b87dd4013be1a9c06e2b8334522ec61905cc2 (#4696)
This commit is contained in:
790
src/vs/workbench/api/browser/mainThreadComments.ts
Normal file
790
src/vs/workbench/api/browser/mainThreadComments.ts
Normal file
@@ -0,0 +1,790 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { keys } from 'vs/base/common/map';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentProviderFeatures } from '../common/extHost.protocol';
|
||||
|
||||
import { ICommentService, ICommentInfo } from 'vs/workbench/contrib/comments/browser/commentService';
|
||||
import { COMMENTS_PANEL_ID, CommentsPanel, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsPanel';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommentsConfiguration } from 'vs/workbench/contrib/comments/browser/comments.contribution';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider {
|
||||
private readonly _proxy: ExtHostCommentsShape;
|
||||
private readonly _handle: number;
|
||||
private readonly _features: CommentProviderFeatures;
|
||||
get startDraftLabel(): string | undefined { return this._features.startDraftLabel; }
|
||||
get deleteDraftLabel(): string | undefined { return this._features.deleteDraftLabel; }
|
||||
get finishDraftLabel(): string | undefined { return this._features.finishDraftLabel; }
|
||||
get reactionGroup(): modes.CommentReaction[] | undefined { return this._features.reactionGroup; }
|
||||
|
||||
constructor(proxy: ExtHostCommentsShape, handle: number, features: CommentProviderFeatures) {
|
||||
this._proxy = proxy;
|
||||
this._handle = handle;
|
||||
this._features = features;
|
||||
}
|
||||
|
||||
async provideDocumentComments(uri: URI, token: CancellationToken) {
|
||||
return this._proxy.$provideDocumentComments(this._handle, uri);
|
||||
}
|
||||
|
||||
async createNewCommentThread(uri: URI, range: Range, text: string, token: CancellationToken) {
|
||||
return this._proxy.$createNewCommentThread(this._handle, uri, range, text);
|
||||
}
|
||||
|
||||
async replyToCommentThread(uri: URI, range: Range, thread: modes.CommentThread, text: string, token: CancellationToken) {
|
||||
return this._proxy.$replyToCommentThread(this._handle, uri, range, thread, text);
|
||||
}
|
||||
|
||||
async editComment(uri: URI, comment: modes.Comment, text: string, token: CancellationToken) {
|
||||
return this._proxy.$editComment(this._handle, uri, comment, text);
|
||||
}
|
||||
|
||||
async deleteComment(uri: URI, comment: modes.Comment, token: CancellationToken) {
|
||||
return this._proxy.$deleteComment(this._handle, uri, comment);
|
||||
}
|
||||
|
||||
async startDraft(uri: URI, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$startDraft(this._handle, uri);
|
||||
}
|
||||
async deleteDraft(uri: URI, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$deleteDraft(this._handle, uri);
|
||||
}
|
||||
async finishDraft(uri: URI, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$finishDraft(this._handle, uri);
|
||||
}
|
||||
async addReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$addReaction(this._handle, uri, comment, reaction);
|
||||
}
|
||||
async deleteReaction(uri: URI, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$deleteReaction(this._handle, uri, comment, reaction);
|
||||
}
|
||||
|
||||
|
||||
// onDidChangeCommentThreads = null;
|
||||
}
|
||||
|
||||
export class MainThreadCommentThread implements modes.CommentThread2 {
|
||||
private _input?: modes.CommentInput;
|
||||
get input(): modes.CommentInput | undefined {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
set input(value: modes.CommentInput | undefined) {
|
||||
this._input = value;
|
||||
this._onDidChangeInput.fire(value);
|
||||
}
|
||||
|
||||
private _onDidChangeInput = new Emitter<modes.CommentInput | undefined>();
|
||||
get onDidChangeInput(): Event<modes.CommentInput | undefined> { return this._onDidChangeInput.event; }
|
||||
|
||||
private _label: string;
|
||||
|
||||
get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
set label(label: string) {
|
||||
this._label = label;
|
||||
this._onDidChangeLabel.fire(this._label);
|
||||
}
|
||||
|
||||
private _onDidChangeLabel = new Emitter<string>();
|
||||
get onDidChangeLabel(): Event<string> { return this._onDidChangeLabel.event; }
|
||||
|
||||
|
||||
public get comments(): modes.Comment[] {
|
||||
return this._comments;
|
||||
}
|
||||
|
||||
public set comments(newComments: modes.Comment[]) {
|
||||
this._comments = newComments;
|
||||
this._onDidChangeComments.fire(this._comments);
|
||||
}
|
||||
|
||||
private _onDidChangeComments = new Emitter<modes.Comment[]>();
|
||||
get onDidChangeComments(): Event<modes.Comment[]> { return this._onDidChangeComments.event; }
|
||||
|
||||
set acceptInputCommand(newCommand: modes.Command) {
|
||||
this._acceptInputCommand = newCommand;
|
||||
this._onDidChangeAcceptInputCommand.fire(this._acceptInputCommand);
|
||||
}
|
||||
|
||||
get acceptInputCommand(): modes.Command {
|
||||
return this._acceptInputCommand!;
|
||||
}
|
||||
|
||||
private _onDidChangeAcceptInputCommand = new Emitter<modes.Command>();
|
||||
get onDidChangeAcceptInputCommand(): Event<modes.Command> { return this._onDidChangeAcceptInputCommand.event; }
|
||||
|
||||
set additionalCommands(newCommands: modes.Command[]) {
|
||||
this._additionalCommands = newCommands;
|
||||
this._onDidChangeAdditionalCommands.fire(this._additionalCommands);
|
||||
}
|
||||
|
||||
get additionalCommands(): modes.Command[] {
|
||||
return this._additionalCommands;
|
||||
}
|
||||
|
||||
set deleteCommand(newCommand: modes.Command | undefined) {
|
||||
this._deleteCommand = newCommand;
|
||||
}
|
||||
|
||||
get deleteCommand(): modes.Command | undefined {
|
||||
return this._deleteCommand;
|
||||
}
|
||||
|
||||
private _onDidChangeAdditionalCommands = new Emitter<modes.Command[]>();
|
||||
get onDidChangeAdditionalCommands(): Event<modes.Command[]> { return this._onDidChangeAdditionalCommands.event; }
|
||||
|
||||
set range(range: IRange) {
|
||||
this._range = range;
|
||||
this._onDidChangeRange.fire(this._range);
|
||||
}
|
||||
|
||||
get range(): IRange {
|
||||
return this._range;
|
||||
}
|
||||
|
||||
private _onDidChangeRange = new Emitter<IRange>();
|
||||
public onDidChangeRange = this._onDidChangeRange.event;
|
||||
|
||||
get collapsibleState() {
|
||||
return this._collapsibleState;
|
||||
}
|
||||
|
||||
set collapsibleState(newState: modes.CommentThreadCollapsibleState) {
|
||||
this._collapsibleState = newState;
|
||||
this._onDidChangeCollasibleState.fire(this._collapsibleState);
|
||||
}
|
||||
|
||||
private _onDidChangeCollasibleState = new Emitter<modes.CommentThreadCollapsibleState>();
|
||||
public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event;
|
||||
|
||||
constructor(
|
||||
public commentThreadHandle: number,
|
||||
public controller: MainThreadCommentController,
|
||||
public extensionId: string,
|
||||
public threadId: string,
|
||||
public resource: string,
|
||||
private _range: IRange,
|
||||
private _comments: modes.Comment[],
|
||||
private _acceptInputCommand: modes.Command | undefined,
|
||||
private _additionalCommands: modes.Command[],
|
||||
private _deleteCommand: modes.Command | undefined,
|
||||
private _collapsibleState: modes.CommentThreadCollapsibleState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
dispose() { }
|
||||
|
||||
toJSON(): any {
|
||||
return {
|
||||
$mid: 7,
|
||||
commentControlHandle: this.controller.handle,
|
||||
commentThreadHandle: this.commentThreadHandle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadCommentController {
|
||||
get handle(): number {
|
||||
return this._handle;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get proxy(): ExtHostCommentsShape {
|
||||
return this._proxy;
|
||||
}
|
||||
|
||||
get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
private _reactions: modes.CommentReaction[] | undefined;
|
||||
|
||||
get reactions() {
|
||||
return this._reactions;
|
||||
}
|
||||
|
||||
set reactions(reactions: modes.CommentReaction[] | undefined) {
|
||||
this._reactions = reactions;
|
||||
}
|
||||
|
||||
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
|
||||
public activeCommentThread?: MainThreadCommentThread;
|
||||
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostCommentsShape,
|
||||
private readonly _commentService: ICommentService,
|
||||
private readonly _handle: number,
|
||||
private readonly _uniqueId: string,
|
||||
private readonly _id: string,
|
||||
private readonly _label: string,
|
||||
private _features: CommentProviderFeatures
|
||||
) { }
|
||||
|
||||
updateFeatures(features: CommentProviderFeatures) {
|
||||
this._features = features;
|
||||
}
|
||||
|
||||
createCommentThread(commentThreadHandle: number,
|
||||
threadId: string,
|
||||
resource: UriComponents,
|
||||
range: IRange,
|
||||
comments: modes.Comment[],
|
||||
acceptInputCommand: modes.Command | undefined,
|
||||
additionalCommands: modes.Command[],
|
||||
deleteCommand: modes.Command | undefined,
|
||||
collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 {
|
||||
let thread = new MainThreadCommentThread(
|
||||
commentThreadHandle,
|
||||
this,
|
||||
'',
|
||||
threadId,
|
||||
URI.revive(resource).toString(),
|
||||
range,
|
||||
comments,
|
||||
acceptInputCommand,
|
||||
additionalCommands,
|
||||
deleteCommand,
|
||||
collapseState
|
||||
);
|
||||
|
||||
this._threads.set(commentThreadHandle, thread);
|
||||
this._commentService.updateComments(this._uniqueId, {
|
||||
added: [thread],
|
||||
removed: [],
|
||||
changed: [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
});
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
deleteCommentThread(commentThreadHandle: number) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
this._threads.delete(commentThreadHandle);
|
||||
|
||||
this._commentService.updateComments(this._uniqueId, {
|
||||
added: [],
|
||||
removed: [thread],
|
||||
changed: [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
});
|
||||
|
||||
thread.dispose();
|
||||
}
|
||||
|
||||
updateComments(commentThreadHandle: number, comments: modes.Comment[]) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.comments = comments;
|
||||
|
||||
this._commentService.updateComments(this._uniqueId, {
|
||||
added: [],
|
||||
removed: [],
|
||||
changed: [thread],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
});
|
||||
}
|
||||
|
||||
updateAcceptInputCommand(commentThreadHandle: number, acceptInputCommand: modes.Command) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.acceptInputCommand = acceptInputCommand;
|
||||
}
|
||||
|
||||
updateAdditionalCommands(commentThreadHandle: number, additionalCommands: modes.Command[]) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.additionalCommands = additionalCommands;
|
||||
}
|
||||
|
||||
updateDeleteCommand(commentThreadHandle: number, deleteCommand: modes.Command) {
|
||||
const thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.deleteCommand = deleteCommand;
|
||||
}
|
||||
|
||||
updateCollapsibleState(commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.collapsibleState = collapseState;
|
||||
}
|
||||
|
||||
updateCommentThreadRange(commentThreadHandle: number, range: IRange) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.range = range;
|
||||
}
|
||||
|
||||
updateCommentThreadLabel(commentThreadHandle: number, label: string) {
|
||||
let thread = this.getKnownThread(commentThreadHandle);
|
||||
thread.label = label;
|
||||
}
|
||||
|
||||
updateInput(input: string) {
|
||||
let thread = this.activeCommentThread;
|
||||
|
||||
if (thread && thread.input) {
|
||||
let commentInput = thread.input;
|
||||
commentInput.value = input;
|
||||
thread.input = commentInput;
|
||||
}
|
||||
}
|
||||
|
||||
private getKnownThread(commentThreadHandle: number): MainThreadCommentThread {
|
||||
const thread = this._threads.get(commentThreadHandle);
|
||||
if (!thread) {
|
||||
throw new Error('unknown thread');
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
||||
async getDocumentComments(resource: URI, token: CancellationToken) {
|
||||
let ret: modes.CommentThread2[] = [];
|
||||
for (let thread of keys(this._threads)) {
|
||||
const commentThread = this._threads.get(thread)!;
|
||||
if (commentThread.resource === resource.toString()) {
|
||||
ret.push(commentThread);
|
||||
}
|
||||
}
|
||||
|
||||
let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
|
||||
|
||||
return <ICommentInfo>{
|
||||
owner: this._uniqueId,
|
||||
threads: ret,
|
||||
commentingRanges: commentingRanges ?
|
||||
{
|
||||
resource: resource, ranges: commentingRanges, newCommentThreadCallback: async (uri: UriComponents, range: IRange) => {
|
||||
await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token);
|
||||
}
|
||||
} : [],
|
||||
draftMode: modes.DraftMode.NotSupported
|
||||
};
|
||||
}
|
||||
|
||||
async getCommentingRanges(resource: URI, token: CancellationToken): Promise<IRange[]> {
|
||||
let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
|
||||
return commentingRanges || [];
|
||||
}
|
||||
|
||||
getReactionGroup(): modes.CommentReaction[] | undefined {
|
||||
return this._features.reactionGroup;
|
||||
}
|
||||
|
||||
async toggleReaction(uri: URI, thread: modes.CommentThread2, comment: modes.Comment, reaction: modes.CommentReaction, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction);
|
||||
}
|
||||
|
||||
getAllComments(): MainThreadCommentThread[] {
|
||||
let ret: MainThreadCommentThread[] = [];
|
||||
for (let thread of keys(this._threads)) {
|
||||
ret.push(this._threads.get(thread)!);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return {
|
||||
$mid: 6,
|
||||
handle: this.handle
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadComments)
|
||||
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
|
||||
private _disposables: IDisposable[];
|
||||
private _activeCommentThreadDisposables: IDisposable[];
|
||||
private readonly _proxy: ExtHostCommentsShape;
|
||||
private _documentProviders = new Map<number, IDisposable>();
|
||||
private _workspaceProviders = new Map<number, IDisposable>();
|
||||
private _handlers = new Map<number, string>();
|
||||
private _commentControllers = new Map<number, MainThreadCommentController>();
|
||||
|
||||
private _activeCommentThread?: MainThreadCommentThread;
|
||||
private _input?: modes.CommentInput;
|
||||
private _openPanelListener: IDisposable | null;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ICommentService private readonly _commentService: ICommentService,
|
||||
@IPanelService private readonly _panelService: IPanelService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
this._disposables = [];
|
||||
this._activeCommentThreadDisposables = [];
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
|
||||
this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => {
|
||||
let controller = (thread as MainThreadCommentThread).controller;
|
||||
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
|
||||
this._activeCommentThread = thread as MainThreadCommentThread;
|
||||
controller.activeCommentThread = this._activeCommentThread;
|
||||
|
||||
this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose
|
||||
this._input = input;
|
||||
this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
|
||||
await this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
}
|
||||
|
||||
$registerCommentController(handle: number, id: string, label: string): void {
|
||||
const providerId = generateUuid();
|
||||
this._handlers.set(handle, providerId);
|
||||
|
||||
const provider = new MainThreadCommentController(this._proxy, this._commentService, handle, providerId, id, label, {});
|
||||
this._commentService.registerCommentController(providerId, provider);
|
||||
this._commentControllers.set(handle, provider);
|
||||
|
||||
const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
|
||||
if (!commentsPanelAlreadyConstructed) {
|
||||
this.registerPanel(commentsPanelAlreadyConstructed);
|
||||
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
|
||||
}
|
||||
this._commentService.setWorkspaceComments(String(handle), []);
|
||||
}
|
||||
|
||||
$unregisterCommentController(handle: number): void {
|
||||
const providerId = this._handlers.get(handle);
|
||||
if (typeof providerId !== 'string') {
|
||||
throw new Error('unknown handler');
|
||||
}
|
||||
this._commentService.unregisterCommentController(providerId);
|
||||
this._handlers.delete(handle);
|
||||
this._commentControllers.delete(handle);
|
||||
}
|
||||
|
||||
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
provider.updateFeatures(features);
|
||||
}
|
||||
|
||||
$createCommentThread(handle: number,
|
||||
commentThreadHandle: number,
|
||||
threadId: string,
|
||||
resource: UriComponents,
|
||||
range: IRange,
|
||||
comments: modes.Comment[],
|
||||
acceptInputCommand: modes.Command | undefined,
|
||||
additionalCommands: modes.Command[],
|
||||
deleteCommand: modes.Command,
|
||||
collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return provider.createCommentThread(commentThreadHandle, threadId, resource, range, comments, acceptInputCommand, additionalCommands, deleteCommand, collapseState);
|
||||
}
|
||||
|
||||
$deleteCommentThread(handle: number, commentThreadHandle: number) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
return provider.deleteCommentThread(commentThreadHandle);
|
||||
}
|
||||
|
||||
$updateComments(handle: number, commentThreadHandle: number, comments: modes.Comment[]) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateComments(commentThreadHandle, comments);
|
||||
}
|
||||
|
||||
$setInputValue(handle: number, input: string) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateInput(input);
|
||||
}
|
||||
|
||||
$updateCommentThreadAcceptInputCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateAcceptInputCommand(commentThreadHandle, acceptInputCommand);
|
||||
}
|
||||
|
||||
$updateCommentThreadAdditionalCommands(handle: number, commentThreadHandle: number, additionalCommands: modes.Command[]) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateAdditionalCommands(commentThreadHandle, additionalCommands);
|
||||
}
|
||||
|
||||
$updateCommentThreadDeleteCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command) {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateDeleteCommand(commentThreadHandle, acceptInputCommand);
|
||||
}
|
||||
|
||||
$updateCommentThreadCollapsibleState(handle: number, commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState): void {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateCollapsibleState(commentThreadHandle, collapseState);
|
||||
}
|
||||
|
||||
$updateCommentThreadRange(handle: number, commentThreadHandle: number, range: any): void {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateCommentThreadRange(commentThreadHandle, range);
|
||||
}
|
||||
|
||||
$updateCommentThreadLabel(handle: number, commentThreadHandle: number, label: string): void {
|
||||
let provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateCommentThreadLabel(commentThreadHandle, label);
|
||||
}
|
||||
|
||||
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void {
|
||||
this._documentProviders.set(handle, undefined);
|
||||
const handler = new MainThreadDocumentCommentProvider(this._proxy, handle, features);
|
||||
|
||||
const providerId = generateUuid();
|
||||
this._handlers.set(handle, providerId);
|
||||
|
||||
this._commentService.registerDataProvider(providerId, handler);
|
||||
}
|
||||
|
||||
private registerPanel(commentsPanelAlreadyConstructed: boolean) {
|
||||
if (!commentsPanelAlreadyConstructed) {
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
|
||||
CommentsPanel,
|
||||
COMMENTS_PANEL_ID,
|
||||
COMMENTS_PANEL_TITLE,
|
||||
'commentsPanel',
|
||||
10
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the comments panel has never been opened, the constructor for it has not yet run so it has
|
||||
* no listeners for comment threads being set or updated. Listen for the panel opening for the
|
||||
* first time and send it comments then.
|
||||
*/
|
||||
private registerOpenPanelListener(commentsPanelAlreadyConstructed: boolean) {
|
||||
if (!commentsPanelAlreadyConstructed && !this._openPanelListener) {
|
||||
this._openPanelListener = this._panelService.onDidPanelOpen(e => {
|
||||
if (e.panel.getId() === COMMENTS_PANEL_ID) {
|
||||
keys(this._workspaceProviders).forEach(handle => {
|
||||
this._proxy.$provideWorkspaceComments(handle).then(commentThreads => {
|
||||
if (commentThreads) {
|
||||
const providerId = this.getHandler(handle);
|
||||
this._commentService.setWorkspaceComments(providerId, commentThreads);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
keys(this._commentControllers).forEach(handle => {
|
||||
let threads = this._commentControllers.get(handle)!.getAllComments();
|
||||
|
||||
if (threads.length) {
|
||||
const providerId = this.getHandler(handle);
|
||||
this._commentService.setWorkspaceComments(providerId, threads);
|
||||
}
|
||||
});
|
||||
|
||||
if (this._openPanelListener) {
|
||||
this._openPanelListener.dispose();
|
||||
this._openPanelListener = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getHandler(handle: number) {
|
||||
if (!this._handlers.has(handle)) {
|
||||
throw new Error('Unknown handler');
|
||||
}
|
||||
return this._handlers.get(handle)!;
|
||||
}
|
||||
|
||||
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void {
|
||||
this._workspaceProviders.set(handle, undefined);
|
||||
|
||||
const providerId = generateUuid();
|
||||
this._handlers.set(handle, providerId);
|
||||
|
||||
const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
|
||||
if (!commentsPanelAlreadyConstructed) {
|
||||
this.registerPanel(commentsPanelAlreadyConstructed);
|
||||
}
|
||||
|
||||
const openPanel = this._configurationService.getValue<ICommentsConfiguration>('comments').openPanel;
|
||||
|
||||
if (openPanel === 'neverOpen') {
|
||||
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
|
||||
}
|
||||
|
||||
if (openPanel === 'openOnSessionStart') {
|
||||
this._panelService.openPanel(COMMENTS_PANEL_ID);
|
||||
}
|
||||
|
||||
this._proxy.$provideWorkspaceComments(handle).then(commentThreads => {
|
||||
if (commentThreads) {
|
||||
if (openPanel === 'openOnSessionStartWithComments' && commentThreads.length) {
|
||||
if (commentThreads.length) {
|
||||
this._panelService.openPanel(COMMENTS_PANEL_ID);
|
||||
} else {
|
||||
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
|
||||
}
|
||||
}
|
||||
|
||||
this._commentService.setWorkspaceComments(providerId, commentThreads);
|
||||
}
|
||||
});
|
||||
|
||||
/* __GDPR__
|
||||
"comments:registerWorkspaceCommentProvider" : {
|
||||
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', {
|
||||
extensionId: extensionId.value
|
||||
});
|
||||
}
|
||||
|
||||
$unregisterDocumentCommentProvider(handle: number): void {
|
||||
this._documentProviders.delete(handle);
|
||||
const handlerId = this.getHandler(handle);
|
||||
this._commentService.unregisterDataProvider(handlerId);
|
||||
this._handlers.delete(handle);
|
||||
}
|
||||
|
||||
$unregisterWorkspaceCommentProvider(handle: number): void {
|
||||
this._workspaceProviders.delete(handle);
|
||||
if (this._workspaceProviders.size === 0) {
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).deregisterPanel(COMMENTS_PANEL_ID);
|
||||
|
||||
if (this._openPanelListener) {
|
||||
this._openPanelListener.dispose();
|
||||
this._openPanelListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
const handlerId = this.getHandler(handle);
|
||||
this._commentService.removeWorkspaceComments(handlerId);
|
||||
this._handlers.delete(handle);
|
||||
}
|
||||
|
||||
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent) {
|
||||
// notify comment service
|
||||
const providerId = this.getHandler(handle);
|
||||
this._commentService.updateComments(providerId, event);
|
||||
}
|
||||
|
||||
getVisibleEditors(): ICodeEditor[] {
|
||||
let ret: ICodeEditor[] = [];
|
||||
|
||||
this._editorService.visibleControls.forEach(control => {
|
||||
if (isCodeEditor(control.getControl())) {
|
||||
ret.push(control.getControl() as ICodeEditor);
|
||||
}
|
||||
|
||||
if (isDiffEditor(control.getControl())) {
|
||||
let diffEditor = control.getControl() as IDiffEditor;
|
||||
ret.push(diffEditor.getOriginalEditor(), diffEditor.getModifiedEditor());
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
async provideWorkspaceComments(): Promise<modes.CommentThread[]> {
|
||||
const result: modes.CommentThread[] = [];
|
||||
for (const handle of keys(this._workspaceProviders)) {
|
||||
const result = await this._proxy.$provideWorkspaceComments(handle);
|
||||
if (Array.isArray(result)) {
|
||||
result.push(...result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async provideDocumentComments(resource: URI): Promise<Array<modes.CommentInfo | null>> {
|
||||
const result: Array<modes.CommentInfo | null> = [];
|
||||
for (const handle of keys(this._documentProviders)) {
|
||||
result.push(await this._proxy.$provideDocumentComments(handle, resource));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
|
||||
this._workspaceProviders.forEach(value => dispose(value));
|
||||
this._workspaceProviders.clear();
|
||||
this._documentProviders.forEach(value => dispose(value));
|
||||
this._documentProviders.clear();
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
|
||||
private getSession(sessionId: DebugSessionUUID | undefined): IDebugSession | undefined {
|
||||
if (sessionId) {
|
||||
return this.debugService.getModel().getSessions(true).filter(s => s.getId() === sessionId).pop();
|
||||
return this.debugService.getModel().getSession(sessionId, true);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -246,7 +246,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
}
|
||||
|
||||
public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): Promise<any> {
|
||||
const session = this.debugService.getModel().getSessions(true).filter(s => s.getId() === sessionId).pop();
|
||||
const session = this.debugService.getModel().getSession(sessionId, true);
|
||||
if (session) {
|
||||
return session.customRequest(request, args).then(response => {
|
||||
if (response && response.success) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
|
||||
import { getRealAndSyntheticDocumentFormattersOrdered, formatDocumentWithProvider } from 'vs/editor/contrib/format/format';
|
||||
import { formatDocumentWithFirstProvider } from 'vs/editor/contrib/format/format';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
@@ -256,24 +256,15 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
const source = new CancellationTokenSource();
|
||||
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', overrides);
|
||||
const request = this._instantiationService.invokeFunction(formatDocumentWithFirstProvider, model, source.token);
|
||||
|
||||
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
|
||||
if (provider.length !== 1) {
|
||||
// print message for >1 case?
|
||||
resolve();
|
||||
setTimeout(() => {
|
||||
reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout));
|
||||
source.cancel();
|
||||
}, timeout);
|
||||
|
||||
} else {
|
||||
// having 1 formatter -> go for it
|
||||
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', overrides);
|
||||
const request = this._instantiationService.invokeFunction(formatDocumentWithProvider, provider[0], model, source.token);
|
||||
|
||||
setTimeout(() => {
|
||||
reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout));
|
||||
source.cancel();
|
||||
}, timeout);
|
||||
|
||||
request.then(resolve, reject);
|
||||
}
|
||||
request.then(resolve, reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
617
src/vs/workbench/api/browser/mainThreadTask.ts
Normal file
617
src/vs/workbench/api/browser/mainThreadTask.ts
Normal file
@@ -0,0 +1,617 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import { IStringDictionary, forEach } from 'vs/base/common/collections';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
import {
|
||||
ContributedTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
|
||||
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource,
|
||||
TaskSourceKind, ExtensionTaskSource, RunOptions, TaskSet, TaskDefinition
|
||||
} from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
|
||||
|
||||
import { ResolveSet, ResolvedVariables } from 'vs/workbench/contrib/tasks/common/taskSystem';
|
||||
import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/contrib/tasks/common/taskService';
|
||||
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import {
|
||||
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
|
||||
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
|
||||
RunOptionsDTO
|
||||
} from 'vs/workbench/api/common/shared/tasks';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
|
||||
namespace TaskExecutionDTO {
|
||||
export function from(value: TaskExecution): TaskExecutionDTO {
|
||||
return {
|
||||
id: value.id,
|
||||
task: TaskDTO.from(value.task)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskProcessStartedDTO {
|
||||
export function from(value: TaskExecution, processId: number): TaskProcessStartedDTO {
|
||||
return {
|
||||
id: value.id,
|
||||
processId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskProcessEndedDTO {
|
||||
export function from(value: TaskExecution, exitCode: number): TaskProcessEndedDTO {
|
||||
return {
|
||||
id: value.id,
|
||||
exitCode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDefinitionDTO {
|
||||
export function from(value: KeyedTaskIdentifier): TaskDefinitionDTO {
|
||||
const result = Objects.assign(Object.create(null), value);
|
||||
delete result._key;
|
||||
return result;
|
||||
}
|
||||
export function to(value: TaskDefinitionDTO, executeOnly: boolean): KeyedTaskIdentifier | undefined {
|
||||
let result = TaskDefinition.createTaskIdentifier(value, console);
|
||||
if (result === undefined && executeOnly) {
|
||||
result = {
|
||||
_key: generateUuid(),
|
||||
type: '$executeOnly'
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskPresentationOptionsDTO {
|
||||
export function from(value: PresentationOptions | undefined): TaskPresentationOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
export function to(value: TaskPresentationOptionsDTO | undefined): PresentationOptions {
|
||||
if (value === undefined || value === null) {
|
||||
return PresentationOptions.defaults;
|
||||
}
|
||||
return Objects.assign(Object.create(null), PresentationOptions.defaults, value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace RunOptionsDTO {
|
||||
export function from(value: RunOptions): RunOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return Objects.assign(Object.create(null), value);
|
||||
}
|
||||
export function to(value: RunOptionsDTO | undefined): RunOptions {
|
||||
if (value === undefined || value === null) {
|
||||
return RunOptions.defaults;
|
||||
}
|
||||
return Objects.assign(Object.create(null), RunOptions.defaults, value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionOptionsDTO {
|
||||
export function from(value: CommandOptions): ProcessExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
}
|
||||
export function to(value: ProcessExecutionOptionsDTO | undefined): CommandOptions {
|
||||
if (value === undefined || value === null) {
|
||||
return CommandOptions.defaults;
|
||||
}
|
||||
return {
|
||||
cwd: value.cwd || CommandOptions.defaults.cwd,
|
||||
env: value.env
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProcessExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO {
|
||||
const candidate = value as ProcessExecutionDTO;
|
||||
return candidate && !!candidate.process;
|
||||
}
|
||||
export function from(value: CommandConfiguration): ProcessExecutionDTO {
|
||||
const process: string = Types.isString(value.name) ? value.name : value.name!.value;
|
||||
const args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : [];
|
||||
const result: ProcessExecutionDTO = {
|
||||
process: process,
|
||||
args: args
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ProcessExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ProcessExecutionDTO): CommandConfiguration {
|
||||
const result: CommandConfiguration = {
|
||||
runtime: RuntimeType.Process,
|
||||
name: value.process,
|
||||
args: value.args,
|
||||
presentation: undefined
|
||||
};
|
||||
result.options = ProcessExecutionOptionsDTO.to(value.options);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionOptionsDTO {
|
||||
export function from(value: CommandOptions): ShellExecutionOptionsDTO | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: ShellExecutionOptionsDTO = {
|
||||
cwd: value.cwd || CommandOptions.defaults.cwd,
|
||||
env: value.env
|
||||
};
|
||||
if (value.shell) {
|
||||
result.executable = value.shell.executable;
|
||||
result.shellArgs = value.shell.args;
|
||||
result.shellQuoting = value.shell.quoting;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionOptionsDTO): CommandOptions | undefined {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
const result: CommandOptions = {
|
||||
cwd: value.cwd,
|
||||
env: value.env
|
||||
};
|
||||
if (value.executable) {
|
||||
result.shell = {
|
||||
executable: value.executable
|
||||
};
|
||||
if (value.shellArgs) {
|
||||
result.shell.args = value.shellArgs;
|
||||
}
|
||||
if (value.shellQuoting) {
|
||||
result.shell.quoting = value.shellQuoting;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO {
|
||||
const candidate = value as ShellExecutionDTO;
|
||||
return candidate && (!!candidate.commandLine || !!candidate.command);
|
||||
}
|
||||
export function from(value: CommandConfiguration): ShellExecutionDTO {
|
||||
const result: ShellExecutionDTO = {};
|
||||
if (value.name && Types.isString(value.name) && (value.args === undefined || value.args === null || value.args.length === 0)) {
|
||||
result.commandLine = value.name;
|
||||
} else {
|
||||
result.command = value.name;
|
||||
result.args = value.args;
|
||||
}
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: ShellExecutionDTO): CommandConfiguration {
|
||||
const result: CommandConfiguration = {
|
||||
runtime: RuntimeType.Shell,
|
||||
name: value.commandLine ? value.commandLine : value.command,
|
||||
args: value.args,
|
||||
presentation: undefined
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = ShellExecutionOptionsDTO.to(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CustomExecutionDTO {
|
||||
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO {
|
||||
const candidate = value as CustomExecutionDTO;
|
||||
return candidate && candidate.customExecution === 'customExecution';
|
||||
}
|
||||
|
||||
export function from(value: CommandConfiguration): CustomExecutionDTO {
|
||||
return {
|
||||
customExecution: 'customExecution'
|
||||
};
|
||||
}
|
||||
|
||||
export function to(value: CustomExecutionDTO): CommandConfiguration {
|
||||
return {
|
||||
runtime: RuntimeType.CustomExecution,
|
||||
presentation: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskSourceDTO {
|
||||
export function from(value: TaskSource): TaskSourceDTO {
|
||||
const result: TaskSourceDTO = {
|
||||
label: value.label
|
||||
};
|
||||
if (value.kind === TaskSourceKind.Extension) {
|
||||
result.extensionId = value.extension;
|
||||
if (value.workspaceFolder) {
|
||||
result.scope = value.workspaceFolder.uri;
|
||||
} else {
|
||||
result.scope = value.scope;
|
||||
}
|
||||
} else if (value.kind === TaskSourceKind.Workspace) {
|
||||
result.extensionId = '$core';
|
||||
result.scope = value.config.workspaceFolder.uri;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export function to(value: TaskSourceDTO, workspace: IWorkspaceContextService): ExtensionTaskSource {
|
||||
let scope: TaskScope;
|
||||
let workspaceFolder: IWorkspaceFolder | undefined;
|
||||
if ((value.scope === undefined) || ((typeof value.scope === 'number') && (value.scope !== TaskScope.Global))) {
|
||||
if (workspace.getWorkspace().folders.length === 0) {
|
||||
scope = TaskScope.Global;
|
||||
workspaceFolder = undefined;
|
||||
} else {
|
||||
scope = TaskScope.Folder;
|
||||
workspaceFolder = workspace.getWorkspace().folders[0];
|
||||
}
|
||||
} else if (typeof value.scope === 'number') {
|
||||
scope = value.scope;
|
||||
} else {
|
||||
scope = TaskScope.Folder;
|
||||
workspaceFolder = Types.withNullAsUndefined(workspace.getWorkspaceFolder(URI.revive(value.scope)));
|
||||
}
|
||||
const result: ExtensionTaskSource = {
|
||||
kind: TaskSourceKind.Extension,
|
||||
label: value.label,
|
||||
extension: value.extensionId,
|
||||
scope,
|
||||
workspaceFolder
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskHandleDTO {
|
||||
export function is(value: any): value is TaskHandleDTO {
|
||||
const candidate: TaskHandleDTO = value;
|
||||
return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskDTO {
|
||||
export function from(task: Task): TaskDTO | undefined {
|
||||
if (task === undefined || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
|
||||
return undefined;
|
||||
}
|
||||
const result: TaskDTO = {
|
||||
_id: task._id,
|
||||
name: task.configurationProperties.name,
|
||||
definition: TaskDefinitionDTO.from(task.getDefinition()),
|
||||
source: TaskSourceDTO.from(task._source),
|
||||
execution: undefined,
|
||||
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
|
||||
isBackground: task.configurationProperties.isBackground,
|
||||
problemMatchers: [],
|
||||
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false,
|
||||
runOptions: RunOptionsDTO.from(task.runOptions),
|
||||
};
|
||||
if (task.configurationProperties.group) {
|
||||
result.group = task.configurationProperties.group;
|
||||
}
|
||||
if (task.command) {
|
||||
if (task.command.runtime === RuntimeType.Process) {
|
||||
result.execution = ProcessExecutionDTO.from(task.command);
|
||||
} else if (task.command.runtime === RuntimeType.Shell) {
|
||||
result.execution = ShellExecutionDTO.from(task.command);
|
||||
}
|
||||
}
|
||||
if (task.configurationProperties.problemMatchers) {
|
||||
for (let matcher of task.configurationProperties.problemMatchers) {
|
||||
if (Types.isString(matcher)) {
|
||||
result.problemMatchers.push(matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function to(task: TaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined {
|
||||
if (!task || (typeof task.name !== 'string')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let command: CommandConfiguration | undefined;
|
||||
if (task.execution) {
|
||||
if (ShellExecutionDTO.is(task.execution)) {
|
||||
command = ShellExecutionDTO.to(task.execution);
|
||||
} else if (ProcessExecutionDTO.is(task.execution)) {
|
||||
command = ProcessExecutionDTO.to(task.execution);
|
||||
} else if (CustomExecutionDTO.is(task.execution)) {
|
||||
command = CustomExecutionDTO.to(task.execution);
|
||||
}
|
||||
}
|
||||
|
||||
if (!command) {
|
||||
return undefined;
|
||||
}
|
||||
command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions);
|
||||
const source = TaskSourceDTO.to(task.source, workspace);
|
||||
|
||||
const label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
|
||||
const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!;
|
||||
const id = `${task.source.extensionId}.${definition._key}`;
|
||||
const result: ContributedTask = new ContributedTask(
|
||||
id, // uuidMap.getUUID(identifier)
|
||||
source,
|
||||
label,
|
||||
definition.type,
|
||||
definition,
|
||||
command,
|
||||
task.hasDefinedMatchers,
|
||||
RunOptionsDTO.to(task.runOptions),
|
||||
{
|
||||
name: task.name,
|
||||
identifier: label,
|
||||
group: task.group,
|
||||
isBackground: !!task.isBackground,
|
||||
problemMatchers: task.problemMatchers.slice(),
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskFilterDTO {
|
||||
export function from(value: TaskFilter): TaskFilterDTO {
|
||||
return value;
|
||||
}
|
||||
export function to(value: TaskFilterDTO | undefined): TaskFilter | undefined {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTask)
|
||||
export class MainThreadTask implements MainThreadTaskShape {
|
||||
|
||||
private readonly _extHostContext: IExtHostContext;
|
||||
private readonly _proxy: ExtHostTaskShape;
|
||||
private readonly _providers: Map<number, { disposable: IDisposable, provider: ITaskProvider }>;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITaskService private readonly _taskService: ITaskService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextServer: IWorkspaceContextService,
|
||||
@IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask);
|
||||
this._providers = new Map();
|
||||
this._taskService.onDidStateChange((event: TaskEvent) => {
|
||||
const task = event.__task!;
|
||||
if (event.kind === TaskEventKind.Start) {
|
||||
this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()), event.terminalId!);
|
||||
} else if (event.kind === TaskEventKind.ProcessStarted) {
|
||||
this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!));
|
||||
} else if (event.kind === TaskEventKind.ProcessEnded) {
|
||||
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode!));
|
||||
} else if (event.kind === TaskEventKind.End) {
|
||||
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._providers.forEach((value) => {
|
||||
value.disposable.dispose();
|
||||
});
|
||||
this._providers.clear();
|
||||
}
|
||||
|
||||
$createTaskId(taskDTO: TaskDTO): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let task = TaskDTO.to(taskDTO, this._workspaceContextServer, true);
|
||||
if (task) {
|
||||
resolve(task._id);
|
||||
} else {
|
||||
reject(new Error('Task could not be created from DTO'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public $registerTaskProvider(handle: number): Promise<void> {
|
||||
const provider: ITaskProvider = {
|
||||
provideTasks: (validTypes: IStringDictionary<boolean>) => {
|
||||
return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => {
|
||||
const tasks: Task[] = [];
|
||||
for (let dto of value.tasks) {
|
||||
const task = TaskDTO.to(dto, this._workspaceContextServer, true);
|
||||
if (task) {
|
||||
tasks.push(task);
|
||||
} else {
|
||||
console.error(`Task System: can not convert task: ${JSON.stringify(dto.definition, undefined, 0)}. Task will be dropped`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
tasks,
|
||||
extension: value.extension
|
||||
} as TaskSet;
|
||||
});
|
||||
}
|
||||
};
|
||||
const disposable = this._taskService.registerTaskProvider(provider);
|
||||
this._providers.set(handle, { disposable, provider });
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public $unregisterTaskProvider(handle: number): Promise<void> {
|
||||
const provider = this._providers.get(handle);
|
||||
if (provider) {
|
||||
provider.disposable.dispose();
|
||||
this._providers.delete(handle);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public $fetchTasks(filter?: TaskFilterDTO): Promise<TaskDTO[]> {
|
||||
return this._taskService.tasks(TaskFilterDTO.to(filter)).then((tasks) => {
|
||||
const result: TaskDTO[] = [];
|
||||
for (let task of tasks) {
|
||||
const item = TaskDTO.from(task);
|
||||
if (item) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
|
||||
return new Promise<TaskExecutionDTO>((resolve, reject) => {
|
||||
if (TaskHandleDTO.is(value)) {
|
||||
const workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
|
||||
if (workspaceFolder) {
|
||||
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
|
||||
this._taskService.run(task).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
const result: TaskExecutionDTO = {
|
||||
id: value.id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}, (_error) => {
|
||||
reject(new Error('Task not found'));
|
||||
});
|
||||
} else {
|
||||
reject(new Error('No workspace folder'));
|
||||
}
|
||||
} else {
|
||||
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
|
||||
this._taskService.run(task).then(undefined, reason => {
|
||||
// eat the error, it has already been surfaced to the user and we don't care about it here
|
||||
});
|
||||
const result: TaskExecutionDTO = {
|
||||
id: task._id,
|
||||
task: TaskDTO.from(task)
|
||||
};
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public $customExecutionComplete(id: string, result?: number): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this._taskService.getActiveTasks().then((tasks) => {
|
||||
for (let task of tasks) {
|
||||
if (id === task._id) {
|
||||
this._taskService.extensionCallbackTaskComplete(task, result).then((value) => {
|
||||
resolve(undefined);
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
reject(new Error('Task to mark as complete not found'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public $terminateTask(id: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this._taskService.getActiveTasks().then((tasks) => {
|
||||
for (let task of tasks) {
|
||||
if (id === task._id) {
|
||||
this._taskService.terminate(task).then((value) => {
|
||||
resolve(undefined);
|
||||
}, (error) => {
|
||||
reject(undefined);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
reject(new Error('Task to terminate not found'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public $registerTaskSystem(key: string, info: TaskSystemInfoDTO): void {
|
||||
let platform: Platform.Platform;
|
||||
switch (info.platform) {
|
||||
case 'win32':
|
||||
platform = Platform.Platform.Windows;
|
||||
break;
|
||||
case 'darwin':
|
||||
platform = Platform.Platform.Mac;
|
||||
break;
|
||||
case 'linux':
|
||||
platform = Platform.Platform.Linux;
|
||||
break;
|
||||
default:
|
||||
platform = Platform.platform;
|
||||
}
|
||||
this._taskService.registerTaskSystem(key, {
|
||||
platform: platform,
|
||||
uriProvider: (path: string): URI => {
|
||||
return URI.parse(`${info.scheme}://${info.authority}${path}`);
|
||||
},
|
||||
context: this._extHostContext,
|
||||
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise<ResolvedVariables> => {
|
||||
const vars: string[] = [];
|
||||
toResolve.variables.forEach(item => vars.push(item));
|
||||
return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => {
|
||||
const partiallyResolvedVars = new Array<string>();
|
||||
forEach(values.variables, (entry) => {
|
||||
partiallyResolvedVars.push(entry.value);
|
||||
});
|
||||
return new Promise<ResolvedVariables>((resolve, reject) => {
|
||||
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => {
|
||||
const result: ResolvedVariables = {
|
||||
process: undefined,
|
||||
variables: new Map<string, string>()
|
||||
};
|
||||
for (let i = 0; i < partiallyResolvedVars.length; i++) {
|
||||
const variableName = vars[i].substring(2, vars[i].length - 1);
|
||||
if (resolvedVars && values.variables[vars[i]] === vars[i]) {
|
||||
result.variables.set(variableName, resolvedVars.get(variableName));
|
||||
} else {
|
||||
result.variables.set(variableName, partiallyResolvedVars[i]);
|
||||
}
|
||||
}
|
||||
if (Types.isString(values.process)) {
|
||||
result.process = values.process;
|
||||
}
|
||||
resolve(result);
|
||||
}, reason => {
|
||||
reject(reason);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user