Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit af227b1

Browse filesBrowse files
harrisitargos
authored andcommitted
readline: fix issue with newline-less last line
The logic for reading lines was slightly flawed, in that it assumed there would be a final new line. It handled the case where there are no new lines, but this then broke if there were some new lines. The fix in logic is basically removing the special case where there are no new lines by changing it to always read the final line with no new lines. This works because if a file contains no new lines, the final line is the first line, and all is well. There is some subtlety in this functioning, however. If the last line contains no new lines, then `lastIndex` will be the start of the last line, and `kInsertString` will be called from that point. If it does contain a new line, `lastIndex` will be equal to `s.length`, so the slice will be the empty string. Fixes: #47305 PR-URL: #47317 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 826f404 commit af227b1
Copy full SHA for af227b1

File tree

Expand file treeCollapse file tree

4 files changed

+85
-14
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+85
-14
lines changed
Open diff view settings
Collapse file

‎lib/internal/readline/interface.js‎

Copy file name to clipboardExpand all lines: lib/internal/readline/interface.js
+12-14Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,21 +1324,19 @@ class Interface extends InterfaceConstructor {
13241324
if (typeof s === 'string' && s) {
13251325
// Erase state of previous searches.
13261326
lineEnding.lastIndex = 0;
1327-
let nextMatch = RegExpPrototypeExec(lineEnding, s);
1328-
// If no line endings are found, just insert the string as is.
1329-
if (nextMatch === null) {
1330-
this[kInsertString](s);
1331-
} else {
1332-
// Keep track of the end of the last match.
1333-
let lastIndex = 0;
1334-
do {
1335-
this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
1336-
({ lastIndex } = lineEnding);
1337-
this[kLine]();
1338-
// Restore lastIndex as the call to kLine could have mutated it.
1339-
lineEnding.lastIndex = lastIndex;
1340-
} while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null);
1327+
let nextMatch;
1328+
// Keep track of the end of the last match.
1329+
let lastIndex = 0;
1330+
while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) {
1331+
this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index));
1332+
({ lastIndex } = lineEnding);
1333+
this[kLine]();
1334+
// Restore lastIndex as the call to kLine could have mutated it.
1335+
lineEnding.lastIndex = lastIndex;
13411336
}
1337+
// This ensures that the last line is written if it doesn't end in a newline.
1338+
// Note that the last line may be the first line, in which case this still works.
1339+
this[kInsertString](StringPrototypeSlice(s, lastIndex));
13421340
}
13431341
}
13441342
}
Collapse file
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// The lack of a newline at the end of this file is intentional.
2+
const getLunch = () =>
3+
placeOrder('tacos')
4+
.then(eat);
5+
6+
const placeOrder = (order) => Promise.resolve(order);
7+
const eat = (food) => '<nom nom nom>';
Collapse file
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
const common = require('../common');
3+
const ArrayStream = require('../common/arraystream');
4+
const assert = require('assert');
5+
6+
common.skipIfDumbTerminal();
7+
8+
const readline = require('readline');
9+
const rli = new readline.Interface({
10+
terminal: true,
11+
input: new ArrayStream(),
12+
output: new ArrayStream(),
13+
});
14+
15+
// Minimal reproduction for #47305
16+
const testInput = '{\n}';
17+
18+
let accum = '';
19+
20+
rli.output.write = (data) => accum += data.replace('\r', '');
21+
22+
rli.write(testInput);
23+
24+
assert.strictEqual(accum, testInput);
Collapse file
+42Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
const common = require('../common');
3+
const ArrayStream = require('../common/arraystream');
4+
const fixtures = require('../common/fixtures');
5+
const assert = require('assert');
6+
const repl = require('repl');
7+
8+
common.skipIfDumbTerminal();
9+
10+
const command = `.load ${fixtures.path('repl-load-multiline-no-trailing-newline.js')}`;
11+
const terminalCode = '\u001b[1G\u001b[0J \u001b[1G';
12+
const terminalCodeRegex = new RegExp(terminalCode.replace(/\[/g, '\\['), 'g');
13+
14+
const expected = `${command}
15+
// The lack of a newline at the end of this file is intentional.
16+
const getLunch = () =>
17+
placeOrder('tacos')
18+
.then(eat);
19+
20+
const placeOrder = (order) => Promise.resolve(order);
21+
const eat = (food) => '<nom nom nom>';
22+
undefined
23+
`;
24+
25+
let accum = '';
26+
27+
const inputStream = new ArrayStream();
28+
const outputStream = new ArrayStream();
29+
30+
outputStream.write = (data) => accum += data.replace('\r', '');
31+
32+
const r = repl.start({
33+
prompt: '',
34+
input: inputStream,
35+
output: outputStream,
36+
terminal: true,
37+
useColors: false
38+
});
39+
40+
r.write(`${command}\n`);
41+
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
42+
r.close();

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.