Skip to content

Commit

Permalink
Add support for deleting comments, #58078
Browse files Browse the repository at this point in the history
  • Loading branch information
Rachel Macfarlane authored Sep 18, 2018
1 parent 6ec7423 commit d1dbe4d
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/vs/editor/common/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@ export interface Comment {
readonly userName: string;
readonly gravatar: string;
readonly canEdit?: boolean;
readonly canDelete?: boolean;
readonly command?: Command;
}

Expand Down Expand Up @@ -1041,6 +1042,7 @@ export interface DocumentCommentProvider {
createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread>;
replyToCommentThread(resource: URI, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread>;
editComment(resource: URI, comment: Comment, text: string, token: CancellationToken): Promise<Comment>;
deleteComment(resource: URI, comment: Comment, token: CancellationToken): Promise<void>;
onDidChangeCommentThreads(): Event<CommentThreadChangedEvent>;
}

Expand Down
13 changes: 13 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,14 @@ declare module 'vscode' {
*/
canEdit?: boolean;

/**
* Whether the current user has permission to delete the comment.
*
* This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or
* if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given.
*/
canDelete?: boolean;

/**
* The command to be executed if the comment is selected in the Comments Panel
*/
Expand Down Expand Up @@ -742,6 +750,11 @@ declare module 'vscode' {
*/
editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise<Comment>;

/**
* Called when a user deletes the comment.
*/
deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise<void>;

/**
* Notify of updates to comment threads.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/api/electron-browser/mainThreadComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments
},
editComment: async (uri, comment, text, token) => {
return this._proxy.$editComment(handle, uri, comment, text);
},
deleteComment: async (uri, comment, token) => {
return this._proxy.$deleteComment(handle, uri, comment);
}
}
);
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,7 @@ export interface ExtHostCommentsShape {
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Thenable<modes.CommentThread>;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Thenable<modes.CommentThread>;
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Thenable<modes.Comment>;
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Thenable<void>;
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]>;
}

Expand Down
18 changes: 17 additions & 1 deletion src/vs/workbench/api/node/extHostComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ export class ExtHostComments implements ExtHostCommentsShape {
}).then(comment => convertToComment(provider, comment, this._commandsConverter));
}

$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Thenable<void> {
const data = this._documents.getDocumentData(URI.revive(uri));

if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
}

const provider = this._documentProviders.get(handle);
return asThenable(() => {
return provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None);
});
}

$provideDocumentComments(handle: number, uri: UriComponents): Thenable<modes.CommentInfo> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
Expand Down Expand Up @@ -178,18 +191,21 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
body: extHostTypeConverter.MarkdownString.to(comment.body),
userName: comment.userName,
gravatar: comment.gravatar,
canEdit: comment.canEdit
canEdit: comment.canEdit,
canDelete: comment.canDelete
};
}

function convertToComment(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
const canEdit = !!(provider as vscode.DocumentCommentProvider).editComment && vscodeComment.canEdit;
const canDelete = !!(provider as vscode.DocumentCommentProvider).deleteComment && vscodeComment.canDelete;
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName,
gravatar: vscodeComment.gravatar,
canEdit: canEdit,
canDelete: canDelete,
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null
};
}
36 changes: 35 additions & 1 deletion src/vs/workbench/parts/comments/electron-browser/commentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { SimpleCommentEditor } from 'vs/workbench/parts/comments/electron-browse
import { KeyCode } from 'vs/base/common/keyCodes';
import { isMacintosh } from 'vs/base/common/platform';
import { Selection } from 'vs/editor/common/core/selection';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Emitter, Event } from 'vs/base/common/event';

const UPDATE_COMMENT_LABEL = nls.localize('label.updateComment', "Update comment");
const UPDATE_IN_PROGRESS_LABEL = nls.localize('label.updatingComment', "Updating comment...");
Expand All @@ -43,6 +45,9 @@ export class CommentNode extends Disposable {
private _updateCommentButton: Button;
private _errorEditingContainer: HTMLElement;

private _deleteAction: Action;
private _onDidDelete = new Emitter<CommentNode>();

public get domNode(): HTMLElement {
return this._domNode;
}
Expand All @@ -56,7 +61,8 @@ export class CommentNode extends Disposable {
private instantiationService: IInstantiationService,
private commentService: ICommentService,
private modelService: IModelService,
private modeService: IModeService
private modeService: IModeService,
private dialogService: IDialogService
) {
super();

Expand All @@ -78,6 +84,10 @@ export class CommentNode extends Disposable {
this._clearTimeout = null;
}

public get onDidDelete(): Event<CommentNode> {
return this._onDidDelete.event;
}

private createHeader(commentDetailsContainer: HTMLElement): void {
const header = dom.append(commentDetailsContainer, dom.$('div.comment-title'));
const author = dom.append(header, dom.$('strong.author'));
Expand All @@ -89,6 +99,11 @@ export class CommentNode extends Disposable {
actions.push(this._editAction);
}

if (this.comment.canDelete) {
this._deleteAction = this.createDeleteAction();
actions.push(this._deleteAction);
}

if (actions.length) {
const actionsContainer = dom.append(header, dom.$('.comment-actions.hidden'));
const actionBar = new ActionBar(actionsContainer, {});
Expand Down Expand Up @@ -158,6 +173,25 @@ export class CommentNode extends Disposable {
}
}

private createDeleteAction(): Action {
return new Action('comment.delete', nls.localize('label.delete', "Delete"), 'octicon octicon-x', true, () => {
return this.dialogService.confirm({
title: nls.localize('deleteCommentTitle', "Delete Comment"),
message: nls.localize('confirmDelete', "Delete comment?"),
type: 'question',
primaryButton: nls.localize('label.delete', "Delete")
}).then(result => {
if (result.confirmed) {
this.commentService.deleteComment(this.owner, this.resource, this.comment).then((didDelete) => {
if (didDelete) {
this._onDidDelete.fire(this);
}
});
}
});
});
}

private createEditAction(commentDetailsContainer: HTMLElement): Action {
return new Action('comment.edit', nls.localize('label.edit', "Edit"), 'octicon octicon-pencil', true, () => {
this._body.classList.add('hidden');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

'use strict';

import * as nls from 'vs/nls';
import { CommentThread, DocumentCommentProvider, CommentThreadChangedEvent, CommentInfo, Comment } from 'vs/editor/common/modes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
Expand All @@ -13,6 +14,7 @@ import { URI } from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { keys } from 'vs/base/common/map';
import { CancellationToken } from 'vs/base/common/cancellation';
import { INotificationService } from 'vs/platform/notification/common/notification';

export const ICommentService = createDecorator<ICommentService>('commentService');

Expand Down Expand Up @@ -42,6 +44,7 @@ export interface ICommentService {
createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise<CommentThread>;
replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise<CommentThread>;
editComment(owner: number, resource: URI, comment: Comment, text: string): Promise<Comment>;
deleteComment(owner: number, resource: URI, comment: Comment): Promise<boolean>;
getComments(resource: URI): Promise<CommentInfo[]>;
}

Expand All @@ -65,7 +68,7 @@ export class CommentService extends Disposable implements ICommentService {

private _commentProviders = new Map<number, DocumentCommentProvider>();

constructor() {
constructor(@INotificationService private notificationService: INotificationService) {
super();
}

Expand Down Expand Up @@ -125,6 +128,21 @@ export class CommentService extends Disposable implements ICommentService {
return null;
}

deleteComment(owner: number, resource: URI, comment: Comment): Promise<boolean> {
const commentProvider = this._commentProviders.get(owner);

if (commentProvider) {
try {
return commentProvider.deleteComment(resource, comment, CancellationToken.None).then(() => true);
} catch (e) {
this.notificationService.error(nls.localize('commentDeletionError', "Deleting the comment failed: {0}.", e.message));
return Promise.resolve(false);
}
}

return Promise.resolve(false);
}

getComments(resource: URI): Promise<CommentInfo[]> {
const result = [];
for (const handle of keys(this._commentProviders)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { CommentNode } from 'vs/workbench/parts/comments/electron-browser/commentNode';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';

export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration';
const COLLAPSE_ACTION_CLASS = 'expand-review-action octicon octicon-x';
Expand Down Expand Up @@ -83,6 +84,7 @@ export class ReviewZoneWidget extends ZoneWidget {
private themeService: IThemeService,
private commentService: ICommentService,
private openerService: IOpenerService,
private dialogService: IDialogService,
editor: ICodeEditor,
owner: number,
commentThread: modes.CommentThread,
Expand Down Expand Up @@ -212,17 +214,7 @@ export class ReviewZoneWidget extends ZoneWidget {
lastCommentElement = oldCommentNode[0].domNode;
newCommentNodeList.unshift(oldCommentNode[0]);
} else {
let newElement = new CommentNode(
currentComment,
this.owner,
this.editor.getModel().uri,
this._markdownRenderer,
this.themeService,
this.instantiationService,
this.commentService,
this.modelService,
this.modeService);
this._disposables.push(newElement);
const newElement = this.createNewCommentNode(currentComment);

newCommentNodeList.unshift(newElement);
if (lastCommentElement) {
Expand Down Expand Up @@ -260,16 +252,8 @@ export class ReviewZoneWidget extends ZoneWidget {

this._commentElements = [];
for (let i = 0; i < this._commentThread.comments.length; i++) {
let newCommentNode = new CommentNode(this._commentThread.comments[i],
this.owner,
this.editor.getModel().uri,
this._markdownRenderer,
this.themeService,
this.instantiationService,
this.commentService,
this.modelService,
this.modeService);
this._disposables.push(newCommentNode);
const newCommentNode = this.createNewCommentNode(this._commentThread.comments[i]);

this._commentElements.push(newCommentNode);
this._commentsElement.appendChild(newCommentNode.domNode);
}
Expand Down Expand Up @@ -357,6 +341,43 @@ export class ReviewZoneWidget extends ZoneWidget {
}
}

private createNewCommentNode(comment: modes.Comment): CommentNode {
let newCommentNode = new CommentNode(
comment,
this.owner,
this.editor.getModel().uri,
this._markdownRenderer,
this.themeService,
this.instantiationService,
this.commentService,
this.modelService,
this.modeService,
this.dialogService);

this._disposables.push(newCommentNode);
this._disposables.push(newCommentNode.onDidDelete(deletedNode => {
const deletedNodeId = deletedNode.comment.commentId;
const deletedElementIndex = arrays.firstIndex(this._commentElements, commentNode => commentNode.comment.commentId === deletedNodeId);
if (deletedElementIndex > -1) {
this._commentElements.splice(deletedElementIndex, 1);
}

const deletedCommentIndex = arrays.firstIndex(this._commentThread.comments, comment => comment.commentId === deletedNodeId);
if (deletedCommentIndex > -1) {
this._commentThread.comments.splice(deletedCommentIndex, 1);
}

this._commentsElement.removeChild(deletedNode.domNode);
deletedNode.dispose();

if (this._commentThread.comments.length === 0) {
this.dispose();
}
}));

return newCommentNode;
}

private async createComment(lineNumber: number): Promise<void> {
try {
let newCommentThread;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { IModelDecorationOptions } from 'vs/editor/common/model';
import { Color, RGBA } from 'vs/base/common/color';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';

export const ctxReviewPanelVisible = new RawContextKey<boolean>('reviewPanelVisible', false);

Expand Down Expand Up @@ -184,7 +185,8 @@ export class ReviewController implements IEditorContribution {
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@ICodeEditorService private codeEditorService: ICodeEditorService,
@IOpenerService private openerService: IOpenerService
@IOpenerService private openerService: IOpenerService,
@IDialogService private dialogService: IDialogService
) {
this.editor = editor;
this.globalToDispose = [];
Expand Down Expand Up @@ -356,7 +358,7 @@ export class ReviewController implements IEditorContribution {
}
});
added.forEach(thread => {
let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, e.owner, thread, {});
let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.editor, e.owner, thread, {});
zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions);
this._commentWidgets.push(zoneWidget);
this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread);
Expand All @@ -378,7 +380,7 @@ export class ReviewController implements IEditorContribution {
// add new comment
this._reviewPanelVisible.set(true);
const { replyCommand, ownerId } = newCommentInfo;
this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, ownerId, {
this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.editor, ownerId, {
threadId: null,
resource: null,
comments: [],
Expand Down Expand Up @@ -505,7 +507,7 @@ export class ReviewController implements IEditorContribution {

this._commentInfos.forEach(info => {
info.threads.forEach(thread => {
let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, info.owner, thread, {});
let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.editor, info.owner, thread, {});
zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions);
this._commentWidgets.push(zoneWidget);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}

.monaco-editor .review-widget .body .review-comment .comment-actions .action-item {
width: 30px;
width: 22px;
}

.monaco-editor .review-widget .body .review-comment .comment-title {
Expand Down

0 comments on commit d1dbe4d

Please sign in to comment.