repl: support mult-line string-keyed objects · nodejs/node@e4f3468 · GitHub
Skip to content

Commit e4f3468

Browse files
rubystargos
authored andcommitted
repl: support mult-line string-keyed objects
isRecoverableError is completely reimplemented using acorn and an acorn plugin that examines the state of the parser at the time of the error to determine if the code could be completed on a subsequent line. PR-URL: #21805 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 28870a4 commit e4f3468

4 files changed

Lines changed: 97 additions & 78 deletions

File tree

lib/internal/repl/recoverable.js

Lines changed: 79 additions & 0 deletions

lib/repl.js

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const {
7373
} = require('internal/errors').codes;
7474
const { sendInspectorCommand } = require('internal/util/inspector');
7575
const { experimentalREPLAwait } = process.binding('config');
76+
const { isRecoverableError } = require('internal/repl/recoverable');
7677

7778
// Lazy-loaded.
7879
let processTopLevelAwait;
@@ -227,7 +228,8 @@ function REPLServer(prompt,
227228
// It's confusing for `{ a : 1 }` to be interpreted as a block
228229
// statement rather than an object literal. So, we first try
229230
// to wrap it in parentheses, so that it will be interpreted as
230-
// an expression.
231+
// an expression. Note that if the above condition changes,
232+
// lib/internal/repl/recoverable.js needs to be changed to match.
231233
code = `(${code.trim()})\n`;
232234
wrappedCmd = true;
233235
}
@@ -1505,76 +1507,6 @@ function regexpEscape(s) {
15051507
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
15061508
}
15071509

1508-
// If the error is that we've unexpectedly ended the input,
1509-
// then let the user try to recover by adding more input.
1510-
function isRecoverableError(e, code) {
1511-
if (e && e.name === 'SyntaxError') {
1512-
var message = e.message;
1513-
if (message === 'Unterminated template literal' ||
1514-
message === 'Unexpected end of input') {
1515-
return true;
1516-
}
1517-
1518-
if (message === 'missing ) after argument list') {
1519-
const frames = e.stack.split(/\r?\n/);
1520-
const pos = frames.findIndex((f) => f.match(/^\s*\^+$/));
1521-
return pos > 0 && frames[pos - 1].length === frames[pos].length;
1522-
}
1523-
1524-
if (message === 'Invalid or unexpected token')
1525-
return isCodeRecoverable(code);
1526-
}
1527-
return false;
1528-
}
1529-
1530-
// Check whether a code snippet should be forced to fail in the REPL.
1531-
function isCodeRecoverable(code) {
1532-
var current, previous, stringLiteral;
1533-
var isBlockComment = false;
1534-
var isSingleComment = false;
1535-
var isRegExpLiteral = false;
1536-
var lastChar = code.charAt(code.length - 2);
1537-
var prevTokenChar = null;
1538-
1539-
for (var i = 0; i < code.length; i++) {
1540-
previous = current;
1541-
current = code[i];
1542-
1543-
if (previous === '\\' && (stringLiteral || isRegExpLiteral)) {
1544-
current = null;
1545-
} else if (stringLiteral) {
1546-
if (stringLiteral === current) {
1547-
stringLiteral = null;
1548-
}
1549-
} else if (isRegExpLiteral && current === '/') {
1550-
isRegExpLiteral = false;
1551-
} else if (isBlockComment && previous === '*' && current === '/') {
1552-
isBlockComment = false;
1553-
} else if (isSingleComment && current === '\n') {
1554-
isSingleComment = false;
1555-
} else if (!isBlockComment && !isRegExpLiteral && !isSingleComment) {
1556-
if (current === '/' && previous === '/') {
1557-
isSingleComment = true;
1558-
} else if (previous === '/') {
1559-
if (current === '*') {
1560-
isBlockComment = true;
1561-
// Distinguish between a division operator and the start of a regex
1562-
// by examining the non-whitespace character that precedes the /
1563-
} else if ([null, '(', '[', '{', '}', ';'].includes(prevTokenChar)) {
1564-
isRegExpLiteral = true;
1565-
}
1566-
} else {
1567-
if (current.trim()) prevTokenChar = current;
1568-
if (current === '\'' || current === '"') {
1569-
stringLiteral = current;
1570-
}
1571-
}
1572-
}
1573-
}
1574-
1575-
return stringLiteral ? lastChar === '\\' : isBlockComment;
1576-
}
1577-
15781510
function Recoverable(err) {
15791511
this.err = err;
15801512
}

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
'lib/internal/readline.js',
147147
'lib/internal/repl.js',
148148
'lib/internal/repl/await.js',
149+
'lib/internal/repl/recoverable.js',
149150
'lib/internal/socket_list.js',
150151
'lib/internal/test/binding.js',
151152
'lib/internal/test/heap.js',

test/parallel/test-repl.js

Lines changed: 14 additions & 7 deletions

0 commit comments

Comments
 (0)