Rewrote the handling of . Now there is one FoldLine per folded line/row as it's displayed on the screen.

This commit is contained in:
Julian Viereck 2011-04-23 21:51:39 +02:00
commit 7b58e782a7
4 changed files with 180 additions and 158 deletions

View file

@ -896,9 +896,6 @@ var EditSession = function(text, mode) {
if (useWrapMode && this.$wrapData.length != this.doc.$lines.length) {
console.error("The length of doc.$lines and $wrapData have to be the same!");
}
if (this.$foldData.length != this.doc.$lines.length) {
console.error("The length of doc.$lines and $foldData have to be the same!");
}
// TODO:
// this.$updateFoldData(firstRow, lastRow);
@ -1242,65 +1239,45 @@ var EditSession = function(text, mode) {
return this.documentToScreenPosition(docRow, docColumn).row;
}
this.$buildFoldedTextLine = function(row, endRow, endColumn) {
this.$buildFoldedTextLine = function(foldLine, endRow, endColumn) {
var textLine = "";
// Build a one line string for the current fold sequence that starts
// at foldStartRow.
var lastEnd = 0,
line = this.getLine(foldLine.start.row),
comp,
folds = foldLine.folds,
fold;
while_loop:
while (true) {
var folds = this.getFoldData(row);
var line = this.getLine(row);
for (var i = 0; i < folds.length; i++) {
fold = folds[i];
var lastEnd = 0;
var startIdx = 0;
if (folds[0].start.row != row) {
startIdx = 1;
lastEnd = folds[0].end.column;
comp = fold.compare(endRow, endColumn);
// This fold is after the endRow/Column.
if (comp == -1) {
textLine += line.substring(lastEnd, endColumn);
return textLine;
}
fold = null;
for (var i = startIdx; i < folds.length; i++) {
fold = folds[i];
// STOP
if (row == endRow) {
if (endColumn <= fold.start.column) {
textLine += line.substring(lastEnd, endColumn);
break while_loop;
} else if (!fold.sameLine || endColumn < fold.end.column) {
textLine += line.substring(lastEnd, fold.start.column);
break while_loop;
}
}
// The endRow/Column is inside of the current fold.
else if (comp == 0) {
textLine += line.substring(lastEnd, fold.start.column);
return textLine;
}
// Stop if the end position is inside of this fold.
if (endRow < fold.end.row ||
endRow == fold.end.row && endColumn < fold.end.column
) {
break while_loop;
}
textLine += fold.placeholder;
if (fold.sameLine) {
lastEnd = fold.end.column;
} else {
row = fold.end.row;
// STOP
if (row == endRow && endColumn <= fold.end.column) {
break while_loop;
}
}
}
if (row > endRow) {
break;
}
if (row == endRow && (!fold || fold.sameLine)) {
if (fold) {
textLine += line.substring(lastEnd, endColumn);
}
break;
textLine += line.substring(lastEnd, fold.start.column);
textLine += fold.placeholder;
if (fold.sameLine) {
lastEnd = fold.end.column;
} else {
row = fold.end.row;
line = this.getLine(row);
lastEnd = fold.end.column;
}
}
textLine += line.substring(lastEnd, endColumn);
return textLine;
}
@ -1308,40 +1285,53 @@ var EditSession = function(text, mode) {
var screenRow = 0,
screenColumn = 0,
foldStartRow = null,
fold = null;
fold = null,
folds,
comp,
foldLine = null;
// Clamp the docRow position in case it's inside of a folded block.
if (!this.isRowVisible(docRow)) {
// As the line is not visible, getFoldData will return only a
// single fold and also not an array.
var lfold = this.getFoldData(docRow);
docRow = lfold.start.row;
docColumn = lfold.start.column;
foldLine = this.getFoldLine(docRow);
if (foldLine) {
folds = foldLine.folds;
for (var i = 0; i < folds.length; i++) {
fold = folds[i];
comp = fold.compare(docRow, docColumn);
if (comp == 0) {
docRow = fold.start.row;
docColumn = fold.start.column;
break;
} else if (comp == -1) {
break;
}
}
foldLine = null;
}
for (var row = 0; row < docRow; row++) {
var fold = this.getRowLastFold(row);
if (fold && !fold.sameLine) {
if (foldStartRow == null) {
foldStartRow = row;
foldLine = this.getFoldLine(row);
if (foldLine) {
if (foldLine.end.row >= docRow) {
break;
}
row = fold.end.row - 1;
row = foldLine.end.row;
screenRow += foldLine.getRowLength();
} else {
screenRow += this.getRowLength(row);
foldStartRow = null;
}
}
// Calculate the text line that is displayed in docRow on the screen.
var textLine = "";
foldLine = this.getFoldLine(docRow);
// Check if the final row we want to reach is inside of a fold.
if (!this.isRowFolded(docRow)) {
if (!foldLine) {
textLine = this.getLine(docRow).substring(0, docColumn);
foldStartRow = docRow;
} else {
textLine = this.$buildFoldedTextLine(
(foldStartRow != null ? foldStartRow : docRow),
foldLine,
docRow,
docColumn);
}
@ -1453,67 +1443,87 @@ var EditSession = function(text, mode) {
this.sameLine = range.start.row == range.end.row;
}
function FoldRow(row) {
this.row = row;
this.linkIn = null;
this.linkOut = null;
this.inRow = [];
Fold.prototype.contains = function(row, column) {
if (this.end.row == row && this.end.column == column) {
return false;
}
return this.range.contains(row, column);
}
Fold.prototype.compare = function(row, column) {
if (this.end.row == row && this.end.column == column) {
return 1;
}
return this.range.compare(row, column);
}
function FoldLine(fold) {
this.folds = [fold];
this.range = fold.range.clone();
this.start = this.range.start;
this.end = this.range.end;
}
(function() {
this.addInRowFold = function(fold) {
if (!fold.sameLine || fold.start.row != this.row) {
throw "NOPE: Can't add fold here!";
}
this.inRow.push(fold);
this.inRow.sort(function(a, b) {
return a.start.column - b.start.column;
});
}
this.addFold = function(fold) {
if (fold.sameLine) {
this.addInRowFold(fold)
} else if (fold.start.row == this.row) {
if (this.linkOut) {
throw "There is already a linkOut fold";
if (fold.start.row < this.startRow || fold.endRow > this.endRow) {
throw "Can't add a fold to this FoldLine as it has no connection";
}
this.linkOut = fold;
} else if (fold.end.row == this.row) {
if (this.linkIn) {
throw "There is already a linkIn fold";
}
this.linkIn = fold;
this.folds.push(fold);
this.folds.sort(function(a, b) {
if (a.sameLine && b.sameLine) {
if (a.start.row == b.start.row) {
return a.start.colun - b.start.column;
} else {
return a.start.row - b.start.row;
}
} else if (!a.sameLine && !b.sameLine) {
return a.end.row - b.start.row;
} else if (!a.sameLine) {
return a.end.row - b.start.row;
} else if (!b.sameLine) {
return b.end.row - a.start.row;
}
});
} else if (fold.start.row == this.endRow) {
this.folds.push(fold);
this.end.row = fold.end.row;
this.end.column = fold.end.column;
} else if (fold.end.row == this.startRow) {
this.folds.unshift(fold);
this.start.row = fold.start.row;
this.start.column = fold.start.column;
} else {
throw "Trying to add fold to FoldRow that doesn't have a matching row";
}
}
}).call(FoldRow.prototype);
this.getFoldRow = function(docRow, startFoldRow) {
this.getRowLength = function() {
// TODO: Add support for wrapped lines here.
return 1;
}
this.getSplitData = function() {
// TODO: Add support for wrapped lines here.
return undefined;
}
}).call(FoldLine.prototype);
this.getFoldLine = function(docRow, startFoldLine) {
var foldData = this.$foldData;
for (var i = foldData.indexOf(startFoldRow) || 0; i < foldData.length; i++) {
var foldRow = foldData[i];
if (foldRow.row == docRow) {
return foldRow;
} else if (foldRow.row > docRow) {
var i = Math.max(foldData.indexOf(startFoldLine), 0);
for (i; i < foldData.length; i++) {
var foldLine = foldData[i];
if (foldLine.start.row >= docRow || foldLine.end.row >= docRow) {
return foldLine;
} else if (foldLine.end.row > docRow) {
return null;
}
}
return null;
}
this.getFoldRowOrCreate = function(docRow, startFoldRow) {
var foldRow = this.getFoldRow(docRow, startFoldRow);
if (!foldRow) {
foldRow = new FoldRow(startRow);
this.$foldData.push(foldRow);
this.$foldData.sort(function(a, b) {
return a.row - b.row;
});
}
}
/**
* Adds a new fold.
*/
@ -1523,16 +1533,46 @@ var EditSession = function(text, mode) {
foldData = this.$foldData,
foldRow = null;
foldRow = this.getFoldRowOrCreate(startRow);
var fold = new Fold(range, placeholder);
foldRow.addFold(fold);
var added = false;
// If this fold folds more then one line, then add this fold as the
// linkOut of the FoldRow at endRow.
if (!fold.sameLine) {
foldRow = this.getFoldRowOrCreate(endRow, foldRow);
foldRow.addFold(fold);
// For now we assume that no two folds are created for the same range!
for (var i = 0; i < foldData.length; i++) {
var foldLine = foldData[i];
if (endRow == foldLine.start.row) {
foldLine.addFold(fold);
added = true;
break;
} else if (startRow == foldLine.end.row) {
foldLine.addFold(fold);
added = true;
if (!fold.sameLine) {
// Check if we might have to merge two FoldLines.
foldLineNext = foldData[i + 1];
if (foldLineNext && foldLineNext.start.row == endRow) {
// We need to merge!
var nextFolds = foldLineNext.folds;
for (var i = 0; i < nextFolds.length; i++) {
foldLine.addFold(nextFolds[i]);
}
// Remove the foldLineNext - no longer needed, as
// it's merged now with foldLine.
foldData.splice(foldData.indexOf(foldLineNext), 1);
break;
}
}
break;
} else if (endRow <= foldLine.start.row) {
break;
}
}
if (!added) {
foldLine = new FoldLine(fold);
foldData.push(foldLine);
foldData.sort(function(a, b) {
return a.start.row - b.start.row;
});
}
// TODO: Recalculate wrapData
@ -1548,36 +1588,16 @@ var EditSession = function(text, mode) {
* folded parts such that some parts of the line is still visible.
**/
this.isRowFolded = function(docRow, startFoldRow) {
return !!this.getFoldRow(docRow, startFoldRow);
return !!this.getFoldLine(docRow, startFoldRow);
};
this.getRowFoldEnd = function(docRow, startFoldRow) {
var foldRow = this.getFoldRow(docRow, startFoldRow);
return (foldRow
? (foldRow.linkOut ? foldRow.linkOut.end.row : docRow)
: docRow);
var foldLine = this.getFoldLine(docRow, startFoldRow);
return (foldLine
? foldLine.end.row
: docRow)
};
this.buildFoldRowLine = function(docRow, startFoldRow) {
var foldRow = this.getFoldRow(docRow, startFoldRow);
var folds = [];
if (!foldRow) {
throw "Passed in docRow doesn't have a foldRow";
}
if (foldRow.linkIn) {
throw "Can't build a single fold line starting at a FoldRow that is linked in!";
}
var foldData = this.$foldData;
for (var i = foldData.indexOf(foldRow); foldRow; i++) {
if (foldRow.linkIn) {
folds.push(foldRow.linkIn);
}
foldRow = foldRow.linkOut;
}
}
// this.findRowFoldedForward = function(start, limit) {
// if (limit == null) {
// limit = this.getLength();

View file

@ -407,6 +407,9 @@ module.exports = {
assertDoc2Screen(2, 25, 1, 32);
assertDoc2Screen(2, 26, 1, 33);
assertDoc2Screen(2, 99, 1, 40);
// Test one position after the folds. Should be all like normal.
assertDoc2Screen(3, 0, 2, 0);
}
};

View file

@ -21,7 +21,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
* Julian Viereck <julian DOT viereck AT gmail DOT com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or

View file

@ -21,7 +21,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Julian Viereck <julian.viereck@gmail.com>
* Julian Viereck <julian DOT viereck AT gmail DOT com>
* Mihai Sucan <mihai.sucan@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
@ -401,9 +401,9 @@ var Text = function(parentEl) {
// Nothing to do if the entire line is folded.
// TODO: Remove this, once the folding feature is done. Only for
// developing stuff at the moment.
if (!this.session.isRowVisible(row)) {
throw "Calling renderLine on folded line doesn't make sense?";
}
// if (!this.session.isRowVisible(row)) {
// throw "Calling renderLine on folded line doesn't make sense?";
// }
// Check if the line to render is folded or not. If not, things are
// simple, otherwise, we need to fake some things...
@ -417,7 +417,8 @@ var Text = function(parentEl) {
this.$renderFoldLine = function(stringBuilder, row, tokens) {
var session = this.session,
folds = session.getFoldData(row);
foldLine = session.getFoldLine(row),
folds = foldLine.folds;
var renderTokens = [];
@ -461,9 +462,11 @@ var Text = function(parentEl) {
}
}
var lastCol = 0;
var lastCol = 0,
fold = null;
for (var i = 0; i < folds.length; i++) {
var fold = folds[i];
fold = folds[i];
addTokens(tokens, lastCol, fold.start.column);
renderTokens.push({
type: "fold",
@ -471,21 +474,17 @@ var Text = function(parentEl) {
});
if (fold.start.row != fold.end.row) {
row = fold.end.row;
tokens = this.tokenizer.getTokens(row, row)[0].tokens;
folds = session.getFoldData(row);
// Skip the first fold as it's only referencing to the current
// fold that is already rendered.
i = 0;
tokens = this.tokenizer.getTokens(
fold.end.row, fold.end.row)[0].tokens;
}
lastCol = fold.end.column;
}
// Add the rest of the line
addTokens(tokens, lastCol, session.getLine(row).length);
addTokens(tokens, lastCol, session.getLine(foldLine.end.row).length);
// TODO: Build a fake splits array!
var splits = false;
var splits = foldLine.getSplitData();
this.$renderLineCore(stringBuilder, row, renderTokens, splits);
};