| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a5dcff8 commit 0450ce7
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -39,6 +39,8 @@ the following values: | |||
| 39 | 39 | treated like a TTY, and have ANSI/VT100 escape codes written to it. | |
| 40 | 40 | Defaults to checking `isTTY` on the `output` stream upon instantiation. | |
| 41 | 41 | ||
| 42 | + - `historySize` - maximum number of history lines retained. Defaults to `30`. | ||
| 43 | + | ||
| 42 | 44 | The `completer` function is given the current line entered by the user, and | |
| 43 | 45 | is supposed to return an Array with 2 entries: | |
| 44 | 46 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -29,6 +29,18 @@ For example, you could add this to your bashrc file: | |||
| 29 | 29 | ||
| 30 | 30 | alias iojs="env NODE_NO_READLINE=1 rlwrap iojs" | |
| 31 | 31 | ||
| 32 | + The built-in repl (invoked by running `iojs` or `iojs -i`) may be controlled | ||
| 33 | + via the following environment variables: | ||
| 34 | + | ||
| 35 | + - `NODE_REPL_HISTORY_FILE` - if given, must be a path to a user-writable, | ||
| 36 | + user-readable file. When a valid path is given, persistent history support | ||
| 37 | + is enabled: REPL history will persist across `iojs` repl sessions. | ||
| 38 | + - `NODE_REPL_HISTORY_SIZE` - defaults to `1000`. In conjunction with | ||
| 39 | + `NODE_REPL_HISTORY_FILE`, controls how many lines of history will be | ||
| 40 | + persisted. Must be a positive number. | ||
| 41 | + - `NODE_REPL_MODE` - may be any of `sloppy`, `strict`, or `magic`. Defaults | ||
| 42 | + to `magic`, which will automatically run "strict mode only" statements in | ||
| 43 | + strict mode. | ||
| 32 | 44 | ||
| 33 | 45 | ## repl.start(options) | |
| 34 | 46 | ||
@@ -64,6 +76,14 @@ the following values: | |||
| 64 | 76 | returns the formatting (including coloring) to display. Defaults to | |
| 65 | 77 | `util.inspect`. | |
| 66 | 78 | ||
| 79 | + - `replMode` - controls whether the repl runs all commands in strict mode, | ||
| 80 | + default mode, or a hybrid mode ("magic" mode.) Acceptable values are: | ||
| 81 | + * `repl.REPL_MODE_SLOPPY` - run commands in sloppy mode. | ||
| 82 | + * `repl.REPL_MODE_STRICT` - run commands in strict mode. This is equivalent to | ||
| 83 | + prefacing every repl statement with `'use strict'`. | ||
| 84 | + * `repl.REPL_MODE_MAGIC` - attempt to run commands in default mode. If they | ||
| 85 | + fail to parse, re-try in strict mode. | ||
| 86 | + | ||
| 67 | 87 | You can use your own `eval` function if it has following signature: | |
| 68 | 88 | ||
| 69 | 89 | function eval(cmd, context, filename, callback) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,168 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + module.exports = {createRepl: createRepl}; | ||
| 4 | + | ||
| 5 | + const Interface = require('readline').Interface; | ||
| 6 | + const REPL = require('repl'); | ||
| 7 | + const path = require('path'); | ||
| 8 | + | ||
| 9 | + // XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary. | ||
| 10 | + // The debounce is to guard against code pasted into the REPL. | ||
| 11 | + const kDebounceHistoryMS = 15; | ||
| 12 | + | ||
| 13 | + try { | ||
| 14 | + // hack for require.resolve("./relative") to work properly. | ||
| 15 | + module.filename = path.resolve('repl'); | ||
| 16 | + } catch (e) { | ||
| 17 | + // path.resolve('repl') fails when the current working directory has been | ||
| 18 | + // deleted. Fall back to the directory name of the (absolute) executable | ||
| 19 | + // path. It's not really correct but what are the alternatives? | ||
| 20 | + const dirname = path.dirname(process.execPath); | ||
| 21 | + module.filename = path.resolve(dirname, 'repl'); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + // hack for repl require to work properly with node_modules folders | ||
| 25 | + module.paths = require('module')._nodeModulePaths(module.filename); | ||
| 26 | + | ||
| 27 | + function createRepl(env, cb) { | ||
| 28 | + const opts = { | ||
| 29 | + useGlobal: true, | ||
| 30 | + ignoreUndefined: false | ||
| 31 | + }; | ||
| 32 | + | ||
| 33 | + if (parseInt(env.NODE_NO_READLINE)) { | ||
| 34 | + opts.terminal = false; | ||
| 35 | + } | ||
| 36 | + if (parseInt(env.NODE_DISABLE_COLORS)) { | ||
| 37 | + opts.useColors = false; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + opts.replMode = { | ||
| 41 | + 'strict': REPL.REPL_MODE_STRICT, | ||
| 42 | + 'sloppy': REPL.REPL_MODE_SLOPPY, | ||
| 43 | + 'magic': REPL.REPL_MODE_MAGIC | ||
| 44 | + }[String(env.NODE_REPL_MODE).toLowerCase().trim()]; | ||
| 45 | + | ||
| 46 | + if (opts.replMode === undefined) { | ||
| 47 | + opts.replMode = REPL.REPL_MODE_MAGIC; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + const historySize = Number(env.NODE_REPL_HISTORY_SIZE); | ||
| 51 | + if (!isNaN(historySize) && historySize > 0) { | ||
| 52 | + opts.historySize = historySize; | ||
| 53 | + } else { | ||
| 54 | + // XXX(chrisdickinson): set here to avoid affecting existing applications | ||
| 55 | + // using repl instances. | ||
| 56 | + opts.historySize = 1000; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + const repl = REPL.start(opts); | ||
| 60 | + if (env.NODE_REPL_HISTORY_PATH) { | ||
| 61 | + return setupHistory(repl, env.NODE_REPL_HISTORY_PATH, cb); | ||
| 62 | + } | ||
| 63 | + repl._historyPrev = _replHistoryMessage; | ||
| 64 | + cb(null, repl); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + function setupHistory(repl, historyPath, ready) { | ||
| 68 | + const fs = require('fs'); | ||
| 69 | + var timer = null; | ||
| 70 | + var writing = false; | ||
| 71 | + var pending = false; | ||
| 72 | + repl.pause(); | ||
| 73 | + fs.open(historyPath, 'a+', oninit); | ||
| 74 | + | ||
| 75 | + function oninit(err, hnd) { | ||
| 76 | + if (err) { | ||
| 77 | + return ready(err); | ||
| 78 | + } | ||
| 79 | + fs.close(hnd, onclose); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + function onclose(err) { | ||
| 83 | + if (err) { | ||
| 84 | + return ready(err); | ||
| 85 | + } | ||
| 86 | + fs.readFile(historyPath, 'utf8', onread); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + function onread(err, data) { | ||
| 90 | + if (err) { | ||
| 91 | + return ready(err); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + if (data) { | ||
| 95 | + try { | ||
| 96 | + repl.history = JSON.parse(data); | ||
| 97 | + if (!Array.isArray(repl.history)) { | ||
| 98 | + throw new Error('Expected array, got ' + typeof repl.history); | ||
| 99 | + } | ||
| 100 | + repl.history.slice(-repl.historySize); | ||
| 101 | + } catch (err) { | ||
| 102 | + return ready( | ||
| 103 | + new Error(`Could not parse history data in ${historyPath}.`)); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + fs.open(historyPath, 'w', onhandle); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + function onhandle(err, hnd) { | ||
| 111 | + if (err) { | ||
| 112 | + return ready(err); | ||
| 113 | + } | ||
| 114 | + repl._historyHandle = hnd; | ||
| 115 | + repl.on('line', online); | ||
| 116 | + repl.resume(); | ||
| 117 | + return ready(null, repl); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + // ------ history listeners ------ | ||
| 121 | + function online() { | ||
| 122 | + repl._flushing = true; | ||
| 123 | + | ||
| 124 | + if (timer) { | ||
| 125 | + clearTimeout(timer); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + timer = setTimeout(flushHistory, kDebounceHistoryMS); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + function flushHistory() { | ||
| 132 | + timer = null; | ||
| 133 | + if (writing) { | ||
| 134 | + pending = true; | ||
| 135 | + return; | ||
| 136 | + } | ||
| 137 | + writing = true; | ||
| 138 | + const historyData = JSON.stringify(repl.history, null, 2); | ||
| 139 | + fs.write(repl._historyHandle, historyData, 0, 'utf8', onwritten); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + function onwritten(err, data) { | ||
| 143 | + writing = false; | ||
| 144 | + if (pending) { | ||
| 145 | + pending = false; | ||
| 146 | + online(); | ||
| 147 | + } else { | ||
| 148 | + repl._flushing = Boolean(timer); | ||
| 149 | + if (!repl._flushing) { | ||
| 150 | + repl.emit('flushHistory'); | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + function _replHistoryMessage() { | ||
| 158 | + if (this.history.length === 0) { | ||
| 159 | + this._writeToOutput( | ||
| 160 | + '\nPersistent history support disabled. ' + | ||
| 161 | + 'Set the NODE_REPL_HISTORY_PATH environment variable to ' + | ||
| 162 | + 'a valid, user-writable path to enable.\n' | ||
| 163 | + ); | ||
| 164 | + this._refreshLine(); | ||
| 165 | + } | ||
| 166 | + this._historyPrev = Interface.prototype._historyPrev; | ||
| 167 | + return this._historyPrev(); | ||
| 168 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -273,6 +273,17 @@ Module._load = function(request, parent, isMain) { | |||
| 273 | 273 | debug('Module._load REQUEST ' + (request) + ' parent: ' + parent.id); | |
| 274 | 274 | } | |
| 275 | 275 | ||
| 276 | + // REPL is a special case, because it needs the real require. | ||
| 277 | + if (request === 'internal/repl' || request === 'repl') { | ||
| 278 | + if (Module._cache[request]) { | ||
| 279 | + return Module._cache[request]; | ||
| 280 | + } | ||
| 281 | + var replModule = new Module(request); | ||
| 282 | + replModule._compile(NativeModule.getSource(request), `${request}.js`); | ||
| 283 | + NativeModule._cache[request] = replModule; | ||
| 284 | + return replModule.exports; | ||
| 285 | + } | ||
| 286 | + | ||
| 276 | 287 | var filename = Module._resolveFilename(request, parent); | |
| 277 | 288 | ||
| 278 | 289 | var cachedModule = Module._cache[filename]; | |
@@ -281,14 +292,6 @@ Module._load = function(request, parent, isMain) { | |||
| 281 | 292 | } | |
| 282 | 293 | ||
| 283 | 294 | if (NativeModule.nonInternalExists(filename)) { | |
| 284 | - // REPL is a special case, because it needs the real require. | ||
| 285 | - if (filename == 'repl') { | ||
| 286 | - var replModule = new Module('repl'); | ||
| 287 | - replModule._compile(NativeModule.getSource('repl'), 'repl.js'); | ||
| 288 | - NativeModule._cache.repl = replModule; | ||
| 289 | - return replModule.exports; | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | 295 | debug('load native module ' + request); | |
| 293 | 296 | return NativeModule.require(filename); | |
| 294 | 297 | } | |
@@ -502,7 +505,7 @@ Module._initPaths = function() { | |||
| 502 | 505 | ||
| 503 | 506 | // bootstrap repl | |
| 504 | 507 | Module.requireRepl = function() { | |
| 505 | - return Module._load('repl', '.'); | ||
| 508 | + return Module._load('internal/repl', '.'); | ||
| 506 | 509 | }; | |
| 507 | 510 | ||
| 508 | 511 | Module._initPaths(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -35,21 +35,30 @@ function Interface(input, output, completer, terminal) { | |||
| 35 | 35 | this._sawReturn = false; | |
| 36 | 36 | ||
| 37 | 37 | EventEmitter.call(this); | |
| 38 | + var historySize; | ||
| 38 | 39 | ||
| 39 | 40 | if (arguments.length === 1) { | |
| 40 | 41 | // an options object was given | |
| 41 | 42 | output = input.output; | |
| 42 | 43 | completer = input.completer; | |
| 43 | 44 | terminal = input.terminal; | |
| 45 | + historySize = input.historySize; | ||
| 44 | 46 | input = input.input; | |
| 45 | 47 | } | |
| 48 | + historySize = historySize || kHistorySize; | ||
| 46 | 49 | ||
| 47 | 50 | completer = completer || function() { return []; }; | |
| 48 | 51 | ||
| 49 | 52 | if (typeof completer !== 'function') { | |
| 50 | 53 | throw new TypeError('Argument \'completer\' must be a function'); | |
| 51 | 54 | } | |
| 52 | 55 | ||
| 56 | + if (typeof historySize !== 'number' || | ||
| 57 | + isNaN(historySize) || | ||
| 58 | + historySize < 0) { | ||
| 59 | + throw new TypeError('Argument \'historySize\' must be a positive number'); | ||
| 60 | + } | ||
| 61 | + | ||
| 53 | 62 | // backwards compat; check the isTTY prop of the output stream | |
| 54 | 63 | // when `terminal` was not specified | |
| 55 | 64 | if (terminal === undefined && !(output === null || output === undefined)) { | |
@@ -60,6 +69,7 @@ function Interface(input, output, completer, terminal) { | |||
| 60 | 69 | ||
| 61 | 70 | this.output = output; | |
| 62 | 71 | this.input = input; | |
| 72 | + this.historySize = historySize; | ||
| 63 | 73 | ||
| 64 | 74 | // Check arity, 2 - for async, 1 for sync | |
| 65 | 75 | this.completer = completer.length === 2 ? completer : function(v, callback) { | |
@@ -214,7 +224,7 @@ Interface.prototype._addHistory = function() { | |||
| 214 | 224 | this.history.unshift(this.line); | |
| 215 | 225 | ||
| 216 | 226 | // Only store so many | |
| 217 | - if (this.history.length > kHistorySize) this.history.pop(); | ||
| 227 | + if (this.history.length > this.historySize) this.history.pop(); | ||
| 218 | 228 | } | |
| 219 | 229 | ||
| 220 | 230 | this.historyIndex = -1; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments