split mode specific code for structured folding
into separate files
This commit is contained in:
parent
d2eeb44016
commit
bbcb00acc4
27 changed files with 567 additions and 452 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue