Skip to content

Commit 4fe2ff4

Browse files
repl: fix Node crashes when press up arrow with multiline history
1 parent 8dc2bdd commit 4fe2ff4

1 file changed

Lines changed: 32 additions & 16 deletions

File tree

lib/internal/readline/interface.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const {
2424
StringPrototypeCodePointAt,
2525
StringPrototypeEndsWith,
2626
StringPrototypeIncludes,
27+
StringPrototypeLastIndexOf,
2728
StringPrototypeRepeat,
2829
StringPrototypeReplaceAll,
2930
StringPrototypeSlice,
@@ -1091,21 +1092,20 @@ class Interface extends InterfaceConstructor {
10911092
this[kRefreshLine]();
10921093
}
10931094

1094-
[kMultilineMove](direction, splitLines, { rows, cols }) {
1095-
const curr = splitLines[rows];
1095+
[kMultilineMove](direction, splitLines, { logicalRows, logicalCols }) {
1096+
const curr = splitLines[logicalRows];
10961097
const down = direction === 1;
1097-
const adj = splitLines[rows + direction];
1098-
const promptLen = kMultilinePrompt.description.length;
1098+
const adj = splitLines[logicalRows + direction];
10991099
let amountToMove;
1100-
// Clamp distance to end of current + prompt + next/prev line + newline
1100+
// Clamp distance to end of current + next/prev line + newline
11011101
const clamp = down ?
1102-
curr.length - cols + promptLen + adj.length + 1 :
1103-
-cols + 1;
1104-
const shouldClamp = cols > adj.length + 1;
1102+
curr.length - logicalCols + adj.length + 1 :
1103+
-logicalCols - 1;
1104+
const shouldClamp = logicalCols > adj.length;
11051105

11061106
if (shouldClamp) {
11071107
if (this[kPreviousCursorCols] === -1) {
1108-
this[kPreviousCursorCols] = cols;
1108+
this[kPreviousCursorCols] = logicalCols;
11091109
}
11101110
amountToMove = clamp;
11111111
} else {
@@ -1116,7 +1116,7 @@ class Interface extends InterfaceConstructor {
11161116
}
11171117
if (this[kPreviousCursorCols] !== -1) {
11181118
if (this[kPreviousCursorCols] <= adj.length) {
1119-
amountToMove += this[kPreviousCursorCols] - cols;
1119+
amountToMove += this[kPreviousCursorCols] - logicalCols;
11201120
this[kPreviousCursorCols] = -1;
11211121
} else {
11221122
amountToMove = clamp;
@@ -1128,10 +1128,10 @@ class Interface extends InterfaceConstructor {
11281128
}
11291129

11301130
[kMoveDownOrHistoryNext]() {
1131-
const cursorPos = this.getCursorPos();
1131+
const logicalCursorPos = this.getLogicalCursorPos();
11321132
const splitLines = StringPrototypeSplit(this.line, '\n');
1133-
if (this[kIsMultiline] && cursorPos.rows < splitLines.length - 1) {
1134-
this[kMultilineMove](1, splitLines, cursorPos);
1133+
if (this[kIsMultiline] && logicalCursorPos.logicalRows < splitLines.length - 1) {
1134+
this[kMultilineMove](1, splitLines, logicalCursorPos);
11351135
return;
11361136
}
11371137
this[kPreviousCursorCols] = -1;
@@ -1155,10 +1155,10 @@ class Interface extends InterfaceConstructor {
11551155
}
11561156

11571157
[kMoveUpOrHistoryPrev]() {
1158-
const cursorPos = this.getCursorPos();
1159-
if (this[kIsMultiline] && cursorPos.rows > 0) {
1158+
const logicalCursorPos = this.getLogicalCursorPos();
1159+
if (this[kIsMultiline] && logicalCursorPos.logicalRows > 0) {
11601160
const splitLines = StringPrototypeSplit(this.line, '\n');
1161-
this[kMultilineMove](-1, splitLines, cursorPos);
1161+
this[kMultilineMove](-1, splitLines, logicalCursorPos);
11621162
return;
11631163
}
11641164
this[kPreviousCursorCols] = -1;
@@ -1226,6 +1226,22 @@ class Interface extends InterfaceConstructor {
12261226
return this[kGetDisplayPos](strBeforeCursor);
12271227
}
12281228

1229+
/**
1230+
* Returns the logical position of the cursor within the input string
1231+
* excluding prompt and terminal formatting.
1232+
* @returns {{
1233+
* logicalCols: number;
1234+
* logicalRows: number;
1235+
* }}
1236+
*/
1237+
getLogicalCursorPos() {
1238+
const logicalCols = this.cursor - (StringPrototypeLastIndexOf(this.line, '\n', this.cursor - 1) + 1);
1239+
const strlineBeforeCursor = StringPrototypeSlice(this.line, 0, this.cursor);
1240+
const logicalRows = StringPrototypeSplit(strlineBeforeCursor, '\n').length - 1
1241+
1242+
return { logicalCols, logicalRows };
1243+
}
1244+
12291245
// This function moves cursor dx places to the right
12301246
// (-dx for left) and refreshes the line if it is needed.
12311247
[kMoveCursor](dx) {

0 commit comments

Comments
 (0)