Skip to content

Commit ab99ee6

Browse files
Giovanni BucciRafaelGSS
authored andcommitted
repl: fix multiline history editing string order
PR-URL: #57874 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent e92e100 commit ab99ee6

File tree

2 files changed

+21
-23
lines changed

2 files changed

+21
-23
lines changed

lib/internal/readline/interface.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,15 @@ class Interface extends InterfaceConstructor {
467467
}
468468

469469
// Convert newlines to a consistent format for history storage
470-
[kNormalizeHistoryLineEndings](line, from, to) {
470+
[kNormalizeHistoryLineEndings](line, from, to, reverse = true) {
471471
// Multiline history entries are saved reversed
472-
if (StringPrototypeIncludes(line, '\r')) {
472+
// History is structured with the newest entries at the top
473+
// and the oldest at the bottom. Multiline histories, however, only occupy
474+
// one line in the history file. When loading multiline history with
475+
// an old node binary, the history will be saved in the old format.
476+
// This is why we need to reverse the multilines.
477+
// Reversing the multilines is necessary when adding / editing and displaying them
478+
if (reverse) {
473479
// First reverse the lines for proper order, then convert separators
474480
return ArrayPrototypeJoin(
475481
ArrayPrototypeReverse(StringPrototypeSplit(line, from)),
@@ -488,7 +494,7 @@ class Interface extends InterfaceConstructor {
488494

489495
// If the trimmed line is empty then return the line
490496
if (StringPrototypeTrim(this.line).length === 0) return this.line;
491-
const normalizedLine = this[kNormalizeHistoryLineEndings](this.line, '\n', '\r');
497+
const normalizedLine = this[kNormalizeHistoryLineEndings](this.line, '\n', '\r', false);
492498

493499
if (this.history.length === 0 || this.history[0] !== normalizedLine) {
494500
if (this[kLastCommandErrored] && this.historyIndex === 0) {

test/parallel/test-repl-multiline-navigation.js

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const common = require('../common');
77
const assert = require('assert');
88
const repl = require('internal/repl');
99
const stream = require('stream');
10-
const fs = require('fs');
1110

1211
class ActionStream extends stream.Stream {
1312
run(data) {
@@ -42,23 +41,11 @@ class ActionStream extends stream.Stream {
4241
}
4342
ActionStream.prototype.readable = true;
4443

45-
function cleanupTmpFile() {
46-
try {
47-
// Write over the file, clearing any history
48-
fs.writeFileSync(defaultHistoryPath, '');
49-
} catch (err) {
50-
if (err.code === 'ENOENT') return true;
51-
throw err;
52-
}
53-
return true;
54-
}
55-
5644
const tmpdir = require('../common/tmpdir');
5745
tmpdir.refresh();
58-
const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
5946

6047
{
61-
cleanupTmpFile();
48+
const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`);
6249
// Make sure the cursor is at the right places.
6350
// If the cursor is at the end of a long line and the down key is pressed,
6451
// Move the cursor to the end of the next line, if shorter.
@@ -97,7 +84,7 @@ const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
9784
});
9885

9986
repl.createInternalRepl(
100-
{ NODE_REPL_HISTORY: defaultHistoryPath },
87+
{ NODE_REPL_HISTORY: historyPath },
10188
{
10289
terminal: true,
10390
input: new ActionStream(),
@@ -112,7 +99,7 @@ const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
11299
}
113100

114101
{
115-
cleanupTmpFile();
102+
const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`);
116103
// If the last command errored and the user is trying to edit it,
117104
// The errored line should be removed from history
118105
const checkResults = common.mustSucceed((r) => {
@@ -130,12 +117,17 @@ const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
130117
r.input.run([{ name: 'enter' }]);
131118

132119
assert.strictEqual(r.history.length, 1);
133-
assert.strictEqual(r.history[0], 'let lineWithMistake = `I have some\rproblem with my syntax`');
120+
// Check that the line is properly set in the history structure
121+
assert.strictEqual(r.history[0], 'problem with my syntax`\rlet lineWithMistake = `I have some');
134122
assert.strictEqual(r.line, '');
123+
124+
r.input.run([{ name: 'up' }]);
125+
// Check that the line is properly displayed
126+
assert.strictEqual(r.line, 'let lineWithMistake = `I have some\nproblem with my syntax`');
135127
});
136128

137129
repl.createInternalRepl(
138-
{ NODE_REPL_HISTORY: defaultHistoryPath },
130+
{ NODE_REPL_HISTORY: historyPath },
139131
{
140132
terminal: true,
141133
input: new ActionStream(),
@@ -150,7 +142,7 @@ const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
150142
}
151143

152144
{
153-
cleanupTmpFile();
145+
const historyPath = tmpdir.resolve(`.${Math.floor(Math.random() * 10000)}`);
154146
const outputBuffer = [];
155147

156148
// Test that the REPL preview is properly shown on multiline commands
@@ -182,7 +174,7 @@ const defaultHistoryPath = tmpdir.resolve('.node_repl_history');
182174
});
183175

184176
repl.createInternalRepl(
185-
{ NODE_REPL_HISTORY: defaultHistoryPath },
177+
{ NODE_REPL_HISTORY: historyPath },
186178
{
187179
preview: true,
188180
terminal: true,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy