diff --git a/lib/node_modules/@stdlib/repl/lib/defaults.js b/lib/node_modules/@stdlib/repl/lib/defaults.js index 10a683a3f74a..10a1d7053b4b 100644 --- a/lib/node_modules/@stdlib/repl/lib/defaults.js +++ b/lib/node_modules/@stdlib/repl/lib/defaults.js @@ -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 // @@ -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': '', diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 8bb1b56fec8b..9739d1b941ff 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -74,6 +74,7 @@ var EagerEvaluator = require( './eager_evaluator.js' ); 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 // @@ -329,7 +330,7 @@ function REPL( options ) { 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 ); // TODO: check whether to synchronously initialize a REPL history file diff --git a/lib/node_modules/@stdlib/repl/lib/welcome_text.js b/lib/node_modules/@stdlib/repl/lib/welcome_text.js index 08e9aeda7e8b..cd445ce6d80a 100644 --- a/lib/node_modules/@stdlib/repl/lib/welcome_text.js +++ b/lib/node_modules/@stdlib/repl/lib/welcome_text.js @@ -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.) Print help text for a specified property.', - '', - ' example(alias) Run examples for a specified alias.', - ' example(alias.) 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.)', + 'desc': 'Print help text for a specified property.' + }, + { + 'cmd': '', + 'desc': '' + }, + { + 'cmd': 'example(alias)', + 'desc': 'Run examples for a specified alias.' + }, + { + 'cmd': 'example(alias.)', + '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;