From 9c3bb241b51512e98b1dd060d6a5c4443240fd70 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 9 Jun 2024 21:09:24 +0000 Subject: [PATCH 01/10] feat: add multiline editing Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/completer_preview.js | 4 +- lib/node_modules/@stdlib/repl/lib/main.js | 31 +- .../@stdlib/repl/lib/multiline_handler.js | 348 ++++++++++++++++++ .../@stdlib/repl/lib/process_line.js | 65 +++- .../@stdlib/repl/lib/syntax_highlighter.js | 15 +- 5 files changed, 450 insertions(+), 13 deletions(-) create mode 100644 lib/node_modules/@stdlib/repl/lib/multiline_handler.js diff --git a/lib/node_modules/@stdlib/repl/lib/completer_preview.js b/lib/node_modules/@stdlib/repl/lib/completer_preview.js index 8551e2833cee..315a4eb14a9d 100644 --- a/lib/node_modules/@stdlib/repl/lib/completer_preview.js +++ b/lib/node_modules/@stdlib/repl/lib/completer_preview.js @@ -246,7 +246,7 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onK * @type {Function} * @param {string} data - input data * @param {(Object|void)} key - key object -* @returns {void} +* @returns {boolean} boolean indicating whether the preview was auto-completed */ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { if ( !this._enabled ) { @@ -273,7 +273,9 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'beforeKeypress', function debug( 'Completion preview accepted. Performing auto-completion...' ); this._rli.write( this._preview ); this._preview = ''; + return true; } + return false; }); diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index d5fc46caddf4..409a10086c0c 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -63,6 +63,7 @@ var inputPrompt = require( './input_prompt.js' ); var OutputStream = require( './output_stream.js' ); var processLine = require( './process_line.js' ); var completerFactory = require( './completer.js' ); +var MultilineHandler = require( './multiline_handler.js' ); var PreviewCompleter = require( './completer_preview.js' ); var AutoCloser = require( './auto_close_pairs.js' ); var SyntaxHighlighter = require( './syntax_highlighter.js' ); @@ -241,6 +242,9 @@ function REPL( options ) { setNonEnumerable( this, '_multiline', {} ); setNonEnumerable( this._multiline, 'active', false ); setNonEnumerable( this._multiline, 'mode', 'incomplete_expression' ); + setNonEnumerable( this._multiline, 'line', 0 ); + setNonEnumerable( this._multiline, 'lines', [] ); + setNonEnumerable( this._multiline, 'buffer', '' ); // Initialize an internal flag indicating whether the REPL has been closed: setNonEnumerable( this, '_closed', false ); @@ -273,6 +277,9 @@ function REPL( options ) { 'completer': this._completer })); + // Initialize a multiline handler: + setNonEnumerableReadOnly( this, '_multilineHandler', new MultilineHandler( this, this._rli._ttyWrite ) ); + // Create a new auto-closer: setNonEnumerableReadOnly( this, '_autoCloser', new AutoCloser( this._rli, this._settings.autoClosePairs, this._settings.autoDeletePairs ) ); @@ -337,12 +344,20 @@ function REPL( options ) { * @param {(Object|void)} key - key object */ function beforeKeypress( data, key ) { + var completed; + if ( self._ostream.isPaging ) { self._ostream.beforeKeypress( data, key ); return; } self._autoCloser.beforeKeypress( data, key ); - self._previewCompleter.beforeKeypress( data, key ); + completed = self._previewCompleter.beforeKeypress( data, key ); + + // If completion was auto-completed, don't trigger multiline keybindings to avoid double operations... + if ( !completed && self._multiline.active ) { + self._multilineHandler.beforeKeypress( data, key ); + return; + } self._ttyWrite.call( self._rli, data, key ); } @@ -366,6 +381,7 @@ function REPL( options ) { if ( autoClosed ) { self._previewCompleter.clear(); } + self._multilineHandler.onKeypress( data, key ); self._syntaxHighlighter.onKeypress(); self._previewCompleter.onKeypress( data, key ); } @@ -508,6 +524,19 @@ setNonEnumerableReadOnly( REPL.prototype, '_prompt', function prompt() { return inputPrompt( this._inputPrompt, this._count ); }); +/** +* Returns the height of the current input. +* +* @private +* @name _inputHeight +* @memberof REPL.prototype +* @type {Function} +* @returns {number} input rows +*/ +setNonEnumerableReadOnly( REPL.prototype, '_inputHeight', function inputHeight() { + return this._multiline.lines.length; +}); + /** * Returns the REPL viewport. * diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js new file mode 100644 index 000000000000..c41f51de4e1b --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -0,0 +1,348 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* eslint-disable no-underscore-dangle, no-restricted-syntax, no-invalid-this */ + +'use strict'; + +// MODULES // + +var readline = require( 'readline' ); +var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var min = require( '@stdlib/math/base/special/min' ); + + +// MAIN // + +/** +* Constructor for creating a multiline handler. +* +* @private +* @constructor +* @param {REPL} repl - REPL instance +* @param {Function} ttyWrite - function to trigger default behavior of a keypress +* @returns {MultilineHandler} multiline handler instance +*/ +function MultilineHandler( repl, ttyWrite ) { + if ( !( this instanceof MultilineHandler ) ) { + return new MultilineHandler( repl ); + } + + // Cache a reference to the provided REPL instance: + this._repl = repl; + + // Cache a reference to the readline interface: + this._rli = repl._rli; + + // Cache a reference to the output writable stream: + this._ostream = repl._ostream; + + // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: + this._ttyWrite = ttyWrite; + + // Cache a reference to the multiline status object: + this._multiline = repl._multiline; + + return this; +} + +/** +* Returns cursor offset for the current line index based on the prompt. +* +* @private +* @name _xOffset +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {number} `x` offset +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_xOffset', function xOffset() { + // If on first line, include length of input prompt as offset... + if ( this._multiline.line === 0 ) { + return this._repl._inputPrompt.length - 1; + } + return 0; +}); + +/** +* Renders remaining lines. +* +* @name renderLines +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'renderLines', function renderLines() { + var lines; + + // Clear existing renders: + readline.clearScreenDown( this._ostream ); + + // Write remaining lines below the current line: + lines = this._multiline.lines.slice( this._multiline.line + 1 ); + this._ostream.write( '\n' + lines.join('\n') ); + + // Reset cursor position: + readline.moveCursor( this._ostream, 0, min( -1 * lines.length, -1 ) ); + readline.cursorTo( this._ostream, this._xOffset() + this._rli.cursor ); +}); + +/** +* Moves cursor to a specified position in the multiline input. +* +* @private +* @name _moveCursor +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {number} x - cursor position on the line +* @param {number} dy - number of lines to move down +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveCursor', function moveCursor( x, dy ) { + // Clear any existing completion previews before moving lines: + this._repl._previewCompleter.clear(); + + // Change line: + this._multiline.line += dy; + readline.moveCursor( this._ostream, 0, dy ); + this._rli.line = this._repl._cmd[ this._multiline.line ]; + if ( this._multiline.line === 0 ) { + // Refresh prompt if moved to first line: + this._rli.setPrompt( this._repl._prompt() ); + this._rli.prompt(); + } + // Set x cursor position: + readline.cursorTo( this._ostream, this._xOffset() + x ); + this._rli.cursor = x; +}); + +/** +* Moves cursor up to the previous line. +* +* @private +* @name _moveUp +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveUp', function moveUp() { + var x; + + // If already at the first line, ignore... + if ( this._multiline.line <= 0 ) { + return; + } + this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + + // Make sure the cursor never exceeds the length of the line: + x = min( this._rli.cursor, this._repl._cmd[ this._multiline.line - 1 ].length ); // eslint-disable-line max-len + this._moveCursor( x, -1 ); +}); + +/** +* Moves cursor down to the next line. +* +* @private +* @name _moveDown +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveDown', function moveDown() { + var x; + + // If already at the last line, ignore... + if ( this._multiline.line >= this._multiline.lines.length - 1 ) { + return; + } + this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + + // Make sure the cursor never exceeds the length of the line: + x = min( this._rli.cursor, this._repl._cmd[ this._multiline.line + 1 ].length ); // eslint-disable-line max-len + this._moveCursor( x, 1 ); +}); + +/** +* Moves cursor left to the end of the previous line. +* +* @private +* @name _moveLeft +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveLeft', function moveLeft() { + // If already at the first line, ignore... + if ( this._multiline.line <= 0 ) { + return; + } + this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + + // Move cursor to the end of the previous line: + this._moveCursor( this._repl._cmd[ this._multiline.line - 1 ].length, -1 ); +}); + +/** +* Moves cursor right to the start of the next line. +* +* @private +* @name _moveRight +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveRight', function moveRight() { + // If already at the last line, ignore... + if ( this._multiline.line >= this._multiline.lines.length - 1 ) { + return; + } + this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + + // Move cursor to the start of the next line: + this._moveCursor( 0, 1 ); +}); + +/** +* Simulates a backspace by removing the current line and appending it to the previous line. +* +* @private +* @name _backspace +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_backspace', function backspace() { + var x; + + // If already at the first line, ignore... + if ( this._multiline.line <= 0 ) { + return; + } + // Save cursor position: + x = this._repl._cmd[ this._multiline.line - 1 ].length; + + // Append current line to the previous line: + this._repl._cmd[ this._multiline.line - 1 ] += this._rli.line; + this._multiline.lines[ this._multiline.line - 1 ] += this._multiline.lines[ this._multiline.line ]; // eslint-disable-line max-len + + // Remove current line: + this._repl._cmd.splice( this._multiline.line, 1 ); + this._multiline.lines.splice( this._multiline.line, 1 ); + + // Move cursor to the saved cursor position in the previous line: + this._moveCursor( x, -1 ); + + // If we deleted all multilines, update flag... + if ( this._multiline.lines.length <= 1 ) { + this._multiline.active = false; + } +}); + +/** +* Callback for handling a "keypress" event. +* +* @name onKeypress +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {string} data - input data +* @param {(Object|void)} key - key object +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onKeypress( data, key ) { + if ( !key ) { + return; + } + // Add manual newline when encountering `CTRL+E` keybinding... + if ( key.name === 'e' && key.ctrl ) { + // Update flags: + this._multiline.active = true; + this._multiline.mode = 'manual'; + + // Save expression after cursor in buffer: + readline.clearLine( this._ostream, 1 ); + this._multiline.buffer = this._rli.line.substring( this._rli.cursor ); + this._rli.line = this._rli.line.substring( 0, this._rli.cursor ); + + // Simulate `line` event: + this._rli.write( '\n' ); + } else if ( this._multiline.active ) { + // Render remaining lines with each keypress when in multiline mode: + this.renderLines(); + } +}); + +/** +* Callback which should be invoked **before** a "keypress" event is processed by a readline interface. +* +* @name beforeKeypress +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {string} data - input data +* @param {(Object|void)} key - key object +* @returns {void} +*/ +setNonEnumerableReadOnly(MultilineHandler.prototype, 'beforeKeypress', function beforeKeypress(data, key) { + if (!key) { + this._ttyWrite.call(this._rli, data, key); + return; + } + switch ( key.name ) { + case 'up': + this._moveUp(); + this.renderLines(); + break; + case 'down': + this._moveDown(); + this.renderLines(); + break; + case 'left': + // If at the beginning of the line, move up to the previous line, else trigger default behavior... + if ( this._rli.cursor === 0 ) { + this._moveLeft(); + this.renderLines(); + return; + } + this._ttyWrite.call( this._rli, data, key ); + break; + case 'right': + // If at the end of the line, move up to the next line, else trigger default behavior... + if ( this._rli.cursor === this._rli.line.length ) { + this._moveRight(); + this.renderLines(); + return; + } + this._ttyWrite.call( this._rli, data, key ); + break; + case 'backspace': + // If at the beginning of the line, remove and move up to the previous line, else trigger default behavior... + if ( this._rli.cursor === 0 ) { + this._backspace(); + this.renderLines(); + return; + } + this._ttyWrite.call( this._rli, data, key ); + break; + default: + this._ttyWrite.call( this._rli, data, key ); + break; + } +}); + + +// EXPORTS // + +module.exports = MultilineHandler; diff --git a/lib/node_modules/@stdlib/repl/lib/process_line.js b/lib/node_modules/@stdlib/repl/lib/process_line.js index 8dcce73586b4..a56664bda552 100644 --- a/lib/node_modules/@stdlib/repl/lib/process_line.js +++ b/lib/node_modules/@stdlib/repl/lib/process_line.js @@ -22,6 +22,7 @@ // MODULES // +var readline = require( 'readline' ); var logger = require( 'debug' ); var Parser = require( 'acorn' ).Parser; var parseLoose = require( 'acorn-loose' ).parse; @@ -62,9 +63,34 @@ function processLine( repl, line ) { var tmp; debug( 'Line: %s', line ); - repl._multiline.active = false; // false until proven otherwise - cmd = repl._cmd.join( '\n' ) + line; + // Check for manual newline input... + if ( repl._multiline.active && repl._multiline.mode === 'manual' ) { + debug( 'Detected newline input via modifier-key. Waiting for additional lines...' ); + + // Update current line: + repl._cmd[ repl._multiline.line ] = line; + + // Insert a newline: + repl._multiline.line += 1; + repl._cmd.splice( repl._multiline.line, 0, repl._multiline.buffer ); + repl._multiline.lines.splice( repl._multiline.line, 0, repl._multiline.buffer ); // eslint-disable-line max-len + + // Insert buffer input after cursor from previous line: + displayPrompt( repl, false ); + repl._ostream.write( repl._multiline.buffer ); + readline.cursorTo( repl._ostream, 0 ); + repl._rli.line = repl._multiline.buffer; + + // Re-render remaining lines and clear buffer: + repl._multilineHandler.renderLines(); + repl._multiline.buffer = ''; + repl._multiline.mode = 'incomplete_expression'; // reset flag + return; + } + repl._multiline.active = false; // false until proven otherwise + repl._cmd[ repl._multiline.line ] = line; + cmd = repl._cmd.join( '\n' ); if ( RE_WHITESPACE.test( cmd ) ) { displayPrompt( repl, false ); return; @@ -82,8 +108,14 @@ function processLine( repl, line ) { debug( 'Attempting to detect multi-line input...' ); if ( hasMultilineError( cmd, AOPTS ) ) { debug( 'Detected multi-line input. Waiting for additional lines...' ); - repl._cmd.push( line ); repl._multiline.active = true; + + // Insert a newline: + repl._multiline.line += 1; + repl._cmd.splice( repl._multiline.line, 0, '' ); + repl._multiline.lines.splice( repl._multiline.line, 0, '' ); + + // Display next prompt: displayPrompt( repl, false ); return; } @@ -96,15 +128,31 @@ function processLine( repl, line ) { tmp = cmd.slice( node.start, node.end ); if ( hasMultilineError( tmp, AOPTS ) ) { debug( 'Detected multi-line input. Waiting for additional lines...' ); - repl._cmd.push( line ); repl._multiline.active = true; + + // Insert a newline: + repl._multiline.line += 1; + repl._cmd.splice( repl._multiline.line, 0, '' ); + repl._multiline.lines.splice( repl._multiline.line, 0, '' ); + repl._multiline.active = true; + + // Display next prompt: displayPrompt( repl, false ); return; } } debug( 'Multi-line input not detected.' ); - repl._ostream.write( 'Error: '+tmp.message+'\n' ); + + // Move cursor to the output row: + readline.moveCursor( repl._ostream, 0, repl._inputHeight() - repl._multiline.line - 1 ); // eslint-disable-line max-len + + // Reset the command and buffers: repl._cmd.length = 0; + repl._multiline.line = 0; + repl._multiline.lines.length = 0; + + // Write error message and display next prompt: + repl._ostream.write( 'Error: '+tmp.message+'\n' ); repl.emit( 'command', cmd, false ); // command failed displayPrompt( repl, false ); return; @@ -112,8 +160,13 @@ function processLine( repl, line ) { } debug( 'Successfully processed command.' ); - // Reset the command buffer: + // Move cursor to the output row: + readline.moveCursor( repl._ostream, 0, repl._inputHeight() - repl._multiline.line - 1 ); // eslint-disable-line max-len + + // Reset the command and buffers: repl._cmd.length = 0; + repl._multiline.line = 0; + repl._multiline.lines.length = 0; // Attempt to compile the command: debug( 'Attempting to compile command...' ); diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 901e15d105aa..0909937145b4 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -85,6 +85,9 @@ function SyntaxHighlighter( repl, ostream, enabled ) { // Cache a reference to the output writable stream: this._ostream = ostream; + // Cache a reference to the REPL's multiline status object: + this._multiline = repl._multiline; + // Initialize a buffer containing the current line to validate line changes: this._line = ''; @@ -286,6 +289,8 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on var tokens; if ( !this._enabled ) { + // Save raw output if syntax highlighting is disabled: + this._multiline.lines[ this._multiline.line ] = this._rli.line; return; } if ( !this._rli.line ) { @@ -293,18 +298,17 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on return; } if ( this._line !== this._rli.line ) { - // Update line buffer: - this._line = this._rli.line; - // Tokenize: - debug( 'Line change detected. Tokenizing line: %s', this._line ); - tokens = tokenizer( this._line, this._repl._context ); + debug( 'Line change detected. Tokenizing line: %s', this._rli.line ); + tokens = tokenizer( this._rli.line, this._repl._context ); if ( !tokens ) { debug( 'No tokens found. Skipping highlighting...' ); + this._multiline.lines[ this._multiline.line ] = this._rli.line; // save output line return; } // Highlight: debug( '%d tokens found. Highlighting...', tokens.length ); + this._line = this._rli.line; // updated line buffer this._highlightedLine = this._highlightLine( this._line, tokens ); } // Replace: @@ -313,6 +317,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on readline.clearLine( this._ostream, 1 ); this._ostream.write( this._highlightedLine ); readline.moveCursor( this._ostream, this._rli.cursor - this._line.length, 0 ); // eslint-disable-line max-len + this._multiline.lines[ this._multiline.line ] = this._highlightedLine; // save output line }); From 27aaf8fc6b572c62309d13dc4feea2a9631c4d5c Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Mon, 10 Jun 2024 07:40:31 +0000 Subject: [PATCH 02/10] refactor: move `processLine` inside `multilineHandler` class Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 26 +- .../@stdlib/repl/lib/multiline_handler.js | 313 +++++++++++++++--- .../@stdlib/repl/lib/process_line.js | 194 ----------- .../@stdlib/repl/lib/syntax_highlighter.js | 10 +- 4 files changed, 270 insertions(+), 273 deletions(-) delete mode 100644 lib/node_modules/@stdlib/repl/lib/process_line.js diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 409a10086c0c..4b26419ba860 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -61,7 +61,6 @@ var commands = require( './commands.js' ); var displayPrompt = require( './display_prompt.js' ); var inputPrompt = require( './input_prompt.js' ); var OutputStream = require( './output_stream.js' ); -var processLine = require( './process_line.js' ); var completerFactory = require( './completer.js' ); var MultilineHandler = require( './multiline_handler.js' ); var PreviewCompleter = require( './completer_preview.js' ); @@ -238,14 +237,6 @@ function REPL( options ) { // Define the current workspace: setNonEnumerable( this, '_currentWorkspace', 'base' ); - // Initialize an internal status object for multi-line mode: - setNonEnumerable( this, '_multiline', {} ); - setNonEnumerable( this._multiline, 'active', false ); - setNonEnumerable( this._multiline, 'mode', 'incomplete_expression' ); - setNonEnumerable( this._multiline, 'line', 0 ); - setNonEnumerable( this._multiline, 'lines', [] ); - setNonEnumerable( this._multiline, 'buffer', '' ); - // Initialize an internal flag indicating whether the REPL has been closed: setNonEnumerable( this, '_closed', false ); @@ -354,7 +345,7 @@ function REPL( options ) { completed = self._previewCompleter.beforeKeypress( data, key ); // If completion was auto-completed, don't trigger multiline keybindings to avoid double operations... - if ( !completed && self._multiline.active ) { + if ( !completed ) { self._multilineHandler.beforeKeypress( data, key ); return; } @@ -395,7 +386,7 @@ function REPL( options ) { function onLine( line ) { self._SIGINT = false; // reset flag if ( self._closed === false ) { - processLine( self, line ); + self._multilineHandler.processLine( line ); } } @@ -524,19 +515,6 @@ setNonEnumerableReadOnly( REPL.prototype, '_prompt', function prompt() { return inputPrompt( this._inputPrompt, this._count ); }); -/** -* Returns the height of the current input. -* -* @private -* @name _inputHeight -* @memberof REPL.prototype -* @type {Function} -* @returns {number} input rows -*/ -setNonEnumerableReadOnly( REPL.prototype, '_inputHeight', function inputHeight() { - return this._multiline.lines.length; -}); - /** * Returns the REPL viewport. * diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index c41f51de4e1b..4f7875a671f7 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -23,8 +23,28 @@ // MODULES // var readline = require( 'readline' ); +var logger = require( 'debug' ); +var Parser = require( 'acorn' ).Parser; +var parseLoose = require( 'acorn-loose' ).parse; var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var min = require( '@stdlib/math/base/special/min' ); +var displayPrompt = require( './display_prompt.js' ); +var drain = require( './drain.js' ); +var multilinePlugin = require( './acorn_detect_multiline_input.js' ); +var processCommand = require( './process_command.js' ); +var compileCommand = require( './compile_command.js' ); + + +// VARIABLES // + +var debug = logger( 'repl:line' ); +var hasMultilineError = Parser.extend( multilinePlugin ).hasMultilineError; +var RE_WHITESPACE = /^\s*$/; +var RE_SINGLE_LINE_COMMENT = /^\s*\/\//; +var RE_MULTI_LINE_COMMENT = /^\s*\/\*.*\*\/$/; +var AOPTS = { + 'ecmaVersion': 'latest' +}; // MAIN // @@ -40,7 +60,7 @@ var min = require( '@stdlib/math/base/special/min' ); */ function MultilineHandler( repl, ttyWrite ) { if ( !( this instanceof MultilineHandler ) ) { - return new MultilineHandler( repl ); + return new MultilineHandler( repl, ttyWrite ); } // Cache a reference to the provided REPL instance: @@ -55,8 +75,28 @@ function MultilineHandler( repl, ttyWrite ) { // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: this._ttyWrite = ttyWrite; - // Cache a reference to the multiline status object: - this._multiline = repl._multiline; + // Cache a reference to the command array: + this._cmd = repl._cmd; + + // Cache a reference to the command queue: + this._queue = repl._queue; + + // Cache the length of the input prompt: + this._promptLength = repl._inputPrompt.length; + + // Initialize an internal status object for multi-line mode: + this._multiline = {}; + this._multiline.active = false; + this._multiline.mode = 'incomplete_expression'; + + // Initialize a buffer for caching input lines: + this._lines = []; + + // Initialize a variable storing current line index: + this._lineIndex = 0; + + // Initialize a buffer for storing code after cursor when manually entering multiline mode: + this._remainingLine = ''; return this; } @@ -72,8 +112,8 @@ function MultilineHandler( repl, ttyWrite ) { */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_xOffset', function xOffset() { // If on first line, include length of input prompt as offset... - if ( this._multiline.line === 0 ) { - return this._repl._inputPrompt.length - 1; + if ( this._lineIndex === 0 ) { + return this._promptLength - 1; } return 0; }); @@ -81,19 +121,19 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_xOffset', function xOffs /** * Renders remaining lines. * -* @name renderLines +* @name _renderLines * @memberof MultilineHandler.prototype * @type {Function} * @returns {void} */ -setNonEnumerableReadOnly( MultilineHandler.prototype, 'renderLines', function renderLines() { +setNonEnumerableReadOnly( MultilineHandler.prototype, '_renderLines', function renderLines() { var lines; // Clear existing renders: readline.clearScreenDown( this._ostream ); // Write remaining lines below the current line: - lines = this._multiline.lines.slice( this._multiline.line + 1 ); + lines = this._lines.slice( this._lineIndex + 1 ); this._ostream.write( '\n' + lines.join('\n') ); // Reset cursor position: @@ -113,18 +153,23 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'renderLines', function re * @returns {void} */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveCursor', function moveCursor( x, dy ) { + var prompt = ''; + // Clear any existing completion previews before moving lines: this._repl._previewCompleter.clear(); // Change line: - this._multiline.line += dy; + this._lineIndex += dy; readline.moveCursor( this._ostream, 0, dy ); - this._rli.line = this._repl._cmd[ this._multiline.line ]; - if ( this._multiline.line === 0 ) { - // Refresh prompt if moved to first line: - this._rli.setPrompt( this._repl._prompt() ); - this._rli.prompt(); + this._rli.line = this._cmd[ this._lineIndex ]; + + // Reset prompt: + if ( this._lineIndex === 0 ) { + prompt = this._repl._prompt(); // restore input prompt if on the first prompt } + this._rli.setPrompt( prompt ); + this._rli.prompt(); + // Set x cursor position: readline.cursorTo( this._ostream, this._xOffset() + x ); this._rli.cursor = x; @@ -140,17 +185,17 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveCursor', function mo * @returns {void} */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveUp', function moveUp() { - var x; + var cursor; // If already at the first line, ignore... - if ( this._multiline.line <= 0 ) { + if ( this._lineIndex <= 0 ) { return; } - this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + this._cmd[ this._lineIndex ] = this._rli.line; // update current line in command // Make sure the cursor never exceeds the length of the line: - x = min( this._rli.cursor, this._repl._cmd[ this._multiline.line - 1 ].length ); // eslint-disable-line max-len - this._moveCursor( x, -1 ); + cursor = min( this._rli.cursor, this._cmd[ this._lineIndex - 1 ].length ); + this._moveCursor( cursor, -1 ); }); /** @@ -163,17 +208,17 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveUp', function moveUp * @returns {void} */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveDown', function moveDown() { - var x; + var cursor; // If already at the last line, ignore... - if ( this._multiline.line >= this._multiline.lines.length - 1 ) { + if ( this._lineIndex >= this._lines.length - 1 ) { return; } - this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + this._cmd[ this._lineIndex ] = this._rli.line; // update current line in command // Make sure the cursor never exceeds the length of the line: - x = min( this._rli.cursor, this._repl._cmd[ this._multiline.line + 1 ].length ); // eslint-disable-line max-len - this._moveCursor( x, 1 ); + cursor = min( this._rli.cursor, this._cmd[ this._lineIndex + 1 ].length ); + this._moveCursor( cursor, 1 ); }); /** @@ -187,13 +232,13 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveDown', function move */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveLeft', function moveLeft() { // If already at the first line, ignore... - if ( this._multiline.line <= 0 ) { + if ( this._lineIndex <= 0 ) { return; } - this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + this._cmd[ this._lineIndex ] = this._rli.line; // update current line in command // Move cursor to the end of the previous line: - this._moveCursor( this._repl._cmd[ this._multiline.line - 1 ].length, -1 ); + this._moveCursor( this._cmd[ this._lineIndex - 1 ].length, -1 ); }); /** @@ -207,10 +252,10 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveLeft', function move */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveRight', function moveRight() { // If already at the last line, ignore... - if ( this._multiline.line >= this._multiline.lines.length - 1 ) { + if ( this._lineIndex >= this._lines.length - 1 ) { return; } - this._repl._cmd[ this._multiline.line ] = this._rli.line; // update current line in command + this._cmd[ this._lineIndex ] = this._rli.line; // update current line in command // Move cursor to the start of the next line: this._moveCursor( 0, 1 ); @@ -226,32 +271,200 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_moveRight', function mov * @returns {void} */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_backspace', function backspace() { - var x; + var cursor; // If already at the first line, ignore... - if ( this._multiline.line <= 0 ) { + if ( this._lineIndex <= 0 ) { return; } // Save cursor position: - x = this._repl._cmd[ this._multiline.line - 1 ].length; + cursor = this._cmd[ this._lineIndex - 1 ].length; // Append current line to the previous line: - this._repl._cmd[ this._multiline.line - 1 ] += this._rli.line; - this._multiline.lines[ this._multiline.line - 1 ] += this._multiline.lines[ this._multiline.line ]; // eslint-disable-line max-len + this._cmd[ this._lineIndex - 1 ] += this._rli.line; + this._lines[ this._lineIndex - 1 ] += this._lines[ this._lineIndex ]; // Remove current line: - this._repl._cmd.splice( this._multiline.line, 1 ); - this._multiline.lines.splice( this._multiline.line, 1 ); + this._cmd.splice( this._lineIndex, 1 ); + this._lines.splice( this._lineIndex, 1 ); // Move cursor to the saved cursor position in the previous line: - this._moveCursor( x, -1 ); + this._moveCursor( cursor, -1 ); - // If we deleted all multilines, update flag... - if ( this._multiline.lines.length <= 1 ) { + // If we deleted all additional lines, update flag... + if ( this._lines.length <= 1 ) { this._multiline.active = false; } }); +/** +* Resets input buffers. +* +* @private +* @name _resetInput +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_resetInput', function resetInput() { + this._cmd.length = 0; + this._lineIndex = 0; + this._lines.length = 0; +}); + +/** +* Updates current input line in buffer. +* +* @name updateLine +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {string} line - updated line +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'updateLine', function updateLine( line ) { + this._lines[ this._lineIndex ] = line; +}); + +/** +* Processes input line data. +* +* @name processLine +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {string} line - line data +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function processLine( line ) { + var code; + var node; + var ast; + var cmd; + var tmp; + var dy; + + debug( 'Line: %s', line ); + + // Check for manual newline input... + if ( this._multiline.active && this._multiline.mode === 'manual' ) { + debug( 'Detected newline input via modifier-key. Waiting for additional lines...' ); + + // Update current line: + this._cmd[ this._lineIndex ] = line; + + // Insert a newline: + this._lineIndex += 1; + this._cmd.splice( this._lineIndex, 0, this._remainingLine ); + this._lines.splice( this._lineIndex, 0, this._remainingLine ); + + // Insert buffer input after cursor from previous line: + displayPrompt( this._repl, false ); + this._ostream.write( this._remainingLine ); + readline.cursorTo( this._ostream, 0 ); + this._rli.line = this._remainingLine; + + // Re-render remaining lines and clear buffer: + this._renderLines(); + this._remainingLine = ''; + this._multiline.mode = 'incomplete_expression'; // reset flag + return; + } + this._multiline.active = false; // false until proven otherwise + this._cmd[ this._lineIndex ] = line; + cmd = this._cmd.join( '\n' ); + if ( RE_WHITESPACE.test( cmd ) ) { + displayPrompt( this._repl, false ); + return; + } + if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len + debug( 'Detected single-line comment.' ); + tmp = cmd; + } else { + // Check if the command has valid syntax... + debug( 'Processing command...' ); + tmp = processCommand( cmd ); + if ( tmp instanceof Error ) { + debug( 'Unable to process command.' ); + debug( 'Error: %s', tmp.message ); + debug( 'Attempting to detect multi-line input...' ); + if ( hasMultilineError( cmd, AOPTS ) ) { + debug( 'Detected multi-line input. Waiting for additional lines...' ); + this._multiline.active = true; + + // Insert a newline: + this._lineIndex += 1; + this._cmd.splice( this._lineIndex, 0, '' ); + this._lines.splice( this._lineIndex, 0, '' ); + + // Display next prompt: + displayPrompt( this._repl, false ); + return; + } + // Still possible that a user is attempting to enter an object literal across multiple lines... + ast = parseLoose( cmd, AOPTS ); + + // Check for a trailing node which is being interpreted as a block statement, as this could be an object literal... + node = ast.body[ ast.body.length-1 ]; + if ( node.type === 'BlockStatement' && node.end === ast.end ) { + tmp = cmd.slice( node.start, node.end ); + if ( hasMultilineError( tmp, AOPTS ) ) { + debug( 'Detected multi-line input. Waiting for additional lines...' ); + this._multiline.active = true; + + // Insert a newline: + this._lineIndex += 1; + this._cmd.splice( this._lineIndex, 0, '' ); + this._lines.splice( this._lineIndex, 0, '' ); + + // Display next prompt: + displayPrompt( this._repl, false ); + return; + } + } + debug( 'Multi-line input not detected.' ); + + // Move cursor to the output row: + dy = this._lines.length - this._lineIndex - 1; + readline.moveCursor( this._ostream, 0, dy ); + + // Reset the input buffers: + this._resetInput(); + + // Write error message and display next prompt: + this._ostream.write( 'Error: '+tmp.message+'\n' ); + this._repl.emit( 'command', cmd, false ); // command failed + displayPrompt( this._repl, false ); + return; + } + } + debug( 'Successfully processed command.' ); + + // Move cursor to the output row: + dy = this._lines.length - this._lineIndex - 1; + readline.moveCursor( this._ostream, 0, dy ); + + // Reset the input buffers: + this._resetInput(); + + // Attempt to compile the command: + debug( 'Attempting to compile command...' ); + code = compileCommand( tmp ); + if ( code instanceof Error ) { + debug( 'Error: %s', code.message ); + this._ostream.write( 'Error: '+code.message+'\n' ); + this._repl.emit( 'command', cmd, false ); // command failed + displayPrompt( this._repl, false ); + return; + } + debug( 'Successfully compiled command.' ); + code.raw = cmd; + + // Add the command to the command queue: + this._queue.push( code ); + + // Request to run the command: + drain( this._repl ); +}); + /** * Callback for handling a "keypress" event. * @@ -273,15 +486,15 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK this._multiline.mode = 'manual'; // Save expression after cursor in buffer: - readline.clearLine( this._ostream, 1 ); - this._multiline.buffer = this._rli.line.substring( this._rli.cursor ); + readline.clearLine( this._ostream, 1 ); // clear line after cursor + this._remainingLine = this._rli.line.substring( this._rli.cursor ); this._rli.line = this._rli.line.substring( 0, this._rli.cursor ); // Simulate `line` event: this._rli.write( '\n' ); } else if ( this._multiline.active ) { // Render remaining lines with each keypress when in multiline mode: - this.renderLines(); + this._renderLines(); } }); @@ -295,25 +508,25 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK * @param {(Object|void)} key - key object * @returns {void} */ -setNonEnumerableReadOnly(MultilineHandler.prototype, 'beforeKeypress', function beforeKeypress(data, key) { - if (!key) { - this._ttyWrite.call(this._rli, data, key); +setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { + if ( !key || !this._multiline.active ) { + this._ttyWrite.call( this._rli, data, key ); return; } switch ( key.name ) { case 'up': this._moveUp(); - this.renderLines(); + this._renderLines(); break; case 'down': this._moveDown(); - this.renderLines(); + this._renderLines(); break; case 'left': // If at the beginning of the line, move up to the previous line, else trigger default behavior... if ( this._rli.cursor === 0 ) { this._moveLeft(); - this.renderLines(); + this._renderLines(); return; } this._ttyWrite.call( this._rli, data, key ); @@ -322,7 +535,7 @@ setNonEnumerableReadOnly(MultilineHandler.prototype, 'beforeKeypress', function // If at the end of the line, move up to the next line, else trigger default behavior... if ( this._rli.cursor === this._rli.line.length ) { this._moveRight(); - this.renderLines(); + this._renderLines(); return; } this._ttyWrite.call( this._rli, data, key ); @@ -331,7 +544,7 @@ setNonEnumerableReadOnly(MultilineHandler.prototype, 'beforeKeypress', function // If at the beginning of the line, remove and move up to the previous line, else trigger default behavior... if ( this._rli.cursor === 0 ) { this._backspace(); - this.renderLines(); + this._renderLines(); return; } this._ttyWrite.call( this._rli, data, key ); diff --git a/lib/node_modules/@stdlib/repl/lib/process_line.js b/lib/node_modules/@stdlib/repl/lib/process_line.js deleted file mode 100644 index a56664bda552..000000000000 --- a/lib/node_modules/@stdlib/repl/lib/process_line.js +++ /dev/null @@ -1,194 +0,0 @@ -/** -* @license Apache-2.0 -* -* Copyright (c) 2019 The Stdlib Authors. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -/* eslint-disable no-underscore-dangle */ - -'use strict'; - -// MODULES // - -var readline = require( 'readline' ); -var logger = require( 'debug' ); -var Parser = require( 'acorn' ).Parser; -var parseLoose = require( 'acorn-loose' ).parse; -var displayPrompt = require( './display_prompt.js' ); -var drain = require( './drain.js' ); -var multilinePlugin = require( './acorn_detect_multiline_input.js' ); -var processCommand = require( './process_command.js' ); -var compileCommand = require( './compile_command.js' ); - - -// VARIABLES // - -var debug = logger( 'repl:line' ); -var hasMultilineError = Parser.extend( multilinePlugin ).hasMultilineError; -var RE_WHITESPACE = /^\s*$/; -var RE_SINGLE_LINE_COMMENT = /^\s*\/\//; -var RE_MULTI_LINE_COMMENT = /^\s*\/\*.*\*\/$/; -var AOPTS = { - 'ecmaVersion': 'latest' -}; - - -// MAIN // - -/** -* Processes input line data. -* -* @private -* @param {REPL} repl - REPL instance -* @param {string} line - line data -* @returns {void} -*/ -function processLine( repl, line ) { - var code; - var node; - var ast; - var cmd; - var tmp; - - debug( 'Line: %s', line ); - - // Check for manual newline input... - if ( repl._multiline.active && repl._multiline.mode === 'manual' ) { - debug( 'Detected newline input via modifier-key. Waiting for additional lines...' ); - - // Update current line: - repl._cmd[ repl._multiline.line ] = line; - - // Insert a newline: - repl._multiline.line += 1; - repl._cmd.splice( repl._multiline.line, 0, repl._multiline.buffer ); - repl._multiline.lines.splice( repl._multiline.line, 0, repl._multiline.buffer ); // eslint-disable-line max-len - - // Insert buffer input after cursor from previous line: - displayPrompt( repl, false ); - repl._ostream.write( repl._multiline.buffer ); - readline.cursorTo( repl._ostream, 0 ); - repl._rli.line = repl._multiline.buffer; - - // Re-render remaining lines and clear buffer: - repl._multilineHandler.renderLines(); - repl._multiline.buffer = ''; - repl._multiline.mode = 'incomplete_expression'; // reset flag - return; - } - repl._multiline.active = false; // false until proven otherwise - repl._cmd[ repl._multiline.line ] = line; - cmd = repl._cmd.join( '\n' ); - if ( RE_WHITESPACE.test( cmd ) ) { - displayPrompt( repl, false ); - return; - } - if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len - debug( 'Detected single-line comment.' ); - tmp = cmd; - } else { - // Check if the command has valid syntax... - debug( 'Processing command...' ); - tmp = processCommand( cmd ); - if ( tmp instanceof Error ) { - debug( 'Unable to process command.' ); - debug( 'Error: %s', tmp.message ); - debug( 'Attempting to detect multi-line input...' ); - if ( hasMultilineError( cmd, AOPTS ) ) { - debug( 'Detected multi-line input. Waiting for additional lines...' ); - repl._multiline.active = true; - - // Insert a newline: - repl._multiline.line += 1; - repl._cmd.splice( repl._multiline.line, 0, '' ); - repl._multiline.lines.splice( repl._multiline.line, 0, '' ); - - // Display next prompt: - displayPrompt( repl, false ); - return; - } - // Still possible that a user is attempting to enter an object literal across multiple lines... - ast = parseLoose( cmd, AOPTS ); - - // Check for a trailing node which is being interpreted as a block statement, as this could be an object literal... - node = ast.body[ ast.body.length-1 ]; - if ( node.type === 'BlockStatement' && node.end === ast.end ) { - tmp = cmd.slice( node.start, node.end ); - if ( hasMultilineError( tmp, AOPTS ) ) { - debug( 'Detected multi-line input. Waiting for additional lines...' ); - repl._multiline.active = true; - - // Insert a newline: - repl._multiline.line += 1; - repl._cmd.splice( repl._multiline.line, 0, '' ); - repl._multiline.lines.splice( repl._multiline.line, 0, '' ); - repl._multiline.active = true; - - // Display next prompt: - displayPrompt( repl, false ); - return; - } - } - debug( 'Multi-line input not detected.' ); - - // Move cursor to the output row: - readline.moveCursor( repl._ostream, 0, repl._inputHeight() - repl._multiline.line - 1 ); // eslint-disable-line max-len - - // Reset the command and buffers: - repl._cmd.length = 0; - repl._multiline.line = 0; - repl._multiline.lines.length = 0; - - // Write error message and display next prompt: - repl._ostream.write( 'Error: '+tmp.message+'\n' ); - repl.emit( 'command', cmd, false ); // command failed - displayPrompt( repl, false ); - return; - } - } - debug( 'Successfully processed command.' ); - - // Move cursor to the output row: - readline.moveCursor( repl._ostream, 0, repl._inputHeight() - repl._multiline.line - 1 ); // eslint-disable-line max-len - - // Reset the command and buffers: - repl._cmd.length = 0; - repl._multiline.line = 0; - repl._multiline.lines.length = 0; - - // Attempt to compile the command: - debug( 'Attempting to compile command...' ); - code = compileCommand( tmp ); - if ( code instanceof Error ) { - debug( 'Error: %s', code.message ); - repl._ostream.write( 'Error: '+code.message+'\n' ); - repl.emit( 'command', cmd, false ); // command failed - displayPrompt( repl, false ); - return; - } - debug( 'Successfully compiled command.' ); - code.raw = cmd; - - // Add the command to the command queue: - repl._queue.push( code ); - - // Request to run the command: - drain( repl ); -} - - -// EXPORTS // - -module.exports = processLine; diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 0909937145b4..49a3a831f09f 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -85,8 +85,8 @@ function SyntaxHighlighter( repl, ostream, enabled ) { // Cache a reference to the output writable stream: this._ostream = ostream; - // Cache a reference to the REPL's multiline status object: - this._multiline = repl._multiline; + // Cache a reference to the REPL's multiline handler: + this._multilineHandler = repl._multilineHandler; // Initialize a buffer containing the current line to validate line changes: this._line = ''; @@ -290,7 +290,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on if ( !this._enabled ) { // Save raw output if syntax highlighting is disabled: - this._multiline.lines[ this._multiline.line ] = this._rli.line; + this._multilineHandler.updateLine( this._rli.line ); // save displayed line return; } if ( !this._rli.line ) { @@ -303,7 +303,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on tokens = tokenizer( this._rli.line, this._repl._context ); if ( !tokens ) { debug( 'No tokens found. Skipping highlighting...' ); - this._multiline.lines[ this._multiline.line ] = this._rli.line; // save output line + this._multilineHandler.updateLine( this._rli.line ); // save displayed line return; } // Highlight: @@ -317,7 +317,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on readline.clearLine( this._ostream, 1 ); this._ostream.write( this._highlightedLine ); readline.moveCursor( this._ostream, this._rli.cursor - this._line.length, 0 ); // eslint-disable-line max-len - this._multiline.lines[ this._multiline.line ] = this._highlightedLine; // save output line + this._multilineHandler.updateLine( this._highlightedLine ); // save displayed line }); From a4f20ea7b236fdce5de8e8c3fe5100edcd89f882 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 11 Jun 2024 19:46:48 +0000 Subject: [PATCH 03/10] fix: failing rendering in mid line insertions Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/multiline_handler.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index 4f7875a671f7..88a584bf33f5 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -362,8 +362,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr readline.cursorTo( this._ostream, 0 ); this._rli.line = this._remainingLine; - // Re-render remaining lines and clear buffer: - this._renderLines(); + // Clear buffer: this._remainingLine = ''; this._multiline.mode = 'incomplete_expression'; // reset flag return; @@ -492,7 +491,8 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK // Simulate `line` event: this._rli.write( '\n' ); - } else if ( this._multiline.active ) { + } + if ( this._multiline.active ) { // Render remaining lines with each keypress when in multiline mode: this._renderLines(); } From 6dd3ea05fd38533682d5ca1eb04ea96638572af3 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 12 Jun 2024 07:01:30 +0000 Subject: [PATCH 04/10] feat: allow breaking line with ENTER as a multiline trigger Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/multiline_handler.js | 171 +++++++++++------- 1 file changed, 105 insertions(+), 66 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index 88a584bf33f5..4a50355fbb52 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -27,6 +27,7 @@ var logger = require( 'debug' ); var Parser = require( 'acorn' ).Parser; var parseLoose = require( 'acorn-loose' ).parse; var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var copy = require( '@stdlib/array/base/copy' ); var min = require( '@stdlib/math/base/special/min' ); var displayPrompt = require( './display_prompt.js' ); var drain = require( './drain.js' ); @@ -84,10 +85,10 @@ function MultilineHandler( repl, ttyWrite ) { // Cache the length of the input prompt: this._promptLength = repl._inputPrompt.length; - // Initialize an internal status object for multi-line mode: + // Initialize an internal status object for multiline mode: this._multiline = {}; this._multiline.active = false; - this._multiline.mode = 'incomplete_expression'; + this._multiline.trigger = false; // Initialize a buffer for caching input lines: this._lines = []; @@ -312,6 +313,74 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_resetInput', function re this._lines.length = 0; }); +/** +* Updates flags and buffers before to trigger multiline mode. +* +* @private +* @name _triggerMultiline +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_triggerMultiline', function triggerMultiline() { + // Update flag: + this._multiline.trigger = true; + + // Save expression after cursor in buffer: + readline.clearLine( this._ostream, 1 ); // clear line after cursor + this._remainingLine = this._rli.line.substring( this._rli.cursor ); + this._rli.line = this._rli.line.substring( 0, this._rli.cursor ); +}); + +/** +* Checks if the command is incomplete and a multiline input. +* +* @private +* @name _isMultilineInput +* @memberof MultilineHandler.prototype +* @type {Function} +* @param {string} cmd - command +* @returns {boolean} boolean indicating whether the command is a multiline input +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', function isMultilineInput( cmd ) { + var node; + var tmp; + var ast; + + debug( 'Attempting to detect multi-line input...' ); + if ( RE_WHITESPACE.test( cmd ) ) { + debug( 'Detected multi-line input. Triggering multiline mode...' ); + return true; + } + if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len + debug( 'Multi-line input not detected.' ); + return false; + } + // Check if the command has valid syntax... + tmp = processCommand( cmd ); + if ( !( tmp instanceof Error ) ) { + return false; + } + if ( hasMultilineError( cmd, AOPTS ) ) { + debug( 'Detected multi-line input. Triggering multiline mode...' ); + return true; + } + // Still possible that a user is attempting to enter an object literal across multiple lines... + ast = parseLoose( cmd, AOPTS ); + + // Check for a trailing node which is being interpreted as a block statement, as this could be an object literal... + node = ast.body[ ast.body.length-1 ]; + if ( node.type === 'BlockStatement' && node.end === ast.end ) { + tmp = cmd.slice( node.start, node.end ); + if ( hasMultilineError( tmp, AOPTS ) ) { + debug( 'Detected multi-line input. Triggering multiline mode...' ); + return true; + } + } + debug( 'Multi-line input not detected.' ); + return false; +}); + /** * Updates current input line in buffer. * @@ -336,20 +405,17 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'updateLine', function upd */ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function processLine( line ) { var code; - var node; - var ast; var cmd; var tmp; var dy; + // Save line: debug( 'Line: %s', line ); + this._cmd[ this._lineIndex ] = line; - // Check for manual newline input... - if ( this._multiline.active && this._multiline.mode === 'manual' ) { - debug( 'Detected newline input via modifier-key. Waiting for additional lines...' ); - - // Update current line: - this._cmd[ this._lineIndex ] = line; + // Check for multiline triggers... + if ( this._multiline.trigger ) { + debug( 'Detected multiline trigger input. Waiting for additional lines...' ); // Insert a newline: this._lineIndex += 1; @@ -362,18 +428,14 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr readline.cursorTo( this._ostream, 0 ); this._rli.line = this._remainingLine; - // Clear buffer: + // Update flags and buffers: this._remainingLine = ''; - this._multiline.mode = 'incomplete_expression'; // reset flag + this._multiline.trigger = false; + this._multiline.active = true; return; } this._multiline.active = false; // false until proven otherwise - this._cmd[ this._lineIndex ] = line; cmd = this._cmd.join( '\n' ); - if ( RE_WHITESPACE.test( cmd ) ) { - displayPrompt( this._repl, false ); - return; - } if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len debug( 'Detected single-line comment.' ); tmp = cmd; @@ -382,45 +444,6 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr debug( 'Processing command...' ); tmp = processCommand( cmd ); if ( tmp instanceof Error ) { - debug( 'Unable to process command.' ); - debug( 'Error: %s', tmp.message ); - debug( 'Attempting to detect multi-line input...' ); - if ( hasMultilineError( cmd, AOPTS ) ) { - debug( 'Detected multi-line input. Waiting for additional lines...' ); - this._multiline.active = true; - - // Insert a newline: - this._lineIndex += 1; - this._cmd.splice( this._lineIndex, 0, '' ); - this._lines.splice( this._lineIndex, 0, '' ); - - // Display next prompt: - displayPrompt( this._repl, false ); - return; - } - // Still possible that a user is attempting to enter an object literal across multiple lines... - ast = parseLoose( cmd, AOPTS ); - - // Check for a trailing node which is being interpreted as a block statement, as this could be an object literal... - node = ast.body[ ast.body.length-1 ]; - if ( node.type === 'BlockStatement' && node.end === ast.end ) { - tmp = cmd.slice( node.start, node.end ); - if ( hasMultilineError( tmp, AOPTS ) ) { - debug( 'Detected multi-line input. Waiting for additional lines...' ); - this._multiline.active = true; - - // Insert a newline: - this._lineIndex += 1; - this._cmd.splice( this._lineIndex, 0, '' ); - this._lines.splice( this._lineIndex, 0, '' ); - - // Display next prompt: - displayPrompt( this._repl, false ); - return; - } - } - debug( 'Multi-line input not detected.' ); - // Move cursor to the output row: dy = this._lines.length - this._lineIndex - 1; readline.moveCursor( this._ostream, 0, dy ); @@ -478,16 +501,9 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK if ( !key ) { return; } - // Add manual newline when encountering `CTRL+E` keybinding... + // Trigger multiline input when encountering `CTRL+E` keybinding... if ( key.name === 'e' && key.ctrl ) { - // Update flags: - this._multiline.active = true; - this._multiline.mode = 'manual'; - - // Save expression after cursor in buffer: - readline.clearLine( this._ostream, 1 ); // clear line after cursor - this._remainingLine = this._rli.line.substring( this._rli.cursor ); - this._rli.line = this._rli.line.substring( 0, this._rli.cursor ); + this._triggerMultiline(); // Simulate `line` event: this._rli.write( '\n' ); @@ -509,10 +525,33 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK * @returns {void} */ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { - if ( !key || !this._multiline.active ) { + var cmd; + + if ( !key ) { + this._ttyWrite.call( this._rli, data, key ); + return; + } + // Check whether to trigger multiline mode or execute the command when `return` key is encountered... + if ( key.name === 'return' ) { + cmd = copy( this._cmd ); + cmd[ this._lineIndex ] = this._rli.line; + + // If command is incomplete, trigger multiline mode... + if ( !this._isMultilineInput( cmd.join( '\n' ) ) ) { + this._ttyWrite.call( this._rli, data, key ); + return; + } + this._triggerMultiline(); + + // Trigger `line` event: + this._ttyWrite.call( this._rli, data, key ); + return; + } + if ( !this._multiline.active ) { this._ttyWrite.call( this._rli, data, key ); return; } + // If multiline mode is active, enable navigation... switch ( key.name ) { case 'up': this._moveUp(); From a293eee6000db3eb6ef0847d3176d41034bcc787 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 13 Jun 2024 15:26:59 +0000 Subject: [PATCH 05/10] fix: line not getting updated Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index 49a3a831f09f..4ea68918d89d 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -295,6 +295,7 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on } if ( !this._rli.line ) { debug( 'Empty line detected. Skipping highlighting...' ); + this._multilineHandler.updateLine( this._rli.line ); // save displayed line return; } if ( this._line !== this._rli.line ) { From 588b3f52a5d286f1e519444686a61f8c6af7eb45 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 13 Jun 2024 15:29:09 +0000 Subject: [PATCH 06/10] fix: return boolean definitely Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/completer_preview.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/completer_preview.js b/lib/node_modules/@stdlib/repl/lib/completer_preview.js index 315a4eb14a9d..2eae0a89ac4e 100644 --- a/lib/node_modules/@stdlib/repl/lib/completer_preview.js +++ b/lib/node_modules/@stdlib/repl/lib/completer_preview.js @@ -250,23 +250,24 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onK */ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { if ( !this._enabled ) { - return; + return false; } if ( !key || this._preview === '' ) { - return; + return false; } // Avoid clashing with existing TAB completion behavior... if ( key.name === 'tab' ) { - return this.clear(); + this.clear(); + return false; } // Handle the case where the user is not at the end of the line... if ( this._rli.cursor !== this._rli.line.length ) { // If a user is in the middle of a line and presses ENTER, clear the preview string, as the preview was not accepted prior to executing the expression... if ( key.name === 'return' || key.name === 'enter' ) { debug( 'Received an ENTER keypress event while in the middle of the line.' ); - return this.clear(); + this.clear(); } - return; + return false; } // When the user is at the end of the line, auto-complete the line with the completion preview when a user presses RETURN or the RIGHT arrow key (note: pressing ENTER will result in both completion AND execution)... if ( key.name === 'return' || key.name === 'enter' || key.name === 'right' ) { From 53ee8669c9d80f41a097d8d93183436f0128f9f3 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 13 Jun 2024 15:33:09 +0000 Subject: [PATCH 07/10] fix: update `clearCommand()` prototype method Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 2 +- .../@stdlib/repl/lib/multiline_handler.js | 36 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 4b26419ba860..8f0b43b289ae 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -1268,7 +1268,7 @@ setNonEnumerableReadOnly( REPL.prototype, 'clearCommand', function onClearComman throw new Error( 'invalid operation. Cannot clear the command buffer of a REPL which has already closed.' ); } // Clear any command which has been buffered but not yet executed: - this._cmd.length = 0; + this._multilineHandler.resetInput(); return this; }); diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index 4a50355fbb52..d3e338669444 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -298,21 +298,6 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_backspace', function bac } }); -/** -* Resets input buffers. -* -* @private -* @name _resetInput -* @memberof MultilineHandler.prototype -* @type {Function} -* @returns {void} -*/ -setNonEnumerableReadOnly( MultilineHandler.prototype, '_resetInput', function resetInput() { - this._cmd.length = 0; - this._lineIndex = 0; - this._lines.length = 0; -}); - /** * Updates flags and buffers before to trigger multiline mode. * @@ -394,6 +379,21 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'updateLine', function upd this._lines[ this._lineIndex ] = line; }); +/** +* Resets input and command buffers. +* +* @private +* @name resetInput +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'resetInput', function resetInput() { + this._cmd.length = 0; + this._lineIndex = 0; + this._lines.length = 0; +}); + /** * Processes input line data. * @@ -449,7 +449,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr readline.moveCursor( this._ostream, 0, dy ); // Reset the input buffers: - this._resetInput(); + this.resetInput(); // Write error message and display next prompt: this._ostream.write( 'Error: '+tmp.message+'\n' ); @@ -465,7 +465,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr readline.moveCursor( this._ostream, 0, dy ); // Reset the input buffers: - this._resetInput(); + this.resetInput(); // Attempt to compile the command: debug( 'Attempting to compile command...' ); @@ -502,7 +502,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK return; } // Trigger multiline input when encountering `CTRL+E` keybinding... - if ( key.name === 'e' && key.ctrl ) { + if ( key.name === 'o' && key.ctrl ) { this._triggerMultiline(); // Simulate `line` event: From 0713000b600c5922bcd887e90388da09d3282ba0 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 13 Jun 2024 17:35:38 +0000 Subject: [PATCH 08/10] fix: correct keybinding Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/multiline_handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index d3e338669444..4159f798e4b6 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -502,7 +502,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK return; } // Trigger multiline input when encountering `CTRL+E` keybinding... - if ( key.name === 'o' && key.ctrl ) { + if ( key.name === 'e' && key.ctrl ) { this._triggerMultiline(); // Simulate `line` event: From 4caf0af2277fffe90b49f2572d1f68fe108c161f Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Sun, 16 Jun 2024 03:34:58 +0000 Subject: [PATCH 09/10] fix: change to CTRL+O and don't trigger multi-line for whitespaces Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/multiline_handler.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index 4159f798e4b6..e87d0fa84ceb 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -334,8 +334,8 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', funct debug( 'Attempting to detect multi-line input...' ); if ( RE_WHITESPACE.test( cmd ) ) { - debug( 'Detected multi-line input. Triggering multiline mode...' ); - return true; + debug( 'Multi-line input not detected.' ); + return false; } if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len debug( 'Multi-line input not detected.' ); @@ -344,6 +344,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', funct // Check if the command has valid syntax... tmp = processCommand( cmd ); if ( !( tmp instanceof Error ) ) { + debug( 'Multi-line input not detected.' ); return false; } if ( hasMultilineError( cmd, AOPTS ) ) { @@ -436,6 +437,11 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr } this._multiline.active = false; // false until proven otherwise cmd = this._cmd.join( '\n' ); + if ( RE_WHITESPACE.test( cmd ) ) { + this.resetInput(); + displayPrompt( this._repl, false ); + return; + } if ( RE_SINGLE_LINE_COMMENT.test( cmd ) || RE_MULTI_LINE_COMMENT.test( cmd ) ) { // eslint-disable-line max-len debug( 'Detected single-line comment.' ); tmp = cmd; @@ -501,8 +507,8 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK if ( !key ) { return; } - // Trigger multiline input when encountering `CTRL+E` keybinding... - if ( key.name === 'e' && key.ctrl ) { + // Trigger multiline input when encountering `CTRL+O` keybinding... + if ( key.name === 'o' && key.ctrl ) { this._triggerMultiline(); // Simulate `line` event: From 083e16e3846779f381353d61d133a810f1ad0ea5 Mon Sep 17 00:00:00 2001 From: Athan Reines Date: Fri, 21 Jun 2024 01:51:31 -0700 Subject: [PATCH 10/10] docs: update comments and spelling for consistency --- lib/node_modules/@stdlib/repl/lib/main.js | 4 +- .../@stdlib/repl/lib/multiline_handler.js | 42 +++++++++---------- .../@stdlib/repl/lib/syntax_highlighter.js | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 8f0b43b289ae..52c7ee038fb8 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -268,7 +268,7 @@ function REPL( options ) { 'completer': this._completer })); - // Initialize a multiline handler: + // Initialize a multi-line handler: setNonEnumerableReadOnly( this, '_multilineHandler', new MultilineHandler( this, this._rli._ttyWrite ) ); // Create a new auto-closer: @@ -344,7 +344,7 @@ function REPL( options ) { self._autoCloser.beforeKeypress( data, key ); completed = self._previewCompleter.beforeKeypress( data, key ); - // If completion was auto-completed, don't trigger multiline keybindings to avoid double operations... + // If completion was auto-completed, don't trigger multi-line keybindings to avoid double operations... if ( !completed ) { self._multilineHandler.beforeKeypress( data, key ); return; diff --git a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js index e87d0fa84ceb..ba9899380cd9 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -51,13 +51,13 @@ var AOPTS = { // MAIN // /** -* Constructor for creating a multiline handler. +* Constructor for creating a multi-line handler. * * @private * @constructor * @param {REPL} repl - REPL instance * @param {Function} ttyWrite - function to trigger default behavior of a keypress -* @returns {MultilineHandler} multiline handler instance +* @returns {MultilineHandler} multi-line handler instance */ function MultilineHandler( repl, ttyWrite ) { if ( !( this instanceof MultilineHandler ) ) { @@ -85,7 +85,7 @@ function MultilineHandler( repl, ttyWrite ) { // Cache the length of the input prompt: this._promptLength = repl._inputPrompt.length; - // Initialize an internal status object for multiline mode: + // Initialize an internal status object for multi-line mode: this._multiline = {}; this._multiline.active = false; this._multiline.trigger = false; @@ -96,7 +96,7 @@ function MultilineHandler( repl, ttyWrite ) { // Initialize a variable storing current line index: this._lineIndex = 0; - // Initialize a buffer for storing code after cursor when manually entering multiline mode: + // Initialize a buffer for storing code after cursor when manually entering multi-line mode: this._remainingLine = ''; return this; @@ -135,7 +135,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_renderLines', function r // Write remaining lines below the current line: lines = this._lines.slice( this._lineIndex + 1 ); - this._ostream.write( '\n' + lines.join('\n') ); + this._ostream.write( '\n' + lines.join( '\n' ) ); // Reset cursor position: readline.moveCursor( this._ostream, 0, min( -1 * lines.length, -1 ) ); @@ -143,7 +143,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_renderLines', function r }); /** -* Moves cursor to a specified position in the multiline input. +* Moves cursor to a specified position in the multi-line input. * * @private * @name _moveCursor @@ -299,7 +299,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_backspace', function bac }); /** -* Updates flags and buffers before to trigger multiline mode. +* Updates flags and buffers before triggering multi-line mode. * * @private * @name _triggerMultiline @@ -318,14 +318,14 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_triggerMultiline', funct }); /** -* Checks if the command is incomplete and a multiline input. +* Checks if the command is incomplete and a multi-line input. * * @private * @name _isMultilineInput * @memberof MultilineHandler.prototype * @type {Function} * @param {string} cmd - command -* @returns {boolean} boolean indicating whether the command is a multiline input +* @returns {boolean} boolean indicating whether the command is a multi-line input */ setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', function isMultilineInput( cmd ) { var node; @@ -348,7 +348,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', funct return false; } if ( hasMultilineError( cmd, AOPTS ) ) { - debug( 'Detected multi-line input. Triggering multiline mode...' ); + debug( 'Detected multi-line input. Triggering multi-line mode...' ); return true; } // Still possible that a user is attempting to enter an object literal across multiple lines... @@ -359,7 +359,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_isMultilineInput', funct if ( node.type === 'BlockStatement' && node.end === ast.end ) { tmp = cmd.slice( node.start, node.end ); if ( hasMultilineError( tmp, AOPTS ) ) { - debug( 'Detected multi-line input. Triggering multiline mode...' ); + debug( 'Detected multi-line input. Triggering multi-line mode...' ); return true; } } @@ -414,9 +414,9 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'processLine', function pr debug( 'Line: %s', line ); this._cmd[ this._lineIndex ] = line; - // Check for multiline triggers... + // Check for multi-line triggers... if ( this._multiline.trigger ) { - debug( 'Detected multiline trigger input. Waiting for additional lines...' ); + debug( 'Detected multi-line trigger. Waiting for additional lines...' ); // Insert a newline: this._lineIndex += 1; @@ -507,7 +507,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK if ( !key ) { return; } - // Trigger multiline input when encountering `CTRL+O` keybinding... + // Trigger multi-line input when encountering `CTRL+O` keybinding... if ( key.name === 'o' && key.ctrl ) { this._triggerMultiline(); @@ -515,7 +515,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK this._rli.write( '\n' ); } if ( this._multiline.active ) { - // Render remaining lines with each keypress when in multiline mode: + // Render remaining lines with each keypress when in multi-line mode: this._renderLines(); } }); @@ -537,12 +537,12 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function this._ttyWrite.call( this._rli, data, key ); return; } - // Check whether to trigger multiline mode or execute the command when `return` key is encountered... + // Check whether to trigger multi-line mode or execute the command when `return` key is encountered... if ( key.name === 'return' ) { cmd = copy( this._cmd ); cmd[ this._lineIndex ] = this._rli.line; - // If command is incomplete, trigger multiline mode... + // If command is incomplete, trigger multi-line mode... if ( !this._isMultilineInput( cmd.join( '\n' ) ) ) { this._ttyWrite.call( this._rli, data, key ); return; @@ -557,7 +557,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function this._ttyWrite.call( this._rli, data, key ); return; } - // If multiline mode is active, enable navigation... + // If multi-line mode is active, enable navigation... switch ( key.name ) { case 'up': this._moveUp(); @@ -568,7 +568,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function this._renderLines(); break; case 'left': - // If at the beginning of the line, move up to the previous line, else trigger default behavior... + // If at the beginning of the line, move up to the previous line; otherwise, trigger default behavior... if ( this._rli.cursor === 0 ) { this._moveLeft(); this._renderLines(); @@ -577,7 +577,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function this._ttyWrite.call( this._rli, data, key ); break; case 'right': - // If at the end of the line, move up to the next line, else trigger default behavior... + // If at the end of the line, move up to the next line; otherwise, trigger default behavior... if ( this._rli.cursor === this._rli.line.length ) { this._moveRight(); this._renderLines(); @@ -586,7 +586,7 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function this._ttyWrite.call( this._rli, data, key ); break; case 'backspace': - // If at the beginning of the line, remove and move up to the previous line, else trigger default behavior... + // If at the beginning of the line, remove and move up to the previous line; otherwise, trigger default behavior... if ( this._rli.cursor === 0 ) { this._backspace(); this._renderLines(); diff --git a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js index c2acb1cb30dc..d0b1461340e9 100644 --- a/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js +++ b/lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js @@ -85,7 +85,7 @@ function SyntaxHighlighter( repl, ostream, enabled ) { // Cache a reference to the output writable stream: this._ostream = ostream; - // Cache a reference to the REPL's multiline handler: + // Cache a reference to the REPL's multi-line handler: this._multilineHandler = repl._multilineHandler; // Initialize a buffer containing the current line to validate line changes: