Skip to content
Open
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
5 changes: 2 additions & 3 deletions lib/node_modules/@stdlib/repl/lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
var stdin = require( '@stdlib/streams/node/stdin' );
var stdout = require( '@stdlib/streams/node/stdout' );
var DEFAULT_KEYBINDINGS = require( './default_keybindings.js' );
var WELCOME = require( './welcome_text.js' );


// MAIN //
Expand Down Expand Up @@ -64,8 +63,8 @@ function defaults() {
// Flag indicating whether to run a REPL in a sandboxed context:
'sandbox': true,

// Welcome message:
'welcome': WELCOME,
// Welcome message (null means generate dynamically based on terminal width):
'welcome': null,

// File path specifying where to save REPL command history:
'save': '',
Expand Down
3 changes: 2 additions & 1 deletion lib/node_modules/@stdlib/repl/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
var ALIAS_OVERRIDES = require( './alias_overrides.js' );
var SETTINGS = require( './settings.js' );
var SETTINGS_VALIDATORS = require( './settings_validators.js' );
var welcomeText = require( './welcome_text.js' );


// VARIABLES //
Expand Down Expand Up @@ -311,7 +312,7 @@
setNonEnumerableReadOnly( this, '_ttyWrite', this._rli._ttyWrite );

// Overwrite the private `ttyWrite` method to allow processing input before a "keypress" event is triggered:
this._rli._ttyWrite = beforeKeypress; // WARNING: overwriting a private property

Check warning on line 315 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'warning' comment: 'WARNING: overwriting a private property'

// Add event listeners:
this._rli.on( 'close', onClose );
Expand All @@ -329,11 +330,11 @@
this._istream.on( 'keypress', onKeypress );

// Write a welcome message:
this._wstream.write( opts.welcome );
this._wstream.write( ( opts.welcome === null ) ? welcomeText( this._wstream.columns || 80 ) : opts.welcome );

Check warning on line 333 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

This line has a length of 113. Maximum allowed is 80

// TODO: check whether to synchronously initialize a REPL history file

Check warning on line 335 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: check whether to synchronously...'

// TODO: check whether to synchronously initialize a REPL log file

Check warning on line 337 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: check whether to synchronously...'

// Add any provided user-defined themes...
if ( opts.themes ) {
Expand Down Expand Up @@ -531,9 +532,9 @@
// Update the internal command history buffer: [..., <id>, <cmd>, <success>, ...]
self._history.push( self._count, cmd, success );

// TODO: if successful and if necessary, (asynchronously?) write the command to a history file (question: do we only want to write successful commands to the history file? maybe we need to option for limiting to successful commands?)

Check warning on line 535 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: if successful and if necessary,...'

// TODO: if necessary, (asynchronously?) write the command and result to a log file (JSON serialization?)

Check warning on line 537 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: if necessary, (asynchronously?)...'
}
}

Expand Down Expand Up @@ -800,7 +801,7 @@

// Before creating a new execution context in a non-sandboxed environment, remove current workspace variables in order to allow garbage collection and avoid memory leaks (e.g., variables/functions declared during a REPL session which might remain bound to the environment `global` after clearing a REPL):
if ( this._sandbox === false ) {
// WARNING: in a non-sandboxed environment, if a global variable is externally introduced during a REPL session (i.e., introduced via a mechanism outside of the REPL environment), we will delete that global variable, which means the following logic may introduce unintended side-effects for this particular edge case (e.g., application code may expect the presence of the subsequently deleted global variable). While not ideal, (a) user applications should not be introducing globals to begin with and (b) the probability of a user running a REPL session, a user clearing that REPL session, AND a global variable being introduced between starting a REPL and clearing the REPL should be negligible.

Check warning on line 804 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'warning' comment: 'WARNING: in a non-sandboxed environment,...'
tmp = this._context.vars();
for ( i = 0; i < tmp.length; i++ ) {
if ( isConfigurableProperty( this._context, tmp[ i ] ) ) {
Expand Down Expand Up @@ -1223,7 +1224,7 @@
debug( 'Loading filepath: %s', fpath );

// Attempt to synchronously read the file:
file = readFileSync( fpath, 'utf8' );

Check warning on line 1227 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected sync method: 'readFileSync'
if ( file instanceof Error ) {
debug( 'Error: %s', file.message );
clbk( file );
Expand Down Expand Up @@ -1478,9 +1479,9 @@
// Clear the command queue:
this._queue.clear();

// TODO: ensure REPL history is saved (flushed) to file before closing the REPL (see https://github.com/nodejs/node/blob/b21e7c7bcf23a2715951e4cd96180e4dbf1dcd4d/lib/repl.js#L805)

Check warning on line 1482 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: ensure REPL history is saved...'

// TODO: ensure REPL log is saved (flushed) to file before closing the REPL

Check warning on line 1484 in lib/node_modules/@stdlib/repl/lib/main.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'todo' comment: 'TODO: ensure REPL log is saved (flushed)...'

nextTick( onTick );

Expand Down
242 changes: 218 additions & 24 deletions lib/node_modules/@stdlib/repl/lib/welcome_text.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,229 @@

'use strict';

// MAIN //
// VARIABLES //

// Minimum terminal width (in columns) for rendering the logo and info text side-by-side. Below this threshold, the welcome message switches to a stacked layout. NOTE: this value corresponds to the width of the widest side-by-side line; if the logo, info text, or command descriptions are modified, re-verify that this threshold still exceeds the widest rendered line.
var SIDE_BY_SIDE_MIN_WIDTH = 70;

// Minimum number of spaces separating the command column from the description column in the side-by-side help layout:
var HELP_GAP = 4;

// Indentation preceding each command in the help section:
var HELP_INDENT = ' ';

// Indentation preceding each description in the stacked (narrow) help layout:
var HELP_DESC_INDENT = ' ';

// Indentation preceding each line of info text in the stacked (narrow) layout:
var INFO_INDENT = ' ';

var MSG = [
// Gutter placed on each side of the vertical bar separating the logo from the info text in the side-by-side layout:
var LOGO_GUTTER = ' ';

// Vertical bar separating the logo from the info text in the side-by-side layout:
var LOGO_BAR = '|';

var LOGO = [
' _ _ _ _ _',
' ___| |_ __| | (_) |__ | A better REPL for JavaScript and Node.js.',
'/ __| __/ _` | | | \'_ \\ |',
'\\__ \\ || (_| | | | |_) | | For more info, see the source repository:',
'|___/\\__\\__,_|_|_|_.__/ | https://github.com/stdlib-js/stdlib',
'',
' ___| |_ __| | (_) |__',
'/ __| __/ _` | | | \'_ \\',
'\\__ \\ || (_| | | | |_) |',
'|___/\\__\\__,_|_|_|_.__/'
];

// TODO: include platform, copyright, version (see Julia, R, and Python for inspiration)
'',
' help() Print help text.',
' help(alias) Print help text for a specified alias.',
' help(alias.<key>) Print help text for a specified property.',
'',
' example(alias) Run examples for a specified alias.',
' example(alias.<key>) Run examples for a specified property.',
// Info text rendered beside the logo. In the side-by-side layout, the first logo line has no counterpart, so these lines align to the logo lines beginning with the second (hence `LOGO.length` must equal `INFO_LINES.length+1`):
var INFO_LINES = [
'A better REPL for JavaScript and Node.js.',
'',
' reset() Reset the REPL.',
' quit() Exit the REPL.',
'',
' license() Print license information.',
' copyright() Print copyright information.',
'',
''
].join( '\n' );
'For more info, see the source repository:',
'https://github.com/stdlib-js/stdlib'
];

var COMMANDS = [
{
'cmd': 'help()',
'desc': 'Print help text.'
},
{
'cmd': 'help(alias)',
'desc': 'Print help text for a specified alias.'
},
{
'cmd': 'help(alias.<key>)',
'desc': 'Print help text for a specified property.'
},
{
'cmd': '',
'desc': ''
},
{
'cmd': 'example(alias)',
'desc': 'Run examples for a specified alias.'
},
{
'cmd': 'example(alias.<key>)',
'desc': 'Run examples for a specified property.'
},
{
'cmd': '',
'desc': ''
},
{
'cmd': 'reset()',
'desc': 'Reset the REPL.'
},
{
'cmd': 'quit()',
'desc': 'Exit the REPL.'
},
{
'cmd': '',
'desc': ''
},
{
'cmd': 'license()',
'desc': 'Print license information.'
},
{
'cmd': 'copyright()',
'desc': 'Print copyright information.'
}
];


// FUNCTIONS //

/**
* Returns a string consisting of a character repeated a specified number of times.
*
* @private
* @param {string} ch - character to repeat
* @param {NonNegativeInteger} n - number of repetitions
* @returns {string} repeated string
*
* @example
* var str = repeat( '-', 3 );
* // returns '---'
*/
function repeat( ch, n ) {
var out;
var i;

out = '';
for ( i = 0; i < n; i++ ) {
out += ch;
}
return out;
}

/**
* Right-pads a string with spaces up to a minimum length.
*
* ## Notes
*
* - If the input string is already at least the desired length, the string is returned unchanged.
*
* @private
* @param {string} str - input string
* @param {NonNegativeInteger} len - minimum output length
* @returns {string} padded string
*
* @example
* var str = rightPad( 'abc', 5 );
* // returns 'abc '
*/
function rightPad( str, len ) {
if ( str.length >= len ) {
return str;
}
return str + repeat( ' ', len-str.length );
}


// MAIN //

/**
* Returns the welcome message formatted for the given terminal width.
*
* @private
* @param {PositiveInteger} columns - terminal width in columns
* @returns {string} formatted welcome message
*/
function welcomeText( columns ) {
var cmdWidth;
var isWide;
var logoW;
var lines;
var line;
var text;
var i;

isWide = ( columns >= SIDE_BY_SIDE_MIN_WIDTH );

// Resolve the width of the widest logo line (used to align the separator in both layouts):
logoW = 0;
for ( i = 0; i < LOGO.length; i++ ) {
if ( LOGO[ i ].length > logoW ) {
logoW = LOGO[ i ].length;
}
}
lines = [];
if ( isWide ) {
// Side-by-side layout: render the first logo line on its own, then pair each subsequent logo line with a line of info text separated by a vertical bar...
lines.push( LOGO[ 0 ] );
for ( i = 1; i < LOGO.length; i++ ) {
line = rightPad( LOGO[ i ], logoW ) + LOGO_GUTTER + LOGO_BAR;
text = INFO_LINES[ i-1 ];
if ( text ) {
line += LOGO_GUTTER + text;
}
lines.push( line );
}
} else {
// Stacked layout for narrow terminals: render the logo, a separator, and then the info text on successive lines...
for ( i = 0; i < LOGO.length; i++ ) {
lines.push( LOGO[ i ] );
}
lines.push( '' );
lines.push( repeat( '-', logoW ) );
lines.push( '' );
for ( i = 0; i < INFO_LINES.length; i++ ) {
lines.push( INFO_INDENT + INFO_LINES[ i ] );
}
}

// Append the help text section:
lines.push( '' );

// In the side-by-side layout, resolve the command column width from the longest command so that descriptions remain aligned and separated by at least `HELP_GAP` spaces:
cmdWidth = 0;
if ( isWide ) {
for ( i = 0; i < COMMANDS.length; i++ ) {
if ( COMMANDS[ i ].cmd.length > cmdWidth ) {
cmdWidth = COMMANDS[ i ].cmd.length;
}
}
cmdWidth += HELP_GAP;
}
for ( i = 0; i < COMMANDS.length; i++ ) {
if ( COMMANDS[ i ].cmd === '' ) {
lines.push( '' );
} else if ( isWide ) {
lines.push( HELP_INDENT + rightPad( COMMANDS[ i ].cmd, cmdWidth ) + COMMANDS[ i ].desc );
} else {
// Stack the command and its description on separate lines:
lines.push( HELP_INDENT + COMMANDS[ i ].cmd );
lines.push( HELP_DESC_INDENT + COMMANDS[ i ].desc );
}
}
lines.push( '' );
lines.push( '' );

return lines.join( '\n' );
}


// EXPORTS //

module.exports = MSG;
module.exports = welcomeText;