From 83142377ae560b0b7b741437d117ea4aa6a0bd6d Mon Sep 17 00:00:00 2001 From: Snehil Shah <130062020+Snehil-Shah@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:06:12 +0530 Subject: [PATCH] feat: add support for auto-closing brackets/quotations in the REPL PR-URL: https://github.com/stdlib-js/stdlib/pull/1680 Closes: https://github.com/stdlib-js/stdlib/issues/1672 Co-authored-by: Athan Reines Reviewed-by: Athan Reines Signed-off-by: Snehil Shah <130062020+Snehil-Shah@users.noreply.github.com> Signed-off-by: Snehil Shah --- lib/node_modules/@stdlib/repl/README.md | 98 ++++- .../@stdlib/repl/lib/auto_close_pairs.js | 322 ++++++++++++++ .../lib/auto_close_pairs_close_symbols.js | 42 ++ .../repl/lib/auto_close_pairs_open_symbols.js | 42 ++ .../@stdlib/repl/lib/auto_close_pairs_walk.js | 401 ++++++++++++++++++ lib/node_modules/@stdlib/repl/lib/commands.js | 2 + .../@stdlib/repl/lib/commands/settings.js | 119 ++++++ .../@stdlib/repl/lib/complete_settings.js | 91 ++++ .../repl/lib/complete_walk_find_last.js | 306 ++++++------- .../@stdlib/repl/lib/completer.js | 24 ++ .../@stdlib/repl/lib/completer_preview.js | 16 +- lib/node_modules/@stdlib/repl/lib/defaults.js | 39 +- .../@stdlib/repl/lib/help_text.js | 4 + .../@stdlib/repl/lib/is_setting_name.js | 47 ++ lib/node_modules/@stdlib/repl/lib/main.js | 166 ++++++-- .../@stdlib/repl/lib/process_line.js | 6 +- .../lib/regexp_reserved_syntax_characters.js | 42 ++ .../@stdlib/repl/lib/regexp_settings.js | 106 +++++ lib/node_modules/@stdlib/repl/lib/settings.js | 48 +++ .../@stdlib/repl/lib/settings_alias_args.js | 48 +++ .../@stdlib/repl/lib/settings_aliases.js | 46 ++ .../@stdlib/repl/lib/settings_names.js | 41 ++ .../@stdlib/repl/lib/settings_validators.js | 112 +++++ lib/node_modules/@stdlib/repl/lib/validate.js | 10 +- .../@stdlib/repl/lib/validate_settings.js | 78 ++++ .../array_expression_right_bracket.json | 23 + .../object_expression_right_brace.json | 18 + .../negative/right_backtick.json | 35 ++ .../negative/right_double_quote.json | 25 ++ .../negative/right_parenthesis.json | 17 + .../negative/right_single_quote.json | 25 ++ .../negative/template_literal.json | 35 ++ .../template_literal_with_expression.json | 52 +++ .../positive/arrow_function_body.json | 33 ++ ...binary_left_member_expression_literal.json | 37 ++ .../positive/call_expression.json | 30 ++ ...pression_string_argument_single_quote.json | 38 ++ ...ssion_template_argument_left_backtick.json | 66 +++ .../positive/do_while_body.json | 29 ++ .../positive/do_while_test.json | 29 ++ .../positive/do_while_test_left_bracket.json | 42 ++ .../auto-close-pairs/positive/for_body.json | 74 ++++ .../auto-close-pairs/positive/for_init.json | 30 ++ .../auto-close-pairs/positive/for_test.json | 73 ++++ .../positive/for_test_member_expression.json | 74 ++++ .../auto-close-pairs/positive/for_update.json | 79 ++++ .../for_update_member_expression.json | 80 ++++ .../positive/function_declaration_body.json | 33 ++ .../function_declaration_signature.json | 33 ++ .../positive/left_backtick.json | 51 +++ .../auto-close-pairs/positive/left_brace.json | 18 + .../positive/left_bracket.json | 23 + .../positive/left_double_quote.json | 25 ++ .../positive/left_parenthesis.json | 17 + .../positive/left_single_quote.json | 25 ++ ...ogical_left_member_expression_literal.json | 37 ++ .../logical_with_left_member_expression.json | 54 +++ ...al_with_left_nested_member_expression.json | 87 ++++ .../logical_with_right_member_expression.json | 54 +++ .../member_assignment_expression.json | 49 +++ .../member_assignment_expression_literal.json | 37 ++ .../positive/member_expression.json | 37 ++ .../member_expression_call_expression.json | 43 ++ .../object_expression_left_single_quote.json | 59 +++ .../positive/regexp_body_single_quote.json | 42 ++ .../positive/regexp_literal_left_bracket.json | 36 ++ .../positive/regexp_single_quote.json | 40 ++ .../positive/tagged_template.json | 62 +++ .../tagged_template_left_backtick.json | 91 ++++ ...te_literal_expression_with_left_brace.json | 105 +++++ ..._literal_expression_with_left_bracket.json | 101 +++++ ...ral_expression_with_left_double_quote.json | 67 +++ ...eral_expression_with_left_parenthesis.json | 94 ++++ .../positive/three_backticks.json | 74 ++++ .../positive/variable_declaration_string.json | 38 ++ .../auto-close-pairs/positive/while_body.json | 30 ++ .../auto-close-pairs/positive/while_test.json | 28 ++ .../fixtures/auto-close-pairs/script.js | 63 +++ ...expression_left_bracket_within_string.json | 25 ++ .../negative/left_backtick_within_string.json | 25 ++ .../left_double_quote_within_string.json | 25 ++ .../left_parenthesis_within_string.json | 25 ++ .../left_single_quote_within_string.json | 25 ++ ...t_expression_left_brace_within_string.json | 25 ++ .../array_expression_left_bracket.json | 23 + .../positive/left_backtick.json | 35 ++ .../positive/left_double_quote.json | 25 ++ .../positive/left_parenthesis.json | 17 + .../positive/left_single_quote.json | 25 ++ .../object_expression_left_brace.json | 18 + .../fixtures/auto-delete-pairs/script.js | 63 +++ .../test/{ => integration}/fixtures/repl.js | 2 +- .../test/integration/test.auto_close_pairs.js | 361 ++++++++++++++++ .../integration/test.auto_delete_pairs.js | 349 +++++++++++++++ .../integration/test.completion_previews.js | 2 +- 95 files changed, 5625 insertions(+), 198 deletions(-) create mode 100644 lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js create mode 100644 lib/node_modules/@stdlib/repl/lib/auto_close_pairs_close_symbols.js create mode 100644 lib/node_modules/@stdlib/repl/lib/auto_close_pairs_open_symbols.js create mode 100644 lib/node_modules/@stdlib/repl/lib/auto_close_pairs_walk.js create mode 100644 lib/node_modules/@stdlib/repl/lib/commands/settings.js create mode 100644 lib/node_modules/@stdlib/repl/lib/complete_settings.js create mode 100644 lib/node_modules/@stdlib/repl/lib/is_setting_name.js create mode 100644 lib/node_modules/@stdlib/repl/lib/regexp_reserved_syntax_characters.js create mode 100644 lib/node_modules/@stdlib/repl/lib/regexp_settings.js create mode 100644 lib/node_modules/@stdlib/repl/lib/settings.js create mode 100644 lib/node_modules/@stdlib/repl/lib/settings_alias_args.js create mode 100644 lib/node_modules/@stdlib/repl/lib/settings_aliases.js create mode 100644 lib/node_modules/@stdlib/repl/lib/settings_names.js create mode 100644 lib/node_modules/@stdlib/repl/lib/settings_validators.js create mode 100644 lib/node_modules/@stdlib/repl/lib/validate_settings.js create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/array_expression_right_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/object_expression_right_brace.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_backtick.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_double_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_parenthesis.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal_with_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/arrow_function_body.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/binary_left_member_expression_literal.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_string_argument_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_template_argument_left_backtick.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_body.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test_left_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_body.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_init.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test_member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update_member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_body.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_signature.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_backtick.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_brace.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_double_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_parenthesis.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_left_member_expression_literal.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_nested_member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_right_member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression_literal.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression_call_expression.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/object_expression_left_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_body_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_literal_left_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template_left_backtick.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_brace.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_double_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_parenthesis.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/three_backticks.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/variable_declaration_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_body.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_test.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/script.js create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/array_expression_left_bracket_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_backtick_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_double_quote_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_parenthesis_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_single_quote_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/object_expression_left_brace_within_string.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/array_expression_left_bracket.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_backtick.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_double_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_parenthesis.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_single_quote.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/object_expression_left_brace.json create mode 100644 lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/script.js rename lib/node_modules/@stdlib/repl/test/{ => integration}/fixtures/repl.js (98%) create mode 100644 lib/node_modules/@stdlib/repl/test/integration/test.auto_close_pairs.js create mode 100644 lib/node_modules/@stdlib/repl/test/integration/test.auto_delete_pairs.js diff --git a/lib/node_modules/@stdlib/repl/README.md b/lib/node_modules/@stdlib/repl/README.md index 78c9846b312..0d52ba9a4b0 100644 --- a/lib/node_modules/@stdlib/repl/README.md +++ b/lib/node_modules/@stdlib/repl/README.md @@ -64,9 +64,9 @@ The function accepts the following `options`: - **input**: input (readable) stream. Default: [`stdin`][@stdlib/streams/node/stdin]. - **output**: output (writable) stream. Default: [`stdout`][@stdlib/streams/node/stdout]. -- **sandbox**: `boolean` indicating whether to run a REPL in a sandboxed context. Default: `false`. +- **sandbox**: boolean indicating whether to run a REPL in a sandboxed context. Default: `false`. - **timeout**: number of milliseconds to execute a command before terminating execution. Default: `4294967295`. -- **isTTY**: `boolean` indicating whether the input and output streams should be treated like a TTY (terminal) and whether the REPL should use ANSI/VT100 escape codes when writing to the output stream. +- **isTTY**: boolean indicating whether the input and output streams should be treated like a TTY (terminal) and whether the REPL should use ANSI/VT100 escape codes when writing to the output stream. - **inputPrompt**: input prompt. If the input prompt includes the character sequence `%d`, the input prompt includes line numbers. Default: `'In [%d]: '`. - **outputPrompt**: output prompt. If the output prompt includes the character sequence `%d`, the output prompt includes line numbers. Default: `'Out[%d]: '`. - **welcome**: welcome message. @@ -74,7 +74,14 @@ The function accepts the following `options`: - **load**: file path specifying a JavaScript file to load and evaluate line-by-line (e.g., a previous REPL history file). - **save**: file path specifying where to save REPL command history. - **log**: file path specifying where to save REPL commands and printed output. -- **quiet**: `boolean` indicating whether log information, confirmation messages, and other possible REPL diagnostics should be silenced. Default: `false`. +- **quiet**: boolean indicating whether log information, confirmation messages, and other possible REPL diagnostics should be silenced. Default: `false`. +- **settings**: object specifying REPL settings. + +The function supports specifying the following settings: + +- **autoClosePairs**: boolean indicating whether to automatically insert matching brackets, parentheses, and quotes. Default: `true`. +- **autoDeletePairs**: boolean indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes. Default: `true`. +- **completionPreviews**: boolean indicating whether to display completion previews for auto-completion. When streams are TTY, the default is `true`; otherwise, the default is `false`. #### REPL.prototype.createContext() @@ -289,6 +296,71 @@ repl.clearCommand(); repl.close(); ``` +#### REPL.prototype.settings( \[name\[, value]] ) + +Returns REPL settings. + +```javascript +var debug = require( '@stdlib/streams/node/debug-sink' ); + +// Create a new REPL: +var repl = new REPL({ + 'output': debug() +}); + +// ... + +// Retrieve REPL settings: +var o = repl.settings(); + +// ... + +// Close the REPL: +repl.close(); +``` + +To retrieve the current value for a specific setting, provide a `name` argument. + +```javascript +var debug = require( '@stdlib/streams/node/debug-sink' ); + +// Create a new REPL: +var repl = new REPL({ + 'output': debug() +}); + +// ... + +// Retrieve current setting value: +var v = repl.settings( 'autoClosePairs' ); + +// ... + +// Close the REPL: +repl.close(); +``` + +To update a specific setting, provide a `value` argument. + +```javascript +var debug = require( '@stdlib/streams/node/debug-sink' ); + +// Create a new REPL: +var repl = new REPL({ + 'output': debug() +}); + +// ... + +// Update setting value: +repl.settings( 'autoClosePairs', false ); + +// ... + +// Close the REPL: +repl.close(); +``` + #### REPL.prototype.close() Closes a REPL. @@ -966,6 +1038,26 @@ Stops saving commands to a file path associated with a specified record identifi // TODO ``` +#### settings( \[name\[, value]] ) + +Displays REPL settings. + +```text +In [1]: settings() +``` + +To retrieve the current value for a specific setting, provide a `name` argument. + +```text +In [1]: settings( 'autoClosePairs' ) +``` + +To update a specific setting, provide a `value` argument. + +```text +In [1]: settings( 'autoClosePairs', false ) +``` + #### tutorial( \[name, \[options]] ) Starts a tutorial. diff --git a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js new file mode 100644 index 00000000000..a4bf667e51d --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js @@ -0,0 +1,322 @@ +/** +* @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-restricted-syntax, no-invalid-this */ + +'use strict'; + +// MODULES // + +var logger = require( 'debug' ); +var parse = require( 'acorn-loose' ).parse; +var findNodeAround = require( 'acorn-walk' ).findNodeAround; +var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var reservedCharsRegExp = require( './regexp_reserved_syntax_characters.js' ); +var walk = require( './auto_close_pairs_walk.js' ); +var OPEN_SYMBOLS = require( './auto_close_pairs_open_symbols.js' ); +var CLOSE_SYMBOLS = require( './auto_close_pairs_close_symbols.js' ); + + +// VARIABLES // + +var debug = logger( 'repl:auto_close_pairs' ); +var AOPTS = { + 'ecmaVersion': 'latest' +}; + + +// FUNCTIONS // + +/** +* Tests whether a character is a quote character. +* +* @private +* @param {string} ch - input character +* @returns {boolean} boolean indicating whether a character is a quote character +*/ +function isQuote( ch ) { + return ( ch === '\'' || ch === '"' ); +} + + +// MAIN // + +/** +* Constructor for creating an auto-closer. +* +* @private +* @constructor +* @param {Object} rli - readline instance +* @returns {AutoCloser} auto-closer instance +*/ +function AutoCloser( rli ) { + if ( !(this instanceof AutoCloser) ) { + return new AutoCloser( rli ); + } + debug( 'Creating an auto-closer...' ); + this._rli = rli; + this._ignoreBackspace = false; + return this; +} + +/** +* Processes an opening symbol when performing auto-close. +* +* @private +* @name _autocloseOpenSymbol +* @memberof AutoCloser.prototype +* @param {string} line - current line content +* @param {NonNegativeInteger} cursor - cursor position +* @param {string} data - input data +* @returns {boolean} boolean indicating whether line content was updated +*/ +setNonEnumerableReadOnly( AutoCloser.prototype, '_autocloseOpenSymbol', function _autocloseOpenSymbol( line, cursor, data ) { + var ast; + var out; + var ch; + + debug( 'Checking for an opening symbol...' ); + ch = OPEN_SYMBOLS[ data ]; + if ( !isString( ch ) ) { + debug( 'Failed to detect an opening symbol.' ); + return false; + } + debug( 'Detected an opening symbol.' ); + + // Avoid auto-closing when the input data is already present, and further account for `foo`` being valid syntax (i.e., using untagged template literals as tags); hence, we need to guard against unnecessarily inserting a closing symbol when a user types `foo<|>` and then instinctively types ` in order to close a template literal... + if ( data === line[ cursor ] ) { + debug( 'Failed to detect an auto-close candidate. Closing symbol is already present.' ); + return false; + } + // Generate an AST for the current line: + debug( 'Generating an AST...' ); + ast = parse( line, AOPTS ); + + // Attempt to walk the AST to determine whether to auto-close... + try { + debug( 'Determining whether to auto-close...' ); + out = walk( ast, cursor ); + } catch ( err ) { + // If parsing failed, stay conservative and don't auto-close: + debug( 'Error: %s', err.message ); + return false; + } + // If we failed to conclusively determine whether to auto-close, stay conservative and don't auto-close... + if ( !out ) { + debug( 'Failed to detect an auto-close candidate. AST parsing did not conclusively support auto-closing.' ); + return false; + } + // If the auto-close candidate is a quote character, avoid auto-closing when the opening character is inserted immediately before characters which could reasonably be considered string characters... + if ( isQuote( ch ) && line[ cursor+1 ] && !reservedCharsRegExp().test( line[ cursor+1 ] ) ) { // eslint-disable-line max-len + debug( 'Failed to detect an auto-close candidate. Detected a quote character immediately preceding potential character belonging to a string literal.' ); + return false; + } + debug( 'Successfully detected an auto-close candidate. Inserting closing symbol...' ); + this._rli.write( ch ); + + // Move back one character: + this._rli.write( null, { + 'ctrl': true, + 'name': 'b' + }); + debug( 'Resulting expression: %s', this._rli.line ); + return true; +}); + +/** +* Processes a closing symbol when performing auto-close. +* +* @private +* @name _autocloseCloseSymbol +* @memberof AutoCloser.prototype +* @param {string} line - current line content +* @param {NonNegativeInteger} cursor - cursor position +* @param {string} data - input data +* @returns {boolean} boolean indicating whether line content was updated +*/ +setNonEnumerableReadOnly( AutoCloser.prototype, '_autocloseCloseSymbol', function _autocloseCloseSymbol( line, cursor, data ) { + var ch; + + debug( 'Checking for a closing symbol...' ); + ch = CLOSE_SYMBOLS[ data ]; + if ( !isString( ch ) ) { + debug( 'Failed to detect a closing symbol.' ); + return false; + } + debug( 'Detected a closing symbol.' ); + + // Support users who may instinctively add a closing symbol by skipping over the closing symbol character, and thus avoid inserting an unwanted duplicate character... + debug( 'Determining whether a closing symbol already exists...' ); + if ( data !== line[ cursor ] ) { + // Did not detect a closing symbol to skip over... + debug( 'Did not find an existing closing symbol.' ); + return false; + } + // Set an internal flag to avoid having auto-deletion logic consider this backspace to be user input: + this._ignoreBackspace = true; + + debug( 'Closing symbol already exists. Removing duplicate symbol...' ); + this._rli.write( null, { + 'name': 'backspace' + }); + + // Move to the right one character: + this._rli.write( null, { + 'name': 'right' + }); + debug( 'Resulting expression: %s', this._rli.line ); + return true; +}); + +/** +* Processes an opening symbol when performing auto-delete. +* +* @private +* @name _autocloseOpenSymbol +* @memberof AutoCloser.prototype +* @param {string} line - current line content +* @param {NonNegativeInteger} cursor - cursor position +* @param {string} data - character to delete +* @returns {boolean} boolean indicating whether line content was updated +*/ +setNonEnumerableReadOnly( AutoCloser.prototype, '_autodeleteOpenSymbol', function _autodeleteOpenSymbol( line, cursor, data ) { + var node; + var ast; + var ch; + + debug( 'Checking for an opening symbol...' ); + ch = OPEN_SYMBOLS[ data ]; + if ( !isString( ch ) ) { + debug( 'Failed to detect an opening symbol.' ); + return false; + } + debug( 'Detected an opening symbol.' ); + + debug( 'Checking if immediately followed by a corresponding closing symbol...' ); + if ( ch !== line[ cursor ] ) { + debug( 'Failed to detect an auto-delete candidate. Opening symbol is not followed by a corresponding closing symbol.' ); + return false; + } + debug( 'Detected a closing symbol.' ); + + debug( 'Generating an AST...' ); + ast = parse( line, AOPTS ); + + debug( 'Checking whether characters are within a string literal...' ); + node = findNodeAround( ast, cursor-1, 'Literal' ); + if ( node && node.node.start < cursor-1 ) { + debug( 'Failed to detect an auto-delete candidate. Characters are within a string literal.' ); + return false; + } + debug( 'Characters are not within a string literal.' ); + debug( 'Successfully detected an auto-delete candidate. Deleting symbols...' ); + this._rli.write( null, { + 'name': 'right' + }); + + // Set an internal flag to avoid having auto-deletion logic consider this backspace to be user input: + this._ignoreBackspace = true; + this._rli.write( null, { + 'name': 'backspace' + }); + debug( 'Resulting expression: %s', this._rli.line ); + return true; +}); + +/** +* Callback which should be invoked **before** a "keypress" event is processed by a readline interface. +* +* @name beforeKeypress +* @memberof AutoCloser.prototype +* @param {string} data - input data +* @param {(Object|void)} key - key object +* @returns {boolean} boolean indicating whether line content was updated +*/ +setNonEnumerableReadOnly( AutoCloser.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { + var status; + var cursor; + var line; + + if ( !key || key.name !== 'backspace' ) { + return false; + } + if ( this._ignoreBackspace ) { + // Reset the backspace flag to re-enable auto-deletion behavior: + this._ignoreBackspace = false; + return false; + } + cursor = this._rli.cursor; + line = this._rli.line; + + debug( 'Expression: %s', line ); + debug( 'Cursor position: %d', cursor ); + + data = line[ cursor-1 ]; + debug( 'Character to delete: %s', data ); + + debug( 'Performing auto-delete...' ); + status = this._autodeleteOpenSymbol( line, cursor, data ); + if ( status ) { + debug( 'Finished performing auto-delete.' ); + return true; + } + debug( 'Finished performing auto-delete.' ); + return false; +}); + +/** +* Callback for handling a "keypress" event. +* +* @name onKeypress +* @memberof AutoCloser.prototype +* @param {string} data - input data +* @param {(Object|void)} key - key object +* @returns {boolean} boolean indicating whether line content was updated +*/ +setNonEnumerableReadOnly( AutoCloser.prototype, 'onKeypress', function onKeypress( data ) { + var status; + var cursor; + var line; + + cursor = this._rli.cursor; + line = this._rli.line; + + debug( 'Expression: %s', line ); + debug( 'Cursor position: %d', cursor ); + debug( 'Character to insert: %s', data ); + + debug( 'Performing auto-close...' ); + status = this._autocloseOpenSymbol( line, cursor, data ); + if ( status ) { + debug( 'Finished performing auto-close.' ); + return true; + } + status = this._autocloseCloseSymbol( line, cursor, data ); + if ( status ) { + debug( 'Finished performing auto-close.' ); + return true; + } + debug( 'Finished performing auto-close.' ); + return false; +}); + + +// EXPORTS // + +module.exports = AutoCloser; diff --git a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_close_symbols.js b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_close_symbols.js new file mode 100644 index 00000000000..d8a3c81951e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_close_symbols.js @@ -0,0 +1,42 @@ +/** +* @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 // + +/** +* Table mapping closing symbols with their opening counterparts. +* +* @private +* @name CLOSE_SYMBOLS +* @type {Object} +*/ +var CLOSE_SYMBOLS = { + '}': '{', + ']': '[', + ')': '(', + '\'': '\'', + '"': '"', + '`': '`' +}; + + +// EXPORTS // + +module.exports = CLOSE_SYMBOLS; diff --git a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_open_symbols.js b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_open_symbols.js new file mode 100644 index 00000000000..96cbd69e22e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_open_symbols.js @@ -0,0 +1,42 @@ +/** +* @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 // + +/** +* Table mapping opening symbols with their closing counterparts. +* +* @private +* @name OPEN_SYMBOLS +* @type {Object} +*/ +var OPEN_SYMBOLS = { + '{': '}', + '[': ']', + '(': ')', + '\'': '\'', + '"': '"', + '`': '`' +}; + + +// EXPORTS // + +module.exports = OPEN_SYMBOLS; diff --git a/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_walk.js b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_walk.js new file mode 100644 index 00000000000..8fe1cec7cb3 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/auto_close_pairs_walk.js @@ -0,0 +1,401 @@ +/** +* @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'; + +// MODULES // + +var logger = require( 'debug' ); + + +// VARIABLES // + +var debug = logger( 'repl:auto_close_pairs:walk' ); + + +// FUNCTIONS // + +/** +* Returns the AST node associated with a provided cursor position. +* +* @private +* @param {Array} nodes - list of AST nodes +* @param {NonNegativeInteger} cursor - cursor position +* @returns {(Node|null)} associated AST node or null +*/ +function findNode( nodes, cursor ) { + var node; + var i; + for ( i = 0; i < nodes.length; i++ ) { + node = nodes[ i ]; + if ( node.start <= cursor && cursor <= node.end ) { + return node; + } + } + return null; +} + + +// MAIN // + +/** +* Walks an abstract syntax tree (AST) to determine whether to auto-close. +* +* @private +* @param {Node} ast - AST to walk +* @param {NonNegativeInteger} cursor - cursor position +* @returns {boolean} boolean indicating whether to auto-close +*/ +function walk( ast, cursor ) { // eslint-disable-line max-lines-per-function + var node; + var tmp; + + debug( 'Searching for AST node associated with current cursor position...' ); + node = findNode( ast.body, cursor ); + + debug( 'Walking the AST...' ); + while ( node && ( node.type !== 'Identifier' || node.start === node.end ) ) { + debug( 'Node type: %s', node.type ); + switch ( node.type ) { + case 'ArrayExpression': + // `[<|>` + if ( node.elements.length === 0 ) { + debug( 'Detected the start of an array expression.' ); + return true; + } + // `['<|>` || `[ 1, '<|>` || `[ ['<|>, 1, 2, 3 ]` || etc + node = findNode( node.elements, cursor ); + if ( node === null ) { + // `${foo([<|>)}` + return true; + } + break; + case 'ArrowFunctionExpression': + // `() => [<|>` || `() => { return '<|>` + node = node.body; + break; + case 'AssignmentExpression': + // `x[ = 5` + if ( node.left.type === 'Identifier' && node.left.start === node.left.end ) { + node = node.left; + break; + } + // `x = [<|>` || `x = foo['<|>` + node = node.right; + break; + case 'BinaryExpression': + // `x[ | 5` + if ( node.left.type === 'Identifier' && node.left.start === node.left.end ) { + node = node.left; + break; + } + // `x | foo[<|>` + node = node.right; + break; + case 'BlockStatement': + if ( node.body.length === 0 ) { + debug( 'Detected the start of a block statement.' ); + return true; + } + node = findNode( node.body, cursor ); + break; + case 'CallExpression': + // `foo(<|>` + if ( node.arguments.length === 0 ) { + debug( 'Detected the start of a call expression.' ); + return true; + } + // `foo( bar, x['<|>` + node = findNode( node.arguments, cursor ); + if ( node === null ) { + // `${foo(<|>}` + return true; + } + break; + case 'ConditionalExpression': + // `( foo ) ? '<|>` + if ( node.end === node.consequent.end ) { + node = node.consequent; + break; + } + // `( foo ) ? bar : '<|>` + node = node.alternate; + break; + case 'DoWhileStatement': + // `do {<|>` + if ( node.end === node.body.end ) { + node = node.body; + break; + } + // `do {} while (<|>` + if ( node.test.start === node.test.end ) { + debug( 'Detected the start of a do-while test.' ); + return true; + } + node = node.test; + break; + case 'EmptyStatement': + // `(` + debug( 'Detected the start of an empty statement.' ); + return true; + case 'ExpressionStatement': + node = node.expression; + break; + case 'ForInStatement': + // `for ( k in obj ) {<|>` + if ( node.body.type !== 'EmptyStatement' ) { + node = node.body; + break; + } + // We can omit the `left` case, as `for ( var i = '` will be parsed as a `ForStatement`: + node = node.right; + break; + case 'ForOfStatement': + // `for ( i of iter ) {<|>` + if ( node.body.type !== 'EmptyStatement' ) { + node = node.body; + break; + } + // We can omit the `left` case, as `for ( var i = '` will be parsed as a `ForStatement`: + node = node.right; + break; + case 'ForStatement': + // `for ( i = 0; i < 10; i++ ) {<|>` + if ( node.body.type !== 'EmptyStatement' ) { + node = node.body; + break; + } + // `for ( i = 0; i < 10; foo[<|>` + if ( node.update ) { + node = node.update; + break; + } + // `for ( i = 0; foo[<|>` + if ( node.test ) { + node = node.test; + break; + } + // By elimination, must be `init` (e.g., `for ( foo[<|>`): + node = node.init; + break; + case 'FunctionDeclaration': + // `function foo(<|>` + if ( node.body.start === node.body.end ) { + debug( 'Detected the start of a function declaration.' ); + return true; + } + // `function foo() {<|>` + node = node.body; + break; + case 'FunctionExpression': + // `foo = function(<|>` + if ( node.body.start === node.body.end ) { + debug( 'Detected the start of a function expression.' ); + return true; + } + // `foo = function() {<|>` + node = node.body; + break; + case 'Identifier': + if ( node.start === node.end ) { + debug( 'Detected an "empty" identifier.' ); + return true; + } + return false; + case 'IfStatement': + // `if ( foo ) {<|>` + if ( node.alternate ) { + node = node.alternate; + break; + } + // `if ( foo ) {} else {<|>` + if ( node.consequent.type !== 'EmptyStatement' ) { + node = node.consequent; + break; + } + // By elimination, must be `test` (e.g., `if (<|>`): + node = node.test; + break; + case 'Literal': + // `'<|>` || `"<|>` + if ( node.start === node.end-1 ) { + debug( 'Detected the start of a string literal.' ); + return true; + } + // `foo('<|>)` + if ( node.start === cursor-1 ) { + debug( 'Detected the start of a string literal.' ); + return true; + } + // `/'<|>` || `/'<|>/` + if ( node.regex ) { + debug( 'Detected a regular expression literal.' ); + return true; + } + // `'foo[<|>` + return false; + case 'LogicalExpression': + // `x[<|> || true` + if ( node.left.type === 'Identifier' && node.left.start === node.left.end ) { + node = node.left; + break; + } + // `x || [<|>` + node = node.right; + break; + case 'MemberExpression': + // `x['<|>` || `x['<|>] = 5` + if ( node.property.start === node.property.end || node.property.type === 'Literal' ) { + debug( 'Detected the start of a member expression.' ); + return true; + } + node = node.property; + break; + case 'NewExpression': + // `new (<|>` + if ( node.end === node.callee.end ) { + node = node.callee; + break; + } + // `new Foo(<|>` + if ( node.arguments.length === 0 ) { + debug( 'Detected the start of a new expression.' ); + return true; + } + // `new Foo( bar, '<|>` + node = findNode( node.arguments, cursor ); + break; + case 'ObjectExpression': + // `{<|>` + if ( node.properties.length === 0 ) { + debug( 'Detected the start of an object expression.' ); + return true; + } + // `{ 'a': 1, 'b': '<|>` + node = findNode( node.properties, cursor ); + break; + case 'Property': + // `{'<|>}` + return true; + case 'ReturnStatement': + // `return '<|>` + node = node.argument; + break; + case 'SequenceExpression': + // `x, '<|>` + node = findNode( node.expressions, cursor ); + break; + case 'SpreadElement': + // `[...foo['<|>` + node = node.argument; + break; + case 'SwitchCase': + // `switch ( foo ) { case '<|>` + if ( node.end === node.test.end ) { + node = node.test; + break; + } + node = findNode( node.consequent, cursor ); + break; + case 'SwitchStatement': + // `switch ( '<|>` || `switch ( foo[ '<|>` + if ( node.end === node.discriminant.end ) { + node = node.discriminant; + break; + } + // `switch ( foo ) {<|>` + if ( node.cases.length === 0 ) { + debug( 'Detected the start of a switch statement.' ); + return true; + } + // `switch ( foo ) { case 'bar': '<|>` + node = findNode( node.cases, cursor ); + break; + case 'TaggedTemplateExpression': + tmp = findNode( [ node.tag ], cursor ); + if ( tmp && !findNode( [ node.quasi ], cursor ) ) { + node = node.tag; + break; + } + debug( 'Detected the start of a tagged template expression.' ); + node = node.quasi; + break; + case 'TemplateElement': + return false; + case 'TemplateLiteral': + // `<|> + if ( node.start === node.end-1 ) { + debug( 'Detected the start of a template literal.' ); + return true; + } + if ( node.expressions.length > 0 ) { + tmp = findNode( node.expressions, cursor ); + if ( tmp ) { + node = tmp; + break; + } + } + node = findNode( node.quasis, cursor-1 ); + if ( node === null ) { + return true; + } + break; + case 'ThrowStatement': + // `throw new errs[ '<|>` || `throw new Error( '` + node = node.argument; + break; + case 'TryStatement': + // `try { y = foo( '<|>` + node = node.handler.body; + break; + case 'UpdateExpression': + // `++foo['<|>` + node = node.argument; + break; + case 'VariableDeclaration': + // `var x = '<|>` || `var x = {'<|>` || `let x = ['<|>` || `let x = {'<|>` || `const x = '<|>` || `const x = {'<|>` || `var x = 5, y = '<|>` || etc. + node = findNode( node.declarations, cursor ); + break; + case 'VariableDeclarator': + // `var x = [<|>` || `var x = {<|>` || etc + node = node.init; + break; + case 'WhileStatement': + // `while ( true ) {<|>` + if ( node.body.type !== 'EmptyStatement' ) { + node = node.body; + break; + } + // `while (<|>` || `while ( foo[<|>` + node = node.test; + break; + case 'YieldExpression': + // `yield [<|>` || `yield {<|>` || etc + node = node.argument; + break; + default: + return false; + } + } + return false; +} + + +// EXPORTS // + +module.exports = walk; diff --git a/lib/node_modules/@stdlib/repl/lib/commands.js b/lib/node_modules/@stdlib/repl/lib/commands.js index 7c279e49802..e065d0d0d86 100644 --- a/lib/node_modules/@stdlib/repl/lib/commands.js +++ b/lib/node_modules/@stdlib/repl/lib/commands.js @@ -55,6 +55,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 onSettings = require( './commands/settings.js' ); var onTutorial = require( './commands/tutorial.js' ); var onUserDoc = require( './commands/user_doc.js' ); var onVars = require( './commands/vars.js' ); @@ -122,6 +123,7 @@ function commands( repl ) { cmds.push( [ 'rerequire', onRerequire( repl ), false ] ); cmds.push( [ 'rerun', onRerun( repl ), false ] ); cmds.push( [ 'reset', onReset( repl ), false ] ); + cmds.push( [ 'settings', onSettings( repl ), false ] ); cmds.push( [ 'tutorial', onTutorial( repl ), false ] ); cmds.push( [ 'userDoc', onUserDoc( repl ), false ] ); cmds.push( [ 'vars', onVars( repl ), false ] ); diff --git a/lib/node_modules/@stdlib/repl/lib/commands/settings.js b/lib/node_modules/@stdlib/repl/lib/commands/settings.js new file mode 100644 index 00000000000..16b8ebab64f --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/commands/settings.js @@ -0,0 +1,119 @@ +/** +* @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 logger = require( 'debug' ); +var format = require( '@stdlib/string/format' ); +var log = require( './../log.js' ); +var SETTINGS = require( './../settings.js' ); +var SETTINGS_NAMES = require( './../settings_names.js' ); + + +// VARIABLES // + +var debug = logger( 'repl:command:settings' ); + + +// FUNCTIONS // + +/** +* Returns settings help text. +* +* @private +* @param {Object} settings - REPL settings +* @returns {string} settings help text +*/ +function help( settings ) { + var HELP_TEXT; + var name; + var o; + var i; + + HELP_TEXT = '\n'; + for ( i = 0; i < SETTINGS_NAMES.length; i++ ) { + name = SETTINGS_NAMES[ i ]; + o = SETTINGS[ name ]; + HELP_TEXT += name + '\n'; + HELP_TEXT += ' '; + HELP_TEXT += o.desc; // TODO: auto-wrap description + HELP_TEXT += format( ' Value: %s.', settings[ name ] ); + HELP_TEXT += '\n\n'; + } + return HELP_TEXT; +} + + +// MAIN // + +/** +* Returns a callback to be invoked upon calling the `settings` command. +* +* @private +* @param {REPL} repl - REPL instance +* @returns {Function} callback +*/ +function command( repl ) { + return onCommand; + + /** + * Gets (and sets) REPL settings. + * + * @private + * @param {string} [name] - setting name + * @param {*} [value] - new setting value + * @returns {(void|*)} setting value or undefined + */ + function onCommand() { + var nargs; + var v; + + nargs = arguments.length; + if ( nargs === 0 ) { + repl._ostream.write( help( repl._settings ) ); + return; + } + if ( nargs === 1 ) { + try { + v = repl.settings( arguments[ 0 ] ); + } catch ( err ) { + debug( 'Error: %s', err.message ); + repl._ostream.write( format( 'Error: %s\n', err.message ) ); + return; + } + return v; + } + try { + repl.settings( arguments[ 0 ], arguments[ 1 ] ); + } catch ( err ) { + debug( 'Error: %s', err.message ); + repl._ostream.write( format( 'Error: %s\n', err.message ) ); + return; + } + log( repl, '\nSuccessfully updated setting.' ); + } +} + + +// EXPORTS // + +module.exports = command; diff --git a/lib/node_modules/@stdlib/repl/lib/complete_settings.js b/lib/node_modules/@stdlib/repl/lib/complete_settings.js new file mode 100644 index 00000000000..93672a1f4da --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/complete_settings.js @@ -0,0 +1,91 @@ +/** +* @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'; + +// MODULES // + +var logger = require( 'debug' ); +var parse = require( 'acorn-loose' ).parse; +var startsWith = require( '@stdlib/string/starts-with' ); +var settingsAliasArgs = require( './settings_alias_args.js' ); +var SETTINGS_NAMES = require( './settings_names.js' ); + + +// VARIABLES // + +var debug = logger( 'repl:completer:settings' ); +var AOPTS = { + 'ecmaVersion': 'latest' +}; + + +// MAIN // + +/** +* Completes a settings API expression. +* +* @private +* @param {Array} out - output array for storing completions +* @param {REPL} repl - REPL instance +* @param {string} expression - expression to complete +* @param {string} alias - settings API alias +* @param {string} value - value to complete +* @returns {string} value filter +*/ +function complete( out, repl, expression, alias, value ) { + var args; + var ast; + var arg; + var v; + var i; + + // Get the list of argument types for the desired API: + debug( 'Settings API: %s', alias ); + args = settingsAliasArgs( alias ); + + // Parse the expression into an AST: + debug( 'Expression: %s', expression ); + ast = parse( expression, AOPTS ); + + // Check whether the argument which triggered TAB completion has a corresponding argument type which is completable: + debug( 'Checking if argument is completable...' ); + arg = args[ ast.body[ 0 ].expression.arguments.length-1 ]; + if ( !arg ) { + debug( 'Argument which triggered TAB completion is not completable.' ); + return ''; + } + debug( 'Argument is completable.' ); + + debug( 'Searching for completion candidates...' ); + for ( i = 0; i < SETTINGS_NAMES.length; i++ ) { + v = SETTINGS_NAMES[ i ]; + if ( startsWith( v, value ) ) { + debug( 'Found a completion: %s', v ); + out.push( v ); + } else { + debug( '%s does not match filter %s. Skipping...', v, value ); + } + } + return value; +} + + +// EXPORTS // + +module.exports = complete; diff --git a/lib/node_modules/@stdlib/repl/lib/complete_walk_find_last.js b/lib/node_modules/@stdlib/repl/lib/complete_walk_find_last.js index 8553ac5b020..3a1bfba0657 100644 --- a/lib/node_modules/@stdlib/repl/lib/complete_walk_find_last.js +++ b/lib/node_modules/@stdlib/repl/lib/complete_walk_find_last.js @@ -75,83 +75,68 @@ function walk( node ) { // eslint-disable-line max-lines-per-function while ( FLG && node.type !== 'Identifier' && node.type !== 'MemberExpression' ) { debug( 'Node type: %s', node.type ); switch ( node.type ) { - case 'ExpressionStatement': - node = node.expression; + case 'ArrayExpression': + // `[ <|>` || `[ foo<|>` || `[ 1, 2, <|>` || `[ 1, 2, foo<|>` || etc + if ( node.elements.length === 0 ) { + FLG = false; + break; + } + node = node.elements[ node.elements.length-1 ]; + break; + case 'ArrowFunctionExpression': + // `() => <|>` || `() => { <|>` + node = node.body; break; case 'AssignmentExpression': // `x = <|>` || `x = foo<|>` node = node.right; break; - case 'LogicalExpression': - // `x || <|>` || `x || foo<|>` - node = node.right; - break; case 'BinaryExpression': // `x | <|>` || `x | foo<|>` node = node.right; break; - case 'UpdateExpression': - // `++<|>` || `++foo<|>` - node = node.argument; - break; - case 'YieldExpression': - // `yield foo<|>` - if ( node.argument ) { - node = node.argument; + case 'BlockStatement': + if ( node.body.length ) { + node = node.body[ node.body.length-1 ]; break; } - // `yield <|>` + out.keywords.push.apply( out.keywords, RESERVED_KEYWORDS_EMPTY_BLOCK_STATEMENT ); // eslint-disable-line max-len FLG = false; break; - case 'SequenceExpression': - // `x, foo<|>` - node = node.expressions[ node.expressions.length-1 ]; - break; - case 'VariableDeclaration': - // `var x = <|>` || `var x = foo<|>` || `let x = <|>` || `let x = foo<|>` || `const x = <|>` || `const x = foo<|>` || `var x = 5, y = <|>` || etc. - node = node.declarations[ node.declarations.length-1 ]; - break; - case 'VariableDeclarator': - // `var foo<|>` - if ( node.id.end === node.end ) { - debug( 'Detected attempt to complete a newly created variable identifier, which is not supported.' ); - out.context = 'none'; - FLG = false; + case 'CallExpression': + // `foo( bar, <|>` + if ( node.arguments.length ) { + debug( 'Detected attempt to complete a call expression argument.' ); + node = node.arguments[ node.arguments.length-1 ]; break; } - // `var x = <|>` || `var x = foo<|>` - node = node.init; + // `foo( <|>` + debug( 'Detected attempt to complete a call expression argument.' ); + FLG = false; break; - case 'ArrayExpression': - // `[ <|>` || `[ foo<|>` || `[ 1, 2, <|>` || `[ 1, 2, foo<|>` || etc - if ( node.elements.length === 0 ) { - FLG = false; + case 'ConditionalExpression': + // `( foo ) ? <|>` + if ( node.end === node.consequent.end ) { + debug( 'Detected attempt to complete within a conditional expression consequent.' ); + node = node.consequent; break; } - node = node.elements[ node.elements.length-1 ]; + // `( foo ) ? bar : <|>` + debug( 'Detected attempt to complete within a conditional expression alternate.' ); + node = node.alternate; break; - case 'ForStatement': - // Determine from where in the statement we are trying to TAB complete... - if ( node.body.type !== 'EmptyStatement' ) { + case 'DoWhileStatement': + // `do { <|>` + if ( node.end === node.body.end ) { node = node.body; break; } - // `for ( i = 0; i < 10; <|>` - if ( node.update ) { - debug( 'Detected attempt to complete within a for loop update expression.' ); - node = node.update; - break; - } - // `for ( i = 0; <|>` - if ( node.test ) { - debug( 'Detected attempt to complete within a for loop test expression.' ); - node = node.test; - break; - } - // By elimination, must be `init` (e.g., `for ( <|>`): - debug( 'Detected attempt to complete within a for loop initialization expression.' ); - node = node.init; - out.keywords.push( 'let', 'var' ); + // `do {} while ( <|>` + debug( 'Detected attempt to complete within a do-while loop test condition.' ); + node = node.test; + break; + case 'ExpressionStatement': + node = node.expression; break; case 'ForInStatement': // `for ( k in obj ) { <|>` @@ -173,63 +158,28 @@ function walk( node ) { // eslint-disable-line max-lines-per-function debug( 'Detected attempt to complete within a for-of loop condition expression.' ); node = node.right; break; - case 'WhileStatement': - // `while ( true ) { <|>` + case 'ForStatement': + // Determine from where in the statement we are trying to TAB complete... if ( node.body.type !== 'EmptyStatement' ) { node = node.body; break; } - // `while ( <|>` - debug( 'Detected attempt to complete within a while loop test condition.' ); - node = node.test; - break; - case 'DoWhileStatement': - // `do { <|>` - if ( node.end === node.body.end ) { - node = node.body; - break; - } - // `do {} while ( <|>` - debug( 'Detected attempt to complete within a do-while loop test condition.' ); - node = node.test; - break; - case 'BlockStatement': - if ( node.body.length ) { - node = node.body[ node.body.length-1 ]; - break; - } - out.keywords.push.apply( out.keywords, RESERVED_KEYWORDS_EMPTY_BLOCK_STATEMENT ); // eslint-disable-line max-len - FLG = false; - break; - case 'CallExpression': - // `foo( bar, <|>` - if ( node.arguments.length ) { - debug( 'Detected attempt to complete a call expression argument.' ); - node = node.arguments[ node.arguments.length-1 ]; - break; - } - // `foo( <|>` - debug( 'Detected attempt to complete a call expression argument.' ); - FLG = false; - break; - case 'NewExpression': - // `new <|>` || `new Foo<|>` - if ( node.end === node.callee.end ) { - debug( 'Detected attempt to complete a new expression callee name.' ); - out.context = 'NewExpression'; - node = node.callee; + // `for ( i = 0; i < 10; <|>` + if ( node.update ) { + debug( 'Detected attempt to complete within a for loop update expression.' ); + node = node.update; break; } - // `new Foo( bar, <|>` - if ( node.arguments.length ) { - debug( 'Detected attempt to complete a new expression argument.' ); - node = node.arguments[ node.arguments.length-1 ]; + // `for ( i = 0; <|>` + if ( node.test ) { + debug( 'Detected attempt to complete within a for loop test expression.' ); + node = node.test; break; } - // `new Foo( <|>` - debug( 'Detected attempt to complete a new expression argument.' ); - out.context = '*'; - FLG = false; + // By elimination, must be `init` (e.g., `for ( <|>`): + debug( 'Detected attempt to complete within a for loop initialization expression.' ); + node = node.init; + out.keywords.push( 'let', 'var' ); break; case 'FunctionDeclaration': // `function foo( <|>` @@ -255,19 +205,6 @@ function walk( node ) { // eslint-disable-line max-lines-per-function // `foo = function() { <|>` node = node.body; break; - case 'ArrowFunctionExpression': - // `() => <|>` || `() => { <|>` - node = node.body; - break; - case 'ReturnStatement': - // `return foo<|>` - if ( node.argument ) { - node = node.argument; - break; - } - // `return <|>` - FLG = false; - break; case 'IfStatement': // Determine from where in the statement we are trying to TAB complete... @@ -287,36 +224,54 @@ function walk( node ) { // eslint-disable-line max-lines-per-function debug( 'Detected attempt to complete within an if statement test condition.' ); node = node.test; break; - case 'ConditionalExpression': - // `( foo ) ? <|>` - if ( node.end === node.consequent.end ) { - debug( 'Detected attempt to complete within a conditional expression consequent.' ); - node = node.consequent; + case 'LogicalExpression': + // `x || <|>` || `x || foo<|>` + node = node.right; + break; + case 'NewExpression': + // `new <|>` || `new Foo<|>` + if ( node.end === node.callee.end ) { + debug( 'Detected attempt to complete a new expression callee name.' ); + out.context = 'NewExpression'; + node = node.callee; break; } - // `( foo ) ? bar : <|>` - debug( 'Detected attempt to complete within a conditional expression alternate.' ); - node = node.alternate; - break; - case 'SwitchStatement': - // `switch ( <|>` || `switch ( foo<|>` - if ( node.end === node.discriminant.end ) { - debug( 'Detected attempt to complete within a switch statement discriminant.' ); - node = node.discriminant; + // `new Foo( bar, <|>` + if ( node.arguments.length ) { + debug( 'Detected attempt to complete a new expression argument.' ); + node = node.arguments[ node.arguments.length-1 ]; break; } - // `switch ( foo ) { <|>` - if ( node.cases.length === 0 ) { - debug( 'Detected attempt to complete the `case` keyword.' ); - out.context = 'SwitchCase'; - out.filter = ''; - out.keywords.push( 'case', 'default' ); + // `new Foo( <|>` + debug( 'Detected attempt to complete a new expression argument.' ); + out.context = '*'; + FLG = false; + break; + case 'ObjectExpression': + // `{<|>` + if ( node.properties.length === 0 ) { FLG = false; break; } - // `switch ( foo ) { case 'bar': <|>` - debug( 'Detected attempt to complete within a switch statement case.' ); - node = node.cases[ node.cases.length-1 ]; + // `{ 'a': 1, ...<|>` || `{ 'a': 1, ...foo<|>` + node = node.properties[ node.properties.length-1 ]; + break; + case 'ReturnStatement': + // `return foo<|>` + if ( node.argument ) { + node = node.argument; + break; + } + // `return <|>` + FLG = false; + break; + case 'SequenceExpression': + // `x, foo<|>` + node = node.expressions[ node.expressions.length-1 ]; + break; + case 'SpreadElement': + // `[...<|>` || `[...foo<|>` + node = node.argument; break; case 'SwitchCase': if ( node.test === null ) { @@ -369,6 +324,34 @@ function walk( node ) { // eslint-disable-line max-lines-per-function out.keywords.push( 'break' ); FLG = false; break; + case 'SwitchStatement': + // `switch ( <|>` || `switch ( foo<|>` + if ( node.end === node.discriminant.end ) { + debug( 'Detected attempt to complete within a switch statement discriminant.' ); + node = node.discriminant; + break; + } + // `switch ( foo ) { <|>` + if ( node.cases.length === 0 ) { + debug( 'Detected attempt to complete the `case` keyword.' ); + out.context = 'SwitchCase'; + out.filter = ''; + out.keywords.push( 'case', 'default' ); + FLG = false; + break; + } + // `switch ( foo ) { case 'bar': <|>` + debug( 'Detected attempt to complete within a switch statement case.' ); + node = node.cases[ node.cases.length-1 ]; + break; + case 'TemplateLiteral': + // ``<|> + if ( node.expressions.length === 0 ) { + FLG = false; + break; + } + node = node.expressions[ node.expressions.length-1 ]; + break; case 'ThrowStatement': // `throw <|>` || `throw foo<|>` node = node.argument; @@ -377,26 +360,43 @@ function walk( node ) { // eslint-disable-line max-lines-per-function // `try { <|>` node = node.handler.body; break; - case 'TemplateLiteral': - // ``<|> - if ( node.expressions.length === 0 ) { + case 'UpdateExpression': + // `++<|>` || `++foo<|>` + node = node.argument; + break; + case 'VariableDeclaration': + // `var x = <|>` || `var x = foo<|>` || `let x = <|>` || `let x = foo<|>` || `const x = <|>` || `const x = foo<|>` || `var x = 5, y = <|>` || etc. + node = node.declarations[ node.declarations.length-1 ]; + break; + case 'VariableDeclarator': + // `var foo<|>` + if ( node.id.end === node.end ) { + debug( 'Detected attempt to complete a newly created variable identifier, which is not supported.' ); + out.context = 'none'; FLG = false; break; } - node = node.expressions[ node.expressions.length-1 ]; + // `var x = <|>` || `var x = foo<|>` + node = node.init; break; - case 'SpreadElement': - // `[...<|>` || `[...foo<|>` - node = node.argument; + case 'WhileStatement': + // `while ( true ) { <|>` + if ( node.body.type !== 'EmptyStatement' ) { + node = node.body; + break; + } + // `while ( <|>` + debug( 'Detected attempt to complete within a while loop test condition.' ); + node = node.test; break; - case 'ObjectExpression': - // `{<|>` - if ( node.properties.length === 0 ) { - FLG = false; + case 'YieldExpression': + // `yield foo<|>` + if ( node.argument ) { + node = node.argument; break; } - // `{ 'a': 1, ...<|>` || `{ 'a': 1, ...foo<|>` - node = node.properties[ node.properties.length-1 ]; + // `yield <|>` + FLG = false; break; default: out.context = 'none'; diff --git a/lib/node_modules/@stdlib/repl/lib/completer.js b/lib/node_modules/@stdlib/repl/lib/completer.js index a8ca2b851b2..5871b7887ae 100644 --- a/lib/node_modules/@stdlib/repl/lib/completer.js +++ b/lib/node_modules/@stdlib/repl/lib/completer.js @@ -29,10 +29,13 @@ var fsRegExp = require( './regexp_fs_aliases.js' ); var requireRegExp = require( './regexp_require.js' ); var workspaceRegExp = require( './regexp_workspace.js' ); var tutorialRegExp = require( './regexp_tutorial.js' ); +var settingsRegExp = require( './regexp_settings.js' ); +var reservedCharsRegExp = require( './regexp_reserved_syntax_characters.js' ); var completeRequire = require( './complete_require.js' ); var completeFS = require( './complete_fs.js' ); var completeWorkspace = require( './complete_workspace.js' ); var completeTutorial = require( './complete_tutorial.js' ); +var completeSettings = require( './complete_settings.js' ); var completeExpression = require( './complete_expression.js' ); @@ -162,6 +165,27 @@ function completer( repl ) { debug( 'Results: %s', res.join( ', ' ) ); return clbk( null, [ res, line ] ); } + // Test if the line has an incomplete settings expression: + match = line.match( settingsRegExp() ); + if ( match ) { + debug( 'Detected incomplete settings expression.' ); + + debug( 'Expression: %s', match[ 0 ] ); + debug( 'Settings API: %s', match[ 1 ] ); + debug( 'Value to complete: %s', match[ 3 ] ); + line = completeSettings( res, repl, match[ 0 ], match[ 1 ], match[ 3 ] ); // eslint-disable-line max-len + res = normalize( res ); + + debug( 'Completion filter: %s', line ); + debug( 'Results: %s', res.join( ', ' ) ); + return clbk( null, [ res, line ] ); + } + // Sanity check that we are attempting to complete something which is completable: + if ( reservedCharsRegExp().test( line[ repl._rli.cursor-1 ] ) ) { + debug( 'Detected attempt to trigger completion after a special character.' ); + debug( 'Results: %s', res.join( ', ' ) ); + return clbk( null, [ res, line ] ); + } debug( 'Attempting to complete an incomplete expression.' ); line = completeExpression( res, repl._context, line ); res = normalize( res ); diff --git a/lib/node_modules/@stdlib/repl/lib/completer_preview.js b/lib/node_modules/@stdlib/repl/lib/completer_preview.js index 59d4ae1efdc..ac14a6f9d8e 100644 --- a/lib/node_modules/@stdlib/repl/lib/completer_preview.js +++ b/lib/node_modules/@stdlib/repl/lib/completer_preview.js @@ -16,7 +16,7 @@ * limitations under the License. */ -/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-invalid-this */ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-invalid-this */ 'use strict'; @@ -179,11 +179,19 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'clear', function clear() * @name onKeypress * @memberof PreviewCompleter.prototype * @param {string} data - input data -* @param {Object} key - key object +* @param {(Object|void)} key - key object * @returns {void} */ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onKeypress() { - this._completer( this._rli.line, this._onCompletions ); + // Check for existing content beyond the cursor which could "collide" with a preview completion... + if ( /[^a-zA-Z0-9_$]/.test( this._rli.line.substring( this._rli.cursor ) ) ) { // FIXME: this is not robust (see https://mathiasbynens.be/notes/javascript-identifiers) + return; + } + try { + this._completer( this._rli.line, this._onCompletions ); + } catch ( err ) { + debug( 'Error: %s', err.message ); + } }); /** @@ -192,7 +200,7 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onK * @name beforeKeypress * @memberof PreviewCompleter.prototype * @param {string} data - input data -* @param {Object} key - key object +* @param {(Object|void)} key - key object * @returns {void} */ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'beforeKeypress', function beforeKeypress( data, key ) { diff --git a/lib/node_modules/@stdlib/repl/lib/defaults.js b/lib/node_modules/@stdlib/repl/lib/defaults.js index d42e673d124..a9ddd5c657a 100644 --- a/lib/node_modules/@stdlib/repl/lib/defaults.js +++ b/lib/node_modules/@stdlib/repl/lib/defaults.js @@ -39,19 +39,56 @@ var WELCOME = require( './welcome_text.js' ); */ function defaults() { return { + // Default input stream: 'input': stdin, + + // Default output stream: 'output': stdout, + + // Input prompt: 'inputPrompt': 'In [%d]: ', + + // Output prompt: 'outputPrompt': 'Out[%d]: ', + + // Number of empty lines between successive commands: 'padding': 1, + + // Flag indicating whether the input and output streams should be treated like a TTY (terminal) and whether the REPL should use ANSI/VT100 escape codes when writing to the output stream: 'isTTY': void 0, + + // Number of milliseconds to execute a command before terminating execution: 'timeout': 4294967295, + + // Flag indicating whether to run a REPL in a sandboxed context: 'sandbox': true, + + // Welcome message: 'welcome': WELCOME, + + // File path specifying where to save REPL command history: 'save': '', + + // File path specifying a JavaScript file to load and evaluate line-by-line (e.g., a previous REPL history file): 'load': '', + + // File path specifying where to save REPL commands and printed output: 'log': '', - 'quiet': false + + // Flag indicating whether log information, confirmation messages, and other possible REPL diagnostics should be silenced: + 'quiet': false, + + // User settings: + 'settings': { + // Flag indicating whether to automatically insert matching brackets, parentheses, and quotes: + 'autoClosePairs': true, + + // Flag indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes: + 'autoDeletePairs': true, + + // Flag indicating whether to enable the display of completion previews for auto-completion (note: default depends on whether TTY): + 'completionPreviews': void 0 + } }; } diff --git a/lib/node_modules/@stdlib/repl/lib/help_text.js b/lib/node_modules/@stdlib/repl/lib/help_text.js index 26979f561a1..2daf600c19b 100644 --- a/lib/node_modules/@stdlib/repl/lib/help_text.js +++ b/lib/node_modules/@stdlib/repl/lib/help_text.js @@ -35,6 +35,10 @@ var MSG = [ ' 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.', diff --git a/lib/node_modules/@stdlib/repl/lib/is_setting_name.js b/lib/node_modules/@stdlib/repl/lib/is_setting_name.js new file mode 100644 index 00000000000..5e74a88cf50 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/is_setting_name.js @@ -0,0 +1,47 @@ +/** +* @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'; + +// MODULES // + +var contains = require( '@stdlib/array/base/assert/contains' ).factory; +var SETTINGS_NAMES = require( './settings_names.js' ); + + +// MAIN // + +/** +* Returns a boolean indicating whether a provided value is a recognized settings name. +* +* @private +* @name isSettingName +* @type {Function} +* @param {*} value - input value +* @returns {boolean} boolean indicating whether a provided value is a recognized settings name +* +* @example +* var out = isSettingName( 'beep_boop_foo_bar' ); +* // returns false +*/ +var isSettingName = contains( SETTINGS_NAMES ); + + +// EXPORTS // + +module.exports = isSettingName; diff --git a/lib/node_modules/@stdlib/repl/lib/main.js b/lib/node_modules/@stdlib/repl/lib/main.js index e002c41c260..a9af0d4dc0c 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 */ +/* eslint-disable no-restricted-syntax, no-invalid-this, no-underscore-dangle, max-lines */ 'use strict'; @@ -43,8 +43,10 @@ var readFileSync = require( '@stdlib/fs/read-file' ).sync; var RE_EOL = require( '@stdlib/regexp/eol' ).REGEXP; 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 defaults = require( './defaults.js' ); +var isSettingName = require( './is_setting_name.js' ); var setAliases = require( './set_aliases.js' ); var setAliasesGlobal = require( './set_aliases_global.js' ); var setCommands = require( './set_commands.js' ); @@ -57,7 +59,10 @@ var inputPrompt = require( './input_prompt.js' ); var processLine = require( './process_line.js' ); var completerFactory = require( './completer.js' ); var PreviewCompleter = require( './completer_preview.js' ); +var AutoCloser = require( './auto_close_pairs.js' ); var ALIAS_OVERRIDES = require( './alias_overrides.js' ); +var SETTINGS = require( './settings.js' ); +var SETTINGS_VALIDATORS = require( './settings_validators.js' ); // VARIABLES // @@ -85,6 +90,10 @@ var debug = logger( 'repl' ); * @param {string} [options.save] - file path specifying where to save REPL command history * @param {string} [options.log] - file path specifying where to save REPL commands and printed output * @param {string} [options.quiet=false] - boolean indicating whether log information, confirmation messages, and other possible REPL diagnostics should be silenced +* @param {Object} [options.settings] - REPL settings +* @param {boolean} [options.settings.autoClosePairs=true] - boolean indicating whether to automatically insert matching brackets, parentheses, and quotes +* @param {boolean} [options.settings.autoDeletePairs=true] - boolean indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes +* @param {boolean} [options.settings.completionPreviews] - boolean indicating whether to enable completion previews for auto-completion * @throws {Error} must provide valid options * @returns {REPL} REPL instance * @@ -107,9 +116,6 @@ var debug = logger( 'repl' ); * repl.close(); */ function REPL( options ) { - var previewCompleter; - var completer; - var ttyWrite; var opts; var self; var err; @@ -130,6 +136,7 @@ function REPL( options ) { } } opts.isTTY = ( opts.isTTY === void 0 ) ? opts.output.isTTY : opts.isTTY; + opts.settings.completionPreviews = ( opts.settings.completionPreviews === void 0 ) ? opts.isTTY : opts.settings.completionPreviews; // eslint-disable-line max-len debug( 'Options: %s', JSON.stringify({ 'input': '', @@ -144,7 +151,8 @@ function REPL( options ) { 'load': opts.load, 'save': opts.save, 'log': opts.log, - 'quiet': opts.quiet + 'quiet': opts.quiet, + 'settings': opts.settings })); // Call the parent constructor: @@ -161,6 +169,7 @@ function REPL( options ) { setNonEnumerableReadOnly( this, '_timeout', opts.timeout ); setNonEnumerableReadOnly( this, '_isTTY', opts.isTTY ); setNonEnumerableReadOnly( this, '_sandbox', opts.sandbox ); + setNonEnumerableReadOnly( this, '_settings', opts.settings ); setNonEnumerable( this, '_quiet', opts.quiet ); // allow this to be internally toggled // Initialize an internal data store: @@ -206,8 +215,10 @@ function REPL( options ) { // Define the current workspace: setNonEnumerable( this, '_currentWorkspace', 'base' ); - // Initialize an internal flag indicating whether a user is entering a multi-line command: - setNonEnumerable( this, '_multiline_mode', false ); + // Initialize an internal status object for multi-line mode: + setNonEnumerable( this, '_multiline', {} ); + setNonEnumerable( this._multiline, 'active', false ); + setNonEnumerable( this._multiline, 'mode', 'incomplete_expression' ); // Initialize an internal flag indicating whether the REPL has been closed: setNonEnumerable( this, '_closed', false ); @@ -228,7 +239,7 @@ function REPL( options ) { setNonEnumerable( this, '_context', this.createContext() ); // Create a new TAB completer: - completer = completerFactory( this ); + setNonEnumerableReadOnly( this, '_completer', completerFactory( this ) ); // Create an internal readline interface: debug( 'Creating readline interface...' ); @@ -237,9 +248,22 @@ function REPL( options ) { 'output': this._ostream, 'terminal': opts.isTTY, 'prompt': opts.inputPrompt, - 'completer': completer + 'completer': this._completer })); + // Create a new auto-closer: + setNonEnumerableReadOnly( this, '_autoCloser', new AutoCloser( this._rli ) ); + + // Initialize a preview completer: + setNonEnumerableReadOnly( this, '_previewCompleter', new PreviewCompleter( this._rli, this._completer, this._ostream ) ); + + // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: + 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 + + // Add event listeners: this._rli.on( 'close', onClose ); this._rli.on( 'line', onLine ); this._rli.on( 'SIGINT', onSIGINT ); @@ -247,23 +271,11 @@ function REPL( options ) { // Add listener for "command" events: this.on( 'command', onCommand ); - // If operating in "terminal" mode, initialize a preview completer... - if ( this._isTTY ) { - // Create a new preview completer: - previewCompleter = new PreviewCompleter( this._rli, completer, this._ostream ); - - // Instruct the input stream to begin emitting "keypress" events: - readline.emitKeypressEvents( this._istream, this._rli ); - - // Add a listener for "keypress" events: - this._istream.on( 'keypress', onKeypress ); + // Instruct the input stream to begin emitting "keypress" events: + readline.emitKeypressEvents( this._istream, this._rli ); - // Cache a reference to the private readline interface `ttyWrite` to allow calling the method when wanting default behavior: - 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 - } + // Add a listener for "keypress" events: + this._istream.on( 'keypress', onKeypress ); // Write a welcome message: this._ostream.write( opts.welcome ); @@ -286,11 +298,17 @@ function REPL( options ) { * * @private * @param {string} data - input data - * @param {Object} key - key object + * @param {(Object|void)} key - key object */ function beforeKeypress( data, key ) { - previewCompleter.beforeKeypress( data, key ); - ttyWrite.call( self._rli, data, key ); + var settings = self._settings; + if ( settings.autoDeletePairs ) { + self._autoCloser.beforeKeypress( data, key ); + } + if ( settings.completionPreviews ) { + self._previewCompleter.beforeKeypress( data, key ); + } + self._ttyWrite.call( self._rli, data, key ); } /** @@ -298,10 +316,29 @@ function REPL( options ) { * * @private * @param {string} data - input data - * @param {Object} key - key object + * @param {(Object|void)} key - key object + * @returns {void} */ function onKeypress( data, key ) { - previewCompleter.onKeypress( data, key ); + var autoClosed; + var settings; + + if ( key && key.name === 'tab' ) { + return; + } + settings = self._settings; + + if ( settings.autoClosePairs ) { + autoClosed = self._autoCloser.onKeypress( data, key ); + } + if ( settings.completionPreviews ) { + // If auto-closing was performed, explicitly remove any currently displayed completion preview... + if ( autoClosed ) { + self._previewCompleter.clear(); + } else { + self._previewCompleter.onKeypress( data, key ); + } + } } /** @@ -816,7 +853,7 @@ setNonEnumerableReadOnly( REPL.prototype, 'clear', function onClear() { * @name clearLine * @memberof REPL.prototype * @type {Function} -* @throws {Error} cannot clear the current line a closed REPL +* @throws {Error} cannot clear the current line of a closed REPL * @returns {REPL} REPL instance * * @example @@ -969,6 +1006,73 @@ setNonEnumerableReadOnly( REPL.prototype, 'close', function close() { } }); +/** +* Gets (and sets) REPL settings. +* +* @name settings +* @memberof REPL.prototype +* @type {Function} +* @param {string} [name] - setting name +* @param {*} [value] - new setting value +* @throws {TypeError} first argument must be a string +* @throws {TypeError} first argument must be a recognized setting +* @throws {TypeError} second argument must be a valid setting value +* @throws {Error} cannot access settings for a closed REPL +* @returns {(*|Object|REPL)} settings or a REPL instance +* +* @example +* var debug = require( '@stdlib/streams/node/debug-sink' ); +* +* // Create a new REPL: +* var repl = new REPL({ +* 'output': debug() +* }); +* +* // ... +* +* // Retrieve the current REPL settings: +* var o = repl.settings(); +* +* // ... +* +* // Close the REPL: +* repl.close(); +*/ +setNonEnumerableReadOnly( REPL.prototype, 'settings', function settings() { + var nargs; + var value; + var name; + var f; + + if ( this._closed ) { + throw new Error( 'invalid operation. Cannot access settings for a REPL which has already closed.' ); + } + nargs = arguments.length; + if ( nargs === 0 ) { + return assign( {}, this._settings ); + } + name = arguments[ 0 ]; + if ( !isString( name ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', name ) ); + } + if ( !isSettingName( name ) ) { + throw new Error( format( 'invalid argument. First argument must be a recognized setting. Value: `%s`.', name ) ); + } + if ( nargs === 1 ) { + return this._settings[ name ]; // TODO: we should consider returning a deep copy if settings are allowed to be objects, not just primitives, in order to avoid unintentional mutation + } + value = arguments[ 1 ]; + f = SETTINGS_VALIDATORS[ SETTINGS[ name ].type ]; + if ( !f.assert( value ) ) { + throw new TypeError( f.msg( name, value ) ); + } + debug( 'Updating setting `%s` to `%s`...', name, JSON.stringify( value ) ); + this._settings[ name ] = value; + debug( 'Successfully updated setting: `%s`.', name ); + + return this; +}); + // EXPORTS // diff --git a/lib/node_modules/@stdlib/repl/lib/process_line.js b/lib/node_modules/@stdlib/repl/lib/process_line.js index fc97f0f127b..8dcce73586b 100644 --- a/lib/node_modules/@stdlib/repl/lib/process_line.js +++ b/lib/node_modules/@stdlib/repl/lib/process_line.js @@ -62,7 +62,7 @@ function processLine( repl, line ) { var tmp; debug( 'Line: %s', line ); - repl._multiline_mode = false; // false until proven otherwise + repl._multiline.active = false; // false until proven otherwise cmd = repl._cmd.join( '\n' ) + line; if ( RE_WHITESPACE.test( cmd ) ) { @@ -83,7 +83,7 @@ function processLine( repl, line ) { if ( hasMultilineError( cmd, AOPTS ) ) { debug( 'Detected multi-line input. Waiting for additional lines...' ); repl._cmd.push( line ); - repl._multiline_mode = true; + repl._multiline.active = true; displayPrompt( repl, false ); return; } @@ -97,7 +97,7 @@ function processLine( repl, line ) { if ( hasMultilineError( tmp, AOPTS ) ) { debug( 'Detected multi-line input. Waiting for additional lines...' ); repl._cmd.push( line ); - repl._multiline_mode = true; + repl._multiline.active = true; displayPrompt( repl, false ); return; } diff --git a/lib/node_modules/@stdlib/repl/lib/regexp_reserved_syntax_characters.js b/lib/node_modules/@stdlib/repl/lib/regexp_reserved_syntax_characters.js new file mode 100644 index 00000000000..90c75e3c0a7 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/regexp_reserved_syntax_characters.js @@ -0,0 +1,42 @@ +/** +* @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 // + +/** +* Returns a regular expression for matching reserved JavaScript syntax characters. +* +* @private +* @returns {RegExp} regular expression +* +* @example +* var RE_SYNTAX_CHARS = createRegExp(); +* +* var bool = RE_SYNTAX_CHARS.test( '()' ); +* // returns true +*/ +function createRegExp() { + return /[`~!@#%^&*()-=+[\]{}\\/|;:'",<>?. ]/; +} + + +// EXPORTS // + +module.exports = createRegExp; diff --git a/lib/node_modules/@stdlib/repl/lib/regexp_settings.js b/lib/node_modules/@stdlib/repl/lib/regexp_settings.js new file mode 100644 index 00000000000..161d77940a1 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/regexp_settings.js @@ -0,0 +1,106 @@ +/** +* @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'; + +// MODULES // + +var SETTINGS_ALIASES = require( './settings_aliases.js' ); + + +// MAIN // + +/** +* Returns a regular expression for matching settings APIs. +* +* ## Notes +* +* - Example output: +* +* ```text +* /\b(?:(settings)\s*\(\s*(.*,\s*)*(?:\[\s*(?:[^\]]*,\s*)*)?['"]([^'"]*))$/ +* ``` +* +* @private +* @returns {RegExp} regular expression +*/ +function createRegExp() { + var re; + var N; + var M; + var i; + + N = SETTINGS_ALIASES.length; + M = N - 1; + re = ''; + + // Match a word boundary: + re += '\\b'; + + // Create a capture group for detecting an alias with an incomplete invocation (e.g., `settings( 'foo`): + re += '(?:'; + + // Create a capture group for matching the alias... + re += '('; + for ( i = 0; i < N; i++ ) { + // Append the alias: + re += SETTINGS_ALIASES[ i ][ 0 ]; + + // Separate aliases with an OR separator: + if ( i < M ) { + re += '|'; + } + } + // Close the alias capture group: + re += ')'; + + // Match zero or more whitespace characters after the alias: + re += '\\s*'; + + // Match an opening parenthesis literal: + re += '\\('; + + // Match zero or more whitespace characters after the opening parenthesis: + re += '\\s*'; + + // Capture provided (comma-separated) arguments which precede the last argument: + re += '(.*,\\s*)*'; + + // Capture, but don't remember, an incomplete array argument (including all items except for the last) which occurs zero or one time: + re += '(?:\\[\\s*(?:[^\\]]*,\\s*)*)?'; + + // Match either a `'` or `"` literal: + re += '[\'"]'; + + // Capture the last (incomplete) argument (or array item): + re += '([^\'"]*)'; + + // Close the incomplete invocation capture group: + re += ')'; + + // Match the end of input (i.e., ensure that the match is an incomplete invocation): + re += '$'; + + // Return the regular expression: + return new RegExp( re ); +} + + +// EXPORTS // + +module.exports = createRegExp; diff --git a/lib/node_modules/@stdlib/repl/lib/settings.js b/lib/node_modules/@stdlib/repl/lib/settings.js new file mode 100644 index 00000000000..caa2e40de20 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/settings.js @@ -0,0 +1,48 @@ +/** +* @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 // + +/** +* REPL settings. +* +* @private +* @name SETTINGS +* @type {Object} +*/ +var SETTINGS = { + 'autoClosePairs': { + 'desc': 'Automatically insert matching brackets, parentheses, and quotes.', + 'type': 'boolean' + }, + 'autoDeletePairs': { + 'desc': 'Automatically delete adjacent matching brackets, parentheses, and quotes.', + 'type': 'boolean' + }, + 'completionPreviews': { + 'desc': 'Enable the display of completion previews for auto-completion.', + 'type': 'boolean' + } +}; + + +// EXPORTS // + +module.exports = SETTINGS; diff --git a/lib/node_modules/@stdlib/repl/lib/settings_alias_args.js b/lib/node_modules/@stdlib/repl/lib/settings_alias_args.js new file mode 100644 index 00000000000..ead6beda6e6 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/settings_alias_args.js @@ -0,0 +1,48 @@ +/** +* @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'; + +// MODULES // + +var SETTINGS_ALIASES = require( './settings_aliases.js' ); + + +// MAIN // + +/** +* Returns a list of argument completion flags for a specified settings API. +* +* @private +* @param {string} alias - alias +* @returns {(Array|null)} argument completion flags +*/ +function argFlags( alias ) { + var i; + for ( i = 0; i < SETTINGS_ALIASES.length; i++ ) { + if ( SETTINGS_ALIASES[ i ][ 0 ] === alias ) { + return SETTINGS_ALIASES[ i ].slice( 1 ); + } + } + return null; +} + + +// EXPORTS // + +module.exports = argFlags; diff --git a/lib/node_modules/@stdlib/repl/lib/settings_aliases.js b/lib/node_modules/@stdlib/repl/lib/settings_aliases.js new file mode 100644 index 00000000000..7e27cf22212 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/settings_aliases.js @@ -0,0 +1,46 @@ +/** +* @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 settings API aliases and corresponding argument completion compatibility. +* +* ## Notes +* +* - Each entry in the list has the following format: +* +* ``` +* [ , , ... ] +* ``` +* +* where `` is a settings API alias and `` is a `boolean` indicating whether an argument is compatible with settings name completion. +* +* - For the purposes of TAB completion, only those positional arguments which expect settings names need to be included. For example, if an API has three parameters and only the first argument expects a settings name, only that first argument needs to be included below; the remaining two arguments can be omitted, as those arguments are assumed to be incompatible with settings name completion. If an API has three parameters and only the second argument expects a settings name, only the first two arguments need to be included below, with the first argument documented as `null`; the remaining argument can be omitted. +*/ +var aliases = [ + // Note: keep in alphabetical order... + [ 'settings', true ] +]; + + +// EXPORTS // + +module.exports = aliases; diff --git a/lib/node_modules/@stdlib/repl/lib/settings_names.js b/lib/node_modules/@stdlib/repl/lib/settings_names.js new file mode 100644 index 00000000000..fb750e4b566 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/settings_names.js @@ -0,0 +1,41 @@ +/** +* @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'; + +// MODULES // + +var objectKeys = require( '@stdlib/utils/keys' ); +var SETTINGS = require( './settings.js' ); + + +// MAIN // + +/** +* List of settings names. +* +* @private +* @name SETTINGS_NAMES +* @type {Array} +*/ +var SETTINGS_NAMES = objectKeys( SETTINGS ); + + +// EXPORTS // + +module.exports = SETTINGS_NAMES; diff --git a/lib/node_modules/@stdlib/repl/lib/settings_validators.js b/lib/node_modules/@stdlib/repl/lib/settings_validators.js new file mode 100644 index 00000000000..345b07acaa4 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/settings_validators.js @@ -0,0 +1,112 @@ +/** +* @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'; + +// MODULES // + +var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var isNumber = require( '@stdlib/assert/is-number' ).isPrimitive; +var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive; +var format = require( '@stdlib/string/format' ); + + +// FUNCTIONS // + +/** +* Returns a formatted error message for a value is not a boolean. +* +* @private +* @param {string} name - setting name +* @param {*} value - invalid value +* @returns {string} formatted error message +*/ +function booleanErrMsg( name, value ) { + return format( 'invalid argument. `%s` setting must be a boolean. Value: `%s`.', name, value ); +} + +/** +* Returns a formatted error message for a value is not an integer. +* +* @private +* @param {string} name - setting name +* @param {*} value - invalid value +* @returns {string} formatted error message +*/ +function integerErrMsg( name, value ) { + return format( 'invalid argument. `%s` setting must be an integer. Value: `%s`.', name, value ); +} + +/** +* Returns a formatted error message for a value is not a number. +* +* @private +* @param {string} name - setting name +* @param {*} value - invalid value +* @returns {string} formatted error message +*/ +function numberErrMsg( name, value ) { + return format( 'invalid argument. `%s` setting must be a number. Value: `%s`.', name, value ); +} + +/** +* Returns a formatted error message for a value is not a string. +* +* @private +* @param {string} name - setting name +* @param {*} value - invalid value +* @returns {string} formatted error message +*/ +function stringErrMsg( name, value ) { + return format( 'invalid argument. `%s` setting must be a string. Value: `%s`.', name, value ); +} + + +// MAIN // + +/** +* Mapping from data types to validation functions. +* +* @private +* @name VALIDATORS +* @type {Object} +*/ +var VALIDATORS = { // eslint-disable-line vars-on-top + 'boolean': { + 'assert': isBoolean, + 'msg': booleanErrMsg + }, + 'integer': { + 'assert': isInteger, + 'msg': integerErrMsg + }, + 'number': { + 'assert': isNumber, + 'msg': numberErrMsg + }, + 'string': { + 'assert': isString, + 'msg': stringErrMsg + } +}; + + +// EXPORTS // + +module.exports = VALIDATORS; diff --git a/lib/node_modules/@stdlib/repl/lib/validate.js b/lib/node_modules/@stdlib/repl/lib/validate.js index 9ce9eeac16c..adaeee1a68f 100644 --- a/lib/node_modules/@stdlib/repl/lib/validate.js +++ b/lib/node_modules/@stdlib/repl/lib/validate.js @@ -29,6 +29,7 @@ var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive; var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive; var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive; var format = require( '@stdlib/string/format' ); +var validateSettings = require( './validate_settings.js' ); // MAIN // @@ -52,7 +53,7 @@ var format = require( '@stdlib/string/format' ); * @param {string} [options.save] - file path specifying where to save REPL command history * @param {string} [options.log] - file path specifying where to save REPL commands and printed output * @param {string} [options.quiet] - boolean indicating whether log information, confirmation messages, and other possible REPL diagnostics should be silenced -* @throws {Error} must provide valid options +* @param {Object} [options.settings] - REPL settings * @returns {(Error|null)} error or null * * @example @@ -66,6 +67,7 @@ var format = require( '@stdlib/string/format' ); * } */ function validate( opts, options ) { + var err; if ( !isPlainObject( options ) ) { return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); } @@ -147,6 +149,12 @@ function validate( opts, options ) { return new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'quiet', options.quiet ) ); } } + if ( hasOwnProp( options, 'settings' ) ) { + err = validateSettings( opts.settings, options.settings ); + if ( err ) { + return err; + } + } return null; } diff --git a/lib/node_modules/@stdlib/repl/lib/validate_settings.js b/lib/node_modules/@stdlib/repl/lib/validate_settings.js new file mode 100644 index 00000000000..923f860f602 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/lib/validate_settings.js @@ -0,0 +1,78 @@ +/** +* @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'; + +// MODULES // + +var isPlainObject = require( '@stdlib/assert/is-plain-object' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var format = require( '@stdlib/string/format' ); +var SETTINGS = require( './settings.js' ); +var SETTINGS_NAMES = require( './settings_names.js' ); +var VALIDATORS = require( './settings_validators.js' ); + + +// MAIN // + +/** +* Validates settings. +* +* @private +* @param {Object} opts - destination object +* @param {Object} options - settings options +* @returns {(Error|null)} error or null +* +* @example +* var options = { +* 'autoClosePairs': true +* }; +* var opts = {}; +* var err = validate( opts, options ); +* if ( err ) { +* throw err; +* } +*/ +function validate( opts, options ) { + var name; + var o; + var f; + var v; + var i; + if ( !isPlainObject( options ) ) { + return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); + } + for ( i = 0; i < SETTINGS_NAMES.length; i++ ) { + name = SETTINGS_NAMES[ i ]; + if ( hasOwnProp( options, name ) ) { + v = options[ name ]; + o = SETTINGS[ name ]; + f = VALIDATORS[ o.type ]; + if ( !f.assert( v ) ) { + return new TypeError( f.msg( name, v ) ); + } + opts[ name ] = v; + } + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/array_expression_right_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/array_expression_right_bracket.json new file mode 100644 index 00000000000..08cd5bda846 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/array_expression_right_bracket.json @@ -0,0 +1,23 @@ +{ + "expression": "[]", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "ArrayExpression", + "start": 0, + "end": 2, + "elements": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/object_expression_right_brace.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/object_expression_right_brace.json new file mode 100644 index 00000000000..af0eda1167e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/object_expression_right_brace.json @@ -0,0 +1,18 @@ +{ + "expression": "{}", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "BlockStatement", + "start": 0, + "end": 2, + "body": [] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_backtick.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_backtick.json new file mode 100644 index 00000000000..5348d6d37f8 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_backtick.json @@ -0,0 +1,35 @@ +{ + "expression": "``", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 2, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_double_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_double_quote.json new file mode 100644 index 00000000000..9639e899d39 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_double_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "\"\"", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "Literal", + "start": 0, + "end": 2, + "value": "", + "raw": "\"\"" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_parenthesis.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_parenthesis.json new file mode 100644 index 00000000000..b73b4e93051 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_parenthesis.json @@ -0,0 +1,17 @@ +{ + "expression": "()", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "EmptyStatement", + "start": 0, + "end": 2 + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_single_quote.json new file mode 100644 index 00000000000..d35d8a7ee2c --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/right_single_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "''", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "Literal", + "start": 0, + "end": 2, + "value": "", + "raw": "''" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal.json new file mode 100644 index 00000000000..aa50464f6f1 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal.json @@ -0,0 +1,35 @@ +{ + "expression": "`foo`", + "cursor": 4, + "ast": { + "type": "Program", + "start": 0, + "end": 5, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 5, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 5, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 4, + "value": { + "raw": "foo", + "cooked": "foo" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal_with_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal_with_expression.json new file mode 100644 index 00000000000..e56eaf79a46 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/negative/template_literal_with_expression.json @@ -0,0 +1,52 @@ +{ + "expression": "`foo${bar}`", + "cursor": 10, + "ast": { + "type": "Program", + "start": 0, + "end": 11, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 11, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 11, + "expressions": [ + { + "type": "Identifier", + "start": 6, + "end": 9, + "name": "bar" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 4, + "value": { + "raw": "foo", + "cooked": "foo" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 10, + "end": 10, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/arrow_function_body.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/arrow_function_body.json new file mode 100644 index 00000000000..a51d9ac35b0 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/arrow_function_body.json @@ -0,0 +1,33 @@ +{ + "expression": "() => {", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "expression": { + "type": "ArrowFunctionExpression", + "start": 0, + "end": 7, + "id": null, + "params": [], + "generator": false, + "expression": false, + "async": false, + "body": { + "type": "BlockStatement", + "start": 6, + "end": 7, + "body": [] + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/binary_left_member_expression_literal.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/binary_left_member_expression_literal.json new file mode 100644 index 00000000000..6d86d276325 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/binary_left_member_expression_literal.json @@ -0,0 +1,37 @@ +{ + "expression": "x['] | 1", + "cursor": 2, + "ast": { + "type": "Program", + "start": 0, + "end": 8, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 8, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 8, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 2, + "end": 8, + "value": "] | 1", + "raw": "'] | 1" + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression.json new file mode 100644 index 00000000000..f2677a5ad96 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression.json @@ -0,0 +1,30 @@ +{ + "expression": "noop(", + "cursor": 4, + "ast": { + "type": "Program", + "start": 0, + "end": 5, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 5, + "expression": { + "type": "CallExpression", + "start": 0, + "end": 5, + "callee": { + "type": "Identifier", + "start": 0, + "end": 4, + "name": "noop" + }, + "arguments": [], + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_string_argument_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_string_argument_single_quote.json new file mode 100644 index 00000000000..9f04820cbae --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_string_argument_single_quote.json @@ -0,0 +1,38 @@ +{ + "expression": "foo(')", + "cursor": 4, + "ast": { + "type": "Program", + "start": 0, + "end": 6, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 6, + "expression": { + "type": "CallExpression", + "start": 0, + "end": 6, + "callee": { + "type": "Identifier", + "start": 0, + "end": 3, + "name": "foo" + }, + "arguments": [ + { + "type": "Literal", + "start": 4, + "end": 6, + "value": ")", + "raw": "')" + } + ], + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_template_argument_left_backtick.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_template_argument_left_backtick.json new file mode 100644 index 00000000000..97b46e5f2a6 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/call_expression_template_argument_left_backtick.json @@ -0,0 +1,66 @@ +{ + "expression": "foo(`)", + "cursor": 4, + "ast": { + "type": "Program", + "start": 0, + "end": 6, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 6, + "expression": { + "type": "CallExpression", + "start": 0, + "end": 6, + "callee": { + "type": "Identifier", + "start": 0, + "end": 3, + "name": "foo" + }, + "arguments": [ + { + "type": "TemplateLiteral", + "start": 4, + "end": 6, + "expressions": [ + { + "type": "Identifier", + "start": 6, + "end": 6, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 5, + "end": 6, + "value": { + "raw": ")", + "cooked": ")" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 6, + "end": 6, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + ], + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_body.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_body.json new file mode 100644 index 00000000000..9ee3d13e953 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_body.json @@ -0,0 +1,29 @@ +{ + "expression": "do {", + "cursor": 3, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "DoWhileStatement", + "start": 0, + "end": 4, + "body": { + "type": "BlockStatement", + "start": 3, + "end": 4, + "body": [] + }, + "test": { + "type": "Identifier", + "start": 4, + "end": 4, + "name": "✖" + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test.json new file mode 100644 index 00000000000..0668c8d1569 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test.json @@ -0,0 +1,29 @@ +{ + "expression": "do {} while (", + "cursor": 12, + "ast": { + "type": "Program", + "start": 0, + "end": 13, + "body": [ + { + "type": "DoWhileStatement", + "start": 0, + "end": 13, + "body": { + "type": "BlockStatement", + "start": 3, + "end": 5, + "body": [] + }, + "test": { + "type": "Identifier", + "start": 13, + "end": 13, + "name": "✖" + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test_left_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test_left_bracket.json new file mode 100644 index 00000000000..81bd83ae2a3 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/do_while_test_left_bracket.json @@ -0,0 +1,42 @@ +{ + "expression": "do {} while ( x[ )", + "cursor": 15, + "ast": { + "type": "Program", + "start": 0, + "end": 18, + "body": [ + { + "type": "DoWhileStatement", + "start": 0, + "end": 18, + "body": { + "type": "BlockStatement", + "start": 3, + "end": 5, + "body": [] + }, + "test": { + "type": "MemberExpression", + "start": 14, + "end": 17, + "object": { + "type": "Identifier", + "start": 14, + "end": 15, + "name": "x" + }, + "property": { + "type": "Identifier", + "start": 17, + "end": 17, + "name": "✖" + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_body.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_body.json new file mode 100644 index 00000000000..b019986cc3f --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_body.json @@ -0,0 +1,74 @@ +{ + "expression": "for ( i = 0; i < 10; i++ ) {", + "cursor": 27, + "ast": { + "type": "Program", + "start": 0, + "end": 28, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 28, + "init": { + "type": "AssignmentExpression", + "start": 6, + "end": 11, + "operator": "=", + "left": { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "i" + }, + "right": { + "type": "Literal", + "start": 10, + "end": 11, + "value": 0, + "raw": "0" + } + }, + "update": { + "type": "UpdateExpression", + "start": 21, + "end": 24, + "operator": "++", + "prefix": false, + "argument": { + "type": "Identifier", + "start": 21, + "end": 22, + "name": "i" + } + }, + "test": { + "type": "BinaryExpression", + "start": 13, + "end": 19, + "left": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "i" + }, + "operator": "<", + "right": { + "type": "Literal", + "start": 17, + "end": 19, + "value": 10, + "raw": "10" + } + }, + "body": { + "type": "BlockStatement", + "start": 27, + "end": 28, + "body": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_init.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_init.json new file mode 100644 index 00000000000..d9023ac5e8e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_init.json @@ -0,0 +1,30 @@ +{ + "expression": "for (", + "cursor": 4, + "ast": { + "type": "Program", + "start": 0, + "end": 5, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 5, + "init": { + "type": "Identifier", + "start": 5, + "end": 5, + "name": "✖" + }, + "update": null, + "test": null, + "body": { + "type": "EmptyStatement", + "start": 5, + "end": 5 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test.json new file mode 100644 index 00000000000..c9a322be08d --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test.json @@ -0,0 +1,73 @@ +{ + "expression": "for ( i = 0; i < x[", + "cursor": 18, + "ast": { + "type": "Program", + "start": 0, + "end": 19, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 19, + "init": { + "type": "AssignmentExpression", + "start": 6, + "end": 11, + "operator": "=", + "left": { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "i" + }, + "right": { + "type": "Literal", + "start": 10, + "end": 11, + "value": 0, + "raw": "0" + } + }, + "update": null, + "test": { + "type": "BinaryExpression", + "start": 13, + "end": 19, + "left": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "i" + }, + "operator": "<", + "right": { + "type": "MemberExpression", + "start": 17, + "end": 19, + "object": { + "type": "Identifier", + "start": 17, + "end": 18, + "name": "x" + }, + "property": { + "type": "Identifier", + "start": 19, + "end": 19, + "name": "✖" + }, + "computed": true, + "optional": false + } + }, + "body": { + "type": "EmptyStatement", + "start": 19, + "end": 19 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test_member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test_member_expression.json new file mode 100644 index 00000000000..f42bcba5081 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_test_member_expression.json @@ -0,0 +1,74 @@ +{ + "expression": "for ( i = 0; i < x[']", + "cursor": 19, + "ast": { + "type": "Program", + "start": 0, + "end": 21, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 21, + "init": { + "type": "AssignmentExpression", + "start": 6, + "end": 11, + "operator": "=", + "left": { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "i" + }, + "right": { + "type": "Literal", + "start": 10, + "end": 11, + "value": 0, + "raw": "0" + } + }, + "update": null, + "test": { + "type": "BinaryExpression", + "start": 13, + "end": 21, + "left": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "i" + }, + "operator": "<", + "right": { + "type": "MemberExpression", + "start": 17, + "end": 21, + "object": { + "type": "Identifier", + "start": 17, + "end": 18, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 19, + "end": 21, + "value": "]", + "raw": "']" + }, + "computed": true, + "optional": false + } + }, + "body": { + "type": "EmptyStatement", + "start": 21, + "end": 21 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update.json new file mode 100644 index 00000000000..133c55fbba6 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update.json @@ -0,0 +1,79 @@ +{ + "expression": "for ( i = 0; i < 10; x[", + "cursor": 22, + "ast": { + "type": "Program", + "start": 0, + "end": 23, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 23, + "init": { + "type": "AssignmentExpression", + "start": 6, + "end": 11, + "operator": "=", + "left": { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "i" + }, + "right": { + "type": "Literal", + "start": 10, + "end": 11, + "value": 0, + "raw": "0" + } + }, + "update": { + "type": "MemberExpression", + "start": 21, + "end": 23, + "object": { + "type": "Identifier", + "start": 21, + "end": 22, + "name": "x" + }, + "property": { + "type": "Identifier", + "start": 23, + "end": 23, + "name": "✖" + }, + "computed": true, + "optional": false + }, + "test": { + "type": "BinaryExpression", + "start": 13, + "end": 19, + "left": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "i" + }, + "operator": "<", + "right": { + "type": "Literal", + "start": 17, + "end": 19, + "value": 10, + "raw": "10" + } + }, + "body": { + "type": "EmptyStatement", + "start": 23, + "end": 23 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update_member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update_member_expression.json new file mode 100644 index 00000000000..b69479546d8 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/for_update_member_expression.json @@ -0,0 +1,80 @@ +{ + "expression": "for ( i = 0; i < 10; x[']", + "cursor": 23, + "ast": { + "type": "Program", + "start": 0, + "end": 25, + "body": [ + { + "type": "ForStatement", + "start": 0, + "end": 25, + "init": { + "type": "AssignmentExpression", + "start": 6, + "end": 11, + "operator": "=", + "left": { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "i" + }, + "right": { + "type": "Literal", + "start": 10, + "end": 11, + "value": 0, + "raw": "0" + } + }, + "update": { + "type": "MemberExpression", + "start": 21, + "end": 25, + "object": { + "type": "Identifier", + "start": 21, + "end": 22, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 23, + "end": 25, + "value": "]", + "raw": "']" + }, + "computed": true, + "optional": false + }, + "test": { + "type": "BinaryExpression", + "start": 13, + "end": 19, + "left": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "i" + }, + "operator": "<", + "right": { + "type": "Literal", + "start": 17, + "end": 19, + "value": 10, + "raw": "10" + } + }, + "body": { + "type": "EmptyStatement", + "start": 25, + "end": 25 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_body.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_body.json new file mode 100644 index 00000000000..497ea9a1489 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_body.json @@ -0,0 +1,33 @@ +{ + "expression": "function noop() {", + "cursor": 16, + "ast": { + "type": "Program", + "start": 0, + "end": 17, + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 17, + "id": { + "type": "Identifier", + "start": 9, + "end": 13, + "name": "noop" + }, + "params": [], + "generator": false, + "expression": false, + "async": false, + "body": { + "type": "BlockStatement", + "start": 16, + "end": 17, + "body": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_signature.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_signature.json new file mode 100644 index 00000000000..431b588a66b --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/function_declaration_signature.json @@ -0,0 +1,33 @@ +{ + "expression": "function noop(", + "cursor": 13, + "ast": { + "type": "Program", + "start": 0, + "end": 14, + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 14, + "id": { + "type": "Identifier", + "start": 9, + "end": 13, + "name": "noop" + }, + "params": [], + "generator": false, + "expression": false, + "async": false, + "body": { + "type": "BlockStatement", + "start": 14, + "end": 14, + "body": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_backtick.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_backtick.json new file mode 100644 index 00000000000..086b6b5f686 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_backtick.json @@ -0,0 +1,51 @@ +{ + "expression": "`", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 1, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 1, + "expressions": [ + { + "type": "Identifier", + "start": 1, + "end": 1, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_brace.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_brace.json new file mode 100644 index 00000000000..79e13548328 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_brace.json @@ -0,0 +1,18 @@ +{ + "expression": "{", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "BlockStatement", + "start": 0, + "end": 1, + "body": [] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_bracket.json new file mode 100644 index 00000000000..5873c89e4a5 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_bracket.json @@ -0,0 +1,23 @@ +{ + "expression": "[", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 1, + "expression": { + "type": "ArrayExpression", + "start": 0, + "end": 1, + "elements": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_double_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_double_quote.json new file mode 100644 index 00000000000..64af1833cca --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_double_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "\"", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 1, + "expression": { + "type": "Literal", + "start": 0, + "end": 1, + "value": "", + "raw": "\"" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_parenthesis.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_parenthesis.json new file mode 100644 index 00000000000..fd32590f710 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_parenthesis.json @@ -0,0 +1,17 @@ +{ + "expression": "(", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "EmptyStatement", + "start": 0, + "end": 1 + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_single_quote.json new file mode 100644 index 00000000000..c29e222e77e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/left_single_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "'", + "cursor": 0, + "ast": { + "type": "Program", + "start": 0, + "end": 1, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 1, + "expression": { + "type": "Literal", + "start": 0, + "end": 1, + "value": "", + "raw": "'" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_left_member_expression_literal.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_left_member_expression_literal.json new file mode 100644 index 00000000000..0a07673b39f --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_left_member_expression_literal.json @@ -0,0 +1,37 @@ +{ + "expression": "x['] || true", + "cursor": 2, + "ast": { + "type": "Program", + "start": 0, + "end": 12, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 12, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 12, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 2, + "end": 12, + "value": "] || true", + "raw": "'] || true" + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_member_expression.json new file mode 100644 index 00000000000..6b0deea4810 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_member_expression.json @@ -0,0 +1,54 @@ +{ + "expression": "x[ || y", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": + [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "expression": + { + "type": "MemberExpression", + "start": 0, + "end": 7, + "object": + { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": + { + "type": "LogicalExpression", + "start": 3, + "end": 7, + "left": + { + "type": "Identifier", + "start": 3, + "end": 3, + "name": "✖" + }, + "operator": "||", + "right": + { + "type": "Identifier", + "start": 6, + "end": 7, + "name": "y" + } + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_nested_member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_nested_member_expression.json new file mode 100644 index 00000000000..51f11052a85 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_left_nested_member_expression.json @@ -0,0 +1,87 @@ +{ + "expression": "x[ z.f[ ] || y[]", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 16, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 16, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 16, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "LogicalExpression", + "start": 3, + "end": 16, + "left": { + "type": "MemberExpression", + "start": 3, + "end": 9, + "object": { + "type": "MemberExpression", + "start": 3, + "end": 6, + "object": { + "type": "Identifier", + "start": 3, + "end": 4, + "name": "z" + }, + "property": { + "type": "Identifier", + "start": 5, + "end": 6, + "name": "f" + }, + "computed": false, + "optional": false + }, + "property": { + "type": "Identifier", + "start": 8, + "end": 8, + "name": "✖" + }, + "computed": true, + "optional": false + }, + "operator": "||", + "right": { + "type": "MemberExpression", + "start": 13, + "end": 16, + "object": { + "type": "Identifier", + "start": 13, + "end": 14, + "name": "y" + }, + "property": { + "type": "Identifier", + "start": 15, + "end": 15, + "name": "✖" + }, + "computed": true, + "optional": false + } + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_right_member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_right_member_expression.json new file mode 100644 index 00000000000..c2bd4dca564 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/logical_with_right_member_expression.json @@ -0,0 +1,54 @@ +{ + "expression": "x || x[", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": + [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "expression": + { + "type": "LogicalExpression", + "start": 0, + "end": 7, + "left": + { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "operator": "||", + "right": + { + "type": "MemberExpression", + "start": 5, + "end": 7, + "object": + { + "type": "Identifier", + "start": 5, + "end": 6, + "name": "x" + }, + "property": + { + "type": "Identifier", + "start": 7, + "end": 7, + "name": "✖" + }, + "computed": true, + "optional": false + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression.json new file mode 100644 index 00000000000..b8e3289eb41 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression.json @@ -0,0 +1,49 @@ +{ + "expression": "x[ = 5;", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 6, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "AssignmentExpression", + "start": 3, + "end": 6, + "operator": "=", + "left": { + "type": "Identifier", + "start": 3, + "end": 3, + "name": "✖" + }, + "right": { + "type": "Literal", + "start": 5, + "end": 6, + "value": 5, + "raw": "5" + } + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression_literal.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression_literal.json new file mode 100644 index 00000000000..327a1c8380e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_assignment_expression_literal.json @@ -0,0 +1,37 @@ +{ + "expression": "x['] = 5;", + "cursor": 2, + "ast": { + "type": "Program", + "start": 0, + "end": 9, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 9, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 9, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 2, + "end": 9, + "value": "] = 5;", + "raw": "'] = 5;" + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression.json new file mode 100644 index 00000000000..c2444e831aa --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression.json @@ -0,0 +1,37 @@ +{ + "expression": "x[ '", + "cursor": 3, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 4, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "Literal", + "start": 3, + "end": 4, + "value": "", + "raw": "'" + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression_call_expression.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression_call_expression.json new file mode 100644 index 00000000000..6352e2e99fe --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/member_expression_call_expression.json @@ -0,0 +1,43 @@ +{ + "expression": "x[ foo( ]", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 9, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 9, + "expression": { + "type": "MemberExpression", + "start": 0, + "end": 9, + "object": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "property": { + "type": "CallExpression", + "start": 3, + "end": 9, + "callee": { + "type": "Identifier", + "start": 3, + "end": 6, + "name": "foo" + }, + "arguments": [], + "optional": false + }, + "computed": true, + "optional": false + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/object_expression_left_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/object_expression_left_single_quote.json new file mode 100644 index 00000000000..5ee0164eb41 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/object_expression_left_single_quote.json @@ -0,0 +1,59 @@ +{ + "expression": "x = {'}", + "cursor": 5, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 7, + "expression": { + "type": "AssignmentExpression", + "start": 0, + "end": 7, + "operator": "=", + "left": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "right": { + "type": "ObjectExpression", + "start": 4, + "end": 7, + "properties": [ + { + "type": "Property", + "start": 5, + "end": 7, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Literal", + "start": 5, + "end": 7, + "value": "}", + "raw": "'}" + }, + "kind": "init", + "value": { + "type": "Literal", + "start": 5, + "end": 7, + "value": "}", + "raw": "'}" + } + } + ] + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_body_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_body_single_quote.json new file mode 100644 index 00000000000..99cd06dea64 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_body_single_quote.json @@ -0,0 +1,42 @@ +{ + "expression": "var x = /'/;", + "cursor": 9, + "ast": { + "type": "Program", + "start": 0, + "end": 12, + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 12, + "kind": "var", + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 11, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "name": "x" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 11, + "regex": { + "pattern": "'", + "flags": "" + }, + "value": {}, + "raw": "/'/" + } + } + ] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_literal_left_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_literal_left_bracket.json new file mode 100644 index 00000000000..7a9258a530e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_literal_left_bracket.json @@ -0,0 +1,36 @@ +{ + "expression": "x = /[/;", + "cursor": 5, + "ast": { + "type": "Program", + "start": 0, + "end": 8, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 8, + "expression": { + "type": "AssignmentExpression", + "start": 0, + "end": 8, + "operator": "=", + "left": { + "type": "Identifier", + "start": 0, + "end": 1, + "name": "x" + }, + "right": { + "type": "Literal", + "start": 5, + "end": 8, + "regex": {}, + "raw": "[/;" + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_single_quote.json new file mode 100644 index 00000000000..b36f28b6210 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/regexp_single_quote.json @@ -0,0 +1,40 @@ +{ + "expression": "var x = /'", + "cursor": 9, + "ast": { + "type": "Program", + "start": 0, + "end": 10, + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 10, + "kind": "var", + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 10, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "name": "x" + }, + "init": { + "type": "Literal", + "start": 9, + "end": 10, + "regex": { + "flags": "" + }, + "raw": "'" + } + } + ] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template.json new file mode 100644 index 00000000000..3eb3b3f2d69 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template.json @@ -0,0 +1,62 @@ +{ + "expression": "foo`", + "cursor": 3, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "TaggedTemplateExpression", + "start": 0, + "end": 4, + "tag": { + "type": "Identifier", + "start": 0, + "end": 3, + "name": "foo" + }, + "quasi": { + "type": "TemplateLiteral", + "start": 3, + "end": 4, + "expressions": [ + { + "type": "Identifier", + "start": 4, + "end": 4, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 4, + "end": 4, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 4, + "end": 4, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template_left_backtick.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template_left_backtick.json new file mode 100644 index 00000000000..0131a265703 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/tagged_template_left_backtick.json @@ -0,0 +1,91 @@ +{ + "expression": "`foo${bar}``", + "cursor": 11, + "ast": { + "type": "Program", + "start": 0, + "end": 12, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 12, + "expression": { + "type": "TaggedTemplateExpression", + "start": 0, + "end": 12, + "tag": { + "type": "TemplateLiteral", + "start": 0, + "end": 11, + "expressions": [ + { + "type": "Identifier", + "start": 6, + "end": 9, + "name": "bar" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 4, + "value": { + "raw": "foo", + "cooked": "foo" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 10, + "end": 10, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + }, + "quasi": { + "type": "TemplateLiteral", + "start": 11, + "end": 12, + "expressions": [ + { + "type": "Identifier", + "start": 12, + "end": 12, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 12, + "end": 12, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 12, + "end": 12, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_brace.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_brace.json new file mode 100644 index 00000000000..eeb39b7ded8 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_brace.json @@ -0,0 +1,105 @@ +{ + "expression": "`${foo({)}`", + "cursor": 7, + "ast": { + "type": "Program", + "start": 0, + "end": 11, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 11, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 11, + "expressions": [ + { + "type": "CallExpression", + "start": 3, + "end": 11, + "callee": { + "type": "Identifier", + "start": 3, + "end": 6, + "name": "foo" + }, + "arguments": [ + { + "type": "TaggedTemplateExpression", + "start": 7, + "end": 11, + "tag": { + "type": "ObjectExpression", + "start": 7, + "end": 10, + "properties": [] + }, + "quasi": { + "type": "TemplateLiteral", + "start": 10, + "end": 11, + "expressions": [ + { + "type": "Identifier", + "start": 11, + "end": 11, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "optional": false + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_bracket.json new file mode 100644 index 00000000000..cd630a99ac6 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_bracket.json @@ -0,0 +1,101 @@ +{ + "expression": "`${foo([)}`", + "cursor": 7, + "ast": { + "type": "Program", + "start": 0, + "end": 11, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 11, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 11, + "expressions": [ + { + "type": "CallExpression", + "start": 3, + "end": 11, + "callee": { + "type": "Identifier", + "start": 3, + "end": 6, + "name": "foo" + }, + "arguments": [ + { + "type": "ArrayExpression", + "start": 7, + "end": 11, + "elements": [ + { + "type": "TemplateLiteral", + "start": 10, + "end": 11, + "expressions": [ + { + "type": "Identifier", + "start": 11, + "end": 11, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + ] + } + ], + "optional": false + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_double_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_double_quote.json new file mode 100644 index 00000000000..6effaaac5ae --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_double_quote.json @@ -0,0 +1,67 @@ +{ + "expression": "`${foo(\")}`", + "cursor": 7, + "ast": { + "type": "Program", + "start": 0, + "end": 11, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 11, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 11, + "expressions": [ + { + "type": "CallExpression", + "start": 3, + "end": 11, + "callee": { + "type": "Identifier", + "start": 3, + "end": 6, + "name": "foo" + }, + "arguments": [ + { + "type": "Literal", + "start": 7, + "end": 11, + "value": ")}`", + "raw": "\")}`" + } + ], + "optional": false + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 11, + "end": 11, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_parenthesis.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_parenthesis.json new file mode 100644 index 00000000000..00230e25a7e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/template_literal_expression_with_left_parenthesis.json @@ -0,0 +1,94 @@ +{ + "expression": "`${foo(}`", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 9, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 9, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 9, + "expressions": [ + { + "type": "CallExpression", + "start": 3, + "end": 9, + "callee": { + "type": "Identifier", + "start": 3, + "end": 6, + "name": "foo" + }, + "arguments": [ + { + "type": "TemplateLiteral", + "start": 8, + "end": 9, + "expressions": [ + { + "type": "Identifier", + "start": 9, + "end": 9, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 9, + "end": 9, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 9, + "end": 9, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + ], + "optional": false + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 9, + "end": 9, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/three_backticks.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/three_backticks.json new file mode 100644 index 00000000000..0f8a68a7d99 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/three_backticks.json @@ -0,0 +1,74 @@ +{ + "expression": "```", + "cursor": 2, + "ast": { + "type": "Program", + "start": 0, + "end": 3, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 3, + "expression": { + "type": "TaggedTemplateExpression", + "start": 0, + "end": 3, + "tag": { + "type": "TemplateLiteral", + "start": 0, + "end": 2, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + }, + "quasi": { + "type": "TemplateLiteral", + "start": 2, + "end": 3, + "expressions": [ + { + "type": "Identifier", + "start": 3, + "end": 3, + "name": "✖" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 3, + "end": 3, + "value": { + "raw": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 3, + "end": 3, + "value": { + "cooked": "", + "raw": "" + }, + "tail": true + } + ] + } + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/variable_declaration_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/variable_declaration_string.json new file mode 100644 index 00000000000..d45a0a9e8b6 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/variable_declaration_string.json @@ -0,0 +1,38 @@ +{ + "expression": "var x = '", + "cursor": 8, + "ast": { + "type": "Program", + "start": 0, + "end": 9, + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 9, + "kind": "var", + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 9, + "id": { + "type": "Identifier", + "start": 4, + "end": 5, + "name": "x" + }, + "init": { + "type": "Literal", + "start": 8, + "end": 9, + "value": "", + "raw": "'" + } + } + ] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_body.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_body.json new file mode 100644 index 00000000000..2fd33cd0ffc --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_body.json @@ -0,0 +1,30 @@ +{ + "expression": "while ( true ) {", + "cursor": 15, + "ast": { + "type": "Program", + "start": 0, + "end": 16, + "body": [ + { + "type": "WhileStatement", + "start": 0, + "end": 16, + "test": { + "type": "Literal", + "start": 8, + "end": 12, + "value": true, + "raw": "true" + }, + "body": { + "type": "BlockStatement", + "start": 15, + "end": 16, + "body": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_test.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_test.json new file mode 100644 index 00000000000..944a75ba9ee --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/positive/while_test.json @@ -0,0 +1,28 @@ +{ + "expression": "while (", + "cursor": 6, + "ast": { + "type": "Program", + "start": 0, + "end": 7, + "body": [ + { + "type": "WhileStatement", + "start": 0, + "end": 7, + "test": { + "type": "Identifier", + "start": 7, + "end": 7, + "name": "✖" + }, + "body": { + "type": "EmptyStatement", + "start": 7, + "end": 7 + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/script.js b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/script.js new file mode 100644 index 00000000000..461d30d7c66 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-close-pairs/script.js @@ -0,0 +1,63 @@ +#!/usr/bin/env node + +/** +* @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'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var parse = require( 'acorn-loose' ).parse; +var writeFile = require( '@stdlib/fs/write-file' ).sync; + + +// VARIABLES // + +var AOPTS = { + 'ecmaVersion': 'latest' +}; +var FOPTS = { + 'encoding': 'utf8' +}; + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var str; + var ast; + var out; + + str = 'x = /[/;'; + ast = parse( str, AOPTS ); + + out = { + 'expression': str, + 'cursor': str.length-3, + 'ast': ast + }; + writeFile( resolve( __dirname, 'positive', 'regexp_literal_left_bracket.json' ), JSON.stringify( out, null, ' ' )+'\n', FOPTS ); +} + +main(); diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/array_expression_left_bracket_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/array_expression_left_bracket_within_string.json new file mode 100644 index 00000000000..6b594194e43 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/array_expression_left_bracket_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "'[]'", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "[]", + "raw": "'[]'" + }, + "directive": "[]" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_backtick_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_backtick_within_string.json new file mode 100644 index 00000000000..d5eb5b4bbef --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_backtick_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "'``'", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "``", + "raw": "'``'" + }, + "directive": "``" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_double_quote_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_double_quote_within_string.json new file mode 100644 index 00000000000..ff10a879931 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_double_quote_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "'\"\"'", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "\"\"", + "raw": "'\"\"'" + }, + "directive": "\"\"" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_parenthesis_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_parenthesis_within_string.json new file mode 100644 index 00000000000..828cc79f114 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_parenthesis_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "'()'", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "()", + "raw": "'()'" + }, + "directive": "()" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_single_quote_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_single_quote_within_string.json new file mode 100644 index 00000000000..d4adf591ae0 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/left_single_quote_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "\"''\"", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "''", + "raw": "\"''\"" + }, + "directive": "''" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/object_expression_left_brace_within_string.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/object_expression_left_brace_within_string.json new file mode 100644 index 00000000000..b21b0721757 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/negative/object_expression_left_brace_within_string.json @@ -0,0 +1,25 @@ +{ + "expression": "'{}'", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 4, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 4, + "expression": { + "type": "Literal", + "start": 0, + "end": 4, + "value": "{}", + "raw": "'{}'" + }, + "directive": "{}" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/array_expression_left_bracket.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/array_expression_left_bracket.json new file mode 100644 index 00000000000..08cd5bda846 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/array_expression_left_bracket.json @@ -0,0 +1,23 @@ +{ + "expression": "[]", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "ArrayExpression", + "start": 0, + "end": 2, + "elements": [] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_backtick.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_backtick.json new file mode 100644 index 00000000000..5348d6d37f8 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_backtick.json @@ -0,0 +1,35 @@ +{ + "expression": "``", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 2, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_double_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_double_quote.json new file mode 100644 index 00000000000..9639e899d39 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_double_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "\"\"", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "Literal", + "start": 0, + "end": 2, + "value": "", + "raw": "\"\"" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_parenthesis.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_parenthesis.json new file mode 100644 index 00000000000..b73b4e93051 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_parenthesis.json @@ -0,0 +1,17 @@ +{ + "expression": "()", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "EmptyStatement", + "start": 0, + "end": 2 + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_single_quote.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_single_quote.json new file mode 100644 index 00000000000..d35d8a7ee2c --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/left_single_quote.json @@ -0,0 +1,25 @@ +{ + "expression": "''", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 2, + "expression": { + "type": "Literal", + "start": 0, + "end": 2, + "value": "", + "raw": "''" + }, + "directive": "" + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/object_expression_left_brace.json b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/object_expression_left_brace.json new file mode 100644 index 00000000000..af0eda1167e --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/positive/object_expression_left_brace.json @@ -0,0 +1,18 @@ +{ + "expression": "{}", + "cursor": 1, + "ast": { + "type": "Program", + "start": 0, + "end": 2, + "body": [ + { + "type": "BlockStatement", + "start": 0, + "end": 2, + "body": [] + } + ], + "sourceType": "script" + } +} diff --git a/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/script.js b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/script.js new file mode 100644 index 00000000000..2fdb45c2398 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/auto-delete-pairs/script.js @@ -0,0 +1,63 @@ +#!/usr/bin/env node + +/** +* @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'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var parse = require( 'acorn-loose' ).parse; +var writeFile = require( '@stdlib/fs/write-file' ).sync; + + +// VARIABLES // + +var AOPTS = { + 'ecmaVersion': 'latest' +}; +var FOPTS = { + 'encoding': 'utf8' +}; + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var str; + var ast; + var out; + + str = '``'; + ast = parse( str, AOPTS ); + + out = { + 'expression': str, + 'cursor': 1, + 'ast': ast + }; + writeFile( resolve( __dirname, 'positive', 'left_backtick.json' ), JSON.stringify( out, null, ' ' )+'\n', FOPTS ); +} + +main(); diff --git a/lib/node_modules/@stdlib/repl/test/fixtures/repl.js b/lib/node_modules/@stdlib/repl/test/integration/fixtures/repl.js similarity index 98% rename from lib/node_modules/@stdlib/repl/test/fixtures/repl.js rename to lib/node_modules/@stdlib/repl/test/integration/fixtures/repl.js index 7e702c4f037..d59238dd217 100644 --- a/lib/node_modules/@stdlib/repl/test/fixtures/repl.js +++ b/lib/node_modules/@stdlib/repl/test/integration/fixtures/repl.js @@ -24,7 +24,7 @@ var isFunction = require( '@stdlib/assert/is-function' ); var inspectSinkStream = require( '@stdlib/streams/node/inspect-sink' ); var assign = require( '@stdlib/object/assign' ); var format = require( '@stdlib/string/format' ); -var REPL = require( './../../lib' ); +var REPL = require( './../../../lib' ); // FUNCTIONS // diff --git a/lib/node_modules/@stdlib/repl/test/integration/test.auto_close_pairs.js b/lib/node_modules/@stdlib/repl/test/integration/test.auto_close_pairs.js new file mode 100644 index 00000000000..5c652ad58b3 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/test.auto_close_pairs.js @@ -0,0 +1,361 @@ +/** +* @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'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var tape = require( 'tape' ); +var DebugStream = require( '@stdlib/streams/node/debug' ); +var trim = require( '@stdlib/string/trim' ); +var replace = require( '@stdlib/string/replace' ); +var readDir = require( '@stdlib/fs/read-dir' ).sync; +var format = require( '@stdlib/string/format' ); +var OPEN_SYMBOLS = require( './../../lib/auto_close_pairs_open_symbols.js' ); +var repl = require( './fixtures/repl.js' ); + + +// VARIABLES // + +var RE_JSON = /\.json$/; + +var RE_ANSI = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g; // eslint-disable-line no-control-regex + +var POSITIVE_FIXTURES_DIR = resolve( __dirname, 'fixtures', 'auto-close-pairs', 'positive' ); +var POSITIVE_FIXTURES_FILES = filter( readDir( POSITIVE_FIXTURES_DIR ) ); + +var NEGATIVE_FIXTURES_DIR = resolve( __dirname, 'fixtures', 'auto-close-pairs', 'negative' ); +var NEGATIVE_FIXTURES_FILES = filter( readDir( NEGATIVE_FIXTURES_DIR ) ); + + +// FUNCTIONS // + +/** +* Filters a list of files for those having a `*.json` file extension. +* +* @private +* @param {Array} list - file list +* @returns {Array} filtered list +*/ +function filter( list ) { + var out; + var i; + + out = []; + for ( i = 0; i < list.length; i++ ) { + if ( RE_JSON.test( list[ i ] ) ) { + out.push( list[ i ] ); + } + } + return out; +} + +/** +* Splices a string. +* +* @private +* @param {string} str - input string +* @param {NonNegativeInteger} idx - index at which to splice +* @returns {Array} array containing the resulting string and the removed character +*/ +function splice( str, idx ) { + var parts; + var out; + + parts = [ + str.substring( 0, idx ), + str.substring( idx+1 ) + ]; + out = parts[ 0 ] + parts[ 1 ]; + return [ out, str[ idx ], parts ]; +} + +/** +* Removes ANSI escape codes from a string. +* +* @private +* @param {string} str - input string +* @returns {string} string with ANSI escape codes removed +*/ +function stripANSI( str ) { + return replace( str, RE_ANSI, '' ); +} + +/** +* Processes output stream text and returns a string representing what is currently displayed in the REPL. +* +* ## Notes +* +* - We can rely on simple concatenation when a test expression proceeds from left-to-right (i.e., as if a user normally types); however, when a user backtracks (e.g., by moving the cursor to the left), the REPL needs to refresh the displayed text (in full) in order to shift any text after the cursor to the right to make room for inserted characters. +* +* @private +* @param {Array} raw - unprocessed output data +* @param {boolean} flg - boolean indicating whether to concatenate unprocessed output data +* @returns {string} output string +*/ +function processOutput( raw, flg ) { + var i; + if ( flg ) { + return trim( stripANSI( raw.join( '' ) ) ); + } + for ( i = raw.length-1; i >= 0; i-- ) { + // Check whether the screen display was erased, as the next element is the refreshed line... + if ( raw[ i ] === '\u001b[0J' ) { + return raw[ i+1 ]; + } + } +} + +/** +* Converts splice results to an expected output string. +* +* @private +* @param {Array} s - splice results +* @returns {string} expected output string +*/ +function spliced2expected( s ) { + return s[ 2 ][ 0 ] + s[ 1 ] + OPEN_SYMBOLS[ s[1] ] + s[ 2 ][ 1 ]; +} + +/** +* Returns default settings. +* +* @private +* @returns {Object} default settings +*/ +function defaultSettings() { + return { + 'autoClosePairs': false, + 'autoDeletePairs': false, + 'completionPreviews': false + }; +} + +/** +* Moves a cursor backward a specified number of positions. +* +* @private +* @param {WriteStream} stream - writable stream +* @param {NonNegativeInteger} N - number of positions +*/ +function moveBack( stream, N ) { + var i; + for ( i = 0; i < N; i++ ) { + stream.write( '\u001b[1D' ); + } +} + +/** +* Asserts that a provided expression triggers expected automatic insertion. +* +* @private +* @param {Object} t - test object +* @param {Object} fixture - fixture object +* @param {string} fixture.expression - incomplete expression string +* @param {NonNegativeInteger} fixture.cursor - cursor position for inserting a character to trigger an insertion +* @param {Function} done - callback to invoke upon completion +*/ +function assertAutoClose( t, fixture, done ) { + var expected; + var istream; + var opts; + var N; + var r; + var s; + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + // Create an incomplete expression: + s = splice( fixture.expression, fixture.cursor ); + + // Construct the expected output: + expected = spliced2expected( s ); + + // Emulate the presence of an existing expression: + istream.write( s[ 0 ] ); + + // Move the cursor to where we want to insert a character to trigger auto-close: + N = s[ 0 ].length - fixture.cursor; + moveBack( istream, N ); + + // Enable auto-close: + r.settings( 'autoClosePairs', true ); + + // Insert the character in order to trigger auto-close: + istream.write( s[ 1 ] ); + + // Close the input stream: + istream.end(); + + // Close the REPL: + r.close(); + + function onClose( error, data ) { + var actual; + if ( error ) { + t.fail( error.message ); + return; + } + actual = processOutput( data, N === 0 ); + t.strictEqual( actual, expected, 'returns expected value' ); + done(); + } +} + +/** +* Asserts that a provided expression does not trigger automatic insertion. +* +* @private +* @param {Object} t - test object +* @param {Object} fixture - fixture object +* @param {string} fixture.expression - incomplete expression string +* @param {NonNegativeInteger} fixture.cursor - cursor position for inserting a character to trigger an insertion +* @param {Function} done - callback to invoke upon completion +*/ +function assertNoAutoClose( t, fixture, done ) { + var expected; + var istream; + var opts; + var N; + var r; + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + // Construct the expected output: + expected = fixture.expression; + + // Emulate the presence of an existing expression: + istream.write( fixture.expression ); + + // Move the cursor to where we want to insert a character: + N = fixture.expression.length - fixture.cursor; + moveBack( istream, N ); + + // Enable auto-close: + r.settings( 'autoClosePairs', true ); + + // Insert the character in order to trigger auto-close: + istream.write( fixture.expression[ fixture.cursor ] ); + + // Close the input stream: + istream.end(); + + // Close the REPL: + r.close(); + + function onClose( error, data ) { + var actual; + if ( error ) { + t.fail( error.message ); + return; + } + actual = processOutput( data, N === 0 ); + t.strictEqual( actual, expected, 'returns expected value' ); + done(); + } +} + +/** +* Generates a test name from a fixture file name. +* +* @private +* @param {string} msg - test description +* @param {string} filename - file name +* @returns {string} test name +*/ +function testName( msg, filename ) { + var str = replace( filename, RE_JSON, '' ); + str = replace( str, '_', ' ' ); + return format( '%s (%s)', msg, str ); +} + +/** +* Returns a test function for testing against a specified fixture file. +* +* @private +* @param {string} fpath - fixture file path +* @param {Function} assert - assertion function +* @returns {Function} test function +*/ +function testFcn( fpath, assert ) { + return test; + + function test( t ) { + var fixture = require( fpath ); // eslint-disable-line stdlib/no-dynamic-require + assert( t, fixture, done ); + + function done() { + t.end(); + } + } +} + +/** +* Run tests against test fixtures. +* +* @private +* @param {string} msg - test description +* @param {string} dir - fixtures directory +* @param {Array} fixtures - list of fixtures +* @param {Function} assert - assert function +*/ +function test( msg, dir, fixtures, assert ) { + var fpath; + var f; + var t; + var i; + + for ( i = 0; i < fixtures.length; i++ ) { + f = fixtures[ i ]; + t = testName( msg, f ); + fpath = resolve( dir, f ); + tape( t, testFcn( fpath, assert ) ); + } +} + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof repl, 'function', 'main export is a function' ); + t.end(); +}); + +test( 'a REPL instance supports automatically closing paired symbols', POSITIVE_FIXTURES_DIR, POSITIVE_FIXTURES_FILES, assertAutoClose ); + +test( 'a REPL instance avoids unnecessarily inserting closing symbols', NEGATIVE_FIXTURES_DIR, NEGATIVE_FIXTURES_FILES, assertNoAutoClose ); diff --git a/lib/node_modules/@stdlib/repl/test/integration/test.auto_delete_pairs.js b/lib/node_modules/@stdlib/repl/test/integration/test.auto_delete_pairs.js new file mode 100644 index 00000000000..73c0858ad89 --- /dev/null +++ b/lib/node_modules/@stdlib/repl/test/integration/test.auto_delete_pairs.js @@ -0,0 +1,349 @@ +/** +* @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'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var tape = require( 'tape' ); +var DebugStream = require( '@stdlib/streams/node/debug' ); +var trim = require( '@stdlib/string/trim' ); +var replace = require( '@stdlib/string/replace' ); +var readDir = require( '@stdlib/fs/read-dir' ).sync; +var format = require( '@stdlib/string/format' ); +var repl = require( './fixtures/repl.js' ); + + +// VARIABLES // + +var RE_JSON = /\.json$/; + +var RE_ANSI = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g; // eslint-disable-line no-control-regex + +var POSITIVE_FIXTURES_DIR = resolve( __dirname, 'fixtures', 'auto-delete-pairs', 'positive' ); +var POSITIVE_FIXTURES_FILES = filter( readDir( POSITIVE_FIXTURES_DIR ) ); + +var NEGATIVE_FIXTURES_DIR = resolve( __dirname, 'fixtures', 'auto-delete-pairs', 'negative' ); +var NEGATIVE_FIXTURES_FILES = filter( readDir( NEGATIVE_FIXTURES_DIR ) ); + + +// FUNCTIONS // + +/** +* Filters a list of files for those having a `*.json` file extension. +* +* @private +* @param {Array} list - file list +* @returns {Array} filtered list +*/ +function filter( list ) { + var out; + var i; + + out = []; + for ( i = 0; i < list.length; i++ ) { + if ( RE_JSON.test( list[ i ] ) ) { + out.push( list[ i ] ); + } + } + return out; +} + +/** +* Removes ANSI escape codes from a string. +* +* @private +* @param {string} str - input string +* @returns {string} string with ANSI escape codes removed +*/ +function stripANSI( str ) { + return replace( str, RE_ANSI, '' ); +} + +/** +* Processes output stream text and returns a string representing what is currently displayed in the REPL. +* +* ## Notes +* +* - We can rely on simple concatenation when a test expression proceeds from left-to-right (i.e., as if a user normally types); however, when a user backtracks (e.g., by moving the cursor to the left), the REPL needs to refresh the displayed text (in full) in order to shift any text after the cursor to the right to make room for inserted characters. +* +* @private +* @param {Array} raw - unprocessed output data +* @param {boolean} flg - boolean indicating whether to concatenate unprocessed output data +* @returns {string} output string +*/ +function processOutput( raw, flg ) { + var i; + if ( flg ) { + return trim( stripANSI( raw.join( '' ) ) ); + } + for ( i = raw.length-1; i >= 0; i-- ) { + // Check whether the screen display was erased, as the next element is the refreshed line... + if ( raw[ i ] === '\u001b[0J' ) { + return raw[ i+1 ]; + } + } +} + +/** +* Returns default settings. +* +* @private +* @returns {Object} default settings +*/ +function defaultSettings() { + return { + 'autoClosePairs': false, + 'autoDeletePairs': false, + 'completionPreviews': false + }; +} + +/** +* Moves a cursor backward a specified number of positions. +* +* @private +* @param {WriteStream} stream - writable stream +* @param {NonNegativeInteger} N - number of positions +*/ +function moveBack( stream, N ) { + var i; + for ( i = 0; i < N; i++ ) { + stream.write( '\u001b[1D' ); + } +} + +/** +* Asserts that a provided expression triggers expected automatic deletion of closed symbols. +* +* @private +* @param {Object} t - test object +* @param {Object} fixture - fixture object +* @param {string} fixture.expression - incomplete expression string +* @param {NonNegativeInteger} fixture.cursor - cursor position for deleting a character +* @param {Function} done - callback to invoke upon completion +*/ +function assertAutoDelete( t, fixture, done ) { + var expected; + var istream; + var opts; + var N; + var r; + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + // Construct the expected output: + expected = fixture.expression.substring( 0, fixture.cursor-1 ); + expected += fixture.expression.substring( fixture.cursor+1 ); + + // Emulate the presence of an existing expression: + istream.write( fixture.expression ); + + // Move the cursor to where we want to delete a character: + N = fixture.expression.length - fixture.cursor; + moveBack( istream, N ); + + // Enable auto-delete: + r.settings( 'autoDeletePairs', true ); + + // Delete the character in order to trigger auto-delete: + istream.write( '\u0008' ); + + // Close the input stream: + istream.end(); + + // Close the REPL: + r.close(); + + function onClose( error, data ) { + var actual; + if ( error ) { + t.fail( error.message ); + return; + } + actual = processOutput( data, N === 0 ); + t.strictEqual( actual, expected, 'returns expected value' ); + done(); + } +} + +/** +* Asserts that a provided expression does not trigger automatic deletion of closed symbols. +* +* @private +* @param {Object} t - test object +* @param {Object} fixture - fixture object +* @param {string} fixture.expression - incomplete expression string +* @param {NonNegativeInteger} fixture.cursor - cursor position for deleting a character +* @param {Function} done - callback to invoke upon completion +*/ +function assertNoAutoDelete( t, fixture, done ) { + var expected; + var istream; + var opts; + var N; + var r; + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + istream = new DebugStream({ + 'name': 'repl-input-stream' + }); + opts = { + 'input': istream, + 'inputPrompt': '', + 'quiet': true, + 'settings': defaultSettings() + }; + r = repl( opts, onClose ); + + // Construct the expected output: + expected = fixture.expression.substring( 0, fixture.cursor-1 ); + expected += fixture.expression.substring( fixture.cursor ); + + // Emulate the presence of an existing expression: + istream.write( fixture.expression ); + + // Move the cursor to where we want to delete a character: + N = fixture.expression.length - fixture.cursor; + moveBack( istream, N ); + + // Enable auto-delete: + r.settings( 'autoDeletePairs', true ); + + // Delete the character in order to trigger auto-delete: + istream.write( '\u0008' ); + + // Close the input stream: + istream.end(); + + // Close the REPL: + r.close(); + + function onClose( error, data ) { + var actual; + if ( error ) { + t.fail( error.message ); + return; + } + actual = processOutput( data, N === 0 ); + t.strictEqual( actual, expected, 'returns expected value' ); + done(); + } +} + +/** +* Generates a test name from a fixture file name. +* +* @private +* @param {string} msg - test description +* @param {string} filename - file name +* @returns {string} test name +*/ +function testName( msg, filename ) { + var str = replace( filename, RE_JSON, '' ); + str = replace( str, '_', ' ' ); + return format( '%s (%s)', msg, str ); +} + +/** +* Returns a test function for testing against a specified fixture file. +* +* @private +* @param {string} fpath - fixture file path +* @param {Function} assert - assertion function +* @returns {Function} test function +*/ +function testFcn( fpath, assert ) { + return test; + + function test( t ) { + var fixture = require( fpath ); // eslint-disable-line stdlib/no-dynamic-require + assert( t, fixture, done ); + + function done() { + t.end(); + } + } +} + +/** +* Run tests against test fixtures. +* +* @private +* @param {string} msg - test description +* @param {string} dir - fixtures directory +* @param {Array} fixtures - list of fixtures +* @param {Function} assert - assert function +*/ +function test( msg, dir, fixtures, assert ) { + var fpath; + var f; + var t; + var i; + + for ( i = 0; i < fixtures.length; i++ ) { + f = fixtures[ i ]; + t = testName( msg, f ); + fpath = resolve( dir, f ); + tape( t, testFcn( fpath, assert ) ); + } +} + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof repl, 'function', 'main export is a function' ); + t.end(); +}); + +test( 'a REPL instance supports automatically deleting paired symbols', POSITIVE_FIXTURES_DIR, POSITIVE_FIXTURES_FILES, assertAutoDelete ); + +test( 'a REPL instance avoids auto-deletion when symbols are within a string literal', NEGATIVE_FIXTURES_DIR, NEGATIVE_FIXTURES_FILES, assertNoAutoDelete ); diff --git a/lib/node_modules/@stdlib/repl/test/integration/test.completion_previews.js b/lib/node_modules/@stdlib/repl/test/integration/test.completion_previews.js index 6cda8239274..5fbb90da1d4 100644 --- a/lib/node_modules/@stdlib/repl/test/integration/test.completion_previews.js +++ b/lib/node_modules/@stdlib/repl/test/integration/test.completion_previews.js @@ -23,7 +23,7 @@ var tape = require( 'tape' ); var DebugStream = require( '@stdlib/streams/node/debug' ); var trim = require( '@stdlib/string/trim' ); -var repl = require( './../fixtures/repl.js' ); +var repl = require( './fixtures/repl.js' ); // TESTS //