Add fold documentToScreen unit tests and fixed some small bugs

This commit is contained in:
Julian Viereck 2011-04-23 15:05:11 +02:00
commit aea1bc31ea
4 changed files with 221 additions and 144 deletions

View file

@ -149,6 +149,7 @@ exports.launch = function(env) {
// BEGING TESTING
var Range = require("ace/range").Range;
docs.js.addFold(new Range(0, 13, 0, 18), "args...");
docs.js.addFold(new Range(1, 10, 2, 10), "foo...");
docs.js.addFold(new Range(2, 20, 2, 25), "bar...");
window.s = docs.js;

View file

@ -22,6 +22,7 @@
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* Mihai Sucan <mihai DOT sucan AT gmail DOT 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
@ -55,6 +56,7 @@ var EditSession = function(text, mode) {
this.$backMarkers = {};
this.$markerId = 1;
this.$wrapData = [];
this.$foldData = [];
if (text instanceof Document) {
this.setDocument(text);
@ -875,8 +877,8 @@ var EditSession = function(text, mode) {
if (len != 0) {
if (action.indexOf("remove") != -1) {
useWrapMode && this.$wrapData.splice(firstRow, len);
// TODO: More checking needed here.
this.$foldData.splice(firstRow, len);
// TODO: Remove no longer needed folds here.
// TODO: Update row data on folds.
lastRow = firstRow;
} else {
var args;
@ -886,10 +888,8 @@ var EditSession = function(text, mode) {
this.$wrapData.splice.apply(this.$wrapData, args);
}
args = [firstRow, 0];
for (var i = 0; i < len; i++) args.push(false);
// TODO: More checking needed here.
this.$foldData.splice.apply(this.$foldData, args);
// TODO: Expand folds here if needed.
// TODO: Update row data on folds.
}
}
@ -1041,7 +1041,7 @@ var EditSession = function(text, mode) {
screenColumn += 1;
}
}
return screenColumn;
}
@ -1242,7 +1242,7 @@ var EditSession = function(text, mode) {
return this.documentToScreenPosition(docRow, docColumn).row;
}
this.$buildWrappedTextLine = function(row, endRow, endColumn) {
this.$buildFoldedTextLine = function(row, endRow, endColumn) {
var textLine = "";
// Build a one line string for the current fold sequence that starts
@ -1273,6 +1273,13 @@ var EditSession = function(text, mode) {
}
}
textLine += line.substring(lastEnd, fold.start.column);
// 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;
@ -1333,8 +1340,10 @@ var EditSession = function(text, mode) {
textLine = this.getLine(docRow).substring(0, docColumn);
foldStartRow = docRow;
} else {
textLine = this.$buildWrappedTextLine(
(foldStartRow != null ? foldStartRow : docRow), docRow, docColumn);
textLine = this.$buildFoldedTextLine(
(foldStartRow != null ? foldStartRow : docRow),
docRow,
docColumn);
}
// Clamp textLine if in wrapMode.
@ -1381,7 +1390,13 @@ var EditSession = function(text, mode) {
}
}
screenRows -= this.getFoldedRowLength(0, length - 1);
var foldData = this.$foldData;
for (var i = 0; i < foldData.length; i++) {
var foldRow = foldData[i];
if (foldRow.linkOut) {
screenRows -= foldRow.linkOut.end.row - foldRow.linkOut.start.row;
}
}
return screenRows;
}
@ -1438,35 +1453,86 @@ 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 = [];
}
(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";
}
this.linkOut = fold;
} else if (fold.end.row == this.row) {
if (this.linkIn) {
throw "There is already a linkIn fold";
}
this.linkIn = fold;
} else {
throw "Trying to add fold to FoldRow that doesn't have a matching row";
}
}
}).call(FoldRow.prototype);
this.getFoldRow = function(docRow, startFoldRow) {
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) {
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.
*/
this.addFold = function(range, placeholder) {
var startRow = range.start.row,
endRow = range.end.row,
foldData = this.$foldData;
foldData = this.$foldData,
foldRow = null;
// In case there is no fold data for the start row yet.
if (!Array.isArray(foldData[startRow])) {
foldData[startRow] = [];
}
foldRow = this.getFoldRowOrCreate(startRow);
var fold = new Fold(range, placeholder);
// TODO: The folds have to be ordered by range.start!
foldData[startRow].push(fold);
foldRow.addFold(fold);
// Mark all lines folded by this fold as folded.
for (var row = startRow + 1; row < endRow; row++) {
foldData[row] = fold;
}
// If this fold folds more then one line, then add the fold at the
// beginning of the foldData[endRow] as well.
// If this fold folds more then one line, then add this fold as the
// linkOut of the FoldRow at endRow.
if (!fold.sameLine) {
if (!Array.isArray(foldData[endRow])) {
foldData[endRow] = [];
}
foldData[endRow].splice(0, 0, fold);
foldRow = this.getFoldRowOrCreate(endRow, foldRow);
foldRow.addFold(fold);
}
// TODO: Recalculate wrapData
@ -1481,104 +1547,60 @@ var EditSession = function(text, mode) {
* 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) {
return !!this.$foldData[docRow];
this.isRowFolded = function(docRow, startFoldRow) {
return !!this.getFoldRow(docRow, startFoldRow);
};
this.getRowLastFold = function(docRow) {
var fold = this.$foldData[docRow];
if (!fold) {
return null;
} else if (Array.isArray(fold)) {
return fold[fold.length - 1];
} else {
return fold;
}
};
this.getRowFirstFold = function(docRow, skipLastWrap) {
var fold = this.$foldData[docRow];
if (!fold) {
return null;
} else if (Array.isArray(fold)) {
if (skipLastWrap) {
if (fold[0].row.start != docRow) {
return fold[1] || null;
}
}
return fold[0];
} else {
return fold;
}
};
this.getRowFoldEnd = function(docRow) {
return (this.$foldData[docRow]
? this.getRowLastFold(docRow).end.row
this.getRowFoldEnd = function(docRow, startFoldRow) {
var foldRow = this.getFoldRow(docRow, startFoldRow);
return (foldRow
? (foldRow.linkOut ? foldRow.linkOut.end.row : docRow)
: docRow);
};
/**
* Checks if a given documentRow is visible or not. Not beeing visible means
* it's folded completly.
**/
this.isRowVisible = function(docRow) {
var fold = this.$foldData[docRow];
return !fold || Array.isArray(fold);
};
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!";
}
this.getFoldData = function(docRow) {
return this.$foldData[docRow];
var foldData = this.$foldData;
for (var i = foldData.indexOf(foldRow); foldRow; i++) {
if (foldRow.linkIn) {
folds.push(foldRow.linkIn);
}
foldRow = foldRow.linkOut;
}
}
/**
* Returns the number of folded lines between start and end row.
*/
this.getFoldedRowLength = function(start, end) {
var row = start;
var count = 0;
while (row <= end) {
row = this.findRowFoldedForward(row, end);
if (row != null) {
var fold = this.getRowLastFold(row);
if (!fold.sameLine) {
if (row == start) {
count += fold.end.row - start;
} else {
count += fold.end.row - fold.start.row;
}
row = fold.end.row + 1;
}
} else {
break;
}
}
return count;
};
this.findRowFoldedForward = function(start, limit) {
if (limit == null) {
limit = this.getLength();
}
for (var row = start; row != limit; row++) {
if (this.isRowFolded(row)) {
return row;
}
}
return null;
}
this.findRowFoldedBackwards = function(start, limit) {
if (limit == null) {
limit = -1;
}
for (var row = start; row != limit; row--) {
if (this.isRowFolded(row)) {
return row;
}
}
return null;
}
// this.findRowFoldedForward = function(start, limit) {
// if (limit == null) {
// limit = this.getLength();
// }
// for (var row = start; row != limit; row++) {
// if (this.isRowFolded(row)) {
// return row;
// }
// }
// return null;
// }
//
// this.findRowFoldedBackwards = function(start, limit) {
// if (limit == null) {
// limit = -1;
// }
// for (var row = start; row != limit; row--) {
// if (this.isRowFolded(row)) {
// return row;
// }
// }
// return null;
// }
}).call(EditSession.prototype);

View file

@ -20,6 +20,7 @@
*
* Contributor(s):
* Fabian Jakobs <fabian AT ajax DOT org>
* 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
@ -49,6 +50,21 @@ var MockRenderer = require("ace/test/mockrenderer");
var Range = require("ace/range").Range;
var assert = require("ace/test/assertions");
function createFoldTestSession() {
var lines = [
"function foo(items) {",
" for (var i=0; i<items.length; i++) {",
" alert(items[i] + \"juhu\");",
" } // Real Tab.",
"}"
];
var session = new EditSession(lines.join("\n"));
session.addFold(new Range(0, 13, 0, 18), "args...");
session.addFold(new Range(1, 10, 2, 10), "foo...");
session.addFold(new Range(2, 20, 2, 25), "bar...");
return session;
}
module.exports = {
"test: find matching opening bracket" : function() {
@ -353,6 +369,44 @@ module.exports = {
session.setValue("\nfoo");
assert.equal(session.doc.getValue(), ["", "foo"].join("\n"));
},
"test fold documentToScreen": function() {
var session = createFoldTestSession();
function assertDoc2Screen(docRow, docCol, screenRow, screenCol) {
assert.position(
session.documentToScreenPosition(docRow, docCol),
screenRow, screenCol
);
}
// One fold ending in the same row.
assertDoc2Screen(0, 0, 0, 0);
assertDoc2Screen(0, 13, 0, 13);
assertDoc2Screen(0, 14, 0, 13);
assertDoc2Screen(0, 17, 0, 13);
assertDoc2Screen(0, 18, 0, 20);
// Fold ending on some other row.
assertDoc2Screen(1, 0, 1, 0);
assertDoc2Screen(1, 10, 1, 10);
assertDoc2Screen(1, 11, 1, 10);
assertDoc2Screen(1, 99, 1, 10);
assertDoc2Screen(2, 0, 1, 10);
assertDoc2Screen(2, 9, 1, 10);
assertDoc2Screen(2, 10, 1, 16);
assertDoc2Screen(2, 11, 1, 17);
// Fold in the same row with fold over more then one row in the same row.
assertDoc2Screen(2, 19, 1, 25);
assertDoc2Screen(2, 20, 1, 26);
assertDoc2Screen(2, 21, 1, 26);
assertDoc2Screen(2, 24, 1, 26);
assertDoc2Screen(2, 25, 1, 32);
assertDoc2Screen(2, 26, 1, 33);
assertDoc2Screen(2, 99, 1, 40);
}
};

View file

@ -10,27 +10,27 @@ var failed = 0
var log = document.getElementById("log")
var tests = [
require("ace/editor_change_document_test"),
require("ace/editor_navigation_test"),
require("ace/editor_highlight_selected_word_test"),
require("ace/editor_text_edit_test"),
require("ace/document_test"),
require("ace/edit_session_test"),
require("ace/test/event_emitter_test"),
require("ace/range_test"),
require("ace/search_test"),
require("ace/selection_test"),
require("ace/virtual_renderer_test"),
require("ace/anchor_test"),
require("ace/mode/css_test"),
require("ace/mode/css_tokenizer_test"),
require("ace/mode/html_test"),
require("ace/mode/html_tokenizer_test"),
require("ace/mode/javascript_test"),
require("ace/mode/javascript_tokenizer_test"),
require("ace/mode/text_test"),
require("ace/mode/xml_test"),
require("ace/mode/xml_tokenizer_test")
// require("ace/editor_change_document_test"),
// require("ace/editor_navigation_test"),
// require("ace/editor_highlight_selected_word_test"),
// require("ace/editor_text_edit_test"),
// require("ace/document_test"),
require("ace/edit_session_test") //,
// require("ace/test/event_emitter_test"),
// require("ace/range_test"),
// require("ace/search_test"),
// require("ace/selection_test"),
// require("ace/virtual_renderer_test"),
// require("ace/anchor_test"),
// require("ace/mode/css_test"),
// require("ace/mode/css_tokenizer_test"),
// require("ace/mode/html_test"),
// require("ace/mode/html_tokenizer_test"),
// require("ace/mode/javascript_test"),
// require("ace/mode/javascript_tokenizer_test"),
// require("ace/mode/text_test"),
// require("ace/mode/xml_test"),
// require("ace/mode/xml_tokenizer_test")
]
async.list(tests)
@ -41,21 +41,21 @@ async.list(tests)
.each(function(test, next) {
var node = document.createElement("div");
node.className = test.passed ? "passed" : "failed";
var name = test.name
if (test.suiteName)
name = test.suiteName + ": " + test.name
var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL")
if (!test.passed) {
if (test.err.stack)
var err = test.err.stack
else
var err = test.err
msg += "<pre class='error'>" + err + "</pre>";
}
node.innerHTML = msg;
log.appendChild(node);