Skip to content

Commit

Permalink
experimental line wrapping reflow
Browse files Browse the repository at this point in the history
  • Loading branch information
jerch committed Jun 24, 2016
1 parent 3dee8c7 commit bdced8c
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 18 deletions.
127 changes: 120 additions & 7 deletions dist/ansiterminal.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@
this.uniqueId = _uniqueId++|0;
this.version = 1;
this.doubled = 0; // 0 - normal, 1 - width, 3 - height top, 4 - height bottom
this.overflow = 0;
this.cells = [];
for (var i=0; i<length; ++i) {
//this.cells.push(tchar.clone()); // to slow?
Expand Down Expand Up @@ -972,7 +973,7 @@
function TScreen(cols, rows, scrollLength) {
this.rows = rows;
this.cols = cols;
this.scrollLength = scrollLength | 0;
this.scrollLength = (scrollLength==Infinity) ? 2147483647 : (scrollLength|0);

this.buffer = [];
this.scrollbuffer = [];
Expand Down Expand Up @@ -1105,6 +1106,86 @@
this.cols = cols;
};

// experimental resize with line wrapping reflow
// TODO: make less expensive
TScreen.prototype.resize_reflow = function(cols, rows, cursor) {
var line = 0, cell = 0, tchar = null;

// build temp array with overflown lines merged
var temp_ar = [[]];
var a_line = temp_ar[temp_ar.length - 1];
for (line=0; line<this.scrollbuffer.length; ++line) {
for (cell=0; cell<this.cols; ++cell) {
tchar = this.scrollbuffer[line].cells[cell];
a_line.push(tchar);
}
if (!this.scrollbuffer[line].overflow) {
temp_ar.push([]);
a_line = temp_ar[temp_ar.length - 1];
}
}
for (line=0; line<this.rows; ++line) {
for (cell=0; cell<this.cols; ++cell) {
tchar = this.buffer[line].cells[cell];
a_line.push(tchar);
}
if (!this.buffer[line].overflow) {
temp_ar.push([]);
a_line = temp_ar[temp_ar.length - 1];
}
}

// right trim temp
for (var i=0; i<temp_ar.length; ++i) {
var right = temp_ar[i].pop();
while (right && right.c =='' && right.width==1)
right = temp_ar[i].pop();
if (right && right.c !='')
temp_ar[i].push(right);
}

// bottom trim temp
var bottom = temp_ar.pop();
while (bottom && !bottom.length)
bottom = temp_ar.pop();
if (bottom)
temp_ar.push(bottom);

// transfer data
var final_buffer = [];
for (line=0; line<temp_ar.length; ++line) {
var trow = new TRow(cols, new TChar(''));
var offset = 0;
for (cell=0; cell<temp_ar[line].length; ++cell) {
if (parseInt(cell/cols)>offset) {
offset++;
trow.overflow = 1;
final_buffer.push(trow);
trow = new TRow(cols, new TChar(''));
}
trow.cells[cell%cols] = temp_ar[line][cell];
}
final_buffer.push(trow);
trow = new TRow(cols, new TChar(''));
}

// adjust final buffer and scrollbuffer
this.scrollbuffer = [];
while (final_buffer.length<rows)
final_buffer.push(new TRow(cols, new TChar('')));
while (final_buffer.length>rows)
this.appendToScrollBuffer(final_buffer.shift());

this.buffer = final_buffer;
if (cursor.row >= rows)
cursor.row = rows - 1;
if (cursor.col >= cols)
cursor.col = cols - 1;

this.rows = rows;
this.cols = cols;
};

/** minimal support for switching charsets (only basic drawing symbols supported) */
// see http://www.vt100.net/charsets/technical.html for technical charset
var CHARSET_0 = {
Expand Down Expand Up @@ -1174,7 +1255,7 @@
function AnsiTerminal(cols, rows, scrollLength) {
this.rows = rows;
this.cols = cols;
this.scrollLength = scrollLength | 0;
this.scrollLength = (scrollLength===Infinity) ? 2147483647 : (scrollLength|0);
this.send = function (s) {}; // callback for writing back to stream
this.beep = function (tone, duration) {}; // callback for sending console beep
this.changedMouseHandling = function(mode, protocol){}; // announce changes in mouse handling
Expand Down Expand Up @@ -1234,6 +1315,9 @@
this.G1 = null;
this.charset = this.G0;
this.active_charset = 0;

// resize settings
this.reflow = false; // experimental
};

/**
Expand All @@ -1259,15 +1343,17 @@
* @method module:node-ansiterminal.AnsiTerminal#resize
*/
AnsiTerminal.prototype.resize = function(cols, rows) {
var resize_func = (this.reflow) ? 'resize_reflow' : 'resize';

// skip insane values
if ((cols < 2) || (rows < 2))
return false;

// normal scroll buffer
this.normal_screen.resize(cols, rows, this.normal_cursor);
this.normal_screen[resize_func](cols, rows, this.normal_cursor);

// alternative buffer
this.alternate_screen.resize(cols, rows, this.alternate_cursor);
this.alternate_screen[resize_func](cols, rows, this.alternate_cursor);

// set new rows / cols to terminal
this.rows = rows;
Expand All @@ -1281,12 +1367,29 @@
this.DECSC();
};

/**
* Register a DCS handler for `flag` and `collected`.
* The handler must follow the DCS "interface" with a `hook`, `feed` and
* `unhook` method (see {@link module:node-ansiterminal.DCS_Dummy}).
*
* @param {function} handler
* @param {string} collected
* @param {string} flag
* @method module:node-ansiterminal.AnsiTerminal#registerDCSHandler
*/
AnsiTerminal.prototype.registerDCSHandler = function(handler, collected, flag) {
this.dcs_handlers[flag+collected] = handler;
};

/**
* Unregister a DCS handler.
*
* @param {function} handler - previously registered handler
* @method module:node-ansiterminal.AnsiTerminal#unregisterDCSHandler
*/
AnsiTerminal.prototype.unregisterDCSHandler = function(handler) {
for (var prop in this.dcs_handlers) {
if (this.dcs_handlers.hasOwnProperty(prop) && this.dcs_handlers[prop] == handler) {
if (this.dcs_handlers.hasOwnProperty(prop) && this.dcs_handlers[prop] === handler) {
delete this.dcs_handlers[prop];
break;
}
Expand Down Expand Up @@ -1350,6 +1453,7 @@

if (this.wrap && width) {
this.cursor.col = 0;
this.screen.buffer[this.cursor.row].overflow = 1; // mark line as overflown
this.cursor.row++;
this.wrap = false;
}
Expand Down Expand Up @@ -1465,6 +1569,7 @@
this.wrap = false;
switch (flag) {
case '\n':
this.screen.buffer[this.cursor.row].overflow = 0;
this.cursor.row++;
if (this.cursor.row >= this.scrolling_bottom) {
this.SU();
Expand Down Expand Up @@ -2631,6 +2736,11 @@

/**
* DCS dummy handler
*
* This is a dummy for a dcs handler. It handles all DCS sequences that have no
* real implementation.
*
* @function module:node-ansiterminal.DCS_Dummy
*/
function DCS_Dummy() {
return new (function() {
Expand All @@ -2645,13 +2755,16 @@
})();
}

// http://www.vt100.net/docs/vt510-rm/DECRQSS.html
/**
* DECRQSS - Request Selection or Setting - DCS $ q D..D ST
*
* Difference to DEC specification - P1 for valid, P0 for invalid requests
* DCS handler for DECRQSS. Currently only SGR and DECSTBM reports are fully implemented.
*
* Difference to DEC specification - P1 for valid, P0 for invalid request
* (following the xterm scheme)
*
* @see {@link http://www.vt100.net/docs/vt510-rm/DECRQSS.html}
* @function module:node-ansiterminal.DCS_DECRQSS
*/
function DCS_DECRQSS() {
return new (function () {
Expand Down
4 changes: 2 additions & 2 deletions dist/ansiterminal.min.js

Large diffs are not rendered by default.

56 changes: 53 additions & 3 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ var ansiterminal = require('node-ansiterminal')
* [.reset()](#module_node-ansiterminal.AnsiTerminal+reset)
* [.toString(opts)](#module_node-ansiterminal.AnsiTerminal+toString) ⇒ <code>string</code>
* [.resize(cols, rows)](#module_node-ansiterminal.AnsiTerminal+resize)
* [.registerDCSHandler(handler, collected, flag)](#module_node-ansiterminal.AnsiTerminal+registerDCSHandler)
* [.unregisterDCSHandler(handler)](#module_node-ansiterminal.AnsiTerminal+unregisterDCSHandler)
* [.inst_p(s)](#module_node-ansiterminal.AnsiTerminal+inst_p)
* [.inst_o(s)](#module_node-ansiterminal.AnsiTerminal+inst_o)
* [.inst_x(flag)](#module_node-ansiterminal.AnsiTerminal+inst_x)
Expand Down Expand Up @@ -78,6 +80,8 @@ var ansiterminal = require('node-ansiterminal')
* [.SGR(params)](#module_node-ansiterminal.AnsiTerminal+SGR)
* [.wcswidth(s)](#module_node-ansiterminal.wcswidth) ⇒ <code>number</code>
* [.get_color(value)](#module_node-ansiterminal.get_color) ⇒ <code>string</code>
* [.DCS_Dummy()](#module_node-ansiterminal.DCS_Dummy)
* [.DCS_DECRQSS()](#module_node-ansiterminal.DCS_DECRQSS)
* _inner_
* [~TColors](#module_node-ansiterminal..TColors) : <code>Object</code>
* [~TAttributes](#module_node-ansiterminal..TAttributes) : <code>Object</code>
Expand Down Expand Up @@ -423,6 +427,8 @@ screen is always accessible via the `screen` attribute.
* [.reset()](#module_node-ansiterminal.AnsiTerminal+reset)
* [.toString(opts)](#module_node-ansiterminal.AnsiTerminal+toString) ⇒ <code>string</code>
* [.resize(cols, rows)](#module_node-ansiterminal.AnsiTerminal+resize)
* [.registerDCSHandler(handler, collected, flag)](#module_node-ansiterminal.AnsiTerminal+registerDCSHandler)
* [.unregisterDCSHandler(handler)](#module_node-ansiterminal.AnsiTerminal+unregisterDCSHandler)
* [.inst_p(s)](#module_node-ansiterminal.AnsiTerminal+inst_p)
* [.inst_o(s)](#module_node-ansiterminal.AnsiTerminal+inst_o)
* [.inst_x(flag)](#module_node-ansiterminal.AnsiTerminal+inst_x)
Expand Down Expand Up @@ -517,6 +523,32 @@ Resize terminal to cols x rows.
| cols | <code>number</code> | new columns value |
| rows | <code>number</code> | new rows value |

<a name="module_node-ansiterminal.AnsiTerminal+registerDCSHandler"></a>

#### ansiTerminal.registerDCSHandler(handler, collected, flag)
Register a DCS handler for `flag` and `collected`.
The handler must follow the DCS "interface" with a `hook`, `feed` and
`unhook` method (see [DCS_Dummy](#module_node-ansiterminal.DCS_Dummy)).

**Kind**: instance method of <code>[AnsiTerminal](#module_node-ansiterminal.AnsiTerminal)</code>

| Param | Type |
| --- | --- |
| handler | <code>function</code> |
| collected | <code>string</code> |
| flag | <code>string</code> |

<a name="module_node-ansiterminal.AnsiTerminal+unregisterDCSHandler"></a>

#### ansiTerminal.unregisterDCSHandler(handler)
Unregister a DCS handler.

**Kind**: instance method of <code>[AnsiTerminal](#module_node-ansiterminal.AnsiTerminal)</code>

| Param | Type | Description |
| --- | --- | --- |
| handler | <code>function</code> | previously registered handler |

<a name="module_node-ansiterminal.AnsiTerminal+inst_p"></a>

#### ansiTerminal.inst_p(s)
Expand Down Expand Up @@ -584,7 +616,6 @@ inst_e - handle ESC instruction
inst_H - enter DCS handler state

**Kind**: instance method of <code>[AnsiTerminal](#module_node-ansiterminal.AnsiTerminal)</code>
**Note**: not implemented

| Param |
| --- |
Expand All @@ -598,7 +629,6 @@ inst_H - enter DCS handler state
inst_P - handle DCS data

**Kind**: instance method of <code>[AnsiTerminal](#module_node-ansiterminal.AnsiTerminal)</code>
**Note**: not implemented

| Param |
| --- |
Expand All @@ -610,7 +640,6 @@ inst_P - handle DCS data
inst_U - leave DCS handler state

**Kind**: instance method of <code>[AnsiTerminal](#module_node-ansiterminal.AnsiTerminal)</code>
**Note**: not implemented
<a name="module_node-ansiterminal.AnsiTerminal+DECALN"></a>

#### ansiTerminal.DECALN()
Expand Down Expand Up @@ -1007,6 +1036,27 @@ Default color mapper function with xterm colorset in white on black.
| --- |
| value |

<a name="module_node-ansiterminal.DCS_Dummy"></a>

### ansiterminal.DCS_Dummy()
DCS dummy handler

This is a dummy for a dcs handler. It handles all DCS sequences that have no
real implementation.

**Kind**: static method of <code>[node-ansiterminal](#module_node-ansiterminal)</code>
<a name="module_node-ansiterminal.DCS_DECRQSS"></a>

### ansiterminal.DCS_DECRQSS()
DECRQSS - Request Selection or Setting - DCS $ q D..D ST

DCS handler for DECRQSS. Currently only SGR and DECSTBM reports are fully implemented.

Difference to DEC specification - P1 for valid, P0 for invalid request
(following the xterm scheme)

**Kind**: static method of <code>[node-ansiterminal](#module_node-ansiterminal)</code>
**See**: [http://www.vt100.net/docs/vt510-rm/DECRQSS.html](http://www.vt100.net/docs/vt510-rm/DECRQSS.html)
<a name="module_node-ansiterminal..TColors"></a>

### ansiterminal~TColors : <code>Object</code>
Expand Down
9 changes: 7 additions & 2 deletions examples/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ var ptyterm = pty.spawn('bash', [], {
});

// create terminal emulator and parser
var terminal = new AnsiTerminal(COLS, ROWS);
//terminal.debug = true;
var terminal = new AnsiTerminal(COLS, ROWS, 50000);
terminal.debug = true;
var parser = new AnsiParser(terminal);

function printTerminal() {
process.stdout.write('\x1b[2;2H');
var debug_out = '';
for (var i=0; i<terminal.screen.buffer.length; ++i) {
process.stdout.write(terminal.screen.buffer[i].toEscapeString({rtrim:false, empty_cell: ' '}));
process.stdout.write('\x1b[B\x1b[2G');
debug_out += terminal.screen.buffer[i].toEscapeString({rtrim:true, empty_cell: ' '});
if (!terminal.screen.buffer[i].overflow)
debug_out += '\n';
}
console.log(debug_out);
}

// parse data from pseudoterminal and write to terminal
Expand Down
20 changes: 16 additions & 4 deletions examples/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var AnsiTerminal = require('../dist/ansiterminal.js').AnsiTerminal;
function AnsiConverter(cols, rows, history) {
this.term = new AnsiTerminal(cols, rows, history);
this.parser = new AnsiParser(this.term);

// assume newline mode so single a '\n' gets translated to '\r\n'
this.term.newline_mode = true;
}
AnsiConverter.prototype.parse = function(s) {
Expand Down Expand Up @@ -32,14 +34,24 @@ AnsiConverter.prototype.toJSON = function(opts) {
return {appendHistory: history, screen: screen};
};

/**
* convenient functions
*
* By appending NLs the terminal gets cleared and
* the final output is fetched from the history buffer.
*/
function ansi2html(s, opts) {
var converter = new AnsiConverter(1000, 1, 50);
converter.parse(s + '\n');
var cols = opts.cols || 200;
var rows = opts.rows || 10;
var converter = new AnsiConverter(cols, rows, Infinity);
converter.parse(s + Array(rows+1).join('\n'));
return converter.toHTML(opts).appendHistory;
}
function ansi2json(s, opts) {
var converter = new AnsiConverter(1000, 1, 50);
converter.parse(s + '\n');
var cols = opts.cols || 200;
var rows = opts.rows || 10;
var converter = new AnsiConverter(cols, rows, Infinity);
converter.parse(s + Array(rows+1).join('\n'));
return converter.toJSON(opts).appendHistory;
}

Expand Down

0 comments on commit bdced8c

Please sign in to comment.