This commit is contained in:
2026-05-13 17:11:09 +02:00
commit ea63897455
2785 changed files with 359868 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
namespace = "annotatescrollbar_";
(function () {
function test(name, run, content, query, expected) {
return testCM(name, function (cm) {
var annotation = cm.annotateScrollbar({
listenForChanges: false,
className: "CodeMirror-search-match"
});
var matches = [];
var cursor = cm.getSearchCursor(query, CodeMirror.Pos(0, 0));
while (cursor.findNext()) {
var match = {
from: cursor.from(),
to: cursor.to()
};
matches.push(match)
}
if (run) run(cm);
cm.display.barWidth = 5;
annotation.update(matches);
var annotations = cm.getWrapperElement().getElementsByClassName(annotation.options.className);
eq(annotations.length, expected, "Expected " + expected + " annotations on the scrollbar.")
}, {
value: content,
mode: "javascript",
foldOptions: {
rangeFinder: CodeMirror.fold.brace
}
});
}
function doFold(cm) {
cm.foldCode(cm.getCursor());
}
var simpleProg = "function foo() {\n\n return \"foo\";\n\n}\n\nfoo();\n";
var consecutiveLineMatches = "function foo() {\n return \"foo\";\n}\nfoo();\n";
var singleLineMatches = "function foo() { return \"foo\"; }foo();\n";
// Base case - expect 3 matches and 3 annotations
test("simple", null, simpleProg, "foo", 3);
// Consecutive line matches are combines into a single annotation - expect 3 matches and 2 annotations
test("combineConsecutiveLine", null, consecutiveLineMatches, "foo", 2);
// Matches on a single line get a single annotation - expect 3 matches and 1 annotation
test("combineSingleLine", null, singleLineMatches, "foo", 1);
// Matches within a fold are annotated on the folded line - expect 3 matches and 2 annotations
test("simpleFold", doFold, simpleProg, "foo", 2);
// Combination of combineConsecutiveLine and simpleFold cases - expect 3 matches and 1 annotation
test("foldedMatch", doFold, consecutiveLineMatches, "foo", 1);
// Hidden matches within a fold are annotated on the folded line - expect 1 match and 1 annotation
test("hiddenMatch", doFold, simpleProg, "return", 1);
})();

View File

@@ -0,0 +1,118 @@
namespace = "comment_";
(function() {
function test(name, mode, run, before, after) {
return testCM(name, function(cm) {
run(cm);
eq(cm.getValue(), after);
}, {value: before, mode: mode});
}
var simpleProg = "function foo() {\n return bar;\n}";
var inlineBlock = "foo(/* bar */ true);";
var inlineBlocks = "foo(/* bar */ true, /* baz */ false);";
var multiLineInlineBlock = ["above();", "foo(/* bar */ true);", "below();"];
test("block", "javascript", function(cm) {
cm.blockComment(Pos(0, 3), Pos(3, 0), {blockCommentLead: " *"});
}, simpleProg + "\n", "/* function foo() {\n * return bar;\n * }\n */");
test("blockToggle", "javascript", function(cm) {
cm.blockComment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"});
cm.uncomment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"});
}, simpleProg, simpleProg);
test("blockToggle2", "javascript", function(cm) {
cm.setCursor({line: 0, ch: 7 /* inside the block comment */});
cm.execCommand("toggleComment");
}, inlineBlock, "foo(bar true);");
// This test should work but currently fails.
// test("blockToggle3", "javascript", function(cm) {
// cm.setCursor({line: 0, ch: 7 /* inside the first block comment */});
// cm.execCommand("toggleComment");
// }, inlineBlocks, "foo(bar true, /* baz */ false);");
test("line", "javascript", function(cm) {
cm.lineComment(Pos(1, 1), Pos(1, 1));
}, simpleProg, "function foo() {\n// return bar;\n}");
test("lineToggle", "javascript", function(cm) {
cm.lineComment(Pos(0, 0), Pos(2, 1));
cm.uncomment(Pos(0, 0), Pos(2, 1));
}, simpleProg, simpleProg);
test("fallbackToBlock", "css", function(cm) {
cm.lineComment(Pos(0, 0), Pos(2, 1));
}, "html {\n border: none;\n}", "/* html {\n border: none;\n} */");
test("fallbackToLine", "ruby", function(cm) {
cm.blockComment(Pos(0, 0), Pos(1));
}, "def blah()\n return hah\n", "# def blah()\n# return hah\n");
test("ignoreExternalBlockComments", "javascript", function(cm) {
cm.execCommand("toggleComment");
}, inlineBlocks, "// " + inlineBlocks);
test("ignoreExternalBlockComments2", "javascript", function(cm) {
cm.setCursor({line: 0, ch: null /* eol */});
cm.execCommand("toggleComment");
}, inlineBlocks, "// " + inlineBlocks);
test("ignoreExternalBlockCommentsMultiLineAbove", "javascript", function(cm) {
cm.setSelection({line: 0, ch: 0}, {line: 1, ch: 1});
cm.execCommand("toggleComment");
}, multiLineInlineBlock.join("\n"), ["// " + multiLineInlineBlock[0],
"// " + multiLineInlineBlock[1],
multiLineInlineBlock[2]].join("\n"));
test("ignoreExternalBlockCommentsMultiLineBelow", "javascript", function(cm) {
cm.setSelection({line: 1, ch: 13 /* after end of block comment */}, {line: 2, ch: 1});
cm.execCommand("toggleComment");
}, multiLineInlineBlock.join("\n"), [multiLineInlineBlock[0],
"// " + multiLineInlineBlock[1],
"// " + multiLineInlineBlock[2]].join("\n"));
test("commentRange", "javascript", function(cm) {
cm.blockComment(Pos(1, 2), Pos(1, 13), {fullLines: false});
}, simpleProg, "function foo() {\n /*return bar;*/\n}");
test("indented", "javascript", function(cm) {
cm.lineComment(Pos(1, 0), Pos(2), {indent: true});
}, simpleProg, "function foo() {\n// return bar;\n// }");
test("emptyIndentedLine", "javascript", function(cm) {
cm.lineComment(Pos(1, 2), Pos(1, 2), {indent: true});
}, "function foo() {\n \n}", "function foo() {\n // \n}");
test("singleEmptyLine", "javascript", function(cm) {
cm.setCursor(1);
cm.execCommand("toggleComment");
}, "a;\n\nb;", "a;\n// \nb;");
test("dontMessWithStrings", "javascript", function(cm) {
cm.execCommand("toggleComment");
}, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");");
test("dontMessWithStrings2", "javascript", function(cm) {
cm.execCommand("toggleComment");
}, "console.log(\"// string\");", "// console.log(\"// string\");");
test("dontMessWithStrings3", "javascript", function(cm) {
cm.execCommand("toggleComment");
}, "// console.log(\"// string\");", "console.log(\"// string\");");
test("includeLastLine", "javascript", function(cm) {
cm.execCommand("selectAll")
cm.execCommand("toggleComment")
}, "// foo\n// bar\nbaz", "// // foo\n// // bar\n// baz")
test("uncommentWithTrailingBlockEnd", "xml", function(cm) {
cm.execCommand("toggleComment")
}, "<!-- foo --> -->", "foo -->")
test("dontCommentInComment", "xml", function(cm) {
cm.setCursor(1, 0)
cm.execCommand("toggleComment")
}, "<!-- foo\nbar -->", "<!-- foo\nbar -->")
})();

View File

@@ -0,0 +1,110 @@
(function() {
"use strict";
namespace = "contenteditable_";
var Pos = CodeMirror.Pos
function findTextNode(dom, text) {
if (dom instanceof CodeMirror) dom = dom.getInputField()
if (dom.nodeType == 1) {
for (var ch = dom.firstChild; ch; ch = ch.nextSibling) {
var found = findTextNode(ch, text)
if (found) return found
}
} else if (dom.nodeType == 3 && dom.nodeValue == text) {
return dom
}
}
function lineElt(node) {
for (;;) {
var parent = node.parentNode
if (/CodeMirror-code/.test(parent.className)) return node
node = parent
}
}
testCM("insert_text", function(cm) {
findTextNode(cm, "foobar").nodeValue = "foo bar"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "foo bar")
}, {inputStyle: "contenteditable", value: "foobar"})
testCM("split_line", function(cm) {
cm.setSelection(Pos(2, 3))
var node = findTextNode(cm, "foobar")
node.nodeValue = "foo"
var lineNode = lineElt(node)
lineNode.parentNode.insertBefore(document.createElement("pre"), lineNode.nextSibling).textContent = "bar"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "one\ntwo\nfoo\nbar\nthree\nfour\n")
}, {inputStyle: "contenteditable", value: "one\ntwo\nfoobar\nthree\nfour\n"})
testCM("join_line", function(cm) {
cm.setSelection(Pos(2, 3))
var node = findTextNode(cm, "foo")
node.nodeValue = "foobar"
var lineNode = lineElt(node)
lineNode.parentNode.removeChild(lineNode.nextSibling)
cm.display.input.updateFromDOM()
eq(cm.getValue(), "one\ntwo\nfoobar\nthree\nfour\n")
}, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"})
testCM("delete_multiple", function(cm) {
cm.setSelection(Pos(1, 3), Pos(4, 0))
var text = findTextNode(cm, "two"), startLine = lineElt(text)
for (var i = 0; i < 3; i++)
startLine.parentNode.removeChild(startLine.nextSibling)
text.nodeValue = "twothree"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "one\ntwothree\nfour\n")
}, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"})
testCM("ambiguous_diff_middle", function(cm) {
cm.setSelection(Pos(0, 2))
findTextNode(cm, "baah").nodeValue = "baaah"
cm.display.input.updateFromDOM()
eqCharPos(cm.getCursor(), Pos(0, 3))
}, {inputStyle: "contenteditable", value: "baah"})
testCM("ambiguous_diff_start", function(cm) {
cm.setSelection(Pos(0, 1))
findTextNode(cm, "baah").nodeValue = "baaah"
cm.display.input.updateFromDOM()
eqCharPos(cm.getCursor(), Pos(0, 2))
}, {inputStyle: "contenteditable", value: "baah"})
testCM("ambiguous_diff_end", function(cm) {
cm.setSelection(Pos(0, 3))
findTextNode(cm, "baah").nodeValue = "baaah"
cm.display.input.updateFromDOM()
eqCharPos(cm.getCursor(), Pos(0, 4))
}, {inputStyle: "contenteditable", value: "baah"})
testCM("force_redraw", function(cm) {
findTextNode(cm, "foo").parentNode.appendChild(document.createElement("hr")).className = "inserted"
cm.display.input.updateFromDOM()
eq(byClassName(cm.getInputField(), "inserted").length, 0)
}, {inputStyle: "contenteditable", value: "foo"})
testCM("type_on_empty_line", function(cm) {
cm.setSelection(Pos(1, 0))
findTextNode(cm, "\u200b").nodeValue += "hello"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "foo\nhello\nbar")
}, {inputStyle: "contenteditable", value: "foo\n\nbar"})
testCM("type_after_empty_line", function(cm) {
cm.setSelection(Pos(2, 0))
findTextNode(cm, "bar").nodeValue = "hellobar"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "foo\n\nhellobar")
}, {inputStyle: "contenteditable", value: "foo\n\nbar"})
testCM("type_before_empty_line", function(cm) {
cm.setSelection(Pos(0, 3))
findTextNode(cm, "foo").nodeValue = "foohello"
cm.display.input.updateFromDOM()
eq(cm.getValue(), "foohello\n\nbar")
}, {inputStyle: "contenteditable", value: "foo\n\nbar"})
})();

View File

@@ -0,0 +1,371 @@
(function() {
// A minilanguage for instantiating linked CodeMirror instances and Docs
function instantiateSpec(spec, place, opts) {
var names = {}, pos = 0, l = spec.length, editors = [];
while (spec) {
var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/);
var name = m[1], isDoc = m[2], cur;
if (m[3]) {
cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]}));
} else {
var other = m[5];
if (!names.hasOwnProperty(other)) {
names[other] = editors.length;
editors.push(CodeMirror(place, opts));
}
var doc = editors[names[other]].linkedDoc({
sharedHist: !m[4],
from: m[6] ? Number(m[6]) : null,
to: m[7] ? Number(m[7]) : null
});
cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc}));
}
names[name] = editors.length;
editors.push(cur);
spec = spec.slice(m[0].length);
}
return editors;
}
function clone(obj, props) {
if (!obj) return;
clone.prototype = obj;
var inst = new clone();
if (props) for (var n in props) if (props.hasOwnProperty(n))
inst[n] = props[n];
return inst;
}
function eqAll(val) {
var end = arguments.length, msg = null;
if (typeof arguments[end-1] == "string")
msg = arguments[--end];
if (i == end) throw new Error("No editors provided to eqAll");
for (var i = 1; i < end; ++i)
eq(arguments[i].getValue(), val, msg)
}
function testDoc(name, spec, run, opts, expectFail) {
if (!opts) opts = {};
return test("doc_" + name, function() {
var place = document.getElementById("testground");
var editors = instantiateSpec(spec, place, opts);
var successful = false;
try {
run.apply(null, editors);
successful = true;
} finally {
if (!successful || verbose) {
place.style.visibility = "visible";
} else {
for (var i = 0; i < editors.length; ++i)
if (editors[i] instanceof CodeMirror)
place.removeChild(editors[i].getWrapperElement());
}
}
}, expectFail);
}
var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
function testBasic(a, b) {
eqAll("x", a, b);
a.setValue("hey");
eqAll("hey", a, b);
b.setValue("wow");
eqAll("wow", a, b);
a.replaceRange("u\nv\nw", Pos(0, 3));
b.replaceRange("i", Pos(0, 4));
b.replaceRange("j", Pos(2, 1));
eqAll("wowui\nv\nwj", a, b);
}
testDoc("basic", "A='x' B<A", testBasic);
testDoc("basicSeparate", "A='x' B<~A", testBasic);
testDoc("sharedHist", "A='ab\ncd\nef' B<A", function(a, b) {
a.replaceRange("x", Pos(0));
b.replaceRange("y", Pos(1));
a.replaceRange("z", Pos(2));
eqAll("abx\ncdy\nefz", a, b);
a.undo();
a.undo();
eqAll("abx\ncd\nef", a, b);
a.redo();
eqAll("abx\ncdy\nef", a, b);
b.redo();
eqAll("abx\ncdy\nefz", a, b);
a.undo(); b.undo(); a.undo(); a.undo();
eqAll("ab\ncd\nef", a, b);
}, null, ie_lt8);
testDoc("undoIntact", "A='ab\ncd\nef' B<~A", function(a, b) {
a.replaceRange("x", Pos(0));
b.replaceRange("y", Pos(1));
a.replaceRange("z", Pos(2));
a.replaceRange("q", Pos(0));
eqAll("abxq\ncdy\nefz", a, b);
a.undo();
a.undo();
eqAll("abx\ncdy\nef", a, b);
b.undo();
eqAll("abx\ncd\nef", a, b);
a.redo();
eqAll("abx\ncd\nefz", a, b);
a.redo();
eqAll("abxq\ncd\nefz", a, b);
a.undo(); a.undo(); a.undo(); a.undo();
eqAll("ab\ncd\nef", a, b);
b.redo();
eqAll("ab\ncdy\nef", a, b);
});
testDoc("undoConflict", "A='ab\ncd\nef' B<~A", function(a, b) {
a.replaceRange("x", Pos(0));
a.replaceRange("z", Pos(2));
// This should clear the first undo event in a, but not the second
b.replaceRange("y", Pos(0));
a.undo(); a.undo();
eqAll("abxy\ncd\nef", a, b);
a.replaceRange("u", Pos(2));
a.replaceRange("v", Pos(0));
// This should clear both events in a
b.replaceRange("w", Pos(0));
a.undo(); a.undo();
eqAll("abxyvw\ncd\nefu", a, b);
});
testDoc("doubleRebase", "A='ab\ncd\nef\ng' B<~A C<B", function(a, b, c) {
c.replaceRange("u", Pos(3));
a.replaceRange("", Pos(0, 0), Pos(1, 0));
c.undo();
eqAll("cd\nef\ng", a, b, c);
});
testDoc("undoUpdate", "A='ab\ncd\nef' B<~A", function(a, b) {
a.replaceRange("x", Pos(2));
b.replaceRange("u\nv\nw\n", Pos(0, 0));
a.undo();
eqAll("u\nv\nw\nab\ncd\nef", a, b);
a.redo();
eqAll("u\nv\nw\nab\ncd\nefx", a, b);
a.undo();
eqAll("u\nv\nw\nab\ncd\nef", a, b);
b.undo();
a.redo();
eqAll("ab\ncd\nefx", a, b);
a.undo();
eqAll("ab\ncd\nef", a, b);
});
testDoc("undoKeepRanges", "A='abcdefg' B<A", function(a, b) {
var m = a.markText(Pos(0, 1), Pos(0, 3), {className: "foo"});
b.replaceRange("x", Pos(0, 0));
eqCharPos(m.find().from, Pos(0, 2));
b.replaceRange("yzzy", Pos(0, 1), Pos(0));
eq(m.find(), null);
b.undo();
eqCharPos(m.find().from, Pos(0, 2));
b.undo();
eqCharPos(m.find().from, Pos(0, 1));
});
testDoc("longChain", "A='uv' B<A C<B D<C", function(a, b, c, d) {
a.replaceSelection("X");
eqAll("Xuv", a, b, c, d);
d.replaceRange("Y", Pos(0));
eqAll("XuvY", a, b, c, d);
});
testDoc("broadCast", "B<A C<A D<A E<A", function(a, b, c, d, e) {
b.setValue("uu");
eqAll("uu", a, b, c, d, e);
a.replaceRange("v", Pos(0, 1));
eqAll("uvu", a, b, c, d, e);
});
// A and B share a history, C and D share a separate one
testDoc("islands", "A='x\ny\nz' B<A C<~A D<C", function(a, b, c, d) {
a.replaceRange("u", Pos(0));
d.replaceRange("v", Pos(2));
b.undo();
eqAll("x\ny\nzv", a, b, c, d);
c.undo();
eqAll("x\ny\nz", a, b, c, d);
a.redo();
eqAll("xu\ny\nz", a, b, c, d);
d.redo();
eqAll("xu\ny\nzv", a, b, c, d);
});
testDoc("unlink", "B<A C<A D<B", function(a, b, c, d) {
a.setValue("hi");
b.unlinkDoc(a);
d.setValue("aye");
eqAll("hi", a, c);
eqAll("aye", b, d);
a.setValue("oo");
eqAll("oo", a, c);
eqAll("aye", b, d);
});
testDoc("bareDoc", "A*='foo' B*<A C<B", function(a, b, c) {
is(a instanceof CodeMirror.Doc);
is(b instanceof CodeMirror.Doc);
is(c instanceof CodeMirror);
eqAll("foo", a, b, c);
a.replaceRange("hey", Pos(0, 0), Pos(0));
c.replaceRange("!", Pos(0));
eqAll("hey!", a, b, c);
b.unlinkDoc(a);
b.setValue("x");
eqAll("x", b, c);
eqAll("hey!", a);
});
testDoc("swapDoc", "A='a' B*='b' C<A", function(a, b, c) {
var d = a.swapDoc(b);
d.setValue("x");
eqAll("x", c, d);
eqAll("b", a, b);
});
testDoc("docKeepsScroll", "A='x' B*='y'", function(a, b) {
addDoc(a, 200, 200);
a.scrollIntoView(Pos(199, 200));
var c = a.swapDoc(b);
a.swapDoc(c);
var pos = a.getScrollInfo();
is(pos.left > 0, "not at left");
is(pos.top > 0, "not at top");
});
testDoc("copyDoc", "A='u'", function(a) {
var copy = a.getDoc().copy(true);
a.setValue("foo");
copy.setValue("bar");
var old = a.swapDoc(copy);
eq(a.getValue(), "bar");
a.undo();
eq(a.getValue(), "u");
a.swapDoc(old);
eq(a.getValue(), "foo");
eq(old.historySize().undo, 1);
eq(old.copy(false).historySize().undo, 0);
});
testDoc("docKeepsMode", "A='1+1'", function(a) {
var other = CodeMirror.Doc("hi", "text/x-markdown");
a.setOption("mode", "text/javascript");
var old = a.swapDoc(other);
eq(a.getOption("mode"), "text/x-markdown");
eq(a.getMode().name, "markdown");
a.swapDoc(old);
eq(a.getOption("mode"), "text/javascript");
eq(a.getMode().name, "javascript");
});
testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) {
eq(b.getValue(), "2\n3");
eq(b.firstLine(), 1);
b.setCursor(Pos(4));
eqCharPos(b.getCursor(), Pos(2, 1));
a.replaceRange("-1\n0\n", Pos(0, 0));
eq(b.firstLine(), 3);
eqCharPos(b.getCursor(), Pos(4, 1));
a.undo();
eqCharPos(b.getCursor(), Pos(2, 1));
b.replaceRange("oyoy\n", Pos(2, 0));
eq(a.getValue(), "1\n2\noyoy\n3\n4\n5");
b.undo();
eq(a.getValue(), "1\n2\n3\n4\n5");
});
testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) {
a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1));
eq(b.firstLine(), 2);
eq(b.lineCount(), 2);
eq(b.getValue(), "z3\n44");
a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1));
eq(b.firstLine(), 2);
eq(b.getValue(), "z3\n4q");
eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5");
a.execCommand("selectAll");
a.replaceSelection("!");
eqAll("!", a, b);
});
testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) {
var mark = b.markText(Pos(0, 1), Pos(3, 1),
{className: "cm-searching", shared: true});
var found = a.findMarksAt(Pos(0, 2));
eq(found.length, 1);
eq(found[0], mark);
eq(c.findMarksAt(Pos(1, 1)).length, 1);
eqCharPos(mark.find().from, Pos(0, 1));
eqCharPos(mark.find().to, Pos(3, 1));
b.replaceRange("x\ny\n", Pos(0, 0));
eqCharPos(mark.find().from, Pos(2, 1));
eqCharPos(mark.find().to, Pos(5, 1));
var cleared = 0;
CodeMirror.on(mark, "clear", function() {++cleared;});
b.operation(function(){mark.clear();});
eq(a.findMarksAt(Pos(3, 1)).length, 0);
eq(b.findMarksAt(Pos(3, 1)).length, 0);
eq(c.findMarksAt(Pos(3, 1)).length, 0);
eq(mark.find(), null);
eq(cleared, 1);
});
testDoc("sharedMarkerCopy", "A='abcde'", function(a) {
var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true});
var b = a.linkedDoc();
var found = b.findMarksAt(Pos(0, 2));
eq(found.length, 1);
eq(found[0], shared);
shared.clear();
eq(b.findMarksAt(Pos(0, 2)), 0);
});
testDoc("sharedMarkerDetach", "A='abcde' B<A C<B", function(a, b, c) {
var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true});
a.unlinkDoc(b);
var inB = b.findMarksAt(Pos(0, 2));
eq(inB.length, 1);
is(inB[0] != shared);
var inC = c.findMarksAt(Pos(0, 2));
eq(inC.length, 1);
is(inC[0] != shared);
inC[0].clear();
is(shared.find());
});
testDoc("sharedBookmark", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) {
var mark = b.setBookmark(Pos(1, 1), {shared: true});
var found = a.findMarksAt(Pos(1, 1));
eq(found.length, 1);
eq(found[0], mark);
eq(c.findMarksAt(Pos(1, 1)).length, 1);
eqCharPos(mark.find(), Pos(1, 1));
b.replaceRange("x\ny\n", Pos(0, 0));
eqCharPos(mark.find(), Pos(3, 1));
var cleared = 0;
CodeMirror.on(mark, "clear", function() {++cleared;});
b.operation(function() {mark.clear();});
eq(a.findMarks(Pos(0, 0), Pos(5)).length, 0);
eq(b.findMarks(Pos(0, 0), Pos(5)).length, 0);
eq(c.findMarks(Pos(0, 0), Pos(5)).length, 0);
eq(mark.find(), null);
eq(cleared, 1);
});
testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B<A/1-4", function(a, b) {
b.replaceRange("x", Pos(2, 0));
a.undo();
eq(a.getValue(), "line 0\nline 1\nline 2\nline 3\nline 4");
eq(b.getValue(), "line 1\nline 2\nline 3");
});
})();

View File

@@ -0,0 +1,144 @@
var tests = [], filters = [], nameCounts = {};
function Failure(why) {this.message = why;}
Failure.prototype.toString = function() { return this.message; };
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
function test(name, run, expectedFail) {
// Force unique names
if (nameCounts[name] == undefined){
nameCounts[name] = 2;
} else {
// Append number if not first test with this name.
name = name + '_' + (nameCounts[name]++);
}
// Add test
tests.push({name: name, func: run, expectedFail: expectedFail});
return name;
}
var namespace = "";
function testCM(name, run, opts, expectedFail) {
return test(namespace + name, function() {
var place = document.getElementById("testground"), cm = window.cm = CodeMirror(place, opts);
var successful = false;
try {
run(cm);
successful = true;
} finally {
if (!successful || verbose) {
place.style.visibility = "visible";
} else {
place.removeChild(cm.getWrapperElement());
}
}
}, expectedFail);
}
function runTests(callback) {
var totalTime = 0;
function step(i) {
for (;;) {
if (i === tests.length) {
running = false;
return callback("done");
}
var test = tests[i], skip = false;
if (filters.length) {
skip = true;
for (var j = 0; j < filters.length; j++)
if (test.name.match(filters[j])) skip = false;
}
if (skip) {
callback("skipped", test.name, message);
i++;
} else {
break;
}
}
var expFail = test.expectedFail, startTime = +new Date, threw = false;
try {
var message = test.func();
} catch(e) {
threw = true;
if (expFail) callback("expected", test.name);
else if (e instanceof Failure) callback("fail", test.name, e.message);
else {
var pos = /(?:\bat |@).*?([^\/:]+):(\d+)/.exec(e.stack);
if (pos) console["log"](e.stack);
callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : ""));
}
}
if (!threw) {
if (expFail) callback("fail", test.name, message || "expected failure, but passed");
else callback("ok", test.name, message);
}
if (!quit) { // Run next test
var delay = 0;
totalTime += (+new Date) - startTime;
if (totalTime > 500){
totalTime = 0;
delay = 50;
}
setTimeout(function(){step(i + 1);}, delay);
} else { // Quit tests
running = false;
return null;
}
}
step(0);
}
function label(str, msg) {
if (msg) return str + " (" + msg + ")";
return str;
}
function eq(a, b, msg) {
if (a != b) throw new Failure(label(a + " != " + b, msg));
}
function notEq(a, b, msg) {
if (a == b) throw new Failure(label(a + " == " + b, msg));
}
function near(a, b, margin, msg) {
if (Math.abs(a - b) > margin)
throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg));
}
function eqCharPos(a, b, msg) {
function str(p) { return "{line:" + p.line + ",ch:" + p.ch + ",sticky:" + p.sticky + "}"; }
if (a == b) return;
if (a == null) throw new Failure(label("comparing null to " + str(b), msg));
if (b == null) throw new Failure(label("comparing " + str(a) + " to null", msg));
if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg));
}
function eqCursorPos(a, b, msg) {
eqCharPos(a, b, msg);
if (a) eq(a.sticky, b.sticky, msg ? msg + ' (sticky)' : 'sticky');
}
function is(a, msg) {
if (!a) throw new Failure(label("assertion failed", msg));
}
function countTests() {
if (!filters.length) return tests.length;
var sum = 0;
for (var i = 0; i < tests.length; ++i) {
var name = tests[i].name;
for (var j = 0; j < filters.length; j++) {
if (name.match(filters[j])) {
++sum;
break;
}
}
}
return sum;
}
function parseTestFilter(s) {
if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i");
else return new RegExp(s, "i");
}

View File

@@ -0,0 +1,158 @@
(function() {
"use strict";
var Pos = CodeMirror.Pos;
namespace = "emacs_";
var eventCache = {};
function fakeEvent(keyName) {
var event = eventCache[key];
if (event) return event;
var ctrl, shift, alt;
var key = keyName.replace(/\w+-/g, function(type) {
if (type == "Ctrl-") ctrl = true;
else if (type == "Alt-") alt = true;
else if (type == "Shift-") shift = true;
return "";
});
var code;
for (var c in CodeMirror.keyNames)
if (CodeMirror.keyNames[c] == key) { code = c; break; }
if (code == null) throw new Error("Unknown key: " + key);
return eventCache[keyName] = {
type: "keydown", keyCode: code, ctrlKey: ctrl, shiftKey: shift, altKey: alt,
preventDefault: function(){}, stopPropagation: function(){}
};
}
function sim(name, start /*, actions... */) {
var keys = Array.prototype.slice.call(arguments, 2);
testCM(name, function(cm) {
for (var i = 0; i < keys.length; ++i) {
var cur = keys[i];
if (cur instanceof Pos) cm.setCursor(cur);
else if (cur.call) cur(cm);
else cm.triggerOnKeyDown(fakeEvent(cur));
}
}, {keyMap: "emacs", value: start, mode: "javascript"});
}
function dialog(answer) { return function(cm) { cm.openDialog = function(_, cb) { cb(answer); }; }; }
function at(line, ch, sticky) { return function(cm) { eqCursorPos(cm.getCursor(), Pos(line, ch, sticky)); }; }
function txt(str) { return function(cm) { eq(cm.getValue(), str); }; }
sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1, "after"));
sim("motionHMulti", "abcde",
"Ctrl-4", "Ctrl-F", at(0, 4, "before"), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2, "after"),
"Ctrl-5", "Ctrl-B", at(0, 0, "after"));
sim("motionHWord", "abc. def ghi",
"Alt-F", at(0, 3, "before"), "Alt-F", at(0, 8, "before"),
"Ctrl-B", "Alt-B", at(0, 5, "after"), "Alt-B", at(0, 0, "after"));
sim("motionHWordMulti", "abc. def ghi ",
"Ctrl-3", "Alt-F", at(0, 12, "before"), "Ctrl-2", "Alt-B", at(0, 5, "after"),
"Ctrl--", "Alt-B", at(0, 8, "before"));
sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0, "after"));
sim("motionVMulti", "a\nb\nc\nd\ne\n",
"Ctrl-2", "Ctrl-N", at(2, 0, "after"), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1, "before"),
"Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1, "before"));
sim("killYank", "abc\ndef\nghi",
"Ctrl-F", "Ctrl-Space", "Ctrl-N", "Ctrl-N", "Ctrl-W", "Ctrl-E", "Ctrl-Y",
txt("ahibc\ndef\ng"));
sim("killRing", "abcdef",
"Ctrl-Space", "Ctrl-F", "Ctrl-W", "Ctrl-Space", "Ctrl-F", "Ctrl-W",
"Ctrl-Y", "Alt-Y",
txt("acdef"));
sim("copyYank", "abcd",
"Ctrl-Space", "Ctrl-E", "Alt-W", "Ctrl-Y",
txt("abcdabcd"));
sim("killLineSimple", "foo\nbar", "Ctrl-F", "Ctrl-K", txt("f\nbar"));
sim("killLineEmptyLine", "foo\n \nbar", "Ctrl-N", "Ctrl-K", txt("foo\nbar"));
sim("killLineMulti", "foo\nbar\nbaz",
"Ctrl-F", "Ctrl-F", "Ctrl-K", "Ctrl-K", "Ctrl-K", "Ctrl-A", "Ctrl-Y",
txt("o\nbarfo\nbaz"));
sim("moveByParagraph", "abc\ndef\n\n\nhij\nklm\n\n",
"Ctrl-F", "Ctrl-Down", at(2, 0), "Ctrl-Down", at(6, 0),
"Ctrl-N", "Ctrl-Up", at(3, 0), "Ctrl-Up", at(0, 0),
Pos(1, 2), "Ctrl-Down", at(2, 0), Pos(4, 2), "Ctrl-Up", at(3, 0));
sim("moveByParagraphMulti", "abc\n\ndef\n\nhij\n\nklm",
"Ctrl-U", "2", "Ctrl-Down", at(3, 0),
"Shift-Alt-.", "Ctrl-3", "Ctrl-Up", at(1, 0));
sim("moveBySentence", "sentence one! sentence\ntwo\n\nparagraph two",
"Alt-E", at(0, 13), "Alt-E", at(1, 3), "Ctrl-F", "Alt-A", at(0, 13));
sim("moveByExpr", "function foo(a, b) {}",
"Ctrl-Alt-F", at(0, 8), "Ctrl-Alt-F", at(0, 12), "Ctrl-Alt-F", at(0, 18),
"Ctrl-Alt-B", at(0, 12), "Ctrl-Alt-B", at(0, 9));
sim("moveByExprMulti", "foo bar baz bug",
"Ctrl-2", "Ctrl-Alt-F", at(0, 7),
"Ctrl--", "Ctrl-Alt-F", at(0, 4),
"Ctrl--", "Ctrl-2", "Ctrl-Alt-B", at(0, 11));
sim("delExpr", "var x = [\n a,\n b\n c\n];",
Pos(0, 8), "Ctrl-Alt-K", txt("var x = ;"), "Ctrl-/",
Pos(4, 1), "Ctrl-Alt-Backspace", txt("var x = ;"));
sim("delExprMulti", "foo bar baz",
"Ctrl-2", "Ctrl-Alt-K", txt(" baz"),
"Ctrl-/", "Ctrl-E", "Ctrl-2", "Ctrl-Alt-Backspace", txt("foo "));
sim("justOneSpace", "hi bye ",
Pos(0, 4), "Alt-Space", txt("hi bye "),
Pos(0, 4), "Alt-Space", txt("hi b ye "),
"Ctrl-A", "Alt-Space", "Ctrl-E", "Alt-Space", txt(" hi b ye "));
sim("openLine", "foo bar", "Alt-F", "Ctrl-O", txt("foo\n bar"))
sim("transposeChar", "abcd\ne",
"Ctrl-F", "Ctrl-T", "Ctrl-T", txt("bcad\ne"), at(0, 3),
"Ctrl-F", "Ctrl-T", "Ctrl-T", "Ctrl-T", txt("bcda\ne"), at(0, 4),
"Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 1));
sim("manipWordCase", "foo BAR bAZ",
"Alt-C", "Alt-L", "Alt-U", txt("Foo bar BAZ"),
"Ctrl-A", "Alt-U", "Alt-L", "Alt-C", txt("FOO bar Baz"));
sim("manipWordCaseMulti", "foo Bar bAz",
"Ctrl-2", "Alt-U", txt("FOO BAR bAz"),
"Ctrl-A", "Ctrl-3", "Alt-C", txt("Foo Bar Baz"));
sim("upExpr", "foo {\n bar[];\n baz(blah);\n}",
Pos(2, 7), "Ctrl-Alt-U", at(2, 5), "Ctrl-Alt-U", at(0, 4));
sim("transposeExpr", "do foo[bar] dah",
Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah"));
sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F",
"Ctrl-G", "Ctrl-W", txt("abcde"));
sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde"));
sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde"));
sim("backspaceDoesntAddToRing", "foobar", "Ctrl-F", "Ctrl-F", "Ctrl-F", "Ctrl-K", "Backspace", "Backspace", "Ctrl-Y", txt("fbar"));
sim("gotoLine", "0\n1\n2\n3", dialog("3"), "Alt-G", "G", at(2, 0));
sim("gotoInvalidLineFloat", "0\n1\n2\n3", dialog("2.2"), "Alt-G", "G", at(0, 0));
testCM("gotoDialogTemplate", function(cm) {
cm.openDialog = function(template, cb) {
var input = template.querySelector("input");
eq(template.textContent, "Goto line: ");
eq(input.tagName, "INPUT");
};
cm.triggerOnKeyDown(fakeEvent("Alt-G"));
cm.triggerOnKeyDown(fakeEvent("G"));
}, {value: "", keyMap: "emacs"});
testCM("save", function(cm) {
var saved = false;
CodeMirror.commands.save = function(cm) { saved = cm.getValue(); };
cm.triggerOnKeyDown(fakeEvent("Ctrl-X"));
cm.triggerOnKeyDown(fakeEvent("Ctrl-S"));
is(saved, "hi");
}, {value: "hi", keyMap: "emacs"});
})();

View File

@@ -0,0 +1,83 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
(function() {
var Pos = CodeMirror.Pos;
namespace = "html-hint_";
testData =[
{
name: "html-element",
value: "<htm",
list: ["<html"]
},
{
name: "element-close",
value: "<a href='#a'>\n</",
list: ["</a>"]
},
{
name: "linkref-attribute",
value: "<link hreflang='z",
from: Pos(0,"<link hreflang=".length),
list: ["'zh'","'za'","'zu'"]
},
{
name: "html-completion",
value: "<html>\n",
list: ["<head","<body","</html>"]
}
];
function escapeHtmlList(o) {
return '<code>' +
JSON.stringify(o.list,null,2)
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;") +
'</code>'
}
function test(name, spec) {
testCM(name, function(cm) {
cm.setValue(spec.value);
cm.setCursor(spec.cursor);
var completion = CodeMirror.hint.html(cm);
if (!deepCompare(completion.list, spec.list))
throw new Failure("Wrong completion results. Got" +
escapeHtmlList(completion) +" but expected" +
escapeHtmlList(spec));
eqCharPos(completion.from, spec.from,'from-failed');
eqCharPos(completion.to, spec.to, 'to-failed');
}, {
value: spec.value,
mode: spec.mode || "text/html"
});
}
testData.forEach(function (value) {
// Use sane defaults
var lines = value.value.split(/\n/);
value.to = value.pos || Pos(lines.length-1, lines[lines.length-1].length);
value.from = value.from || Pos(lines.length-1,0);
value.cursor = value.cursor || value.to;
var name = value.name ||value.value;
test(name,value)
});
function deepCompare(a, b) {
if (a === b) return true;
if (!(a && typeof a === "object") ||
!(b && typeof b === "object")) return false;
var array = a instanceof Array
if ((b instanceof Array) !== array) return false;
if (array) {
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; i++) if (!deepCompare(a[i], b[i])) return false
} else {
for (var p in a) if (!(p in b) || !deepCompare(a[p], b[p])) return false;
for (var p in b) if (!(p in a)) return false
}
return true
}
})();

View File

@@ -0,0 +1,290 @@
<!doctype html>
<meta charset="utf-8"/>
<title>CodeMirror: Test Suite</title>
<link rel=stylesheet href="../doc/docs.css">
<link rel="stylesheet" href="../lib/codemirror.css">
<link rel="stylesheet" href="mode_test.css">
<script>
var errored = []
window.onerror = function(e) { errored.push(e) }
</script>
<script src="../doc/activebookmark.js"></script>
<script src="../lib/codemirror.js"></script>
<script src="../mode/meta.js"></script>
<script src="../addon/mode/overlay.js"></script>
<script src="../addon/mode/multiplex.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/dialog/dialog.js"></script>
<script src="../addon/edit/matchbrackets.js"></script>
<script src="../addon/hint/sql-hint.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
<script src="../addon/hint/html-hint.js"></script>
<script src="../addon/comment/comment.js"></script>
<script src="../addon/mode/simple.js"></script>
<script src="../mode/css/css.js"></script>
<script src="../mode/clike/clike.js"></script>
<!-- clike must be after css or vim and sublime tests will fail -->
<script src="../mode/clojure/clojure.js"></script>
<script src="../mode/cypher/cypher.js"></script>
<script src="../mode/d/d.js"></script>
<script src="../mode/dockerfile/dockerfile.js"></script>
<script src="../mode/gfm/gfm.js"></script>
<script src="../mode/haml/haml.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/jsx/jsx.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/php/php.js"></script>
<script src="../mode/python/python.js"></script>
<script src="../mode/powershell/powershell.js"></script>
<script src="../mode/ruby/ruby.js"></script>
<script src="../mode/sass/sass.js"></script>
<script src="../mode/shell/shell.js"></script>
<script src="../mode/slim/slim.js"></script>
<script src="../mode/soy/soy.js"></script>
<script src="../mode/sql/sql.js"></script>
<script src="../mode/stex/stex.js"></script>
<script src="../mode/swift/swift.js"></script>
<script src="../mode/textile/textile.js"></script>
<script src="../mode/verilog/verilog.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/xquery/xquery.js"></script>
<script src="../keymap/emacs.js"></script>
<script src="../keymap/sublime.js"></script>
<script src="../keymap/vim.js"></script>
<script src="../mode/rust/rust.js"></script>
<script src="../mode/mscgen/mscgen.js"></script>
<script src="../mode/dylan/dylan.js"></script>
<script src="../mode/wast/wast.js"></script>
<style>
.ok { color: #090; }
.fail { color: #e00; }
.error { color: #c90; }
.done { font-weight: bold; }
#progress {
background: #45d;
color: white;
text-shadow: 0 0 1px #45d, 0 0 2px #45d, 0 0 3px #45d;
font-weight: bold;
white-space: pre;
}
#testground {
visibility: hidden;
}
#testground.offscreen {
visibility: visible;
position: absolute;
left: -10000px;
top: -10000px;
}
.CodeMirror { border: 1px solid black; }
</style>
<div id=nav>
<a href="https://codemirror.net/5"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
<ul>
<li><a href="../index.html">Home</a>
<li><a href="../doc/manual.html">Manual</a>
<li><a href="https://github.com/codemirror/codemirror5">Code</a>
</ul>
<ul>
<li><a class=active href="#">Test suite</a>
</ul>
</div>
<article>
<h2>Test Suite</h2>
<p>A limited set of programmatic sanity tests for CodeMirror.</p>
<div style="border: 1px solid black; padding: 1px; max-width: 700px;">
<div style="width: 0px;" id=progress><div style="padding: 3px;">Ran <span id="progress_ran">0</span><span id="progress_total"> of 0</span> tests</div></div>
</div>
<p id=status>Please enable JavaScript...</p>
<div id=output></div>
<div id=testground></div>
<script src="driver.js"></script>
<script src="test.js"></script>
<script src="doc_test.js"></script>
<script src="multi_test.js"></script>
<script src="contenteditable_test.js"></script>
<script src="scroll_test.js"></script>
<script src="comment_test.js"></script>
<script src="search_test.js"></script>
<script src="mode_test.js"></script>
<script src="../mode/clike/test.js"></script>
<script src="../mode/clojure/test.js"></script>
<script src="../mode/css/test.js"></script>
<script src="../mode/css/gss_test.js"></script>
<script src="../mode/css/scss_test.js"></script>
<script src="../mode/css/less_test.js"></script>
<script src="../mode/cypher/test.js"></script>
<script src="../mode/d/test.js"></script>
<script src="../mode/dockerfile/test.js"></script>
<script src="../mode/gfm/test.js"></script>
<script src="../mode/haml/test.js"></script>
<script src="../mode/javascript/test.js"></script>
<script src="../mode/jsx/test.js"></script>
<script src="../mode/markdown/test.js"></script>
<script src="../mode/php/test.js"></script>
<script src="../mode/powershell/test.js"></script>
<script src="../mode/ruby/test.js"></script>
<script src="../mode/sass/test.js"></script>
<script src="../mode/shell/test.js"></script>
<script src="../mode/slim/test.js"></script>
<script src="../mode/soy/test.js"></script>
<script src="../mode/stex/test.js"></script>
<script src="../mode/swift/test.js"></script>
<script src="../mode/textile/test.js"></script>
<script src="../mode/verilog/test.js"></script>
<script src="../mode/xml/test.js"></script>
<script src="../mode/xquery/test.js"></script>
<script src="../mode/python/test.js"></script>
<script src="../mode/rust/test.js"></script>
<script src="../mode/wast/test.js"></script>
<script src="../mode/mscgen/mscgen_test.js"></script>
<script src="../mode/mscgen/xu_test.js"></script>
<script src="../mode/mscgen/msgenny_test.js"></script>
<script src="../mode/dylan/test.js"></script>
<script src="../addon/mode/multiplex_test.js"></script>
<script src="../addon/fold/foldcode.js"></script>
<script src="../addon/fold/brace-fold.js"></script>
<script src="../addon/fold/xml-fold.js"></script>
<script src="../addon/scroll/annotatescrollbar.js"></script>
<script src="emacs_test.js"></script>
<script src="sql-hint-test.js"></script>
<script src="sublime_test.js"></script>
<script src="html-hint-test.js"></script>
<script src="annotatescrollbar.js"></script>
<script>
window.onload = runHarness;
CodeMirror.on(window, 'hashchange', runHarness);
function esc(str) {
return str.replace(/[<&]/, function(ch) { return ch == "<" ? "&lt;" : "&amp;"; });
}
var output = document.getElementById("output"),
progress = document.getElementById("progress"),
progressRan = document.getElementById("progress_ran").childNodes[0],
progressTotal = document.getElementById("progress_total").childNodes[0];
var count = 0,
failed = 0,
skipped = 0,
bad = "",
running = false, // Flag that states tests are running
quit = false, // Flag to quit tests ASAP
verbose = false, // Adds message for *every* test to output
done = false
function runHarness(){
if (running) {
quit = true;
setStatus("Restarting tests...", '', true);
setTimeout(function(){ runHarness(); }, 500);
return;
}
filters = [];
verbose = false;
if (window.location.hash.substr(1)){
var strings = window.location.hash.substr(1).split(",");
while (strings.length) {
var s = strings.shift();
if (s === "verbose")
verbose = true;
else
filters.push(parseTestFilter(decodeURIComponent(s)));
}
}
quit = false;
running = true;
setStatus("Loading tests...");
count = 0;
failed = 0;
done = false;
skipped = 0;
bad = "";
totalTests = countTests();
progressTotal.nodeValue = " of " + totalTests;
progressRan.nodeValue = count;
output.innerHTML = '';
document.getElementById("testground").innerHTML = "<form>" +
"<textarea id=\"code\" name=\"code\"></textarea>" +
"<input type=submit value=ok name=submit>" +
"</form>";
runTests(displayTest);
}
function setStatus(message, className, force){
if (quit && !force) return;
if (!message) throw("must provide message");
var status = document.getElementById("status").childNodes[0];
status.nodeValue = message;
status.parentNode.className = className;
}
function addOutput(name, className, code){
var newOutput = document.createElement("dl");
var newTitle = document.createElement("dt");
newTitle.className = className;
newTitle.appendChild(document.createTextNode(name));
newOutput.appendChild(newTitle);
var newMessage = document.createElement("dd");
newMessage.innerHTML = code;
newOutput.appendChild(newTitle);
newOutput.appendChild(newMessage);
output.appendChild(newOutput);
}
function displayTest(type, name, customMessage) {
var message = "???";
if (type != "done" && type != "skipped") ++count;
progress.style.width = (count * (progress.parentNode.clientWidth - 2) / (totalTests || 1)) + "px";
progressRan.nodeValue = count;
if (type == "ok") {
message = "Test '" + name + "' passed";
if (!verbose) customMessage = false;
} else if (type == "skipped") {
message = "Test '" + name + "' skipped";
++skipped;
if (!verbose) customMessage = false;
} else if (type == "expected") {
message = "Test '" + name + "' failed as expected";
if (!verbose) customMessage = false;
} else if (type == "error" || type == "fail") {
++failed;
message = "Test '" + name + "' failed";
} else if (type == "done") {
if (failed) {
type += " fail";
message = failed + " failure" + (failed > 1 ? "s" : "");
} else if (count < totalTests) {
failed = totalTests - count;
type += " fail";
message = failed + " failure" + (failed > 1 ? "s" : "");
} else {
type += " ok";
message = "All passed"
if (skipped) {
message += " (" + skipped + " skipped)";
}
}
done = true
progressTotal.nodeValue = '';
customMessage = true; // Hack to avoid adding to output
}
if (verbose && !customMessage) customMessage = message;
setStatus(message, type);
if (customMessage && customMessage.length > 0) {
addOutput(name, type, customMessage);
}
}
</script>
</article>

View File

@@ -0,0 +1,21 @@
var blint = require("blint");
["mode", "lib", "addon", "keymap"].forEach(function(dir) {
blint.checkDir(dir, {
browser: true,
allowedGlobals: ["CodeMirror", "define", "test", "requirejs", "globalThis", "WeakSet"],
ecmaVersion: 5,
tabs: dir == "lib"
});
});
["src"].forEach(function(dir) {
blint.checkDir(dir, {
browser: true,
allowedGlobals: ["WeakSet"],
ecmaVersion: 6,
semicolons: false
});
});
module.exports = {ok: blint.success()};

View File

@@ -0,0 +1,23 @@
.mt-output .mt-token {
border: 1px solid #ddd;
white-space: pre;
font-family: "Consolas", monospace;
text-align: center;
}
.mt-output .mt-style {
font-size: x-small;
}
.mt-output .mt-state {
font-size: x-small;
vertical-align: top;
}
.mt-output .mt-state-row {
display: none;
}
.mt-state-unhide .mt-output .mt-state-row {
display: table-row;
}

View File

@@ -0,0 +1,193 @@
/**
* Helper to test CodeMirror highlighting modes. It pretty prints output of the
* highlighter and can check against expected styles.
*
* Mode tests are registered by calling test.mode(testName, mode,
* tokens), where mode is a mode object as returned by
* CodeMirror.getMode, and tokens is an array of lines that make up
* the test.
*
* These lines are strings, in which styled stretches of code are
* enclosed in brackets `[]`, and prefixed by their style. For
* example, `[keyword if]`. Brackets in the code itself must be
* duplicated to prevent them from being interpreted as token
* boundaries. For example `a[[i]]` for `a[i]`. If a token has
* multiple styles, the styles must be separated by ampersands, for
* example `[tag&error </hmtl>]`.
*
* See the test.js files in the css, markdown, gfm, and stex mode
* directories for examples.
*/
(function() {
function findSingle(str, pos, ch) {
for (;;) {
var found = str.indexOf(ch, pos);
if (found == -1) return null;
if (str.charAt(found + 1) != ch) return found;
pos = found + 2;
}
}
var styleName = /[\w&-_]+/g;
function parseTokens(strs) {
var tokens = [], plain = "";
for (var i = 0; i < strs.length; ++i) {
if (i) plain += "\n";
var str = strs[i], pos = 0;
while (pos < str.length) {
var style = null, text;
if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") {
styleName.lastIndex = pos + 1;
var m = styleName.exec(str);
style = m[0].replace(/&/g, " ");
var textStart = pos + style.length + 2;
var end = findSingle(str, textStart, "]");
if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style);
text = str.slice(textStart, end);
pos = end + 1;
} else {
var end = findSingle(str, pos, "[");
if (end == null) end = str.length;
text = str.slice(pos, end);
pos = end;
}
text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);});
tokens.push({style: style, text: text});
plain += text;
}
}
return {tokens: tokens, plain: plain};
}
test.mode = function(name, mode, tokens, modeName) {
var data = parseTokens(tokens);
return test((modeName || mode.name) + "_" + name, function() {
return compare(data.plain, data.tokens, mode);
});
};
function esc(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}
function compare(text, expected, mode) {
var expectedOutput = [];
for (var i = 0; i < expected.length; ++i) {
var sty = expected[i].style;
if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
expectedOutput.push({style: sty, text: expected[i].text});
}
var observedOutput = highlight(text, mode);
var s = "";
var diff = highlightOutputsDifferent(expectedOutput, observedOutput);
if (diff != null) {
s += '<div class="mt-test mt-fail">';
s += '<pre>' + esc(text) + '</pre>';
s += '<div class="cm-s-default">';
s += 'expected:';
s += prettyPrintOutputTable(expectedOutput, diff);
s += 'observed: [<a onclick="this.parentElement.className+=\' mt-state-unhide\'">display states</a>]';
s += prettyPrintOutputTable(observedOutput, diff);
s += '</div>';
s += '</div>';
}
if (observedOutput.indentFailures) {
for (var i = 0; i < observedOutput.indentFailures.length; i++)
s += "<div class='mt-test mt-fail'>" + esc(observedOutput.indentFailures[i]) + "</div>";
}
if (s) throw new Failure(s);
}
function stringify(obj) {
function replacer(key, obj) {
if (typeof obj == "function") {
var m = obj.toString().match(/function\s*[^\s(]*/);
return m ? m[0] : "function";
}
return obj;
}
if (window.JSON && JSON.stringify)
return JSON.stringify(obj, replacer, 2);
return "[unsupported]"; // Fail safely if no native JSON.
}
function highlight(string, mode) {
var state = mode.startState();
var lines = string.replace(/\r\n/g,'\n').split('\n');
var st = [], pos = 0;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i], newLine = true;
if (mode.indent) {
var ws = line.match(/^\s*/)[0];
var indent = mode.indent(state, line.slice(ws.length), line);
if (indent != CodeMirror.Pass && indent != ws.length)
(st.indentFailures || (st.indentFailures = [])).push(
"Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")");
}
var stream = new CodeMirror.StringStream(line, 4, {
lookAhead: function(n) { return lines[i + n] }
});
if (line == "" && mode.blankLine) mode.blankLine(state);
/* Start copied code from CodeMirror.highlight */
while (!stream.eol()) {
for (var j = 0; j < 10 && stream.start >= stream.pos; j++)
var compare = mode.token(stream, state);
if (j == 10)
throw new Failure("Failed to advance the stream." + stream.string + " " + stream.pos);
var substr = stream.current();
if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' ');
stream.start = stream.pos;
if (pos && st[pos-1].style == compare && !newLine) {
st[pos-1].text += substr;
} else if (substr) {
st[pos++] = {style: compare, text: substr, state: stringify(state)};
}
// Give up when line is ridiculously long
if (stream.pos > 5000) {
st[pos++] = {style: null, text: this.text.slice(stream.pos)};
break;
}
newLine = false;
}
}
return st;
}
function highlightOutputsDifferent(o1, o2) {
var minLen = Math.min(o1.length, o2.length);
for (var i = 0; i < minLen; ++i)
if (o1[i].style != o2[i].style || o1[i].text != o2[i].text) return i;
if (o1.length > minLen || o2.length > minLen) return minLen;
}
function prettyPrintOutputTable(output, diffAt) {
var s = '<table class="mt-output">';
s += '<tr>';
for (var i = 0; i < output.length; ++i) {
var style = output[i].style, val = output[i].text;
s +=
'<td class="mt-token"' + (i == diffAt ? " style='background: pink'" : "") + '>' +
'<span class="cm-' + esc(String(style)) + '">' +
esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT
'</span>' +
'</td>';
}
s += '</tr><tr>';
for (var i = 0; i < output.length; ++i) {
s += '<td class="mt-style"><span>' + (output[i].style || null) + '</span></td>';
}
if(output[0].state) {
s += '</tr><tr class="mt-state-row" title="State AFTER each token">';
for (var i = 0; i < output.length; ++i) {
s += '<td class="mt-state"><pre>' + esc(output[i].state) + '</pre></td>';
}
}
s += '</tr></table>';
return s;
}
})();

View File

@@ -0,0 +1,295 @@
(function() {
namespace = "multi_";
function hasSelections(cm) {
var sels = cm.listSelections();
var given = (arguments.length - 1) / 4;
if (sels.length != given)
throw new Failure("expected " + given + " selections, found " + sels.length);
for (var i = 0, p = 1; i < given; i++, p += 4) {
var anchor = Pos(arguments[p], arguments[p + 1]);
var head = Pos(arguments[p + 2], arguments[p + 3]);
eqCharPos(sels[i].anchor, anchor, "anchor of selection " + i);
eqCharPos(sels[i].head, head, "head of selection " + i);
}
}
function hasCursors(cm) {
var sels = cm.listSelections();
var given = (arguments.length - 1) / 2;
if (sels.length != given)
throw new Failure("expected " + given + " selections, found " + sels.length);
for (var i = 0, p = 1; i < given; i++, p += 2) {
eqCursorPos(sels[i].anchor, sels[i].head, "something selected for " + i);
var head = Pos(arguments[p], arguments[p + 1]);
eqCharPos(sels[i].head, head, "selection " + i);
}
}
testCM("getSelection", function(cm) {
select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, {anchor: Pos(2, 2), head: Pos(2, 0)});
eq(cm.getSelection(), "1234\n56\n90");
eq(cm.getSelection(false).join("|"), "1234|56|90");
eq(cm.getSelections().join("|"), "1234\n56|90");
}, {value: "1234\n5678\n90"});
testCM("setSelection", function(cm) {
select(cm, Pos(3, 0), Pos(0, 0), {anchor: Pos(2, 5), head: Pos(1, 0)});
hasSelections(cm, 0, 0, 0, 0,
2, 5, 1, 0,
3, 0, 3, 0);
cm.setSelection(Pos(1, 2), Pos(1, 1));
hasSelections(cm, 1, 2, 1, 1);
select(cm, {anchor: Pos(1, 1), head: Pos(2, 4)},
{anchor: Pos(0, 0), head: Pos(1, 3)},
Pos(3, 0), Pos(2, 2));
hasSelections(cm, 0, 0, 2, 4,
3, 0, 3, 0);
cm.setSelections([{anchor: Pos(0, 1), head: Pos(0, 2)},
{anchor: Pos(1, 1), head: Pos(1, 2)},
{anchor: Pos(2, 1), head: Pos(2, 2)}], 1);
eqCharPos(cm.getCursor("head"), Pos(1, 2));
eqCharPos(cm.getCursor("anchor"), Pos(1, 1));
eqCharPos(cm.getCursor("from"), Pos(1, 1));
eqCharPos(cm.getCursor("to"), Pos(1, 2));
cm.setCursor(Pos(1, 1));
hasCursors(cm, 1, 1);
}, {value: "abcde\nabcde\nabcde\n"});
testCM("somethingSelected", function(cm) {
select(cm, Pos(0, 1), {anchor: Pos(0, 3), head: Pos(0, 5)});
eq(cm.somethingSelected(), true);
select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5));
eq(cm.somethingSelected(), false);
}, {value: "123456789"});
testCM("extendSelection", function(cm) {
select(cm, Pos(0, 1), Pos(1, 1), Pos(2, 1));
cm.setExtending(true);
cm.extendSelections([Pos(0, 2), Pos(1, 0), Pos(2, 3)]);
hasSelections(cm, 0, 1, 0, 2,
1, 1, 1, 0,
2, 1, 2, 3);
cm.extendSelection(Pos(2, 4), Pos(2, 0));
hasSelections(cm, 2, 4, 2, 0);
}, {value: "1234\n1234\n1234"});
testCM("addSelection", function(cm) {
select(cm, Pos(0, 1), Pos(1, 1));
cm.addSelection(Pos(0, 0), Pos(0, 4));
hasSelections(cm, 0, 0, 0, 4,
1, 1, 1, 1);
cm.addSelection(Pos(2, 2));
hasSelections(cm, 0, 0, 0, 4,
1, 1, 1, 1,
2, 2, 2, 2);
}, {value: "1234\n1234\n1234"});
testCM("replaceSelection", function(cm) {
var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)},
{anchor: Pos(0, 2), head: Pos(0, 3)},
{anchor: Pos(0, 4), head: Pos(0, 5)},
{anchor: Pos(2, 1), head: Pos(2, 4)},
{anchor: Pos(2, 5), head: Pos(2, 6)}];
var val = "123456\n123456\n123456";
cm.setValue(val);
cm.setSelections(selections);
cm.replaceSelection("ab", "around");
eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab");
hasSelections(cm, 0, 0, 0, 2,
0, 3, 0, 5,
0, 6, 0, 8,
2, 1, 2, 3,
2, 4, 2, 6);
cm.setValue(val);
cm.setSelections(selections);
cm.replaceSelection("", "around");
eq(cm.getValue(), "246\n123456\n15");
hasSelections(cm, 0, 0, 0, 0,
0, 1, 0, 1,
0, 2, 0, 2,
2, 1, 2, 1,
2, 2, 2, 2);
cm.setValue(val);
cm.setSelections(selections);
cm.replaceSelection("X\nY\nZ", "around");
hasSelections(cm, 0, 0, 2, 1,
2, 2, 4, 1,
4, 2, 6, 1,
8, 1, 10, 1,
10, 2, 12, 1);
cm.replaceSelection("a", "around");
hasSelections(cm, 0, 0, 0, 1,
0, 2, 0, 3,
0, 4, 0, 5,
2, 1, 2, 2,
2, 3, 2, 4);
cm.replaceSelection("xy", "start");
hasSelections(cm, 0, 0, 0, 0,
0, 3, 0, 3,
0, 6, 0, 6,
2, 1, 2, 1,
2, 4, 2, 4);
cm.replaceSelection("z\nf");
hasSelections(cm, 1, 1, 1, 1,
2, 1, 2, 1,
3, 1, 3, 1,
6, 1, 6, 1,
7, 1, 7, 1);
eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy");
});
function select(cm) {
var sels = [];
for (var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
if (arg.head) sels.push(arg);
else sels.push({head: arg, anchor: arg});
}
cm.setSelections(sels, sels.length - 1);
}
testCM("indentSelection", function(cm) {
select(cm, Pos(0, 1), Pos(1, 1));
cm.indentSelection(4);
eq(cm.getValue(), " foo\n bar\nbaz");
select(cm, Pos(0, 2), Pos(0, 3), Pos(0, 4));
cm.indentSelection(-2);
eq(cm.getValue(), " foo\n bar\nbaz");
select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)},
{anchor: Pos(1, 3), head: Pos(2, 0)});
cm.indentSelection(-2);
eq(cm.getValue(), "foo\n bar\nbaz");
}, {value: "foo\nbar\nbaz"});
testCM("killLine", function(cm) {
select(cm, Pos(0, 1), Pos(0, 2), Pos(1, 1));
cm.execCommand("killLine");
eq(cm.getValue(), "f\nb\nbaz");
cm.execCommand("killLine");
eq(cm.getValue(), "fbbaz");
cm.setValue("foo\nbar\nbaz");
select(cm, Pos(0, 1), {anchor: Pos(0, 2), head: Pos(2, 1)});
cm.execCommand("killLine");
eq(cm.getValue(), "faz");
}, {value: "foo\nbar\nbaz"});
testCM("deleteLine", function(cm) {
select(cm, Pos(0, 0),
{head: Pos(0, 1), anchor: Pos(2, 0)},
Pos(4, 0));
cm.execCommand("deleteLine");
eq(cm.getValue(), "4\n6\n7");
select(cm, Pos(2, 1));
cm.execCommand("deleteLine");
eq(cm.getValue(), "4\n6\n");
}, {value: "1\n2\n3\n4\n5\n6\n7"});
testCM("deleteH", function(cm) {
select(cm, Pos(0, 4), {anchor: Pos(1, 4), head: Pos(1, 5)});
cm.execCommand("delWordAfter");
eq(cm.getValue(), "foo bar baz\nabc ef ghi\n");
cm.execCommand("delWordAfter");
eq(cm.getValue(), "foo baz\nabc ghi\n");
cm.execCommand("delCharBefore");
cm.execCommand("delCharBefore");
eq(cm.getValue(), "fo baz\nab ghi\n");
select(cm, Pos(0, 3), Pos(0, 4), Pos(0, 5));
cm.execCommand("delWordAfter");
eq(cm.getValue(), "fo \nab ghi\n");
}, {value: "foo bar baz\nabc def ghi\n"});
testCM("goLineStart", function(cm) {
select(cm, Pos(0, 2), Pos(0, 3), Pos(1, 1));
cm.execCommand("goLineStart");
hasCursors(cm, 0, 0, 1, 0);
select(cm, Pos(1, 1), Pos(0, 1));
cm.setExtending(true);
cm.execCommand("goLineStart");
hasSelections(cm, 0, 1, 0, 0,
1, 1, 1, 0);
}, {value: "foo\nbar\nbaz"});
testCM("moveV", function(cm) {
select(cm, Pos(0, 2), Pos(1, 2));
cm.execCommand("goLineDown");
hasCursors(cm, 1, 2, 2, 2);
cm.execCommand("goLineUp");
hasCursors(cm, 0, 2, 1, 2);
cm.execCommand("goLineUp");
hasCursors(cm, 0, 0, 0, 2);
cm.execCommand("goLineUp");
hasCursors(cm, 0, 0);
select(cm, Pos(0, 2), Pos(1, 2));
cm.setExtending(true);
cm.execCommand("goLineDown");
hasSelections(cm, 0, 2, 2, 2);
}, {value: "12345\n12345\n12345"});
testCM("moveH", function(cm) {
select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5), Pos(2, 3));
cm.execCommand("goCharRight");
hasCursors(cm, 0, 2, 0, 4, 1, 0, 2, 4);
cm.execCommand("goCharLeft");
hasCursors(cm, 0, 1, 0, 3, 0, 5, 2, 3);
for (var i = 0; i < 15; i++)
cm.execCommand("goCharRight");
hasCursors(cm, 2, 4, 2, 5);
}, {value: "12345\n12345\n12345"});
testCM("newlineAndIndent", function(cm) {
select(cm, Pos(0, 5), Pos(1, 5));
cm.execCommand("newlineAndIndent");
hasCursors(cm, 1, 2, 3, 2);
eq(cm.getValue(), "x = [\n 1];\ny = [\n 2];");
cm.undo();
eq(cm.getValue(), "x = [1];\ny = [2];");
hasCursors(cm, 0, 5, 1, 5);
select(cm, Pos(0, 5), Pos(0, 6));
cm.execCommand("newlineAndIndent");
hasCursors(cm, 1, 2, 2, 0);
eq(cm.getValue(), "x = [\n 1\n];\ny = [2];");
}, {value: "x = [1];\ny = [2];", mode: "javascript"});
testCM("goDocStartEnd", function(cm) {
select(cm, Pos(0, 1), Pos(1, 1));
cm.execCommand("goDocStart");
hasCursors(cm, 0, 0);
select(cm, Pos(0, 1), Pos(1, 1));
cm.execCommand("goDocEnd");
hasCursors(cm, 1, 3);
select(cm, Pos(0, 1), Pos(1, 1));
cm.setExtending(true);
cm.execCommand("goDocEnd");
hasSelections(cm, 1, 1, 1, 3);
}, {value: "abc\ndef"});
testCM("selectionHistory", function(cm) {
for (var i = 0; i < 3; ++i)
cm.addSelection(Pos(0, i * 2), Pos(0, i * 2 + 1));
cm.execCommand("undoSelection");
eq(cm.getSelection(), "1\n2");
cm.execCommand("undoSelection");
eq(cm.getSelection(), "1");
cm.execCommand("undoSelection");
eq(cm.getSelection(), "");
eqCharPos(cm.getCursor(), Pos(0, 0));
cm.execCommand("redoSelection");
eq(cm.getSelection(), "1");
cm.execCommand("redoSelection");
eq(cm.getSelection(), "1\n2");
cm.execCommand("redoSelection");
eq(cm.getSelection(), "1\n2\n3");
}, {value: "1 2 3"});
testCM("selectionsMayTouch", function(cm) {
select(cm, Pos(0, 0), Pos(0, 2))
cm.setExtending(true);
cm.extendSelections([Pos(0, 2), Pos(0, 4)])
hasSelections(cm, 0, 0, 0, 2,
0, 2, 0, 4)
cm.extendSelections([Pos(0, 3), Pos(0, 4)])
hasSelections(cm, 0, 0, 0, 4)
}, {selectionsMayTouch: true, value: "1234"})
})();

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env node
var lint = require("./lint");
var files = new (require('node-static').Server)();
var server = require('http').createServer(function (req, res) {
req.addListener('end', function () {
files.serve(req, res, function (err/*, result */) {
if (err) {
console.error(err);
process.exit(1);
}
});
}).resume();
}).addListener('error', function (err) {
throw err;
}).listen(3000,(async () => {
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({args: ["--no-sandbox", "--disable-setuid-sandbox"]})
const page = await browser.newPage()
page.on('console', msg => console.log("console:", msg.text()))
page.on('dialog', async dialog => {
console.log(dialog.message())
await dialog.dismiss()
})
page.evaluateOnNewDocument(() => window.automatedTests = true)
await page.goto('http://localhost:3000/test/index.html#' + (process.argv[2] || ""))
while(1) {
if (await page.evaluate(() => window.done)) break
await sleep(200)
}
let [failed, errors] = await page.evaluate(() => [window.failed, window.errored])
for (let error of errors) console.log(error)
console.log(await page.evaluate(() => document.getElementById('output').innerText + "\n" +
document.getElementById('status').innerText))
process.exit(failed > 0 || errors.length || !lint.ok ? 1 : 0)
await browser.close()
})())
function sleep(n) { return new Promise(acc => setTimeout(acc, n)) }

View File

@@ -0,0 +1,126 @@
(function() {
"use strict";
namespace = "scroll_";
testCM("bars_hidden", function(cm) {
for (var i = 0;; i++) {
var wrapBox = cm.getWrapperElement().getBoundingClientRect();
var scrollBox = cm.getScrollerElement().getBoundingClientRect();
is(wrapBox.bottom < scrollBox.bottom - 10);
is(wrapBox.right < scrollBox.right - 10);
if (i == 1) break;
cm.getWrapperElement().style.height = "auto";
cm.refresh();
}
});
function barH(cm) { return byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; }
function barV(cm) { return byClassName(cm.getWrapperElement(), "CodeMirror-vscrollbar")[0]; }
function displayBottom(cm, scrollbar) {
if (scrollbar && cm.display.scroller.offsetHeight > cm.display.scroller.clientHeight)
return barH(cm).getBoundingClientRect().top;
else
return cm.getWrapperElement().getBoundingClientRect().bottom - 1;
}
function displayRight(cm, scrollbar) {
if (scrollbar && cm.display.scroller.offsetWidth > cm.display.scroller.clientWidth)
return barV(cm).getBoundingClientRect().left;
else
return cm.getWrapperElement().getBoundingClientRect().right - 1;
}
function testMovedownFixed(cm, hScroll) {
cm.setSize("100px", "100px");
if (hScroll) cm.setValue(new Array(100).join("x"));
var bottom = displayBottom(cm, hScroll);
for (var i = 0; i < 30; i++) {
cm.replaceSelection("x\n");
var cursorBottom = cm.cursorCoords(null, "window").bottom;
is(cursorBottom <= bottom);
}
is(cursorBottom >= bottom - 5);
}
testCM("movedown_fixed", function(cm) {testMovedownFixed(cm, false);});
testCM("movedown_hscroll_fixed", function(cm) {testMovedownFixed(cm, true);});
function testMovedownResize(cm, hScroll) {
cm.getWrapperElement().style.height = "auto";
if (hScroll) cm.setValue(new Array(100).join("x"));
cm.refresh();
for (var i = 0; i < 30; i++) {
cm.replaceSelection("x\n");
var bottom = displayBottom(cm, hScroll);
var cursorBottom = cm.cursorCoords(null, "window").bottom;
is(cursorBottom <= bottom);
is(cursorBottom >= bottom - 5);
}
}
testCM("movedown_resize", function(cm) {testMovedownResize(cm, false);});
testCM("movedown_hscroll_resize", function(cm) {testMovedownResize(cm, true);});
function testMoveright(cm, wrap, scroll) {
cm.setSize("100px", "100px");
if (wrap) cm.setOption("lineWrapping", true);
if (scroll) {
cm.setValue("\n" + new Array(100).join("x\n"));
cm.setCursor(Pos(0, 0));
}
var right = displayRight(cm, scroll);
for (var i = 0; i < 10; i++) {
cm.replaceSelection("xxxxxxxxxx");
var cursorRight = cm.cursorCoords(null, "window").right;
is(cursorRight < right);
}
if (!wrap) is(cursorRight > right - 20);
}
testCM("moveright", function(cm) {testMoveright(cm, false, false);});
testCM("moveright_wrap", function(cm) {testMoveright(cm, true, false);});
testCM("moveright_scroll", function(cm) {testMoveright(cm, false, true);});
testCM("moveright_scroll_wrap", function(cm) {testMoveright(cm, true, true);});
testCM("suddenly_wide", function(cm) {
addDoc(cm, 100, 100);
cm.replaceSelection(new Array(600).join("l ") + "\n");
cm.execCommand("goLineUp");
cm.execCommand("goLineEnd");
is(barH(cm).scrollLeft > cm.getScrollerElement().scrollLeft - 1);
});
testCM("wrap_changes_height", function(cm) {
var line = new Array(20).join("a ") + "\n";
cm.setValue(new Array(20).join(line));
var box = cm.getWrapperElement().getBoundingClientRect();
cm.setSize(cm.cursorCoords(Pos(0), "window").right - box.left + 2,
cm.cursorCoords(Pos(19, 0), "window").bottom - box.top + 2);
cm.setCursor(Pos(19, 0));
cm.replaceSelection("\n");
is(cm.cursorCoords(null, "window").bottom < displayBottom(cm, false));
}, {lineWrapping: true});
testCM("height_auto_with_gutter_expect_no_scroll_after_line_delete", function(cm) {
cm.setSize(null, "auto");
cm.setValue("x\n");
cm.execCommand("goDocEnd");
cm.execCommand("delCharBefore");
eq(cm.getScrollInfo().top, 0);
cm.scrollTo(null, 10);
is(cm.getScrollInfo().top < 5);
}, {lineNumbers: true});
testCM("bidi_ensureCursorVisible", function(cm) {
cm.setValue("<dd>وضع الاستخدام. عندما لا تعطى، وهذا الافتراضي إلى الطريقة الاولى\n");
cm.execCommand("goLineStart");
eq(cm.getScrollInfo().left, 0);
cm.execCommand("goCharRight");
cm.execCommand("goCharRight");
cm.execCommand("goCharRight");
eqCursorPos(cm.getCursor(), Pos(0, 3, "before"));
eq(cm.getScrollInfo().left, 0);
}, {lineWrapping: false});
})();

View File

@@ -0,0 +1,91 @@
(function() {
"use strict";
function run(doc, query, options) {
var cursor = doc.getSearchCursor(query, null, options);
for (var i = 3; i < arguments.length; i += 4) {
var found = cursor.findNext();
is(found, "not enough results (forward)");
eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, forward, " + (i - 3) / 4);
eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, forward, " + (i - 3) / 4);
}
is(!cursor.findNext(), "too many matches (forward)");
for (var i = arguments.length - 4; i >= 3; i -= 4) {
var found = cursor.findPrevious();
is(found, "not enough results (backwards)");
eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, backwards, " + (i - 3) / 4);
eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, backwards, " + (i - 3) / 4);
}
is(!cursor.findPrevious(), "too many matches (backwards)");
}
function test(name, f) { window.test("search_" + name, f) }
test("simple", function() {
var doc = new CodeMirror.Doc("abcdefg\nabcdefg")
run(doc, "cde", false, 0, 2, 0, 5, 1, 2, 1, 5);
});
test("multiline", function() {
var doc = new CodeMirror.Doc("hallo\na\nb\ngoodbye")
run(doc, "llo\na\nb\ngoo", false, 0, 2, 3, 3);
run(doc, "blah\na\nb\nhall", false);
run(doc, "bye\nx\neye", false);
});
test("regexp", function() {
var doc = new CodeMirror.Doc("abcde\nabcde")
run(doc, /bcd/, false, 0, 1, 0, 4, 1, 1, 1, 4);
run(doc, /BCD/, false);
run(doc, /BCD/i, false, 0, 1, 0, 4, 1, 1, 1, 4);
});
test("regexpMultiline", function() {
var doc = new CodeMirror.Doc("fom fom\nbar\nbaz")
run(doc, /fo[^]*az/, {multiline: true}, 0, 0, 2, 3)
run(doc, /[oa][^u]/, {multiline: true}, 0, 1, 0, 3, 0, 5, 0, 7, 1, 1, 1, 3, 2, 1, 2, 3)
run(doc, /[a][^u]{2}/, {multiline: true}, 1, 1, 2, 0)
})
test("insensitive", function() {
var doc = new CodeMirror.Doc("hallo\nHALLO\noink\nhAllO")
run(doc, "All", false, 3, 1, 3, 4);
run(doc, "All", true, 0, 1, 0, 4, 1, 1, 1, 4, 3, 1, 3, 4);
});
test("multilineInsensitive", function() {
var doc = new CodeMirror.Doc("zie ginds komT\nDe Stoomboot\nuit Spanje weer aan")
run(doc, "komt\nde stoomboot\nuit", false);
run(doc, "komt\nde stoomboot\nuit", {caseFold: true}, 0, 10, 2, 3);
run(doc, "kOMt\ndE stOOmboot\nuiT", {caseFold: true}, 0, 10, 2, 3);
});
test("multilineInsensitiveSlow", function() {
var text = ""
for (var i = 0; i < 1000; i++) text += "foo\nbar\n"
var doc = new CodeMirror.Doc("find\nme\n" + text + "find\nme\n")
var t0 = +new Date
run(doc, /find\nme/, {multiline: true}, 0, 0, 1, 2, 2002, 0, 2003, 2)
is(+new Date - t0 < 100)
})
test("expandingCaseFold", function() {
var doc = new CodeMirror.Doc("<b>İİ İİ</b>\n<b>uu uu</b>")
run(doc, "</b>", true, 0, 8, 0, 12, 1, 8, 1, 12);
run(doc, "İİ", true, 0, 3, 0, 5, 0, 6, 0, 8);
});
test("normalize", function() {
if (!String.prototype.normalize) return
var doc = new CodeMirror.Doc("yılbaşı\n수 있을까\nLe taux d'humidité à London")
run(doc, "s", false, 0, 5, 0, 6)
run(doc, "이", false, 1, 2, 1, 3)
run(doc, "a", false, 0, 4, 0, 5, 2, 4, 2, 5, 2, 19, 2, 20)
})
test("endOfLine", function() {
var doc = new CodeMirror.Doc("bbcdb\nabcd\nbbcdb\nabcd")
run(doc, /[^b]$/, {multiline: true}, 1, 3, 1, 4, 3, 3, 3, 4)
run(doc, /b$/, false, 0, 4, 0, 5, 2, 4, 2, 5)
})
})();

View File

@@ -0,0 +1,301 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
(function() {
var Pos = CodeMirror.Pos;
var simpleTables = {
"users": ["name", "score", "birthDate"],
"xcountries": ["name", "population", "size"]
};
var schemaTables = {
"schema.users": ["name", "score", "birthDate"],
"schema.countries": ["name", "population", "size"]
};
var displayTextTables = [{
text: "mytable",
displayText: "mytable | The main table",
columns: [{text: "id", displayText: "id | Unique ID"},
{text: "name", displayText: "name | The name"}]
}];
var displayTextTablesWithDefault = [
{
text: "Api__TokenAliases",
columns: [
{
text: "token",
displayText: "token | varchar(255) | Primary",
columnName: "token",
columnHint: "varchar(255) | Primary"
},
{
text: "alias",
displayText: "alias | varchar(255) | Primary",
columnName: "alias",
columnHint: "varchar(255) | Primary"
}
]
},
{
text: "mytable",
columns: [
{ text: "id", displayText: "id | Unique ID" },
{ text: "name", displayText: "name | The name" }
]
}
];
namespace = "sql-hint_";
function test(name, spec) {
testCM(name, function(cm) {
cm.setValue(spec.value);
cm.setCursor(spec.cursor);
var completion = CodeMirror.hint.sql(cm, {
tables: spec.tables,
defaultTable: spec.defaultTable,
disableKeywords: spec.disableKeywords
});
if (!deepCompare(completion.list, spec.list))
throw new Failure("Wrong completion results " + JSON.stringify(completion.list) + " vs " + JSON.stringify(spec.list));
eqCharPos(completion.from, spec.from);
eqCharPos(completion.to, spec.to);
}, {
value: spec.value,
mode: spec.mode || "text/x-mysql"
});
}
test("keywords", {
value: "SEL",
cursor: Pos(0, 3),
list: [{"text":"SELECT","className":"CodeMirror-hint-keyword"}],
from: Pos(0, 0),
to: Pos(0, 3)
});
test("keywords_disabled", {
value: "SEL",
cursor: Pos(0, 3),
disableKeywords: true,
list: [],
from: Pos(0, 0),
to: Pos(0, 3)
});
test("from", {
value: "SELECT * fr",
cursor: Pos(0, 11),
list: [{"text":"FROM","className":"CodeMirror-hint-keyword"}],
from: Pos(0, 9),
to: Pos(0, 11)
});
test("table", {
value: "SELECT xc",
cursor: Pos(0, 9),
tables: simpleTables,
list: [{"text":"xcountries","className":"CodeMirror-hint-table"}],
from: Pos(0, 7),
to: Pos(0, 9)
});
test("columns", {
value: "SELECT users.",
cursor: Pos(0, 13),
tables: simpleTables,
list: ["users.name", "users.score", "users.birthDate"],
from: Pos(0, 7),
to: Pos(0, 13)
});
test("singlecolumn", {
value: "SELECT users.na",
cursor: Pos(0, 15),
tables: simpleTables,
list: ["users.name"],
from: Pos(0, 7),
to: Pos(0, 15)
});
test("quoted", {
value: "SELECT `users`.`na",
cursor: Pos(0, 18),
tables: simpleTables,
list: ["`users`.`name`"],
from: Pos(0, 7),
to: Pos(0, 18)
});
test("doublequoted", {
value: "SELECT \"users\".\"na",
cursor: Pos(0, 18),
tables: simpleTables,
list: ["\"users\".\"name\""],
from: Pos(0, 7),
to: Pos(0, 18),
mode: "text/x-sqlite"
});
test("quotedcolumn", {
value: "SELECT users.`na",
cursor: Pos(0, 16),
tables: simpleTables,
list: ["`users`.`name`"],
from: Pos(0, 7),
to: Pos(0, 16)
});
test("doublequotedcolumn", {
value: "SELECT users.\"na",
cursor: Pos(0, 16),
tables: simpleTables,
list: ["\"users\".\"name\""],
from: Pos(0, 7),
to: Pos(0, 16),
mode: "text/x-sqlite"
});
test("schema", {
value: "SELECT schem",
cursor: Pos(0, 12),
tables: schemaTables,
list: [{"text":"schema.users","className":"CodeMirror-hint-table"},
{"text":"schema.countries","className":"CodeMirror-hint-table"},
{"text":"SCHEMA","className":"CodeMirror-hint-keyword"},
{"text":"SCHEMA_NAME","className":"CodeMirror-hint-keyword"},
{"text":"SCHEMAS","className":"CodeMirror-hint-keyword"}],
from: Pos(0, 7),
to: Pos(0, 12)
});
test("schemaquoted", {
value: "SELECT `sch",
cursor: Pos(0, 11),
tables: schemaTables,
list: ["`schema`.`users`", "`schema`.`countries`"],
from: Pos(0, 7),
to: Pos(0, 11)
});
test("schemadoublequoted", {
value: "SELECT \"sch",
cursor: Pos(0, 11),
tables: schemaTables,
list: ["\"schema\".\"users\"", "\"schema\".\"countries\""],
from: Pos(0, 7),
to: Pos(0, 11),
mode: "text/x-sqlite"
});
test("schemacolumn", {
value: "SELECT schema.users.",
cursor: Pos(0, 20),
tables: schemaTables,
list: ["schema.users.name",
"schema.users.score",
"schema.users.birthDate"],
from: Pos(0, 7),
to: Pos(0, 20)
});
test("schemacolumnquoted", {
value: "SELECT `schema`.`users`.",
cursor: Pos(0, 24),
tables: schemaTables,
list: ["`schema`.`users`.`name`",
"`schema`.`users`.`score`",
"`schema`.`users`.`birthDate`"],
from: Pos(0, 7),
to: Pos(0, 24)
});
test("schemacolumndoublequoted", {
value: "SELECT \"schema\".\"users\".",
cursor: Pos(0, 24),
tables: schemaTables,
list: ["\"schema\".\"users\".\"name\"",
"\"schema\".\"users\".\"score\"",
"\"schema\".\"users\".\"birthDate\""],
from: Pos(0, 7),
to: Pos(0, 24),
mode: "text/x-sqlite"
});
test("displayText_default_table", {
value: "SELECT a",
cursor: Pos(0, 8),
disableKeywords: true,
defaultTable: "Api__TokenAliases",
tables: displayTextTablesWithDefault,
list: [
{
text: "alias",
displayText: "alias | varchar(255) | Primary",
columnName: "alias",
columnHint: "varchar(255) | Primary",
className: "CodeMirror-hint-table CodeMirror-hint-default-table"
},
{ text: "Api__TokenAliases", className: "CodeMirror-hint-table" }
],
from: Pos(0, 7),
to: Pos(0, 8)
});
test("displayText_table", {
value: "SELECT myt",
cursor: Pos(0, 10),
tables: displayTextTables,
list: [{text: "mytable", displayText: "mytable | The main table", "className":"CodeMirror-hint-table"}],
from: Pos(0, 7),
to: Pos(0, 10)
});
test("displayText_column", {
value: "SELECT mytable.",
cursor: Pos(0, 15),
tables: displayTextTables,
list: [{text: "mytable.id", displayText: "id | Unique ID"},
{text: "mytable.name", displayText: "name | The name"}],
from: Pos(0, 7),
to: Pos(0, 15)
});
test("alias_complete", {
value: "SELECT t. FROM users t",
cursor: Pos(0, 9),
tables: simpleTables,
list: ["t.name", "t.score", "t.birthDate"],
from: Pos(0, 7),
to: Pos(0, 9)
});
test("alias_complete_with_displayText", {
value: "SELECT t. FROM mytable t",
cursor: Pos(0, 9),
tables: displayTextTables,
list: [{text: "t.id", displayText: "id | Unique ID"},
{text: "t.name", displayText: "name | The name"}],
from: Pos(0, 7),
to: Pos(0, 9)
})
function deepCompare(a, b) {
if (a === b) return true
if (!(a && typeof a == "object") ||
!(b && typeof b == "object")) return false
var array = Array.isArray(a)
if (Array.isArray(b) != array) return false
if (array) {
if (a.length != b.length) return false
for (var i = 0; i < a.length; i++) if (!deepCompare(a[i], b[i])) return false
} else {
for (var p in a) if (!(p in b) || !deepCompare(a[p], b[p])) return false
for (var p in b) if (!(p in a)) return false
}
return true
}
})();

View File

@@ -0,0 +1,295 @@
(function() {
"use strict";
var Pos = CodeMirror.Pos;
namespace = "sublime_";
function stTest(name) {
var actions = Array.prototype.slice.call(arguments, 1);
testCM(name, function(cm) {
for (var i = 0; i < actions.length; i++) {
var action = actions[i];
if (typeof action == "string" && i == 0)
cm.setValue(action);
else if (typeof action == "string")
cm.execCommand(action);
else if (action instanceof Pos)
cm.setCursor(action);
else
action(cm);
}
});
}
function at(line, ch, msg) {
return function(cm) {
eq(cm.listSelections().length, 1);
eqCursorPos(cm.getCursor("head"), Pos(line, ch), msg);
eqCursorPos(cm.getCursor("anchor"), Pos(line, ch), msg);
};
}
function val(content, msg) {
return function(cm) { eq(cm.getValue(), content, msg); };
}
function argsToRanges(args) {
if (args.length % 4) throw new Error("Wrong number of arguments for ranges.");
var ranges = [];
for (var i = 0; i < args.length; i += 4)
ranges.push({anchor: Pos(args[i], args[i + 1]),
head: Pos(args[i + 2], args[i + 3])});
return ranges;
}
function setSel() {
var ranges = argsToRanges(arguments);
return function(cm) { cm.setSelections(ranges, 0); };
}
function hasSel() {
var ranges = argsToRanges(arguments);
return function(cm) {
var sels = cm.listSelections();
if (sels.length != ranges.length)
throw new Failure("Expected " + ranges.length + " selections, but found " + sels.length);
for (var i = 0; i < sels.length; i++) {
eqCharPos(sels[i].anchor, ranges[i].anchor, "anchor " + i);
eqCharPos(sels[i].head, ranges[i].head, "head " + i);
}
};
}
stTest("bySubword", "the foo_bar DooDahBah \n a FOOBar",
"goSubwordLeft", at(0, 0),
"goSubwordRight", at(0, 3),
"goSubwordRight", at(0, 7),
"goSubwordRight", at(0, 11),
"goSubwordRight", at(0, 15),
"goSubwordRight", at(0, 18),
"goSubwordRight", at(0, 21),
"goSubwordRight", at(0, 22),
"goSubwordRight", at(1, 0),
"goSubwordRight", at(1, 2),
"goSubwordRight", at(1, 6),
"goSubwordRight", at(1, 9),
"goSubwordLeft", at(1, 6),
"goSubwordLeft", at(1, 3),
"goSubwordLeft", at(1, 1),
"goSubwordLeft", at(1, 0),
"goSubwordLeft", at(0, 22),
"goSubwordLeft", at(0, 18),
"goSubwordLeft", at(0, 15),
"goSubwordLeft", at(0, 12),
"goSubwordLeft", at(0, 8),
"goSubwordLeft", at(0, 4),
"goSubwordLeft", at(0, 0));
stTest("splitSelectionByLine", "abc\ndef\nghi",
setSel(0, 1, 2, 2),
"splitSelectionByLine",
hasSel(0, 1, 0, 3,
1, 0, 1, 3,
2, 0, 2, 2));
stTest("splitSelectionByLineMulti", "abc\ndef\nghi\njkl",
setSel(0, 1, 1, 1,
1, 2, 3, 2,
3, 3, 3, 3),
"splitSelectionByLine",
hasSel(0, 1, 0, 3,
1, 0, 1, 1,
1, 2, 1, 3,
2, 0, 2, 3,
3, 0, 3, 2,
3, 3, 3, 3));
stTest("selectLine", "abc\ndef\nghi",
setSel(0, 1, 0, 1,
2, 0, 2, 1),
"selectLine",
hasSel(0, 0, 1, 0,
2, 0, 2, 3),
setSel(0, 1, 1, 0),
"selectLine",
hasSel(0, 0, 2, 0));
stTest("insertLineAfter", "abcde\nfghijkl\nmn",
setSel(0, 1, 0, 1,
0, 3, 0, 3,
1, 2, 1, 2,
1, 3, 1, 5), "insertLineAfter",
hasSel(1, 0, 1, 0,
3, 0, 3, 0), val("abcde\n\nfghijkl\n\nmn"));
stTest("insertLineBefore", "abcde\nfghijkl\nmn",
setSel(0, 1, 0, 1,
0, 3, 0, 3,
1, 2, 1, 2,
1, 3, 1, 5), "insertLineBefore",
hasSel(0, 0, 0, 0,
2, 0, 2, 0), val("\nabcde\n\nfghijkl\nmn"));
stTest("skipAndSelectNextOccurrence", "a foo bar\nfoobar foo",
setSel(0, 2, 0, 5), "skipAndSelectNextOccurrence", hasSel(1, 0, 1, 3),
"skipAndSelectNextOccurrence", hasSel(1, 7, 1, 10),
"skipAndSelectNextOccurrence", hasSel(0, 2, 0, 5),
Pos(0, 3), "skipAndSelectNextOccurrence", hasSel(0, 2, 0, 5),
"skipAndSelectNextOccurrence", hasSel(1, 7, 1, 10),
setSel(0, 6, 0, 9), "skipAndSelectNextOccurrence", hasSel(1, 3, 1, 6));
stTest("selectNextOccurrence", "a foo bar\nfoobar foo",
setSel(0, 2, 0, 5),
"selectNextOccurrence", hasSel(0, 2, 0, 5,
1, 0, 1, 3),
"selectNextOccurrence", hasSel(0, 2, 0, 5,
1, 0, 1, 3,
1, 7, 1, 10),
"selectNextOccurrence", hasSel(0, 2, 0, 5,
1, 0, 1, 3,
1, 7, 1, 10),
Pos(0, 3), "selectNextOccurrence", hasSel(0, 2, 0, 5),
"selectNextOccurrence", hasSel(0, 2, 0, 5,
1, 7, 1, 10),
setSel(0, 6, 0, 9),
"selectNextOccurrence", hasSel(0, 6, 0, 9,
1, 3, 1, 6));
stTest("selectScope", "foo(a) {\n bar[1, 2];\n}",
"selectScope", hasSel(0, 0, 2, 1),
Pos(0, 4), "selectScope", hasSel(0, 4, 0, 5),
Pos(0, 5), "selectScope", hasSel(0, 4, 0, 5),
Pos(0, 6), "selectScope", hasSel(0, 0, 2, 1),
Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0),
Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0),
Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10),
Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10),
"selectScope", hasSel(0, 8, 2, 0),
"selectScope", hasSel(0, 0, 2, 1));
stTest("goToBracket", "foo(a) {\n bar[1, 2];\n}",
Pos(0, 0), "goToBracket", at(0, 0),
Pos(0, 4), "goToBracket", at(0, 5), "goToBracket", at(0, 4),
Pos(0, 8), "goToBracket", at(2, 0), "goToBracket", at(0, 8),
Pos(1, 2), "goToBracket", at(2, 0),
Pos(1, 7), "goToBracket", at(1, 10), "goToBracket", at(1, 6));
stTest("swapLine", "1\n2\n3---\n4\n5",
"swapLineDown", val("2\n1\n3---\n4\n5"),
"swapLineUp", val("1\n2\n3---\n4\n5"),
"swapLineUp", val("1\n2\n3---\n4\n5"),
Pos(4, 1), "swapLineDown", val("1\n2\n3---\n4\n5"),
setSel(0, 1, 0, 1,
1, 0, 2, 0,
2, 2, 2, 2),
"swapLineDown", val("4\n1\n2\n3---\n5"),
hasSel(1, 1, 1, 1,
2, 0, 3, 0,
3, 2, 3, 2),
"swapLineUp", val("1\n2\n3---\n4\n5"),
hasSel(0, 1, 0, 1,
1, 0, 2, 0,
2, 2, 2, 2));
stTest("swapLineEmptyBottomSel", "1\n2\n3",
setSel(0, 1, 1, 0),
"swapLineDown", val("2\n1\n3"), hasSel(1, 1, 2, 0),
"swapLineUp", val("1\n2\n3"), hasSel(0, 1, 1, 0),
"swapLineUp", val("1\n2\n3"), hasSel(0, 0, 0, 0));
stTest("swapLineUpFromEnd", "a\nb\nc",
Pos(2, 1), "swapLineUp",
hasSel(1, 1, 1, 1), val("a\nc\nb"));
stTest("joinLines", "abc\ndef\nghi\njkl",
"joinLines", val("abc def\nghi\njkl"), at(0, 4),
"undo",
setSel(0, 2, 1, 1), "joinLines",
val("abc def ghi\njkl"), hasSel(0, 2, 0, 8),
"undo",
setSel(0, 1, 0, 1,
1, 1, 1, 1,
3, 1, 3, 1), "joinLines",
val("abc def ghi\njkl"), hasSel(0, 4, 0, 4,
0, 8, 0, 8,
1, 3, 1, 3));
stTest("duplicateLine", "abc\ndef\nghi",
Pos(1, 0), "duplicateLine", val("abc\ndef\ndef\nghi"), at(2, 0),
"undo",
setSel(0, 1, 0, 1,
1, 1, 1, 1,
2, 1, 2, 1), "duplicateLine",
val("abc\nabc\ndef\ndef\nghi\nghi"), hasSel(1, 1, 1, 1,
3, 1, 3, 1,
5, 1, 5, 1));
stTest("duplicateLineSelection", "abcdef",
setSel(0, 1, 0, 1,
0, 2, 0, 4,
0, 5, 0, 5),
"duplicateLine",
val("abcdef\nabcdcdef\nabcdcdef"), hasSel(2, 1, 2, 1,
2, 4, 2, 6,
2, 7, 2, 7));
stTest("sortLines", "c\nb\na\nC\nB\nA",
"sortLines", val("A\nB\nC\na\nb\nc"),
"undo",
setSel(0, 0, 2, 0,
3, 0, 5, 0),
"sortLines", val("b\nc\na\nB\nC\nA"),
hasSel(0, 0, 2, 0,
3, 0, 5, 0),
"undo",
setSel(1, 0, 5, 0), "sortLinesInsensitive", val("c\na\nB\nb\nC\nA"));
stTest("bookmarks", "abc\ndef\nghi\njkl",
Pos(0, 1), "toggleBookmark",
setSel(1, 1, 1, 2), "toggleBookmark",
setSel(2, 1, 2, 2), "toggleBookmark",
"nextBookmark", hasSel(0, 1, 0, 1),
"nextBookmark", hasSel(1, 1, 1, 2),
"nextBookmark", hasSel(2, 1, 2, 2),
"prevBookmark", hasSel(1, 1, 1, 2),
"prevBookmark", hasSel(0, 1, 0, 1),
"prevBookmark", hasSel(2, 1, 2, 2),
"prevBookmark", hasSel(1, 1, 1, 2),
"toggleBookmark",
"prevBookmark", hasSel(2, 1, 2, 2),
"prevBookmark", hasSel(0, 1, 0, 1),
"selectBookmarks", hasSel(0, 1, 0, 1,
2, 1, 2, 2),
"clearBookmarks",
Pos(0, 0), "selectBookmarks", at(0, 0));
stTest("smartBackspace", " foo\n bar",
setSel(0, 2, 0, 2, 1, 4, 1, 4, 1, 6, 1, 6), "smartBackspace",
val("foo\n br"))
stTest("upAndDowncaseAtCursor", "abc\ndef x\nghI",
setSel(0, 1, 0, 3,
1, 1, 1, 1,
1, 4, 1, 4), "upcaseAtCursor",
val("aBC\nDEF x\nghI"), hasSel(0, 1, 0, 3,
1, 3, 1, 3,
1, 4, 1, 4),
"downcaseAtCursor",
val("abc\ndef x\nghI"), hasSel(0, 1, 0, 3,
1, 3, 1, 3,
1, 4, 1, 4));
stTest("mark", "abc\ndef\nghi",
Pos(1, 1), "setSublimeMark",
Pos(2, 1), "selectToSublimeMark", hasSel(2, 1, 1, 1),
Pos(0, 1), "swapWithSublimeMark", at(1, 1), "swapWithSublimeMark", at(0, 1),
"deleteToSublimeMark", val("aef\nghi"),
"sublimeYank", val("abc\ndef\nghi"), at(1, 1));
stTest("findUnder", "foo foobar a",
"findUnder", hasSel(0, 4, 0, 7),
"findUnder", hasSel(0, 0, 0, 3),
"findUnderPrevious", hasSel(0, 4, 0, 7),
"findUnderPrevious", hasSel(0, 0, 0, 3),
Pos(0, 4), "findUnder", hasSel(0, 4, 0, 10),
Pos(0, 11), "findUnder", hasSel(0, 11, 0, 11));
})();

File diff suppressed because it is too large Load Diff