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: https://github.com/nodejs/node/pull/41662
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Ray authored and GitHub committed 2022-02-07 17:40:05 +00:00
1 parent 42ad4137aa
commit a0668572c7
3 files changed
+50 -14

No files matched your search

+12 -1
View File
@@ -1374,7 +1374,18 @@ const { createInterface } = require('readline');
<tr>
<td><kbd>Ctrl</kbd>+<kbd>-</kbd></td>
<td>Undo previous change</td>
<td>Any keystroke emits key code <code>0x1F</code> would do this action.</td>
<td>Any keystroke that emits key code <code>0x1F</code> will do this action.
In many terminals, for example <code>xterm</code>,
this is bound to <kbd>Ctrl</kbd>+<kbd>-</kbd>.</td>
<td></td>
</tr>
<tr>
<td><kbd>Ctrl</kbd>+<kbd>6</kbd></td>
<td>Redo previous change</td>
<td>Many terminals don't have a default redo keystroke.
We choose key code <code>0x1E</code> to perform redo.
In <code>xterm</code>, it is bound to <kbd>Ctrl</kbd>+<kbd>6</kbd>
by default.</td>
<td></td>
</tr>
<tr>
+22 -9
View File
@@ -889,24 +889,30 @@ class Interface extends InterfaceConstructor {
[kUndo]() {
if (this[kUndoStack].length <= 0) return;
const entry = this[kUndoStack].pop();
ArrayPrototypePush(
this[kRedoStack],
{ text: this.line, cursor: this.cursor },
);
const entry = ArrayPrototypePop(this[kUndoStack]);
this.line = entry.text;
this.cursor = entry.cursor;
ArrayPrototypePush(this[kRedoStack], entry);
this[kRefreshLine]();
}
[kRedo]() {
if (this[kRedoStack].length <= 0) return;
const entry = this[kRedoStack].pop();
ArrayPrototypePush(
this[kUndoStack],
{ text: this.line, cursor: this.cursor },
);
const entry = ArrayPrototypePop(this[kRedoStack]);
this.line = entry.text;
this.cursor = entry.cursor;
ArrayPrototypePush(this[kUndoStack], entry);
this[kRefreshLine]();
}
@@ -1071,11 +1077,18 @@ class Interface extends InterfaceConstructor {
}
}
// Undo
if (typeof key.sequence === 'string' &&
StringPrototypeCodePointAt(key.sequence, 0) === 0x1f) {
this[kUndo]();
return;
// Undo & Redo
if (typeof key.sequence === 'string') {
switch (StringPrototypeCodePointAt(key.sequence, 0)) {
case 0x1f:
this[kUndo]();
return;
case 0x1e:
this[kRedo]();
return;
default:
break;
}
}
// Ignore escape key, fixes
+16 -4
View File
@@ -792,24 +792,36 @@ function assertCursorRowsAndCols(rli, rows, cols) {
rli.close();
}
// Undo
// Undo & Redo
{
const [rli, fi] = getInterface({ terminal: true, prompt: '' });
fi.emit('data', 'the quick brown fox');
assertCursorRowsAndCols(rli, 0, 19);
// Delete right line from the 5th char
// Delete the last eight chars
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'u' });
assertCursorRowsAndCols(rli, 0, 0);
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
assertCursorRowsAndCols(rli, 0, 11);
// Perform undo twice
fi.emit('keypress', ',', { sequence: '\x1F' });
assert.strictEqual(rli.line, 'the quick brown');
fi.emit('keypress', ',', { sequence: '\x1F' });
assert.strictEqual(rli.line, 'the quick brown fox');
// Perform redo twice
fi.emit('keypress', ',', { sequence: '\x1E' });
assert.strictEqual(rli.line, 'the quick brown');
fi.emit('keypress', ',', { sequence: '\x1E' });
assert.strictEqual(rli.line, 'the quick b');
fi.emit('data', '\n');
rli.close();
}