Skip to content

Commit

Permalink
repl: refactor to use more primordials
Browse files Browse the repository at this point in the history
PR-URL: nodejs#36264
Reviewed-By: Rich Trott <rtrott@gmail.com>
  • Loading branch information
aduh95 authored and targos committed Aug 8, 2021
1 parent be634c6 commit 0750ac4
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 147 deletions.
15 changes: 10 additions & 5 deletions lib/internal/repl/await.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
'use strict';

const {
ArrayFrom,
ArrayPrototypeJoin,
ArrayPrototypePop,
ArrayPrototypePush,
FunctionPrototype,
ObjectKeys,
} = primordials;

Expand All @@ -19,7 +24,7 @@ const parser = acorn.Parser.extend(
staticClassFeatures
);

const noop = () => {};
const noop = FunctionPrototype;
const visitorsWithoutAncestors = {
ClassDeclaration(node, state, c) {
if (state.ancestors[state.ancestors.length - 2] === state.body) {
Expand Down Expand Up @@ -76,18 +81,18 @@ for (const nodeType of ObjectKeys(walk.base)) {
visitors[nodeType] = (node, state, c) => {
const isNew = node !== state.ancestors[state.ancestors.length - 1];
if (isNew) {
state.ancestors.push(node);
ArrayPrototypePush(state.ancestors, node);
}
callback(node, state, c);
if (isNew) {
state.ancestors.pop();
ArrayPrototypePop(state.ancestors);
}
};
}

function processTopLevelAwait(src) {
const wrapped = `(async () => { ${src} })()`;
const wrappedArray = wrapped.split('');
const wrappedArray = ArrayFrom(wrapped);
let root;
try {
root = parser.parse(wrapped, { ecmaVersion: 'latest' });
Expand Down Expand Up @@ -142,7 +147,7 @@ function processTopLevelAwait(src) {
state.append(last.expression, ')');
}

return wrappedArray.join('');
return ArrayPrototypeJoin(wrappedArray, '');
}

module.exports = {
Expand Down
14 changes: 10 additions & 4 deletions lib/internal/repl/history.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
'use strict';

const {
ArrayPrototypeJoin,
Boolean,
FunctionPrototype,
StringPrototypeSplit,
StringPrototypeTrim,
} = primordials;

const { Interface } = require('readline');
Expand All @@ -13,6 +17,8 @@ let debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
});
const { clearTimeout, setTimeout } = require('timers');

const noop = FunctionPrototype;

// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
// The debounce is to guard against code pasted into the REPL.
const kDebounceHistoryMS = 15;
Expand All @@ -27,7 +33,7 @@ function _writeToOutput(repl, message) {
function setupHistory(repl, historyPath, ready) {
// Empty string disables persistent history
if (typeof historyPath === 'string')
historyPath = historyPath.trim();
historyPath = StringPrototypeTrim(historyPath);

if (historyPath === '') {
repl._historyPrev = _replHistoryMessage;
Expand Down Expand Up @@ -84,7 +90,7 @@ function setupHistory(repl, historyPath, ready) {
}

if (data) {
repl.history = data.split(/[\n\r]+/, repl.historySize);
repl.history = StringPrototypeSplit(data, /[\n\r]+/, repl.historySize);
} else {
repl.history = [];
}
Expand Down Expand Up @@ -128,7 +134,7 @@ function setupHistory(repl, historyPath, ready) {
return;
}
writing = true;
const historyData = repl.history.join(os.EOL);
const historyData = ArrayPrototypeJoin(repl.history, os.EOL);
fs.write(repl._historyHandle, historyData, 0, 'utf8', onwritten);
}

Expand All @@ -151,7 +157,7 @@ function setupHistory(repl, historyPath, ready) {
return;
}
repl.off('line', online);
fs.close(repl._historyHandle, () => {});
fs.close(repl._historyHandle, noop);
}
}

Expand Down
72 changes: 48 additions & 24 deletions lib/internal/repl/utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
'use strict';

const {
ArrayPrototypeFilter,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
Boolean,
FunctionPrototypeBind,
MathMin,
RegExpPrototypeTest,
SafeSet,
SafeStringIterator,
Set,
StringPrototypeEndsWith,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
StringPrototypeReplace,
StringPrototypeSlice,
StringPrototypeStartsWith,
StringPrototypeToLowerCase,
StringPrototypeTrim,
Symbol,
} = primordials;

Expand Down Expand Up @@ -60,7 +74,9 @@ function isRecoverableError(e, code) {
// curly brace with parenthesis. Note: only the open parenthesis is added
// here as the point is to test for potentially valid but incomplete
// expressions.
if (/^\s*\{/.test(code) && isRecoverableError(e, `(${code}`)) return true;
if (RegExpPrototypeTest(/^\s*\{/, code) &&
isRecoverableError(e, `(${code}`))
return true;

let recoverable = false;

Expand Down Expand Up @@ -100,9 +116,11 @@ function isRecoverableError(e, code) {
break;

case 'Unterminated string constant':
const token = this.input.slice(this.lastTokStart, this.pos);
const token = StringPrototypeSlice(this.input,
this.lastTokStart, this.pos);
// See https://www.ecma-international.org/ecma-262/#sec-line-terminators
if (/\\(?:\r\n?|\n|\u2028|\u2029)$/.test(token)) {
if (RegExpPrototypeTest(/\\(?:\r\n?|\n|\u2028|\u2029)$/,
token)) {
recoverable = true;
}
}
Expand Down Expand Up @@ -236,15 +254,15 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
hasCompletions = true;

// If there is a common prefix to all matches, then apply that portion.
const completions = rawCompletions.filter((e) => e);
const completions = ArrayPrototypeFilter(rawCompletions, Boolean);
const prefix = commonPrefix(completions);

// No common prefix found.
if (prefix.length <= completeOn.length) {
return;
}

const suffix = prefix.slice(completeOn.length);
const suffix = StringPrototypeSlice(prefix, completeOn.length);

if (insertPreview) {
repl._insertString(suffix);
Expand Down Expand Up @@ -272,16 +290,22 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
}

function isInStrictMode(repl) {
return repl.replMode === REPL_MODE_STRICT || process.execArgv
.map((e) => e.toLowerCase().replace(/_/g, '-'))
.includes('--use-strict');
return repl.replMode === REPL_MODE_STRICT || ArrayPrototypeIncludes(
ArrayPrototypeMap(process.execArgv,
(e) => StringPrototypeReplace(
StringPrototypeToLowerCase(e),
/_/g,
'-'
)),
'--use-strict');
}

// This returns a code preview for arbitrary input code.
function getInputPreview(input, callback) {
// For similar reasons as `defaultEval`, wrap expressions starting with a
// curly brace with parenthesis.
if (input.startsWith('{') && !input.endsWith(';') && !wrapped) {
if (StringPrototypeStartsWith(input, '{') &&
!StringPrototypeEndsWith(input, ';') && !wrapped) {
input = `(${input})`;
wrapped = true;
}
Expand Down Expand Up @@ -347,7 +371,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
return;
}

const line = repl.line.trim();
const line = StringPrototypeTrim(repl.line);

// Do not preview in case the line only contains whitespace.
if (line === '') {
Expand Down Expand Up @@ -413,9 +437,9 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {

// Line breaks are very rare and probably only occur in case of error
// messages with line breaks.
const lineBreakPos = inspected.indexOf('\n');
const lineBreakPos = StringPrototypeIndexOf(inspected, '\n');
if (lineBreakPos !== -1) {
inspected = `${inspected.slice(0, lineBreakPos)}`;
inspected = `${StringPrototypeSlice(inspected, 0, lineBreakPos)}`;
}

const result = repl.useColors ?
Expand Down Expand Up @@ -453,7 +477,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
// Refresh prints the whole screen again and the preview will be removed
// during that procedure. Print the preview again. This also makes sure
// the preview is always correct after resizing the terminal window.
const originalRefresh = repl._refreshLine.bind(repl);
const originalRefresh = FunctionPrototypeBind(repl._refreshLine, repl);
repl._refreshLine = () => {
inputPreview = null;
originalRefresh();
Expand All @@ -463,7 +487,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
let insertCompletionPreview = true;
// Insert the longest common suffix of the current input in case the user
// moves to the right while already being at the current input end.
const originalMoveCursor = repl._moveCursor.bind(repl);
const originalMoveCursor = FunctionPrototypeBind(repl._moveCursor, repl);
repl._moveCursor = (dx) => {
const currentCursor = repl.cursor;
originalMoveCursor(dx);
Expand All @@ -477,7 +501,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {

// This is the only function that interferes with the completion insertion.
// Monkey patch it to prevent inserting the completion when it shouldn't be.
const originalClearLine = repl.clearLine.bind(repl);
const originalClearLine = FunctionPrototypeBind(repl.clearLine, repl);
repl.clearLine = () => {
insertCompletionPreview = false;
originalClearLine();
Expand All @@ -493,7 +517,7 @@ function setupReverseSearch(repl) {
return { reverseSearch() { return false; } };
}

const alreadyMatched = new Set();
const alreadyMatched = new SafeSet();
const labels = {
r: 'bck-i-search: ',
s: 'fwd-i-search: '
Expand Down Expand Up @@ -557,18 +581,18 @@ function setupReverseSearch(repl) {
if (cursor === -1) {
cursor = entry.length;
}
cursor = entry.lastIndexOf(input, cursor - 1);
cursor = StringPrototypeLastIndexOf(entry, input, cursor - 1);
} else {
cursor = entry.indexOf(input, cursor + 1);
cursor = StringPrototypeIndexOf(entry, input, cursor + 1);
}
// Match not found.
if (cursor === -1) {
goToNextHistoryIndex();
// Match found.
} else {
if (repl.useColors) {
const start = entry.slice(0, cursor);
const end = entry.slice(cursor + input.length);
const start = StringPrototypeSlice(entry, 0, cursor);
const end = StringPrototypeSlice(entry, cursor + input.length);
entry = `${start}\x1B[4m${input}\x1B[24m${end}`;
}
print(entry, `${labels[dir]}${input}_`, cursor);
Expand Down Expand Up @@ -611,7 +635,7 @@ function setupReverseSearch(repl) {
// tick end instead of after each operation.
let rows = 0;
if (lastMatch !== -1) {
const line = repl.history[lastMatch].slice(0, lastCursor);
const line = StringPrototypeSlice(repl.history[lastMatch], 0, lastCursor);
rows = repl._getDisplayPos(`${repl.getPrompt()}${line}`).rows;
cursorTo(repl.output, promptPos.cols);
} else if (isInReverseSearch && repl.line !== '') {
Expand All @@ -633,7 +657,7 @@ function setupReverseSearch(repl) {
// To know exactly how many rows we have to move the cursor back we need the
// cursor rows, the output rows and the input rows.
const prompt = repl.getPrompt();
const cursorLine = `${prompt}${outputLine.slice(0, cursor)}`;
const cursorLine = prompt + StringPrototypeSlice(outputLine, 0, cursor);
const cursorPos = repl._getDisplayPos(cursorLine);
const outputPos = repl._getDisplayPos(`${prompt}${outputLine}`);
const inputPos = repl._getDisplayPos(inputLine);
Expand Down Expand Up @@ -691,7 +715,7 @@ function setupReverseSearch(repl) {
search();
} else if (key.name === 'backspace' ||
(key.ctrl && (key.name === 'h' || key.name === 'w'))) {
reset(input.slice(0, input.length - 1));
reset(StringPrototypeSlice(input, 0, input.length - 1));
search();
// Special handle <ctrl> + c and escape. Those should only cancel the
// reverse search. The original line is visible afterwards again.
Expand Down
Loading

0 comments on commit 0750ac4

Please sign in to comment.