Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lib/node_modules/@stdlib/repl/lib/output_stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
var out;
var i;

lines = str.split( RE_EOL ); // FIXME: this approach is not robust for three reasons: (1) line breaks may include carriage returns, not just line feeds (so the `+1` may not be appropriate), (2) it does not consider line feeds which are escaped (e.g., `\\\n`), and (3) assumes that line breaks map directly to displayed lines (while true most of the line for text which has been wrapped to 80 chars, such as repl.txt files, this may not be true for all outputs, some of which may spill across multiple lines)

Check warning on line 71 in lib/node_modules/@stdlib/repl/lib/output_stream.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'fixme' comment: 'FIXME: this approach is not robust for...'
out = [ 0 ];
for ( i = 0; i < lines.length-1; i++ ) {
out.push( out[ i ] + lines[ i ].length + 1 );
Expand Down Expand Up @@ -271,7 +271,7 @@
if ( vh < MIN_VIEWPORT_HEIGHT ) {
return -1;
}
return vh - RESERVED_PAGING_ROWS; // FIXME: reserved paging rows being a constant assumes that the input prompt spans just 1 row. This might not be the case with multi-line inputs triggering the pager as it will result in an incomplete UI with only the last line of the multi-line input visible in the pager view.

Check warning on line 274 in lib/node_modules/@stdlib/repl/lib/output_stream.js

View workflow job for this annotation

GitHub Actions / Lint Changed Files

Unexpected 'fixme' comment: 'FIXME: reserved paging rows being a...'
});

/**
Expand Down Expand Up @@ -473,6 +473,23 @@
return this._isPaging;
});

/**
* Number of columns (i.e., the viewport width).
*
* ## Notes
*
* - A readline interface resolves the terminal width from its output stream in order to determine when a line wraps. As this stream sits between a readline interface and the underlying output stream, we must forward the viewport width; otherwise, a readline interface assumes an infinitely wide terminal and fails to account for wrapped lines when rendering.
* - We resolve `undefined` when a viewport width is unavailable (e.g., when not a TTY), as a readline interface only falls back to assuming an infinitely wide terminal for falsy values.
*
* @name columns
* @memberof OutputStream.prototype
* @type {(integer|void)}
*/
setNonEnumerableReadOnlyAccessor( OutputStream.prototype, 'columns', function columns() {
var w = this._repl.viewportWidth();
return ( w > 0 ) ? w : void 0;
});

/**
* Callback which should be invoked **before** a "keypress" event is processed by a readline interface.
*
Expand Down Expand Up @@ -510,6 +527,8 @@
*/
setNonEnumerableReadOnly( OutputStream.prototype, 'onResize', function onResize() {
if ( !this._isPaging ) {
// Notify the readline interface in order to allow it to re-render (possibly wrapped) input according to the new viewport width:
this.emit( 'resize' );
return;
}
if ( !this._isScrollable( this._indices.length ) ) {
Expand Down
66 changes: 63 additions & 3 deletions lib/node_modules/@stdlib/repl/lib/syntax_highlighter.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,39 @@ function removeDuplicateTokens( tokens ) {
return out;
}

/**
* Resolves the display position of a character offset within a (possibly wrapped) input line.
*
* ## Notes
*
* - The returned row and column offsets are relative to the beginning of the input line (i.e., relative to the first character of the prompt).
* - This mirrors the arithmetic performed by a readline interface when resolving cursor positions, thus ensuring that we remain in agreement with a readline interface when rendering.
* - If a viewport width is unknown (e.g., when the output stream is not a TTY), a line is assumed to never wrap.
*
* @private
* @param {NonNegativeInteger} offset - character offset
* @param {integer} columns - viewport width
* @returns {Object} display position
*
* @example
* var pos = displayPosition( 45, 40 );
* // returns { 'cols': 5, 'rows': 1 }
*/
function displayPosition( offset, columns ) {
var cols;
if ( columns <= 0 ) {
return {
'cols': offset,
'rows': 0
};
}
cols = offset % columns;
return {
'cols': cols,
'rows': ( offset-cols ) / columns
};
}


// MAIN //

Expand Down Expand Up @@ -313,6 +346,10 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'enable', function enable
* @returns {void}
*/
setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function onKeypress() {
var cursorPos;
var promptLen;
var columns;
var endPos;
var tokens;

if ( !this._enabled ) {
Expand Down Expand Up @@ -345,10 +382,33 @@ setNonEnumerableReadOnly( SyntaxHighlighter.prototype, 'onKeypress', function on
}
// Replace:
debug( 'Replacing current line with the highlighted line...' );
readline.moveCursor( this._ostream, -1 * this._rli.cursor, 0 );
readline.clearLine( this._ostream, 1 );

// Resolve the viewport width (note: the width is negative when the output stream is not a TTY):
columns = this._repl.viewportWidth();
promptLen = this._repl.promptLength();

// Resolve the display positions of the cursor and of the end of the input line in order to support lines which wrap across multiple rows:
cursorPos = displayPosition( promptLen+this._rli.cursor, columns );
endPos = displayPosition( promptLen+this._line.length, columns );

// Move the cursor to the row on which the prompt is displayed and to the column immediately following the prompt:
readline.moveCursor( this._ostream, 0, -cursorPos.rows );
readline.cursorTo( this._ostream, promptLen );

// Rewrite the line:
this._ostream.write( this._highlightedLine );
readline.moveCursor( this._ostream, this._rli.cursor - this._line.length, 0 ); // eslint-disable-line max-len

// If the line ends exactly at the end of a row, force the terminal to allocate the next row:
if ( endPos.cols === 0 && endPos.rows > 0 ) {
this._ostream.write( ' ' );
}

// Move the cursor back to its original position:
readline.cursorTo( this._ostream, cursorPos.cols );
if ( cursorPos.rows < endPos.rows ) {
readline.moveCursor( this._ostream, 0, cursorPos.rows-endPos.rows );
}

this._multilineHandler.updateLine( this._highlightedLine ); // save displayed line
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ function assert( t, fixture, done ) {
t.fail( error.message );
return;
}
actual = data[ data.length - 1 ];
// NOTE: after rewriting the highlighted line, the syntax highlighter repositions the cursor (to support wrapped lines), so the highlighted line is the penultimate chunk, followed by a trailing cursor-repositioning escape sequence...
actual = data[ data.length - 2 ];
t.strictEqual( actual, expected, 'returns expected value' );
done();
}
Expand Down