diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 1c56cb6b5d4ab4c9993a87298c9e1b0514a199c3..f91d9b30784d7f59658143a290f6a4575904c038 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1239,9 +1239,16 @@ export interface CommentWidget { /** * @internal */ +export interface CommentInput { + value: string; + uri: URI; +} +/** + * @internal + */ export interface CommentThread2 { - commentThreadHandle: number; // use optional type for now to avoid breaking existing api + commentThreadHandle: number; extensionId: string; threadId: string; resource: string; @@ -1249,8 +1256,8 @@ export interface CommentThread2 { comments: Comment[]; onDidChangeComments: Event; collapsibleState?: CommentThreadCollapsibleState; - input: string; - onDidChangeInput: Event; + input: CommentInput; + onDidChangeInput: Event; acceptInputCommands: Command[]; onDidChangeAcceptInputCommands: Event; } @@ -1321,22 +1328,22 @@ export interface CommentThreadChangedEvent { /** * Added comment threads. */ - readonly added: CommentThread[]; + readonly added: (CommentThread | CommentThread2)[]; /** * Removed comment threads. */ - readonly removed: CommentThread[]; + readonly removed: (CommentThread | CommentThread2)[]; /** * Changed comment threads. */ - readonly changed: CommentThread[]; + readonly changed: (CommentThread | CommentThread2)[]; /** * changed draft mode. */ - readonly draftMode: DraftMode; + readonly draftMode?: DraftMode; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f101c1248f3ef6a26d93f2766c9a87a5281121c7..acebf46c3c4746f7553112c2abc3e19abc5b0b53 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -799,6 +799,7 @@ declare module 'vscode' { * Whether the thread should be collapsed or expanded when opening the document. Defaults to Collapsed. */ collapsibleState?: CommentThreadCollapsibleState; + dispose?(): void; } /** @@ -966,8 +967,8 @@ declare module 'vscode' { readonly id: string; readonly label: string; /** - * The active (focused) comment widget. - */ + * The active (focused) comment widget. + */ readonly widget?: CommentWidget; createCommentThread(id: string, resource: Uri, range: Range, comments: Comment[], acceptInputCommands: Command[], collapsibleState?: CommentThreadCollapsibleState): CommentThread; createCommentingRanges(resource: Uri, ranges: Range[], acceptInputCommands: Command[]): CommentingRanges; diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 80b1024d003775fb79b6ea8c25cf8cb0f48a1acb..382bb4754f1808f960f2011433189514b2fa0976 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -81,33 +81,18 @@ export class MainThreadDocumentCommentProvider implements modes.DocumentCommentP } export class MainThreadCommentThread implements modes.CommentThread2 { - private _input: string = ''; - get input(): string { + private _input?: modes.CommentInput; + get input(): modes.CommentInput | undefined { return this._input; } - set input(value: string) { + set input(value: modes.CommentInput | undefined) { this._input = value; this._onDidChangeInput.fire(value); } - private _onDidChangeInput = new Emitter(); - get onDidChangeInput(): Event { return this._onDidChangeInput.event; } - - private _activeComment?: modes.Comment; - - get activeComment(): modes.Comment { - return this._activeComment; - } - - set activeComment(comment: modes.Comment | undefined) { - this._activeComment = comment; - this._onDidChangeActiveComment.fire(comment); - } - - private _onDidChangeActiveComment = new Emitter(); - get onDidChangeActiveComment(): Event { return this._onDidChangeActiveComment.event; } - + private _onDidChangeInput = new Emitter(); + get onDidChangeInput(): Event { return this._onDidChangeInput.event; } public get comments(): modes.Comment[] { return this._comments; @@ -200,10 +185,23 @@ export class MainThreadCommentControl { return this._handle; } + get id(): string { + return this._id; + } + + get proxy(): ExtHostCommentsShape { + return this._proxy; + } + + get label(): string { + return this._label; + } + private _threads: Map = new Map(); private _commentingRanges: Map = new Map(); constructor( private _proxy: ExtHostCommentsShape, + private _commentService: ICommentService, private _handle: number, private _id: string, private _label: string @@ -223,6 +221,12 @@ export class MainThreadCommentControl { ); this._threads.set(commentThreadHandle, thread); + this._commentService.updateComments(`${this.handle}`, { + added: [thread], + removed: [], + changed: [], + draftMode: modes.DraftMode.NotSupported + }); return thread; } @@ -230,6 +234,14 @@ export class MainThreadCommentControl { deleteCommentThread(commentThreadHandle: number) { let thread = this._threads.get(commentThreadHandle); this._threads.delete(commentThreadHandle); + + this._commentService.updateComments(`${this.handle}`, { + added: [], + removed: [thread], + changed: [], + draftMode: modes.DraftMode.NotSupported + }); + thread.dispose(); } @@ -277,7 +289,11 @@ export class MainThreadCommentControl { updateInput(commentThreadHandle: number, input: string) { let thread = this._threads.get(commentThreadHandle); - thread.input = input; + let commentInput = thread.input; + if (commentInput) { + commentInput.value = input; + thread.input = commentInput; + } } getDocumentComments(resource: URI) { @@ -288,7 +304,7 @@ export class MainThreadCommentControl { } } - return { + return { owner: String(this.handle), threads: ret, commentingRanges: [], @@ -307,6 +323,7 @@ export class MainThreadCommentControl { @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private _disposables: IDisposable[]; + private _activeCommentThreadDisposables: IDisposable[]; private _proxy: ExtHostCommentsShape; private _documentProviders = new Map(); private _workspaceProviders = new Map(); @@ -315,7 +332,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments private _activeCommentThread?: MainThreadCommentThread; private _activeComment?: modes.Comment; - private _input?: string; + private _input?: modes.CommentInput; private _openPanelListener: IDisposable | null; constructor( @@ -328,6 +345,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments ) { super(); this._disposables = []; + this._activeCommentThreadDisposables = []; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments); this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => { let control = (thread as MainThreadCommentThread).control; @@ -336,26 +354,28 @@ export class MainThreadComments extends Disposable implements MainThreadComments return; } + this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables); this._activeCommentThread = thread as MainThreadCommentThread; - this._activeCommentThread.onDidChangeInput(input => { // todo, dispose + this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose this._input = input; - this._proxy.$onActiveCommentWidgetChange(control.handle, this._activeCommentThread, this._activeComment, this._input); - }); - - this._activeCommentThread.onDidChangeActiveComment(comment => { // todo, dispose - this._activeComment = comment; - this._proxy.$onActiveCommentWidgetChange(control.handle, this._activeCommentThread, this._activeComment, this._input); - }); + this._proxy.$onActiveCommentWidgetChange(control.handle, this._activeCommentThread, this._activeComment, this._input ? this._input.value : undefined); + })); - await this._proxy.$onActiveCommentWidgetChange(control.handle, this._activeCommentThread, this._activeComment, this._input); + await this._proxy.$onActiveCommentWidgetChange(control.handle, this._activeCommentThread, this._activeComment, this._input ? this._input.value : undefined); })); } $registerCommentControl(handle: number, id: string, label: string): void { - const provider = new MainThreadCommentControl(this._proxy, handle, id, label); + const provider = new MainThreadCommentControl(this._proxy, this._commentService, handle, id, label); this._commentService.registerCommentControl(String(handle), provider); this._commentControls.set(handle, provider); + + const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID); + if (!commentsPanelAlreadyConstructed) { + this.registerPanel(commentsPanelAlreadyConstructed); + } + this._commentService.setWorkspaceComments(String(handle), []); } $createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], commands: modes.Command[], collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined { @@ -459,6 +479,18 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._commentService.registerDataProvider(providerId, handler); } + private registerPanel(commentsPanelAlreadyConstructed: boolean) { + if (!commentsPanelAlreadyConstructed) { + Registry.as(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 @@ -492,13 +524,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._handlers.set(handle, providerId); const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID); - Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( - CommentsPanel, - COMMENTS_PANEL_ID, - COMMENTS_PANEL_TITLE, - 'commentsPanel', - 10 - )); + if (!commentsPanelAlreadyConstructed) { + this.registerPanel(commentsPanelAlreadyConstructed); + } const openPanel = this._configurationService.getValue('comments').openPanel; @@ -598,6 +626,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments 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)); diff --git a/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts b/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts index e85adf64e4a3caa064f46a1a3411eb199ebb208e..1ece57bc8d99dc2e2dfa966ea2900c289247e475 100644 --- a/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/electron-browser/commentNode.ts @@ -305,7 +305,6 @@ export class CommentNode extends Disposable { this._commentEditor.layout({ width: container.clientWidth - 14, height: 90 }); this._commentEditor.focus(); - const lastLine = this._commentEditorModel.getLineCount(); const lastColumn = this._commentEditorModel.getLineContent(lastLine).length + 1; this._commentEditor.setSelection(new Selection(lastLine, lastColumn, lastLine, lastColumn)); @@ -319,11 +318,28 @@ export class CommentNode extends Disposable { let commentThread = this.commentThread as modes.CommentThread2; if (commentThread.commentThreadHandle) { - commentThread.input = this.comment.body.value; + commentThread.input = { + uri: this._commentEditor.getModel().uri, + value: this.comment.body.value + }; + this.commentService.setActiveCommentThread(commentThread); + + this._commentEditorDisposables.push(this._commentEditor.onDidFocusEditorWidget(() => { + commentThread.input = { + uri: this._commentEditor.getModel().uri, + value: this.comment.body.value + }; + this.commentService.setActiveCommentThread(commentThread); + })); + this._commentEditorDisposables.push(this._commentEditor.onDidChangeModelContent(e => { - let newVal = this._commentEditor.getValue(); - if (newVal !== commentThread.input) { - commentThread.input = newVal; + if (commentThread.input && this._commentEditor.getModel().uri === commentThread.input.uri) { + let newVal = this._commentEditor.getValue(); + if (newVal !== commentThread.input.value) { + let input = commentThread.input; + input.value = newVal; + commentThread.input = input; + } } })); } @@ -356,7 +372,10 @@ export class CommentNode extends Disposable { if (useCommand) { let commentThread = this.commentThread as modes.CommentThread2; - commentThread.input = newBody; + commentThread.input = { + uri: this._commentEditor.getModel().uri, + value: newBody + }; this.commentService.setActiveCommentThread(commentThread); let commandId = this.comment.editCommand!.id; let args = this.comment.editCommand!.arguments || []; @@ -480,7 +499,6 @@ export class CommentNode extends Disposable { } update(newComment: modes.Comment) { - this.comment = newComment; if (newComment.body !== this.comment.body) { this._body.removeChild(this._md); @@ -488,6 +506,8 @@ export class CommentNode extends Disposable { this._body.appendChild(this._md); } + this.comment = newComment; + if (newComment.isDraft) { this._isPendingLabel.innerText = 'Pending'; } else { diff --git a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts index 4cdcd8125a92c9b5708f9dc7ed4b3be778382adc..4c66151d0e2daecd7b5fedb4cb4481a5cff8fd5c 100644 --- a/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/electron-browser/commentThreadWidget.ts @@ -258,6 +258,7 @@ export class ReviewZoneWidget extends ZoneWidget { let currentComment = commentThread.comments[i]; let oldCommentNode = this._commentElements.filter(commentNode => commentNode.comment.commentId === currentComment.commentId); if (oldCommentNode.length) { + oldCommentNode[0].update(currentComment); lastCommentElement = oldCommentNode[0].domNode; newCommentNodeList.unshift(oldCommentNode[0]); } else { @@ -342,18 +343,36 @@ export class ReviewZoneWidget extends ZoneWidget { this._localToDispose.push(this._commentEditor); this._localToDispose.push(this._commentEditor.getModel().onDidChangeContent(() => this.setCommentEditorDecorations())); if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { + this._localToDispose.push(this._commentEditor.onDidFocusEditorWidget(() => { + let commentThread = this._commentThread as modes.CommentThread2; + commentThread.input = { + uri: this._commentEditor.getModel().uri, + value: this._commentEditor.getValue() + }; + this.commentService.setActiveCommentThread(this._commentThread); + })); + this._localToDispose.push(this._commentEditor.getModel().onDidChangeContent(() => { let modelContent = this._commentEditor.getValue(); - if ((this._commentThread as modes.CommentThread2).input !== modelContent) { - (this._commentThread as modes.CommentThread2).input = modelContent; + let thread = (this._commentThread as modes.CommentThread2); + if (thread.input.uri === this._commentEditor.getModel().uri && thread.input.value !== modelContent) { + let newInput: modes.CommentInput = thread.input; + newInput.value = modelContent; + thread.input = newInput; } })); this._localToDispose.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => { - if (this._commentEditor.getValue() !== input) { - this._commentEditor.setValue(input); + let thread = (this._commentThread as modes.CommentThread2); + + if (thread.input.uri !== this._commentEditor.getModel().uri) { + return; + } - if (input === '') { + if (this._commentEditor.getValue() !== input.value) { + this._commentEditor.setValue(input.value); + + if (input.value === '') { this._pendingComment = ''; if (dom.hasClass(this._commentForm, 'expand')) { dom.removeClass(this._commentForm, 'expand'); @@ -554,9 +573,11 @@ export class ReviewZoneWidget extends ZoneWidget { let commandId = command.id; let args = command.arguments || []; this._localToDispose.push(button.onDidClick(async () => { - commentThread.input = this._commentEditor.getValue(); + commentThread.input = { + uri: this._commentEditor.getModel().uri, + value: this._commentEditor.getValue() + }; this.commentService.setActiveCommentThread(this._commentThread); - await this.commandService.executeCommand(commandId, ...args); })); });