Splitting edit_session.js up - moving the folding code parts into edit_session/fold_line.js|folding.js.
This commit is contained in:
parent
46a877e563
commit
79678b5e96
3 changed files with 648 additions and 562 deletions
|
|
@ -1492,569 +1492,9 @@ var EditSession = function(text, mode) {
|
||||||
c >= 0xFFE0 && c <= 0xFFE6;
|
c >= 0xFFE0 && c <= 0xFFE6;
|
||||||
};
|
};
|
||||||
|
|
||||||
// == Folding Code ========================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple fold-data struct.
|
|
||||||
**/
|
|
||||||
function Fold(range, placeholder) {
|
|
||||||
this.foldLine = null;
|
|
||||||
this.placeholder = placeholder;
|
|
||||||
this.range = range;
|
|
||||||
this.start = range.start;
|
|
||||||
this.end = range.end;
|
|
||||||
|
|
||||||
this.sameRow = range.start.row == range.end.row;
|
|
||||||
}
|
|
||||||
|
|
||||||
Fold.prototype.toString = function() {
|
|
||||||
return '"' + this.placeholder + '" ' + this.range.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new FoldLine. If the an array is passed in, the folds are
|
|
||||||
* expected to be sorted already.
|
|
||||||
*/
|
|
||||||
function FoldLine(foldData, folds) {
|
|
||||||
this.foldData = foldData;
|
|
||||||
if (Array.isArray(folds)) {
|
|
||||||
this.folds = folds;
|
|
||||||
} else {
|
|
||||||
folds = this.folds = [ folds ];
|
|
||||||
}
|
|
||||||
|
|
||||||
var last = folds[folds.length - 1]
|
|
||||||
this.range = new Range(folds[0].start.row, folds[0].start.column,
|
|
||||||
last.end.row, last.end.column);
|
|
||||||
this.start = this.range.start;
|
|
||||||
this.end = this.range.end;
|
|
||||||
|
|
||||||
this.folds.forEach(function(fold) {
|
|
||||||
fold.foldLine = this;
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
/**
|
|
||||||
* Note: This doesn't update wrapData!
|
|
||||||
*/
|
|
||||||
this.shiftRow = function(shift) {
|
|
||||||
this.start.row += shift;
|
|
||||||
this.end.row += shift;
|
|
||||||
this.folds.forEach(function(fold) {
|
|
||||||
fold.start.row += shift;
|
|
||||||
fold.end.row += shift;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addFold = function(fold) {
|
|
||||||
if (fold.sameRow) {
|
|
||||||
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.folds.push(fold);
|
|
||||||
this.folds.sort(function(a, b) {
|
|
||||||
return -a.range.compareEnd(b.start.row, b.start.column);
|
|
||||||
});
|
|
||||||
if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) {
|
|
||||||
this.end.row = fold.end.row;
|
|
||||||
this.end.column = fold.end.column;
|
|
||||||
} else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) {
|
|
||||||
this.start.row = fold.start.row;
|
|
||||||
this.start.column = fold.start.column;
|
|
||||||
}
|
|
||||||
} else if (fold.start.row == this.end.row) {
|
|
||||||
this.folds.push(fold);
|
|
||||||
this.end.row = fold.end.row;
|
|
||||||
this.end.column = fold.end.column;
|
|
||||||
} else if (fold.end.row == this.start.row) {
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
fold.foldLine = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.containsRow = function(row) {
|
|
||||||
return row >= this.start.row && row <= this.end.row;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.walk = function(callback, endRow, endColumn) {
|
|
||||||
var lastEnd = 0,
|
|
||||||
folds = this.folds,
|
|
||||||
fold,
|
|
||||||
comp, stop, isNewRow = true;
|
|
||||||
|
|
||||||
if (endRow == null) {
|
|
||||||
endRow = this.end.row;
|
|
||||||
endColumn = this.end.column;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < folds.length; i++) {
|
|
||||||
fold = folds[i];
|
|
||||||
|
|
||||||
comp = fold.range.compareEnd(endRow, endColumn);
|
|
||||||
// This fold is after the endRow/Column.
|
|
||||||
if (comp == -1) {
|
|
||||||
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The endRow/Column is inside of the current fold.
|
|
||||||
else if (comp == 0) {
|
|
||||||
callback(null, endRow, fold.start.column, lastEnd, isNewRow);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow);
|
|
||||||
stop = stop || callback(fold.placeholder, null, null, lastEnd);
|
|
||||||
|
|
||||||
if (stop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note the new lastEnd might not be on the same line. However,
|
|
||||||
// it's the callback's job to recognize this.
|
|
||||||
isNewRow = !fold.sameRow;
|
|
||||||
lastEnd = fold.end.column;
|
|
||||||
}
|
|
||||||
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getNextFoldTo = function(row, column) {
|
|
||||||
var fold, cmp;
|
|
||||||
for (var i = 0; i < this.folds.length; i++) {
|
|
||||||
fold = this.folds[i];
|
|
||||||
cmp = fold.range.compareEnd(row, column);
|
|
||||||
if (cmp == -1) {
|
|
||||||
return {
|
|
||||||
fold: fold,
|
|
||||||
kind: "after"
|
|
||||||
};
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
return {
|
|
||||||
fold: fold,
|
|
||||||
kind: "inside"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addRemoveChars = function(row, column, len) {
|
|
||||||
var ret = this.getNextFoldTo(row, column),
|
|
||||||
fold, folds;
|
|
||||||
if (ret) {
|
|
||||||
fold = ret.fold;
|
|
||||||
if (ret.kind == "inside"
|
|
||||||
&& fold.start.column != column
|
|
||||||
&& fold.start.row != row)
|
|
||||||
{
|
|
||||||
throw "Moving characters inside of a fold should never be reached";
|
|
||||||
} else if (fold.start.row == row) {
|
|
||||||
folds = this.folds;
|
|
||||||
var i = folds.indexOf(fold);
|
|
||||||
if (i == 0) {
|
|
||||||
this.start.column += len;
|
|
||||||
}
|
|
||||||
for (i; i < folds.length; i++) {
|
|
||||||
fold = folds[i];
|
|
||||||
fold.start.column += len;
|
|
||||||
if (!fold.sameRow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fold.end.column += len;
|
|
||||||
}
|
|
||||||
this.end.column += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.split = function(row, column) {
|
|
||||||
var fold = this.getNextFoldTo(row, column).fold,
|
|
||||||
folds = this.folds;
|
|
||||||
var foldData = this.foldData;
|
|
||||||
|
|
||||||
if (!fold) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var i = folds.indexOf(fold);
|
|
||||||
var foldBefore = folds[i - 1];
|
|
||||||
this.end.row = foldBefore.end.row;
|
|
||||||
this.end.column = foldBefore.end.column;
|
|
||||||
|
|
||||||
// Remove the folds after row/column and create a new FoldLine
|
|
||||||
// containing these removed folds.
|
|
||||||
folds = folds.splice(i, folds.length - i);
|
|
||||||
|
|
||||||
var newFoldLine = new FoldLine(foldData, folds);
|
|
||||||
foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine);
|
|
||||||
return newFoldLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.merge = function(foldLineNext) {
|
|
||||||
var folds = foldLineNext.folds;
|
|
||||||
for (var i = 0; i < folds.length; i++) {
|
|
||||||
this.addFold(folds[i]);
|
|
||||||
}
|
|
||||||
// Remove the foldLineNext - no longer needed, as
|
|
||||||
// it's merged now with foldLineNext.
|
|
||||||
var foldData = this.foldData;
|
|
||||||
foldData.splice(foldData.indexOf(foldLineNext), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toString = function() {
|
|
||||||
var ret = [this.range.toString() + ": [" ];
|
|
||||||
|
|
||||||
this.folds.forEach(function(fold) {
|
|
||||||
ret.push(" " + fold.toString());
|
|
||||||
});
|
|
||||||
ret.push("]")
|
|
||||||
return ret.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.idxToPosition = function(idx) {
|
|
||||||
var lastFoldEndColumn = 0;
|
|
||||||
var fold;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.folds.length; i++) {
|
|
||||||
var fold = this.folds[i];
|
|
||||||
|
|
||||||
idx -= fold.start.column - lastFoldEndColumn;
|
|
||||||
if (idx < 0) {
|
|
||||||
return {
|
|
||||||
row: fold.start.row,
|
|
||||||
column: fold.start.column + idx
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
idx -= fold.placeholder.length;
|
|
||||||
if (idx < 0) {
|
|
||||||
return fold.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastFoldEndColumn = fold.end.column;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
row: this.end.row,
|
|
||||||
column: this.end.column + idx
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}).call(FoldLine.prototype);
|
|
||||||
|
|
||||||
FoldLine.prototype.__defineGetter__("rangeDBG", function() {
|
|
||||||
return this.range + "";
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up a fold at a given row/column. Possible values for side:
|
|
||||||
* -1: ignore a fold if fold.start = row/column
|
|
||||||
* +1: ignore a fold if fold.end = row/column
|
|
||||||
*/
|
|
||||||
this.getFoldAt = function(row, column, side) {
|
|
||||||
var foldLine = this.getFoldLine(row);
|
|
||||||
if (foldLine) {
|
|
||||||
var folds = foldLine.folds,
|
|
||||||
fold;
|
|
||||||
|
|
||||||
for (var i = 0; i < folds.length; i++) {
|
|
||||||
fold = folds[i];
|
|
||||||
if (fold.range.contains(row, column)) {
|
|
||||||
if (side == 1 && fold.range.isEnd(row, column)) {
|
|
||||||
continue;
|
|
||||||
} else if (side == -1 && fold.range.isStart(row, column)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return fold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all folds in the given range. Note, that this will return folds
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
this.getFoldsInRange = function(range) {
|
|
||||||
range = range.clone();
|
|
||||||
var start = range.start,
|
|
||||||
end = range.end;
|
|
||||||
var foldLines = this.$foldData,
|
|
||||||
folds,
|
|
||||||
fold;
|
|
||||||
var cmp,
|
|
||||||
foundFolds = [];
|
|
||||||
|
|
||||||
start.column += 1;
|
|
||||||
end.column -= 1;
|
|
||||||
|
|
||||||
for (var i = 0; i < foldLines.length; i++) {
|
|
||||||
cmp = foldLines[i].range.compareRange(range);
|
|
||||||
// Range is before foldLine. No intersection. This means,
|
|
||||||
// there might be other foldLines that intersect.
|
|
||||||
if (cmp == 2) {
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
// Range is after foldLine. There can't be any other foldLines then,
|
|
||||||
// so let's give up.
|
|
||||||
if (cmp == -2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
folds = foldLines[i].folds;
|
|
||||||
for (var j = 0; j < folds.length; j++) {
|
|
||||||
fold = folds[j];
|
|
||||||
cmp = fold.range.compareRange(range);
|
|
||||||
if (cmp == -2) {
|
|
||||||
break;
|
|
||||||
} else if (cmp == 2) {
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
// WTF-state: Can happen due to -1/+1 to start/end column.
|
|
||||||
if (cmp == 42) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
foundFolds.push(fold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundFolds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the string between folds at the given position.
|
|
||||||
* E.g.
|
|
||||||
* foo<fold>b|ar<fold>wolrd -> "bar"
|
|
||||||
* foo<fold>bar<fold>wol|rd -> "world"
|
|
||||||
* foo<fold>bar<fo|ld>wolrd -> <null>
|
|
||||||
*
|
|
||||||
* where | means the position of row/column
|
|
||||||
*
|
|
||||||
* The trim option determs if the return string should be trimed according
|
|
||||||
* to the "side" passed with the trim value:
|
|
||||||
*
|
|
||||||
* E.g.
|
|
||||||
* foo<fold>b|ar<fold>wolrd -trim=-1> "b"
|
|
||||||
* foo<fold>bar<fold>wol|rd -trim=+1> "rld"
|
|
||||||
* fo|o<fold>bar<fold>wolrd -trim=00> "foo"
|
|
||||||
*/
|
|
||||||
this.getFoldStringAt = function(row, column, trim, foldLine) {
|
|
||||||
var foldLine = foldLine || this.getFoldLine(row);
|
|
||||||
if (!foldLine) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
var fold, lastFold, cmp, str;
|
|
||||||
lastFold = {
|
|
||||||
end: { column: 0 }
|
|
||||||
};
|
|
||||||
// TODO: Refactor to use getNextFoldTo function.
|
|
||||||
for (var i = 0; i < foldLine.folds.length; i++) {
|
|
||||||
fold = foldLine.folds[i];
|
|
||||||
cmp = fold.range.compareEnd(row, column);
|
|
||||||
if (cmp == -1) {
|
|
||||||
str = this.getLine(fold.start.row).
|
|
||||||
substring(lastFold.end.column, fold.start.column);
|
|
||||||
break;
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
lastFold = fold;
|
|
||||||
}
|
|
||||||
if (!str) {
|
|
||||||
str = this.getLine(fold.start.row).
|
|
||||||
substring(lastFold.end.column);
|
|
||||||
}
|
|
||||||
if (trim == -1) {
|
|
||||||
return str.substring(0, column - lastFold.end.column);
|
|
||||||
} else if (trim == 1) {
|
|
||||||
return str.substring(column - lastFold.end.column)
|
|
||||||
} else {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getFoldLine = function(docRow, startFoldLine) {
|
|
||||||
var foldData = this.$foldData;
|
|
||||||
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.$addFoldLine = function(foldLine) {
|
|
||||||
this.$foldData.push(foldLine);
|
|
||||||
this.$foldData.sort(function(a, b) {
|
|
||||||
return a.start.row - b.start.row;
|
|
||||||
});
|
|
||||||
return foldLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new fold.
|
|
||||||
*/
|
|
||||||
this.addFold = function(range, placeholder) {
|
|
||||||
var startRow = range.start.row,
|
|
||||||
endRow = range.end.row,
|
|
||||||
foldData = this.$foldData,
|
|
||||||
foldRow = null;
|
|
||||||
var foldLine;
|
|
||||||
|
|
||||||
var fold = new Fold(range, placeholder);
|
|
||||||
var added = false;
|
|
||||||
|
|
||||||
// For now we assume that no two folds are created for the same range!
|
|
||||||
for (var i = 0; i < foldData.length; i++) {
|
|
||||||
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.sameRow) {
|
|
||||||
// Check if we might have to merge two FoldLines.
|
|
||||||
foldLineNext = foldData[i + 1];
|
|
||||||
if (foldLineNext && foldLineNext.start.row == endRow) {
|
|
||||||
// We need to merge!
|
|
||||||
foldLine.merge(foldLineNext);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if (endRow <= foldLine.start.row) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!added) {
|
|
||||||
foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Recalculate wrapData
|
|
||||||
if (this.$useWrapMode) {
|
|
||||||
this.$updateWrapData(foldLine.start.row, foldLine.start.row);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify that fold data has changed.
|
|
||||||
this.$modified = true;
|
|
||||||
this._dispatchEvent("changeFold");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.removeFold = function(fold) {
|
|
||||||
var foldLine = fold.foldLine;
|
|
||||||
var startRow = foldLine.start.row;
|
|
||||||
var endRow = foldLine.end.row;
|
|
||||||
|
|
||||||
var foldLines = this.$foldData,
|
|
||||||
folds = foldLine.folds;
|
|
||||||
// Simple case where there is only one fold in the FoldLine such that
|
|
||||||
// the entire fold line can get removed directly.
|
|
||||||
if (folds.length == 1) {
|
|
||||||
foldLines.splice(foldLines.indexOf(foldLine), 1);
|
|
||||||
} else
|
|
||||||
// If the fold is the last fold of the foldLine, just remove it.
|
|
||||||
if (foldLine.range.isEnd(fold.end.row, fold.end.column)) {
|
|
||||||
folds.pop();
|
|
||||||
foldLine.end.row = folds[folds.length - 1].end.row;
|
|
||||||
foldLine.end.column = folds[folds.length - 1].end.column;
|
|
||||||
} else
|
|
||||||
// If the fold is the first fold of the foldLine, just remove it.
|
|
||||||
if (foldLine.range.isStart(fold.start.row, fold.start.column)) {
|
|
||||||
folds.shift();
|
|
||||||
foldLine.start.row = folds[0].start.row;
|
|
||||||
foldLine.start.column = folds[0].start.column;
|
|
||||||
} else
|
|
||||||
// We know there are more then 2 folds and the fold is not at the edge.
|
|
||||||
// This means, the fold is somewhere in between.
|
|
||||||
//
|
|
||||||
// If the fold is in one row, we just can remove it.
|
|
||||||
if (fold.sameRow) {
|
|
||||||
folds.splice(folds.indexOf(fold), 1);
|
|
||||||
} else
|
|
||||||
// The fold goes over more then one row. This means remvoing this fold
|
|
||||||
// will cause the fold line to get splitted up.
|
|
||||||
{
|
|
||||||
var newFoldLine = foldLine.split(fold.start.row, fold.start.column);
|
|
||||||
newFoldLine.folds.shift();
|
|
||||||
foldLine.start.row = folds[0].start.row;
|
|
||||||
foldLine.start.column = folds[0].start.column;
|
|
||||||
this.$addFoldLine(newFoldLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.$useWrapMode) {
|
|
||||||
this.$updateWrapData(startRow, endRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify that fold data has changed.
|
|
||||||
this.$modified = true;
|
|
||||||
this._dispatchEvent("changeFold");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeFolds = function(folds) {
|
|
||||||
// We need to clone the folds array passed in as it might be the folds
|
|
||||||
// array of a fold line and as we call this.removeFold(fold), folds
|
|
||||||
// are removed from folds and changes the current index.
|
|
||||||
var cloneFolds = [];
|
|
||||||
for (var i = 0; i < folds.length; i++) {
|
|
||||||
cloneFolds.push(folds[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cloneFolds.forEach(function(fold) {
|
|
||||||
this.removeFold(fold);
|
|
||||||
}, this);
|
|
||||||
this.$modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a given documentRow is folded. This is true if there are some
|
|
||||||
* folded parts such that some parts of the line is still visible.
|
|
||||||
**/
|
|
||||||
this.isRowFolded = function(docRow, startFoldRow) {
|
|
||||||
return !!this.getFoldLine(docRow, startFoldRow);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getRowFoldEnd = function(docRow, startFoldRow) {
|
|
||||||
var foldLine = this.getFoldLine(docRow, startFoldRow);
|
|
||||||
return (foldLine
|
|
||||||
? foldLine.end.row
|
|
||||||
: docRow)
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getFoldDisplayLine = function(foldLine, endRow, endColumn) {
|
|
||||||
if (endRow == null) {
|
|
||||||
endRow = foldLine.end.row;
|
|
||||||
endColumn = this.getLine(endRow).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the textline using the FoldLine walker.
|
|
||||||
var line = "",
|
|
||||||
textLine = "";
|
|
||||||
|
|
||||||
foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
|
|
||||||
if (placeholder) {
|
|
||||||
textLine += placeholder;
|
|
||||||
} else {
|
|
||||||
if (isNewRow) {
|
|
||||||
line = this.getLine(row);
|
|
||||||
}
|
|
||||||
textLine += line.substring(lastColumn, column);
|
|
||||||
}
|
|
||||||
}.bind(this), endRow, endColumn);
|
|
||||||
return textLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
}).call(EditSession.prototype);
|
}).call(EditSession.prototype);
|
||||||
|
|
||||||
|
require("ace/edit_session/folding").Folding.call(EditSession.prototype);
|
||||||
|
|
||||||
exports.EditSession = EditSession;
|
exports.EditSession = EditSession;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
276
lib/ace/edit_session/fold_line.js
Normal file
276
lib/ace/edit_session/fold_line.js
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
/* vim:ts=4:sts=4:sw=4:
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Ajax.org Code Editor (ACE).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Ajax.org B.V.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* 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
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var Range = require("ace/range").Range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the an array is passed in, the folds are expected to be sorted already.
|
||||||
|
*/
|
||||||
|
function FoldLine(foldData, folds) {
|
||||||
|
this.foldData = foldData;
|
||||||
|
if (Array.isArray(folds)) {
|
||||||
|
this.folds = folds;
|
||||||
|
} else {
|
||||||
|
folds = this.folds = [ folds ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var last = folds[folds.length - 1]
|
||||||
|
this.range = new Range(folds[0].start.row, folds[0].start.column,
|
||||||
|
last.end.row, last.end.column);
|
||||||
|
this.start = this.range.start;
|
||||||
|
this.end = this.range.end;
|
||||||
|
|
||||||
|
this.folds.forEach(function(fold) {
|
||||||
|
fold.foldLine = this;
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
/**
|
||||||
|
* Note: This doesn't update wrapData!
|
||||||
|
*/
|
||||||
|
this.shiftRow = function(shift) {
|
||||||
|
this.start.row += shift;
|
||||||
|
this.end.row += shift;
|
||||||
|
this.folds.forEach(function(fold) {
|
||||||
|
fold.start.row += shift;
|
||||||
|
fold.end.row += shift;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addFold = function(fold) {
|
||||||
|
if (fold.sameRow) {
|
||||||
|
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.folds.push(fold);
|
||||||
|
this.folds.sort(function(a, b) {
|
||||||
|
return -a.range.compareEnd(b.start.row, b.start.column);
|
||||||
|
});
|
||||||
|
if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) {
|
||||||
|
this.end.row = fold.end.row;
|
||||||
|
this.end.column = fold.end.column;
|
||||||
|
} else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) {
|
||||||
|
this.start.row = fold.start.row;
|
||||||
|
this.start.column = fold.start.column;
|
||||||
|
}
|
||||||
|
} else if (fold.start.row == this.end.row) {
|
||||||
|
this.folds.push(fold);
|
||||||
|
this.end.row = fold.end.row;
|
||||||
|
this.end.column = fold.end.column;
|
||||||
|
} else if (fold.end.row == this.start.row) {
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
fold.foldLine = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.containsRow = function(row) {
|
||||||
|
return row >= this.start.row && row <= this.end.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.walk = function(callback, endRow, endColumn) {
|
||||||
|
var lastEnd = 0,
|
||||||
|
folds = this.folds,
|
||||||
|
fold,
|
||||||
|
comp, stop, isNewRow = true;
|
||||||
|
|
||||||
|
if (endRow == null) {
|
||||||
|
endRow = this.end.row;
|
||||||
|
endColumn = this.end.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < folds.length; i++) {
|
||||||
|
fold = folds[i];
|
||||||
|
|
||||||
|
comp = fold.range.compareEnd(endRow, endColumn);
|
||||||
|
// This fold is after the endRow/Column.
|
||||||
|
if (comp == -1) {
|
||||||
|
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The endRow/Column is inside of the current fold.
|
||||||
|
else if (comp == 0) {
|
||||||
|
callback(null, endRow, fold.start.column, lastEnd, isNewRow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow);
|
||||||
|
stop = stop || callback(fold.placeholder, null, null, lastEnd);
|
||||||
|
|
||||||
|
if (stop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note the new lastEnd might not be on the same line. However,
|
||||||
|
// it's the callback's job to recognize this.
|
||||||
|
isNewRow = !fold.sameRow;
|
||||||
|
lastEnd = fold.end.column;
|
||||||
|
}
|
||||||
|
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getNextFoldTo = function(row, column) {
|
||||||
|
var fold, cmp;
|
||||||
|
for (var i = 0; i < this.folds.length; i++) {
|
||||||
|
fold = this.folds[i];
|
||||||
|
cmp = fold.range.compareEnd(row, column);
|
||||||
|
if (cmp == -1) {
|
||||||
|
return {
|
||||||
|
fold: fold,
|
||||||
|
kind: "after"
|
||||||
|
};
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
return {
|
||||||
|
fold: fold,
|
||||||
|
kind: "inside"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addRemoveChars = function(row, column, len) {
|
||||||
|
var ret = this.getNextFoldTo(row, column),
|
||||||
|
fold, folds;
|
||||||
|
if (ret) {
|
||||||
|
fold = ret.fold;
|
||||||
|
if (ret.kind == "inside"
|
||||||
|
&& fold.start.column != column
|
||||||
|
&& fold.start.row != row)
|
||||||
|
{
|
||||||
|
throw "Moving characters inside of a fold should never be reached";
|
||||||
|
} else if (fold.start.row == row) {
|
||||||
|
folds = this.folds;
|
||||||
|
var i = folds.indexOf(fold);
|
||||||
|
if (i == 0) {
|
||||||
|
this.start.column += len;
|
||||||
|
}
|
||||||
|
for (i; i < folds.length; i++) {
|
||||||
|
fold = folds[i];
|
||||||
|
fold.start.column += len;
|
||||||
|
if (!fold.sameRow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fold.end.column += len;
|
||||||
|
}
|
||||||
|
this.end.column += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.split = function(row, column) {
|
||||||
|
var fold = this.getNextFoldTo(row, column).fold,
|
||||||
|
folds = this.folds;
|
||||||
|
var foldData = this.foldData;
|
||||||
|
|
||||||
|
if (!fold) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var i = folds.indexOf(fold);
|
||||||
|
var foldBefore = folds[i - 1];
|
||||||
|
this.end.row = foldBefore.end.row;
|
||||||
|
this.end.column = foldBefore.end.column;
|
||||||
|
|
||||||
|
// Remove the folds after row/column and create a new FoldLine
|
||||||
|
// containing these removed folds.
|
||||||
|
folds = folds.splice(i, folds.length - i);
|
||||||
|
|
||||||
|
var newFoldLine = new FoldLine(foldData, folds);
|
||||||
|
foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine);
|
||||||
|
return newFoldLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.merge = function(foldLineNext) {
|
||||||
|
var folds = foldLineNext.folds;
|
||||||
|
for (var i = 0; i < folds.length; i++) {
|
||||||
|
this.addFold(folds[i]);
|
||||||
|
}
|
||||||
|
// Remove the foldLineNext - no longer needed, as
|
||||||
|
// it's merged now with foldLineNext.
|
||||||
|
var foldData = this.foldData;
|
||||||
|
foldData.splice(foldData.indexOf(foldLineNext), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toString = function() {
|
||||||
|
var ret = [this.range.toString() + ": [" ];
|
||||||
|
|
||||||
|
this.folds.forEach(function(fold) {
|
||||||
|
ret.push(" " + fold.toString());
|
||||||
|
});
|
||||||
|
ret.push("]")
|
||||||
|
return ret.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.idxToPosition = function(idx) {
|
||||||
|
var lastFoldEndColumn = 0;
|
||||||
|
var fold;
|
||||||
|
|
||||||
|
for (var i = 0; i < this.folds.length; i++) {
|
||||||
|
var fold = this.folds[i];
|
||||||
|
|
||||||
|
idx -= fold.start.column - lastFoldEndColumn;
|
||||||
|
if (idx < 0) {
|
||||||
|
return {
|
||||||
|
row: fold.start.row,
|
||||||
|
column: fold.start.column + idx
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
idx -= fold.placeholder.length;
|
||||||
|
if (idx < 0) {
|
||||||
|
return fold.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFoldEndColumn = fold.end.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
row: this.end.row,
|
||||||
|
column: this.end.column + idx
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}).call(FoldLine.prototype);
|
||||||
|
|
||||||
|
exports.FoldLine = FoldLine;
|
||||||
|
});
|
||||||
370
lib/ace/edit_session/folding.js
Normal file
370
lib/ace/edit_session/folding.js
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
/* vim:ts=4:sts=4:sw=4:
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Ajax.org Code Editor (ACE).
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Ajax.org B.V.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* 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
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var FoldLine = require("ace/edit_session/fold_line").FoldLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple fold-data struct.
|
||||||
|
**/
|
||||||
|
function Fold(range, placeholder) {
|
||||||
|
this.foldLine = null;
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
this.range = range;
|
||||||
|
this.start = range.start;
|
||||||
|
this.end = range.end;
|
||||||
|
|
||||||
|
this.sameRow = range.start.row == range.end.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fold.prototype.toString = function() {
|
||||||
|
return '"' + this.placeholder + '" ' + this.range.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Folding() {
|
||||||
|
/**
|
||||||
|
* Looks up a fold at a given row/column. Possible values for side:
|
||||||
|
* -1: ignore a fold if fold.start = row/column
|
||||||
|
* +1: ignore a fold if fold.end = row/column
|
||||||
|
*/
|
||||||
|
this.getFoldAt = function(row, column, side) {
|
||||||
|
var foldLine = this.getFoldLine(row);
|
||||||
|
if (foldLine) {
|
||||||
|
var folds = foldLine.folds,
|
||||||
|
fold;
|
||||||
|
|
||||||
|
for (var i = 0; i < folds.length; i++) {
|
||||||
|
fold = folds[i];
|
||||||
|
if (fold.range.contains(row, column)) {
|
||||||
|
if (side == 1 && fold.range.isEnd(row, column)) {
|
||||||
|
continue;
|
||||||
|
} else if (side == -1 && fold.range.isStart(row, column)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return fold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all folds in the given range. Note, that this will return folds
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.getFoldsInRange = function(range) {
|
||||||
|
range = range.clone();
|
||||||
|
var start = range.start,
|
||||||
|
end = range.end;
|
||||||
|
var foldLines = this.$foldData,
|
||||||
|
folds,
|
||||||
|
fold;
|
||||||
|
var cmp,
|
||||||
|
foundFolds = [];
|
||||||
|
|
||||||
|
start.column += 1;
|
||||||
|
end.column -= 1;
|
||||||
|
|
||||||
|
for (var i = 0; i < foldLines.length; i++) {
|
||||||
|
cmp = foldLines[i].range.compareRange(range);
|
||||||
|
// Range is before foldLine. No intersection. This means,
|
||||||
|
// there might be other foldLines that intersect.
|
||||||
|
if (cmp == 2) {
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
// Range is after foldLine. There can't be any other foldLines then,
|
||||||
|
// so let's give up.
|
||||||
|
if (cmp == -2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
folds = foldLines[i].folds;
|
||||||
|
for (var j = 0; j < folds.length; j++) {
|
||||||
|
fold = folds[j];
|
||||||
|
cmp = fold.range.compareRange(range);
|
||||||
|
if (cmp == -2) {
|
||||||
|
break;
|
||||||
|
} else if (cmp == 2) {
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
// WTF-state: Can happen due to -1/+1 to start/end column.
|
||||||
|
if (cmp == 42) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foundFolds.push(fold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundFolds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string between folds at the given position.
|
||||||
|
* E.g.
|
||||||
|
* foo<fold>b|ar<fold>wolrd -> "bar"
|
||||||
|
* foo<fold>bar<fold>wol|rd -> "world"
|
||||||
|
* foo<fold>bar<fo|ld>wolrd -> <null>
|
||||||
|
*
|
||||||
|
* where | means the position of row/column
|
||||||
|
*
|
||||||
|
* The trim option determs if the return string should be trimed according
|
||||||
|
* to the "side" passed with the trim value:
|
||||||
|
*
|
||||||
|
* E.g.
|
||||||
|
* foo<fold>b|ar<fold>wolrd -trim=-1> "b"
|
||||||
|
* foo<fold>bar<fold>wol|rd -trim=+1> "rld"
|
||||||
|
* fo|o<fold>bar<fold>wolrd -trim=00> "foo"
|
||||||
|
*/
|
||||||
|
this.getFoldStringAt = function(row, column, trim, foldLine) {
|
||||||
|
var foldLine = foldLine || this.getFoldLine(row);
|
||||||
|
if (!foldLine) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
var fold, lastFold, cmp, str;
|
||||||
|
lastFold = {
|
||||||
|
end: { column: 0 }
|
||||||
|
};
|
||||||
|
// TODO: Refactor to use getNextFoldTo function.
|
||||||
|
for (var i = 0; i < foldLine.folds.length; i++) {
|
||||||
|
fold = foldLine.folds[i];
|
||||||
|
cmp = fold.range.compareEnd(row, column);
|
||||||
|
if (cmp == -1) {
|
||||||
|
str = this.getLine(fold.start.row).
|
||||||
|
substring(lastFold.end.column, fold.start.column);
|
||||||
|
break;
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
lastFold = fold;
|
||||||
|
}
|
||||||
|
if (!str) {
|
||||||
|
str = this.getLine(fold.start.row).
|
||||||
|
substring(lastFold.end.column);
|
||||||
|
}
|
||||||
|
if (trim == -1) {
|
||||||
|
return str.substring(0, column - lastFold.end.column);
|
||||||
|
} else if (trim == 1) {
|
||||||
|
return str.substring(column - lastFold.end.column)
|
||||||
|
} else {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getFoldLine = function(docRow, startFoldLine) {
|
||||||
|
var foldData = this.$foldData;
|
||||||
|
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.$addFoldLine = function(foldLine) {
|
||||||
|
this.$foldData.push(foldLine);
|
||||||
|
this.$foldData.sort(function(a, b) {
|
||||||
|
return a.start.row - b.start.row;
|
||||||
|
});
|
||||||
|
return foldLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new fold.
|
||||||
|
*/
|
||||||
|
this.addFold = function(range, placeholder) {
|
||||||
|
var startRow = range.start.row,
|
||||||
|
endRow = range.end.row,
|
||||||
|
foldData = this.$foldData,
|
||||||
|
foldRow = null;
|
||||||
|
var foldLine;
|
||||||
|
|
||||||
|
var fold = new Fold(range, placeholder);
|
||||||
|
var added = false;
|
||||||
|
|
||||||
|
// For now we assume that no two folds are created for the same range!
|
||||||
|
for (var i = 0; i < foldData.length; i++) {
|
||||||
|
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.sameRow) {
|
||||||
|
// Check if we might have to merge two FoldLines.
|
||||||
|
foldLineNext = foldData[i + 1];
|
||||||
|
if (foldLineNext && foldLineNext.start.row == endRow) {
|
||||||
|
// We need to merge!
|
||||||
|
foldLine.merge(foldLineNext);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (endRow <= foldLine.start.row) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!added) {
|
||||||
|
foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Recalculate wrapData
|
||||||
|
if (this.$useWrapMode) {
|
||||||
|
this.$updateWrapData(foldLine.start.row, foldLine.start.row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify that fold data has changed.
|
||||||
|
this.$modified = true;
|
||||||
|
this._dispatchEvent("changeFold");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeFold = function(fold) {
|
||||||
|
var foldLine = fold.foldLine;
|
||||||
|
var startRow = foldLine.start.row;
|
||||||
|
var endRow = foldLine.end.row;
|
||||||
|
|
||||||
|
var foldLines = this.$foldData,
|
||||||
|
folds = foldLine.folds;
|
||||||
|
// Simple case where there is only one fold in the FoldLine such that
|
||||||
|
// the entire fold line can get removed directly.
|
||||||
|
if (folds.length == 1) {
|
||||||
|
foldLines.splice(foldLines.indexOf(foldLine), 1);
|
||||||
|
} else
|
||||||
|
// If the fold is the last fold of the foldLine, just remove it.
|
||||||
|
if (foldLine.range.isEnd(fold.end.row, fold.end.column)) {
|
||||||
|
folds.pop();
|
||||||
|
foldLine.end.row = folds[folds.length - 1].end.row;
|
||||||
|
foldLine.end.column = folds[folds.length - 1].end.column;
|
||||||
|
} else
|
||||||
|
// If the fold is the first fold of the foldLine, just remove it.
|
||||||
|
if (foldLine.range.isStart(fold.start.row, fold.start.column)) {
|
||||||
|
folds.shift();
|
||||||
|
foldLine.start.row = folds[0].start.row;
|
||||||
|
foldLine.start.column = folds[0].start.column;
|
||||||
|
} else
|
||||||
|
// We know there are more then 2 folds and the fold is not at the edge.
|
||||||
|
// This means, the fold is somewhere in between.
|
||||||
|
//
|
||||||
|
// If the fold is in one row, we just can remove it.
|
||||||
|
if (fold.sameRow) {
|
||||||
|
folds.splice(folds.indexOf(fold), 1);
|
||||||
|
} else
|
||||||
|
// The fold goes over more then one row. This means remvoing this fold
|
||||||
|
// will cause the fold line to get splitted up.
|
||||||
|
{
|
||||||
|
var newFoldLine = foldLine.split(fold.start.row, fold.start.column);
|
||||||
|
newFoldLine.folds.shift();
|
||||||
|
foldLine.start.row = folds[0].start.row;
|
||||||
|
foldLine.start.column = folds[0].start.column;
|
||||||
|
this.$addFoldLine(newFoldLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$useWrapMode) {
|
||||||
|
this.$updateWrapData(startRow, endRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify that fold data has changed.
|
||||||
|
this.$modified = true;
|
||||||
|
this._dispatchEvent("changeFold");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeFolds = function(folds) {
|
||||||
|
// We need to clone the folds array passed in as it might be the folds
|
||||||
|
// array of a fold line and as we call this.removeFold(fold), folds
|
||||||
|
// are removed from folds and changes the current index.
|
||||||
|
var cloneFolds = [];
|
||||||
|
for (var i = 0; i < folds.length; i++) {
|
||||||
|
cloneFolds.push(folds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneFolds.forEach(function(fold) {
|
||||||
|
this.removeFold(fold);
|
||||||
|
}, this);
|
||||||
|
this.$modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given documentRow is folded. This is true if there are some
|
||||||
|
* folded parts such that some parts of the line is still visible.
|
||||||
|
**/
|
||||||
|
this.isRowFolded = function(docRow, startFoldRow) {
|
||||||
|
return !!this.getFoldLine(docRow, startFoldRow);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getRowFoldEnd = function(docRow, startFoldRow) {
|
||||||
|
var foldLine = this.getFoldLine(docRow, startFoldRow);
|
||||||
|
return (foldLine
|
||||||
|
? foldLine.end.row
|
||||||
|
: docRow)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getFoldDisplayLine = function(foldLine, endRow, endColumn) {
|
||||||
|
if (endRow == null) {
|
||||||
|
endRow = foldLine.end.row;
|
||||||
|
endColumn = this.getLine(endRow).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the textline using the FoldLine walker.
|
||||||
|
var line = "",
|
||||||
|
textLine = "";
|
||||||
|
|
||||||
|
foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
|
||||||
|
if (placeholder) {
|
||||||
|
textLine += placeholder;
|
||||||
|
} else {
|
||||||
|
if (isNewRow) {
|
||||||
|
line = this.getLine(row);
|
||||||
|
}
|
||||||
|
textLine += line.substring(lastColumn, column);
|
||||||
|
}
|
||||||
|
}.bind(this), endRow, endColumn);
|
||||||
|
return textLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.Folding = Folding;
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue