Pending edits for vscode.CommentMode.Editing lost when switching file tabs (#174678)

Fixes #166693
This commit is contained in:
Alex Ross
2023-02-20 11:10:26 +01:00
committed by GitHub
parent 7e97d0757b
commit 2d6e2ef39d
8 changed files with 100 additions and 36 deletions

View File

@@ -88,6 +88,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
constructor(
private commentThread: languages.CommentThread<T>,
public comment: languages.Comment,
private pendingEdit: string | undefined,
private owner: string,
private resource: URI,
private parentThread: ICommentThreadWidget,
@@ -139,6 +140,10 @@ export class CommentNode<T extends IRange | ICellRange> 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<T extends IRange | ICellRange> 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<T extends IRange | ICellRange> 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) {

View File

@@ -54,7 +54,7 @@ export class CommentReply<T extends IRange | ICellRange> 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<T extends IRange | ICellRange> 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<T extends IRange | ICellRange> 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) {

View File

@@ -47,6 +47,7 @@ export class CommentThreadBody<T extends IRange | ICellRange = IRange> extends D
readonly container: HTMLElement,
private _options: IMarkdownRendererOptions,
private _commentThread: languages.CommentThread<T>,
private _pendingEdits: { [key: number]: string } | undefined,
private _scopedInstatiationService: IInstantiationService,
private _parentCommentThreadWidget: ICommentThreadWidget,
@ICommentService private commentService: ICommentService,
@@ -125,6 +126,20 @@ export class CommentThreadBody<T extends IRange | ICellRange = IRange> 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<T extends IRange | ICellRange = IRange> extends D
const newCommentNode = this._scopedInstatiationService.createInstance(CommentNode,
this._commentThread,
comment,
this._pendingEdits ? this._pendingEdits[comment.uniqueIdInThread!] : undefined,
this.owner,
this.parentResourceUri,
this._parentCommentThreadWidget,

View File

@@ -56,7 +56,8 @@ export class CommentThreadWidget<T extends IRange | ICellRange = IRange> extends
private _contextKeyService: IContextKeyService,
private _scopedInstatiationService: IInstantiationService,
private _commentThread: languages.CommentThread<T>,
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<T extends IRange | ICellRange = IRange> extends
bodyElement,
this._markdownOptions,
this._commentThread,
this._pendingEdits,
this._scopedInstatiationService,
this
) as unknown as CommentThreadBody<T>;
@@ -259,12 +261,16 @@ export class CommentThreadWidget<T extends IRange | ICellRange = IRange> 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() {

View File

@@ -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<IRange | ICellRange>,
this._pendingComment,
this._pendingEdits,
{ editor: this.editor, codeBlockFontSize: '' },
this._commentOptions,
{

View File

@@ -296,7 +296,8 @@ export class CommentController implements IEditorContribution {
private _emptyThreadsToAddQueue: [Range, IEditorMouseEvent | undefined][] = [];
private _computeCommentingRangePromise!: CancelablePromise<ICommentInfo[]> | null;
private _computeCommentingRangeScheduler!: Delayer<Array<ICommentInfo | null>> | 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<boolean>;
@@ -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();
});
}

View File

@@ -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;

View File

@@ -68,7 +68,8 @@ export class CellComments extends CellContentPart {
this.contextKeyService,
this.instantiationService,
commentThread,
null,
undefined,
undefined,
{
codeBlockFontFamily: this.configurationService.getValue<IEditorOptions>('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily
},