Skip to content

Commit 3c31c1f

Browse files
Snehil-Shahkgryte
andauthored
feat: add REPL pager
PR-URL: #2162 Closes: #2149 Co-authored-by: Athan Reines <[email protected]> Reviewed-by: Athan Reines <[email protected]> Signed-off-by: Snehil Shah <[email protected]>
1 parent a20a58f commit 3c31c1f

16 files changed

+1477
-38
lines changed

lib/node_modules/@stdlib/repl/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ The function supports specifying the following settings:
8181

8282
- **autoClosePairs**: boolean indicating whether to automatically insert matching brackets, parentheses, and quotes. Default: `true`.
8383
- **autoDeletePairs**: boolean indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes. Default: `true`.
84+
- **autoPage**: boolean indicating whether to automatically page return values having a display size exceeding the visible screen. When streams are TTY, the default is `true`; otherwise, the default is `false`.
8485
- **completionPreviews**: boolean indicating whether to display completion previews for auto-completion. When streams are TTY, the default is `true`; otherwise, the default is `false`.
8586

8687
#### REPL.prototype.createContext()

lib/node_modules/@stdlib/repl/lib/auto_close_pairs.js

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,19 @@ function isQuote( ch ) {
6363
* @private
6464
* @constructor
6565
* @param {Object} rli - readline instance
66+
* @param {boolean} autoClose - boolean indicating whether auto-closing should be initially enabled
67+
* @param {boolean} autoDelete - boolean indicating whether auto-deleting should be initially enabled
6668
* @returns {AutoCloser} auto-closer instance
6769
*/
68-
function AutoCloser( rli ) {
70+
function AutoCloser( rli, autoClose, autoDelete ) {
6971
if ( !(this instanceof AutoCloser) ) {
70-
return new AutoCloser( rli );
72+
return new AutoCloser( rli, autoClose, autoDelete );
7173
}
7274
debug( 'Creating an auto-closer...' );
7375
this._rli = rli;
7476
this._ignoreBackspace = false;
77+
this._autoClose = autoClose;
78+
this._autoDelete = autoDelete;
7579
return this;
7680
}
7781

@@ -239,6 +243,62 @@ setNonEnumerableReadOnly( AutoCloser.prototype, '_autodeleteOpenSymbol', functio
239243
return true;
240244
});
241245

246+
/**
247+
* Disables auto-closing pairs.
248+
*
249+
* @name disableAutoClose
250+
* @memberof AutoCloser.prototype
251+
* @type {Function}
252+
* @returns {AutoCloser} auto-close instance
253+
*/
254+
setNonEnumerableReadOnly( AutoCloser.prototype, 'disableAutoClose', function disableAutoClose() {
255+
debug( 'Disabling auto-closing pairs...' );
256+
this._autoClose = false;
257+
return this;
258+
});
259+
260+
/**
261+
* Enables auto-closing pairs.
262+
*
263+
* @name enableAutoClose
264+
* @memberof AutoCloser.prototype
265+
* @type {Function}
266+
* @returns {AutoCloser} auto-close instance
267+
*/
268+
setNonEnumerableReadOnly( AutoCloser.prototype, 'enableAutoClose', function enableAutoClose() {
269+
debug( 'Enabling auto-closing pairs...' );
270+
this._autoClose = true;
271+
return this;
272+
});
273+
274+
/**
275+
* Disables auto-deleting pairs.
276+
*
277+
* @name disableAutoDelete
278+
* @memberof AutoCloser.prototype
279+
* @type {Function}
280+
* @returns {AutoCloser} auto-close instance
281+
*/
282+
setNonEnumerableReadOnly( AutoCloser.prototype, 'disableAutoDelete', function disableAutoDelete() {
283+
debug( 'Disabling auto-deleting pairs...' );
284+
this._autoDelete = false;
285+
return this;
286+
});
287+
288+
/**
289+
* Enables auto-deleting pairs.
290+
*
291+
* @name enableAutoDelete
292+
* @memberof AutoCloser.prototype
293+
* @type {Function}
294+
* @returns {AutoCloser} auto-close instance
295+
*/
296+
setNonEnumerableReadOnly( AutoCloser.prototype, 'enableAutoDelete', function enableAutoDelete() {
297+
debug( 'Enabling auto-deleting pairs...' );
298+
this._autoDelete = true;
299+
return this;
300+
});
301+
242302
/**
243303
* Callback which should be invoked **before** a "keypress" event is processed by a readline interface.
244304
*
@@ -253,6 +313,9 @@ setNonEnumerableReadOnly( AutoCloser.prototype, 'beforeKeypress', function befor
253313
var cursor;
254314
var line;
255315

316+
if ( !this._autoDelete ) {
317+
return false;
318+
}
256319
if ( !key || key.name !== 'backspace' ) {
257320
return false;
258321
}
@@ -294,6 +357,9 @@ setNonEnumerableReadOnly( AutoCloser.prototype, 'onKeypress', function onKeypres
294357
var cursor;
295358
var line;
296359

360+
if ( !this._autoClose ) {
361+
return false;
362+
}
297363
cursor = this._rli.cursor;
298364
line = this._rli.line;
299365

lib/node_modules/@stdlib/repl/lib/commands.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var isKeyword = require( './commands/is_keyword.js' );
4848
var onLicense = require( './commands/license_text.js' );
4949
var onLoad = require( './commands/load.js' );
5050
var onLoadWorkspace = require( './commands/load_workspace.js' );
51+
var onPager = require( './commands/pager.js' );
5152
var onPresentationStart = require( './commands/presentation_start.js' );
5253
var onPresentationStop = require( './commands/presentation_stop.js' );
5354
var onQuit = require( './commands/quit.js' );
@@ -116,6 +117,7 @@ function commands( repl ) {
116117
cmds.push( [ 'license', onLicense( repl ), false ] );
117118
cmds.push( [ 'load', onLoad( repl ), false ] );
118119
cmds.push( [ 'loadWorkspace', onLoadWorkspace( repl ), false ] );
120+
cmds.push( [ 'pager', onPager( repl ), false ] );
119121
cmds.push( [ 'presentationStart', onPresentationStart( repl ), false ] );
120122
cmds.push( [ 'presentationStop', onPresentationStop( repl ), false ] );
121123
cmds.push( [ 'quit', onQuit( repl ), false ] );
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2024 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/* eslint-disable no-underscore-dangle */
20+
21+
'use strict';
22+
23+
// MAIN //
24+
25+
/**
26+
* Returns a callback to be invoked upon calling the `pager` command.
27+
*
28+
* @private
29+
* @param {REPL} repl - REPL instance
30+
* @returns {Function} callback
31+
*/
32+
function command( repl ) {
33+
return onCommand;
34+
35+
/**
36+
* Enables paging for a provided string.
37+
*
38+
* @private
39+
* @param {string} value - input string
40+
*/
41+
function onCommand( value ) {
42+
var ostream = repl._ostream;
43+
44+
// Check whether auto-paging is already enabled...
45+
if ( repl.settings( 'autoPage' ) ) {
46+
// Nothing needed here, as we can defer to already enabled behavior:
47+
ostream.write( value );
48+
return;
49+
}
50+
// Temporarily enable paging:
51+
ostream.enablePaging();
52+
53+
// Write the input value:
54+
ostream.write( value );
55+
56+
// Disable paging:
57+
ostream.disablePaging();
58+
}
59+
}
60+
61+
62+
// EXPORTS //
63+
64+
module.exports = command;

lib/node_modules/@stdlib/repl/lib/completer_preview.js

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,18 @@ var debug = logger( 'repl:completer:preview' );
4444
* @param {Object} rli - readline instance
4545
* @param {Function} completer - function for generating possible completions
4646
* @param {WritableStream} ostream - writable stream
47+
* @param {boolean} enabled - boolean indicating whether the completer should be initially enabled
4748
* @returns {PreviewCompleter} completer instance
4849
*/
49-
function PreviewCompleter( rli, completer, ostream ) {
50+
function PreviewCompleter( rli, completer, ostream, enabled ) {
5051
if ( !(this instanceof PreviewCompleter) ) {
51-
return new PreviewCompleter( rli, completer, ostream );
52+
return new PreviewCompleter( rli, completer, ostream, enabled );
5253
}
5354
debug( 'Creating a preview completer...' );
5455

56+
// Initialize a flag indicating whether the preview completer is enabled:
57+
this._enabled = enabled;
58+
5559
// Cache a reference to the provided readline interface:
5660
this._rli = rli;
5761

@@ -76,6 +80,7 @@ function PreviewCompleter( rli, completer, ostream ) {
7680
* @private
7781
* @name _completionCallback
7882
* @memberof PreviewCompleter.prototype
83+
* @type {Function}
7984
* @returns {Function} completion callback
8085
*/
8186
setNonEnumerableReadOnly( PreviewCompleter.prototype, '_completionCallback', function completionCallback() {
@@ -143,12 +148,16 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, '_completionCallback', fun
143148
*
144149
* @name clear
145150
* @memberof PreviewCompleter.prototype
151+
* @type {Function}
146152
* @returns {void}
147153
*/
148154
setNonEnumerableReadOnly( PreviewCompleter.prototype, 'clear', function clear() {
149155
var preview;
150156
var N;
151157

158+
if ( !this._enabled ) {
159+
return;
160+
}
152161
preview = this._preview;
153162

154163
// If no preview currently displayed, nothing to clear...
@@ -173,16 +182,51 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'clear', function clear()
173182
this._preview = '';
174183
});
175184

185+
/**
186+
* Disables the preview completer.
187+
*
188+
* @name disable
189+
* @memberof PreviewCompleter.prototype
190+
* @type {Function}
191+
* @returns {PreviewCompleter} completer instance
192+
*/
193+
setNonEnumerableReadOnly( PreviewCompleter.prototype, 'disable', function disable() {
194+
this.clear();
195+
196+
debug( 'Disabling the preview completer...' );
197+
this._enabled = false;
198+
199+
return this;
200+
});
201+
202+
/**
203+
* Enables the preview completer.
204+
*
205+
* @name enable
206+
* @memberof PreviewCompleter.prototype
207+
* @type {Function}
208+
* @returns {PreviewCompleter} completer instance
209+
*/
210+
setNonEnumerableReadOnly( PreviewCompleter.prototype, 'enable', function enable() {
211+
debug( 'Enabling the preview completer...' );
212+
this._enabled = true;
213+
return this;
214+
});
215+
176216
/**
177217
* Callback for handling a "keypress" event.
178218
*
179219
* @name onKeypress
180220
* @memberof PreviewCompleter.prototype
221+
* @type {Function}
181222
* @param {string} data - input data
182223
* @param {(Object|void)} key - key object
183224
* @returns {void}
184225
*/
185226
setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onKeypress() {
227+
if ( !this._enabled ) {
228+
return;
229+
}
186230
// Check for existing content beyond the cursor which could "collide" with a preview completion...
187231
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)
188232
return;
@@ -199,14 +243,22 @@ setNonEnumerableReadOnly( PreviewCompleter.prototype, 'onKeypress', function onK
199243
*
200244
* @name beforeKeypress
201245
* @memberof PreviewCompleter.prototype
246+
* @type {Function}
202247
* @param {string} data - input data
203248
* @param {(Object|void)} key - key object
204249
* @returns {void}
205250
*/
206251
setNonEnumerableReadOnly( PreviewCompleter.prototype, 'beforeKeypress', function beforeKeypress( data, key ) {
252+
if ( !this._enabled ) {
253+
return;
254+
}
207255
if ( !key || this._preview === '' ) {
208256
return;
209257
}
258+
// Avoid clashing with existing TAB completion behavior...
259+
if ( key.name === 'tab' ) {
260+
return this.clear();
261+
}
210262
// Handle the case where the user is not at the end of the line...
211263
if ( this._rli.cursor !== this._rli.line.length ) {
212264
// If a user is in the middle of a line and presses ENTER, clear the preview string, as the preview was not accepted prior to executing the expression...

lib/node_modules/@stdlib/repl/lib/defaults.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ function defaults() {
8686
// Flag indicating whether to automatically delete adjacent matching brackets, parentheses, and quotes:
8787
'autoDeletePairs': true,
8888

89+
// Flag indicating whether to enable automatically page return values requiring a display size exceeding the visible screen (note: default depends on whether TTY):
90+
'autoPage': void 0,
91+
8992
// Flag indicating whether to enable the display of completion previews for auto-completion (note: default depends on whether TTY):
9093
'completionPreviews': void 0
9194
}

lib/node_modules/@stdlib/repl/lib/display_prompt.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ function displayPrompt( repl, preserveCursor ) {
4141
var re;
4242
var ws;
4343

44+
// Avoid displaying a prompt if the REPL is currently in paging mode...
45+
if ( repl._ostream.isPaging ) {
46+
return;
47+
}
4448
len = repl._cmd.length;
4549
if ( len === 0 ) {
4650
if ( repl._padding && repl._count >= 0 ) {

0 commit comments

Comments
 (0)