summaryrefslogtreecommitdiff
path: root/js/codeq/console.js
diff options
context:
space:
mode:
authorAleš Smodiš <aless@guru.si>2015-09-17 11:45:03 +0200
committerAleš Smodiš <aless@guru.si>2015-09-17 11:45:03 +0200
commit8ae11f070aafea81bc91bdaf6918f396777f88df (patch)
treed4356153098ccffd3097259a0dda53a4a406fb60 /js/codeq/console.js
parent0eec2bd8f8fd8669682cf618bf22983a36f828a9 (diff)
Console fix: a line can now have multiple CSS classes, so text can e.g. now be in multiple colors.
Diffstat (limited to 'js/codeq/console.js')
-rw-r--r--js/codeq/console.js237
1 files changed, 206 insertions, 31 deletions
diff --git a/js/codeq/console.js b/js/codeq/console.js
index 99c1550..9331bc1 100644
--- a/js/codeq/console.js
+++ b/js/codeq/console.js
@@ -160,7 +160,7 @@
};
codeq.makeConsole = function (jqElt, options) {
- var lines = [], // console content
+ var lines = [],// console content, line descriptors of the form {'content': 'string', 'classNames': [], 'jqLine': jQuery object, 'row': number}
maxLines = (options && options.maxLines) || 10000, // how many lines to display at most
currentRow = 0, // cursor position
currentCol = 0,
@@ -169,14 +169,65 @@
inputDisabled = false, // whether to ignore input
lineBuffered = true, // whether we are line-buffered or not buffered
+ renderSpan = function (lineDescriptor, startCol, length) {
+ var jqLine = lineDescriptor.jqLine,
+ content = lineDescriptor.content,
+ classes = lineDescriptor.classNames,
+ endCol = startCol + length,
+ N = classes ? classes.length : 0,
+ i = 0,
+ spans = [], // what to render, items are of the form {'content': string, 'cssClass': string}
+ entry, classSpan, jq, span, s;
+ // seek out the first css class
+ while (i < N) {
+ entry = classes[i];
+ if (((entry.start + entry.length) > startCol) && (entry.start < endCol)) break;
+ i++;
+ }
+ if (i < N) {
+ // render the first span
+ classSpan = entry.length - startCol + entry.start;
+ span = {'content': content.substr(startCol, classSpan), 'cssClass': entry.name};
+ spans.push(span);
+ startCol += classSpan;
+ i++;
+ // render the rest
+ while ((startCol < endCol) && (i < N)) {
+ entry = classes[i];
+ s = content.substr(startCol, entry.length);
+ if (entry.name === span.cssClass) {
+ span.content += s; // join content where the class is the same
+ }
+ else {
+ span = {'content': s, 'cssClass': entry.name};
+ spans.push(span);
+ }
+ startCol += entry.length;
+ i++;
+ }
+ }
+ // render any leftover
+ if (startCol < endCol) {
+ entry = makeClassDescriptor('', startCol, endCol - startCol);
+ spans.push({'content': content.substr(startCol), 'cssClass': ''});
+ }
+ // render spans
+ for (i = 0; i < spans.length; i++) {
+ span = spans[i];
+ jq = $('<span class="cq-con-text ' + span.cssClass + '"></span>');
+ jqLine.append(jq);
+ jq.text(span.content);
+ }
+ return entry; // return the last entry used
+ },
+
renderLine = function (lineDescriptor) {
var jqLine = lineDescriptor.jqLine,
- className = 'cq-con-text ' + lineDescriptor.className,
- content = lineDescriptor.content || '',
- jq1, jq2, jqCursor;
+ content = lineDescriptor.content,
+ jqCursor, classDescriptor;
if (!jqLine) {
- jqLine = $('<pre class="cq-con-line ' + lineDescriptor.className + '"></pre>');
+ jqLine = $('<pre class="cq-con-line"></pre>');
if (lineDescriptor.row == 0) {
jqContent.prepend(jqLine);
}
@@ -196,36 +247,30 @@
if (currentRow == lineDescriptor.row) {
// mark the cursor in the current line
if (currentCol >= content.length) {
- jq1 = $('<span class="' + className + '"></span>');
+ // cursor after the last character
+ renderSpan(lineDescriptor, 0, content.length);
jqCursor = $('<span class="cq-con-cursor">&nbsp;</span>');
- jqLine.append(jq1);
jqLine.append(jqCursor);
- jq1.text(content);
}
else if (currentCol <= 0) {
- jq2 = $('<span class="' + className + '"></span>');
- jqCursor = $('<span class="cq-con-cursor"></span>');
+ // cursor at the first character
+ classDescriptor = lineDescriptor.classNames[0] || makeClassDescriptor('', 0, content.length);
+ jqCursor = $('<span class="cq-con-cursor ' + classDescriptor.name + '"></span>');
jqLine.append(jqCursor);
- jqLine.append(jq2);
jqCursor.text(content.charAt(0));
- jq2.text(content.substr(1));
+ renderSpan(lineDescriptor, 1, content.length - 1);
}
else {
- jq1 = $('<span class="' + className + '"></span>');
- jq2 = $('<span class="' + className + '"></span>');
- jqCursor = $('<span class="cq-con-cursor"></span>');
- jqLine.append(jq1);
+ // cursor somewhere in between
+ classDescriptor = renderSpan(lineDescriptor, 0, currentCol);
+ jqCursor = $('<span class="cq-con-cursor ' + classDescriptor.name + '"></span>');
jqLine.append(jqCursor);
- jqLine.append(jq2);
- jq1.text(content.substr(0, currentCol));
jqCursor.text(content.charAt(currentCol));
- jq2.text(content.substr(currentCol + 1));
+ renderSpan(lineDescriptor, currentCol + 1, content.length - currentCol - 1);
}
}
else {
- jq1 = $('<span class="' + className + '"></span>');
- jqLine.append(jq1);
- jq1.text(lineDescriptor.content);
+ renderSpan(lineDescriptor, 0, content.length);
}
},
@@ -259,6 +304,120 @@
return newLines;
},
+ makeClassDescriptor = function (name, start, length) {
+ return {'name': name, 'start': start, 'length': length};
+ },
+
+ indexOfClassAtOrAfterColumn = function (classes, col) {
+ var N = classes.length,
+ i;
+ for (i = 0; i < N; i++) {
+ if (classes[i].start >= col) return i;
+ }
+ return N;
+ },
+
+ insertClass = function (classes, newClassName, startingCol, length) {
+ var newEntry = makeClassDescriptor(newClassName, startingCol, length),
+ endCol = startingCol + length,
+ i, N, prevClass, pos, auxEntry, delta;
+ // seek out the position where to insert the new class
+ i = indexOfClassAtOrAfterColumn(classes, startingCol);
+ prevClass = i > 0 ? classes[i-1] : null;
+ // insert the new class
+ classes.splice(i, 0, newEntry);
+ // if needed, adjust the length of the previous class
+ if (prevClass) {
+ pos = prevClass.start + prevClass.length;
+ if (pos > startingCol) {
+ // split the previous class in two, the new class gets inserted in between
+ delta = pos - startingCol;
+ auxEntry = makeClassDescriptor(prevClass.name, endCol, delta);
+ classes.splice(i+1, 0, auxEntry);
+ prevClass.length = prevClass.length - delta;
+ }
+ }
+ // readjust positions of the classes after the new one
+ N = classes.length;
+ for (i++; i < N; i++) {
+ auxEntry = classes[i];
+ auxEntry.start = endCol;
+ endCol += auxEntry.length;
+ }
+ },
+
+ removeClasses = function (classes, startCol, length) {
+ var i, entry, endCol, e, prefixDelta, suffixDelta, N;
+ if (!length) return;
+ endCol = startCol + length;
+ for (i = classes.length - 1; i >= 0; i--) {
+ entry = classes[i];
+ e = entry.start + entry.length;
+ if ((e > startCol) && (entry.start < endCol)) {
+ prefixDelta = entry.start < startCol ? startCol - entry.start : 0;
+ suffixDelta = e > endCol ? e - endCol : 0;
+ if ((prefixDelta == 0) && (suffixDelta == 0)) classes.splice(i, 1);
+ else entry.length = prefixDelta + suffixDelta;
+ }
+ }
+ // readjust positions
+ N = classes.length;
+ endCol = 0;
+ for (i = 0; i < N; i++) {
+ entry = classes[i];
+ entry.start = endCol;
+ endCol += entry.length;
+ }
+ },
+
+ removeClassesBefore = function (classes, col) {
+ var i = indexOfClassAtOrAfterColumn(classes, col);
+ if (i == 0) return; // nothing to remove
+ classes.splice(0, i);
+ },
+
+ replaceClassesBeforeColumn = function (classes, col, newClassName, newLength) {
+ var i = indexOfClassAtOrAfterColumn(classes, col),
+ newEntry = makeClassDescriptor(newClassName, 0, newLength);
+ classes.splice(0, i, newEntry);
+ },
+
+ classesBetween = function (classes, startCol, endCol) {
+ var N = classes.length,
+ result = [],
+ i, e, entry, prefixDelta, suffixDelta;
+ for (i = 0; i < N; i++) {
+ entry = classes[i];
+ e = entry.start + entry.length; // end column of this css class
+ if ((e > startCol) && (entry.start < endCol)) {
+ prefixDelta = entry.start < startCol ? startCol - entry.start : 0;
+ suffixDelta = e > endCol ? e - endCol : 0;
+ result.push(makeClassDescriptor(entry.name, entry.start + prefixDelta, entry.length - prefixDelta - suffixDelta));
+ }
+ }
+ return result;
+ },
+
+ mergeClasses = function () {
+ var result = [],
+ i, arg, j, entry, endCol;
+ for (i = 0; i < arguments.length; i++) {
+ arg = arguments[i];
+ if (arg instanceof Array) {
+ for (j = 0; j < arg.length; j++) result.push(arg[j]);
+ }
+ else result.push(arg);
+ }
+ // adjust positions of css classes
+ endCol = 0;
+ for (i = 0; i < result.length; i++) {
+ entry = result[i];
+ entry.start = endCol;
+ endCol += entry.length;
+ }
+ return result;
+ },
+
// the handler object that is returned
handler = {
'setLineBuffered': function () { lineBuffered = true; },
@@ -309,14 +468,15 @@
'insertRow': function (row, content, className, deferReflow) {
// inserts a new row at the position specified
- var lineDescriptor, i, jqLine;
+ var lineDescriptor, i;
+ if (!className) className = 'output';
for (i = lines.length; i < row; i++) {
// insert any empty rows
- lineDescriptor = {content: '', className: className, row: i};
+ lineDescriptor = {content: '', classNames: [], row: i};
lines.push(lineDescriptor);
if (!deferReflow) renderLine(lineDescriptor);
}
- lineDescriptor = {content: content, className: className, row: row};
+ lineDescriptor = {content: content, classNames: content.length > 0 ? [makeClassDescriptor(className, 0, content.length)] : [], row: row};
if (row >= lines.length) lines.push(lineDescriptor);
else lines.splice(row, 0, lineDescriptor);
if (!deferReflow) this.reflow(row);
@@ -324,7 +484,7 @@
},
'splice': function (startRow, startCol, endRow, endCol, newText, className) {
- var i, j, n, startLine, endLine, newLines, newTextLines, part1, part2;
+ var i, j, n, startLine, endLine, newLines, part1, part2, s, auxLine;
if (!className) className = 'output';
// pre-process newText, convert it to newLines: an array of strings without newlines
@@ -370,19 +530,25 @@
if (newLines.length > 1) {
// the first and the last new lines are separate existing lines, modify them
startLine.content = part1 + newLines[0];
+ startLine.classNames = mergeClasses(classesBetween(startLine.classNames, 0, part1.length), makeClassDescriptor(className, part1.length, newLines[0].length));
if (startRow == endRow) {
// we need to create one additional line to hold the compositum of the last line and part2
- this.insertRow(j, newLines[newLines.length - 1] + part2, className, true);
+ auxLine = this.insertRow(j, newLines[newLines.length - 1], className, true);
+ insertClass(auxLine.classNames, auxLine.content.length, part2.length);
+ auxLine.content = auxLine.content + part2;
j++;
}
else {
- endLine.content = newLines[newLines.length - 1] + part2;
+ s = newLines[newLines.length - 1];
+ endLine.content = s + part2;
+ replaceClassesBeforeColumn(endLine.classNames, endCol, className, s.length);
n--; // retain the last line
}
}
else {
// the first and the last new lines are the same existing line
startLine.content = part1 + newLines[0] + part2;
+ startLine.classNames = mergeClasses(classesBetween(startLine.classNames, 0, part1.length), makeClassDescriptor(className, part1.length, newLines[0].length), classesBetween(endLine.classNames, endCol, endCol + part2.length));
}
// remove the rest of the old lines
while (n > 0) {
@@ -422,7 +588,9 @@
startRow = lines.length - 1,
i, lineDescriptor;
if (newLines.length == 0) return; // nothing to do
+ if (typeof className !== 'string') className = 'output';
lineDescriptor = lines[startRow];
+ insertClass(lineDescriptor.classNames, className, lineDescriptor.content.length, newLines[0].length);
lineDescriptor.content = lineDescriptor.content + newLines[0];
for (i = 1; i < newLines.length; i++) {
lineDescriptor = this.insertRow(startRow + i, newLines[i], className, true);
@@ -441,20 +609,25 @@
part1 = currentContent.substring(0, currentCol),
part2 = currentContent.substring(currentCol),
newLines = formNewLines(text),
- i, n;
+ i, n, auxLine;
+ if (typeof className !== 'string') className = 'output';
if (newLines.length == 0) return; // nothing to do
else if (newLines.length == 1) {
+ insertClass(currentLine.classNames, className, part1.length, newLines[0].length);
currentLine.content = part1 + newLines[0] + part2;
if (!dontMoveCursor) currentCol += newLines[0].length;
renderLine(currentLine);
}
else {
+ currentLine.classNames = mergeClasses(classesBetween(currentLine.classNames, 0, part1.length), makeClassDescriptor(className, part1.length, newLines[0].length));
currentLine.content = part1 + newLines[0];
n = newLines.length - 1;
for (i = 1; i < n; i++) {
this.insertRow(startRow + i, newLines[i], className, true);
}
- this.insertRow(startRow + n, newLines[n] + part2, className, true);
+ auxLine = this.insertRow(startRow + n, newLines[n], className, true);
+ insertClass(auxLine.classNames, auxLine.content.length, part2.length);
+ auxLine.content = auxLine.content + part2;
if (!dontMoveCursor) {
currentRow = startRow + n;
currentCol = newLines[n].length;
@@ -485,6 +658,7 @@
leftmost = typeof this.leftmostCol === 'number' && this.leftmostCol || 0;
if ((currentCol > leftmost) && (currentCol <= content.length)) {
currentCol--;
+ removeClasses(lineDescriptor.classNames, currentCol, 1);
lineDescriptor.content = content.substring(0, currentCol) + content.substring(currentCol + 1);
renderLine(lineDescriptor);
}
@@ -494,6 +668,7 @@
var lineDescriptor = lines[currentRow],
content = lineDescriptor.content;
if ((currentCol >= 0) && (currentCol < content.length)) {
+ removeClasses(lineDescriptor.classNames, currentCol, 1);
lineDescriptor.content = content.substring(0, currentCol) + content.substring(currentCol + 1);
renderLine(lineDescriptor);
}
@@ -739,7 +914,7 @@
lines.push({content: options.greeting, className: 'greeting'});
currentRow++;
}
- lines.push({content: '', className: 'input'}); // initial input line
+ lines.push({content: '', classNames: []}); // initial input line
render();
return handler;