split mode specific code for structured folding

into separate files
This commit is contained in:
Fabian Jakobs 2011-12-01 18:19:24 +01:00
commit bbcb00acc4
27 changed files with 567 additions and 452 deletions

View file

@ -112,7 +112,7 @@ function Folding() {
}
}
return foundFolds;
}
};
/**
* Returns the string between folds at the given position.
@ -132,7 +132,7 @@ function Folding() {
* fo|o<fold>bar<fold>wolrd -trim=00> "foo"
*/
this.getFoldStringAt = function(row, column, trim, foldLine) {
var foldLine = foldLine || this.getFoldLine(row);
foldLine = foldLine || this.getFoldLine(row);
if (!foldLine)
return null;
@ -140,16 +140,17 @@ function Folding() {
end: { column: 0 }
};
// TODO: Refactor to use getNextFoldTo function.
var str, fold;
for (var i = 0; i < foldLine.folds.length; i++) {
var fold = foldLine.folds[i];
fold = foldLine.folds[i];
var cmp = fold.range.compareEnd(row, column);
if (cmp == -1) {
var str = this
str = this
.getLine(fold.start.row)
.substring(lastFold.end.column, fold.start.column);
break;
}
else if (cmp == 0) {
else if (cmp === 0) {
return null;
}
lastFold = fold;
@ -160,10 +161,10 @@ function Folding() {
if (trim == -1)
return str.substring(0, column - lastFold.end.column);
else if (trim == 1)
return str.substring(column - lastFold.end.column)
return str.substring(column - lastFold.end.column);
else
return str;
}
};
this.getFoldLine = function(docRow, startFoldLine) {
var foldData = this.$foldData;
@ -181,11 +182,11 @@ function Folding() {
}
}
return null;
}
};
// returns the fold which starts after or contains docRow
this.getNextFoldLine = function(docRow, startFoldLine) {
var foldData = this.$foldData, ans;
var foldData = this.$foldData;
var i = 0;
if (startFoldLine)
i = foldData.indexOf(startFoldLine);
@ -198,7 +199,7 @@ function Folding() {
}
}
return null;
}
};
this.getFoldedRowCount = function(first, last) {
var foldData = this.$foldData, rowCount = last-first+1;
@ -222,7 +223,7 @@ function Folding() {
}
}
return rowCount;
}
};
this.$addFoldLine = function(foldLine) {
this.$foldData.push(foldLine);
@ -230,7 +231,7 @@ function Folding() {
return a.start.row - b.start.row;
});
return foldLine;
}
};
/**
* Adds a new fold.
@ -242,9 +243,10 @@ function Folding() {
this.addFold = function(placeholder, range) {
var foldData = this.$foldData;
var added = false;
var fold;
if (placeholder instanceof Fold)
var fold = placeholder;
fold = placeholder;
else
fold = new Fold(range, placeholder);
@ -295,7 +297,7 @@ function Folding() {
added = true;
if (!fold.sameRow) {
// Check if we might have to merge two FoldLines.
foldLineNext = foldData[i + 1];
var foldLineNext = foldData[i + 1];
if (foldLineNext && foldLineNext.start.row == endRow) {
// We need to merge!
foldLine.merge(foldLineNext);
@ -376,7 +378,7 @@ function Folding() {
// Notify that fold data has changed.
this.$modified = true;
this._dispatchEvent("changeFold", { data: fold });
}
};
this.removeFolds = function(folds) {
// We need to clone the folds array passed in as it might be the folds
@ -549,7 +551,7 @@ function Folding() {
if (fold && fold.range.toString() == range.toString()){
this.expandFold(fold);
return
return;
}
var placeholder = "...";
@ -557,7 +559,7 @@ function Folding() {
placeholder = this.getTextRange(range);
if(placeholder.length < 4)
return;
placeholder = placeholder.trim().substring(0, 2) + ".."
placeholder = placeholder.trim().substring(0, 2) + "..";
}
this.addFold(placeholder, range);
@ -571,7 +573,7 @@ function Folding() {
var t;
do {
t = iterator.stepBackward();
} while(t && t.type == token.type)
} while(t && t.type == token.type);
iterator.stepForward();
range.start.row = iterator.getCurrentTokenRow();
@ -581,73 +583,60 @@ function Folding() {
do {
t = iterator.stepForward();
} while(t && t.type == token.type)
} while(t && t.type == token.type);
t = iterator.stepBackward();
range.end.row = iterator.getCurrentTokenRow();
range.end.column = iterator.getCurrentTokenColumn() + t.value.length - 1;
return range
return range;
}
};
this.foldAll = function() {
var foldWidgets = this.foldWidgets
var foldWidgets = this.foldWidgets;
for (var row = foldWidgets.length; row--; ) {
if (foldWidgets[row] == null)
foldWidgets[row] = this.getFoldWidget(row)
foldWidgets[row] = this.getFoldWidget(row);
if (foldWidgets[row] != "start")
continue
continue;
var range = this.getFoldWidgetRange(row);
if (range)
this.addFold("...", range)
this.addFold("...", range);
}
}
};
// structured folding
this.$setFolding = function(mode) {
mode = mode && mode.foldingRules;
var foldRules = Folding.commonFoldingRules
if (typeof mode == "string")
mode = foldRules[mode];
if (mode) {
this.foldWidgets = [];
this.removeListener('change', this.$updateFoldWidgets);
if (mode.getFoldWidget)
this.getFoldWidget = mode.getFoldWidget;
else if (mode.foldingStopMarker)
this.getFoldWidget = foldRules.$testBoth;
else
this.getFoldWidget = foldRules.$testStart;
this.foldingStopMarker = mode.foldingStopMarker;
this.foldingStartMarker = mode.foldingStartMarker;
if (typeof mode.getFoldWidgetRange == "string")
this.getFoldWidgetRange = foldRules[mode.getFoldWidgetRange];
else
this.getFoldWidgetRange = mode.getFoldWidgetRange;
this.$updateFoldWidgets = (mode.onChange || foldRules.onChange).bind(this);
this.on('change', this.$updateFoldWidgets);
} else {
this.$setFolding = function(foldMode) {
if (this.$foldMode == foldMode)
return;
this.$foldMode = foldMode;
this.removeListener('change', this.$updateFoldWidgets);
if (!foldMode) {
this.foldWidgets = null;
this.removeListener('change', this.$updateFoldWidgets);
return;
}
this.foldWidgets = [];
this.getFoldWidget = foldMode.getFoldWidget.bind(foldMode, this);
this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this);
this.$updateFoldWidgets = this.updateFoldWidgets.bind(this);
this.on('change', this.$updateFoldWidgets);
};
this.onFoldWidgetClick = function(row, htmlEvent) {
var type = this.getFoldWidget(row);
var line = this.getLine(row);
var fold;
if (type == "end")
var fold = this.getFoldAt(row, 0, -1);
fold = this.getFoldAt(row, 0, -1);
else
var fold = this.getFoldAt(row, line.length, 1);
fold = this.getFoldAt(row, line.length, 1);
if (fold) {
this.expandFold(fold);
@ -657,260 +646,25 @@ function Folding() {
var range = this.getFoldWidgetRange(row);
if (range)
this.addFold("...", range);
}
}
Folding.commonFoldingRules = {
$testStart: function(row) {
if(this.foldingStartMarker.test(this.getLine(row)))
return "start";
return "";
},
$testBoth: function(row) {
var line = this.getLine(row);
if(this.foldingStartMarker.test(line))
return "start";
if(this.foldingStopMarker.test(line))
return "end";
return "";
},
onChange: function(e) {
};
this.updateFoldWidgets = function(e) {
var delta = e.data;
var range = delta.range;
var firstRow = range.start.row;
var len = range.end.row - firstRow;
if (len == 0) {
if (len === 0) {
this.foldWidgets[firstRow] = null;
} else if (delta.action == "removeText" || delta.action == "removeLines") {
this.foldWidgets.splice(firstRow, len + 1, null);
} else {
var args = Array(len + 1);
args.unshift(firstRow, 1)
args.unshift(firstRow, 1);
this.foldWidgets.splice.apply(this.foldWidgets, args);
}
},
};
indentationBlock: function(row) {
var re = /^\s*/;
var startRow = row, endRow = row;
var line = this.getLine(row);
var startColumn = line.length - 1;
var startLevel = line.match(re)[0].length;
while (line = this.getLine(++row)) {
var level = line.match(re)[0].length;
if (level == line.length)
continue;
if (level <= startLevel)
break;
endRow = row;
}
if (endRow > startRow) {
var endColumn = this.getLine(endRow).length;
return new Range(startRow, startColumn, endRow, endColumn);
}
},
"cStyle": {
foldingStartMarker : /(\{|\[)[^\}\]]*$|^\s*(\/\*)/,
foldingStopMarker : /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,
getFoldWidgetRange: function(row) {
var line = this.getLine(row);
var match = line.match(this.foldingStartMarker);
if (match) {
var i = match.index;
if (match[2])
return this.getCommentFoldRange(row, i + match[0].length);
var start = {row: row, column: i+1};
var end = this.$findClosingBracket(match[1], start);
if (end) {
var fw = this.foldWidgets[end.row];
if (fw == null)
fw = this.getFoldWidget(end.row);
if (fw == "start"){
end.row --;
end.column = this.getLine(end.row).length;
}
} else {
end = {row: this.getLength(), column: 0};
}
return Range.fromPoints(start, end);
}
var match = line.match(this.foldingStopMarker);
if (match) {
var i = match.index + match[0].length;
if (match[2])
return this.getCommentFoldRange(row, i);
var end = {row: row, column: i};
var start = this.$findOpeningBracket(match[1], end)
if (start){
start.column++;
end.column--;
} else {
start = {row: 0, column: this.getLine(0).length}
}
return Range.fromPoints(start, end);
}
}
},
// TODO: folding based only on indentation
"indentation": null,
"xml": {
voidElements: {
"area": 1,
"base": 1,
"br": 1,
"col": 1,
"command": 1,
"embed": 1,
"hr": 1,
"img": 1,
"input": 1,
"keygen": 1,
"link": 1,
"meta": 1,
"param": 1,
"source": 1,
"track": 1,
"wbr": 1
},
getFoldWidget: function(row) {
var tags = this.getTokens(row, row)[0].tokens
.filter(function(token) {
return token.type === "meta.tag"
})
.map(function(token) {
return token.value;
}).
join("")
.trim()
.replace(/^<|>$|\s+/g, "")
.split("><")
var fold = tags[0];
if (!fold || Folding.commonFoldingRules.xml.voidElements[fold])
return;
if (fold.charAt(0) == "/")
return "end";
if (tags.indexOf("/" + fold) !== -1)
return;
return "start";
},
getFoldWidgetRange: function(row) {
var start, end;
var stack = [];
var iterator = new TokenIterator(this, row, 0);
var step = "stepForward";
var isBack = false;
// http://dev.w3.org/html5/spec/syntax.html#optional-tags
// TODO
// var optionalTags = {
// "html": 1,
// "head": 1,
// "body": 1,
// "li": 1,
// "dt": 1,
// "dd": 1,
// "p": 1,
// "rt": 1,
// "rp": 1,
// "optgroup": 1,
// "option": 1,
// "colgroup": 1,
// "thead": 1,
// "tbody": 1,
// "tfoot": 1,
// "tr": 1,
// "td": 1,
// "th": 1
// };
// limited XML parsing to find matching tag
do {
var token = iterator.getCurrentToken();
var value = token.value.trim();
if (token && token.type == "meta.tag" && token.value !== ">") {
var tagName = value.replace(/^[<\s]*|[\s*>]$/g, "");
if (Folding.commonFoldingRules.xml.voidElements[tagName])
continue;
if (!start) {
if (tagName.charAt(0) == "/") {
tagName = tagName.slice(1);
step = "stepBackward";
isBack = true;
}
start = {
row: row,
column: iterator.getCurrentTokenColumn() + (isBack ? 0 : value.length + 1)
};
// console.log("push", tagName)
stack.push(tagName);
}
else {
if (tagName.charAt(0) == "/") {
tagName = tagName.slice(1);
var close = !isBack;
}
else
close = isBack;
if (close) {
if (stack[stack.length-1] == tagName) {
// console.log("pop", tagName)
stack.pop();
if (stack.length == 0) {
end = {
row: iterator.getCurrentTokenRow(),
column: iterator.getCurrentTokenColumn() + (isBack ? value.length : 0)
};
if (isBack)
return Range.fromPoints(end, start);
else
return Range.fromPoints(start, end);
}
}
else {
// console.error("unmatched tags!", tagName, stack)
}
}
else {
// console.log("push", tagName)
stack.push(tagName);
}
}
}
} while(token = iterator[step]());
}
}
}
exports.Folding = Folding;