From 84ea6fc57ce75448431c85684139b8a241503f81 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 26 May 2016 11:36:20 -0700 Subject: [PATCH] doc: general improvements to repl.md copy The repl documentation has always been rather lacking. This is a first step towards making significant improvements. PR-URL: https://github.com/nodejs/node/pull/7002 Reviewed-By: Anna Henningsen --- doc/api/repl.md | 541 ++++++++++++++++++++++++++++++------------------ 1 file changed, 335 insertions(+), 206 deletions(-) diff --git a/doc/api/repl.md b/doc/api/repl.md index 26f2889f0b45f3..5437880eb36827 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -2,82 +2,131 @@ Stability: 2 - Stable -A Read-Eval-Print-Loop (REPL) is available both as a standalone program and -easily includable in other programs. The REPL provides a way to interactively -run JavaScript and see the results. It can be used for debugging, testing, or -just trying things out. - -By executing `node` without any arguments from the command-line you will be -dropped into the REPL. It has simplistic emacs line-editing. +The `repl` module provides a Read-Eval-Print-Loop (REPL) implementation that +is available both as a standalone program or includable in other applications. +It can be accessed using: +```js +const repl = require('repl'); ``` -$ node -Type '.help' for options. -> a = [1, 2, 3]; -[ 1, 2, 3 ] -> a.forEach((v) => { -... console.log(v); -... }); -1 + +## Design and Features + +The `repl` module exports the `repl.REPLServer` class. While running, instances +of `repl.REPLServer` will accept individual lines of user input, evaluate those +according to a user-defined evaluation function, then output the result. Input +and output may be from `stdin` and `stdout`, respectively, or may be connected +to any Node.js [stream][]. + +Instances of `repl.REPLServer` support automatic completion of inputs, +simplistic Emacs-style line editing, multi-line inputs, ANSI-styled output, +saving and restoring current REPL session state, error recovery, and +customizable evaluation functions. + +### Commands and Special Keys + +The following special commands are supported by all REPL instances: + +* `.break` - When in the process of inputting a multi-line expression, entering + the `.break` command (or pressing the `-C` key combination) will abort + further input or processing of that expression. +* `.clear` - Resets the REPL `context` to an empty object and clears any + multi-line expression currently being input. +* `.exit` - Close the I/O stream, causing the REPL to exit. +* `.help` - Show this list of special commands. +* `.save` - Save the current REPL session to a file: + `> .save ./file/to/save.js` +* `.load` - Load a file into the current REPL session. + `> .load ./file/to/load.js` + +The following key combinations in the REPL have these special effects: + +* `-C` - When pressed once, has the same effect as the `.break` command. + When pressed twice on a blank line, has the same effect as the `.exit` + command. +* `-D` - Has the same effect as the `.exit` command. +* `` - When pressed on a blank line, displays global and local(scope) + variables. When pressed while entering other input, displays relevant + autocompletion options. + +### Default Evaluation + +By default, all instances of `repl.REPLServer` use an evaluation function that +evaluates JavaScript expressions and provides access to Node.js' built-in +modules. This default behavior can be overridden by passing in an alternative +evaluation function when the `repl.REPLServer` instance is created. + +#### JavaScript Expressions + +The default evaluator supports direct evaluation of JavaScript expressions: + +```js +> 1 + 1 2 +> var m = 2 +undefined +> m + 1 3 ``` -For advanced line-editors, start Node.js with the environmental variable -`NODE_NO_READLINE=1`. This will start the main and debugger REPL in canonical -terminal settings which will allow you to use with `rlwrap`. +Unless otherwise scoped within blocks (e.g. `{ ... }`) or functions, variables +declared either implicitly or using the `var` keyword are declared at the +`global` scope. -For example, you could add this to your bashrc file: +#### Global and Local Scope -``` -alias node="env NODE_NO_READLINE=1 rlwrap node" -``` - -## Environment Variable Options +The default evaluator provides access to any variables that exist in the global +scope. It is possible to expose a variable to the REPL explicitly by assigning +it to the `context` object associated with each `REPLServer`. For example: -The built-in repl (invoked by running `node` or `node -i`) may be controlled -via the following environment variables: +```js +const repl = require('repl'); +var msg = 'message'; - - `NODE_REPL_HISTORY` - When a valid path is given, persistent REPL history - will be saved to the specified file rather than `.node_repl_history` in the - user's home directory. Setting this value to `""` will disable persistent - REPL history. Whitespace will be trimmed from the value. - - `NODE_REPL_HISTORY_SIZE` - Defaults to `1000`. Controls how many lines of - history will be persisted if history is available. Must be a positive number. - - `NODE_REPL_MODE` - May be any of `sloppy`, `strict`, or `magic`. Defaults - to `magic`, which will automatically run "strict mode only" statements in - strict mode. +repl.start('> ').context.m = msg; +``` -## Persistent History +Properties in the `context` object appear as local within the REPL: -By default, the REPL will persist history between `node` REPL sessions by saving -to a `.node_repl_history` file in the user's home directory. This can be -disabled by setting the environment variable `NODE_REPL_HISTORY=""`. +```js +$ node repl_test.js +> m +'message' +``` -### NODE_REPL_HISTORY_FILE +It is important to note that context properties are *not* read-only by default. +To specify read-only globals, context properties must be defined using +`Object.defineProperty()`: - Stability: 0 - Deprecated: Use `NODE_REPL_HISTORY` instead. +```js +const repl = require('repl'); +var msg = 'message'; -Previously in Node.js/io.js v2.x, REPL history was controlled by using a -`NODE_REPL_HISTORY_FILE` environment variable, and the history was saved in JSON -format. This variable has now been deprecated, and your REPL history will -automatically be converted to using plain text. The new file will be saved to -either your home directory, or a directory defined by the `NODE_REPL_HISTORY` -variable, as documented [here](#repl_environment_variable_options). +const r = repl.start('> '); +Object.defineProperty(r, 'm', { + configurable: false, + enumerable: true, + value: msg +}); +``` -## REPL Features +#### Accessing Core Node.js Modules - +The default evaluator will automatically load Node.js core modules into the +REPL environment when used. For instance, unless otherwise declared as a +global or scoped variable, the input `fs` will be evaluated on-demand as +`global.fs = require('fs')`. -Inside the REPL, Control+D will exit. Multi-line expressions can be input. -Tab completion is supported for both global and local variables. +```js +> fs.createReadStream('./some/file'); +``` -Core modules will be loaded on-demand into the environment. For example, -accessing `fs` will `require()` the `fs` module as `global.fs`. +#### Assignment of the `_` (underscore) variable -The special variable `_` (underscore) contains the result of the last expression. +The default evaluator will, by default, assign the result of the most recently +evaluated expression to the special variable `_` (underscore). -``` +```js > [ 'a', 'b', 'c' ] [ 'a', 'b', 'c' ] > _.length @@ -86,239 +135,318 @@ The special variable `_` (underscore) contains the result of the last expression 4 ``` -Explicitly setting `_` will disable this behavior until the context is reset. +Explicitly setting `_` to a value will disable this behavior. + +### Custom Evaluation Functions + +When a new `repl.REPLServer` is created, a custom evaluation function may be +provided. This can be used, for instance, to implement fully customized REPL +applications. -The REPL provides access to any variables in the global scope. You can expose -a variable to the REPL explicitly by assigning it to the `context` object -associated with each `REPLServer`. For example: +The following illustrates a hypothetical example of a REPL that performs +translation of text from one language to another: ```js -// repl_test.js const repl = require('repl'); -var msg = 'message'; +const Translator = require('translator').Translator; -repl.start('> ').context.m = msg; -``` +const myTranslator = new Translator('en', 'fr'); -Things in the `context` object appear as local within the REPL: +function myEval(cmd, context, filename, callback) { + callback(null, myTranslator.translate(cmd)); +} -``` -$ node repl_test.js -> m -'message' +repl.start({prompt: '> ', eval: myEval}); ``` -There are a few special REPL commands: +#### Recoverable Errors - - `.break` - While inputting a multi-line expression, sometimes you get lost - or just don't care about completing it. `.break` will start over. - - `.clear` - Resets the `context` object to an empty object and clears any - multi-line expression. - - `.exit` - Close the I/O stream, which will cause the REPL to exit. - - `.help` - Show this list of special commands. - - `.save` - Save the current REPL session to a file - >.save ./file/to/save.js - - `.load` - Load a file into the current REPL session. - >.load ./file/to/load.js +As a user is typing input into the REPL prompt, pressing the `` key will +send the current line of input to the `eval` function. In order to support +multi-line input, the eval function can return an instance of `repl.Recoverable` +to the provided callback function: -The following key combinations in the REPL have these special effects: +```js +function eval(cmd, context, filename, callback) { + var result; + try { + result = vm.runInThisContext(cmd); + } catch (e) { + if (isRecoverableError(e)) { + return callback(new repl.Recoverable(e)); + } + } + callback(null, result); +} - - `C` - Similar to the `.break` keyword. Terminates the current - command. Press twice on a blank line to forcibly exit. - - `D` - Similar to the `.exit` keyword. - - `` - Show both global and local(scope) variables +function isRecoverableError(error) { + if (error.name === 'SyntaxError') { + return /^(Unexpected end of input|Unexpected token)/.test(error.message); + } + return false; +} +``` +### Customizing REPL Output -### Customizing Object displays in the REPL +By default, `repl.REPLServer` instances format output using the +[`util.inspect()`][] method before writing the output to the provided Writable +stream (`process.stdout` by default). The `useColors` boolean option can be +specified at construction to instruct the default writer to use ANSI style +codes to colorize the output from the `util.inspect()` method. -The REPL module internally uses -[`util.inspect()`][], when printing values. However, `util.inspect` delegates the - call to the object's `inspect()` function, if it has one. You can read more - about this delegation [here][]. +It is possible to fully customize the output of a `repl.REPLServer` instance +by passing a new function in using the `writer` option on construction. The +following example, for instance, simply converts any input text to upper case: -For example, if you have defined an `inspect()` function on an object, like this: +```js +const repl = require('repl'); -``` -> var obj = {foo: 'this will not show up in the inspect() output'}; -undefined -> obj.inspect = () => { -... return {bar: 'baz'}; -... }; -[Function] -``` +const r = repl.start({prompt: '>', eval: myEval, writer: myWriter}); -and try to print `obj` in REPL, it will invoke the custom `inspect()` function: +function myEval(cmd, context, filename, callback) { + callback(null,cmd); +} -``` -> obj -{bar: 'baz'} +function myWriter(output) { + return output.toUpperCase(); +} ``` ## Class: REPLServer -This inherits from [Readline Interface][] with the following events: +The `repl.REPLServer` class inherits from the [`readline.Interface`][] class. +Instances of `repl.REPLServer` are created using the `repl.start()` method and +*should not* be created directly using the JavaScript `new` keyword. ### Event: 'exit' -`function () {}` - -Emitted when the user exits the REPL in any of the defined ways. Namely, typing -`.exit` at the repl, pressing Ctrl+C twice to signal `SIGINT`, or pressing Ctrl+D -to signal `'end'` on the `input` stream. - -Example of listening for `exit`: +The `'exit'` event is emitted when the REPL is exited either by receiving the +`.exit` command as input, the user pressing `-C` twice to signal `SIGINT`, +or by pressing `-D` to signal `'end'` on the input stream. The listener +callback is invoked without any arguments. ```js replServer.on('exit', () => { - console.log('Got "exit" event from repl!'); + console.log('Received "exit" event from repl!'); process.exit(); }); ``` - ### Event: 'reset' -`function (context) {}` +The `'reset'` event is emitted when the REPL's context is reset. This occurs +whenever the `.clear` command is received as input *unless* the REPL is using +the default evaluator and the `repl.REPLServer` instance was created with the +`useGlobal` option set to `true`. The listener callback will be called with a +reference to the `context` object as the only argument. + +This can be used primarily to re-initialize REPL context to some pre-defined +state as illustrated in the following simple example: + +```js +const repl = require('repl'); -Emitted when the REPL's context is reset. This happens when you type `.clear`. -If you start the repl with `{ useGlobal: true }` then this event will never -be emitted. +function initializeContext(context) { + context.m = 'test'; +} -Example of listening for `reset`: +var r = repl.start({prompt: '>'}); +initializeContext(r.context); + +r.on('reset', initializeContext); +``` + +When this code is executed, the global `'m'` variable can be modified but then +reset to its initial value using the `.clear` command: ```js -// Extend the initial repl context. -var replServer = repl.start({ options ... }); -someExtension.extend(r.context); - -// When a new context is created extend it as well. -replServer.on('reset', (context) => { - console.log('repl has a new context'); - someExtension.extend(context); -}); +$ ./node example.js +>m +'test' +>m = 1 +1 +>m +1 +>.clear +Clearing context... +>m +'test' +> ``` ### replServer.defineCommand(keyword, cmd) -* `keyword` {String} -* `cmd` {Object|Function} - -Makes a command available in the REPL. The command is invoked by typing a `.` -followed by the keyword. The `cmd` is an object with the following values: +* `keyword` {String} The command keyword (*without* a leading `.` character). +* `cmd` {Object|Function} The function to invoke when the command is processed. - - `help` - help text to be displayed when `.help` is entered (Optional). - - `action` - a function to execute, potentially taking in a string argument, - when the command is invoked, bound to the REPLServer instance (Required). +The `replServer.defineCommand()` method is used to add new `.`-prefixed commands +to the REPL instance. Such commands are invoked by typing a `.` followed by the +`keyword`. The `cmd` is either a Function or an object with the following +properties: -If a function is provided instead of an object for `cmd`, it is treated as the -`action`. +* `help` {String} Help text to be displayed when `.help` is entered (Optional). +* `action` {Function} The function to execute, optionally accepting a single + string argument. -Example of defining a command: +The following example shows two new commands added to the REPL instance: ```js -// repl_test.js const repl = require('repl'); -var replServer = repl.start(); +var replServer = repl.start({prompt: '> '}); replServer.defineCommand('sayhello', { help: 'Say hello', action: function(name) { + this.lineParser.reset(); + this.bufferedCommand = ''; this.write(`Hello, ${name}!\n`); this.displayPrompt(); } }); +replServer.defineCommand('saybye', function() { + this.write('Goodbye!\n'); + this.close(); +}); ``` -Example of invoking that command from the REPL: +The new commands can then be used from within the REPL instance: ``` > .sayhello Node.js User Hello, Node.js User! +> .saybye +Goodbye! ``` ### replServer.displayPrompt([preserveCursor]) * `preserveCursor` {Boolean} -Like [`readline.prompt`][] except also adding indents with ellipses when inside -blocks. The `preserveCursor` argument is passed to [`readline.prompt`][]. This is -used primarily with `defineCommand`. It's also used internally to render each -prompt line. +The `replServer.displayPrompt()` method readies the REPL instance for input +from the user, printing the configured `prompt` to a new line in the `output` +and resuming the `input` to accept new input. + +When multi-line input is being entered, an ellipsis is printed rather than the +'prompt'. + +When `preserveCursor` is `true`, the cursor placement will not be reset to `0`. + +The `replServer.displayPrompt` method is primarily intended to be called from +within the action function for commands registered using the +`replServer.defineCommand()` method. ## repl.start([options]) -Returns and starts a `REPLServer` instance, that inherits from -[Readline Interface][]. Accepts an "options" Object that takes -the following values: +* `options` {Object} + * `prompt` {String} The input prompt to display. Defaults to `> `. + * `input` {Readable} The Readable stream from which REPL input will be read. + Defaults to `process.stdin`. + * `output` {Writable} The Writable stream to which REPL output will be + written. Defaults to `process.stdout`. + * `terminal` {boolean} If `true`, specifies that the `output` should be + treated as a a TTY terminal, and have ANSI/VT100 escape codes written to it. + Defaults to checking the value of the `isTTY` property on the `output` + stream upon instantiation. + * `eval` {Function} The function to be used when evaluating each given line + of input. Defaults to an async wrapper for the JavaScript `eval()` + function. An `eval` function can error with `repl.Recoverable` to indicate + the input was incomplete and prompt for additional lines. + * `useColors` {boolean} If `true`, specifies that the default `writer` + function should include ANSI color styling to REPL output. If a custom + `writer` function is provided then this has no effect. Defaults to the + REPL instances `terminal` value. + * `useGlobal` {boolean} If `true`, specifies that the default evaluation + function will use the JavaScript `global` as the context as opposed to + creating a new separate context for the REPL instance. Defaults to `false`. + * `ignoreUndefined` {boolean} If `true`, specifies that the default writer + will not output the return value of a command if it evaluates to + `undefined`. Defaults to `false`. + * `writer` {Function} The function to invoke to format the output of each + command before writing to `output`. Defaults to [`util.inspect()`][]. + * `replMode` - A flag that specifies whether the default evaluator executes + all JavaScript commands in strict mode, default mode, or a hybrid mode + ("magic" mode.) Acceptable values are: + * `repl.REPL_MODE_SLOPPY` - evaluates expressions in sloppy mode. + * `repl.REPL_MODE_STRICT` - evaluates expressions in strict mode. This is + equivalent to prefacing every repl statement with `'use strict'`. + * `repl.REPL_MODE_MAGIC` - attempt to evaluates expressions in default + mode. If expressions fail to parse, re-try in strict mode. + +The `repl.start()` method creates and starts a `repl.REPLServer` instance. + +## The Node.js REPL + +Node.js itself uses the `repl` module to provide its own interactive interface +for executing JavaScript. This can used by executing the Node.js binary without +passing any arguments (or by passing the `-i` argument): - - `prompt` - the prompt and `stream` for all I/O. Defaults to `> `. +```js +$ node +> a = [1, 2, 3]; +[ 1, 2, 3 ] +> a.forEach((v) => { +... console.log(v); +... }); +1 +2 +3 +``` - - `input` - the readable stream to listen to. Defaults to `process.stdin`. +### Environment Variable Options - - `output` - the writable stream to write readline data to. Defaults to - `process.stdout`. +Various behaviors of the Node.js REPL can be customized using the following +environment variables: - - `terminal` - pass `true` if the `stream` should be treated like a TTY, and - have ANSI/VT100 escape codes written to it. Defaults to checking `isTTY` - on the `output` stream upon instantiation. + - `NODE_REPL_HISTORY` - When a valid path is given, persistent REPL history + will be saved to the specified file rather than `.node_repl_history` in the + user's home directory. Setting this value to `""` will disable persistent + REPL history. Whitespace will be trimmed from the value. + - `NODE_REPL_HISTORY_SIZE` - Defaults to `1000`. Controls how many lines of + history will be persisted if history is available. Must be a positive number. + - `NODE_REPL_MODE` - May be any of `sloppy`, `strict`, or `magic`. Defaults + to `magic`, which will automatically run "strict mode only" statements in + strict mode. - - `eval` - a function that will be used to eval each given line. Defaults to - an async wrapper for `eval()`. An `eval` function can error with - `repl.Recoverable` to indicate the code was incomplete and prompt for more - lines. See below for an example of a custom `eval`. +### Persistent History - - `useColors` - a boolean which specifies whether or not the `writer` function - should output colors. If a different `writer` function is set then this does - nothing. Defaults to the repl's `terminal` value. +By default, the Node.js REPL will persist history between `node` REPL sessions +by saving inputs to a `.node_repl_history` file located in the user's home +directory. This can be disabled by setting the environment variable +`NODE_REPL_HISTORY=""`. - - `useGlobal` - if set to `true`, then the repl will use the `global` object, - instead of running scripts in a separate context. Defaults to `false`. +#### NODE_REPL_HISTORY_FILE - - `ignoreUndefined` - if set to `true`, then the repl will not output the - return value of command if it's `undefined`. Defaults to `false`. + Stability: 0 - Deprecated: Use `NODE_REPL_HISTORY` instead. - - `writer` - the function to invoke for each command that gets evaluated which - returns the formatting (including coloring) to display. Defaults to - `util.inspect`. +Previously in Node.js/io.js v2.x, REPL history was controlled by using a +`NODE_REPL_HISTORY_FILE` environment variable, and the history was saved in JSON +format. This variable has now been deprecated, and the old JSON REPL history +file will be automatically converted to a simplified plain text format. This new +file will be saved to either the user's home directory, or a directory defined +by the `NODE_REPL_HISTORY` variable, as documented in the +[Environment Variable Options](#repl_environment_variable_options). - - `replMode` - controls whether the repl runs all commands in strict mode, - default mode, or a hybrid mode ("magic" mode.) Acceptable values are: - * `repl.REPL_MODE_SLOPPY` - run commands in sloppy mode. - * `repl.REPL_MODE_STRICT` - run commands in strict mode. This is equivalent to - prefacing every repl statement with `'use strict'`. - * `repl.REPL_MODE_MAGIC` - attempt to run commands in default mode. If they - fail to parse, re-try in strict mode. +### Using the Node.js REPL with advanced line-editors -It is possible to use a custom `eval` function as illustrated below: +For advanced line-editors, start Node.js with the environmental variable +`NODE_NO_READLINE=1`. This will start the main and debugger REPL in canonical +terminal settings which will allow you to use with `rlwrap`. -```js -function eval(cmd, context, filename, callback) { - var result; - try { - result = vm.runInThisContext(cmd); - } catch (e) { - if (isRecoverableError(e)) { - return callback(new repl.Recoverable(e)); - } - } - callback(null, result); -} +For example, you could add this to your bashrc file: -function isRecoverableError(error) { - if (error.name === 'SyntaxError') { - return /^(Unexpected end of input|Unexpected token)/.test(error.message); - } - return false; -} +```text +alias node="env NODE_NO_READLINE=1 rlwrap node" ``` -On tab completion, `eval` will be called with `.scope` as an input string. It -is expected to return an array of scope names to be used for the auto-completion. +### Starting multiple REPL instances against a single running instance -Multiple REPLs may be started against the same running instance of Node.js. Each -will share the same global object but will have unique I/O. +It is possible to create and run multiple REPL instances against a single +running instance of Node.js that share a single `global` object but have +separate I/O interfaces. -Here is an example that starts a REPL on stdin, a Unix socket, and a TCP socket: +The following example, for instance, provides separate REPLs on `stdin`, a Unix +socket, and a TCP socket: ```js const net = require('net'); @@ -354,13 +482,13 @@ net.createServer((socket) => { }).listen(5001); ``` -Running this program from the command line will start a REPL on stdin. Other -REPL clients may connect through the Unix socket or TCP socket. `telnet` is useful -for connecting to TCP sockets, and `socat` can be used to connect to both Unix and -TCP sockets. +Running this application from the command line will start a REPL on stdin. +Other REPL clients may connect through the Unix socket or TCP socket. `telnet`, +for instance, is useful for connecting to TCP sockets, while `socat` can be used +to connect to both Unix and TCP sockets. -By starting a REPL from a Unix socket-based server instead of stdin, you can -connect to a long-running Node.js process without restarting it. +By starting a REPL from a Unix socket-based server instead of stdin, it is +possible to connect to a long-running Node.js process without restarting it. For an example of running a "full-featured" (`terminal`) REPL over a `net.Server` and `net.Socket` instance, see: https://gist.github.com/2209310 @@ -368,7 +496,8 @@ a `net.Server` and `net.Socket` instance, see: https://gist.github.com/2209310 For an example of running a REPL instance over `curl(1)`, see: https://gist.github.com/2053342 +[stream]: stream.html [`readline.prompt`]: readline.html#readline_rl_prompt_preservecursor [`util.inspect()`]: util.html#util_util_inspect_object_options [here]: util.html#util_custom_inspect_function_on_objects -[Readline Interface]: readline.html#readline_class_interface +[`readline.Interface`]: readline.html#readline_class_interface