Skip to content

fix: example command hanging in REPL when executing multi-line code #2706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion lib/node_modules/@stdlib/repl/lib/commands/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
function onCommand( alias ) {
var aliases;
var entry;
var lines;
var out;
var len;
var N;
Expand All @@ -81,7 +82,7 @@
if ( !out ) {
out = alias2example( alias );
}
// TODO: add support for user docs

Check warning on line 85 in lib/node_modules/@stdlib/repl/lib/commands/example.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: add support for user docs'
}
// If unable to resolve an associated example, check if we were provided a reference to a REPL-specific command...
if ( !out ) {
Expand Down Expand Up @@ -115,7 +116,7 @@
out = alias2example( aliases[ i-1 ] );
}
}
// TODO: add support for searching user documentation

Check warning on line 119 in lib/node_modules/@stdlib/repl/lib/commands/example.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: add support for searching user...'

// If we failed to resolve an associated example and the provided value is an object, try finding a provided value's constructor (e.g., if provided a `Uint32Array`, try finding examples for `Uint32Array`)...
if ( !out && typeof alias === 'object' && alias !== null && alias.constructor ) {
Expand All @@ -133,6 +134,7 @@
}
}
if ( out ) {
lines = [];
out = out.split( RE_EOL );
len = out.length;
i = -1;
Expand All @@ -149,13 +151,27 @@
* @private
* @param {string} cmd - command
* @param {boolean} success - boolean indicating whether the command successfully executed
* @returns {void}
*/
function next() {
var j;
i += 1;
if ( i < len ) {
// Forward the next line to the REPL readline interface in order to mimic user input...
if ( out[ i ] ) {
repl._rli.write( out[ i ]+'\n' );
lines.push( out[ i ] );

// If line is part of a multi-line input, wait for the next line...
if ( repl._multilineHandler.isMultilineInput( lines.join( '\n' ) ) ) {
return next();
}
for ( j = 0; j < lines.length; j++ ) {
repl._rli.write( lines[ j ] );
repl._rli.write( '\n', {
'name': 'return'
});
}
lines = [];
repl.once( 'drain', next );
} else {
nextTick( next );
Expand Down
102 changes: 51 additions & 51 deletions lib/node_modules/@stdlib/repl/lib/multiline_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,56 +394,6 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, '_triggerMultiline', funct
this._rli.line = this._rli.line.substring( 0, this._rli.cursor );
});

/**
* 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 multi-line 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( '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.' );
return false;
}
// 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 ) ) {
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...
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 multi-line mode...' );
return true;
}
}
debug( 'Multi-line input not detected.' );
return false;
});

/**
* Resets input buffers.
*
Expand Down Expand Up @@ -581,6 +531,55 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'isPasting', function isPa
return this._multiline.pasteMode;
});

/**
* Checks if the command is incomplete and a multi-line input.
*
* @name isMultilineInput
* @memberof MultilineHandler.prototype
* @type {Function}
* @param {string} cmd - command
* @returns {boolean} boolean indicating whether the command is a multi-line 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( '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.' );
return false;
}
// 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 ) ) {
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...
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 multi-line mode...' );
return true;
}
}
debug( 'Multi-line input not detected.' );
return false;
});

/**
* Processes input line data.
*
Expand Down Expand Up @@ -737,9 +736,10 @@ setNonEnumerableReadOnly( MultilineHandler.prototype, 'beforeKeypress', function
case 'return':
cmd = copy( this._cmd );
cmd[ this._lineIndex ] = this._rli.line;
this._lines[ this._lineIndex ] = this._rli.line;

// If we are in paste mode or the command is incomplete, trigger multi-line mode...
if ( !this._multiline.pasteMode && !this._isMultilineInput( cmd.join( '\n' ) ) ) {
if ( !this._multiline.pasteMode && !this.isMultilineInput( cmd.join( '\n' ) ) ) {
this._ttyWrite.call( this._rli, data, key );
return;
}
Expand Down
Loading