Skip to content

Commit 3243701

Browse files
rayw000bengl
authored andcommitted
readline: bind keystroke ctrl+6 to redo
1. Any keystroke emits `0x1E` will do redo action. 2. Fix bug of undo/redo. 3. More detailed document. 4. Unit tests. PR-URL: #41662 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 0a6f0b4 commit 3243701

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

doc/api/readline.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,18 @@ const { createInterface } = require('readline');
13541354
<tr>
13551355
<td><kbd>Ctrl</kbd>+<kbd>-</kbd></td>
13561356
<td>Undo previous change</td>
1357-
<td>Any keystroke emits key code <code>0x1F</code> would do this action.</td>
1357+
<td>Any keystroke that emits key code <code>0x1F</code> will do this action.
1358+
In many terminals, for example <code>xterm</code>,
1359+
this is bound to <kbd>Ctrl</kbd>+<kbd>-</kbd>.</td>
1360+
<td></td>
1361+
</tr>
1362+
<tr>
1363+
<td><kbd>Ctrl</kbd>+<kbd>6</kbd></td>
1364+
<td>Redo previous change</td>
1365+
<td>Many terminals don't have a default redo keystroke.
1366+
We choose key code <code>0x1E</code> to perform redo.
1367+
In <code>xterm</code>, it is bound to <kbd>Ctrl</kbd>+<kbd>6</kbd>
1368+
by default.</td>
13581369
<td></td>
13591370
</tr>
13601371
<tr>

lib/internal/readline/interface.js

+22-9
Original file line numberDiff line numberDiff line change
@@ -889,24 +889,30 @@ class Interface extends InterfaceConstructor {
889889
[kUndo]() {
890890
if (this[kUndoStack].length <= 0) return;
891891

892-
const entry = this[kUndoStack].pop();
892+
ArrayPrototypePush(
893+
this[kRedoStack],
894+
{ text: this.line, cursor: this.cursor },
895+
);
893896

897+
const entry = ArrayPrototypePop(this[kUndoStack]);
894898
this.line = entry.text;
895899
this.cursor = entry.cursor;
896900

897-
ArrayPrototypePush(this[kRedoStack], entry);
898901
this[kRefreshLine]();
899902
}
900903

901904
[kRedo]() {
902905
if (this[kRedoStack].length <= 0) return;
903906

904-
const entry = this[kRedoStack].pop();
907+
ArrayPrototypePush(
908+
this[kUndoStack],
909+
{ text: this.line, cursor: this.cursor },
910+
);
905911

912+
const entry = ArrayPrototypePop(this[kRedoStack]);
906913
this.line = entry.text;
907914
this.cursor = entry.cursor;
908915

909-
ArrayPrototypePush(this[kUndoStack], entry);
910916
this[kRefreshLine]();
911917
}
912918

@@ -1071,11 +1077,18 @@ class Interface extends InterfaceConstructor {
10711077
}
10721078
}
10731079

1074-
// Undo
1075-
if (typeof key.sequence === 'string' &&
1076-
StringPrototypeCodePointAt(key.sequence, 0) === 0x1f) {
1077-
this[kUndo]();
1078-
return;
1080+
// Undo & Redo
1081+
if (typeof key.sequence === 'string') {
1082+
switch (StringPrototypeCodePointAt(key.sequence, 0)) {
1083+
case 0x1f:
1084+
this[kUndo]();
1085+
return;
1086+
case 0x1e:
1087+
this[kRedo]();
1088+
return;
1089+
default:
1090+
break;
1091+
}
10791092
}
10801093

10811094
// Ignore escape key, fixes

test/parallel/test-readline-interface.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -792,24 +792,36 @@ function assertCursorRowsAndCols(rli, rows, cols) {
792792
rli.close();
793793
}
794794

795-
// Undo
795+
// Undo & Redo
796796
{
797797
const [rli, fi] = getInterface({ terminal: true, prompt: '' });
798798
fi.emit('data', 'the quick brown fox');
799799
assertCursorRowsAndCols(rli, 0, 19);
800800

801-
// Delete right line from the 5th char
801+
// Delete the last eight chars
802802
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
803803
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
804804
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
805805
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
806806
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
807-
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'u' });
808-
assertCursorRowsAndCols(rli, 0, 0);
807+
808+
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
809+
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
810+
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
811+
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
812+
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
813+
814+
assertCursorRowsAndCols(rli, 0, 11);
815+
// Perform undo twice
809816
fi.emit('keypress', ',', { sequence: '\x1F' });
810817
assert.strictEqual(rli.line, 'the quick brown');
811818
fi.emit('keypress', ',', { sequence: '\x1F' });
812819
assert.strictEqual(rli.line, 'the quick brown fox');
820+
// Perform redo twice
821+
fi.emit('keypress', ',', { sequence: '\x1E' });
822+
assert.strictEqual(rli.line, 'the quick brown');
823+
fi.emit('keypress', ',', { sequence: '\x1E' });
824+
assert.strictEqual(rli.line, 'the quick b');
813825
fi.emit('data', '\n');
814826
rli.close();
815827
}

0 commit comments

Comments
 (0)