diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index f8414f2b54e..0be0a184deb 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -88,6 +88,7 @@ export class CommentNode extends Disposable { constructor( private commentThread: languages.CommentThread, public comment: languages.Comment, + private pendingEdit: string | undefined, private owner: string, private resource: URI, private parentThread: ICommentThreadWidget, @@ -139,6 +140,10 @@ export class CommentNode extends Disposable { this._register(dom.addDisposableListener(this._domNode, dom.EventType.CONTEXT_MENU, e => { return this.onContextMenu(e); })); + + if (pendingEdit) { + this.switchToEditMode(); + } } private createScroll(container: HTMLElement, body: HTMLElement) { @@ -431,12 +436,13 @@ export class CommentNode extends Disposable { private createCommentEditor(editContainer: HTMLElement): void { const container = dom.append(editContainer, dom.$('.edit-textarea')); - this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions(this.configurationService), this.parentThread); + this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions(this.configurationService), this._contextKeyService, this.parentThread); const resource = URI.parse(`comment:commentinput-${this.comment.uniqueIdInThread}-${Date.now()}.md`); this._commentEditorModel = this.modelService.createModel('', this.languageService.createByFilepathOrFirstLine(resource), resource, false); this._commentEditor.setModel(this._commentEditorModel); - this._commentEditor.setValue(this.commentBodyValue); + this._commentEditor.setValue(this.pendingEdit ?? this.commentBodyValue); + this.pendingEdit = undefined; this._commentEditor.layout({ width: container.clientWidth - 14, height: 90 }); this._commentEditor.focus(); @@ -480,6 +486,14 @@ export class CommentNode extends Disposable { this._register(this._commentEditorModel); } + getPendingEdit(): string | undefined { + const model = this._commentEditor?.getModel(); + if (model && model.getValueLength() > 0) { + return model.getValue(); + } + return undefined; + } + private removeCommentEditor() { this.isEditing = false; if (this._editAction) { diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index e6821f828ef..d6c2f10dff6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -54,7 +54,7 @@ export class CommentReply extends Disposable { private _contextKeyService: IContextKeyService, private _commentMenus: CommentMenus, private _commentOptions: languages.CommentOptions | undefined, - private _pendingComment: string | null, + private _pendingComment: string | undefined, private _parentThread: ICommentThreadWidget, private _actionRunDelegate: (() => void) | null, @ICommentService private commentService: ICommentService, @@ -66,7 +66,7 @@ export class CommentReply extends Disposable { super(); this.form = dom.append(container, dom.$('.comment-form')); - this.commentEditor = this._register(this._scopedInstatiationService.createInstance(SimpleCommentEditor, this.form, SimpleCommentEditor.getEditorOptions(configurationService), this._parentThread)); + this.commentEditor = this._register(this._scopedInstatiationService.createInstance(SimpleCommentEditor, this.form, SimpleCommentEditor.getEditorOptions(configurationService), _contextKeyService, this._parentThread)); this.commentEditorIsEmpty = CommentContextKeys.commentIsEmpty.bindTo(this._contextKeyService); this.commentEditorIsEmpty.set(!this._pendingComment); @@ -126,14 +126,14 @@ export class CommentReply extends Disposable { } } - public getPendingComment(): string | null { + public getPendingComment(): string | undefined { const model = this.commentEditor.getModel(); if (model && model.getValueLength() > 0) { // checking length is cheap return model.getValue(); } - return null; + return undefined; } public layout(widthInPixel: number) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts index f8c9397134f..047d6c8cb1f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts @@ -47,6 +47,7 @@ export class CommentThreadBody extends D readonly container: HTMLElement, private _options: IMarkdownRendererOptions, private _commentThread: languages.CommentThread, + private _pendingEdits: { [key: number]: string } | undefined, private _scopedInstatiationService: IInstantiationService, private _parentCommentThreadWidget: ICommentThreadWidget, @ICommentService private commentService: ICommentService, @@ -125,6 +126,20 @@ export class CommentThreadBody extends D }); } + getPendingEdits(): { [key: number]: string } { + const pendingEdits: { [key: number]: string } = {}; + this._commentElements.forEach(element => { + if (element.isEditing) { + const pendingEdit = element.getPendingEdit(); + if (pendingEdit) { + pendingEdits[element.comment.uniqueIdInThread] = pendingEdit; + } + } + }); + + return pendingEdits; + } + getCommentCoords(commentUniqueId: number): { thread: dom.IDomNodePagePosition; comment: dom.IDomNodePagePosition } | undefined { const matchedNode = this._commentElements.filter(commentNode => commentNode.comment.uniqueIdInThread === commentUniqueId); if (matchedNode && matchedNode.length) { @@ -236,6 +251,7 @@ export class CommentThreadBody extends D const newCommentNode = this._scopedInstatiationService.createInstance(CommentNode, this._commentThread, comment, + this._pendingEdits ? this._pendingEdits[comment.uniqueIdInThread!] : undefined, this.owner, this.parentResourceUri, this._parentCommentThreadWidget, diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 7c9a90a09b5..49ef23f954b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -56,7 +56,8 @@ export class CommentThreadWidget extends private _contextKeyService: IContextKeyService, private _scopedInstatiationService: IInstantiationService, private _commentThread: languages.CommentThread, - private _pendingComment: string | null, + private _pendingComment: string | undefined, + private _pendingEdits: { [key: number]: string } | undefined, private _markdownOptions: IMarkdownRendererOptions, private _commentOptions: languages.CommentOptions | undefined, private _containerDelegate: { @@ -97,6 +98,7 @@ export class CommentThreadWidget extends bodyElement, this._markdownOptions, this._commentThread, + this._pendingEdits, this._scopedInstatiationService, this ) as unknown as CommentThreadBody; @@ -259,12 +261,16 @@ export class CommentThreadWidget extends return this._body.getCommentCoords(commentUniqueId); } - getPendingComment(): string | null { + getPendingEdits(): { [key: number]: string } { + return this._body.getPendingEdits(); + } + + getPendingComment(): string | undefined { if (this._commentReply) { return this._commentReply.getPendingComment(); } - return null; + return undefined; } getDimensions() { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 882b3371628..48eb3591882 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -117,7 +117,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget editor: ICodeEditor, private _owner: string, private _commentThread: languages.CommentThread, - private _pendingComment: string | null, + private _pendingComment: string | undefined, + private _pendingEdits: { [key: number]: string } | undefined, @IInstantiationService instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @ICommentService private commentService: ICommentService, @@ -196,8 +197,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } - public getPendingComment(): string | null { - return this._commentThreadWidget.getPendingComment(); + public getPendingComments(): { newComment: string | undefined; edits: { [key: number]: string } } { + return { + newComment: this._commentThreadWidget.getPendingComment(), + edits: this._commentThreadWidget.getPendingEdits() + }; } protected _fillContainer(container: HTMLElement): void { @@ -211,6 +215,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._scopedInstantiationService, this._commentThread as unknown as languages.CommentThread, this._pendingComment, + this._pendingEdits, { editor: this.editor, codeBlockFontSize: '' }, this._commentOptions, { diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 3396c53ee4d..3e109f9e4f9 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -296,7 +296,8 @@ export class CommentController implements IEditorContribution { private _emptyThreadsToAddQueue: [Range, IEditorMouseEvent | undefined][] = []; private _computeCommentingRangePromise!: CancelablePromise | null; private _computeCommentingRangeScheduler!: Delayer> | null; - private _pendingCommentCache: { [key: string]: { [key: string]: string } }; + private _pendingNewCommentCache: { [key: string]: { [key: string]: string } }; + private _pendingEditsCache: { [key: string]: { [key: string]: { [key: number]: string } } }; // owner -> threadId -> uniqueIdInThread -> pending comment private _editorDisposables: IDisposable[] = []; private _activeCursorHasCommentingRange: IContextKey; @@ -314,7 +315,8 @@ export class CommentController implements IEditorContribution { ) { this._commentInfos = []; this._commentWidgets = []; - this._pendingCommentCache = {}; + this._pendingNewCommentCache = {}; + this._pendingEditsCache = {}; this._computePromise = null; this._activeCursorHasCommentingRange = ActiveCursorHasCommentingRange.bindTo(contextKeyService); @@ -336,7 +338,8 @@ export class CommentController implements IEditorContribution { this.globalToDispose.add(this._commentThreadRangeDecorator = new CommentThreadRangeDecorator(this.commentService)); this.globalToDispose.add(this.commentService.onDidDeleteDataProvider(ownerId => { - delete this._pendingCommentCache[ownerId]; + delete this._pendingNewCommentCache[ownerId]; + delete this._pendingEditsCache[ownerId]; this.beginCompute(); })); this.globalToDispose.add(this.commentService.onDidSetDataProvider(_ => this.beginCompute())); @@ -676,8 +679,9 @@ export class CommentController implements IEditorContribution { return; } - const pendingCommentText = this._pendingCommentCache[e.owner] && this._pendingCommentCache[e.owner][thread.threadId!]; - this.displayCommentThread(e.owner, thread, pendingCommentText); + const pendingCommentText = this._pendingNewCommentCache[e.owner] && this._pendingNewCommentCache[e.owner][thread.threadId!]; + const pendingEdits = this._pendingEditsCache[e.owner] && this._pendingEditsCache[e.owner][thread.threadId!]; + this.displayCommentThread(e.owner, thread, pendingCommentText, pendingEdits); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); this.tryUpdateReservedSpace(); }); @@ -701,14 +705,14 @@ export class CommentController implements IEditorContribution { return undefined; } - private displayCommentThread(owner: string, thread: languages.CommentThread, pendingComment: string | null): void { + private displayCommentThread(owner: string, thread: languages.CommentThread, pendingComment: string | undefined, pendingEdits: { [key: number]: string } | undefined): void { if (!this.editor) { return; } if (this.isEditorInlineOriginal(this.editor)) { return; } - const zoneWidget = this.instantiationService.createInstance(ReviewZoneWidget, this.editor, owner, thread, pendingComment); + const zoneWidget = this.instantiationService.createInstance(ReviewZoneWidget, this.editor, owner, thread, pendingComment, pendingEdits); zoneWidget.display(thread.range); this._commentWidgets.push(zoneWidget); this.openCommentsView(thread); @@ -943,19 +947,25 @@ export class CommentController implements IEditorContribution { this.removeCommentWidgetsAndStoreCache(); this._commentInfos.forEach(info => { - const providerCacheStore = this._pendingCommentCache[info.owner]; + const providerCacheStore = this._pendingNewCommentCache[info.owner]; + const providerEditsCacheStore = this._pendingEditsCache[info.owner]; info.threads = info.threads.filter(thread => !thread.isDisposed); info.threads.forEach(thread => { - let pendingComment: string | null = null; + let pendingComment: string | undefined = undefined; if (providerCacheStore) { pendingComment = providerCacheStore[thread.threadId!]; } - if (pendingComment) { + let pendingEdits: { [key: number]: string } | undefined = undefined; + if (providerEditsCacheStore) { + pendingEdits = providerEditsCacheStore[thread.threadId!]; + } + + if (pendingComment || pendingEdits) { thread.collapsibleState = languages.CommentThreadCollapsibleState.Expanded; } - this.displayCommentThread(info.owner, thread, pendingComment); + this.displayCommentThread(info.owner, thread, pendingComment, pendingEdits); }); }); @@ -974,8 +984,9 @@ export class CommentController implements IEditorContribution { private removeCommentWidgetsAndStoreCache() { if (this._commentWidgets) { this._commentWidgets.forEach(zone => { - const pendingComment = zone.getPendingComment(); - const providerCacheStore = this._pendingCommentCache[zone.owner]; + const pendingComments = zone.getPendingComments(); + const pendingNewComment = pendingComments.newComment; + const providerNewCommentCacheStore = this._pendingNewCommentCache[zone.owner]; let lastCommentBody; if (zone.commentThread.comments && zone.commentThread.comments.length) { @@ -986,18 +997,29 @@ export class CommentController implements IEditorContribution { lastCommentBody = lastComment.body.value; } } - if (pendingComment && (pendingComment !== lastCommentBody)) { - if (!providerCacheStore) { - this._pendingCommentCache[zone.owner] = {}; + if (pendingNewComment && (pendingNewComment !== lastCommentBody)) { + if (!providerNewCommentCacheStore) { + this._pendingNewCommentCache[zone.owner] = {}; } - this._pendingCommentCache[zone.owner][zone.commentThread.threadId!] = pendingComment; + this._pendingNewCommentCache[zone.owner][zone.commentThread.threadId!] = pendingNewComment; } else { - if (providerCacheStore) { - delete providerCacheStore[zone.commentThread.threadId!]; + if (providerNewCommentCacheStore) { + delete providerNewCommentCacheStore[zone.commentThread.threadId!]; } } + const pendingEdits = pendingComments.edits; + const providerEditsCacheStore = this._pendingEditsCache[zone.owner]; + if (Object.keys(pendingEdits).length > 0) { + if (!providerEditsCacheStore) { + this._pendingEditsCache[zone.owner] = {}; + } + this._pendingEditsCache[zone.owner][zone.commentThread.threadId!] = pendingEdits; + } else if (providerEditsCacheStore) { + delete providerEditsCacheStore[zone.commentThread.threadId!]; + } + zone.dispose(); }); } diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index 708ac4e0be2..c8ec74a476f 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -37,11 +37,11 @@ export class SimpleCommentEditor extends CodeEditorWidget { constructor( domElement: HTMLElement, options: IEditorOptions, + scopedContextKeyService: IContextKeyService, parentThread: ICommentThreadWidget, @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @ICommandService commandService: ICommandService, - @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @INotificationService notificationService: INotificationService, @IAccessibilityService accessibilityService: IAccessibilityService, @@ -58,10 +58,10 @@ export class SimpleCommentEditor extends CodeEditorWidget { ] }; - super(domElement, options, codeEditorWidgetOptions, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService, languageConfigurationService, languageFeaturesService); + super(domElement, options, codeEditorWidgetOptions, instantiationService, codeEditorService, commandService, scopedContextKeyService, themeService, notificationService, accessibilityService, languageConfigurationService, languageFeaturesService); - this._commentEditorFocused = ctxCommentEditorFocused.bindTo(contextKeyService); - this._commentEditorEmpty = CommentContextKeys.commentIsEmpty.bindTo(contextKeyService); + this._commentEditorFocused = ctxCommentEditorFocused.bindTo(scopedContextKeyService); + this._commentEditorEmpty = CommentContextKeys.commentIsEmpty.bindTo(scopedContextKeyService); this._commentEditorEmpty.set(!this.getValue()); this._parentThread = parentThread; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index f98e1f4c709..d8383bcab1c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -68,7 +68,8 @@ export class CellComments extends CellContentPart { this.contextKeyService, this.instantiationService, commentThread, - null, + undefined, + undefined, { codeBlockFontFamily: this.configurationService.getValue('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily },