Skip to content

Commit

Permalink
fix(module:code-editor): memorize cursor position and selections (#6044)
Browse files Browse the repository at this point in the history
Calling #setValue() on a monaco editor instance causes the mouse cursor to jump to the
beginning of the document, which is undesirable for a ControlValueAccessor implementation
which should only update the content. Also see
microsoft/monaco-editor#1397 for more information.

Unfortunately, using #executeEdits() doesn't really work as a replacement because it will
mess with selections. Therefore we memorize and restore the cursor position before/after
setting the value. Since #setValue() also causes a selection to be lost we do the same for
those. Restoring selections may produce slightly unexpected results if the selection covers
part of the content that has been changed, but is still preferable to losing the selection
altogether.

fixes #6038
  • Loading branch information
Airblader committed Nov 16, 2020
1 parent 1575bae commit 84f520d
Showing 1 changed file with 30 additions and 3 deletions.
33 changes: 30 additions & 3 deletions components/code-editor/code-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ export class NzCodeEditorComponent implements OnDestroy, AfterViewInit {

if (this.nzEditorMode === 'normal') {
if (this.modelSet) {
(this.editorInstance.getModel() as ITextModel).setValue(this.value);
const model = this.editorInstance.getModel() as ITextModel;
this.preservePositionAndSelections(() => model.setValue(this.value));
} else {
(this.editorInstance as IStandaloneCodeEditor).setModel(
monaco.editor.createModel(this.value, (this.editorOptionCached as EditorOptions).language)
Expand All @@ -222,8 +223,10 @@ export class NzCodeEditorComponent implements OnDestroy, AfterViewInit {
} else {
if (this.modelSet) {
const model = (this.editorInstance as IStandaloneDiffEditor).getModel()!;
model.modified.setValue(this.value);
model.original.setValue(this.nzOriginalText);
this.preservePositionAndSelections(() => {
model.modified.setValue(this.value);
model.original.setValue(this.nzOriginalText);
});
} else {
const language = (this.editorOptionCached as EditorOptions).language;
(this.editorInstance as IStandaloneDiffEditor).setModel({
Expand All @@ -235,6 +238,30 @@ export class NzCodeEditorComponent implements OnDestroy, AfterViewInit {
}
}

/**
* {@link editor.ICodeEditor}#setValue resets the cursor position to the start of the document.
* This helper memorizes the cursor position and selections and restores them after the given
* function has been called.
*/
private preservePositionAndSelections(fn: () => unknown): void {
if (!this.editorInstance) {
fn();
return;
}

const position = this.editorInstance.getPosition();
const selections = this.editorInstance.getSelections();

fn();

if (position) {
this.editorInstance.setPosition(position);
}
if (selections) {
this.editorInstance.setSelections(selections);
}
}

private setValueEmitter(): void {
const model = (this.nzEditorMode === 'normal'
? (this.editorInstance as IStandaloneCodeEditor).getModel()
Expand Down

0 comments on commit 84f520d

Please sign in to comment.