diff --git a/lib/node_modules/@stdlib/repl/README.md b/lib/node_modules/@stdlib/repl/README.md index 206a2b19097f..884cc6926fd8 100644 --- a/lib/node_modules/@stdlib/repl/README.md +++ b/lib/node_modules/@stdlib/repl/README.md @@ -83,6 +83,8 @@ The function supports specifying the following settings: - **autoClosePairs**: boolean indicating whether to automatically insert matching brackets, parentheses, and quotes. Default: `true`. - **autoDeletePairs**: boolean indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes. Default: `true`. - **autoPage**: boolean indicating whether to automatically page return values having a display size exceeding the visible screen. When streams are TTY, the default is `true`; otherwise, the default is `false`. +- **bracketedPaste**: boolean indicating whether to enable bracketed-paste mode. When streams are TTY, the default is `true`; otherwise, the default is `false`. +- **autoDisableBracketedPasteOnExit**: boolean indicating whether to automatically disable bracketed-paste upon exiting the REPL. When streams are TTY and bracketed paste is enabled, the default is `true`; otherwise, the default is `false`. - **completionPreviews**: boolean indicating whether to display completion previews for auto-completion. When streams are TTY, the default is `true`; otherwise, the default is `false`. - **syntaxHighlighting**: boolean indicating whether to enable syntax highlighting of entered input expressions. When streams are TTY, the default is `true`; otherwise, the default is `false`. - **theme**: initial color theme for syntax highlighting. Default: `stdlib-ansi-basic`. diff --git a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js index 2d8e5cf5dbde..7cb547108bf8 100644 --- a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js +++ b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js @@ -65,17 +65,19 @@ function isQuote( ch ) { * @param {Object} rli - readline instance * @param {boolean} autoClose - boolean indicating whether auto-closing should be initially enabled * @param {boolean} autoDelete - boolean indicating whether auto-deleting should be initially enabled +* @param {MultilineHandler} multiline - multiline handler instance * @returns {AutoCloser} auto-closer instance */ -function AutoCloser( rli, autoClose, autoDelete ) { +function AutoCloser( rli, autoClose, autoDelete, multiline ) { if ( !(this instanceof AutoCloser) ) { - return new AutoCloser( rli, autoClose, autoDelete ); + return new AutoCloser( rli, autoClose, autoDelete, multiline ); } debug( 'Creating an auto-closer...' ); this._rli = rli; this._ignoreBackspace = false; this._autoClose = autoClose; this._autoDelete = autoDelete; + this._multiline = multiline; return this; } @@ -316,6 +318,9 @@ setNonEnumerableReadOnly( AutoCloser.prototype, 'beforeKeypress', function befor if ( !this._autoDelete ) { return false; } + if ( this._multiline.isPasting() ) { + return false; + } if ( !key || key.name !== 'backspace' ) { return false; } @@ -360,6 +365,9 @@ setNonEnumerableReadOnly( AutoCloser.prototype, 'onKeypress', function onKeypres if ( !this._autoClose ) { return false; } + if ( this._multiline.isPasting() ) { + return false; + } cursor = this._rli.cursor; line = this._rli.line; diff --git a/lib/node_modules/@stdlib/repl/lib/completer_engine.js b/lib/node_modules/@stdlib/repl/lib/completer_engine.js index 5de916695d2e..e3058e4f8f35 100644 --- a/lib/node_modules/@stdlib/repl/lib/completer_engine.js +++ b/lib/node_modules/@stdlib/repl/lib/completer_engine.js @@ -628,7 +628,6 @@ setNonEnumerableReadOnly( CompleterEngine.prototype, 'beforeKeypress', function this._ttyWrite.call( this._rli, data, key ); return; } - // If user is already viewing completions, allow navigating it... if ( this._isNavigating ) { switch ( key.name ) { @@ -638,6 +637,12 @@ setNonEnumerableReadOnly( CompleterEngine.prototype, 'beforeKeypress', function this.closeCompleter(); break; + // If paste sequences detected, close the completer: + case 'paste-start': + this.closeCompleter(); + this._ttyWrite.call( this._rli, data, key ); + break; + // If arrow keys detected, allow navigating the completions... case 'down': debug( 'Received a DOWN keypress event...' ); @@ -660,6 +665,11 @@ setNonEnumerableReadOnly( CompleterEngine.prototype, 'beforeKeypress', function } return; } + // If we are in the middle of receiving pasted input, use TAB for indentation and don't trigger completions... + if ( this._multiline.isPasting() ) { + this._ttyWrite.call( this._rli, data, key ); + return; + } // Trigger TAB completions: cursor = this._rli.cursor; line = this._rli.line; diff --git a/lib/node_modules/@stdlib/repl/lib/defaults.js b/lib/node_modules/@stdlib/repl/lib/defaults.js index 8f07cbe2b011..6bc31efa71e0 100644 --- a/lib/node_modules/@stdlib/repl/lib/defaults.js +++ b/lib/node_modules/@stdlib/repl/lib/defaults.js @@ -89,9 +89,15 @@ function defaults() { // Flag indicating whether to enable automatically page return values requiring a display size exceeding the visible screen (note: default depends on whether TTY): 'autoPage': void 0, + // Flag indicating whether to enable bracketed-paste mode (note: default depends on whether TTY): + 'bracketedPaste': void 0, + // Flag indicating whether to enable the display of completion previews for auto-completion (note: default depends on whether TTY): 'completionPreviews': void 0, + // Flag indicating whether to automatically disable bracketed-paste upon exiting the REPL (note: default depends on whether TTY): + 'autoDisableBracketedPasteOnExit': void 0, + // Flag indicating whether to enable syntax highlighting (note: default depends on whether TTY): 'syntaxHighlighting': void 0, diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index b63a95e23f01..4c872c37bf2f 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/* eslint-disable no-restricted-syntax, no-invalid-this, no-underscore-dangle, max-lines */ +/* eslint-disable no-restricted-syntax, no-invalid-this, no-underscore-dangle, max-lines, max-lines-per-function */ 'use strict'; @@ -102,7 +102,9 @@ var debug = logger( 'repl' ); * @param {boolean} [options.settings.autoClosePairs=true] - boolean indicating whether to automatically insert matching brackets, parentheses, and quotes * @param {boolean} [options.settings.autoDeletePairs=true] - boolean indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes * @param {boolean} [options.settings.autoPage] - boolean indicating whether to automatically page return values requiring a display size exceeding the visible screen +* @param {boolean} [options.settings.bracketedPaste] - boolean indicating whether to enable bracketed-paste mode * @param {boolean} [options.settings.completionPreviews] - boolean indicating whether to enable completion previews for auto-completion +* @param {boolean} [options.settings.autoDisableBracketedPasteOnExit] - boolean indicating whether to automatically disable bracketed-paste upon exiting the REPL * @param {boolean} [options.settings.syntaxHighlighting] - boolean indicating whether to enable syntax highlighting * @param {string} [options.settings.theme] - initial color theme for syntax highlighting * @throws {Error} must provide valid options @@ -151,7 +153,9 @@ function REPL( options ) { } opts.isTTY = ( opts.isTTY === void 0 ) ? opts.output.isTTY : opts.isTTY; opts.settings.autoPage = ( opts.settings.autoPage === void 0 ) ? opts.isTTY : opts.settings.autoPage; // eslint-disable-line max-len + opts.settings.bracketedPaste = ( opts.settings.bracketedPaste === void 0 && opts.isTTY ) ? true : opts.settings.bracketedPaste; // eslint-disable-line max-len opts.settings.completionPreviews = ( opts.settings.completionPreviews === void 0 ) ? opts.isTTY : opts.settings.completionPreviews; // eslint-disable-line max-len + opts.settings.autoDisableBracketedPasteOnExit = ( opts.settings.autoDisableBracketedPasteOnExit === void 0 ) ? opts.isTTY : opts.settings.autoDisableBracketedPasteOnExit; // eslint-disable-line max-len opts.settings.syntaxHighlighting = ( opts.settings.syntaxHighlighting === void 0 ) ? opts.isTTY : opts.settings.syntaxHighlighting; // eslint-disable-line max-len debug( 'Options: %s', JSON.stringify({ @@ -275,7 +279,7 @@ function REPL( options ) { setNonEnumerableReadOnly( this, '_completerEngine', new CompleterEngine( this, this._completer, this._wstream, this._rli._ttyWrite ) ); // Create a new auto-closer: - setNonEnumerableReadOnly( this, '_autoCloser', new AutoCloser( this._rli, this._settings.autoClosePairs, this._settings.autoDeletePairs ) ); + setNonEnumerableReadOnly( this, '_autoCloser', new AutoCloser( this._rli, this._settings.autoClosePairs, this._settings.autoDeletePairs, this._multilineHandler ) ); // Initialize a preview completer: setNonEnumerableReadOnly( this, '_previewCompleter', new PreviewCompleter( this._rli, this._completer, this._ostream, this._settings.completionPreviews ) ); @@ -321,6 +325,10 @@ function REPL( options ) { // Set the syntax highlighting theme... this.settings( 'theme', opts.settings.theme ); + // Initialize bracketed-paste: + if ( opts.settings.bracketedPaste !== void 0 ) { + this.settings( 'bracketedPaste', opts.settings.bracketedPaste ); + } // Check whether to load and execute a JavaScript file (e.g., prior REPL history) upon startup... if ( opts.load ) { this.load( opts.load ); @@ -404,6 +412,9 @@ function REPL( options ) { * @private */ function onClose() { + if ( self._settings.bracketedPaste && self._settings.autoDisableBracketedPasteOnExit ) { // eslint-disable-line max-len + self._multilineHandler.disableBracketedPaste(); + } ostream.end(); ostream.unpipe(); @@ -1481,6 +1492,12 @@ setNonEnumerableReadOnly( REPL.prototype, 'settings', function settings() { } } else if ( name === 'theme' ) { this._syntaxHighlighter.setTheme( value ); + } else if ( name === 'bracketedPaste' ) { + if ( value ) { + this._multilineHandler.enableBracketedPaste(); + } else { + this._multilineHandler.disableBracketedPaste(); + } } 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 817704db71fd..d3fa1a59c7c3 100644 --- a/lib/node_modules/@stdlib/repl/lib/multiline_handler.js +++ b/lib/node_modules/@stdlib/repl/lib/multiline_handler.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/* eslint-disable no-underscore-dangle, no-restricted-syntax, no-invalid-this */ +/* eslint-disable no-underscore-dangle, no-restricted-syntax, no-invalid-this, max-lines */ 'use strict'; @@ -87,6 +87,7 @@ function MultilineHandler( repl, ttyWrite ) { this._multiline = {}; this._multiline.active = false; this._multiline.trigger = false; + this._multiline.pasteMode = false; // Initialize a buffer for caching input lines: this._lines = []; @@ -399,6 +400,42 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'resetInput', function res this._lines.length = 0; }); +/** +* Enables bracketed-paste mode. +* +* @name enableBracketedPaste +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'enableBracketedPaste', function enableBracketedPaste() { + this._ostream.write( '\u001b[?2004h' ); +}); + +/** +* Disables bracketed-paste mode. +* +* @name disableBracketedPaste +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {void} +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'disableBracketedPaste', function disableBracketedPaste() { + this._ostream.write( '\u001b[?2004l' ); +}); + +/** +* Checks whether the REPL is currently receiving pasted input. +* +* @name isPasting +* @memberof MultilineHandler.prototype +* @type {Function} +* @returns {boolean} boolean indicating whether the REPL is currently receiving pasted input +*/ +setNonEnumerableReadOnly( MultilineHandler.prototype, 'isPasting', function isPasting() { + return this._multiline.pasteMode; +}); + /** * Processes input line data. * @@ -511,6 +548,15 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'onKeypress', function onK if ( !key ) { return; } + // Check for paste sequences... + if ( key.name === 'paste-start' ) { + this._multiline.pasteMode = true; + return; + } + if ( key.name === 'paste-end' ) { + this._multiline.pasteMode = false; + return; + } // Trigger multi-line input when encountering `CTRL+O` keybinding... if ( key.name === 'o' && key.ctrl ) { this._triggerMultiline(); @@ -546,8 +592,8 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function cmd = copy( this._cmd ); cmd[ this._lineIndex ] = this._rli.line; - // If command is incomplete, trigger multi-line mode... - if ( !this._isMultilineInput( cmd.join( '\n' ) ) ) { + // If we are in paste mode or the command is incomplete, trigger multi-line mode... + if ( !this._multiline.pasteMode && !this._isMultilineInput( cmd.join( '\n' ) ) ) { this._ttyWrite.call( this._rli, data, key ); return; } diff --git a/lib/node_modules/@stdlib/repl/lib/settings.js b/lib/node_modules/@stdlib/repl/lib/settings.js index abfb131a0d26..53a38eaffc00 100644 --- a/lib/node_modules/@stdlib/repl/lib/settings.js +++ b/lib/node_modules/@stdlib/repl/lib/settings.js @@ -40,10 +40,18 @@ var SETTINGS = { 'desc': 'Automatically page return values whose display size exceeds the visible screen.', 'type': 'boolean' }, + 'bracketedPaste': { + 'desc': 'Enable bracketed-paste mode.', + 'type': 'boolean' + }, 'completionPreviews': { 'desc': 'Enable the display of completion previews for auto-completion.', 'type': 'boolean' }, + 'autoDisableBracketedPasteOnExit': { + 'desc': 'Automatically disable bracketed-paste upon exiting the REPL.', + 'type': 'boolean' + }, 'syntaxHighlighting': { 'desc': 'Enable syntax highlighting.', 'type': 'boolean'