From 4186bc0a44a1e6216dfe7e6ff2c07b74d9507fd5 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 17 May 2019 11:49:00 -0700 Subject: [PATCH] menu items --- src/vs/editor/common/modes.ts | 9 + src/vs/platform/actions/common/actions.ts | 4 + src/vs/vscode.d.ts | 170 ++++++++++++++++ src/vs/vscode.proposed.d.ts | 185 ++---------------- .../api/browser/mainThreadComments.ts | 73 ++++--- .../workbench/api/common/extHost.protocol.ts | 2 + .../workbench/api/common/extHostComments.ts | 76 ++++--- src/vs/workbench/api/common/extHostTypes.ts | 6 + .../api/common/menusExtensionPoint.ts | 26 ++- src/vs/workbench/api/node/extHost.api.impl.ts | 1 + .../contrib/comments/browser/commentMenus.ts | 70 +++++++ .../comments/browser/commentService.ts | 49 +++-- .../comments/browser/commentThreadWidget.ts | 44 ++--- .../browser/commentsEditorContribution.ts | 65 ++---- .../comments/common/commentContextKeys.ts | 17 ++ 15 files changed, 471 insertions(+), 326 deletions(-) create mode 100644 src/vs/workbench/contrib/comments/browser/commentMenus.ts create mode 100644 src/vs/workbench/contrib/comments/common/commentContextKeys.ts diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 082dcf810f6..a71c1680e74 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1347,6 +1347,14 @@ export interface CommentReaction { readonly canEdit?: boolean; } +/** + * @internal + */ +export enum CommentMode { + Editing = 0, + Preview = 1 +} + /** * @internal */ @@ -1363,6 +1371,7 @@ export interface Comment { readonly isDraft?: boolean; readonly commentReactions?: CommentReaction[]; readonly label?: string; + readonly mode?: CommentMode; } /** diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 8aacb21dfb2..d0290c179fd 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -95,6 +95,10 @@ export const enum MenuId { TouchBarContext, ViewItemContext, ViewTitle, + CommentThreadTitle, + CommentThreadActions, + CommentTitle, + CommentActions } export interface IMenuActionOptions { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index a4bf1796b7d..6081b3f6ce6 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -8940,6 +8940,176 @@ declare module 'vscode' { */ export const onDidChange: Event; } + + //#region Comments + + /** + * Collapsible state of a [comment thread](#CommentThread) + */ + export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + + /** + * Determines an item is expanded + */ + Expanded = 1 + } + + export enum CommentMode { + Editing = 0, + Preview = 1 + } + + /** + * A collection of [comments](#Comment) representing a conversation at a particular range in a document. + */ + export interface CommentThread { + /** + * The uri of the document the thread has been created on. + */ + readonly resource: Uri; + + /** + * The range the comment thread is located within the document. The thread icon will be shown + * at the first line of the range. + */ + readonly range: Range; + + /** + * The ordered comments of the thread. + */ + comments: ReadonlyArray; + + /** + * Whether the thread should be collapsed or expanded when opening the document. + * Defaults to Collapsed. + */ + collapsibleState: CommentThreadCollapsibleState; + + /** + * The optional human-readable label describing the [Comment Thread](#CommentThread) + */ + label?: string; + + /** + * Dispose this comment thread. + * + * Once disposed, this comment thread will be removed from visible editors and Comment Panel when approriate. + */ + dispose(): void; + } + + /** + * Author information of a [comment](#Comment) + */ + export interface CommentAuthorInformation { + /** + * The display name of the author of the comment + */ + name: string; + + /** + * The optional icon path for the author + */ + iconPath?: Uri; + } + + /** + * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. + */ + export interface Comment { + /** + * The human-readable comment body + */ + body: string | MarkdownString; + + mode: CommentMode; + + /** + * The author information of the comment + */ + author: CommentAuthorInformation; + + /** + * Optional label describing the [Comment](#Comment) + * Label will be rendered next to authorName if exists. + */ + label?: string; + } + + export interface CommentReply { + thread: CommentThread; + + text: string; + } + + /** + * Commenting range provider for a [comment controller](#CommentController). + */ + export interface CommentingRangeProvider { + /** + * Provide a list of ranges which allow new comment threads creation or null for a given document + */ + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + } + + /** + * A comment controller is able to provide [comments](#CommentThread) support to the editor and + * provide users various ways to interact with comments. + */ + export interface CommentController { + /** + * The id of this comment controller. + */ + readonly id: string; + + /** + * The human-readable label of this comment controller. + */ + readonly label: string; + + /** + * Optional commenting range provider. Provide a list [ranges](#Range) which support commenting to any given resource uri. + * + * If not provided and `emptyCommentThreadFactory` exits, users can leave comments in any document opened in the editor. + */ + commentingRangeProvider?: CommentingRangeProvider; + + /** + * Create a [comment thread](#CommentThread). The comment thread will be displayed in visible text editors (if the resource matches) + * and Comments Panel once created. + * + * @param id An `id` for the comment thread. + * @param resource The uri of the document the thread has been created on. + * @param range The range the comment thread is located within the document. + * @param comments The ordered comments of the thread. + */ + createCommentThread(uri: Uri, range: Range, comments: Comment[]): CommentThread; + + /** + * Dispose this comment controller. + * + * Once disposed, all [comment threads](#CommentThread) created by this comment controller will also be removed from the editor + * and Comments Panel. + */ + dispose(): void; + } + + namespace comment { + /** + * Creates a new [comment controller](#CommentController) instance. + * + * @param id An `id` for the comment controller. + * @param label A human-readable string for the comment controller. + * @return An instance of [comment controller](#CommentController). + */ + export function createCommentController(id: string, label: string): CommentController; + } + + //#endregion } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 51846936ee6..b7cf411783c 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -939,15 +939,6 @@ declare module 'vscode' { reactionProvider?: CommentReactionProvider; } - export interface CommentController { - /** - * The active [comment thread](#CommentThread) or `undefined`. The `activeCommentThread` is the comment thread of - * the comment widget that currently has focus. It's `undefined` when the focus is not in any comment thread widget, or - * the comment widget created from [comment thread template](#CommentThreadTemplate). - */ - readonly activeCommentThread: CommentThread | undefined; - } - namespace workspace { /** * DEPRECATED @@ -961,21 +952,6 @@ declare module 'vscode' { export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; } - /** - * Collapsible state of a [comment thread](#CommentThread) - */ - export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - - /** - * Determines an item is expanded - */ - Expanded = 1 - } - /** * A collection of [comments](#Comment) representing a conversation at a particular range in a document. */ @@ -990,28 +966,6 @@ declare module 'vscode' { */ readonly uri: Uri; - /** - * The range the comment thread is located within the document. The thread icon will be shown - * at the first line of the range. - */ - readonly range: Range; - - /** - * The ordered comments of the thread. - */ - comments: Comment[]; - - /** - * Whether the thread should be collapsed or expanded when opening the document. - * Defaults to Collapsed. - */ - collapsibleState: CommentThreadCollapsibleState; - - /** - * The optional human-readable label describing the [Comment Thread](#CommentThread) - */ - label?: string; - /** * Optional accept input command * @@ -1020,46 +974,6 @@ declare module 'vscode' { * This command will disabled when the comment editor is empty. */ acceptInputCommand?: Command; - - - /** - * Dispose this comment thread. - * - * Once disposed, this comment thread will be removed from visible editors and Comment Panel when approriate. - */ - dispose(): void; - } - - /** - * Author information of a [comment](#Comment) - */ - - export interface CommentAuthorInformation { - /** - * The display name of the author of the comment - */ - name: string; - - /** - * The optional icon path for the author - */ - iconPath?: Uri; - } - - /** - * Author information of a [comment](#Comment) - */ - - export interface CommentAuthorInformation { - /** - * The display name of the author of the comment - */ - name: string; - - /** - * The optional icon path for the author - */ - iconPath?: Uri; } /** @@ -1071,22 +985,6 @@ declare module 'vscode' { */ id: string; - /** - * The human-readable comment body - */ - body: MarkdownString; - - /** - * The author information of the comment - */ - author: CommentAuthorInformation; - - /** - * Optional label describing the [Comment](#Comment) - * Label will be rendered next to authorName if exists. - */ - label?: string; - /** * The command to be executed if the comment is selected in the Comments Panel */ @@ -1106,16 +1004,6 @@ declare module 'vscode' { * Setter and getter for the contents of the comment input box */ value: string; - - /** - * The uri of the document comment input box has been created on - */ - resource: Uri; - - /** - * The range the comment input box is located within the document - */ - range: Range; } /** @@ -1128,36 +1016,12 @@ declare module 'vscode' { provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; } - /** - * Comment thread template for new comment thread creation. - */ - export interface CommentThreadTemplate { + export interface EmptyCommentThreadFactory { /** - * The human-readable label describing the [Comment Thread](#CommentThread) + * The method `createEmptyCommentThread` is called when users attempt to create new comment thread from the gutter or command palette. + * Extensions still need to call `createCommentThread` inside this call when appropriate. */ - readonly label: string; - - /** - * Optional accept input command - * - * `acceptInputCommand` is the default action rendered on Comment Widget, which is always placed rightmost. - * This command will be invoked when users the user accepts the value in the comment editor. - * This command will disabled when the comment editor is empty. - */ - readonly acceptInputCommand?: Command; - - /** - * Optional additonal commands. - * - * `additionalCommands` are the secondary actions rendered on Comment Widget. - */ - readonly additionalCommands?: Command[]; - - /** - * The command to be executed when users try to delete the comment thread. Currently, this is only called - * when the user collapses a comment thread that has no comments in it. - */ - readonly deleteCommand?: Command; + createEmptyCommentThread(document: TextDocument, range: Range): ProviderResult; } /** @@ -1165,41 +1029,12 @@ declare module 'vscode' { * provide users various ways to interact with comments. */ export interface CommentController { - /** - * The id of this comment controller. - */ - readonly id: string; - - /** - * The human-readable label of this comment controller. - */ - readonly label: string; /** * The active [comment input box](#CommentInputBox) or `undefined`. The active `inputBox` is the input box of * the comment thread widget that currently has focus. It's `undefined` when the focus is not in any CommentInputBox. */ - readonly inputBox: CommentInputBox | undefined; - - /** - * Optional comment thread template information. - * - * The comment controller will use this information to create the comment widget when users attempt to create new comment thread - * from the gutter or command palette. - * - * When users run `CommentThreadTemplate.acceptInputCommand` or `CommentThreadTemplate.additionalCommands`, extensions should create - * the approriate [CommentThread](#CommentThread). - * - * If not provided, users won't be able to create new comment threads in the editor. - */ - template?: CommentThreadTemplate; - - /** - * Optional commenting range provider. Provide a list [ranges](#Range) which support commenting to any given resource uri. - * - * If not provided and `emptyCommentThreadFactory` exits, users can leave comments in any document opened in the editor. - */ - commentingRangeProvider?: CommentingRangeProvider; + readonly inputBox?: CommentInputBox; /** * Create a [comment thread](#CommentThread). The comment thread will be displayed in visible text editors (if the resource matches) @@ -1212,6 +1047,16 @@ declare module 'vscode' { */ createCommentThread(id: string, uri: Uri, range: Range, comments: Comment[]): CommentThread; + /** + * Optional new comment thread factory. + */ + emptyCommentThreadFactory?: EmptyCommentThreadFactory; + + /** + * Optional reaction provider + */ + reactionProvider?: CommentReactionProvider; + /** * Dispose this comment controller. * diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 4b4896e6ece..df7790d8e3a 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -25,6 +25,8 @@ import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'v import { IRange, Range } from 'vs/editor/common/core/range'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider { private readonly _proxy: ExtHostCommentsShape; @@ -247,6 +249,10 @@ export class MainThreadCommentController { return this._id; } + get contextValue(): string { + return this._id; + } + get proxy(): ExtHostCommentsShape { return this._proxy; } @@ -377,31 +383,26 @@ export class MainThreadCommentController { } let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token); + let staticContribution = await this._proxy.$checkStaticContribution(this.handle); return { owner: this._uniqueId, label: this.label, threads: ret, - commentingRanges: commentingRanges ? - { - resource: resource, ranges: commentingRanges, newCommentThreadCallback: async (uri: UriComponents, range: IRange) => { - let threadHandle = await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token); + commentingRanges: commentingRanges ? { + resource: resource, + ranges: commentingRanges, + newCommentThreadCallback: staticContribution ? undefined : async (uri: UriComponents, range: IRange) => { + let threadHandle = await this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token); - if (threadHandle !== undefined) { - return this.getKnownThread(threadHandle); - } - - return; + if (threadHandle !== undefined) { + return this.getKnownThread(threadHandle); } - } : [], - draftMode: modes.DraftMode.NotSupported, - template: this._features.commentThreadTemplate ? { - controllerHandle: this.handle, - label: this._features.commentThreadTemplate.label, - acceptInputCommand: this._features.commentThreadTemplate.acceptInputCommand, - additionalCommands: this._features.commentThreadTemplate.additionalCommands, - deleteCommand: this._features.commentThreadTemplate.deleteCommand - } : undefined + + return; + } + } : [], + draftMode: modes.DraftMode.NotSupported }; } @@ -427,26 +428,8 @@ export class MainThreadCommentController { return ret; } - getCommentThreadFromTemplate(resource: UriComponents, range: IRange): MainThreadCommentThread { - let thread = new MainThreadCommentThread( - -1, - this.handle, - '', - '', - URI.revive(resource).toString(), - range - ); - - let template = this._features.commentThreadTemplate; - - if (template) { - thread.acceptInputCommand = template.acceptInputCommand; - thread.additionalCommands = template.additionalCommands; - thread.deleteCommand = template.deleteCommand; - thread.label = template.label; - } - - return thread; + createCommentThreadTemplate(resource: UriComponents, range: IRange): void { + this._proxy.$createCommentThreadTemplate(this.handle, resource, range); } toJSON(): any { @@ -471,18 +454,27 @@ export class MainThreadComments extends Disposable implements MainThreadComments private _input?: modes.CommentInput; private _openPanelListener: IDisposable | null; + private _commentThreadEmpty: IContextKey; + private _commentEmpty: IContextKey; + 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 + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); this._disposables = []; this._activeCommentThreadDisposables = []; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments); + + this._commentThreadEmpty = CommentContextKeys.commentThreadIsEmpty.bindTo(_commentService.contextKeyService); + this._commentEmpty = CommentContextKeys.commentIsEmpty.bindTo(_commentService.contextKeyService); + this._commentThreadEmpty.set(true); + this._commentEmpty.set(true); + this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => { let handle = (thread as MainThreadCommentThread).controllerHandle; let controller = this._commentControllers.get(handle); @@ -495,9 +487,12 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._activeCommentThread = thread as MainThreadCommentThread; controller.activeCommentThread = this._activeCommentThread; + this._commentThreadEmpty.set(controller.activeCommentThread.comments!.length === 0); + this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose this._input = input; this._proxy.$onCommentWidgetInputChange(handle, URI.parse(this._activeCommentThread!.resource), this._activeCommentThread!.range, this._input ? this._input.value : undefined); + this._commentEmpty.set(this._input ? true : this._input!.value.length === 0); })); await this._proxy.$onActiveCommentThreadChange(controller.handle, controller.activeCommentThread.commentThreadHandle); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index cff14ddb273..609c00305cf 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1210,9 +1210,11 @@ export interface ExtHostProgressShape { export interface ExtHostCommentsShape { $provideDocumentComments(handle: number, document: UriComponents): Promise; $createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise; + $createCommentThreadTemplate(commentControllerHandle: number, uriComponents: UriComponents, range: IRange): void; $onCommentWidgetInputChange(commentControllerHandle: number, document: UriComponents, range: IRange, input: string | undefined): Promise; $onActiveCommentThreadChange(commentControllerHandle: number, threadHandle: number | undefined): Promise; $provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise; + $checkStaticContribution(commentControllerHandle: number): Promise; $provideReactionGroup(commentControllerHandle: number): Promise; $toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise; $createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index a36f7409934..6b07a0eab22 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -88,6 +88,16 @@ export class ExtHostComments implements ExtHostCommentsShape { return commentController; } + $createCommentThreadTemplate(commentControllerHandle: number, uriComponents: UriComponents, range: IRange): void { + const commentController = this._commentControllers.get(commentControllerHandle); + + if (!commentController) { + return; + } + + commentController.$createCommentThreadTemplate(uriComponents, range); + } + $onCommentWidgetInputChange(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, input: string): Promise { const commentController = this._commentControllers.get(commentControllerHandle); @@ -164,7 +174,7 @@ export class ExtHostComments implements ExtHostCommentsShape { return Promise.resolve(); } - if (!(commentController as any).emptyCommentThreadFactory && !(commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread)) { + if (!(commentController as any).emptyCommentThreadFactory) { return Promise.resolve(); } @@ -173,13 +183,23 @@ export class ExtHostComments implements ExtHostCommentsShape { if ((commentController as any).emptyCommentThreadFactory) { return (commentController as any).emptyCommentThreadFactory!.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range)); } - - if (commentController.commentingRangeProvider && commentController.commentingRangeProvider.createEmptyCommentThread) { - return commentController.commentingRangeProvider.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range)); - } }).then(() => Promise.resolve()); } + $checkStaticContribution(commentControllerHandle: number): Promise { + const commentController = this._commentControllers.get(commentControllerHandle); + + if (!commentController) { + return Promise.resolve(false); + } + + if (!(commentController as any).emptyCommentThreadFactory) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + } + registerWorkspaceCommentProvider( extensionId: ExtensionIdentifier, provider: vscode.WorkspaceCommentProvider @@ -618,30 +638,6 @@ class ExtHostCommentController implements vscode.CommentController { private _threads: Map = new Map(); commentingRangeProvider?: vscode.CommentingRangeProvider & { createEmptyCommentThread: (document: vscode.TextDocument, range: types.Range) => Promise; }; - private _template: vscode.CommentThreadTemplate | undefined; - - get template(): vscode.CommentThreadTemplate | undefined { - return this._template; - } - - set template(newTemplate: vscode.CommentThreadTemplate | undefined) { - this._template = newTemplate; - - if (newTemplate) { - const acceptInputCommand = newTemplate.acceptInputCommand ? this._commandsConverter.toInternal(newTemplate.acceptInputCommand) : undefined; - const additionalCommands = newTemplate.additionalCommands ? newTemplate.additionalCommands.map(x => this._commandsConverter.toInternal(x)) : []; - const deleteCommand = newTemplate.deleteCommand ? this._commandsConverter.toInternal(newTemplate.deleteCommand) : undefined; - this._proxy.$updateCommentControllerFeatures(this.handle, { - commentThreadTemplate: { - label: newTemplate.label, - acceptInputCommand, - additionalCommands, - deleteCommand - } - }); - } - } - private _commentReactionProvider?: vscode.CommentReactionProvider; get reactionProvider(): vscode.CommentReactionProvider | undefined { @@ -666,8 +662,23 @@ class ExtHostCommentController implements vscode.CommentController { this._proxy.$registerCommentController(this.handle, _id, _label); } - createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread { - const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, id, resource, range, comments); + createCommentThread(resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread; + createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread; + createCommentThread(arg0: vscode.Uri | string, arg1: vscode.Uri | vscode.Range, arg2: vscode.Range | vscode.Comment[], arg3?: vscode.Comment[]): vscode.CommentThread { + if (typeof arg0 === 'string') { + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, arg0, arg1 as vscode.Uri, arg2 as vscode.Range, arg3 as vscode.Comment[]); + this._threads.set(commentThread.handle, commentThread); + return commentThread; + } else { + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, '', arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[]); + this._threads.set(commentThread.handle, commentThread); + return commentThread; + } + } + + $createCommentThreadTemplate(uriComponents: UriComponents, range: IRange) { + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, '', URI.revive(uriComponents), extHostTypeConverter.Range.to(range), []); + commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; this._threads.set(commentThread.handle, commentThread); return commentThread; } @@ -759,7 +770,8 @@ function convertFromComment(comment: modes.Comment): vscode.Comment { count: reaction.count, hasReacted: reaction.hasReacted }; - }) : undefined + }) : undefined, + mode: comment.mode ? comment.mode : modes.CommentMode.Preview }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d105723a885..cf0d658c20c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2297,6 +2297,12 @@ export enum CommentThreadCollapsibleState { */ Expanded = 1 } + +export enum CommentMode { + Editing = 0, + Preview = 1 +} + //#endregion @es5ClassCompat diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index bf05772230d..646324ba825 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -45,6 +45,10 @@ namespace schema { case 'statusBar/windowIndicator': return MenuId.StatusBarWindowIndicatorMenu; case 'view/title': return MenuId.ViewTitle; case 'view/item/context': return MenuId.ViewItemContext; + case 'commentThread/title': return MenuId.CommentThreadTitle; + case 'commentThread/actions': return MenuId.CommentThreadActions; + case 'comment/title': return MenuId.CommentTitle; + case 'comment/actions': return MenuId.CommentActions; } return undefined; @@ -182,7 +186,27 @@ namespace schema { description: localize('view.itemContext', "The contributed view item context menu"), type: 'array', items: menuItem - } + }, + 'commentThread/title': { + description: localize('commentThread.title', "The contributed comment thread title menu"), + type: 'array', + items: menuItem + }, + 'commentThread/actions': { + description: localize('commentThread.actions', "The contributed comment thread actions"), + type: 'array', + items: menuItem + }, + 'comment/title': { + description: localize('comment.title', "The contributed comment title menu"), + type: 'array', + items: menuItem + }, + 'comment/actions': { + description: localize('comment.actions', "The contributed comment actions"), + type: 'array', + items: menuItem + }, } }; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 2ad4fa84ca9..e30688fc1cd 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -771,6 +771,7 @@ export function createApiFactory( ColorInformation: extHostTypes.ColorInformation, ColorPresentation: extHostTypes.ColorPresentation, CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, + CommentMode: extHostTypes.CommentMode, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionList: extHostTypes.CompletionList, diff --git a/src/vs/workbench/contrib/comments/browser/commentMenus.ts b/src/vs/workbench/contrib/comments/browser/commentMenus.ts new file mode 100644 index 00000000000..4e57d92e625 --- /dev/null +++ b/src/vs/workbench/contrib/comments/browser/commentMenus.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IAction } from 'vs/base/common/actions'; +import { MainThreadCommentController } from 'vs/workbench/api/browser/mainThreadComments'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { Comment, CommentThread2 } from 'vs/editor/common/modes'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; + +export class CommentMenus implements IDisposable { + private titleMenu: IMenu; + private titleActions: IAction[] = []; + + private readonly disposables: IDisposable[] = []; + + constructor( + controller: MainThreadCommentController, + private contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + @IContextMenuService private readonly contextMenuService: IContextMenuService + ) { + const commentControllerKey = this.contextKeyService.createKey('commentController', undefined); + + commentControllerKey.set(controller.contextValue); + } + + getCommentThreadTitleActions(commentThread: CommentThread2): IAction[] { + return this.getActions(MenuId.CommentThreadTitle, commentThread).primary; + } + + getCommentThreadActions(commentThread: CommentThread2): IAction[] { + return []; + } + + getCommentTitleActions(comment: Comment): IAction[] { + return []; + } + + getCommentActions(comment: Comment): IAction[] { + return []; + } + + + private getActions(menuId: MenuId, thread: CommentThread2) { + const contextKeyService = this.contextKeyService.createScoped(); + // contextKeyService.createKey('commentThread', thread.threadId); + + const menu = this.menuService.createMenu(menuId, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + + fillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => true); + + menu.dispose(); + contextKeyService.dispose(); + + return result; + + } + + dispose(): void { + + } +} diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 296267ceb60..bcfe26b9316 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CommentThread, DocumentCommentProvider, CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread2 } from 'vs/editor/common/modes'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -14,6 +14,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { assign } from 'vs/base/common/objects'; import { ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel'; import { MainThreadCommentController } from 'vs/workbench/api/browser/mainThreadComments'; +import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContext } from 'vs/base/parts/quickopen/browser/quickOpenModel'; export const ICommentService = createDecorator('commentService'); @@ -42,11 +45,14 @@ export interface ICommentService { readonly onDidChangeInput: Event; readonly onDidSetDataProvider: Event; readonly onDidDeleteDataProvider: Event; + readonly contextKeyService: IContextKeyService; setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void; setWorkspaceComments(owner: string, commentsByResource: CommentThread[] | CommentThread2[]): void; removeWorkspaceComments(owner: string): void; registerCommentController(owner: string, commentControl: MainThreadCommentController): void; unregisterCommentController(owner: string): void; + createCommentThreadTemplate(owner: string, resource: URI, range: Range): void; + getCommentMenus(owner: string): CommentMenus; registerDataProvider(owner: string, commentProvider: DocumentCommentProvider): void; unregisterDataProvider(owner: string): void; updateComments(ownerId: string, event: CommentThreadChangedEvent): void; @@ -66,7 +72,6 @@ export interface ICommentService { deleteReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise; getReactionGroup(owner: string): CommentReaction[] | undefined; toggleReaction(owner: string, resource: URI, thread: CommentThread2, comment: Comment, reaction: CommentReaction): Promise; - getCommentThreadFromTemplate(owner: string, resource: URI, range: IRange, ): CommentThread2 | undefined; setActiveCommentThread(commentThread: CommentThread | null): void; setInput(input: string): void; } @@ -106,9 +111,15 @@ export class CommentService extends Disposable implements ICommentService { private _commentProviders = new Map(); private _commentControls = new Map(); + private _commentMenus = new Map(); + contextKeyService: IContextKeyService; - constructor() { + constructor( + @IInstantiationService protected instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService + ) { super(); + this.contextKeyService = contextKeyService.createScoped(); } setActiveCommentThread(commentThread: CommentThread | null) { @@ -141,6 +152,28 @@ export class CommentService extends Disposable implements ICommentService { this._onDidDeleteDataProvider.fire(owner); } + createCommentThreadTemplate(owner: string, resource: URI, range: Range): void { + const commentController = this._commentControls.get(owner); + + if (!commentController) { + return; + } + + commentController.createCommentThreadTemplate(resource, range); + } + + getCommentMenus(owner: string): CommentMenus { + if (this._commentMenus.get(owner)) { + return this._commentMenus.get(owner)!; + } + + let controller = this._commentControls.get(owner); + + let menu = this.instantiationService.createInstance(CommentMenus, controller!, this.contextKeyService); + this._commentMenus.set(owner, menu); + return menu; + } + registerDataProvider(owner: string, commentProvider: DocumentCommentProvider): void { this._commentProviders.set(owner, commentProvider); this._onDidSetDataProvider.fire(); @@ -256,16 +289,6 @@ export class CommentService extends Disposable implements ICommentService { } } - getCommentThreadFromTemplate(owner: string, resource: URI, range: IRange, ): CommentThread2 | undefined { - const commentController = this._commentControls.get(owner); - - if (commentController) { - return commentController.getCommentThreadFromTemplate(resource, range); - } - - return undefined; - } - getReactionGroup(owner: string): CommentReaction[] | undefined { const commentProvider = this._commentControls.get(owner); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index db53215e8e6..595b9c1a4b6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; -import { Action } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -38,6 +38,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { generateUuid } from 'vs/base/common/uuid'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; const COLLAPSE_ACTION_CLASS = 'expand-review-action octicon octicon-x'; @@ -86,6 +87,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget return this._draftMode; } + private _commentMenus: CommentMenus; + constructor( editor: ICodeEditor, private _owner: string, @@ -107,6 +110,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThreadDisposables = []; this._submitActionsDisposables = []; this._formActions = null; + this._commentMenus = this.commentService.getCommentMenus(this._owner); this.create(); this._styleElement = dom.createStyleSheet(this.domNode); @@ -203,7 +207,13 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._collapseAction = new Action('review.expand', nls.localize('label.collapse', "Collapse"), COLLAPSE_ACTION_CLASS, true, () => this.collapse()); - this._actionbarWidget.push(this._collapseAction, { label: false, icon: true }); + let secondaryActions: IAction[] = []; + if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { + secondaryActions = this._commentMenus.getCommentThreadTitleActions(this._commentThread as modes.CommentThread2); + } + + this._actionbarWidget.push([...secondaryActions, this._collapseAction], { label: false, icon: true }); + this._actionbarWidget.context = this._commentThread; } public collapse(): Promise { @@ -245,7 +255,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } - async update(commentThread: modes.CommentThread | modes.CommentThread2, replaceTemplate: boolean = false) { + async update(commentThread: modes.CommentThread | modes.CommentThread2) { const oldCommentsLen = this._commentElements.length; const newCommentsLen = commentThread.comments ? commentThread.comments.length : 0; @@ -294,26 +304,12 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThread = commentThread; this._commentElements = newCommentNodeList; - this.createThreadLabel(replaceTemplate); - - if (replaceTemplate) { - // since we are replacing the old comment thread, we need to rebind the listeners. - this._commentThreadDisposables.forEach(global => global.dispose()); - this._commentThreadDisposables = []; - } - - if (replaceTemplate) { - this.createTextModelListener(); - } + this.createThreadLabel(); if (this._formActions && this._commentEditor.hasModel()) { dom.clearNode(this._formActions); const model = this._commentEditor.getModel(); this.createCommentWidgetActions2(this._formActions, model); - - if (replaceTemplate) { - this.createCommentWidgetActionsListener(this._formActions, model); - } } // Move comment glyph widget and show position if the line has changed. @@ -368,7 +364,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentEditor.layout({ height: 5 * 18, width: widthInPixel - 54 /* margin 20px * 10 + scrollbar 14px*/ }); } - display(lineNumber: number, fromTemplate: boolean = false) { + display(lineNumber: number) { this._commentGlyph = new CommentGlyphWidget(this.editor, lineNumber); this._disposables.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); @@ -426,9 +422,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._formActions = dom.append(this._commentForm, dom.$('.form-actions')); if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { this.createCommentWidgetActions2(this._formActions, model); - if (!fromTemplate) { - this.createCommentWidgetActionsListener(this._formActions, model); - } + this.createCommentWidgetActionsListener(this._formActions, model); } else { this.createCommentWidgetActions(this._formActions, model); } @@ -827,14 +821,13 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } - private createThreadLabel(replaceTemplate: boolean = false) { + private createThreadLabel() { let label: string | undefined; if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { label = (this._commentThread as modes.CommentThread2).label; } - if (label === undefined && !replaceTemplate) { - // if it's for replacing the comment thread template, the comment thread widget title can be undefined as extensions may set it later + if (label === undefined) { if (this._commentThread.comments && this._commentThread.comments.length) { const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList); @@ -847,7 +840,6 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._headingLabel.innerHTML = strings.escape(label); this._headingLabel.setAttribute('aria-label', label); } - } private expandReplyArea() { diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 866bd19ca5a..1cd7b487f01 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -64,7 +64,7 @@ class CommentingRangeDecoration { return this._decorationId; } - constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private _template: modes.CommentThreadTemplate | undefined, private commentingRangesInfo?: modes.CommentingRanges) { + constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string | undefined, private _label: string | undefined, private _range: IRange, private _reply: modes.Command | undefined, commentingOptions: ModelDecorationOptions, private commentingRangesInfo?: modes.CommentingRanges) { const startLineNumber = _range.startLineNumber; const endLineNumber = _range.endLineNumber; let commentingRangeDecorations = [{ @@ -81,14 +81,13 @@ class CommentingRangeDecoration { } } - public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined } { + public getCommentAction(): { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined } { return { extensionId: this._extensionId, label: this._label, replyCommand: this._reply, ownerId: this._ownerId, - commentingRangesInfo: this.commentingRangesInfo, - template: this._template + commentingRangesInfo: this.commentingRangesInfo }; } @@ -125,11 +124,11 @@ class CommentingRangeDecorator { for (const info of commentInfos) { if (Array.isArray(info.commentingRanges)) { info.commentingRanges.forEach(range => { - commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, info.reply, this.decorationOptions, info.template)); + commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, info.reply, this.decorationOptions)); }); } else { (info.commentingRanges ? info.commentingRanges.ranges : []).forEach(range => { - commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, undefined, this.decorationOptions, info.template, info.commentingRanges as modes.CommentingRanges)); + commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, info.label, range, undefined, this.decorationOptions, info.commentingRanges as modes.CommentingRanges)); }); } } @@ -449,7 +448,7 @@ export class ReviewController implements IEditorContribution { let matchedNewCommentThreadZones = this._commentWidgets.filter(zoneWidget => zoneWidget.owner === e.owner && (zoneWidget.commentThread as any).commentThreadHandle === -1 && Range.equalsRange(zoneWidget.commentThread.range, thread.range)); if (matchedNewCommentThreadZones.length) { - matchedNewCommentThreadZones[0].update(thread, true); + matchedNewCommentThreadZones[0].update(thread); return; } @@ -469,22 +468,6 @@ export class ReviewController implements IEditorContribution { this._commentWidgets.push(zoneWidget); } - private addCommentThreadFromTemplate(lineNumber: number, ownerId: string): ReviewZoneWidget { - let templateCommentThread = this.commentService.getCommentThreadFromTemplate(ownerId, this.editor.getModel()!.uri, { - startLineNumber: lineNumber, - startColumn: 1, - endLineNumber: lineNumber, - endColumn: 1 - })!; - - templateCommentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; - templateCommentThread.comments = []; - - let templateReviewZoneWidget = this.instantiationService.createInstance(ReviewZoneWidget, this.editor, ownerId, templateCommentThread, '', modes.DraftMode.NotSupported); - - return templateReviewZoneWidget; - } - private addComment(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, draftMode: modes.DraftMode | undefined, pendingComment: string | null) { if (this._newCommentWidget) { this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position ? this._newCommentWidget.position.lineNumber : -1} before creating a new one.`); @@ -640,16 +623,16 @@ export class ReviewController implements IEditorContribution { const commentInfos = newCommentInfos.filter(info => info.ownerId === pick.id); if (commentInfos.length) { - const { replyCommand, ownerId, extensionId, commentingRangesInfo, template } = commentInfos[0]; - this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template); + const { replyCommand, ownerId, extensionId, commentingRangesInfo } = commentInfos[0]; + this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo); } }).then(() => { this._addInProgress = false; }); } } else { - const { replyCommand, ownerId, extensionId, commentingRangesInfo, template } = newCommentInfos[0]!; - this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template); + const { replyCommand, ownerId, extensionId, commentingRangesInfo } = newCommentInfos[0]!; + this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo); } return Promise.resolve(); @@ -668,11 +651,11 @@ export class ReviewController implements IEditorContribution { return picks; } - private getContextMenuActions(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined }[], lineNumber: number): (IAction | ContextSubMenu)[] { + private getContextMenuActions(commentInfos: { replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, label: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined }[], lineNumber: number): (IAction | ContextSubMenu)[] { const actions: (IAction | ContextSubMenu)[] = []; commentInfos.forEach(commentInfo => { - const { replyCommand, ownerId, extensionId, label, commentingRangesInfo, template } = commentInfo; + const { replyCommand, ownerId, extensionId, label, commentingRangesInfo } = commentInfo; actions.push(new Action( 'addCommentThread', @@ -680,7 +663,7 @@ export class ReviewController implements IEditorContribution { undefined, true, () => { - this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo, template); + this.addCommentAtLine2(lineNumber, replyCommand, ownerId, extensionId, commentingRangesInfo); return Promise.resolve(); } )); @@ -688,23 +671,10 @@ export class ReviewController implements IEditorContribution { return actions; } - public addCommentAtLine2(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined, template: modes.CommentThreadTemplate | undefined) { + public addCommentAtLine2(lineNumber: number, replyCommand: modes.Command | undefined, ownerId: string, extensionId: string | undefined, commentingRangesInfo: modes.CommentingRanges | undefined) { if (commentingRangesInfo) { let range = new Range(lineNumber, 1, lineNumber, 1); - if (template) { - // create comment widget through template - let commentThreadWidget = this.addCommentThreadFromTemplate(lineNumber, ownerId); - commentThreadWidget.display(lineNumber, true); - this._commentWidgets.push(commentThreadWidget); - commentThreadWidget.onDidClose(() => { - this._commentWidgets = this._commentWidgets.filter(zoneWidget => !( - zoneWidget.owner === commentThreadWidget.owner && - (zoneWidget.commentThread as any).commentThreadHandle === -1 && - Range.equalsRange(zoneWidget.commentThread.range, commentThreadWidget.commentThread.range) - )); - }); - this.processNextThreadToAdd(); - } else if (commentingRangesInfo.newCommentThreadCallback) { + if (commentingRangesInfo.newCommentThreadCallback) { return commentingRangesInfo.newCommentThreadCallback(this.editor.getModel()!.uri, range) .then(_ => { this.processNextThreadToAdd(); @@ -713,6 +683,11 @@ export class ReviewController implements IEditorContribution { this.notificationService.error(nls.localize('commentThreadAddFailure', "Adding a new comment thread failed: {0}.", e.message)); this.processNextThreadToAdd(); }); + } else { + // latest api, no comments creation callback + this.commentService.createCommentThreadTemplate(ownerId, this.editor.getModel()!.uri, range); + this.processNextThreadToAdd(); + return; } } else { const commentInfo = this._commentInfos.filter(info => info.owner === ownerId); diff --git a/src/vs/workbench/contrib/comments/common/commentContextKeys.ts b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts new file mode 100644 index 00000000000..b58660a19cc --- /dev/null +++ b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export namespace CommentContextKeys { + /** + * A context key that is set when the editor's text has focus (cursor is blinking). + */ + export const commentThreadIsEmpty = new RawContextKey('commentThreadIsEmpty', false); + /** + * A context key that is set when the editor's text or an editor's widget has focus. + */ + export const commentIsEmpty = new RawContextKey('commentIsEmpty', false); +} \ No newline at end of file