better content parsing and scrolling.
This commit is contained in:
parent
c260bb9a02
commit
ee739c6e7a
189
lib/widget.js
189
lib/widget.js
|
@ -826,6 +826,19 @@ Box.prototype.render = function(stop) {
|
||||||
dattr = ((this.bold << 18) + (this.underline << 18)) | (this.fg << 9) | this.bg;
|
dattr = ((this.bold << 18) + (this.underline << 18)) | (this.fg << 9) | this.bg;
|
||||||
attr = dattr;
|
attr = dattr;
|
||||||
|
|
||||||
|
// Check previous line for escape codes.
|
||||||
|
if (this.childBase > 0 && this.content != null) {
|
||||||
|
var cci = ci - (this._content[this.childBase - 1].length + 1);
|
||||||
|
for (; cci < ci; cci++) {
|
||||||
|
if (this.content[cci] === '\x1b') {
|
||||||
|
if (c = /^\x1b\[(?:\d+(?:;\d+)*)?m/.exec(this.content.substring(cci))) {
|
||||||
|
attr = attrCode(c[0], attr);
|
||||||
|
cci += c[0].length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (; yi < yl; yi++) {
|
for (; yi < yl; yi++) {
|
||||||
if (!lines[yi]) break;
|
if (!lines[yi]) break;
|
||||||
for (xi = this.left; xi < xl; xi++) {
|
for (xi = this.left; xi < xl; xi++) {
|
||||||
|
@ -1265,7 +1278,12 @@ ScrollableText.prototype._scroll = ScrollableText.prototype.scroll;
|
||||||
ScrollableText.prototype.scroll = function(offset) {
|
ScrollableText.prototype.scroll = function(offset) {
|
||||||
var base = this.childBase
|
var base = this.childBase
|
||||||
, ret = this._scroll(offset)
|
, ret = this._scroll(offset)
|
||||||
, diff = this.childBase - base;
|
, cb = this.childBase
|
||||||
|
, diff = cb - base
|
||||||
|
, w
|
||||||
|
, i
|
||||||
|
, max
|
||||||
|
, t;
|
||||||
|
|
||||||
if (diff === 0) {
|
if (diff === 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1275,107 +1293,37 @@ ScrollableText.prototype.scroll = function(offset) {
|
||||||
// feeds. This allows us to take preformatted text output from other programs
|
// feeds. This allows us to take preformatted text output from other programs
|
||||||
// and put it in a scrollable text box.
|
// and put it in a scrollable text box.
|
||||||
if (this.content != null) {
|
if (this.content != null) {
|
||||||
var cb = this.childBase
|
w = this.width - (this.border ? 2 : 0);
|
||||||
, data = this.render(true) || 0
|
if (this.__content == null) this.__content = this.content;
|
||||||
, xi = data.xi
|
if (this._content == null || this._content.width !== w) {
|
||||||
, xl = data.xl
|
this._content = wrapContent(this.__content, w);
|
||||||
, xxl = xl - (this.border ? 1 : 0)
|
this.content = this._content.join('\n');
|
||||||
, xxi
|
|
||||||
, ci = 0
|
|
||||||
, xxxi
|
|
||||||
, cci;
|
|
||||||
|
|
||||||
// XXX Temporary workaround for .render() not working while hidden.
|
|
||||||
if (!data) return ret;
|
|
||||||
|
|
||||||
if (this.contentIndex != null) {
|
|
||||||
ci = this.contentIndex;
|
|
||||||
cb = diff;
|
|
||||||
// Scroll up.
|
|
||||||
// This is confusing because we have to parse the
|
|
||||||
// text backwards if we want to be efficient instead
|
|
||||||
// of being O(ridiculous).
|
|
||||||
// TODO: Remove code duplication.
|
|
||||||
if (cb < 0) {
|
|
||||||
cb = -cb;
|
|
||||||
while (cb--) {
|
|
||||||
if (ci < 0) break;
|
|
||||||
for (xxi = xi + (this.border ? 1 : 0); xxi < xxl; xxi++) {
|
|
||||||
if (this.content[ci] === '\n' || this.content[ci] === '\r') {
|
|
||||||
ci--;
|
|
||||||
// TODO: Come up with a cleaner way of doing this:
|
|
||||||
for (xxxi = xi + (this.border ? 1 : 0); xxxi < xxl; xxxi++) {
|
|
||||||
if (this.content[ci] === '\n' || this.content[ci] === '\r') {
|
|
||||||
ci++;
|
|
||||||
break;
|
|
||||||
} else if (this.content[ci] === 'm') {
|
|
||||||
for (cci = ci - 1; cci >= 0; cci--) {
|
|
||||||
if (/[^\x1b\[\d;]/.test(this.content[cci])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (this.content[cci] === '\x1b') {
|
|
||||||
xxxi -= (ci - cci);
|
|
||||||
ci = cci;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ci--;
|
|
||||||
} else {
|
|
||||||
ci--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (this.content[ci] === 'm') {
|
|
||||||
for (cci = ci - 1; cci >= 0; cci--) {
|
|
||||||
if (/[^\x1b\[\d;]/.test(this.content[cci])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (this.content[cci] === '\x1b') {
|
|
||||||
xxi -= (ci - cci);
|
|
||||||
ci = cci;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ci--;
|
|
||||||
} else {
|
|
||||||
ci--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ci < 0) ci = 0;
|
|
||||||
this.contentIndex = ci;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll down.
|
// Slow:
|
||||||
while (cb--) {
|
// this.contentIndex = this._content.slice(0, cb).join('\n').length;
|
||||||
for (xxi = xi + (this.border ? 1 : 0); xxi < xxl; xxi++) {
|
// if (cb > 0) this.contentIndex++;
|
||||||
if (this.content[ci] === '\n' || this.content[ci] === '\r') {
|
|
||||||
ci++;
|
// Less slow:
|
||||||
break;
|
//this.contentIndex = this._content.slice(0, cb).reduce(function(total, line) {
|
||||||
} else if (this.content[ci] === '\x1b') {
|
// return total + line.length + 1;
|
||||||
for (; ci < this.content.length; ci++) {
|
//}, 0);
|
||||||
xxi--;
|
|
||||||
if (this.content[ci] === 'm') break;
|
// Fast:
|
||||||
}
|
//for (i = 0, t = 0; i < cb; i++) {
|
||||||
ci++;
|
// t += this._content[i].length + 1;
|
||||||
} else {
|
//}
|
||||||
ci++;
|
//this.contentIndex = t;
|
||||||
}
|
|
||||||
}
|
// Faster:
|
||||||
|
if (diff > 0) {
|
||||||
|
for (i = base; i < cb; i++) this.contentIndex += this._content[i].length + 1;
|
||||||
|
} else {
|
||||||
|
for (i = base - 1; i >= cb; i--) this.contentIndex -= this._content[i].length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Parse the last few lines to see how
|
max = this._content.length - 1 - (this.height - (this.border ? 2 : 0));
|
||||||
// many characters we need to subtract.
|
if (cb > max) this.childBase = max;
|
||||||
if (ci >= this.content.length) {
|
|
||||||
if (this.contentIndex >= this.content.length) {
|
|
||||||
this.childBase = base;
|
|
||||||
}
|
|
||||||
ci = this.content.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.contentIndex = ci;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1677,6 +1625,51 @@ function readEditor(callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapContent(content, width) {
|
||||||
|
var lines = content.split('\n')
|
||||||
|
, out = [];
|
||||||
|
|
||||||
|
lines.forEach(function(line) {
|
||||||
|
var total
|
||||||
|
, i
|
||||||
|
, part
|
||||||
|
, esc;
|
||||||
|
|
||||||
|
while (line.length > width) {
|
||||||
|
for (i = 0, total = 0; i < line.length; i++) {
|
||||||
|
while (line[i] === '\x1b') {
|
||||||
|
while (line[i] && line[i++] !== 'm');
|
||||||
|
}
|
||||||
|
if (!line[i]) break;
|
||||||
|
if (++total === width) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
part = line.substring(0, i - 1);
|
||||||
|
esc = /\x1b[\[\d;]*$/.exec(part);
|
||||||
|
|
||||||
|
if (esc) {
|
||||||
|
part = part.slice(0, -esc[0].length);
|
||||||
|
line = line.substring(i - 1 - esc[0].length);
|
||||||
|
out.push(part);
|
||||||
|
} else {
|
||||||
|
line = line.substring(i - 1);
|
||||||
|
out.push(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If only an escape code got cut off, at it to `part`.
|
||||||
|
if (/^(?:\x1b[\[\d;]*m)+$/.test(line)) {
|
||||||
|
out[out.length-1] += line;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.push(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
out.width = width;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants
|
* Constants
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue