doc_test.min.js (11353B)
1 (function() { 2 // A minilanguage for instantiating linked CodeMirror instances and Docs 3 function instantiateSpec(spec, place, opts) { 4 var names = {}, pos = 0, l = spec.length, editors = []; 5 while (spec) { 6 var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); 7 var name = m[1], isDoc = m[2], cur; 8 if (m[3]) { 9 cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); 10 } else { 11 var other = m[5]; 12 if (!names.hasOwnProperty(other)) { 13 names[other] = editors.length; 14 editors.push(CodeMirror(place, opts)); 15 } 16 var doc = editors[names[other]].linkedDoc({ 17 sharedHist: !m[4], 18 from: m[6] ? Number(m[6]) : null, 19 to: m[7] ? Number(m[7]) : null 20 }); 21 cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); 22 } 23 names[name] = editors.length; 24 editors.push(cur); 25 spec = spec.slice(m[0].length); 26 } 27 return editors; 28 } 29 30 function clone(obj, props) { 31 if (!obj) return; 32 clone.prototype = obj; 33 var inst = new clone(); 34 if (props) for (var n in props) if (props.hasOwnProperty(n)) 35 inst[n] = props[n]; 36 return inst; 37 } 38 39 function eqAll(val) { 40 var end = arguments.length, msg = null; 41 if (typeof arguments[end-1] == "string") 42 msg = arguments[--end]; 43 if (i == end) throw new Error("No editors provided to eqAll"); 44 for (var i = 1; i < end; ++i) 45 eq(arguments[i].getValue(), val, msg) 46 } 47 48 function testDoc(name, spec, run, opts, expectFail) { 49 if (!opts) opts = {}; 50 51 return test("doc_" + name, function() { 52 var place = document.getElementById("testground"); 53 var editors = instantiateSpec(spec, place, opts); 54 var successful = false; 55 56 try { 57 run.apply(null, editors); 58 successful = true; 59 } finally { 60 if (!successful || verbose) { 61 place.style.visibility = "visible"; 62 } else { 63 for (var i = 0; i < editors.length; ++i) 64 if (editors[i] instanceof CodeMirror) 65 place.removeChild(editors[i].getWrapperElement()); 66 } 67 } 68 }, expectFail); 69 } 70 71 var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); 72 73 function testBasic(a, b) { 74 eqAll("x", a, b); 75 a.setValue("hey"); 76 eqAll("hey", a, b); 77 b.setValue("wow"); 78 eqAll("wow", a, b); 79 a.replaceRange("u\nv\nw", Pos(0, 3)); 80 b.replaceRange("i", Pos(0, 4)); 81 b.replaceRange("j", Pos(2, 1)); 82 eqAll("wowui\nv\nwj", a, b); 83 } 84 85 testDoc("basic", "A='x' B<A", testBasic); 86 testDoc("basicSeparate", "A='x' B<~A", testBasic); 87 88 testDoc("sharedHist", "A='ab\ncd\nef' B<A", function(a, b) { 89 a.replaceRange("x", Pos(0)); 90 b.replaceRange("y", Pos(1)); 91 a.replaceRange("z", Pos(2)); 92 eqAll("abx\ncdy\nefz", a, b); 93 a.undo(); 94 a.undo(); 95 eqAll("abx\ncd\nef", a, b); 96 a.redo(); 97 eqAll("abx\ncdy\nef", a, b); 98 b.redo(); 99 eqAll("abx\ncdy\nefz", a, b); 100 a.undo(); b.undo(); a.undo(); a.undo(); 101 eqAll("ab\ncd\nef", a, b); 102 }, null, ie_lt8); 103 104 testDoc("undoIntact", "A='ab\ncd\nef' B<~A", function(a, b) { 105 a.replaceRange("x", Pos(0)); 106 b.replaceRange("y", Pos(1)); 107 a.replaceRange("z", Pos(2)); 108 a.replaceRange("q", Pos(0)); 109 eqAll("abxq\ncdy\nefz", a, b); 110 a.undo(); 111 a.undo(); 112 eqAll("abx\ncdy\nef", a, b); 113 b.undo(); 114 eqAll("abx\ncd\nef", a, b); 115 a.redo(); 116 eqAll("abx\ncd\nefz", a, b); 117 a.redo(); 118 eqAll("abxq\ncd\nefz", a, b); 119 a.undo(); a.undo(); a.undo(); a.undo(); 120 eqAll("ab\ncd\nef", a, b); 121 b.redo(); 122 eqAll("ab\ncdy\nef", a, b); 123 }); 124 125 testDoc("undoConflict", "A='ab\ncd\nef' B<~A", function(a, b) { 126 a.replaceRange("x", Pos(0)); 127 a.replaceRange("z", Pos(2)); 128 // This should clear the first undo event in a, but not the second 129 b.replaceRange("y", Pos(0)); 130 a.undo(); a.undo(); 131 eqAll("abxy\ncd\nef", a, b); 132 a.replaceRange("u", Pos(2)); 133 a.replaceRange("v", Pos(0)); 134 // This should clear both events in a 135 b.replaceRange("w", Pos(0)); 136 a.undo(); a.undo(); 137 eqAll("abxyvw\ncd\nefu", a, b); 138 }); 139 140 testDoc("doubleRebase", "A='ab\ncd\nef\ng' B<~A C<B", function(a, b, c) { 141 c.replaceRange("u", Pos(3)); 142 a.replaceRange("", Pos(0, 0), Pos(1, 0)); 143 c.undo(); 144 eqAll("cd\nef\ng", a, b, c); 145 }); 146 147 testDoc("undoUpdate", "A='ab\ncd\nef' B<~A", function(a, b) { 148 a.replaceRange("x", Pos(2)); 149 b.replaceRange("u\nv\nw\n", Pos(0, 0)); 150 a.undo(); 151 eqAll("u\nv\nw\nab\ncd\nef", a, b); 152 a.redo(); 153 eqAll("u\nv\nw\nab\ncd\nefx", a, b); 154 a.undo(); 155 eqAll("u\nv\nw\nab\ncd\nef", a, b); 156 b.undo(); 157 a.redo(); 158 eqAll("ab\ncd\nefx", a, b); 159 a.undo(); 160 eqAll("ab\ncd\nef", a, b); 161 }); 162 163 testDoc("undoKeepRanges", "A='abcdefg' B<A", function(a, b) { 164 var m = a.markText(Pos(0, 1), Pos(0, 3), {className: "foo"}); 165 b.replaceRange("x", Pos(0, 0)); 166 eqCharPos(m.find().from, Pos(0, 2)); 167 b.replaceRange("yzzy", Pos(0, 1), Pos(0)); 168 eq(m.find(), null); 169 b.undo(); 170 eqCharPos(m.find().from, Pos(0, 2)); 171 b.undo(); 172 eqCharPos(m.find().from, Pos(0, 1)); 173 }); 174 175 testDoc("longChain", "A='uv' B<A C<B D<C", function(a, b, c, d) { 176 a.replaceSelection("X"); 177 eqAll("Xuv", a, b, c, d); 178 d.replaceRange("Y", Pos(0)); 179 eqAll("XuvY", a, b, c, d); 180 }); 181 182 testDoc("broadCast", "B<A C<A D<A E<A", function(a, b, c, d, e) { 183 b.setValue("uu"); 184 eqAll("uu", a, b, c, d, e); 185 a.replaceRange("v", Pos(0, 1)); 186 eqAll("uvu", a, b, c, d, e); 187 }); 188 189 // A and B share a history, C and D share a separate one 190 testDoc("islands", "A='x\ny\nz' B<A C<~A D<C", function(a, b, c, d) { 191 a.replaceRange("u", Pos(0)); 192 d.replaceRange("v", Pos(2)); 193 b.undo(); 194 eqAll("x\ny\nzv", a, b, c, d); 195 c.undo(); 196 eqAll("x\ny\nz", a, b, c, d); 197 a.redo(); 198 eqAll("xu\ny\nz", a, b, c, d); 199 d.redo(); 200 eqAll("xu\ny\nzv", a, b, c, d); 201 }); 202 203 testDoc("unlink", "B<A C<A D<B", function(a, b, c, d) { 204 a.setValue("hi"); 205 b.unlinkDoc(a); 206 d.setValue("aye"); 207 eqAll("hi", a, c); 208 eqAll("aye", b, d); 209 a.setValue("oo"); 210 eqAll("oo", a, c); 211 eqAll("aye", b, d); 212 }); 213 214 testDoc("bareDoc", "A*='foo' B*<A C<B", function(a, b, c) { 215 is(a instanceof CodeMirror.Doc); 216 is(b instanceof CodeMirror.Doc); 217 is(c instanceof CodeMirror); 218 eqAll("foo", a, b, c); 219 a.replaceRange("hey", Pos(0, 0), Pos(0)); 220 c.replaceRange("!", Pos(0)); 221 eqAll("hey!", a, b, c); 222 b.unlinkDoc(a); 223 b.setValue("x"); 224 eqAll("x", b, c); 225 eqAll("hey!", a); 226 }); 227 228 testDoc("swapDoc", "A='a' B*='b' C<A", function(a, b, c) { 229 var d = a.swapDoc(b); 230 d.setValue("x"); 231 eqAll("x", c, d); 232 eqAll("b", a, b); 233 }); 234 235 testDoc("docKeepsScroll", "A='x' B*='y'", function(a, b) { 236 addDoc(a, 200, 200); 237 a.scrollIntoView(Pos(199, 200)); 238 var c = a.swapDoc(b); 239 a.swapDoc(c); 240 var pos = a.getScrollInfo(); 241 is(pos.left > 0, "not at left"); 242 is(pos.top > 0, "not at top"); 243 }); 244 245 testDoc("copyDoc", "A='u'", function(a) { 246 var copy = a.getDoc().copy(true); 247 a.setValue("foo"); 248 copy.setValue("bar"); 249 var old = a.swapDoc(copy); 250 eq(a.getValue(), "bar"); 251 a.undo(); 252 eq(a.getValue(), "u"); 253 a.swapDoc(old); 254 eq(a.getValue(), "foo"); 255 eq(old.historySize().undo, 1); 256 eq(old.copy(false).historySize().undo, 0); 257 }); 258 259 testDoc("docKeepsMode", "A='1+1'", function(a) { 260 var other = CodeMirror.Doc("hi", "text/x-markdown"); 261 a.setOption("mode", "text/javascript"); 262 var old = a.swapDoc(other); 263 eq(a.getOption("mode"), "text/x-markdown"); 264 eq(a.getMode().name, "markdown"); 265 a.swapDoc(old); 266 eq(a.getOption("mode"), "text/javascript"); 267 eq(a.getMode().name, "javascript"); 268 }); 269 270 testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { 271 eq(b.getValue(), "2\n3"); 272 eq(b.firstLine(), 1); 273 b.setCursor(Pos(4)); 274 eqCharPos(b.getCursor(), Pos(2, 1)); 275 a.replaceRange("-1\n0\n", Pos(0, 0)); 276 eq(b.firstLine(), 3); 277 eqCharPos(b.getCursor(), Pos(4, 1)); 278 a.undo(); 279 eqCharPos(b.getCursor(), Pos(2, 1)); 280 b.replaceRange("oyoy\n", Pos(2, 0)); 281 eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); 282 b.undo(); 283 eq(a.getValue(), "1\n2\n3\n4\n5"); 284 }); 285 286 testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { 287 a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); 288 eq(b.firstLine(), 2); 289 eq(b.lineCount(), 2); 290 eq(b.getValue(), "z3\n44"); 291 a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); 292 eq(b.firstLine(), 2); 293 eq(b.getValue(), "z3\n4q"); 294 eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); 295 a.execCommand("selectAll"); 296 a.replaceSelection("!"); 297 eqAll("!", a, b); 298 }); 299 300 301 testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) { 302 var mark = b.markText(Pos(0, 1), Pos(3, 1), 303 {className: "cm-searching", shared: true}); 304 var found = a.findMarksAt(Pos(0, 2)); 305 eq(found.length, 1); 306 eq(found[0], mark); 307 eq(c.findMarksAt(Pos(1, 1)).length, 1); 308 eqCharPos(mark.find().from, Pos(0, 1)); 309 eqCharPos(mark.find().to, Pos(3, 1)); 310 b.replaceRange("x\ny\n", Pos(0, 0)); 311 eqCharPos(mark.find().from, Pos(2, 1)); 312 eqCharPos(mark.find().to, Pos(5, 1)); 313 var cleared = 0; 314 CodeMirror.on(mark, "clear", function() {++cleared;}); 315 b.operation(function(){mark.clear();}); 316 eq(a.findMarksAt(Pos(3, 1)).length, 0); 317 eq(b.findMarksAt(Pos(3, 1)).length, 0); 318 eq(c.findMarksAt(Pos(3, 1)).length, 0); 319 eq(mark.find(), null); 320 eq(cleared, 1); 321 }); 322 323 testDoc("sharedMarkerCopy", "A='abcde'", function(a) { 324 var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true}); 325 var b = a.linkedDoc(); 326 var found = b.findMarksAt(Pos(0, 2)); 327 eq(found.length, 1); 328 eq(found[0], shared); 329 shared.clear(); 330 eq(b.findMarksAt(Pos(0, 2)), 0); 331 }); 332 333 testDoc("sharedMarkerDetach", "A='abcde' B<A C<B", function(a, b, c) { 334 var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true}); 335 a.unlinkDoc(b); 336 var inB = b.findMarksAt(Pos(0, 2)); 337 eq(inB.length, 1); 338 is(inB[0] != shared); 339 var inC = c.findMarksAt(Pos(0, 2)); 340 eq(inC.length, 1); 341 is(inC[0] != shared); 342 inC[0].clear(); 343 is(shared.find()); 344 }); 345 346 testDoc("sharedBookmark", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) { 347 var mark = b.setBookmark(Pos(1, 1), {shared: true}); 348 var found = a.findMarksAt(Pos(1, 1)); 349 eq(found.length, 1); 350 eq(found[0], mark); 351 eq(c.findMarksAt(Pos(1, 1)).length, 1); 352 eqCharPos(mark.find(), Pos(1, 1)); 353 b.replaceRange("x\ny\n", Pos(0, 0)); 354 eqCharPos(mark.find(), Pos(3, 1)); 355 var cleared = 0; 356 CodeMirror.on(mark, "clear", function() {++cleared;}); 357 b.operation(function() {mark.clear();}); 358 eq(a.findMarks(Pos(0, 0), Pos(5)).length, 0); 359 eq(b.findMarks(Pos(0, 0), Pos(5)).length, 0); 360 eq(c.findMarks(Pos(0, 0), Pos(5)).length, 0); 361 eq(mark.find(), null); 362 eq(cleared, 1); 363 }); 364 365 testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B<A/1-4", function(a, b) { 366 b.replaceRange("x", Pos(2, 0)); 367 a.undo(); 368 eq(a.getValue(), "line 0\nline 1\nline 2\nline 3\nline 4"); 369 eq(b.getValue(), "line 1\nline 2\nline 3"); 370 }); 371 })();