diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/repl.js | 160 | ||||
-rw-r--r-- | lib/internal/repl/history.js | 153 | ||||
-rw-r--r-- | lib/repl.js | 5 |
3 files changed, 160 insertions, 158 deletions
diff --git a/lib/internal/repl.js b/lib/internal/repl.js index e58752e76e..321f4ab29e 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -1,24 +1,10 @@ 'use strict'; -const { Interface } = require('readline'); const REPL = require('repl'); -const path = require('path'); -const fs = require('fs'); -const os = require('os'); -const util = require('util'); -const debug = util.debuglog('repl'); + module.exports = Object.create(REPL); module.exports.createInternalRepl = createRepl; -// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary. -// The debounce is to guard against code pasted into the REPL. -const kDebounceHistoryMS = 15; - -function _writeToOutput(repl, message) { - repl._writeToOutput(message); - repl._refreshLine(); -} - function createRepl(env, opts, cb) { if (typeof opts === 'function') { cb = opts; @@ -55,151 +41,9 @@ function createRepl(env, opts, cb) { if (!Number.isNaN(historySize) && historySize > 0) { opts.historySize = historySize; } else { - // XXX(chrisdickinson): set here to avoid affecting existing applications - // using repl instances. opts.historySize = 1000; } const repl = REPL.start(opts); - if (opts.terminal) { - return setupHistory(repl, env.NODE_REPL_HISTORY, cb); - } - - repl._historyPrev = _replHistoryMessage; - cb(null, repl); -} - -function setupHistory(repl, historyPath, ready) { - // Empty string disables persistent history - if (typeof historyPath === 'string') - historyPath = historyPath.trim(); - - if (historyPath === '') { - repl._historyPrev = _replHistoryMessage; - return ready(null, repl); - } - - if (!historyPath) { - try { - historyPath = path.join(os.homedir(), '.node_repl_history'); - } catch (err) { - _writeToOutput(repl, '\nError: Could not get the home directory.\n' + - 'REPL session history will not be persisted.\n'); - - debug(err.stack); - repl._historyPrev = _replHistoryMessage; - return ready(null, repl); - } - } - - var timer = null; - var writing = false; - var pending = false; - repl.pause(); - // History files are conventionally not readable by others: - // https://github.com/nodejs/node/issues/3392 - // https://github.com/nodejs/node/pull/3394 - fs.open(historyPath, 'a+', 0o0600, oninit); - - function oninit(err, hnd) { - if (err) { - // Cannot open history file. - // Don't crash, just don't persist history. - _writeToOutput(repl, '\nError: Could not open history file.\n' + - 'REPL session history will not be persisted.\n'); - debug(err.stack); - - repl._historyPrev = _replHistoryMessage; - repl.resume(); - return ready(null, repl); - } - fs.close(hnd, onclose); - } - - function onclose(err) { - if (err) { - return ready(err); - } - fs.readFile(historyPath, 'utf8', onread); - } - - function onread(err, data) { - if (err) { - return ready(err); - } - - if (data) { - repl.history = data.split(/[\n\r]+/, repl.historySize); - } else { - repl.history = []; - } - - fs.open(historyPath, 'r+', onhandle); - } - - function onhandle(err, hnd) { - if (err) { - return ready(err); - } - fs.ftruncate(hnd, 0, (err) => { - repl._historyHandle = hnd; - repl.on('line', online); - - // Reading the file data out erases it - repl.once('flushHistory', function() { - repl.resume(); - ready(null, repl); - }); - flushHistory(); - }); - } - - // ------ history listeners ------ - function online() { - repl._flushing = true; - - if (timer) { - clearTimeout(timer); - } - - timer = setTimeout(flushHistory, kDebounceHistoryMS); - } - - function flushHistory() { - timer = null; - if (writing) { - pending = true; - return; - } - writing = true; - const historyData = repl.history.join(os.EOL); - fs.write(repl._historyHandle, historyData, 0, 'utf8', onwritten); - } - - function onwritten(err, data) { - writing = false; - if (pending) { - pending = false; - online(); - } else { - repl._flushing = Boolean(timer); - if (!repl._flushing) { - repl.emit('flushHistory'); - } - } - } -} - - -function _replHistoryMessage() { - if (this.history.length === 0) { - _writeToOutput( - this, - '\nPersistent history support disabled. ' + - 'Set the NODE_REPL_HISTORY environment\nvariable to ' + - 'a valid, user-writable path to enable.\n' - ); - } - this._historyPrev = Interface.prototype._historyPrev; - return this._historyPrev(); + repl.setupHistory(opts.terminal ? env.NODE_REPL_HISTORY : '', cb); } diff --git a/lib/internal/repl/history.js b/lib/internal/repl/history.js new file mode 100644 index 0000000000..a0ae07441e --- /dev/null +++ b/lib/internal/repl/history.js @@ -0,0 +1,153 @@ +'use strict'; + +const { Interface } = require('readline'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const util = require('util'); +const debug = util.debuglog('repl'); + +// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary. +// The debounce is to guard against code pasted into the REPL. +const kDebounceHistoryMS = 15; + +module.exports = setupHistory; + +function _writeToOutput(repl, message) { + repl._writeToOutput(message); + repl._refreshLine(); +} + +function setupHistory(repl, historyPath, ready) { + // Empty string disables persistent history + if (typeof historyPath === 'string') + historyPath = historyPath.trim(); + + if (historyPath === '') { + repl._historyPrev = _replHistoryMessage; + return ready(null, repl); + } + + if (!historyPath) { + try { + historyPath = path.join(os.homedir(), '.node_repl_history'); + } catch (err) { + _writeToOutput(repl, '\nError: Could not get the home directory.\n' + + 'REPL session history will not be persisted.\n'); + + debug(err.stack); + repl._historyPrev = _replHistoryMessage; + return ready(null, repl); + } + } + + var timer = null; + var writing = false; + var pending = false; + repl.pause(); + // History files are conventionally not readable by others: + // https://github.com/nodejs/node/issues/3392 + // https://github.com/nodejs/node/pull/3394 + fs.open(historyPath, 'a+', 0o0600, oninit); + + function oninit(err, hnd) { + if (err) { + // Cannot open history file. + // Don't crash, just don't persist history. + _writeToOutput(repl, '\nError: Could not open history file.\n' + + 'REPL session history will not be persisted.\n'); + debug(err.stack); + + repl._historyPrev = _replHistoryMessage; + repl.resume(); + return ready(null, repl); + } + fs.close(hnd, onclose); + } + + function onclose(err) { + if (err) { + return ready(err); + } + fs.readFile(historyPath, 'utf8', onread); + } + + function onread(err, data) { + if (err) { + return ready(err); + } + + if (data) { + repl.history = data.split(/[\n\r]+/, repl.historySize); + } else { + repl.history = []; + } + + fs.open(historyPath, 'r+', onhandle); + } + + function onhandle(err, hnd) { + if (err) { + return ready(err); + } + fs.ftruncate(hnd, 0, (err) => { + repl._historyHandle = hnd; + repl.on('line', online); + + // Reading the file data out erases it + repl.once('flushHistory', function() { + repl.resume(); + ready(null, repl); + }); + flushHistory(); + }); + } + + // ------ history listeners ------ + function online(line) { + repl._flushing = true; + + if (timer) { + clearTimeout(timer); + } + + timer = setTimeout(flushHistory, kDebounceHistoryMS); + } + + function flushHistory() { + timer = null; + if (writing) { + pending = true; + return; + } + writing = true; + const historyData = repl.history.join(os.EOL); + fs.write(repl._historyHandle, historyData, 0, 'utf8', onwritten); + } + + function onwritten(err, data) { + writing = false; + if (pending) { + pending = false; + online(); + } else { + repl._flushing = Boolean(timer); + if (!repl._flushing) { + repl.emit('flushHistory'); + } + } + } +} + +function _replHistoryMessage() { + if (this.history.length === 0) { + _writeToOutput( + this, + '\nPersistent history support disabled. ' + + 'Set the NODE_REPL_HISTORY environment\nvariable to ' + + 'a valid, user-writable path to enable.\n' + ); + } + this._historyPrev = Interface.prototype._historyPrev; + return this._historyPrev(); +} diff --git a/lib/repl.js b/lib/repl.js index f1b269b801..1f89f43c24 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -82,6 +82,7 @@ const { startSigintWatchdog, stopSigintWatchdog } = internalBinding('util'); +const history = require('internal/repl/history'); // Lazy-loaded. let processTopLevelAwait; @@ -762,6 +763,10 @@ exports.start = function(prompt, return repl; }; +REPLServer.prototype.setupHistory = function setupHistory(historyFile, cb) { + history(this, historyFile, cb); +}; + REPLServer.prototype.clearBufferedCommand = function clearBufferedCommand() { this[kBufferedCommandSymbol] = ''; }; |