mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 16:07:39 +00:00
repl: refactor to use more primordials
PR-URL: https://github.com/nodejs/node/pull/36264 Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
c91e608a7d
commit
825029ec59
@ -1,6 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayFrom,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypePop,
|
||||
ArrayPrototypePush,
|
||||
FunctionPrototype,
|
||||
ObjectKeys,
|
||||
} = primordials;
|
||||
|
||||
@ -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) {
|
||||
@ -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' });
|
||||
@ -142,7 +147,7 @@ function processTopLevelAwait(src) {
|
||||
state.append(last.expression, ')');
|
||||
}
|
||||
|
||||
return wrappedArray.join('');
|
||||
return ArrayPrototypeJoin(wrappedArray, '');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayPrototypeJoin,
|
||||
Boolean,
|
||||
FunctionPrototype,
|
||||
StringPrototypeSplit,
|
||||
StringPrototypeTrim,
|
||||
} = primordials;
|
||||
|
||||
const { Interface } = require('readline');
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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 = [];
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -151,7 +157,7 @@ function setupHistory(repl, historyPath, ready) {
|
||||
return;
|
||||
}
|
||||
repl.off('line', online);
|
||||
fs.close(repl._historyHandle, () => {});
|
||||
fs.close(repl._historyHandle, noop);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeMap,
|
||||
Boolean,
|
||||
FunctionPrototypeBind,
|
||||
MathMin,
|
||||
Set,
|
||||
RegExpPrototypeTest,
|
||||
SafeSet,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeIndexOf,
|
||||
StringPrototypeLastIndexOf,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeToLowerCase,
|
||||
StringPrototypeTrim,
|
||||
Symbol,
|
||||
} = primordials;
|
||||
|
||||
@ -59,7 +73,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;
|
||||
|
||||
@ -99,9 +115,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;
|
||||
}
|
||||
}
|
||||
@ -235,7 +253,7 @@ 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.
|
||||
@ -243,7 +261,7 @@ function setupPreview(repl, contextSymbol, bufferSymbol, active) {
|
||||
return;
|
||||
}
|
||||
|
||||
const suffix = prefix.slice(completeOn.length);
|
||||
const suffix = StringPrototypeSlice(prefix, completeOn.length);
|
||||
|
||||
if (insertPreview) {
|
||||
repl._insertString(suffix);
|
||||
@ -271,16 +289,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;
|
||||
}
|
||||
@ -346,7 +370,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 === '') {
|
||||
@ -412,9 +436,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 ?
|
||||
@ -452,7 +476,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();
|
||||
@ -462,7 +486,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);
|
||||
@ -476,7 +500,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();
|
||||
@ -492,7 +516,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: '
|
||||
@ -556,9 +580,9 @@ 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) {
|
||||
@ -566,8 +590,8 @@ function setupReverseSearch(repl) {
|
||||
// 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);
|
||||
@ -610,7 +634,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 !== '') {
|
||||
@ -632,7 +656,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);
|
||||
@ -690,7 +714,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.
|
||||
|
||||
290
lib/repl.js
290
lib/repl.js
@ -43,7 +43,22 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayPrototypeConcat,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeFindIndex,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePop,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeReverse,
|
||||
ArrayPrototypeShift,
|
||||
ArrayPrototypeSort,
|
||||
ArrayPrototypeSplice,
|
||||
ArrayPrototypeUnshift,
|
||||
Boolean,
|
||||
Error,
|
||||
FunctionPrototypeBind,
|
||||
MathMax,
|
||||
NumberIsNaN,
|
||||
NumberParseFloat,
|
||||
@ -56,16 +71,30 @@ const {
|
||||
ObjectKeys,
|
||||
ObjectSetPrototypeOf,
|
||||
Promise,
|
||||
PromisePrototypeFinally,
|
||||
PromisePrototypeThen,
|
||||
PromiseRace,
|
||||
ReflectApply,
|
||||
RegExp,
|
||||
Set,
|
||||
RegExpPrototypeExec,
|
||||
RegExpPrototypeTest,
|
||||
SafeSet,
|
||||
SafeWeakSet,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeCodePointAt,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeIncludes,
|
||||
StringPrototypeMatch,
|
||||
StringPrototypeRepeat,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeSplit,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeTrim,
|
||||
StringPrototypeTrimLeft,
|
||||
Symbol,
|
||||
SyntaxError,
|
||||
SyntaxErrorPrototype,
|
||||
WeakSet,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
@ -91,8 +120,10 @@ const {
|
||||
} = require('internal/readline/utils');
|
||||
const { Console } = require('console');
|
||||
const CJSModule = require('internal/modules/cjs/loader').Module;
|
||||
let _builtinLibs = [...CJSModule.builtinModules]
|
||||
.filter((e) => !e.startsWith('_') && !e.includes('/'));
|
||||
let _builtinLibs = ArrayPrototypeFilter(
|
||||
CJSModule.builtinModules,
|
||||
(e) => !StringPrototypeStartsWith(e, '_') && !StringPrototypeIncludes(e, '/')
|
||||
);
|
||||
const domain = require('domain');
|
||||
let debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
|
||||
debug = fn;
|
||||
@ -145,10 +176,10 @@ function getREPLResourceName() {
|
||||
let processTopLevelAwait;
|
||||
|
||||
const globalBuiltins =
|
||||
new Set(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'));
|
||||
new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'));
|
||||
|
||||
const parentModule = module;
|
||||
const domainSet = new WeakSet();
|
||||
const domainSet = new SafeWeakSet();
|
||||
|
||||
const kBufferedCommandSymbol = Symbol('bufferedCommand');
|
||||
const kContextId = Symbol('contextId');
|
||||
@ -335,7 +366,7 @@ function REPLServer(prompt,
|
||||
paused = false;
|
||||
let entry;
|
||||
const tmpCompletionEnabled = self.isCompletionEnabled;
|
||||
while (entry = pausedBuffer.shift()) {
|
||||
while (entry = ArrayPrototypeShift(pausedBuffer)) {
|
||||
const [type, payload, isCompletionEnabled] = entry;
|
||||
switch (type) {
|
||||
case 'key': {
|
||||
@ -369,12 +400,13 @@ function REPLServer(prompt,
|
||||
// to wrap it in parentheses, so that it will be interpreted as
|
||||
// an expression. Note that if the above condition changes,
|
||||
// lib/internal/repl/utils.js needs to be changed to match.
|
||||
if (/^\s*{/.test(code) && !/;\s*$/.test(code)) {
|
||||
code = `(${code.trim()})\n`;
|
||||
if (RegExpPrototypeTest(/^\s*{/, code) &&
|
||||
!RegExpPrototypeTest(/;\s*$/, code)) {
|
||||
code = `(${StringPrototypeTrim(code)})\n`;
|
||||
wrappedCmd = true;
|
||||
}
|
||||
|
||||
if (experimentalREPLAwait && code.includes('await')) {
|
||||
if (experimentalREPLAwait && StringPrototypeIncludes(code, 'await')) {
|
||||
if (processTopLevelAwait === undefined) {
|
||||
({ processTopLevelAwait } = require('internal/repl/await'));
|
||||
}
|
||||
@ -402,7 +434,7 @@ function REPLServer(prompt,
|
||||
while (true) {
|
||||
try {
|
||||
if (self.replMode === module.exports.REPL_MODE_STRICT &&
|
||||
!/^\s*$/.test(code)) {
|
||||
!RegExpPrototypeTest(/^\s*$/, code)) {
|
||||
// "void 0" keeps the repl from returning "use strict" as the result
|
||||
// value for statements and declarations that don't return a value.
|
||||
code = `'use strict'; void 0;\n${code}`;
|
||||
@ -436,7 +468,8 @@ function REPLServer(prompt,
|
||||
|
||||
// This will set the values from `savedRegExMatches` to corresponding
|
||||
// predefined RegExp properties `RegExp.$1`, `RegExp.$2` ... `RegExp.$9`
|
||||
regExMatcher.test(savedRegExMatches.join(sep));
|
||||
RegExpPrototypeTest(regExMatcher,
|
||||
ArrayPrototypeJoin(savedRegExMatches, sep));
|
||||
|
||||
let finished = false;
|
||||
function finishExecution(err, result) {
|
||||
@ -516,7 +549,7 @@ function REPLServer(prompt,
|
||||
promise = PromiseRace([promise, interrupt]);
|
||||
}
|
||||
|
||||
promise.then((result) => {
|
||||
PromisePrototypeFinally(PromisePrototypeThen(promise, (result) => {
|
||||
finishExecution(null, result);
|
||||
}, (err) => {
|
||||
if (err && process.domain) {
|
||||
@ -526,7 +559,7 @@ function REPLServer(prompt,
|
||||
return;
|
||||
}
|
||||
finishExecution(err);
|
||||
}).finally(() => {
|
||||
}), () => {
|
||||
// Remove prioritized SIGINT listener if it was not called.
|
||||
prioritizedSigintQueue.delete(sigintListener);
|
||||
unpause();
|
||||
@ -551,11 +584,12 @@ function REPLServer(prompt,
|
||||
if (typeof stackFrames === 'object') {
|
||||
// Search from the bottom of the call stack to
|
||||
// find the first frame with a null function name
|
||||
const idx = stackFrames
|
||||
.reverse()
|
||||
.findIndex((frame) => frame.getFunctionName() === null);
|
||||
const idx = ArrayPrototypeFindIndex(
|
||||
ArrayPrototypeReverse(stackFrames),
|
||||
(frame) => frame.getFunctionName() === null
|
||||
);
|
||||
// If found, get rid of it and everything below it
|
||||
frames = stackFrames.splice(idx + 1);
|
||||
frames = ArrayPrototypeSplice(stackFrames, idx + 1);
|
||||
} else {
|
||||
frames = stackFrames;
|
||||
}
|
||||
@ -565,8 +599,8 @@ function REPLServer(prompt,
|
||||
if (typeof Error.prepareStackTrace === 'function') {
|
||||
return Error.prepareStackTrace(error, frames);
|
||||
}
|
||||
frames.push(error);
|
||||
return frames.reverse().join('\n at ');
|
||||
ArrayPrototypePush(frames, error);
|
||||
return ArrayPrototypeJoin(ArrayPrototypeReverse(frames), '\n at ');
|
||||
});
|
||||
decorateErrorStack(e);
|
||||
|
||||
@ -579,27 +613,31 @@ function REPLServer(prompt,
|
||||
if (e.stack) {
|
||||
if (e.name === 'SyntaxError') {
|
||||
// Remove stack trace.
|
||||
e.stack = e.stack
|
||||
.replace(/^REPL\d+:\d+\r?\n/, '')
|
||||
.replace(/^\s+at\s.*\n?/gm, '');
|
||||
e.stack = StringPrototypeReplace(StringPrototypeReplace(e.stack,
|
||||
/^REPL\d+:\d+\r?\n/, ''),
|
||||
/^\s+at\s.*\n?/gm, '');
|
||||
const importErrorStr = 'Cannot use import statement outside a ' +
|
||||
'module';
|
||||
if (StringPrototypeIncludes(e.message, importErrorStr)) {
|
||||
e.message = 'Cannot use import statement inside the Node.js ' +
|
||||
'REPL, alternatively use dynamic import';
|
||||
e.stack = e.stack.replace(/SyntaxError:.*\n/,
|
||||
`SyntaxError: ${e.message}\n`);
|
||||
e.stack = StringPrototypeReplace(e.stack,
|
||||
/SyntaxError:.*\n/,
|
||||
`SyntaxError: ${e.message}\n`);
|
||||
}
|
||||
} else if (self.replMode === module.exports.REPL_MODE_STRICT) {
|
||||
e.stack = e.stack.replace(/(\s+at\s+REPL\d+:)(\d+)/,
|
||||
(_, pre, line) => pre + (line - 1));
|
||||
e.stack = StringPrototypeReplace(
|
||||
e.stack,
|
||||
/(\s+at\s+REPL\d+:)(\d+)/,
|
||||
(_, pre, line) => pre + (line - 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
errStack = self.writer(e);
|
||||
|
||||
// Remove one line error braces to keep the old style in place.
|
||||
if (errStack[errStack.length - 1] === ']') {
|
||||
errStack = errStack.slice(1, -1);
|
||||
errStack = StringPrototypeSlice(errStack, 1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -620,12 +658,13 @@ function REPLServer(prompt,
|
||||
if (errStack === '') {
|
||||
errStack = self.writer(e);
|
||||
}
|
||||
const lines = errStack.split(/(?<=\n)/);
|
||||
const lines = StringPrototypeSplit(errStack, /(?<=\n)/);
|
||||
let matched = false;
|
||||
|
||||
errStack = '';
|
||||
for (const line of lines) {
|
||||
if (!matched && /^\[?([A-Z][a-z0-9_]*)*Error/.test(line)) {
|
||||
if (!matched &&
|
||||
RegExpPrototypeTest(/^\[?([A-Z][a-z0-9_]*)*Error/, line)) {
|
||||
errStack += writer.options.breakLength >= line.length ?
|
||||
`Uncaught ${line}` :
|
||||
`Uncaught:\n${line}`;
|
||||
@ -639,7 +678,7 @@ function REPLServer(prompt,
|
||||
errStack = `Uncaught${ln}${errStack}`;
|
||||
}
|
||||
// Normalize line endings.
|
||||
errStack += errStack.endsWith('\n') ? '' : '\n';
|
||||
errStack += StringPrototypeEndsWith(errStack, '\n') ? '' : '\n';
|
||||
self.output.write(errStack);
|
||||
self.clearBufferedCommand();
|
||||
self.lines.level = [];
|
||||
@ -650,18 +689,18 @@ function REPLServer(prompt,
|
||||
self.clearBufferedCommand();
|
||||
|
||||
function completer(text, cb) {
|
||||
complete.call(self, text, self.editorMode ?
|
||||
self.completeOnEditorMode(cb) : cb);
|
||||
ReflectApply(complete, self,
|
||||
[text, self.editorMode ? self.completeOnEditorMode(cb) : cb]);
|
||||
}
|
||||
|
||||
Interface.call(this, {
|
||||
ReflectApply(Interface, this, [{
|
||||
input: options.input,
|
||||
output: options.output,
|
||||
completer: options.completer || completer,
|
||||
terminal: options.terminal,
|
||||
historySize: options.historySize,
|
||||
prompt
|
||||
});
|
||||
}]);
|
||||
|
||||
self.resetContext();
|
||||
|
||||
@ -695,7 +734,7 @@ function REPLServer(prompt,
|
||||
function _parseREPLKeyword(keyword, rest) {
|
||||
const cmd = this.commands[keyword];
|
||||
if (cmd) {
|
||||
cmd.action.call(this, rest);
|
||||
ReflectApply(cmd.action, this, [rest]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -703,7 +742,7 @@ function REPLServer(prompt,
|
||||
|
||||
self.on('close', function emitExit() {
|
||||
if (paused) {
|
||||
pausedBuffer.push(['close']);
|
||||
ArrayPrototypePush(pausedBuffer, ['close']);
|
||||
return;
|
||||
}
|
||||
self.emit('exit');
|
||||
@ -711,7 +750,7 @@ function REPLServer(prompt,
|
||||
|
||||
let sawSIGINT = false;
|
||||
let sawCtrlD = false;
|
||||
const prioritizedSigintQueue = new Set();
|
||||
const prioritizedSigintQueue = new SafeSet();
|
||||
self.on('SIGINT', function onSigInt() {
|
||||
if (prioritizedSigintQueue.size > 0) {
|
||||
for (const task of prioritizedSigintQueue) {
|
||||
@ -753,19 +792,20 @@ function REPLServer(prompt,
|
||||
self[kBufferedCommandSymbol] += cmd + '\n';
|
||||
|
||||
// code alignment
|
||||
const matches = self._sawKeyPress ? cmd.match(/^\s+/) : null;
|
||||
const matches = self._sawKeyPress ?
|
||||
StringPrototypeMatch(cmd, /^\s+/) : null;
|
||||
if (matches) {
|
||||
const prefix = matches[0];
|
||||
self.write(prefix);
|
||||
self.line = prefix;
|
||||
self.cursor = prefix.length;
|
||||
}
|
||||
_memory.call(self, cmd);
|
||||
ReflectApply(_memory, self, [cmd]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check REPL keywords and empty lines against a trimmed line input.
|
||||
const trimmedCmd = cmd.trim();
|
||||
const trimmedCmd = StringPrototypeTrim(cmd);
|
||||
|
||||
// Check to see if a REPL keyword was used. If it returns true,
|
||||
// display next prompt and return.
|
||||
@ -776,7 +816,7 @@ function REPLServer(prompt,
|
||||
const matches = StringPrototypeMatch(trimmedCmd, /^\.([^\s]+)\s*(.*)$/);
|
||||
const keyword = matches && matches[1];
|
||||
const rest = matches && matches[2];
|
||||
if (_parseREPLKeyword.call(self, keyword, rest) === true) {
|
||||
if (ReflectApply(_parseREPLKeyword, self, [keyword, rest]) === true) {
|
||||
return;
|
||||
}
|
||||
if (!self[kBufferedCommandSymbol]) {
|
||||
@ -794,9 +834,10 @@ function REPLServer(prompt,
|
||||
|
||||
function finish(e, ret) {
|
||||
debug('finish', e, ret);
|
||||
_memory.call(self, cmd);
|
||||
ReflectApply(_memory, self, [cmd]);
|
||||
|
||||
if (e && !self[kBufferedCommandSymbol] && cmd.trim().startsWith('npm ')) {
|
||||
if (e && !self[kBufferedCommandSymbol] &&
|
||||
StringPrototypeStartsWith(StringPrototypeTrim(cmd), 'npm ')) {
|
||||
self.output.write('npm should be run outside of the ' +
|
||||
'Node.js REPL, in your normal shell.\n' +
|
||||
'(Press Ctrl+D to exit.)\n');
|
||||
@ -865,11 +906,12 @@ function REPLServer(prompt,
|
||||
);
|
||||
|
||||
// Wrap readline tty to enable editor mode and pausing.
|
||||
const ttyWrite = self._ttyWrite.bind(self);
|
||||
const ttyWrite = FunctionPrototypeBind(self._ttyWrite, self);
|
||||
self._ttyWrite = (d, key) => {
|
||||
key = key || {};
|
||||
if (paused && !(self.breakEvalOnSigint && key.ctrl && key.name === 'c')) {
|
||||
pausedBuffer.push(['key', [d, key], self.isCompletionEnabled]);
|
||||
ArrayPrototypePush(pausedBuffer,
|
||||
['key', [d, key], self.isCompletionEnabled]);
|
||||
return;
|
||||
}
|
||||
if (!self.editorMode || !self.terminal) {
|
||||
@ -942,13 +984,13 @@ REPLServer.prototype.close = function close() {
|
||||
if (this.terminal && this._flushing && !this._closingOnFlush) {
|
||||
this._closingOnFlush = true;
|
||||
this.once('flushHistory', () =>
|
||||
Interface.prototype.close.call(this)
|
||||
ReflectApply(Interface.prototype.close, this, [])
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
process.nextTick(() =>
|
||||
Interface.prototype.close.call(this)
|
||||
ReflectApply(Interface.prototype.close, this, [])
|
||||
);
|
||||
};
|
||||
|
||||
@ -1044,19 +1086,19 @@ REPLServer.prototype.displayPrompt = function(preserveCursor) {
|
||||
if (this[kBufferedCommandSymbol].length) {
|
||||
prompt = '...';
|
||||
const len = this.lines.level.length ? this.lines.level.length - 1 : 0;
|
||||
const levelInd = '..'.repeat(len);
|
||||
const levelInd = StringPrototypeRepeat('..', len);
|
||||
prompt += levelInd + ' ';
|
||||
}
|
||||
|
||||
// Do not overwrite `_initialPrompt` here
|
||||
Interface.prototype.setPrompt.call(this, prompt);
|
||||
ReflectApply(Interface.prototype.setPrompt, this, [prompt]);
|
||||
this.prompt(preserveCursor);
|
||||
};
|
||||
|
||||
// When invoked as an API method, overwrite _initialPrompt
|
||||
REPLServer.prototype.setPrompt = function setPrompt(prompt) {
|
||||
this._initialPrompt = prompt;
|
||||
Interface.prototype.setPrompt.call(this, prompt);
|
||||
ReflectApply(Interface.prototype.setPrompt, this, [prompt]);
|
||||
};
|
||||
|
||||
const requireRE = /\brequire\s*\(\s*['"`](([\w@./-]+\/)?(?:[\w@./-]*))(?![^'"`])$/;
|
||||
@ -1068,13 +1110,13 @@ function isIdentifier(str) {
|
||||
if (str === '') {
|
||||
return false;
|
||||
}
|
||||
const first = str.codePointAt(0);
|
||||
const first = StringPrototypeCodePointAt(str, 0);
|
||||
if (!isIdentifierStart(first)) {
|
||||
return false;
|
||||
}
|
||||
const firstLen = first > 0xffff ? 2 : 1;
|
||||
for (let i = firstLen; i < str.length; i += 1) {
|
||||
const cp = str.codePointAt(i);
|
||||
const cp = StringPrototypeCodePointAt(str, i);
|
||||
if (!isIdentifierChar(cp)) {
|
||||
return false;
|
||||
}
|
||||
@ -1088,7 +1130,8 @@ function isIdentifier(str) {
|
||||
function filteredOwnPropertyNames(obj) {
|
||||
if (!obj) return [];
|
||||
const filter = ALL_PROPERTIES | SKIP_SYMBOLS;
|
||||
return getOwnNonIndexProperties(obj, filter).filter(isIdentifier);
|
||||
return ArrayPrototypeFilter(getOwnNonIndexProperties(obj, filter),
|
||||
isIdentifier);
|
||||
}
|
||||
|
||||
function getGlobalLexicalScopeNames(contextId) {
|
||||
@ -1104,7 +1147,7 @@ function getGlobalLexicalScopeNames(contextId) {
|
||||
}
|
||||
|
||||
REPLServer.prototype.complete = function() {
|
||||
this.completer.apply(this, arguments);
|
||||
ReflectApply(this.completer, this, arguments);
|
||||
};
|
||||
|
||||
function gracefulReaddir(...args) {
|
||||
@ -1115,7 +1158,7 @@ function gracefulReaddir(...args) {
|
||||
|
||||
function completeFSFunctions(line) {
|
||||
let baseName = '';
|
||||
let filePath = line.match(fsAutoCompleteRE)[1];
|
||||
let filePath = StringPrototypeMatch(line, fsAutoCompleteRE)[1];
|
||||
let fileList = gracefulReaddir(filePath, { withFileTypes: true });
|
||||
|
||||
if (!fileList) {
|
||||
@ -1124,9 +1167,13 @@ function completeFSFunctions(line) {
|
||||
fileList = gracefulReaddir(filePath, { withFileTypes: true }) || [];
|
||||
}
|
||||
|
||||
const completions = fileList
|
||||
.filter((dirent) => dirent.name.startsWith(baseName))
|
||||
.map((d) => d.name);
|
||||
const completions = ArrayPrototypeMap(
|
||||
ArrayPrototypeFilter(
|
||||
fileList,
|
||||
(dirent) => StringPrototypeStartsWith(dirent.name, baseName)
|
||||
),
|
||||
(d) => d.name
|
||||
);
|
||||
|
||||
return [[completions], baseName];
|
||||
}
|
||||
@ -1147,24 +1194,25 @@ function complete(line, callback) {
|
||||
let completeOn, group;
|
||||
|
||||
// Ignore right whitespace. It could change the outcome.
|
||||
line = line.trimLeft();
|
||||
line = StringPrototypeTrimLeft(line);
|
||||
|
||||
// REPL commands (e.g. ".break").
|
||||
let filter = '';
|
||||
if (/^\s*\.(\w*)$/.test(line)) {
|
||||
completionGroups.push(ObjectKeys(this.commands));
|
||||
completeOn = line.match(/^\s*\.(\w*)$/)[1];
|
||||
if (RegExpPrototypeTest(/^\s*\.(\w*)$/, line)) {
|
||||
ArrayPrototypePush(completionGroups, ObjectKeys(this.commands));
|
||||
completeOn = StringPrototypeMatch(line, /^\s*\.(\w*)$/)[1];
|
||||
if (completeOn.length) {
|
||||
filter = completeOn;
|
||||
}
|
||||
} else if (requireRE.test(line)) {
|
||||
} else if (RegExpPrototypeTest(requireRE, line)) {
|
||||
// require('...<Tab>')
|
||||
const extensions = ObjectKeys(this.context.require.extensions);
|
||||
const indexes = extensions.map((extension) => `index${extension}`);
|
||||
indexes.push('package.json', 'index');
|
||||
const indexes = ArrayPrototypeMap(extensions,
|
||||
(extension) => `index${extension}`);
|
||||
ArrayPrototypePush(indexes, 'package.json', 'index');
|
||||
const versionedFileNamesRe = /-\d+\.\d+/;
|
||||
|
||||
const match = line.match(requireRE);
|
||||
const match = StringPrototypeMatch(line, requireRE);
|
||||
completeOn = match[1];
|
||||
const subdir = match[2] || '';
|
||||
filter = completeOn;
|
||||
@ -1175,47 +1223,49 @@ function complete(line, callback) {
|
||||
group = ['./', '../'];
|
||||
} else if (completeOn === '..') {
|
||||
group = ['../'];
|
||||
} else if (/^\.\.?\//.test(completeOn)) {
|
||||
} else if (RegExpPrototypeTest(/^\.\.?\//, completeOn)) {
|
||||
paths = [process.cwd()];
|
||||
} else {
|
||||
paths = module.paths.concat(CJSModule.globalPaths);
|
||||
paths = ArrayPrototypeConcat(module.paths, CJSModule.globalPaths);
|
||||
}
|
||||
|
||||
for (let dir of paths) {
|
||||
dir = path.resolve(dir, subdir);
|
||||
const dirents = gracefulReaddir(dir, { withFileTypes: true }) || [];
|
||||
for (const dirent of dirents) {
|
||||
if (versionedFileNamesRe.test(dirent.name) || dirent.name === '.npm') {
|
||||
if (RegExpPrototypeTest(versionedFileNamesRe, dirent.name) ||
|
||||
dirent.name === '.npm') {
|
||||
// Exclude versioned names that 'npm' installs.
|
||||
continue;
|
||||
}
|
||||
const extension = path.extname(dirent.name);
|
||||
const base = dirent.name.slice(0, -extension.length);
|
||||
const base = StringPrototypeSlice(dirent.name, 0, -extension.length);
|
||||
if (!dirent.isDirectory()) {
|
||||
if (extensions.includes(extension) && (!subdir || base !== 'index')) {
|
||||
group.push(`${subdir}${base}`);
|
||||
if (StringPrototypeIncludes(extensions, extension) &&
|
||||
(!subdir || base !== 'index')) {
|
||||
ArrayPrototypePush(group, `${subdir}${base}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
group.push(`${subdir}${dirent.name}/`);
|
||||
ArrayPrototypePush(group, `${subdir}${dirent.name}/`);
|
||||
const absolute = path.resolve(dir, dirent.name);
|
||||
const subfiles = gracefulReaddir(absolute) || [];
|
||||
for (const subfile of subfiles) {
|
||||
if (indexes.includes(subfile)) {
|
||||
group.push(`${subdir}${dirent.name}`);
|
||||
if (ArrayPrototypeIncludes(indexes, subfile)) {
|
||||
ArrayPrototypePush(group, `${subdir}${dirent.name}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (group.length) {
|
||||
completionGroups.push(group);
|
||||
ArrayPrototypePush(completionGroups, group);
|
||||
}
|
||||
|
||||
if (!subdir) {
|
||||
completionGroups.push(_builtinLibs);
|
||||
ArrayPrototypePush(completionGroups, _builtinLibs);
|
||||
}
|
||||
} else if (fsAutoCompleteRE.test(line)) {
|
||||
} else if (RegExpPrototypeTest(fsAutoCompleteRE, line)) {
|
||||
[completionGroups, completeOn] = completeFSFunctions(line);
|
||||
// Handle variable member lookup.
|
||||
// We support simple chained expressions like the following (no function
|
||||
@ -1227,45 +1277,48 @@ function complete(line, callback) {
|
||||
// spam.eggs.<|> # completions for 'spam.eggs' with filter ''
|
||||
// foo<|> # all scope vars with filter 'foo'
|
||||
// foo.<|> # completions for 'foo' with filter ''
|
||||
} else if (line.length === 0 || /\w|\.|\$/.test(line[line.length - 1])) {
|
||||
const [match] = simpleExpressionRE.exec(line) || [''];
|
||||
} else if (line.length === 0 ||
|
||||
RegExpPrototypeTest(/\w|\.|\$/, line[line.length - 1])) {
|
||||
const [match] = RegExpPrototypeExec(simpleExpressionRE, line) || [''];
|
||||
if (line.length !== 0 && !match) {
|
||||
completionGroupsLoaded();
|
||||
return;
|
||||
}
|
||||
let expr = '';
|
||||
completeOn = match;
|
||||
if (line.endsWith('.')) {
|
||||
expr = match.slice(0, -1);
|
||||
if (StringPrototypeEndsWith(line, '.')) {
|
||||
expr = StringPrototypeSlice(match, 0, -1);
|
||||
} else if (line.length !== 0) {
|
||||
const bits = match.split('.');
|
||||
filter = bits.pop();
|
||||
expr = bits.join('.');
|
||||
const bits = StringPrototypeSplit(match, '.');
|
||||
filter = ArrayPrototypePop(bits);
|
||||
expr = ArrayPrototypeJoin(bits, '.');
|
||||
}
|
||||
|
||||
// Resolve expr and get its completions.
|
||||
if (!expr) {
|
||||
// Get global vars synchronously
|
||||
completionGroups.push(getGlobalLexicalScopeNames(this[kContextId]));
|
||||
ArrayPrototypePush(completionGroups,
|
||||
getGlobalLexicalScopeNames(this[kContextId]));
|
||||
let contextProto = this.context;
|
||||
while (contextProto = ObjectGetPrototypeOf(contextProto)) {
|
||||
completionGroups.push(filteredOwnPropertyNames(contextProto));
|
||||
ArrayPrototypePush(completionGroups,
|
||||
filteredOwnPropertyNames(contextProto));
|
||||
}
|
||||
const contextOwnNames = filteredOwnPropertyNames(this.context);
|
||||
if (!this.useGlobal) {
|
||||
// When the context is not `global`, builtins are not own
|
||||
// properties of it.
|
||||
contextOwnNames.push(...globalBuiltins);
|
||||
ArrayPrototypePush(contextOwnNames, ...globalBuiltins);
|
||||
}
|
||||
completionGroups.push(contextOwnNames);
|
||||
ArrayPrototypePush(completionGroups, contextOwnNames);
|
||||
if (filter !== '') addCommonWords(completionGroups);
|
||||
completionGroupsLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
let chaining = '.';
|
||||
if (expr.endsWith('?')) {
|
||||
expr = expr.slice(0, -1);
|
||||
if (StringPrototypeEndsWith(expr, '?')) {
|
||||
expr = StringPrototypeSlice(expr, 0, -1);
|
||||
chaining = '?.';
|
||||
}
|
||||
|
||||
@ -1297,7 +1350,9 @@ function complete(line, callback) {
|
||||
if (memberGroups.length) {
|
||||
expr += chaining;
|
||||
for (const group of memberGroups) {
|
||||
completionGroups.push(group.map((member) => `${expr}${member}`));
|
||||
ArrayPrototypePush(completionGroups,
|
||||
ArrayPrototypeMap(group,
|
||||
(member) => `${expr}${member}`));
|
||||
}
|
||||
if (filter) {
|
||||
filter = `${expr}${filter}`;
|
||||
@ -1318,9 +1373,12 @@ function complete(line, callback) {
|
||||
if (completionGroups.length && filter) {
|
||||
const newCompletionGroups = [];
|
||||
for (const group of completionGroups) {
|
||||
const filteredGroup = group.filter((str) => str.startsWith(filter));
|
||||
const filteredGroup = ArrayPrototypeFilter(
|
||||
group,
|
||||
(str) => StringPrototypeStartsWith(str, filter)
|
||||
);
|
||||
if (filteredGroup.length) {
|
||||
newCompletionGroups.push(filteredGroup);
|
||||
ArrayPrototypePush(newCompletionGroups, filteredGroup);
|
||||
}
|
||||
}
|
||||
completionGroups = newCompletionGroups;
|
||||
@ -1328,27 +1386,27 @@ function complete(line, callback) {
|
||||
|
||||
const completions = [];
|
||||
// Unique completions across all groups.
|
||||
const uniqueSet = new Set(['']);
|
||||
const uniqueSet = new SafeSet(['']);
|
||||
// Completion group 0 is the "closest" (least far up the inheritance
|
||||
// chain) so we put its completions last: to be closest in the REPL.
|
||||
for (const group of completionGroups) {
|
||||
group.sort((a, b) => (b > a ? 1 : -1));
|
||||
ArrayPrototypeSort(group, (a, b) => (b > a ? 1 : -1));
|
||||
const setSize = uniqueSet.size;
|
||||
for (const entry of group) {
|
||||
if (!uniqueSet.has(entry)) {
|
||||
completions.unshift(entry);
|
||||
ArrayPrototypeUnshift(completions, entry);
|
||||
uniqueSet.add(entry);
|
||||
}
|
||||
}
|
||||
// Add a separator between groups.
|
||||
if (uniqueSet.size !== setSize) {
|
||||
completions.unshift('');
|
||||
ArrayPrototypeUnshift(completions, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Remove obsolete group entry, if present.
|
||||
if (completions[0] === '') {
|
||||
completions.shift();
|
||||
ArrayPrototypeShift(completions);
|
||||
}
|
||||
|
||||
callback(null, [completions, completeOn]);
|
||||
@ -1359,7 +1417,7 @@ REPLServer.prototype.completeOnEditorMode = (callback) => (err, results) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
const [completions, completeOn = ''] = results;
|
||||
let result = completions.filter((v) => v);
|
||||
let result = ArrayPrototypeFilter(completions, Boolean);
|
||||
|
||||
if (completeOn && result.length !== 0) {
|
||||
result = [commonPrefix(result)];
|
||||
@ -1388,10 +1446,10 @@ function _memory(cmd) {
|
||||
// Save the line so I can do magic later
|
||||
if (cmd) {
|
||||
const len = self.lines.level.length ? self.lines.level.length - 1 : 0;
|
||||
self.lines.push(' '.repeat(len) + cmd);
|
||||
ArrayPrototypePush(self.lines, StringPrototypeRepeat(' ', len) + cmd);
|
||||
} else {
|
||||
// I don't want to not change the format too much...
|
||||
self.lines.push('');
|
||||
ArrayPrototypePush(self.lines, '');
|
||||
}
|
||||
|
||||
if (!cmd) {
|
||||
@ -1405,8 +1463,8 @@ function _memory(cmd) {
|
||||
|
||||
// Going down is { and ( e.g. function() {
|
||||
// going up is } and )
|
||||
let dw = cmd.match(/[{(]/g);
|
||||
let up = cmd.match(/[})]/g);
|
||||
let dw = StringPrototypeMatch(cmd, /[{(]/g);
|
||||
let up = StringPrototypeMatch(cmd, /[})]/g);
|
||||
up = up ? up.length : 0;
|
||||
dw = dw ? dw.length : 0;
|
||||
let depth = dw - up;
|
||||
@ -1421,13 +1479,13 @@ function _memory(cmd) {
|
||||
// "function()
|
||||
// {" but nothing should break, only tab completion for local
|
||||
// scope will not work for this function.
|
||||
self.lines.level.push({
|
||||
ArrayPrototypePush(self.lines.level, {
|
||||
line: self.lines.length - 1,
|
||||
depth: depth
|
||||
});
|
||||
} else if (depth < 0) {
|
||||
// Going... up.
|
||||
const curr = self.lines.level.pop();
|
||||
const curr = ArrayPrototypePop(self.lines.level);
|
||||
if (curr) {
|
||||
const tmp = curr.depth + depth;
|
||||
if (tmp < 0) {
|
||||
@ -1437,7 +1495,7 @@ function _memory(cmd) {
|
||||
} else if (tmp > 0) {
|
||||
// Remove and push back
|
||||
curr.depth += depth;
|
||||
self.lines.level.push(curr);
|
||||
ArrayPrototypePush(self.lines.level, curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1448,7 +1506,7 @@ function _memory(cmd) {
|
||||
function addCommonWords(completionGroups) {
|
||||
// Only words which do not yet exist as global property should be added to
|
||||
// this list.
|
||||
completionGroups.push([
|
||||
ArrayPrototypePush(completionGroups, [
|
||||
'async', 'await', 'break', 'case', 'catch', 'const', 'continue',
|
||||
'debugger', 'default', 'delete', 'do', 'else', 'export', 'false',
|
||||
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'let',
|
||||
@ -1459,7 +1517,7 @@ function addCommonWords(completionGroups) {
|
||||
|
||||
function _turnOnEditorMode(repl) {
|
||||
repl.editorMode = true;
|
||||
Interface.prototype.setPrompt.call(repl, '');
|
||||
ReflectApply(Interface.prototype.setPrompt, repl, ['']);
|
||||
}
|
||||
|
||||
function _turnOffEditorMode(repl) {
|
||||
@ -1504,15 +1562,15 @@ function defineDefaultCommands(repl) {
|
||||
repl.defineCommand('help', {
|
||||
help: 'Print this help message',
|
||||
action: function() {
|
||||
const names = ObjectKeys(this.commands).sort();
|
||||
const longestNameLength = names.reduce(
|
||||
(max, name) => MathMax(max, name.length),
|
||||
0
|
||||
const names = ArrayPrototypeSort(ObjectKeys(this.commands));
|
||||
const longestNameLength = MathMax(
|
||||
...ArrayPrototypeMap(names, (name) => name.length)
|
||||
);
|
||||
for (let n = 0; n < names.length; n++) {
|
||||
const name = names[n];
|
||||
const cmd = this.commands[name];
|
||||
const spaces = ' '.repeat(longestNameLength - name.length + 3);
|
||||
const spaces =
|
||||
StringPrototypeRepeat(' ', longestNameLength - name.length + 3);
|
||||
const line = `.${name}${cmd.help ? spaces + cmd.help : ''}\n`;
|
||||
this.output.write(line);
|
||||
}
|
||||
@ -1526,7 +1584,7 @@ function defineDefaultCommands(repl) {
|
||||
help: 'Save all evaluated commands in this REPL session to a file',
|
||||
action: function(file) {
|
||||
try {
|
||||
fs.writeFileSync(file, this.lines.join('\n'));
|
||||
fs.writeFileSync(file, ArrayPrototypeJoin(this.lines, '\n'));
|
||||
this.output.write(`Session saved to: ${file}\n`);
|
||||
} catch {
|
||||
this.output.write(`Failed to save: ${file}\n`);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user