From f34f431af8582897e75c18e5629f3ef95401eed1 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 5 Dec 2024 08:07:21 +0000 Subject: [PATCH 01/17] feat(repl): add settings and prototype methods for keybindings Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/actions.js | 60 +++++++++ lib/node_modules/@stdlib/repl/lib/commands.js | 4 + .../@stdlib/repl/lib/commands/keybindings.js | 76 +++++++++++ .../repl/lib/commands/set_keybinding.js | 68 ++++++++++ .../@stdlib/repl/lib/display_prompt.js | 2 +- .../@stdlib/repl/lib/editor_actions.js | 6 +- lib/node_modules/@stdlib/repl/lib/main.js | 118 +++++++++++++++++- .../@stdlib/repl/lib/validate_keybindings.js | 27 +--- 8 files changed, 331 insertions(+), 30 deletions(-) create mode 100644 lib/node_modules/@stdlib/repl/lib/actions.js create mode 100644 lib/node_modules/@stdlib/repl/lib/commands/keybindings.js create mode 100644 lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js diff --git a/lib/node_modules/@stdlib/repl/lib/actions.js b/lib/node_modules/@stdlib/repl/lib/actions.js new file mode 100644 index 000000000000..af0e41bdfda5 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/actions.js @@ -0,0 +1,60 @@ +/** +* @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. +*/ + +'use strict'; + +// MAIN // + +/** +* List of REPL actions. +* +* @private +* @name ACTIONS +* @type {Object} +*/ +var ACTIONS = [ + 'moveRight', + 'moveLeft', + 'moveWordRight', + 'moveWordLeft', + 'moveBeginning', + 'moveEnd', + 'tab', + 'indentLineRight', + 'indentLineLeft', + 'deleteLeft', + 'deleteRight', + 'deleteWordLeft', + 'deleteWordRight', + 'deleteLineLeft', + 'deleteLineRight', + 'yankKilled', + 'yankPop', + 'undo', + 'redo', + 'transposeAboutCursor', + 'uppercaseNextWord', + 'capitalizeNextWord', + 'lowercaseNextWord', + 'clearScreen' +]; + + +// EXPORTS // + +module.exports = ACTIONS; diff --git a/lib/node_modules/@stdlib/repl/lib/commands.js b/lib/node_modules/@stdlib/repl/lib/commands.js index 6d843e9a2369..88c4a5a63ea9 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands.js +++ b/lib/node_modules/@stdlib/repl/lib/commands.js @@ -48,6 +48,7 @@ var onHelp = require( './commands/help.js' ); var onInfo = require( './commands/info.js' ); var isWorkspace = require( './commands/is_workspace.js' ); var isKeyword = require( './commands/is_keyword.js' ); +var onKeybindings = require( './commands/keybindings.js' ); var onLicense = require( './commands/license_text.js' ); var onLoad = require( './commands/load.js' ); var onLoadWorkspace = require( './commands/load_workspace.js' ); @@ -60,6 +61,7 @@ var onRenameWorkspace = require( './commands/rename_workspace.js' ); var onRerequire = require( './commands/rerequire.js' ); var onRerun = require( './commands/rerun.js' ); var onReset = require( './commands/reset.js' ); +var onSetKeybinding = require( './commands/set_keybinding.js' ); var onSettings = require( './commands/settings.js' ); var onThemes = require( './commands/themes.js' ); var onTutorial = require( './commands/tutorial.js' ); @@ -122,6 +124,7 @@ function commands( repl ) { cmds.push( [ 'info', onInfo( repl, cmds ), false ] ); cmds.push( [ 'isKeyword', isKeyword( repl ), false ] ); cmds.push( [ 'isWorkspace', isWorkspace( repl ), false ] ); + cmds.push( [ 'keybindings', onKeybindings( repl ), false ] ); cmds.push( [ 'license', onLicense( repl ), false ] ); cmds.push( [ 'load', onLoad( repl ), false ] ); cmds.push( [ 'loadWorkspace', onLoadWorkspace( repl ), false ] ); @@ -134,6 +137,7 @@ function commands( repl ) { cmds.push( [ 'rerequire', onRerequire( repl ), false ] ); cmds.push( [ 'rerun', onRerun( repl ), false ] ); cmds.push( [ 'reset', onReset( repl ), false ] ); + cmds.push( [ 'setKeybinding', onSetKeybinding( repl ), false ] ); cmds.push( [ 'settings', onSettings( repl ), false ] ); cmds.push( [ 'themes', onThemes( repl ), false ] ); cmds.push( [ 'tutorial', onTutorial( repl ), false ] ); diff --git a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js new file mode 100644 index 000000000000..ae37a1f2c938 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js @@ -0,0 +1,76 @@ +/** +* @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 */ + +'use strict'; + +// MODULES // + +var format = require( '@stdlib/string/format' ); +var contains = require( '@stdlib/assert/contains' ); +var isString = require( '@stdlib/assert/is-string' ); +var ACTIONS = require( './../actions.js' ); + + +// MAIN // + +/** +* Returns a callback to be invoked upon calling the `keybindings` command. +* +* @private +* @param {REPL} repl - REPL instance +* @returns {Function} callback +*/ +function command( repl ) { + return onCommand; + + /** + * Returns all (or select) keybindings. + * + * @private + * @param {string} [action] - action name + * @returns {(Object|Array)} keybindings object or a list of keybindings for an action + */ + function onCommand() { + var action; + var nargs; + + nargs = arguments.length; + if ( nargs === 0 ) { + return repl.keybindings(); + } + if ( nargs === 1 ) { + action = arguments[ 0 ]; + if ( !isString( action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.', action ) ); + return; + } + if ( !contains( ACTIONS, action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); + return; + } + return repl.keybindings()[ action ]; + } + } +} + + +// EXPORTS // + +module.exports = command; diff --git a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js new file mode 100644 index 000000000000..98a0db33a067 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js @@ -0,0 +1,68 @@ +/** +* @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 */ + +'use strict'; + +// MODULES // + +var format = require( '@stdlib/string/format' ); +var contains = require( '@stdlib/assert/contains' ); +var isString = require( '@stdlib/assert/is-string' ); +var ACTIONS = require( './../actions.js' ); + + +// MAIN // + +/** +* Returns a callback to be invoked upon calling the `setKeybinding` command. +* +* @private +* @param {REPL} repl - REPL instance +* @returns {Function} callback +*/ +function command( repl ) { + return onCommand; + + /** + * Allows the user to set a keybinding. + * + * @private + * @param {string} action - action name + * @returns {void} + */ + function onCommand( action ) { + if ( !isString( action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.', action ) ); + return; + } + if ( !contains( ACTIONS, action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); + return; + } + repl._ostream.write( 'Listening for keypresses...' ); + repl._isCapturingKeybinding = true; + repl._targetAction = action; + } +} + + +// EXPORTS // + +module.exports = command; diff --git a/lib/node_modules/@stdlib/repl/lib/display_prompt.js b/lib/node_modules/@stdlib/repl/lib/display_prompt.js index e4004bbaa5ff..344cede3bbf7 100644 --- a/lib/node_modules/@stdlib/repl/lib/display_prompt.js +++ b/lib/node_modules/@stdlib/repl/lib/display_prompt.js @@ -42,7 +42,7 @@ function displayPrompt( repl, preserveCursor ) { var ws; // Avoid displaying a prompt if the REPL is currently in paging mode... - if ( repl._ostream.isPaging ) { + if ( repl._ostream.isPaging || repl._isCapturingKeybinding ) { return; } len = repl._cmd.length; diff --git a/lib/node_modules/@stdlib/repl/lib/editor_actions.js b/lib/node_modules/@stdlib/repl/lib/editor_actions.js index 3a06e7ae34b1..d85edaa924fd 100644 --- a/lib/node_modules/@stdlib/repl/lib/editor_actions.js +++ b/lib/node_modules/@stdlib/repl/lib/editor_actions.js @@ -523,6 +523,7 @@ setNonEnumerableReadOnly( EditorActions.prototype, 'clearScreen', function clear * @returns {boolean} boolean indicating whether an editor action was triggered */ setNonEnumerableReadOnly( EditorActions.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { + var isTriggered; var actionKeys; var inputKeys; var i; @@ -531,6 +532,7 @@ setNonEnumerableReadOnly( EditorActions.prototype, 'beforeKeypress', function be return false; } inputKeys = parseKey( key ); + isTriggered = false; for ( i = 0; i < EDITOR_ACTIONS.length; i++ ) { actionKeys = this._keybindings[ EDITOR_ACTIONS[ i ] ]; if ( !actionKeys ) { @@ -538,10 +540,10 @@ setNonEnumerableReadOnly( EditorActions.prototype, 'beforeKeypress', function be } if ( matchKeybindings( inputKeys, actionKeys ) ) { this[ EDITOR_ACTIONS[ i ] ](); - return true; + isTriggered = true; } } - return false; + return isTriggered; }); diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index b04bcf12da61..a35f39389b4f 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, max-lines-per-function */ +/* eslint-disable no-restricted-syntax, no-invalid-this, no-underscore-dangle, max-lines, max-lines-per-function, max-statements */ 'use strict'; @@ -49,6 +49,8 @@ var fifo = require( '@stdlib/utils/fifo' ); var nextTick = require( '@stdlib/utils/next-tick' ); var assign = require( '@stdlib/object/assign' ); var validate = require( './validate.js' ); +var validateKeybindings = require( './validate_keybindings.js' ); +var parseKey = require( './parse_key.js' ); var defaults = require( './defaults.js' ); var isSettingName = require( './is_setting_name.js' ); var setAliases = require( './set_aliases.js' ); @@ -260,6 +262,10 @@ function REPL( options ) { // Initialize an internal variable for setting a command callback: setNonEnumerable( this, '_done', void 0 ); + // Initialize internal variables for setting a keybinding: + setNonEnumerable( this, '_isCapturingKeybinding', false ); + setNonEnumerable( this, '_targetAction', '' ); + // Create a REPL execution context: setNonEnumerable( this, '_context', this.createContext() ); @@ -359,6 +365,10 @@ function REPL( options ) { self._ostream.beforeKeypress( data, key ); return; } + if ( self._isCapturingKeybinding ) { + self._captureKeybinding( key ); + return; + } self._autoCloser.beforeKeypress( data, key ); completed = self._previewCompleter.beforeKeypress( data, key ); FLG = self._editorActions.beforeKeypress( data, key ); @@ -548,6 +558,30 @@ setNonEnumerableReadOnly( REPL.prototype, '_prompt', function prompt() { return inputPrompt( this._inputPrompt, this._count ); }); +/** +* Captures the next "keypress" as a keybinding. +* +* @private +* @name _captureKeybinding +* @memberof REPL.prototype +* @type {Function} +* @param {(Object|void)} key - key object +* @returns {void} +*/ +setNonEnumerableReadOnly( REPL.prototype, '_captureKeybinding', function captureKeybinding( key ) { + var parsedKeys; + + if ( !key ) { + return; + } + parsedKeys = parseKey( key ); + this.setKeybinding( this._targetAction, parsedKeys ); + this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action: `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); + this._isCapturingKeybinding = false; + this._targetAction = ''; + displayPrompt( this, false ); +}); + /** * Returns the current line's prompt length. * @@ -1064,6 +1098,88 @@ setNonEnumerableReadOnly( REPL.prototype, 'deleteTheme', function deleteTheme( n this._syntaxHighlighter.deleteTheme( name ); }); +/** +* Returns all REPL keybindings. +* +* @name keybindings +* @memberof REPL.prototype +* @type {Function} +* @returns {Object} table mapping action names to a list of keybindings +* +* @example +* var debug = require( '@stdlib/streams/node/debug-sink' ); +* +* // Create a new REPL: +* var repl = new REPL({ +* 'output': debug() +* }); +* +* // ... +* +* // Fetch all current keybindings: +* var keybindings = repl.keybindings(); +* +* // ... +* +* // Close the REPL: +* repl.close(); +*/ +setNonEnumerableReadOnly( REPL.prototype, 'keybindings', function keybindings() { + return this._keybindings; +}); + +/** +* Sets a keybinding. +* +* @name setKeybinding +* @memberof REPL.prototype +* @type {Function} +* @param {string} action - action name +* @param {Array} keys - list of keys +* @throws {TypeError} first argument must be a string +* @throws {TypeError} second argument must be an array of valid keys +* @returns {void} +* +* @example +* var debug = require( '@stdlib/streams/node/debug-sink' ); +* +* // Create a new REPL: +* var repl = new REPL({ +* 'output': debug() +* }); +* +* // ... +* +* // Set a keybinding for the `clearScreen` action: +* repl.setKeybinding( 'clearScreen', [ +* { +* 'name': 'l', +* 'ctrl': true, +* 'shift': false, +* 'meta': false +* } +* ]); +* +* // ... +* +* // Close the REPL: +* repl.close(); +*/ +setNonEnumerableReadOnly( REPL.prototype, 'setKeybinding', function setKeybinding( action, keys ) { + var obj; + var err; + + obj = {}; + if ( !isString( action ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', action ) ); + } + obj[ action ] = keys; + err = validateKeybindings( this._keybindings, obj ); + if ( err ) { + throw err; + } +}); + /** * Loads and evaluates a JavaScript file line-by-line. * diff --git a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js index 7398c0bc7ec7..2979808be8dc 100644 --- a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js @@ -26,36 +26,11 @@ var isString = require( '@stdlib/assert/is-string' ); var isBoolean = require( '@stdlib/assert/is-boolean' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var format = require( '@stdlib/string/format' ); +var ACTIONS = require( './actions.js' ); // VARIABLES // -var ACTIONS = [ - 'moveRight', - 'moveLeft', - 'moveWordRight', - 'moveWordLeft', - 'moveBeginning', - 'moveEnd', - 'tab', - 'indentLineRight', - 'indentLineLeft', - 'deleteLeft', - 'deleteRight', - 'deleteWordLeft', - 'deleteWordRight', - 'deleteLineLeft', - 'deleteLineRight', - 'yankKilled', - 'yankPop', - 'undo', - 'redo', - 'transposeAboutCursor', - 'uppercaseNextWord', - 'capitalizeNextWord', - 'lowercaseNextWord', - 'clearScreen' -]; var KEY_BOOLEAN_PROPS = [ 'ctrl', 'shift', 'meta' ]; From 1647cb7d28efa7da9b1526f99fefefc2294e76cc Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Fri, 13 Dec 2024 10:31:09 +0000 Subject: [PATCH 02/17] feat: allow setting keybinding using optional arg Signed-off-by: Snehil Shah --- .../repl/lib/commands/set_keybinding.js | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js index 98a0db33a067..cff6e3585328 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js @@ -45,20 +45,41 @@ function command( repl ) { * * @private * @param {string} action - action name + * @param {Array} [keys] - list of keys * @returns {void} */ - function onCommand( action ) { - if ( !isString( action ) ) { - repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.', action ) ); + function onCommand() { + var action; + var nargs; + var keys; + + nargs = arguments.length; + if ( nargs === 0 ) { + repl._ostream.write( 'Error: invalid argument. First argument must be an action name.\n' ); return; } - if ( !contains( ACTIONS, action ) ) { - repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); + action = arguments[ 0 ]; + if ( nargs === 1 ) { + if ( !isString( action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.\n', action ) ); + return; + } + if ( !contains( ACTIONS, action ) ) { + repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.\n', action ) ); + return; + } + repl._ostream.write( 'Listening for keypresses...' ); + repl._isCapturingKeybinding = true; + repl._targetAction = action; return; } - repl._ostream.write( 'Listening for keypresses...' ); - repl._isCapturingKeybinding = true; - repl._targetAction = action; + keys = arguments[ 1 ]; + try { + repl.setKeybinding( action, keys ); + repl._ostream.write( format( '\nSuccessfully set keybindings for action `%s`.\n', action ) ); + } catch ( err ) { + repl._ostream.write( format( 'Error: %s\n', err.message ) ); + } } } From ea1e48a95aa760e4b2df6c35ec0c61e488d4d264 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Fri, 13 Dec 2024 10:31:44 +0000 Subject: [PATCH 03/17] feat: update help text and output msg Signed-off-by: Snehil Shah --- .../@stdlib/repl/lib/help_text.js | 159 +++++++++--------- lib/node_modules/@stdlib/repl/lib/main.js | 2 +- 2 files changed, 82 insertions(+), 79 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/help_text.js b/lib/node_modules/@stdlib/repl/lib/help_text.js index 10737246d93c..6bf39b8bdd32 100644 --- a/lib/node_modules/@stdlib/repl/lib/help_text.js +++ b/lib/node_modules/@stdlib/repl/lib/help_text.js @@ -22,84 +22,87 @@ var MSG = [ '', - ' help() Print help text.', - ' help(alias) Print help text for a specified alias.', - ' help(alias.) Print help text for a specified property.', - '', - ' info(alias) Print brief help text for a specified alias.', - ' info(alias.) Print brief help text for a specified property.', - '', - ' example(alias) Run examples for a specified alias.', - ' example(alias.) Run examples for a specified property.', - '', - ' tutorial() List tutorials.', - ' tutorial(name) Run a specified tutorial.', - '', - ' settings() List settings.', - ' settings(name) Return setting value.', - ' settings(name,value) Set a specified setting value.', - '', - ' ans Return the result of the last executed command.', - ' vars() List current workspace variable names.', - ' clearVars() Delete current workspace variables.', - '', - ' workspace() Switch to a specified (possibly new) workspace.', - ' workspaces() List workspace names.', - ' isWorkspace() Assert whether a workspace exists.', - ' varsWorkspace() List workspace variable names.', - ' loadWorkspace() Load variables from a specified workspace.', - ' clearWorkspace() Delete variables in a specified workspace.', - ' deleteWorkspace() Delete a specified workspace.', - ' renameWorkspace() Rename a specified workspace.', - ' currentWorkspace Return the name of the current workspace.', - '', - ' themes() List available color themes.', - ' addTheme(name,value) Add a new color theme.', - ' deleteTheme(name) Delete a specified color theme.', - ' renameTheme(old,new) Rename a specified color theme.', - ' getTheme([name]) Return the current (or a specified) theme\'s color palette.', - '', - ' assignin() Assign a value to a workspace variable.', - ' assignfrom() Read in a value from another workspace.', - '', - ' evalin() Evaluate an expression in a specified workspace.', - '', - ' rerun() Rerun previous commands.', - '', - ' require() Import a module, JSON, or local file.', - ' rerequire() Re-import a module, JSON, or local file.', - ' deeprerequire() Re-import a module and its dependencies.', - ' load() Load and evaluate a JavaScript file line-by-line.', - '', - ' save() Save previous commands to a specified file.', - ' saveStart() Start saving commands to a specified file.', - ' saveStop() Stop saving commands.', - '', - ' presentationStart() Start a REPL presentation.', - ' presentationStop() Stop a REPL presentation.', - '', - ' userDoc() Add user-defined documentation.', - ' clearUserDocs() Clear user-defined documentation.', - '', - ' clearHistory() Clear the REPL history.', - '', - ' clear() Clear the entire REPL screen and scrollback history.', - ' reset() Reset the REPL.', - ' quit() Exit the REPL.', - '', - ' aliases() List namespace contents.', - ' alias2pkg() Convert an alias to a stdlib package name.', - ' pkg2alias() Convert a stdlib package name to an alias.', - ' alias2related() List related aliases.', - ' pkg2related() List related package names.', - ' isKeyword() Assert whether a value is a reserved keyword/alias.', - '', - ' citation() Print how to cite stdlib in publications.', - ' license() Print license information.', - ' copyright() Print copyright information.', - ' contributors() List contributors.', - ' credits() Print credits.', - ' donate() Print donation information.', + ' help() Print help text.', + ' help(alias) Print help text for a specified alias.', + ' help(alias.) Print help text for a specified property.', + '', + ' info(alias) Print brief help text for a specified alias.', + ' info(alias.) Print brief help text for a specified property.', + '', + ' example(alias) Run examples for a specified alias.', + ' example(alias.) Run examples for a specified property.', + '', + ' tutorial() List tutorials.', + ' tutorial(name) Run a specified tutorial.', + '', + ' settings() List settings.', + ' settings(name) Return setting value.', + ' settings(name,value) Set a specified setting value.', + '', + ' ans Return the result of the last executed command.', + ' vars() List current workspace variable names.', + ' clearVars() Delete current workspace variables.', + '', + ' workspace() Switch to a specified (possibly new) workspace.', + ' workspaces() List workspace names.', + ' isWorkspace() Assert whether a workspace exists.', + ' varsWorkspace() List workspace variable names.', + ' loadWorkspace() Load variables from a specified workspace.', + ' clearWorkspace() Delete variables in a specified workspace.', + ' deleteWorkspace() Delete a specified workspace.', + ' renameWorkspace() Rename a specified workspace.', + ' currentWorkspace Return the name of the current workspace.', + '', + ' themes() List available color themes.', + ' addTheme(name,value) Add a new color theme.', + ' deleteTheme(name) Delete a specified color theme.', + ' renameTheme(old,new) Rename a specified color theme.', + ' getTheme([name]) Return the current (or a specified) theme\'s color palette.', + '', + ' keybindings([name]) List all (or select) keybindings.', + ' setKeybinding(name,[keys]) Set a keybinding.', + '', + ' assignin() Assign a value to a workspace variable.', + ' assignfrom() Read in a value from another workspace.', + '', + ' evalin() Evaluate an expression in a specified workspace.', + '', + ' rerun() Rerun previous commands.', + '', + ' require() Import a module, JSON, or local file.', + ' rerequire() Re-import a module, JSON, or local file.', + ' deeprerequire() Re-import a module and its dependencies.', + ' load() Load and evaluate a JavaScript file line-by-line.', + '', + ' save() Save previous commands to a specified file.', + ' saveStart() Start saving commands to a specified file.', + ' saveStop() Stop saving commands.', + '', + ' presentationStart() Start a REPL presentation.', + ' presentationStop() Stop a REPL presentation.', + '', + ' userDoc() Add user-defined documentation.', + ' clearUserDocs() Clear user-defined documentation.', + '', + ' clearHistory() Clear the REPL history.', + '', + ' clear() Clear the entire REPL screen and scrollback history.', + ' reset() Reset the REPL.', + ' quit() Exit the REPL.', + '', + ' aliases() List namespace contents.', + ' alias2pkg() Convert an alias to a stdlib package name.', + ' pkg2alias() Convert a stdlib package name to an alias.', + ' alias2related() List related aliases.', + ' pkg2related() List related package names.', + ' isKeyword() Assert whether a value is a reserved keyword/alias.', + '', + ' citation() Print how to cite stdlib in publications.', + ' license() Print license information.', + ' copyright() Print copyright information.', + ' contributors() List contributors.', + ' credits() Print credits.', + ' donate() Print donation information.', '', '' ].join( '\n' ); diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index a35f39389b4f..22980c3f9218 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -576,7 +576,7 @@ setNonEnumerableReadOnly( REPL.prototype, '_captureKeybinding', function capture } parsedKeys = parseKey( key ); this.setKeybinding( this._targetAction, parsedKeys ); - this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action: `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); + this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); this._isCapturingKeybinding = false; this._targetAction = ''; displayPrompt( this, false ); From 1ad026c8af7a21f870a22f4f5d8299cc9d51dd56 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Thu, 2 Jan 2025 18:51:11 +0000 Subject: [PATCH 04/17] fix: remove `^D` from default keybindings Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/keybindings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/keybindings.js b/lib/node_modules/@stdlib/repl/lib/keybindings.js index e45834a4d360..317c497b99d1 100644 --- a/lib/node_modules/@stdlib/repl/lib/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/keybindings.js @@ -169,12 +169,6 @@ var KEYBINDINGS = { 'ctrl': false, 'shift': false, 'meta': false - }, - { - 'name': 'd', - 'ctrl': true, - 'shift': false, - 'meta': false } ], 'deleteWordLeft': [ From 1333a92797f4d09e0ae6eeac985a8e763c5710c3 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 8 Jan 2025 14:04:43 +0000 Subject: [PATCH 05/17] refactor: maintain hash in a dedicated keybindings class Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/actions.js | 2 +- .../@stdlib/repl/lib/commands/keybindings.js | 2 +- .../repl/lib/commands/set_keybinding.js | 2 +- .../@stdlib/repl/lib/default_keybindings.js | 343 +++++++++++++ lib/node_modules/@stdlib/repl/lib/defaults.js | 4 +- .../@stdlib/repl/lib/editor_actions.js | 21 +- .../@stdlib/repl/lib/keybindings.js | 454 ++++++------------ lib/node_modules/@stdlib/repl/lib/main.js | 26 +- .../@stdlib/repl/lib/match_keybinding.js | 53 -- 9 files changed, 508 insertions(+), 399 deletions(-) create mode 100644 lib/node_modules/@stdlib/repl/lib/default_keybindings.js delete mode 100644 lib/node_modules/@stdlib/repl/lib/match_keybinding.js diff --git a/lib/node_modules/@stdlib/repl/lib/actions.js b/lib/node_modules/@stdlib/repl/lib/actions.js index af0e41bdfda5..86ceb3aecafb 100644 --- a/lib/node_modules/@stdlib/repl/lib/actions.js +++ b/lib/node_modules/@stdlib/repl/lib/actions.js @@ -1,7 +1,7 @@ /** * @license Apache-2.0 * -* Copyright (c) 2024 The Stdlib Authors. +* Copyright (c) 2025 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. diff --git a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js index ae37a1f2c938..e36c3a80cd8d 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js @@ -1,7 +1,7 @@ /** * @license Apache-2.0 * -* Copyright (c) 2024 The Stdlib Authors. +* Copyright (c) 2025 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. diff --git a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js index cff6e3585328..c9912d35c713 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js @@ -1,7 +1,7 @@ /** * @license Apache-2.0 * -* Copyright (c) 2024 The Stdlib Authors. +* Copyright (c) 2025 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. diff --git a/lib/node_modules/@stdlib/repl/lib/default_keybindings.js b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js new file mode 100644 index 000000000000..794d42c28799 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js @@ -0,0 +1,343 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 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 max-lines */ + +'use strict'; + +// MAIN // + +/** +* Table mapping of REPL actions and their corresponding keybinding triggers. +* +* @name DEFAULT_KEYBINDINGS +* @type {Object} +*/ +var DEFAULT_KEYBINDINGS = { + 'moveRight': [ + { + 'name': 'right', + 'ctrl': false, + 'shift': false, + 'meta': false + }, + { + 'name': 'f', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'moveLeft': [ + { + 'name': 'left', + 'ctrl': false, + 'shift': false, + 'meta': false + }, + { + 'name': 'b', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'moveWordRight': [ + { + 'name': 'right', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': 'f', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'moveWordLeft': [ + { + 'name': 'left', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': 'b', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'moveBeginning': [ + { + 'name': 'home', + 'ctrl': false, + 'shift': false, + 'meta': false + }, + { + 'name': 'a', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'moveEnd': [ + { + 'name': 'end', + 'ctrl': false, + 'shift': false, + 'meta': false + }, + { + 'name': 'e', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'tab': [ + { + 'name': 't', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'indentLineRight': [ + { + 'name': 'right', + 'ctrl': false, + 'shift': false, + 'meta': true + }, + { + 'name': 'i', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'indentLineLeft': [ + { + 'name': 'left', + 'ctrl': false, + 'shift': false, + 'meta': true + }, + { + 'name': 'i', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'deleteLeft': [ + { + 'name': 'backspace', + 'ctrl': false, + 'shift': false, + 'meta': false + }, + { + 'name': 'h', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'deleteRight': [ + { + 'name': 'delete', + 'ctrl': false, + 'shift': false, + 'meta': false + } + ], + 'deleteWordLeft': [ + { + 'name': 'backspace', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': 'backspace', + 'ctrl': false, + 'shift': false, + 'meta': true + }, + { + 'name': 'w', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'deleteWordRight': [ + { + 'name': 'delete', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': 'delete', + 'ctrl': false, + 'shift': false, + 'meta': true + }, + { + 'name': 'd', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'deleteLineLeft': [ + { + 'name': 'backspace', + 'ctrl': true, + 'shift': true, + 'meta': false + }, + { + 'name': 'u', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'deleteLineRight': [ + { + 'name': 'delete', + 'ctrl': true, + 'shift': true, + 'meta': false + }, + { + 'name': 'k', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'yankKilled': [ + { + 'name': 'y', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'yankPop': [ + { + 'name': 'y', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'undo': [ + { + 'name': '/', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': '-', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'redo': [ + { + 'name': '6', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': '6', + 'ctrl': true, + 'shift': true, + 'meta': false + }, + { + 'name': '^', + 'ctrl': true, + 'shift': false, + 'meta': false + }, + { + 'name': 'y', + 'ctrl': false, + 'shift': true, + 'meta': false + } + ], + 'transposeAboutCursor': [ + { + 'name': 't', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ], + 'uppercaseNextWord': [ + { + 'name': 'u', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'capitalizeNextWord': [ + { + 'name': 'c', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'lowercaseNextWord': [ + { + 'name': 'l', + 'ctrl': false, + 'shift': false, + 'meta': true + } + ], + 'clearScreen': [ + { + 'name': 'l', + 'ctrl': true, + 'shift': false, + 'meta': false + } + ] +}; + + +// EXPORTS // + +module.exports = DEFAULT_KEYBINDINGS; diff --git a/lib/node_modules/@stdlib/repl/lib/defaults.js b/lib/node_modules/@stdlib/repl/lib/defaults.js index 0c0789ec801c..5811564cd508 100644 --- a/lib/node_modules/@stdlib/repl/lib/defaults.js +++ b/lib/node_modules/@stdlib/repl/lib/defaults.js @@ -22,7 +22,7 @@ var stdin = require( '@stdlib/streams/node/stdin' ); var stdout = require( '@stdlib/streams/node/stdout' ); -var KEYBINDINGS = require( './keybindings.js' ); +var DEFAULT_KEYBINDINGS = require( './default_keybindings.js' ); var WELCOME = require( './welcome_text.js' ); @@ -80,7 +80,7 @@ function defaults() { 'quiet': false, // REPL keybindings: - 'keybindings': KEYBINDINGS, + 'keybindings': DEFAULT_KEYBINDINGS, // User settings: 'settings': { diff --git a/lib/node_modules/@stdlib/repl/lib/editor_actions.js b/lib/node_modules/@stdlib/repl/lib/editor_actions.js index d85edaa924fd..ae159163e50f 100644 --- a/lib/node_modules/@stdlib/repl/lib/editor_actions.js +++ b/lib/node_modules/@stdlib/repl/lib/editor_actions.js @@ -25,10 +25,10 @@ var readline = require( 'readline' ); var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var ltrimN = require( '@stdlib/string/left-trim-n' ); +var contains = require( '@stdlib/assert/contains' ); var uppercase = require( '@stdlib/string/base/uppercase' ); var lowercase = require( '@stdlib/string/base/lowercase' ); var capitalize = require( '@stdlib/string/base/capitalize' ); -var matchKeybindings = require( './match_keybinding.js' ); var parseKey = require( './parse_key.js' ); @@ -523,27 +523,22 @@ setNonEnumerableReadOnly( EditorActions.prototype, 'clearScreen', function clear * @returns {boolean} boolean indicating whether an editor action was triggered */ setNonEnumerableReadOnly( EditorActions.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { - var isTriggered; - var actionKeys; var inputKeys; + var action; var i; if ( !key ) { return false; } inputKeys = parseKey( key ); - isTriggered = false; - for ( i = 0; i < EDITOR_ACTIONS.length; i++ ) { - actionKeys = this._keybindings[ EDITOR_ACTIONS[ i ] ]; - if ( !actionKeys ) { - continue; // no keybindings configured for the action - } - if ( matchKeybindings( inputKeys, actionKeys ) ) { - this[ EDITOR_ACTIONS[ i ] ](); - isTriggered = true; + for ( i = 0; i < inputKeys.length; i++ ) { + action = this._keybindings.getAction( inputKeys[ i ] ); + if ( action && contains( EDITOR_ACTIONS, action ) ) { + this[ action ](); + return true; } } - return isTriggered; + return false; }); diff --git a/lib/node_modules/@stdlib/repl/lib/keybindings.js b/lib/node_modules/@stdlib/repl/lib/keybindings.js index 317c497b99d1..d6a3c60cac49 100644 --- a/lib/node_modules/@stdlib/repl/lib/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/keybindings.js @@ -1,7 +1,7 @@ /** * @license Apache-2.0 * -* Copyright (c) 2024 The Stdlib Authors. +* Copyright (c) 2025 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. @@ -16,328 +16,154 @@ * limitations under the License. */ -/* eslint-disable max-lines */ +/* eslint-disable no-restricted-syntax, no-invalid-this */ 'use strict'; +// MODULES // + +var objectKeys = require( '@stdlib/utils/keys' ); +var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var format = require( '@stdlib/string/format' ); +var contains = require( '@stdlib/assert/contains' ); +var validateKeybindings = require( './validate_keybindings.js' ); +var ACTIONS = require( './actions.js' ); + + +// VARIABLES // + +var REPLACER = [ 'name', 'ctrl', 'shift', 'meta' ]; + + // MAIN // /** -* Table mapping of REPL actions and their corresponding keybinding triggers. +* Keybindings constructor. * -* @name KEYBINDINGS -* @type {Object} +* @private +* @constructor +* @param {Object} keybindings - keybindings object +* @throws {TypeError} each key must correspond to a single action +* @returns {Keybindings} keybindings instance */ -var KEYBINDINGS = { - 'moveRight': [ - { - 'name': 'right', - 'ctrl': false, - 'shift': false, - 'meta': false - }, - { - 'name': 'f', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'moveLeft': [ - { - 'name': 'left', - 'ctrl': false, - 'shift': false, - 'meta': false - }, - { - 'name': 'b', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'moveWordRight': [ - { - 'name': 'right', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': 'f', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'moveWordLeft': [ - { - 'name': 'left', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': 'b', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'moveBeginning': [ - { - 'name': 'home', - 'ctrl': false, - 'shift': false, - 'meta': false - }, - { - 'name': 'a', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'moveEnd': [ - { - 'name': 'end', - 'ctrl': false, - 'shift': false, - 'meta': false - }, - { - 'name': 'e', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'tab': [ - { - 'name': 't', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'indentLineRight': [ - { - 'name': 'right', - 'ctrl': false, - 'shift': false, - 'meta': true - }, - { - 'name': 'i', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'indentLineLeft': [ - { - 'name': 'left', - 'ctrl': false, - 'shift': false, - 'meta': true - }, - { - 'name': 'i', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'deleteLeft': [ - { - 'name': 'backspace', - 'ctrl': false, - 'shift': false, - 'meta': false - }, - { - 'name': 'h', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'deleteRight': [ - { - 'name': 'delete', - 'ctrl': false, - 'shift': false, - 'meta': false - } - ], - 'deleteWordLeft': [ - { - 'name': 'backspace', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': 'backspace', - 'ctrl': false, - 'shift': false, - 'meta': true - }, - { - 'name': 'w', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'deleteWordRight': [ - { - 'name': 'delete', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': 'delete', - 'ctrl': false, - 'shift': false, - 'meta': true - }, - { - 'name': 'd', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'deleteLineLeft': [ - { - 'name': 'backspace', - 'ctrl': true, - 'shift': true, - 'meta': false - }, - { - 'name': 'u', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'deleteLineRight': [ - { - 'name': 'delete', - 'ctrl': true, - 'shift': true, - 'meta': false - }, - { - 'name': 'k', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'yankKilled': [ - { - 'name': 'y', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'yankPop': [ - { - 'name': 'y', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'undo': [ - { - 'name': '/', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': '-', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'redo': [ - { - 'name': '6', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': '6', - 'ctrl': true, - 'shift': true, - 'meta': false - }, - { - 'name': '^', - 'ctrl': true, - 'shift': false, - 'meta': false - }, - { - 'name': 'y', - 'ctrl': false, - 'shift': true, - 'meta': false - } - ], - 'transposeAboutCursor': [ - { - 'name': 't', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ], - 'uppercaseNextWord': [ - { - 'name': 'u', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'capitalizeNextWord': [ - { - 'name': 'c', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'lowercaseNextWord': [ - { - 'name': 'l', - 'ctrl': false, - 'shift': false, - 'meta': true - } - ], - 'clearScreen': [ - { - 'name': 'l', - 'ctrl': true, - 'shift': false, - 'meta': false - } - ] -}; +function Keybindings( keybindings ) { + var actions; + var action; + var keys; + var key; + var i; + var j; + + if ( !( this instanceof Keybindings ) ) { + return new Keybindings( keybindings ); + } + + // Initialize internal variables to store keybindings: + this._action2Keys = keybindings; + this._key2Action = {}; + + // Create a hash table of keybindings: + actions = objectKeys( keybindings ); + for ( i = 0; i < actions.length; i++ ) { + action = actions[ i ]; + keys = keybindings[ action ]; + for ( j = 0; j < keys.length; j++ ) { + key = JSON.stringify( keys[ j ], REPLACER ); + if ( hasOwnProp( this._key2Action, key ) ) { + throw new TypeError( format( 'invalid argument. Each key in the keybindings argument must correspond to a single action. Value: `%s`', key ) ); + } + this._key2Action[ key ] = action; + } + } + + return this; +} + +/** +* Sets a keybinding. +* +* @private +* @name set +* @memberof Keybindings.prototype +* @type {Function} +* @param {string} action - action name +* @param {Array} keys - list of keys +* @throws {TypeError} first argument must be a valid action name +* @throws {TypeError} second argument must be an array of valid keys corresponding to a single action +* @returns {void} +*/ +setNonEnumerableReadOnly( Keybindings.prototype, 'set', function set( action, keys ) { + var validatedKeys; + var JSONKeys; + var key; + var obj; + var out; + var err; + var i; + + if ( !contains( ACTIONS, action ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); + } + + // Validate provided keys: + obj = {}; + out = {}; + obj[ action ] = keys; + err = validateKeybindings( out, obj ); + if ( err ) { + throw err; + } + + // Check for collisions in existing hash table: + JSONKeys = []; + validatedKeys = out[ action ]; + for ( i = 0; i < validatedKeys.length; i++ ) { + key = JSON.stringify( validatedKeys[ i ], REPLACER ); + if ( hasOwnProp( this._key2Action, key ) ) { + throw new TypeError( format( 'invalid argument. Each key in the keys argument must correspond to a single action. Value: `%s`', key ) ); + } + JSONKeys.push( key ); + } + // Update hash table: + for ( i = 0; i < JSONKeys.length; i++ ) { + this._key2Action[ JSONKeys[ i ] ] = action; + } + this._action2Keys[ action ] = validatedKeys; +}); + +/** +* Gets action name corresponding to the specified key. +* +* @private +* @name getAction +* @memberof Keybindings.prototype +* @type {Function} +* @param {Object} key +* @returns {(string|null)} action name +*/ +setNonEnumerableReadOnly( Keybindings.prototype, 'getAction', function getAction( key ) { + var serializedKey = JSON.stringify( key, REPLACER ); + if ( !hasOwnProp( this._key2Action, serializedKey ) ) { + return null; + } + return this._key2Action[ serializedKey ]; +}); + +/** +* Lists all keybindings. +* +* @private +* @name list +* @memberof Keybindings.prototype +* @type {Function} +* @returns {Object} keybindings object +*/ +setNonEnumerableReadOnly( Keybindings.prototype, 'list', function list() { + return this._action2Keys; +}); // EXPORTS // -module.exports = KEYBINDINGS; +module.exports = Keybindings; diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index 22980c3f9218..a3c124876280 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -49,7 +49,6 @@ var fifo = require( '@stdlib/utils/fifo' ); var nextTick = require( '@stdlib/utils/next-tick' ); var assign = require( '@stdlib/object/assign' ); var validate = require( './validate.js' ); -var validateKeybindings = require( './validate_keybindings.js' ); var parseKey = require( './parse_key.js' ); var defaults = require( './defaults.js' ); var isSettingName = require( './is_setting_name.js' ); @@ -65,6 +64,7 @@ var inputPrompt = require( './input_prompt.js' ); var OutputStream = require( './output_stream.js' ); var completerFactory = require( './completer.js' ); var MultilineHandler = require( './multiline_handler.js' ); +var Keybindings = require( './keybindings.js' ); var EditorActions = require( './editor_actions.js' ); var CompleterEngine = require( './completer_engine.js' ); var PreviewCompleter = require( './completer_preview.js' ); @@ -200,7 +200,6 @@ function REPL( options ) { setNonEnumerableReadOnly( this, '_timeout', opts.timeout ); setNonEnumerableReadOnly( this, '_isTTY', opts.isTTY ); setNonEnumerableReadOnly( this, '_sandbox', opts.sandbox ); - setNonEnumerableReadOnly( this, '_keybindings', opts.keybindings ); setNonEnumerableReadOnly( this, '_settings', opts.settings ); setNonEnumerable( this, '_quiet', opts.quiet ); // allow this to be internally toggled @@ -284,6 +283,9 @@ function REPL( options ) { // Initialize a multi-line handler: setNonEnumerableReadOnly( this, '_multilineHandler', new MultilineHandler( this, this._rli._ttyWrite ) ); + // Initialize keybindings: + setNonEnumerableReadOnly( this, '_keybindings', new Keybindings( opts.keybindings ) ); + // Initialize an editor actions instance: setNonEnumerableReadOnly( this, '_editorActions', new EditorActions( this, this._rli._ttyWrite ) ); @@ -575,8 +577,12 @@ setNonEnumerableReadOnly( REPL.prototype, '_captureKeybinding', function capture return; } parsedKeys = parseKey( key ); - this.setKeybinding( this._targetAction, parsedKeys ); - this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); + try { + this.setKeybinding( this._targetAction, parsedKeys ); + this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); + } catch ( error ) { + this._ostream.write( format( '\n\nError: %s\n', error ) ); + } this._isCapturingKeybinding = false; this._targetAction = ''; displayPrompt( this, false ); @@ -1125,7 +1131,7 @@ setNonEnumerableReadOnly( REPL.prototype, 'deleteTheme', function deleteTheme( n * repl.close(); */ setNonEnumerableReadOnly( REPL.prototype, 'keybindings', function keybindings() { - return this._keybindings; + return this._keybindings.list(); }); /** @@ -1166,18 +1172,10 @@ setNonEnumerableReadOnly( REPL.prototype, 'keybindings', function keybindings() * repl.close(); */ setNonEnumerableReadOnly( REPL.prototype, 'setKeybinding', function setKeybinding( action, keys ) { - var obj; - var err; - - obj = {}; if ( !isString( action ) ) { throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', action ) ); } - obj[ action ] = keys; - err = validateKeybindings( this._keybindings, obj ); - if ( err ) { - throw err; - } + this._keybindings.set( action, keys ); }); /** diff --git a/lib/node_modules/@stdlib/repl/lib/match_keybinding.js b/lib/node_modules/@stdlib/repl/lib/match_keybinding.js deleted file mode 100644 index abe171ad9011..000000000000 --- a/lib/node_modules/@stdlib/repl/lib/match_keybinding.js +++ /dev/null @@ -1,53 +0,0 @@ -/** -* @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. -*/ - -'use strict'; - -// MAIN // - -/** -* Matches a list of possible input keybindings against a list of accepted keybindings. -* -* @private -* @param {Array} inputKeys - list of possible input keys -* @param {Array} acceptedKeys - list of accepted keys -* @returns {boolean} boolean indicating whether the input keys match the accepted keys -*/ -function matchKeybindings( inputKeys, acceptedKeys ) { - var i; - var j; - - for ( i = 0; i < acceptedKeys.length; i++ ) { - for ( j = 0; j < inputKeys.length; j++ ) { - if ( - acceptedKeys[ i ].name === inputKeys[ j ].name && - acceptedKeys[ i ].ctrl === inputKeys[ j ].ctrl && - acceptedKeys[ i ].shift === inputKeys[ j ].shift && - acceptedKeys[ i ].meta === inputKeys[ j ].meta - ) { - return true; - } - } - } - return false; -} - - -// EXPORTS // - -module.exports = matchKeybindings; From e8ea3153085ad594b6bab90bd42b44eff69454f6 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 8 Jan 2025 14:12:25 +0000 Subject: [PATCH 06/17] style: remove space between parens Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/validate_keybindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js index 2979808be8dc..fcb366cffc72 100644 --- a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js @@ -99,7 +99,7 @@ function validate( opts, options ) { if ( err ) { return err; } - out.push( { + out.push({ 'name': o.name, 'ctrl': o.ctrl || false, 'shift': o.shift || false, From 986e2f7a623c7e43fde482fd454dc4656731ef1f Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 8 Jan 2025 17:31:13 +0000 Subject: [PATCH 07/17] fix: log error message correctly Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index a3c124876280..33f6595cbc1f 100644 --- a/lib/node_modules/@stdlib/repl/lib/main.js +++ b/lib/node_modules/@stdlib/repl/lib/main.js @@ -580,8 +580,8 @@ setNonEnumerableReadOnly( REPL.prototype, '_captureKeybinding', function capture try { this.setKeybinding( this._targetAction, parsedKeys ); this._ostream.write( format( '\n\nSuccessfully set the following keybindings for action `%s`.\n\n%s\n', this._targetAction, JSON.stringify( parsedKeys, null, 2 ) ) ); - } catch ( error ) { - this._ostream.write( format( '\n\nError: %s\n', error ) ); + } catch ( err ) { + this._ostream.write( format( '\n\nError: %s\n', err.message ) ); } this._isCapturingKeybinding = false; this._targetAction = ''; From d3d5ec9615297bf10446823e6cc2b35db7f17b86 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Tue, 14 Jan 2025 15:18:39 +0000 Subject: [PATCH 08/17] fix: remove conflicting keybinding Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/default_keybindings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/default_keybindings.js b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js index 794d42c28799..94feca05df2e 100644 --- a/lib/node_modules/@stdlib/repl/lib/default_keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js @@ -287,12 +287,6 @@ var DEFAULT_KEYBINDINGS = { 'ctrl': true, 'shift': false, 'meta': false - }, - { - 'name': 'y', - 'ctrl': false, - 'shift': true, - 'meta': false } ], 'transposeAboutCursor': [ From d1138795b3889315ac694c40557437b297f2a4e8 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:30:48 -0800 Subject: [PATCH 09/17] refactor: explicitly check for primitive Signed-off-by: Athan --- lib/node_modules/@stdlib/repl/lib/commands/keybindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js index e36c3a80cd8d..ec5d557166da 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js @@ -24,7 +24,7 @@ var format = require( '@stdlib/string/format' ); var contains = require( '@stdlib/assert/contains' ); -var isString = require( '@stdlib/assert/is-string' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; var ACTIONS = require( './../actions.js' ); From 7c7f8c313d3021e919ff0293f697ca93a4bda260 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:32:04 -0800 Subject: [PATCH 10/17] refactor: use partial application Signed-off-by: Athan --- .../@stdlib/repl/lib/commands/keybindings.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js index ec5d557166da..e54c39921d3f 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js @@ -23,11 +23,16 @@ // MODULES // var format = require( '@stdlib/string/format' ); -var contains = require( '@stdlib/assert/contains' ); +var contains = require( '@stdlib/array/base/assert/contains' ).factory; var isString = require( '@stdlib/assert/is-string' ).isPrimitive; var ACTIONS = require( './../actions.js' ); +// VARIABLES // + +var isAction = contains( ACTIONS ); + + // MAIN // /** @@ -61,7 +66,7 @@ function command( repl ) { repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.', action ) ); return; } - if ( !contains( ACTIONS, action ) ) { + if ( !isAction( action ) ) { repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); return; } From ca9545ee962aef95ee391d97b0d3058bb3c7804b Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:33:55 -0800 Subject: [PATCH 11/17] docs: update description Signed-off-by: Athan --- lib/node_modules/@stdlib/repl/lib/commands/keybindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js index e54c39921d3f..628d2126b959 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/keybindings.js @@ -50,7 +50,7 @@ function command( repl ) { * * @private * @param {string} [action] - action name - * @returns {(Object|Array)} keybindings object or a list of keybindings for an action + * @returns {(Object|Array)} keybindings object or a list of keybindings */ function onCommand() { var action; From e9c1b6571136af83db6722c219d802bf8816ebb9 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:35:48 -0800 Subject: [PATCH 12/17] refactor: use partial application Signed-off-by: Athan --- .../@stdlib/repl/lib/commands/set_keybinding.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js index c9912d35c713..fc3f14cb9953 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js @@ -23,11 +23,16 @@ // MODULES // var format = require( '@stdlib/string/format' ); -var contains = require( '@stdlib/assert/contains' ); -var isString = require( '@stdlib/assert/is-string' ); +var contains = require( '@stdlib/array/base/assert/contains' ).factory; +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; var ACTIONS = require( './../actions.js' ); +// VARIABLES // + +var isAction = contains( ACTIONS ); + + // MAIN // /** @@ -64,7 +69,7 @@ function command( repl ) { repl._ostream.write( format( 'Error: invalid argument. First argument must be a string. Value: `%s`.\n', action ) ); return; } - if ( !contains( ACTIONS, action ) ) { + if ( !isAction( action ) ) { repl._ostream.write( format( 'Error: invalid argument. First argument must be a valid action name. Value: `%s`.\n', action ) ); return; } From 5ad5d51942696ecc88524aae0f8e5d4b04ec5673 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:37:05 -0800 Subject: [PATCH 13/17] docs: update description Signed-off-by: Athan --- lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js index fc3f14cb9953..6a73f0a9712f 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js +++ b/lib/node_modules/@stdlib/repl/lib/commands/set_keybinding.js @@ -46,7 +46,7 @@ function command( repl ) { return onCommand; /** - * Allows the user to set a keybinding. + * Sets a keybinding. * * @private * @param {string} action - action name From 97ce1af5452d2a9c3b4de00a034af6afc4c9e64f Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:37:52 -0800 Subject: [PATCH 14/17] docs: add annotation Signed-off-by: Athan --- lib/node_modules/@stdlib/repl/lib/default_keybindings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/node_modules/@stdlib/repl/lib/default_keybindings.js b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js index 94feca05df2e..4e5ee984a4d4 100644 --- a/lib/node_modules/@stdlib/repl/lib/default_keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/default_keybindings.js @@ -25,6 +25,7 @@ /** * Table mapping of REPL actions and their corresponding keybinding triggers. * +* @private * @name DEFAULT_KEYBINDINGS * @type {Object} */ From 235ebe8d5835727722b8c0ffac6358c20b6009b4 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 14 Jan 2025 15:48:47 -0800 Subject: [PATCH 15/17] refactor: check for primitives Signed-off-by: Athan --- lib/node_modules/@stdlib/repl/lib/validate_keybindings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js index fcb366cffc72..028db55c72ef 100644 --- a/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/validate_keybindings.js @@ -22,8 +22,8 @@ var isPlainObject = require( '@stdlib/assert/is-plain-object' ); var isArray = require( '@stdlib/assert/is-array' ); -var isString = require( '@stdlib/assert/is-string' ); -var isBoolean = require( '@stdlib/assert/is-boolean' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var format = require( '@stdlib/string/format' ); var ACTIONS = require( './actions.js' ); @@ -69,6 +69,7 @@ function validateKey( obj ) { /** * Validates keybindings. * +* @private * @param {Object} opts - destination object * @param {Object} options - settings options * @returns {(Error|null)} error or null From 70a0bf8921f46568d026be9b5bfaf1a710fc2ca3 Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 15 Jan 2025 11:31:13 +0000 Subject: [PATCH 16/17] fix: truncate help text Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/help_text.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/help_text.js b/lib/node_modules/@stdlib/repl/lib/help_text.js index 6bf39b8bdd32..56a16d5245e2 100644 --- a/lib/node_modules/@stdlib/repl/lib/help_text.js +++ b/lib/node_modules/@stdlib/repl/lib/help_text.js @@ -20,6 +20,7 @@ // MAIN // +// NOTE: Width shouldn't exceed 80 characters. var MSG = [ '', ' help() Print help text.', @@ -57,7 +58,8 @@ var MSG = [ ' addTheme(name,value) Add a new color theme.', ' deleteTheme(name) Delete a specified color theme.', ' renameTheme(old,new) Rename a specified color theme.', - ' getTheme([name]) Return the current (or a specified) theme\'s color palette.', + ' getTheme([name]) Return the current (or a specified) theme\'s', + ' color palette.', '', ' keybindings([name]) List all (or select) keybindings.', ' setKeybinding(name,[keys]) Set a keybinding.', @@ -65,14 +67,16 @@ var MSG = [ ' assignin() Assign a value to a workspace variable.', ' assignfrom() Read in a value from another workspace.', '', - ' evalin() Evaluate an expression in a specified workspace.', + ' evalin() Evaluate an expression in a specified', + ' workspace.', '', ' rerun() Rerun previous commands.', '', ' require() Import a module, JSON, or local file.', ' rerequire() Re-import a module, JSON, or local file.', ' deeprerequire() Re-import a module and its dependencies.', - ' load() Load and evaluate a JavaScript file line-by-line.', + ' load() Load and evaluate a JavaScript file', + ' line-by-line.', '', ' save() Save previous commands to a specified file.', ' saveStart() Start saving commands to a specified file.', @@ -86,7 +90,8 @@ var MSG = [ '', ' clearHistory() Clear the REPL history.', '', - ' clear() Clear the entire REPL screen and scrollback history.', + ' clear() Clear the entire REPL screen and scrollback', + ' history.', ' reset() Reset the REPL.', ' quit() Exit the REPL.', '', @@ -95,7 +100,8 @@ var MSG = [ ' pkg2alias() Convert a stdlib package name to an alias.', ' alias2related() List related aliases.', ' pkg2related() List related package names.', - ' isKeyword() Assert whether a value is a reserved keyword/alias.', + ' isKeyword() Assert whether a value is a reserved', + ' keyword/alias.', '', ' citation() Print how to cite stdlib in publications.', ' license() Print license information.', From c6287e22523628ab16f7d74ba5a6a87cd6d026ab Mon Sep 17 00:00:00 2001 From: Snehil Shah Date: Wed, 15 Jan 2025 11:44:34 +0000 Subject: [PATCH 17/17] refactor: use partial application Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/lib/editor_actions.js | 5 +++-- lib/node_modules/@stdlib/repl/lib/keybindings.js | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/repl/lib/editor_actions.js b/lib/node_modules/@stdlib/repl/lib/editor_actions.js index ae159163e50f..e8803da827d7 100644 --- a/lib/node_modules/@stdlib/repl/lib/editor_actions.js +++ b/lib/node_modules/@stdlib/repl/lib/editor_actions.js @@ -25,7 +25,7 @@ var readline = require( 'readline' ); var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var ltrimN = require( '@stdlib/string/left-trim-n' ); -var contains = require( '@stdlib/assert/contains' ); +var contains = require( '@stdlib/array/base/assert/contains' ).factory; var uppercase = require( '@stdlib/string/base/uppercase' ); var lowercase = require( '@stdlib/string/base/lowercase' ); var capitalize = require( '@stdlib/string/base/capitalize' ); @@ -61,6 +61,7 @@ var EDITOR_ACTIONS = [ 'lowercaseNextWord', 'clearScreen' ]; +var isEditorAction = contains( EDITOR_ACTIONS ); // MAIN // @@ -533,7 +534,7 @@ setNonEnumerableReadOnly( EditorActions.prototype, 'beforeKeypress', function be inputKeys = parseKey( key ); for ( i = 0; i < inputKeys.length; i++ ) { action = this._keybindings.getAction( inputKeys[ i ] ); - if ( action && contains( EDITOR_ACTIONS, action ) ) { + if ( action && isEditorAction( action ) ) { this[ action ](); return true; } diff --git a/lib/node_modules/@stdlib/repl/lib/keybindings.js b/lib/node_modules/@stdlib/repl/lib/keybindings.js index d6a3c60cac49..5dacbad65adf 100644 --- a/lib/node_modules/@stdlib/repl/lib/keybindings.js +++ b/lib/node_modules/@stdlib/repl/lib/keybindings.js @@ -26,7 +26,8 @@ var objectKeys = require( '@stdlib/utils/keys' ); var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var format = require( '@stdlib/string/format' ); -var contains = require( '@stdlib/assert/contains' ); +var deepCopy = require( '@stdlib/utils/copy' ); +var contains = require( '@stdlib/array/base/assert/contains' ).factory; var validateKeybindings = require( './validate_keybindings.js' ); var ACTIONS = require( './actions.js' ); @@ -34,6 +35,7 @@ var ACTIONS = require( './actions.js' ); // VARIABLES // var REPLACER = [ 'name', 'ctrl', 'shift', 'meta' ]; +var isAction = contains( ACTIONS ); // MAIN // @@ -102,7 +104,7 @@ setNonEnumerableReadOnly( Keybindings.prototype, 'set', function set( action, ke var err; var i; - if ( !contains( ACTIONS, action ) ) { + if ( !isAction( action ) ) { throw new TypeError( format( 'invalid argument. First argument must be a valid action name. Value: `%s`.', action ) ); } @@ -160,7 +162,7 @@ setNonEnumerableReadOnly( Keybindings.prototype, 'getAction', function getAction * @returns {Object} keybindings object */ setNonEnumerableReadOnly( Keybindings.prototype, 'list', function list() { - return this._action2Keys; + return deepCopy( this._action2Keys ); });