Skip to content

Commit 952ac8d

Browse files
committed
Fix u canceling multi-cursor. Fixes #9835.
1 parent 5f926f4 commit 952ac8d

File tree

3 files changed

+38
-21
lines changed

3 files changed

+38
-21
lines changed

src/actions/commands/actions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -641,12 +641,12 @@ export class CommandUndo extends BaseCommand {
641641
}
642642

643643
public override async exec(position: Position, vimState: VimState): Promise<void> {
644-
const newPosition = await vimState.historyTracker.goBackHistoryStep();
644+
const cursors = await vimState.historyTracker.goBackHistoryStep();
645645

646-
if (newPosition === undefined) {
646+
if (cursors === undefined) {
647647
StatusBar.setText(vimState, 'Already at oldest change');
648648
} else {
649-
vimState.cursors = [new Cursor(newPosition, newPosition)];
649+
vimState.cursors = cursors;
650650
}
651651
}
652652
}
@@ -677,12 +677,12 @@ export class CommandRedo extends BaseCommand {
677677
}
678678

679679
public override async exec(position: Position, vimState: VimState): Promise<void> {
680-
const newPosition = await vimState.historyTracker.goForwardHistoryStep();
680+
const cursors = await vimState.historyTracker.goForwardHistoryStep();
681681

682-
if (newPosition === undefined) {
682+
if (cursors === undefined) {
683683
StatusBar.setText(vimState, 'Already at newest change');
684684
} else {
685-
vimState.cursors = [new Cursor(newPosition, newPosition)];
685+
vimState.cursors = cursors;
686686
}
687687
}
688688
}

src/history/historyTracker.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { globalState } from '../state/globalState';
2121
import { Mode } from '../mode/mode';
2222
import { VimError } from '../error';
2323
import { Logger } from '../util/logger';
24+
import { Cursor } from '../common/motion/cursor';
2425
import { earlierOf } from '../common/motion/position';
2526

2627
const diffEngine = new DiffMatchPatch.diff_match_patch();
@@ -134,9 +135,9 @@ class HistoryStep {
134135

135136
/**
136137
* The cursor position at the start of this history step.
137-
* Restored by `u`. Currently, only one cursor is remembered.
138+
* Restored by `u`.
138139
*/
139-
public cursorStart: Position | undefined;
140+
public cursorsAtStart: readonly Cursor[] | undefined;
140141

141142
/**
142143
* The position of every mark at the start of this history step.
@@ -390,7 +391,7 @@ class ChangeList {
390391
export class HistoryTracker {
391392
public currentContentChanges: vscode.TextDocumentContentChangeEvent[];
392393

393-
private nextStepStartPosition: Position | undefined;
394+
private nextStepCursorsAtStart: readonly Cursor[] | undefined;
394395

395396
private readonly undoStack: UndoStack;
396397

@@ -691,10 +692,9 @@ export class HistoryTracker {
691692
return;
692693
}
693694

694-
if (this.nextStepStartPosition === undefined) {
695-
const cursor = this.vimState.cursorsInitialState[0];
696-
this.nextStepStartPosition = earlierOf(cursor.start, cursor.stop);
697-
Logger.debug(`Set nextStepStartPosition to ${this.nextStepStartPosition}`);
695+
if (this.nextStepCursorsAtStart === undefined) {
696+
this.nextStepCursorsAtStart = this.vimState.cursorsInitialState;
697+
Logger.debug(`Set nextStepCursorsAtStart to ${this.nextStepCursorsAtStart}`);
698698
}
699699

700700
if (
@@ -772,8 +772,10 @@ export class HistoryTracker {
772772
currentHistoryStep.isFinished = true;
773773
currentHistoryStep.timestamp = new Date();
774774

775-
currentHistoryStep.cursorStart ??= this.nextStepStartPosition;
776-
this.nextStepStartPosition = undefined;
775+
if (this.nextStepCursorsAtStart !== undefined) {
776+
currentHistoryStep.cursorsAtStart ??= this.nextStepCursorsAtStart;
777+
this.nextStepCursorsAtStart = undefined;
778+
}
777779

778780
currentHistoryStep.merge(this.vimState.document);
779781

@@ -794,7 +796,7 @@ export class HistoryTracker {
794796
*
795797
* @returns the new cursor positions, or undefined if there are no steps to undo
796798
*/
797-
public async goBackHistoryStep(): Promise<Position | undefined> {
799+
public async goBackHistoryStep(): Promise<Cursor[] | undefined> {
798800
const step = this.undoStack.stepBackward();
799801
if (step === undefined) {
800802
return undefined;
@@ -815,15 +817,18 @@ export class HistoryTracker {
815817
} ${step.howLongAgo()}`,
816818
);
817819

818-
return step.cursorStart;
820+
return step.cursorsAtStart?.map((c) => {
821+
const start = earlierOf(c.start, c.stop);
822+
return new Cursor(start, start);
823+
});
819824
}
820825

821826
/**
822827
* Redo the next HistoryStep, if there is one
823828
*
824829
* @returns the new cursor positions, or undefined if there are no steps to redo
825830
*/
826-
public async goForwardHistoryStep(): Promise<Position | undefined> {
831+
public async goForwardHistoryStep(): Promise<Cursor[] | undefined> {
827832
const step = this.undoStack.stepForward();
828833
if (step === undefined) {
829834
return undefined;
@@ -842,7 +847,10 @@ export class HistoryTracker {
842847
`${changes}; after #${this.undoStack.getCurrentHistoryStepIndex()} ${step.howLongAgo()}`,
843848
);
844849

845-
return step.cursorStart;
850+
return step.cursorsAtStart?.map((c) => {
851+
const start = earlierOf(c.start, c.stop);
852+
return new Cursor(start, start);
853+
});
846854
}
847855

848856
/**
@@ -918,7 +926,7 @@ export class HistoryTracker {
918926
changes: changesToUndo.map((change) => change.reversed()).reverse(),
919927
cameFromU: true,
920928
});
921-
this.nextStepStartPosition = lastChange.start;
929+
this.nextStepCursorsAtStart = [new Cursor(lastChange.start, lastChange.start)];
922930
this.undoStack.pushHistoryStep(newStep);
923931

924932
this.finishCurrentStep();
@@ -957,7 +965,7 @@ export class HistoryTracker {
957965
}
958966

959967
public getLastHistoryStartPosition(): Position | undefined {
960-
return this.undoStack.getCurrentHistoryStep()?.cursorStart;
968+
return this.undoStack.getCurrentHistoryStep()?.cursorsAtStart?.[0]?.start;
961969
}
962970

963971
private getLastChangeStartPosition(): Position | undefined {

test/multicursor.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ suite('Multicursor', () => {
6060
});
6161
});
6262

63+
suite('Undo/redo', () => {
64+
newTest({
65+
title: 'Can undo with multiple cursors',
66+
start: ['|one', '|two', '|three'],
67+
keysPressed: 'l' + 'iXXX<Esc>' + '$' + 'u',
68+
end: ['o|ne', 't|wo', 't|hree'],
69+
});
70+
});
71+
6372
test('can add multiple cursors below', async () => {
6473
await modeHandler.handleMultipleKeyEvents('i11\n22'.split(''));
6574
await modeHandler.handleMultipleKeyEvents(['<Esc>', 'g', 'g']);

0 commit comments

Comments
 (0)