better content parsing and scrolling.

This commit is contained in:
Christopher Jeffrey 2013-06-09 20:19:32 -05:00
parent c260bb9a02
commit ee739c6e7a
1 changed files with 91 additions and 98 deletions

View File

@ -826,6 +826,19 @@ Box.prototype.render = function(stop) {
dattr = ((this.bold << 18) + (this.underline << 18)) | (this.fg << 9) | this.bg;
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++) {
if (!lines[yi]) break;
for (xi = this.left; xi < xl; xi++) {
@ -1265,7 +1278,12 @@ ScrollableText.prototype._scroll = ScrollableText.prototype.scroll;
ScrollableText.prototype.scroll = function(offset) {
var base = this.childBase
, ret = this._scroll(offset)
, diff = this.childBase - base;
, cb = this.childBase
, diff = cb - base
, w
, i
, max
, t;
if (diff === 0) {
return ret;
@ -1275,107 +1293,37 @@ ScrollableText.prototype.scroll = function(offset) {
// feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box.
if (this.content != null) {
var cb = this.childBase
, data = this.render(true) || 0
, xi = data.xi
, xl = data.xl
, xxl = xl - (this.border ? 1 : 0)
, 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;
}
w = this.width - (this.border ? 2 : 0);
if (this.__content == null) this.__content = this.content;
if (this._content == null || this._content.width !== w) {
this._content = wrapContent(this.__content, w);
this.content = this._content.join('\n');
}
// Scroll down.
while (cb--) {
for (xxi = xi + (this.border ? 1 : 0); xxi < xxl; xxi++) {
if (this.content[ci] === '\n' || this.content[ci] === '\r') {
ci++;
break;
} else if (this.content[ci] === '\x1b') {
for (; ci < this.content.length; ci++) {
xxi--;
if (this.content[ci] === 'm') break;
}
ci++;
} else {
ci++;
}
}
// Slow:
// this.contentIndex = this._content.slice(0, cb).join('\n').length;
// if (cb > 0) this.contentIndex++;
// Less slow:
//this.contentIndex = this._content.slice(0, cb).reduce(function(total, line) {
// return total + line.length + 1;
//}, 0);
// Fast:
//for (i = 0, t = 0; i < cb; i++) {
// t += this._content[i].length + 1;
//}
//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
// many characters we need to subtract.
if (ci >= this.content.length) {
if (this.contentIndex >= this.content.length) {
this.childBase = base;
}
ci = this.content.length;
}
this.contentIndex = ci;
max = this._content.length - 1 - (this.height - (this.border ? 2 : 0));
if (cb > max) this.childBase = max;
}
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
*/