Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add support for auto-closing brackets/quotations in the REPL #1680

Merged
merged 95 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
3431b3c
feat: add auto-closing brackets & quotations in REPL
Snehil-Shah Mar 3, 2024
81d3b7d
feat: add option to toggle auto-closing behavior
Snehil-Shah Mar 3, 2024
3fbdd4e
refactor: prevent use of private readline methods
Snehil-Shah Mar 5, 2024
3f5f685
feat: add generic `settings` command to manage all settings
Snehil-Shah Mar 5, 2024
a6bc4e2
fix: remove autoClose from defaults
Snehil-Shah Mar 5, 2024
d8716d7
fix: remove `autoClose` from REPL opts
Snehil-Shah Mar 5, 2024
3ed9eff
fix: error messages
Snehil-Shah Mar 5, 2024
7b7009b
feat: manual multiline mode from cursor position
Snehil-Shah Mar 8, 2024
d0c66c6
remove unused flag
Snehil-Shah Mar 9, 2024
8278728
feat: handle instinctive closing symbols for improved ergonomics
Snehil-Shah Mar 13, 2024
e4884e5
refactor: suggestions
Snehil-Shah Mar 19, 2024
1202732
feat: add auto indentation
Snehil-Shah Mar 19, 2024
db50041
feat: handle special cases to improve ergonomics
Snehil-Shah Mar 20, 2024
15821a5
Merge branch 'develop' of https://github.com/stdlib-js/stdlib into sn…
kgryte Mar 27, 2024
db29963
fix: consolidate keypress listeners
kgryte Mar 27, 2024
19ef85b
refactor: always attach a keypress listener
kgryte Mar 27, 2024
6b865c9
refactor: document `settings` command signatures
kgryte Mar 27, 2024
35ec9b7
refactor: document defaults and add settings object
kgryte Mar 27, 2024
357431a
refactor: add module exposing settings validators
kgryte Mar 27, 2024
6e75e67
refactor: add module to validate a settings object
kgryte Mar 27, 2024
85687d3
fix: update error messages
kgryte Mar 27, 2024
89f9802
fix: add missing settings file
kgryte Mar 27, 2024
e70d3db
docs: update comment
kgryte Mar 27, 2024
8c5f3cd
refactor: add support for validating a settings object
kgryte Mar 27, 2024
6021d0f
refactor: remove duplicate option setting and document settings
kgryte Mar 27, 2024
4ea8a03
refactor: make the default dependent on whether TTY
kgryte Mar 27, 2024
5786c14
style: disable lint rules
kgryte Mar 27, 2024
3747888
docs: document settings
kgryte Mar 27, 2024
1595697
refactor: add module exporting a list of settings names
kgryte Mar 27, 2024
5e54ec5
refactor: use module to get list of settings names
kgryte Mar 27, 2024
3de8ec5
refactor: update help text generation
kgryte Mar 27, 2024
fd3b2a9
refactor: only pass down settings object
kgryte Mar 27, 2024
02fad69
fix: use setting name when constructing error message
kgryte Mar 27, 2024
9dfa13c
refactor: add module to test if a value is a setting name
kgryte Mar 28, 2024
0d8d3a9
refactor: update error messages
kgryte Mar 28, 2024
22aba0c
feat: add `settings` method for getting and setting REPL settings
kgryte Mar 28, 2024
2dff629
refactor: delegate to `settings` method to validate arguments
kgryte Mar 28, 2024
9f35dff
docs: document `settings` method
kgryte Mar 28, 2024
d8da17a
refactor: handle returning setting value as would normal command
kgryte Mar 28, 2024
336d7a0
refactor: update confirmation message
kgryte Mar 28, 2024
d00958e
docs: update description
kgryte Mar 28, 2024
7e8f592
refactor: remove support for manually entering multi-line editing
kgryte Mar 28, 2024
590649d
docs: update copy
kgryte Mar 28, 2024
89415bc
refactor: bind variables to REPL instance to allow "toggabilty"
kgryte Mar 28, 2024
0f577cc
refactor: move symbols table to separate file
kgryte Mar 28, 2024
d4e0730
fix: update require path
kgryte Mar 28, 2024
1f14f24
refactor: convert auto-matcher to a class
kgryte Mar 28, 2024
e4f50ba
refactor: make private properties read-only
kgryte Mar 28, 2024
53ae830
fix: only perform preview completion when no collisions are possible
kgryte Mar 28, 2024
0af02a2
refactor: clean implementation with scoped variables
kgryte Mar 28, 2024
acc8ab6
refactor: use helper functions
kgryte Mar 28, 2024
0c55033
docs: add FIXME
kgryte Mar 28, 2024
ec2c283
refactor: add modules for specifying settings arguments amenable to a…
kgryte Mar 28, 2024
5212dcc
refactor: add module exporting a regexp for matching settings APIs
kgryte Mar 28, 2024
da738c4
refactor: add support for settings name auto-completion
kgryte Mar 28, 2024
a90fc2d
refactor: defer to completer when receiving a TAB character
kgryte Mar 29, 2024
0059b77
refactor: use internal utility to support suppressing log messages
kgryte Mar 29, 2024
5e26c45
refactor: reorder cases in alphabetical order
kgryte Mar 30, 2024
98364f6
refactor: fix order
kgryte Mar 30, 2024
ab671c2
refactor: update implementation to use AST
kgryte Mar 31, 2024
691b45b
style: add lint warnings
kgryte Mar 31, 2024
cc6627b
style: address lint warning
kgryte Mar 31, 2024
1be8b6b
test: add auto-match tests and move fixture
kgryte Mar 31, 2024
ec672ab
refactor: rename setting
kgryte Mar 31, 2024
5434e3b
docs: rename setting
kgryte Mar 31, 2024
1ff156f
refactor: remove unused property
kgryte Mar 31, 2024
662e1cf
fix: update property name
kgryte Mar 31, 2024
6d8390d
fix: allow auto-closing within template and object expressions
kgryte Mar 31, 2024
8e09e8e
test: add RegExp test
kgryte Mar 31, 2024
8f9dce8
fix: address completer bug when completing after special characters
kgryte Mar 31, 2024
0bb6ae9
refactor: add support for auto-deleting character pairs
kgryte Mar 31, 2024
c6d997d
refactor: move deletion logic to auto-closer due to shared state
kgryte Mar 31, 2024
d0d1dbf
fix: ensure corresponding closing symbol when auto-deleting pairs
kgryte Mar 31, 2024
d3fd4ef
docs: update copy
kgryte Mar 31, 2024
ff0a086
docs: fix jsdoc
Snehil-Shah Mar 31, 2024
10de907
test: add tests for auto-deletion
Snehil-Shah Mar 31, 2024
2ae5f73
Merge branch 'stdlib-js:develop' into repl
Snehil-Shah Mar 31, 2024
2f53984
Revert "docs: fix jsdoc"
Snehil-Shah Mar 31, 2024
a1477a3
test: fix tests
Snehil-Shah Mar 31, 2024
6ab1028
refactor: separate auto-delete & auto-closing tests
Snehil-Shah Mar 31, 2024
6b1f6f0
refactor: move conditional to the method itself
Snehil-Shah Mar 31, 2024
b8344c8
refactor: early return to avoid unnecessary indentation and rename pa…
kgryte Mar 31, 2024
d27f4c5
docs: document properties
kgryte Mar 31, 2024
a70003f
test: add separate test generation script
kgryte Mar 31, 2024
0afd3b8
docs: update parameter type to reflect usage
kgryte Mar 31, 2024
b25fcaa
refactor: move regular expression to separate file
kgryte Mar 31, 2024
a960530
refactor: access preceding character directly
kgryte Apr 1, 2024
aee610c
refactor: replicate logic in private method
kgryte Apr 1, 2024
5653b88
refactor: delegate open symbol processing to private method
kgryte Apr 1, 2024
f7cc5c7
refactor: copy logic for handling a closing symbol to a private method
kgryte Apr 1, 2024
badfc2c
refactor: delegate to private method for handling a closing symbol
kgryte Apr 1, 2024
840e972
refactor: implement handling of opening symbol during auto-deletion i…
kgryte Apr 1, 2024
52de42c
refactor: delegate to private method
kgryte Apr 1, 2024
03b1fd0
docs: update param type
kgryte Apr 1, 2024
24bb5cc
docs: fix copyright year
kgryte Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 116 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/auto_match_symbols.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @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';

// VARIABLES //

var SYMBOLS = {
'{': '}',
'[': ']',
'(': ')',
'\'': '\'',
'"': '"',
'`': '`'
};


// MAIN //

/**
* Returns a callback that handles auto matching symbols.
*
* @private
* @param {REPL} repl - REPL instance
* @returns {Function} callback
*/
function autoMatch( repl ) {
return onKeypress;

/**
* Callback invoked upon a readline interface "keypress" event.
*
* @private
* @param {string} chunk - character pressed by the user
* @param {Object} key - keypress event information
*/
function onKeypress( chunk, key ) {
if ( repl._settings.autoMatch ) {
// Handle edge cases
if ( repl._auto_match_status ) {
// Auto delete appended symbol when deleting input
if (
key.name === 'backspace'
) {
repl._rli.write( null, {
'name': 'right'
} );
repl._rli.write( null, {
'name': 'backspace'
} );
return;
}
// Discard instinctive closing symbols
if (
typeof chunk !== 'undefined' &&
repl._rli.line[ repl._rli.cursor ] === chunk &&
SYMBOLS[ repl._rli.line[ repl._rli.cursor - 2 ] ] === chunk
) {
repl._rli.write( null, {
'name': 'backspace'
} );
repl._rli.write( null, {
'name': 'right'
} );
repl._auto_match_status = false;
return;
}
repl._auto_match_status = false;
}

// For a bracket/quotation input, append corresponding closing symbol
if ( SYMBOLS[chunk] ) {
if (
(
SYMBOLS[chunk] === '\'' || SYMBOLS[chunk] === '"'
) &&
(
/^[a-zA-Z0-9"'/\\]$/.test(repl._rli.line[repl._rli.cursor - 2 ]) ||
/^[a-zA-Z0-9"'/\\]$/.test(repl._rli.line[repl._rli.cursor ])
)
) {
// Don't auto-complete quotations around text
return;
}
repl._rli.write( SYMBOLS[chunk] );
repl._rli.write( null, {
'ctrl': true,
'name': 'b'
} );
repl._auto_match_status = true;
}
}
}
}


// EXPORTS //

module.exports = autoMatch;
2 changes: 2 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand Down Expand Up @@ -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 ] );
Expand Down
143 changes: 143 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/commands/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* @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 isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive;
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
var objectKeys = require( '@stdlib/utils/keys' );
var format = require( '@stdlib/string/format' );


// VARIABLES //

var debug = logger( 'repl:command:settings' );
var SETTINGS = {
'autoMatch': {
'desc': 'Automatically insert matching brackets, parentheses, and quotes.',
'default': true,
'type': 'boolean'
}
};
var VALIDATORS = {
'boolean': isBoolean
};


// FUNCTIONS //

/**
* Returns settings help text.
*
* @private
* @param {REPL} repl - REPL instance
* @returns {string} settings help text
*/
function help( repl ) {
var HELP_TEXT;
var names;
var o;
var i;

names = objectKeys( SETTINGS );
HELP_TEXT = '\n';
for ( i = 0; i < names.length; i++ ) {
o = SETTINGS[ names[ i ] ];
HELP_TEXT += names[ i ] + '\n';
HELP_TEXT += ' ';
if ( o.desc ) {
HELP_TEXT += SETTINGS[ names[ i ] ].desc;
} else {
HELP_TEXT += '(no description available)';
}
HELP_TEXT += format( ' Value: %s', repl._settings[names[ i ]] );
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;

/**
* Manages REPL settings.
*
* If no arguments are given, it prints all settings with their description.
* If one argument is given, it fetches the value of the setting with the given name and prints it.
* If two arguments are given, it updates the value of the given setting name to the given value.
*
* @private
* @param {string} [name] - setting name
* @param {*} [value] - new setting value
*/
function onCommand( name, value ) {
var validator;
var err;
if ( arguments.length === 0 ) {
repl._ostream.write( help( repl ) );
return;
}
if ( !isString( name ) ) {
err = new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', name ) );
debug( 'Error: %s', err.message );
repl._ostream.write( format( 'Error: %s\n', err.message ) );
return;
}
if ( !hasOwnProp( SETTINGS, name ) ) {
err = new Error( format( 'invalid argument. Unrecognized setting. Value: `%s`.', name ) );
debug( 'Error: %s', err.message );
repl._ostream.write( format( 'Error: %s\n', err.message ) );
return;
}
if ( arguments.length === 1 ) {
// repl._ostream.write( '\n'+repl._settings[name]+'\n\n' );
repl._ostream.write( format( '\n%s\n\n', repl._settings[name] ) );
return;
}
validator = VALIDATORS[ SETTINGS[name].type ];
if ( !validator(value) ) {
err = new TypeError( format( 'invalid argument. Second argument must be a %s. Value: `%s`.', SETTINGS[name].type, name ) );
debug( 'Error: %s', err.message );
repl._ostream.write( format( 'Error: %s\n', err.message ) );
return;
}
repl._settings[name] = value;
repl._ostream.write( format( '\nUpdated setting %s to %s.\n\n', name, String(value) ) );
}
}


// EXPORTS //

module.exports = command;
3 changes: 1 addition & 2 deletions lib/node_modules/@stdlib/repl/lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ function defaults() {
'save': '',
'load': '',
'log': '',
'quiet': false,
'autoClose': true
'quiet': false
};
}

Expand Down
74 changes: 74 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/handle_manual_multiline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @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 repeat = require( '@stdlib/string/repeat' );


// MAIN //

/**
* Returns a callback that handles multiline editing using a modifier key.
*
* @private
* @param {REPL} repl - REPL instance
* @returns {Function} callback
*/
function manualMultiline( repl ) {
return onKeypress;

/**
* Callback invoked upon a readline interface "keypress" event.
*
* @private
* @param {string} key - key event information
*/
function onKeypress( key ) {
var charsAfterCursor;
var codeAfterCursor;

// For ALT+ENTER keypress, manually enter multi-line mode:
if ( key && key.name === 'return' && key.meta ) {
charsAfterCursor = repl._rli.line.length - repl._rli.cursor;
codeAfterCursor = repl._rli.line.substring( repl._rli.cursor );

// Update flag
repl._multiline.mode = 'modifier';

// Store code after cursor in buffer for next line
repl._multiline.buffer = codeAfterCursor;
repl._ostream.write( repeat( '\x1b[C', charsAfterCursor ) + repeat( '\b \b', charsAfterCursor ) );
repl._rli.line = repl._rli.line.substring( 0, repl._rli.cursor );

// Simulate `line` event
repl._rli.write( null, {
'name': 'return'
});
}
}
}


// EXPORTS //

module.exports = manualMultiline;
2 changes: 2 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/help_text.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ var MSG = [
' userDoc() Add user-defined documentation.',
' clearUserDocs() Clear user-defined documentation.',
'',
' settings() View and update settings.',
'',
' clearHistory() Clear the REPL history.',
'',
' clear() Clear the entire REPL screen and scrollback history.',
Expand Down