codemirror-rails 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  *.gem
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/README.md CHANGED
@@ -7,12 +7,15 @@ applications.
7
7
 
8
8
  If you're using Bundler, you can add codemirror-rails to your Gemfile:
9
9
 
10
- gem 'codemirror-rails'
10
+ ```ruby
11
+ gem 'codemirror-rails'
12
+ ```
11
13
 
12
14
  Or manually install the codemirror-rails gem:
13
15
 
14
- gem install codemirror-rails
15
-
16
+ ```shell
17
+ gem install codemirror-rails
18
+ ```
16
19
 
17
20
  ## CodeMirror for Rails 3.1
18
21
 
@@ -20,23 +23,31 @@ All of the assets from the most latest stable CodeMirror release are vendored
20
23
  so that you can use them with the asset pipeline. At a minimum, you will
21
24
  probably want the following in your application.js and application.css:
22
25
 
23
- //= require codemirror
26
+ ```js
27
+ //= require codemirror
28
+ ```
24
29
 
25
30
  ### Adding a mode
26
31
 
27
32
  Additional syntax modes can be added to your application.js:
28
33
 
29
- //= require codemirror/modes/ruby
34
+ ```js
35
+ //= require codemirror/modes/ruby
36
+ ```
30
37
 
31
38
  ### Adding a theme
32
39
 
33
40
  Additional CSS themes can be added to your application.css
34
41
 
35
- //= require codemirror/themes/night
42
+ ```js
43
+ //= require codemirror/themes/night
44
+ ```
36
45
 
37
46
  ## CodeMirror for Rails 3
38
47
 
39
48
  You can use the generator included with this gem to copy the CodeMirror 2
40
49
  assets into your Rails 3 public directory.
41
50
 
42
- rails generate codemirror:install
51
+ ```shell
52
+ rails generate codemirror:install
53
+ ```
@@ -10,4 +10,6 @@ Gem::Specification.new do |s|
10
10
  s.homepage = 'https://rubygems.org/gems/codemirror-rails'
11
11
 
12
12
  s.files = `git ls-files`.split("\n")
13
+
14
+ s.add_runtime_dependency 'railties', '~> 3.0'
13
15
  end
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '0.3.1'
4
- CODEMIRROR_VERSION = '2.16'
3
+ VERSION = '0.3.2'
4
+ CODEMIRROR_VERSION = '2.18'
5
5
  end
6
6
  end
@@ -1,3 +1,5 @@
1
+ // CodeMirror v2.18
2
+
1
3
  // All functions that need access to the editor's state live inside
2
4
  // the CodeMirror function. Below that, at the bottom of the file,
3
5
  // some utilities are defined.
@@ -16,7 +18,7 @@ var CodeMirror = (function() {
16
18
  var targetDocument = options["document"];
17
19
  // The element in which the editor lives.
18
20
  var wrapper = targetDocument.createElement("div");
19
- wrapper.className = "CodeMirror";
21
+ wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
20
22
  // This mess creates the base DOM structure for the editor.
21
23
  wrapper.innerHTML =
22
24
  '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
@@ -24,11 +26,11 @@ var CodeMirror = (function() {
24
26
  'autocorrect="off" autocapitalize="off"></textarea></div>' +
25
27
  '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
26
28
  '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27
- '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
28
29
  '<div style="position: relative">' + // Moved around its parent to cover visible view
29
30
  '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30
31
  // Provides positioning relative to (visible) text origin
31
- '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
32
+ '<div class="CodeMirror-lines"><div style="position: relative">' +
33
+ '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
32
34
  '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
33
35
  '<div></div>' + // This DIV contains the actual code
34
36
  '</div></div></div></div></div>';
@@ -36,10 +38,10 @@ var CodeMirror = (function() {
36
38
  // I've never seen more elegant code in my life.
37
39
  var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
38
40
  scroller = wrapper.lastChild, code = scroller.firstChild,
39
- measure = code.firstChild, mover = measure.nextSibling,
40
- gutter = mover.firstChild, gutterText = gutter.firstChild,
41
- lineSpace = gutter.nextSibling.firstChild,
42
- cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
41
+ mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
42
+ lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
43
+ cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
44
+ if (!webkit) lineSpace.draggable = true;
43
45
  if (options.tabindex != null) input.tabindex = options.tabindex;
44
46
  if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
45
47
 
@@ -55,11 +57,10 @@ var CodeMirror = (function() {
55
57
  // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
56
58
  var poll = new Delayed(), highlight = new Delayed(), blinker;
57
59
 
58
- // mode holds a mode API object. lines an array of Line objects
59
- // (see Line constructor), work an array of lines that should be
60
- // parsed, and history the undo history (instance of History
61
- // constructor).
62
- var mode, lines = [new Line("")], work, focused;
60
+ // mode holds a mode API object. doc is the tree of Line objects,
61
+ // work an array of lines that should be parsed, and history the
62
+ // undo history (instance of History constructor).
63
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
63
64
  loadMode();
64
65
  // The selection. These are always maintained to point at valid
65
66
  // positions. Inverted is used to remember that the user is
@@ -74,7 +75,7 @@ var CodeMirror = (function() {
74
75
  // happened during the operation.
75
76
  var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
76
77
  // Current visible range (may be bigger than the view window).
77
- var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
78
+ var displayOffset = 0, showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
78
79
  // editing will hold an object describing the things we put in the
79
80
  // textarea, to help figure out whether something changed.
80
81
  // bracketHighlighted is used to remember that a backet has been
@@ -88,10 +89,19 @@ var CodeMirror = (function() {
88
89
  operation(function(){setValue(options.value || ""); updateInput = false;})();
89
90
  var history = new History();
90
91
 
92
+ var slowPollInterval = 2000;
93
+ // Gecko and Opera Linux do not reliably fire any event when starting an IME compose
94
+ var alwaysPollForIME = (!win && !mac) && (gecko || window.opera);
95
+ if (options.pollForIME && alwaysPollForIME) slowPollInterval = 50;
96
+ function keyMightStartIME(keyCode) {
97
+ return (win && ((gecko && keyCode == 229) || (window.opera && keyCode == 197))) || (mac && gecko);
98
+ }
99
+
91
100
  // Register our event handlers.
92
101
  connect(scroller, "mousedown", operation(onMouseDown));
93
102
  connect(scroller, "dblclick", operation(onDoubleClick));
94
103
  connect(lineSpace, "dragstart", onDragStart);
104
+ connect(lineSpace, "selectstart", e_preventDefault);
95
105
  // Gecko browsers fire contextmenu *after* opening the menu, at
96
106
  // which point we can't mess with it anymore. Context menu is
97
107
  // handled in onMouseDown for Gecko.
@@ -122,7 +132,7 @@ var CodeMirror = (function() {
122
132
  if (hasFocus) setTimeout(onFocus, 20);
123
133
  else onBlur();
124
134
 
125
- function isLine(l) {return l >= 0 && l < lines.length;}
135
+ function isLine(l) {return l >= 0 && l < doc.size;}
126
136
  // The instance object that we'll return. Mostly calls out to
127
137
  // local functions in the CodeMirror function. Some do some extra
128
138
  // range checking and/or clipping. operation is used to wrap the
@@ -135,12 +145,15 @@ var CodeMirror = (function() {
135
145
  replaceSelection: operation(replaceSelection),
136
146
  focus: function(){focusInput(); onFocus(); fastPoll();},
137
147
  setOption: function(option, value) {
148
+ var oldVal = options[option];
138
149
  options[option] = value;
139
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
140
- operation(gutterChanged)();
141
- else if (option == "mode" || option == "indentUnit") loadMode();
150
+ if (option == "mode" || option == "indentUnit") loadMode();
142
151
  else if (option == "readOnly" && value == "nocursor") input.blur();
143
152
  else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
153
+ else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
154
+ else if (option == "pollForIME" && alwaysPollForIME) slowPollInterval = value ? 50 : 2000;
155
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
156
+ operation(gutterChanged)();
144
157
  },
145
158
  getOption: function(option) {return options[option];},
146
159
  undo: operation(undo),
@@ -151,12 +164,12 @@ var CodeMirror = (function() {
151
164
  historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
152
165
  clearHistory: function() {history = new History();},
153
166
  matchBrackets: operation(function(){matchBrackets(true);}),
154
- getTokenAt: function(pos) {
167
+ getTokenAt: operation(function(pos) {
155
168
  pos = clipPos(pos);
156
- return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
157
- },
169
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
170
+ }),
158
171
  getStateAfter: function(line) {
159
- line = clipLine(line == null ? lines.length - 1: line);
172
+ line = clipLine(line == null ? doc.size - 1: line);
160
173
  return getStateBefore(line + 1);
161
174
  },
162
175
  cursorCoords: function(start){
@@ -166,14 +179,16 @@ var CodeMirror = (function() {
166
179
  charCoords: function(pos){return pageCoords(clipPos(pos));},
167
180
  coordsChar: function(coords) {
168
181
  var off = eltOffset(lineSpace);
169
- var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
170
- return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
182
+ return coordsChar(coords.x - off.left, coords.y - off.top);
171
183
  },
172
184
  getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
173
185
  markText: operation(markText),
186
+ setBookmark: setBookmark,
174
187
  setMarker: operation(addGutterMarker),
175
188
  clearMarker: operation(removeGutterMarker),
176
189
  setLineClass: operation(setLineClass),
190
+ hideLine: operation(function(h) {return setLineHidden(h, true);}),
191
+ showLine: operation(function(h) {return setLineHidden(h, false);}),
177
192
  lineInfo: lineInfo,
178
193
  addWidget: function(pos, node, scroll, vert, horiz) {
179
194
  pos = localCoords(clipPos(pos));
@@ -182,7 +197,7 @@ var CodeMirror = (function() {
182
197
  code.appendChild(node);
183
198
  if (vert == "over") top = pos.y;
184
199
  else if (vert == "near") {
185
- var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
200
+ var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
186
201
  hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
187
202
  if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
188
203
  top = pos.y - node.offsetHeight;
@@ -203,7 +218,7 @@ var CodeMirror = (function() {
203
218
  scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
204
219
  },
205
220
 
206
- lineCount: function() {return lines.length;},
221
+ lineCount: function() {return doc.size;},
207
222
  getCursor: function(start) {
208
223
  if (start == null) start = sel.inverted;
209
224
  return copyPos(start ? sel.from : sel.to);
@@ -214,9 +229,9 @@ var CodeMirror = (function() {
214
229
  else setCursor(line, ch);
215
230
  }),
216
231
  setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
217
- getLine: function(line) {if (isLine(line)) return lines[line].text;},
232
+ getLine: function(line) {if (isLine(line)) return getLine(line).text;},
218
233
  setLine: operation(function(line, text) {
219
- if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length});
234
+ if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
220
235
  }),
221
236
  removeLine: operation(function(line) {
222
237
  if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
@@ -224,15 +239,15 @@ var CodeMirror = (function() {
224
239
  replaceRange: operation(replaceRange),
225
240
  getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
226
241
 
227
- coordsFromIndex: function(index) {
228
- var total = lines.length, pos = 0, line, ch, len;
229
-
230
- for (line = 0; line < total; line++) {
231
- len = lines[line].text.length + 1;
232
- if (pos + len > index) { ch = index - pos; break; }
233
- pos += len;
234
- }
235
- return clipPos({line: line, ch: ch});
242
+ coordsFromIndex: function(off) {
243
+ var lineNo = 0, ch;
244
+ doc.iter(0, doc.size, function(line) {
245
+ var sz = line.text.length + 1;
246
+ if (sz > off) { ch = off; return true; }
247
+ off -= sz;
248
+ ++lineNo;
249
+ });
250
+ return clipPos({line: lineNo, ch: ch});
236
251
  },
237
252
 
238
253
  operation: function(f){return operation(f)();},
@@ -243,16 +258,22 @@ var CodeMirror = (function() {
243
258
  getGutterElement: function(){return gutter;}
244
259
  };
245
260
 
261
+ function getLine(n) { return getLineAt(doc, n); }
262
+ function updateLineHeight(line, height) {
263
+ gutterDirty = true;
264
+ var diff = height - line.height;
265
+ for (var n = line; n; n = n.parent) n.height += diff;
266
+ }
267
+
246
268
  function setValue(code) {
247
269
  var top = {line: 0, ch: 0};
248
- updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
270
+ updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
249
271
  splitLines(code), top, top);
250
272
  updateInput = true;
251
273
  }
252
274
  function getValue(code) {
253
275
  var text = [];
254
- for (var i = 0, l = lines.length; i < l; ++i)
255
- text.push(lines[i].text);
276
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
256
277
  return text.join("\n");
257
278
  }
258
279
 
@@ -261,7 +282,7 @@ var CodeMirror = (function() {
261
282
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
262
283
  if (n.parentNode == code && n != mover) return;
263
284
 
264
- // First, see if this is a click in the gutter
285
+ // See if this is a click in the gutter
265
286
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
266
287
  if (n.parentNode == gutterText) {
267
288
  if (options.onGutterClick)
@@ -287,20 +308,23 @@ var CodeMirror = (function() {
287
308
  if (!focused) onFocus();
288
309
 
289
310
  var now = +new Date;
290
- if (lastDoubleClick > now - 400) {
311
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
291
312
  e_preventDefault(e);
313
+ setTimeout(focusInput, 20);
292
314
  return selectLine(start.line);
293
- } else if (lastClick > now - 400) {
294
- lastDoubleClick = now;
315
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
316
+ lastDoubleClick = {time: now, pos: start};
295
317
  e_preventDefault(e);
296
318
  return selectWordAt(start);
297
- } else { lastClick = now; }
319
+ } else { lastClick = {time: now, pos: start}; }
298
320
 
299
321
  var last = start, going;
300
322
  if (dragAndDrop && !posEq(sel.from, sel.to) &&
301
323
  !posLess(start, sel.from) && !posLess(sel.to, start)) {
302
324
  // Let the drag handler handle this.
325
+ if (webkit) lineSpace.draggable = true;
303
326
  var up = connect(targetDocument, "mouseup", operation(function(e2) {
327
+ if (webkit) lineSpace.draggable = false;
304
328
  draggingText = false;
305
329
  up();
306
330
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
@@ -344,9 +368,11 @@ var CodeMirror = (function() {
344
368
  }), true);
345
369
  }
346
370
  function onDoubleClick(e) {
371
+ for (var n = e_target(e); n != wrapper; n = n.parentNode)
372
+ if (n.parentNode == gutterText) return e_preventDefault(e);
347
373
  var start = posFromMouse(e);
348
374
  if (!start) return;
349
- lastDoubleClick = +new Date;
375
+ lastDoubleClick = {time: +new Date, pos: start};
350
376
  e_preventDefault(e);
351
377
  selectWordAt(start);
352
378
  }
@@ -361,8 +387,10 @@ var CodeMirror = (function() {
361
387
  text[i] = reader.result;
362
388
  if (++read == n) {
363
389
  pos = clipPos(pos);
364
- var end = replaceRange(text.join(""), pos, pos);
365
- setSelectionUser(pos, end);
390
+ operation(function() {
391
+ var end = replaceRange(text.join(""), pos, pos);
392
+ setSelectionUser(pos, end);
393
+ })();
366
394
  }
367
395
  };
368
396
  reader.readAsText(file);
@@ -435,6 +463,8 @@ var CodeMirror = (function() {
435
463
  // Don't save the key as a movementkey unless it had a modifier
436
464
  if (!mod && !e.altKey) curKeyId = null;
437
465
  fastPoll(curKeyId);
466
+
467
+ if (options.pollForIME && keyMightStartIME(code)) slowPollInterval = 50;
438
468
  }
439
469
  function onKeyUp(e) {
440
470
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
@@ -443,6 +473,8 @@ var CodeMirror = (function() {
443
473
  updateInput = true;
444
474
  }
445
475
  if (e.keyCode == 16) shiftSelecting = null;
476
+
477
+ if (slowPollInterval < 2000 && !alwaysPollForIME) slowPollInterval = 2000;
446
478
  }
447
479
  function onKeyPress(e) {
448
480
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
@@ -485,7 +517,7 @@ var CodeMirror = (function() {
485
517
  function updateLines(from, to, newText, selFrom, selTo) {
486
518
  if (history) {
487
519
  var old = [];
488
- for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text);
520
+ doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
489
521
  history.addChange(from.line, newText.length, old);
490
522
  while (history.done.length > options.undoDepth) history.done.shift();
491
523
  }
@@ -495,11 +527,11 @@ var CodeMirror = (function() {
495
527
  var change = from.pop();
496
528
  if (change) {
497
529
  var replaced = [], end = change.start + change.added;
498
- for (var i = change.start; i < end; ++i) replaced.push(lines[i].text);
530
+ doc.iter(change.start, end, function(line) { replaced.push(line.text); });
499
531
  to.push({start: change.start, added: change.old.length, old: replaced});
500
532
  var pos = clipPos({line: change.start + change.old.length - 1,
501
533
  ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
502
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
534
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
503
535
  updateInput = true;
504
536
  }
505
537
  }
@@ -508,55 +540,66 @@ var CodeMirror = (function() {
508
540
 
509
541
  function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
510
542
  var recomputeMaxLength = false, maxLineLength = maxLine.length;
511
- for (var i = from.line; i <= to.line; ++i) {
512
- if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
513
- }
543
+ if (!options.lineWrapping)
544
+ doc.iter(from.line, to.line, function(line) {
545
+ if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
546
+ });
514
547
 
515
- var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
548
+ var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
516
549
  // First adjust the line structure, taking some care to leave highlighting intact.
517
550
  if (firstLine == lastLine) {
518
551
  if (newText.length == 1)
519
552
  firstLine.replace(from.ch, to.ch, newText[0]);
520
553
  else {
521
554
  lastLine = firstLine.split(to.ch, newText[newText.length-1]);
522
- var spliceargs = [from.line + 1, nlines];
523
555
  firstLine.replace(from.ch, null, newText[0]);
556
+ firstLine.fixMarkEnds(lastLine);
557
+ var added = [];
524
558
  for (var i = 1, e = newText.length - 1; i < e; ++i)
525
- spliceargs.push(Line.inheritMarks(newText[i], firstLine));
526
- spliceargs.push(lastLine);
527
- lines.splice.apply(lines, spliceargs);
559
+ added.push(Line.inheritMarks(newText[i], firstLine));
560
+ added.push(lastLine);
561
+ doc.insert(from.line + 1, added);
528
562
  }
529
563
  }
530
564
  else if (newText.length == 1) {
531
565
  firstLine.replace(from.ch, null, newText[0]);
532
566
  lastLine.replace(null, to.ch, "");
533
567
  firstLine.append(lastLine);
534
- lines.splice(from.line + 1, nlines);
568
+ doc.remove(from.line + 1, nlines);
535
569
  }
536
570
  else {
537
- var spliceargs = [from.line + 1, nlines - 1];
571
+ var added = [];
538
572
  firstLine.replace(from.ch, null, newText[0]);
539
573
  lastLine.replace(null, to.ch, newText[newText.length-1]);
574
+ firstLine.fixMarkEnds(lastLine);
540
575
  for (var i = 1, e = newText.length - 1; i < e; ++i)
541
- spliceargs.push(Line.inheritMarks(newText[i], firstLine));
542
- lines.splice.apply(lines, spliceargs);
543
- }
544
-
545
-
546
- for (var i = from.line, e = i + newText.length; i < e; ++i) {
547
- var l = lines[i].text;
548
- if (l.length > maxLineLength) {
549
- maxLine = l; maxLineLength = l.length; maxWidth = null;
550
- recomputeMaxLength = false;
551
- }
552
- }
553
- if (recomputeMaxLength) {
554
- maxLineLength = 0; maxLine = ""; maxWidth = null;
555
- for (var i = 0, e = lines.length; i < e; ++i) {
556
- var l = lines[i].text;
576
+ added.push(Line.inheritMarks(newText[i], firstLine));
577
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
578
+ doc.insert(from.line + 1, added);
579
+ }
580
+ if (options.lineWrapping) {
581
+ var perLine = scroller.clientWidth / charWidth() - 3;
582
+ doc.iter(from.line, from.line + newText.length, function(line) {
583
+ if (line.hidden) return;
584
+ var guess = Math.ceil(line.text.length / perLine) || 1;
585
+ if (guess != line.height) updateLineHeight(line, guess);
586
+ });
587
+ } else {
588
+ doc.iter(from.line, i + newText.length, function(line) {
589
+ var l = line.text;
557
590
  if (l.length > maxLineLength) {
558
- maxLineLength = l.length; maxLine = l;
591
+ maxLine = l; maxLineLength = l.length; maxWidth = null;
592
+ recomputeMaxLength = false;
559
593
  }
594
+ });
595
+ if (recomputeMaxLength) {
596
+ maxLineLength = 0; maxLine = ""; maxWidth = null;
597
+ doc.iter(0, doc.size, function(line) {
598
+ var l = line.text;
599
+ if (l.length > maxLineLength) {
600
+ maxLineLength = l.length; maxLine = l;
601
+ }
602
+ });
560
603
  }
561
604
  }
562
605
 
@@ -568,12 +611,9 @@ var CodeMirror = (function() {
568
611
  if (task < from.line) newWork.push(task);
569
612
  else if (task > to.line) newWork.push(task + lendiff);
570
613
  }
571
- if (newText.length < 5) {
572
- highlightLines(from.line, from.line + newText.length);
573
- newWork.push(from.line + newText.length);
574
- } else {
575
- newWork.push(from.line);
576
- }
614
+ var hlEnd = from.line + Math.min(newText.length, 500);
615
+ highlightLines(from.line, hlEnd);
616
+ newWork.push(hlEnd);
577
617
  work = newWork;
578
618
  startWorker(100);
579
619
  // Remember that these lines changed, for updating the display
@@ -585,7 +625,7 @@ var CodeMirror = (function() {
585
625
  setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
586
626
 
587
627
  // Make sure the scroll-size div has the correct height.
588
- code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
628
+ code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
589
629
  }
590
630
 
591
631
  function replaceRange(code, from, to) {
@@ -623,10 +663,10 @@ var CodeMirror = (function() {
623
663
 
624
664
  function getRange(from, to) {
625
665
  var l1 = from.line, l2 = to.line;
626
- if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch);
627
- var code = [lines[l1].text.slice(from.ch)];
628
- for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text);
629
- code.push(lines[l2].text.slice(0, to.ch));
666
+ if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
667
+ var code = [getLine(l1).text.slice(from.ch)];
668
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
669
+ code.push(getLine(l2).text.slice(0, to.ch));
630
670
  return code.join("\n");
631
671
  }
632
672
  function getSelection() {
@@ -636,7 +676,7 @@ var CodeMirror = (function() {
636
676
  var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
637
677
  function slowPoll() {
638
678
  if (pollingFast) return;
639
- poll.set(2000, function() {
679
+ poll.set(slowPollInterval, function() {
640
680
  startOperation();
641
681
  readInput();
642
682
  if (focused) slowPoll();
@@ -734,14 +774,16 @@ var CodeMirror = (function() {
734
774
  // editor state.
735
775
  function prepareInput() {
736
776
  var text = [];
737
- var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2);
738
- for (var i = from; i < to; ++i) text.push(lines[i].text);
777
+ var from = Math.max(0, sel.from.line - 1), to = Math.min(doc.size, sel.to.line + 2);
778
+ doc.iter(from, to, function(line) { text.push(line.text); });
739
779
  text = input.value = text.join(lineSep);
740
780
  var startch = sel.from.ch, endch = sel.to.ch;
741
- for (var i = from; i < sel.from.line; ++i)
742
- startch += lineSep.length + lines[i].text.length;
743
- for (var i = from; i < sel.to.line; ++i)
744
- endch += lineSep.length + lines[i].text.length;
781
+ doc.iter(from, sel.from.line, function(line) {
782
+ startch += lineSep.length + line.text.length;
783
+ });
784
+ doc.iter(from, sel.to.line, function(line) {
785
+ endch += lineSep.length + line.text.length;
786
+ });
745
787
  editing = {text: text, from: from, to: to, start: startch, end: endch};
746
788
  setSelRange(input, startch, reducedSelection ? startch : endch);
747
789
  }
@@ -757,10 +799,11 @@ var CodeMirror = (function() {
757
799
  }
758
800
  function scrollCursorIntoView() {
759
801
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
760
- return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
802
+ var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
803
+ return scrollIntoView(x, cursor.y, x, cursor.yBot);
761
804
  }
762
805
  function scrollIntoView(x1, y1, x2, y2) {
763
- var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
806
+ var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
764
807
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
765
808
  var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
766
809
  if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
@@ -783,28 +826,99 @@ var CodeMirror = (function() {
783
826
  }
784
827
 
785
828
  function visibleLines() {
786
- var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
787
- return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
788
- to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
829
+ var lh = textHeight(), top = scroller.scrollTop - paddingTop();
830
+ var from_height = Math.max(0, Math.floor(top / lh));
831
+ var to_height = Math.ceil((top + scroller.clientHeight) / lh);
832
+ return {from: lineAtHeight(doc, from_height),
833
+ to: lineAtHeight(doc, to_height)};
789
834
  }
790
835
  // Uses a set of changes plus the current scroll position to
791
836
  // determine which DOM updates have to be made, and makes the
792
837
  // updates.
793
838
  function updateDisplay(changes) {
794
839
  if (!scroller.clientWidth) {
795
- showingFrom = showingTo = 0;
840
+ showingFrom = showingTo = displayOffset = 0;
796
841
  return;
797
842
  }
798
- // First create a range of theoretically intact lines, and punch
799
- // holes in that using the change info.
800
- var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
843
+ // Compute the new visible window
844
+ var visible = visibleLines();
845
+ // Bail out if the visible area is already rendered and nothing changed.
846
+ if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
847
+ var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
848
+ if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
849
+ if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
850
+
851
+ // Create a range of theoretically intact lines, and punch holes
852
+ // in that using the change info.
853
+ var intact = changes === true ? [] :
854
+ computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
855
+ // Clip off the parts that won't be visible
856
+ var intactLines = 0;
857
+ for (var i = 0; i < intact.length; ++i) {
858
+ var range = intact[i];
859
+ if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
860
+ if (range.to > to) range.to = to;
861
+ if (range.from >= range.to) intact.splice(i--, 1);
862
+ else intactLines += range.to - range.from;
863
+ }
864
+ if (intactLines == to - from) return;
865
+ intact.sort(function(a, b) {return a.domStart - b.domStart;});
866
+
867
+ var th = textHeight(), gutterDisplay = gutter.style.display;
868
+ lineDiv.style.display = gutter.style.display = "none";
869
+ patchDisplay(from, to, intact);
870
+ lineDiv.style.display = "";
871
+
872
+ // Position the mover div to align with the lines it's supposed
873
+ // to be showing (which will cover the visible display)
874
+ var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
875
+ if (different) lastHeight = scroller.clientHeight;
876
+ showingFrom = from; showingTo = to;
877
+ displayOffset = heightAtLine(doc, from);
878
+ mover.style.top = (displayOffset * th) + "px";
879
+ code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
880
+
881
+ // Since this is all rather error prone, it is honoured with the
882
+ // only assertion in the whole file.
883
+ if (lineDiv.childNodes.length != showingTo - showingFrom)
884
+ throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
885
+ " nodes=" + lineDiv.childNodes.length);
886
+
887
+ if (options.lineWrapping) {
888
+ maxWidth = scroller.clientWidth;
889
+ var curNode = lineDiv.firstChild;
890
+ doc.iter(showingFrom, showingTo, function(line) {
891
+ if (!line.hidden) {
892
+ var height = Math.round(curNode.offsetHeight / th) || 1;
893
+ if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
894
+ }
895
+ curNode = curNode.nextSibling;
896
+ });
897
+ } else {
898
+ if (maxWidth == null) maxWidth = stringWidth(maxLine);
899
+ if (maxWidth > scroller.clientWidth) {
900
+ lineSpace.style.width = maxWidth + "px";
901
+ // Needed to prevent odd wrapping/hiding of widgets placed in here.
902
+ code.style.width = "";
903
+ code.style.width = scroller.scrollWidth + "px";
904
+ } else {
905
+ lineSpace.style.width = code.style.width = "";
906
+ }
907
+ }
908
+ gutter.style.display = gutterDisplay;
909
+ if (different || gutterDirty) updateGutter();
910
+ updateCursor();
911
+ }
912
+
913
+ function computeIntact(intact, changes) {
801
914
  for (var i = 0, l = changes.length || 0; i < l; ++i) {
802
915
  var change = changes[i], intact2 = [], diff = change.diff || 0;
803
916
  for (var j = 0, l2 = intact.length; j < l2; ++j) {
804
917
  var range = intact[j];
805
- if (change.to <= range.from)
806
- intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart});
807
- else if (range.to <= change.from)
918
+ if (change.to <= range.from && change.diff)
919
+ intact2.push({from: range.from + diff, to: range.to + diff,
920
+ domStart: range.domStart});
921
+ else if (change.to <= range.from || change.from >= range.to)
808
922
  intact2.push(range);
809
923
  else {
810
924
  if (change.from > range.from)
@@ -816,148 +930,75 @@ var CodeMirror = (function() {
816
930
  }
817
931
  intact = intact2;
818
932
  }
933
+ return intact;
934
+ }
819
935
 
820
- // Then, determine which lines we'd want to see, and which
821
- // updates have to be made to get there.
822
- var visible = visibleLines();
823
- var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)),
824
- to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)),
825
- updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0;
826
-
827
- for (var i = 0, l = intact.length; i < l; ++i) {
828
- var range = intact[i];
829
- if (range.to <= from) continue;
830
- if (range.from >= to) break;
831
- if (range.domStart > domPos || range.from > pos) {
832
- updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
833
- changedLines += range.from - pos;
936
+ function patchDisplay(from, to, intact) {
937
+ // The first pass removes the DOM nodes that aren't intact.
938
+ if (!intact.length) lineDiv.innerHTML = "";
939
+ else {
940
+ function killNode(node) {
941
+ var tmp = node.nextSibling;
942
+ node.parentNode.removeChild(node);
943
+ return tmp;
834
944
  }
835
- pos = range.to;
836
- domPos = range.domStart + (range.to - range.from);
837
- }
838
- if (domPos != domEnd || pos != to) {
839
- changedLines += Math.abs(to - pos);
840
- updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
841
- if (to - pos != domEnd - domPos) gutterDirty = true;
842
- }
843
-
844
- if (!updates.length) return;
845
- lineDiv.style.display = "none";
846
- // If more than 30% of the screen needs update, just do a full
847
- // redraw (which is quicker than patching)
848
- if (changedLines > (visible.to - visible.from) * .3)
849
- refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
850
- // Otherwise, only update the stuff that needs updating.
851
- else
852
- patchDisplay(updates);
853
- lineDiv.style.display = "";
854
-
855
- // Position the mover div to align with the lines it's supposed
856
- // to be showing (which will cover the visible display)
857
- var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
858
- showingFrom = from; showingTo = to;
859
- mover.style.top = (from * lineHeight()) + "px";
860
- if (different) {
861
- lastHeight = scroller.clientHeight;
862
- code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
863
- }
864
- if (different || gutterDirty) updateGutter();
865
-
866
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
867
- if (maxWidth > scroller.clientWidth) {
868
- lineSpace.style.width = maxWidth + "px";
869
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
870
- code.style.width = "";
871
- code.style.width = scroller.scrollWidth + "px";
872
- } else {
873
- lineSpace.style.width = code.style.width = "";
945
+ var domPos = 0, curNode = lineDiv.firstChild, n;
946
+ for (var i = 0; i < intact.length; ++i) {
947
+ var cur = intact[i];
948
+ while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
949
+ for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
950
+ }
951
+ while (curNode) curNode = killNode(curNode);
874
952
  }
875
-
876
- // Since this is all rather error prone, it is honoured with the
877
- // only assertion in the whole file.
878
- if (lineDiv.childNodes.length != showingTo - showingFrom)
879
- throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
880
- " nodes=" + lineDiv.childNodes.length);
881
- updateCursor();
882
- }
883
-
884
- function refreshDisplay(from, to) {
885
- var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
886
- for (var i = from; i < to; ++i) {
953
+ // This pass fills in the lines that actually changed.
954
+ var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
955
+ var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
956
+ var scratch = targetDocument.createElement("div"), newElt;
957
+ doc.iter(from, to, function(line) {
887
958
  var ch1 = null, ch2 = null;
888
959
  if (inSel) {
889
960
  ch1 = 0;
890
- if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;}
891
- }
892
- else if (sel.from.line == i) {
893
- if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
961
+ if (sto == j) {inSel = false; ch2 = sel.to.ch;}
962
+ } else if (sfrom == j) {
963
+ if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
894
964
  else {inSel = true; ch1 = sel.from.ch;}
895
965
  }
896
- html.push(lines[i].getHTML(ch1, ch2, true));
897
- }
898
- lineDiv.innerHTML = html.join("");
899
- }
900
- function patchDisplay(updates) {
901
- // Slightly different algorithm for IE (badInnerHTML), since
902
- // there .innerHTML on PRE nodes is dumb, and discards
903
- // whitespace.
904
- var sfrom = sel.from.line, sto = sel.to.line, off = 0,
905
- scratch = badInnerHTML && targetDocument.createElement("div");
906
- for (var i = 0, e = updates.length; i < e; ++i) {
907
- var rec = updates[i];
908
- var extra = (rec.to - rec.from) - rec.domSize;
909
- var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
910
- if (badInnerHTML)
911
- for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
912
- lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
913
- else if (extra) {
914
- for (var j = Math.max(0, extra); j > 0; --j)
915
- lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
916
- for (var j = Math.max(0, -extra); j > 0; --j)
917
- lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
918
- }
919
- var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
920
- for (var j = rec.from; j < rec.to; ++j) {
921
- var ch1 = null, ch2 = null;
922
- if (inSel) {
923
- ch1 = 0;
924
- if (sto == j) {inSel = false; ch2 = sel.to.ch;}
925
- }
926
- else if (sfrom == j) {
927
- if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
928
- else {inSel = true; ch1 = sel.from.ch;}
929
- }
930
- if (badInnerHTML) {
931
- scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
932
- lineDiv.insertBefore(scratch.firstChild, nodeAfter);
933
- }
934
- else {
935
- node.innerHTML = lines[j].getHTML(ch1, ch2, false);
936
- node.className = lines[j].className || "";
937
- node = node.nextSibling;
938
- }
966
+ if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
967
+ if (!nextIntact || nextIntact.from > j) {
968
+ if (line.hidden) scratch.innerHTML = "<pre></pre>";
969
+ else scratch.innerHTML = line.getHTML(ch1, ch2, true);
970
+ lineDiv.insertBefore(scratch.firstChild, curNode);
971
+ } else {
972
+ curNode = curNode.nextSibling;
939
973
  }
940
- off += extra;
941
- }
974
+ ++j;
975
+ });
942
976
  }
943
977
 
944
978
  function updateGutter() {
945
979
  if (!options.gutter && !options.lineNumbers) return;
946
980
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
947
981
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
948
- var html = [];
949
- for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
950
- var marker = lines[i].gutterMarker;
951
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
952
- if (marker && marker.text)
953
- text = marker.text.replace("%N%", text != null ? text : "");
954
- else if (text == null)
955
- text = "\u00a0";
956
- html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text, "</pre>");
957
- }
982
+ var html = [], i = showingFrom;
983
+ doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
984
+ if (line.hidden) {
985
+ html.push("<pre></pre>");
986
+ } else {
987
+ var marker = line.gutterMarker;
988
+ var text = options.lineNumbers ? i + options.firstLineNumber : null;
989
+ if (marker && marker.text)
990
+ text = marker.text.replace("%N%", text != null ? text : "");
991
+ else if (text == null)
992
+ text = "\u00a0";
993
+ html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
994
+ for (var j = 1; j < line.height; ++j) html.push("<br>&nbsp;");
995
+ html.push("</pre>");
996
+ }
997
+ ++i;
998
+ });
958
999
  gutter.style.display = "none";
959
1000
  gutterText.innerHTML = html.join("");
960
- var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
1001
+ var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
961
1002
  while (val.length + pad.length < minwidth) pad += "\u00a0";
962
1003
  if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
963
1004
  gutter.style.display = "";
@@ -965,14 +1006,14 @@ var CodeMirror = (function() {
965
1006
  gutterDirty = false;
966
1007
  }
967
1008
  function updateCursor() {
968
- var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
969
- var x = charX(head.line, head.ch);
970
- var top = head.line * lh - scroller.scrollTop;
971
- inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
972
- inputDiv.style.left = (x - scroller.scrollLeft) + "px";
1009
+ var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
1010
+ var pos = localCoords(head, true);
1011
+ var globalY = pos.y + displayOffset * textHeight();
1012
+ inputDiv.style.top = Math.max(Math.min(globalY, scroller.offsetHeight), 0) + "px";
1013
+ inputDiv.style.left = (pos.x - scroller.scrollLeft) + "px";
973
1014
  if (posEq(sel.from, sel.to)) {
974
- cursor.style.top = (head.line - showingFrom) * lh + "px";
975
- cursor.style.left = x + "px";
1015
+ cursor.style.top = pos.y + "px";
1016
+ cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
976
1017
  cursor.style.display = "";
977
1018
  }
978
1019
  else cursor.style.display = "none";
@@ -990,9 +1031,14 @@ var CodeMirror = (function() {
990
1031
  // updateLines, since they have to be expressed in the line
991
1032
  // numbers before the update.
992
1033
  function setSelection(from, to, oldFrom, oldTo) {
1034
+ if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
993
1035
  if (posEq(sel.from, from) && posEq(sel.to, to)) return;
994
1036
  if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
995
1037
 
1038
+ // Skip over hidden lines.
1039
+ if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
1040
+ if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1041
+
996
1042
  if (posEq(from, to)) sel.inverted = false;
997
1043
  else if (posEq(from, sel.to)) sel.inverted = false;
998
1044
  else if (posEq(to, sel.from)) sel.inverted = true;
@@ -1000,7 +1046,6 @@ var CodeMirror = (function() {
1000
1046
  // Some ugly logic used to only mark the lines that actually did
1001
1047
  // see a change in selection as changed, rather than the whole
1002
1048
  // selected range.
1003
- if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
1004
1049
  if (posEq(from, to)) {
1005
1050
  if (!posEq(sel.from, sel.to))
1006
1051
  changes.push({from: oldFrom, to: oldTo + 1});
@@ -1025,42 +1070,61 @@ var CodeMirror = (function() {
1025
1070
  sel.from = from; sel.to = to;
1026
1071
  selectionChanged = true;
1027
1072
  }
1073
+ function skipHidden(pos, oldLine, oldCh) {
1074
+ function getNonHidden(dir) {
1075
+ var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
1076
+ while (lNo != end) {
1077
+ var line = getLine(lNo);
1078
+ if (!line.hidden) {
1079
+ var ch = pos.ch;
1080
+ if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1081
+ return {line: lNo, ch: ch};
1082
+ }
1083
+ lNo += dir;
1084
+ }
1085
+ }
1086
+ var line = getLine(pos.line);
1087
+ if (!line.hidden) return pos;
1088
+ if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1089
+ else return getNonHidden(-1) || getNonHidden(1);
1090
+ }
1028
1091
  function setCursor(line, ch, user) {
1029
1092
  var pos = clipPos({line: line, ch: ch || 0});
1030
1093
  (user ? setSelectionUser : setSelection)(pos, pos);
1031
1094
  }
1032
1095
 
1033
- function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
1096
+ function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
1034
1097
  function clipPos(pos) {
1035
1098
  if (pos.line < 0) return {line: 0, ch: 0};
1036
- if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length};
1037
- var ch = pos.ch, linelen = lines[pos.line].text.length;
1099
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
1100
+ var ch = pos.ch, linelen = getLine(pos.line).text.length;
1038
1101
  if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1039
1102
  else if (ch < 0) return {line: pos.line, ch: 0};
1040
1103
  else return pos;
1041
1104
  }
1042
1105
 
1043
1106
  function scrollPage(down) {
1044
- var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
1045
- setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
1107
+ var linesPerPage = Math.floor(scroller.clientHeight / textHeight()), head = sel.inverted ? sel.from : sel.to;
1108
+ var target = heightAtLine(doc, head.line) + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1));
1109
+ setCursor(lineAtHeight(doc, target), head.ch, true);
1046
1110
  }
1047
1111
  function scrollEnd(top) {
1048
- var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
1112
+ var pos = top ? {line: 0, ch: 0} : {line: doc.size - 1, ch: getLine(doc.size-1).text.length};
1049
1113
  setSelectionUser(pos, pos);
1050
1114
  }
1051
1115
  function selectAll() {
1052
- var endLine = lines.length - 1;
1053
- setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length});
1116
+ var endLine = doc.size - 1;
1117
+ setSelection({line: 0, ch: 0}, {line: endLine, ch: getLine(endLine).text.length});
1054
1118
  }
1055
1119
  function selectWordAt(pos) {
1056
- var line = lines[pos.line].text;
1120
+ var line = getLine(pos.line).text;
1057
1121
  var start = pos.ch, end = pos.ch;
1058
1122
  while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
1059
1123
  while (end < line.length && /\w/.test(line.charAt(end))) ++end;
1060
1124
  setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1061
1125
  }
1062
1126
  function selectLine(line) {
1063
- setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
1127
+ setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
1064
1128
  }
1065
1129
  function handleEnter() {
1066
1130
  replaceSelection("\n", "end");
@@ -1093,7 +1157,7 @@ var CodeMirror = (function() {
1093
1157
  return true;
1094
1158
  }
1095
1159
  function smartHome() {
1096
- var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
1160
+ var firstNonWS = Math.max(0, getLine(sel.from.line).text.search(/\S/));
1097
1161
  setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
1098
1162
  }
1099
1163
 
@@ -1103,9 +1167,9 @@ var CodeMirror = (function() {
1103
1167
  else var state = getStateBefore(n);
1104
1168
  }
1105
1169
 
1106
- var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
1170
+ var line = getLine(n), curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
1107
1171
  if (how == "prev") {
1108
- if (n) indentation = lines[n-1].indentation();
1172
+ if (n) indentation = getLine(n-1).indentation();
1109
1173
  else indentation = 0;
1110
1174
  }
1111
1175
  else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
@@ -1130,8 +1194,7 @@ var CodeMirror = (function() {
1130
1194
 
1131
1195
  function loadMode() {
1132
1196
  mode = CodeMirror.getMode(options, options.mode);
1133
- for (var i = 0, l = lines.length; i < l; ++i)
1134
- lines[i].stateAfter = null;
1197
+ doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1135
1198
  work = [0];
1136
1199
  startWorker();
1137
1200
  }
@@ -1141,35 +1204,38 @@ var CodeMirror = (function() {
1141
1204
  if (visible) gutterDirty = true;
1142
1205
  else lineDiv.parentNode.style.marginLeft = 0;
1143
1206
  }
1144
-
1145
- function markText(from, to, className) {
1146
- from = clipPos(from); to = clipPos(to);
1147
- var set = [];
1148
- function add(line, from, to, className) {
1149
- mark = lines[line].addMark(from, to, className, set);
1150
- }
1151
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1152
- else {
1153
- add(from.line, from.ch, null, className);
1154
- for (var i = from.line + 1, e = to.line; i < e; ++i)
1155
- add(i, 0, null, className);
1156
- add(to.line, 0, to.ch, className);
1207
+ function wrappingChanged(from, to) {
1208
+ if (options.lineWrapping) {
1209
+ wrapper.className += " CodeMirror-wrap";
1210
+ var perLine = scroller.clientWidth / charWidth() - 3;
1211
+ doc.iter(0, doc.size, function(line) {
1212
+ if (line.hidden) return;
1213
+ var guess = Math.ceil(line.text.length / perLine) || 1;
1214
+ if (guess != 1) updateLineHeight(line, guess);
1215
+ });
1216
+ lineSpace.style.width = code.style.width = "";
1217
+ } else {
1218
+ wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1219
+ maxWidth = null; maxLine = "";
1220
+ doc.iter(0, doc.size, function(line) {
1221
+ if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1222
+ if (line.text.length > maxLine.length) maxLine = line.text;
1223
+ });
1157
1224
  }
1158
- changes.push({from: from.line, to: to.line + 1});
1159
- return new TextMarker(set);
1225
+ changes.push({from: 0, to: doc.size});
1160
1226
  }
1161
1227
 
1162
- function TextMarker(set) { this.set = set; }
1228
+ function TextMarker() { this.set = []; }
1163
1229
  TextMarker.prototype.clear = operation(function() {
1164
1230
  for (var i = 0, e = this.set.length; i < e; ++i) {
1165
1231
  var mk = this.set[i].marked;
1166
- for (var j = 0; j < mk.length; ++j) {
1232
+ if (!mk) continue;
1233
+ for (var j = 0; j < mk.length; ++j)
1167
1234
  if (mk[j].set == this.set) mk.splice(j--, 1);
1168
- }
1169
1235
  }
1170
1236
  // We don't know the exact lines that changed. Refreshing is
1171
1237
  // cheaper than finding them.
1172
- changes.push({from: 0, to: lines.length});
1238
+ changes.push({from: 0, to: doc.size});
1173
1239
  });
1174
1240
  TextMarker.prototype.find = function() {
1175
1241
  var from, to;
@@ -1179,8 +1245,8 @@ var CodeMirror = (function() {
1179
1245
  var mark = mk[j];
1180
1246
  if (mark.set == this.set) {
1181
1247
  if (mark.from != null || mark.to != null) {
1182
- var found = indexOf(lines, line);
1183
- if (found > -1) {
1248
+ var found = lineNo(line);
1249
+ if (found != null) {
1184
1250
  if (mark.from != null) from = {line: found, ch: mark.from};
1185
1251
  if (mark.to != null) to = {line: found, ch: mark.to};
1186
1252
  }
@@ -1191,45 +1257,85 @@ var CodeMirror = (function() {
1191
1257
  return {from: from, to: to};
1192
1258
  };
1193
1259
 
1260
+ function markText(from, to, className) {
1261
+ from = clipPos(from); to = clipPos(to);
1262
+ var tm = new TextMarker();
1263
+ function add(line, from, to, className) {
1264
+ mark = getLine(line).addMark(new MarkedText(from, to, className, tm.set));
1265
+ }
1266
+ if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1267
+ else {
1268
+ add(from.line, from.ch, null, className);
1269
+ for (var i = from.line + 1, e = to.line; i < e; ++i)
1270
+ add(i, null, null, className);
1271
+ add(to.line, null, to.ch, className);
1272
+ }
1273
+ changes.push({from: from.line, to: to.line + 1});
1274
+ return tm;
1275
+ }
1276
+
1277
+ function setBookmark(pos) {
1278
+ pos = clipPos(pos);
1279
+ var bm = new Bookmark(pos.ch);
1280
+ getLine(pos.line).addMark(bm);
1281
+ return bm;
1282
+ }
1283
+
1194
1284
  function addGutterMarker(line, text, className) {
1195
- if (typeof line == "number") line = lines[clipLine(line)];
1285
+ if (typeof line == "number") line = getLine(clipLine(line));
1196
1286
  line.gutterMarker = {text: text, style: className};
1197
1287
  gutterDirty = true;
1198
1288
  return line;
1199
1289
  }
1200
1290
  function removeGutterMarker(line) {
1201
- if (typeof line == "number") line = lines[clipLine(line)];
1291
+ if (typeof line == "number") line = getLine(clipLine(line));
1202
1292
  line.gutterMarker = null;
1203
1293
  gutterDirty = true;
1204
1294
  }
1205
- function setLineClass(line, className) {
1206
- if (typeof line == "number") {
1207
- var no = line;
1208
- line = lines[clipLine(line)];
1209
- }
1210
- else {
1211
- var no = indexOf(lines, line);
1212
- if (no == -1) return null;
1213
- }
1214
- if (line.className != className) {
1215
- line.className = className;
1216
- changes.push({from: no, to: no + 1});
1217
- }
1295
+
1296
+ function changeLine(handle, op) {
1297
+ var no = handle, line = handle;
1298
+ if (typeof handle == "number") line = getLine(clipLine(handle));
1299
+ else no = lineNo(handle);
1300
+ if (no == null) return null;
1301
+ if (op(line, no)) changes.push({from: no, to: no + 1});
1218
1302
  return line;
1219
1303
  }
1304
+ function setLineClass(handle, className) {
1305
+ return changeLine(handle, function(line) {
1306
+ if (line.className != className) {
1307
+ line.className = className;
1308
+ return true;
1309
+ }
1310
+ });
1311
+ }
1312
+ function setLineHidden(handle, hidden) {
1313
+ return changeLine(handle, function(line, no) {
1314
+ if (line.hidden != hidden) {
1315
+ line.hidden = hidden;
1316
+ updateLineHeight(line, hidden ? 0 : 1);
1317
+ if (hidden && (sel.from.line == no || sel.to.line == no))
1318
+ setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
1319
+ skipHidden(sel.to, sel.to.line, sel.to.ch));
1320
+ return (gutterDirty = true);
1321
+ }
1322
+ });
1323
+ }
1220
1324
 
1221
1325
  function lineInfo(line) {
1222
1326
  if (typeof line == "number") {
1327
+ if (!isLine(line)) return null;
1223
1328
  var n = line;
1224
- line = lines[line];
1329
+ line = getLine(line);
1225
1330
  if (!line) return null;
1226
1331
  }
1227
1332
  else {
1228
- var n = indexOf(lines, line);
1229
- if (n == -1) return null;
1333
+ var n = lineNo(line);
1334
+ if (n == null) return null;
1230
1335
  }
1231
1336
  var marker = line.gutterMarker;
1232
- return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1337
+ return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1338
+ markerClass: marker && marker.style, lineClass: line.className};
1233
1339
  }
1234
1340
 
1235
1341
  function stringWidth(str) {
@@ -1239,21 +1345,16 @@ var CodeMirror = (function() {
1239
1345
  }
1240
1346
  // These are used to go from pixel positions to character
1241
1347
  // positions, taking varying character widths into account.
1242
- function charX(line, pos) {
1243
- if (pos == 0) return 0;
1244
- measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
1245
- return measure.firstChild.firstChild.offsetWidth;
1246
- }
1247
1348
  function charFromX(line, x) {
1248
1349
  if (x <= 0) return 0;
1249
- var lineObj = lines[line], text = lineObj.text;
1350
+ var lineObj = getLine(line), text = lineObj.text;
1250
1351
  function getX(len) {
1251
1352
  measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
1252
1353
  return measure.firstChild.firstChild.offsetWidth;
1253
1354
  }
1254
1355
  var from = 0, fromX = 0, to = text.length, toX;
1255
1356
  // Guess a suitable upper bound for our search.
1256
- var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
1357
+ var estimated = Math.min(to, Math.ceil(x / charWidth()));
1257
1358
  for (;;) {
1258
1359
  var estX = getX(estimated);
1259
1360
  if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
@@ -1272,20 +1373,93 @@ var CodeMirror = (function() {
1272
1373
  }
1273
1374
  }
1274
1375
 
1376
+ var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1377
+ function measureLine(line, ch) {
1378
+ var extra = "";
1379
+ // Include extra text at the end to make sure the measured line is wrapped in the right way.
1380
+ if (options.lineWrapping) {
1381
+ var end = line.text.indexOf(" ", ch + 2);
1382
+ extra = line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0));
1383
+ }
1384
+ measure.innerHTML = "<pre>" + line.getHTML(null, null, false, ch) +
1385
+ '<span id="CodeMirror-temp-' + tempId + '">' + (line.text.charAt(ch) || " ") + "</span>" +
1386
+ extra + "</pre>";
1387
+ var elt = document.getElementById("CodeMirror-temp-" + tempId);
1388
+ var top = elt.offsetTop, left = elt.offsetLeft;
1389
+ // Older IEs report zero offsets for spans directly after a wrap
1390
+ if (ie && ch && top == 0 && left == 0) {
1391
+ var backup = document.createElement("span");
1392
+ backup.innerHTML = "x";
1393
+ elt.parentNode.insertBefore(backup, elt.nextSibling);
1394
+ top = backup.offsetTop;
1395
+ }
1396
+ return {top: top, left: left};
1397
+ }
1275
1398
  function localCoords(pos, inLineWrap) {
1276
- var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0);
1277
- return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh};
1399
+ var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
1400
+ if (pos.ch == 0) x = 0;
1401
+ else {
1402
+ var sp = measureLine(getLine(pos.line), pos.ch);
1403
+ x = sp.left;
1404
+ if (options.lineWrapping) y += Math.max(0, sp.top);
1405
+ }
1406
+ return {x: x, y: y, yBot: y + lh};
1407
+ }
1408
+ // Coords must be lineSpace-local
1409
+ function coordsChar(x, y) {
1410
+ if (y < 0) y = 0;
1411
+ var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1412
+ var lineNo = lineAtHeight(doc, heightPos);
1413
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: 0};
1414
+ var lineObj = getLine(lineNo), text = lineObj.text;
1415
+ var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1416
+ if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1417
+ function getX(len) {
1418
+ var sp = measureLine(lineObj, len);
1419
+ if (tw) {
1420
+ var off = Math.round(sp.top / th);
1421
+ return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1422
+ }
1423
+ return sp.left;
1424
+ }
1425
+ var from = 0, fromX = 0, to = text.length, toX;
1426
+ // Guess a suitable upper bound for our search.
1427
+ var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
1428
+ for (;;) {
1429
+ var estX = getX(estimated);
1430
+ if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1431
+ else {toX = estX; to = estimated; break;}
1432
+ }
1433
+ if (x > toX) return {line: lineNo, ch: to};
1434
+ // Try to guess a suitable lower bound as well.
1435
+ estimated = Math.floor(to * 0.8); estX = getX(estimated);
1436
+ if (estX < x) {from = estimated; fromX = estX;}
1437
+ // Do a binary search between these bounds.
1438
+ for (;;) {
1439
+ if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1440
+ var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1441
+ if (middleX > x) {to = middle; toX = middleX;}
1442
+ else {from = middle; fromX = middleX;}
1443
+ }
1278
1444
  }
1279
1445
  function pageCoords(pos) {
1280
1446
  var local = localCoords(pos, true), off = eltOffset(lineSpace);
1281
1447
  return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1282
1448
  }
1283
1449
 
1284
- function lineHeight() {
1285
- var nlines = lineDiv.childNodes.length;
1286
- if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
1287
- measure.innerHTML = "<pre>x</pre>";
1288
- return measure.firstChild.offsetHeight || 1;
1450
+ var cachedHeight, cachedFor;
1451
+ function textHeight() {
1452
+ var offsetHeight = lineDiv.offsetHeight;
1453
+ if (offsetHeight == cachedFor) return cachedHeight;
1454
+ cachedFor = offsetHeight;
1455
+ measure.innerHTML = "<pre>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x</pre>";
1456
+ return (cachedHeight = measure.firstChild.offsetHeight / 10 || 1);
1457
+ }
1458
+ var cachedWidth, cachedFor = 0;
1459
+ function charWidth() {
1460
+ if (scroller.clientWidth == cachedFor) return cachedWidth;
1461
+ cachedFor = scroller.clientWidth;
1462
+ return (cachedWidth = stringWidth("x"));
1289
1463
  }
1290
1464
  function paddingTop() {return lineSpace.offsetTop;}
1291
1465
  function paddingLeft() {return lineSpace.offsetLeft;}
@@ -1300,8 +1474,7 @@ var CodeMirror = (function() {
1300
1474
  if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1301
1475
  return null;
1302
1476
  var offL = eltOffset(lineSpace, true);
1303
- var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
1304
- return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
1477
+ return coordsChar(x - offL.left, y - offL.top);
1305
1478
  }
1306
1479
  function onContextMenu(e) {
1307
1480
  var pos = posFromMouse(e);
@@ -1352,7 +1525,7 @@ var CodeMirror = (function() {
1352
1525
 
1353
1526
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1354
1527
  function matchBrackets(autoclear) {
1355
- var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1;
1528
+ var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
1356
1529
  var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1357
1530
  if (!match) return;
1358
1531
  var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
@@ -1376,8 +1549,8 @@ var CodeMirror = (function() {
1376
1549
  }
1377
1550
  }
1378
1551
  }
1379
- for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
1380
- var line = lines[i], first = i == head.line;
1552
+ for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
1553
+ var line = getLine(i), first = i == head.line;
1381
1554
  var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1382
1555
  if (found) break;
1383
1556
  }
@@ -1399,7 +1572,7 @@ var CodeMirror = (function() {
1399
1572
  var minindent, minline;
1400
1573
  for (var search = n, lim = n - 40; search > lim; --search) {
1401
1574
  if (search == 0) return 0;
1402
- var line = lines[search-1];
1575
+ var line = getLine(search-1);
1403
1576
  if (line.stateAfter) return search;
1404
1577
  var indented = line.indentation();
1405
1578
  if (minline == null || minindent > indented) {
@@ -1410,56 +1583,57 @@ var CodeMirror = (function() {
1410
1583
  return minline;
1411
1584
  }
1412
1585
  function getStateBefore(n) {
1413
- var start = findStartLine(n), state = start && lines[start-1].stateAfter;
1586
+ var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1414
1587
  if (!state) state = startState(mode);
1415
1588
  else state = copyState(mode, state);
1416
- for (var i = start; i < n; ++i) {
1417
- var line = lines[i];
1589
+ doc.iter(start, n, function(line) {
1418
1590
  line.highlight(mode, state);
1419
1591
  line.stateAfter = copyState(mode, state);
1420
- }
1421
- changes.push({from: start, to: n});
1422
- if (n < lines.length && !lines[n].stateAfter) work.push(n);
1592
+ });
1593
+ if (start < n) changes.push({from: start, to: n});
1594
+ if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1423
1595
  return state;
1424
1596
  }
1425
1597
  function highlightLines(start, end) {
1426
1598
  var state = getStateBefore(start);
1427
- for (var i = start; i < end; ++i) {
1428
- var line = lines[i];
1599
+ doc.iter(start, end, function(line) {
1429
1600
  line.highlight(mode, state);
1430
1601
  line.stateAfter = copyState(mode, state);
1431
- }
1602
+ });
1432
1603
  }
1433
1604
  function highlightWorker() {
1434
1605
  var end = +new Date + options.workTime;
1435
1606
  var foundWork = work.length;
1436
1607
  while (work.length) {
1437
- if (!lines[showingFrom].stateAfter) var task = showingFrom;
1608
+ if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1438
1609
  else var task = work.pop();
1439
- if (task >= lines.length) continue;
1440
- var start = findStartLine(task), state = start && lines[start-1].stateAfter;
1610
+ if (task >= doc.size) continue;
1611
+ var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1441
1612
  if (state) state = copyState(mode, state);
1442
1613
  else state = startState(mode);
1443
1614
 
1444
- var unchanged = 0, compare = mode.compareStates, realChange = false;
1445
- for (var i = start, l = lines.length; i < l; ++i) {
1446
- var line = lines[i], hadState = line.stateAfter;
1615
+ var unchanged = 0, compare = mode.compareStates, realChange = false,
1616
+ i = start, bail = false;
1617
+ doc.iter(i, doc.size, function(line) {
1618
+ var hadState = line.stateAfter;
1447
1619
  if (+new Date > end) {
1448
1620
  work.push(i);
1449
1621
  startWorker(options.workDelay);
1450
1622
  if (realChange) changes.push({from: task, to: i + 1});
1451
- return;
1623
+ return (bail = true);
1452
1624
  }
1453
1625
  var changed = line.highlight(mode, state);
1454
1626
  if (changed) realChange = true;
1455
1627
  line.stateAfter = copyState(mode, state);
1456
1628
  if (compare) {
1457
- if (hadState && compare(hadState, state)) break;
1629
+ if (hadState && compare(hadState, state)) return true;
1458
1630
  } else {
1459
1631
  if (changed !== false || !hadState) unchanged = 0;
1460
- else if (++unchanged > 3) break;
1632
+ else if (++unchanged > 3) return true;
1461
1633
  }
1462
- }
1634
+ ++i;
1635
+ });
1636
+ if (bail) return;
1463
1637
  if (realChange) changes.push({from: task, to: i + 1});
1464
1638
  }
1465
1639
  if (foundWork && options.onHighlightComplete)
@@ -1530,7 +1704,7 @@ var CodeMirror = (function() {
1530
1704
  if (typeof query != "string") // Regexp match
1531
1705
  this.matches = function(reverse, pos) {
1532
1706
  if (reverse) {
1533
- var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
1707
+ var line = getLine(pos.line).text.slice(0, pos.ch), match = line.match(query), start = 0;
1534
1708
  while (match) {
1535
1709
  var ind = line.indexOf(match[0]);
1536
1710
  start += ind;
@@ -1542,7 +1716,7 @@ var CodeMirror = (function() {
1542
1716
  }
1543
1717
  }
1544
1718
  else {
1545
- var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
1719
+ var line = getLine(pos.line).text.slice(pos.ch), match = line.match(query),
1546
1720
  start = match && pos.ch + line.indexOf(match[0]);
1547
1721
  }
1548
1722
  if (match)
@@ -1557,7 +1731,7 @@ var CodeMirror = (function() {
1557
1731
  // Different methods for single-line and multi-line queries
1558
1732
  if (target.length == 1)
1559
1733
  this.matches = function(reverse, pos) {
1560
- var line = fold(lines[pos.line].text), len = query.length, match;
1734
+ var line = fold(getLine(pos.line).text), len = query.length, match;
1561
1735
  if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
1562
1736
  : (match = line.indexOf(query, pos.ch)) != -1)
1563
1737
  return {from: {line: pos.line, ch: match},
@@ -1565,14 +1739,14 @@ var CodeMirror = (function() {
1565
1739
  };
1566
1740
  else
1567
1741
  this.matches = function(reverse, pos) {
1568
- var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
1742
+ var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(getLine(ln).text);
1569
1743
  var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
1570
1744
  if (reverse ? offsetA >= pos.ch || offsetA != match.length
1571
1745
  : offsetA <= pos.ch || offsetA != line.length - match.length)
1572
1746
  return;
1573
1747
  for (;;) {
1574
- if (reverse ? !ln : ln == lines.length - 1) return;
1575
- line = fold(lines[ln += reverse ? -1 : 1].text);
1748
+ if (reverse ? !ln : ln == doc.size - 1) return;
1749
+ line = fold(getLine(ln += reverse ? -1 : 1).text);
1576
1750
  match = target[reverse ? --idx : ++idx];
1577
1751
  if (idx > 0 && idx < target.length - 1) {
1578
1752
  if (line != match) return;
@@ -1608,10 +1782,10 @@ var CodeMirror = (function() {
1608
1782
  }
1609
1783
  if (reverse) {
1610
1784
  if (!pos.line) return savePosAndFail(0);
1611
- pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
1785
+ pos = {line: pos.line-1, ch: getLine(pos.line-1).text.length};
1612
1786
  }
1613
1787
  else {
1614
- if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
1788
+ if (pos.line == doc.size - 1) return savePosAndFail(doc.size);
1615
1789
  pos = {line: pos.line+1, ch: 0};
1616
1790
  }
1617
1791
  }
@@ -1647,6 +1821,7 @@ var CodeMirror = (function() {
1647
1821
  enterMode: "indent",
1648
1822
  electricChars: true,
1649
1823
  onKeyEvent: null,
1824
+ lineWrapping: false,
1650
1825
  lineNumbers: false,
1651
1826
  gutter: false,
1652
1827
  fixedGutter: false,
@@ -1663,6 +1838,7 @@ var CodeMirror = (function() {
1663
1838
  workDelay: 200,
1664
1839
  undoDepth: 40,
1665
1840
  tabindex: null,
1841
+ pollForIME: false,
1666
1842
  document: window.document
1667
1843
  };
1668
1844
 
@@ -1760,11 +1936,11 @@ var CodeMirror = (function() {
1760
1936
  }
1761
1937
  return nstate;
1762
1938
  }
1763
- CodeMirror.startState = startState;
1939
+ CodeMirror.copyState = copyState;
1764
1940
  function startState(mode, a1, a2) {
1765
1941
  return mode.startState ? mode.startState(a1, a2) : true;
1766
1942
  }
1767
- CodeMirror.copyState = copyState;
1943
+ CodeMirror.startState = startState;
1768
1944
 
1769
1945
  // The character stream used by a mode's parser.
1770
1946
  function StringStream(string) {
@@ -1821,22 +1997,86 @@ var CodeMirror = (function() {
1821
1997
  };
1822
1998
  CodeMirror.StringStream = StringStream;
1823
1999
 
2000
+ function MarkedText(from, to, className, set) {
2001
+ this.from = from; this.to = to; this.style = className; this.set = set;
2002
+ }
2003
+ MarkedText.prototype = {
2004
+ attach: function(line) { this.set.push(line); },
2005
+ detach: function(line) {
2006
+ var ix = indexOf(this.set, line);
2007
+ if (ix > -1) this.set.splice(ix, 1);
2008
+ },
2009
+ split: function(pos, lenBefore) {
2010
+ if (this.to <= pos && this.to != null) return null;
2011
+ var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2012
+ var to = this.to == null ? null : this.to - pos + lenBefore;
2013
+ return new MarkedText(from, to, this.style, this.set);
2014
+ },
2015
+ dup: function() { return new MarkedText(null, null, this.style, this.set); },
2016
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
2017
+ if (this.from != null && this.from >= from)
2018
+ this.from = Math.max(to, this.from) + diff;
2019
+ if (this.to != null && this.to > from)
2020
+ this.to = to < this.to ? this.to + diff : from;
2021
+ if (fromOpen && to > this.from && (to < this.to || this.to == null))
2022
+ this.from = null;
2023
+ if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2024
+ this.to = null;
2025
+ },
2026
+ isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2027
+ sameSet: function(x) { return this.set == x.set; }
2028
+ };
2029
+
2030
+ function Bookmark(pos) {
2031
+ this.from = pos; this.to = pos; this.line = null;
2032
+ }
2033
+ Bookmark.prototype = {
2034
+ attach: function(line) { this.line = line; },
2035
+ detach: function(line) { if (this.line == line) this.line = null; },
2036
+ split: function(pos, lenBefore) {
2037
+ if (pos < this.from) {
2038
+ this.from = this.to = (this.from - pos) + lenBefore;
2039
+ return this;
2040
+ }
2041
+ },
2042
+ isDead: function() { return this.from > this.to; },
2043
+ clipTo: function(fromOpen, from, toOpen, to, diff) {
2044
+ if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2045
+ this.from = 0; this.to = -1;
2046
+ } else if (this.from > from) {
2047
+ this.from = this.to = Math.max(to, this.from) + diff;
2048
+ }
2049
+ },
2050
+ sameSet: function(x) { return false; },
2051
+ find: function() {
2052
+ if (!this.line || !this.line.parent) return null;
2053
+ return {line: lineNo(this.line), ch: this.from};
2054
+ },
2055
+ clear: function() {
2056
+ if (this.line) {
2057
+ var found = indexOf(this.line.marked, this);
2058
+ if (found != -1) this.line.marked.splice(found, 1);
2059
+ this.line = null;
2060
+ }
2061
+ }
2062
+ };
2063
+
1824
2064
  // Line objects. These hold state related to a line, including
1825
2065
  // highlighting info (the styles array).
1826
2066
  function Line(text, styles) {
1827
2067
  this.styles = styles || [text, null];
1828
- this.stateAfter = null;
1829
2068
  this.text = text;
2069
+ this.height = 1;
1830
2070
  this.marked = this.gutterMarker = this.className = null;
2071
+ this.stateAfter = this.parent = this.hidden = null;
1831
2072
  }
1832
2073
  Line.inheritMarks = function(text, orig) {
1833
2074
  var ln = new Line(text), mk = orig.marked;
1834
2075
  if (mk) {
1835
2076
  for (var i = 0; i < mk.length; ++i) {
1836
- if (mk[i].to == null) {
2077
+ if (mk[i].to == null && mk[i].style) {
1837
2078
  var newmk = ln.marked || (ln.marked = []), mark = mk[i];
1838
- newmk.push({from: null, to: null, style: mark.style, set: mark.set});
1839
- mark.set.push(ln);
2079
+ var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
1840
2080
  }
1841
2081
  }
1842
2082
  }
@@ -1845,6 +2085,9 @@ var CodeMirror = (function() {
1845
2085
  Line.prototype = {
1846
2086
  // Replace a piece of a line, keeping the styles around it intact.
1847
2087
  replace: function(from, to_, text) {
2088
+ // Reset line class if the whole text was replaced.
2089
+ if (!from && (to_ == null || to_ == this.text.length))
2090
+ this.className = this.gutterMarker = null;
1848
2091
  var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
1849
2092
  copyStyles(0, from, this.styles, st);
1850
2093
  if (text) st.push(text, null);
@@ -1853,23 +2096,10 @@ var CodeMirror = (function() {
1853
2096
  this.text = this.text.slice(0, from) + text + this.text.slice(to);
1854
2097
  this.stateAfter = null;
1855
2098
  if (mk) {
1856
- var diff = text.length - (to - from), end = this.text.length;
1857
- var changeStart = Math.min(from, from + diff);
1858
- for (var i = 0; i < mk.length; ++i) {
1859
- var mark = mk[i], del = false;
1860
- if (mark.from != null && mark.from >= end) del = true;
1861
- else {
1862
- if (mark.from != null && mark.from >= from) {
1863
- mark.from += diff;
1864
- if (mark.from <= 0) mark.from = from == null ? null : 0;
1865
- }
1866
- else if (to_ == null) mark.to = null;
1867
- if (mark.to != null && mark.to > from) {
1868
- mark.to += diff;
1869
- if (mark.to < 0) del = true;
1870
- }
1871
- }
1872
- if (del || (mark.from != null && mark.to != null && mark.from >= mark.to)) mk.splice(i--, 1);
2099
+ var diff = text.length - (to - from);
2100
+ for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
2101
+ mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2102
+ if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
1873
2103
  }
1874
2104
  }
1875
2105
  },
@@ -1881,50 +2111,63 @@ var CodeMirror = (function() {
1881
2111
  if (mk) {
1882
2112
  for (var i = 0; i < mk.length; ++i) {
1883
2113
  var mark = mk[i];
1884
- if (mark.to > pos || mark.to == null) {
2114
+ var newmark = mark.split(pos, textBefore.length);
2115
+ if (newmark) {
1885
2116
  if (!taken.marked) taken.marked = [];
1886
- taken.marked.push({
1887
- from: mark.from < pos || mark.from == null ? null : mark.from - pos + textBefore.length,
1888
- to: mark.to == null ? null : mark.to - pos + textBefore.length,
1889
- style: mark.style, set: mark.set
1890
- });
1891
- mark.set.push(taken);
2117
+ taken.marked.push(newmark); newmark.attach(taken);
1892
2118
  }
1893
2119
  }
1894
2120
  }
1895
2121
  return taken;
1896
2122
  },
1897
2123
  append: function(line) {
1898
- if (!line.text.length) return;
1899
- var mylen = this.text.length, mk = line.marked;
2124
+ var mylen = this.text.length, mk = line.marked, mymk = this.marked;
1900
2125
  this.text += line.text;
1901
2126
  copyStyles(0, line.text.length, line.styles, this.styles);
1902
- if (mk && mk.length) {
1903
- var mymk = this.marked || (this.marked = []);
2127
+ if (mymk) {
1904
2128
  for (var i = 0; i < mymk.length; ++i)
1905
2129
  if (mymk[i].to == null) mymk[i].to = mylen;
2130
+ }
2131
+ if (mk && mk.length) {
2132
+ if (!mymk) this.marked = mymk = [];
1906
2133
  outer: for (var i = 0; i < mk.length; ++i) {
1907
2134
  var mark = mk[i];
1908
2135
  if (!mark.from) {
1909
2136
  for (var j = 0; j < mymk.length; ++j) {
1910
2137
  var mymark = mymk[j];
1911
- if (mymark.to == mylen && mymark.set == mark.set) {
2138
+ if (mymark.to == mylen && mymark.sameSet(mark)) {
1912
2139
  mymark.to = mark.to == null ? null : mark.to + mylen;
2140
+ if (mymark.isDead()) {
2141
+ mymark.detach(this);
2142
+ mk.splice(i--, 1);
2143
+ }
1913
2144
  continue outer;
1914
2145
  }
1915
2146
  }
1916
2147
  }
1917
2148
  mymk.push(mark);
1918
- mark.set.push(this);
2149
+ mark.attach(this);
1919
2150
  mark.from += mylen;
1920
2151
  if (mark.to != null) mark.to += mylen;
1921
2152
  }
1922
2153
  }
1923
2154
  },
1924
- addMark: function(from, to, style, set) {
1925
- set.push(this);
2155
+ fixMarkEnds: function(other) {
2156
+ var mk = this.marked, omk = other.marked;
2157
+ if (!mk) return;
2158
+ for (var i = 0; i < mk.length; ++i) {
2159
+ var mark = mk[i], close = mark.to == null;
2160
+ if (close && omk) {
2161
+ for (var j = 0; j < omk.length; ++j)
2162
+ if (omk[j].sameSet(mark)) {close = false; break;}
2163
+ }
2164
+ if (close) mark.to = this.text.length;
2165
+ }
2166
+ },
2167
+ addMark: function(mark) {
2168
+ mark.attach(this);
1926
2169
  if (this.marked == null) this.marked = [];
1927
- this.marked.push({from: from, to: to, style: style, set: set});
2170
+ this.marked.push(mark);
1928
2171
  this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
1929
2172
  },
1930
2173
  // Run the given mode's parser over a line, update the styles
@@ -1976,11 +2219,14 @@ var CodeMirror = (function() {
1976
2219
  // Produces an HTML fragment for the line, taking selection,
1977
2220
  // marking, and highlighting into account.
1978
2221
  getHTML: function(sfrom, sto, includePre, endAt) {
1979
- var html = [];
2222
+ var html = [], first = true;
1980
2223
  if (includePre)
1981
2224
  html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
1982
2225
  function span(text, style) {
1983
2226
  if (!text) return;
2227
+ // Work around a bug where, in some compat modes, IE ignores leading spaces
2228
+ if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2229
+ first = false;
1984
2230
  if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
1985
2231
  else html.push(htmlEscape(text));
1986
2232
  }
@@ -2040,6 +2286,11 @@ var CodeMirror = (function() {
2040
2286
  }
2041
2287
  if (includePre) html.push("</pre>");
2042
2288
  return html.join("");
2289
+ },
2290
+ cleanUp: function() {
2291
+ this.parent = null;
2292
+ if (this.marked)
2293
+ for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
2043
2294
  }
2044
2295
  };
2045
2296
  // Utility used by replace and split above
@@ -2058,6 +2309,191 @@ var CodeMirror = (function() {
2058
2309
  }
2059
2310
  }
2060
2311
 
2312
+ // Data structure that holds the sequence of lines.
2313
+ function LeafChunk(lines) {
2314
+ this.lines = lines;
2315
+ this.parent = null;
2316
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
2317
+ lines[i].parent = this;
2318
+ height += lines[i].height;
2319
+ }
2320
+ this.height = height;
2321
+ }
2322
+ LeafChunk.prototype = {
2323
+ chunkSize: function() { return this.lines.length; },
2324
+ remove: function(at, n) {
2325
+ for (var i = at, e = at + n; i < e; ++i) {
2326
+ var line = this.lines[i];
2327
+ line.cleanUp();
2328
+ this.height -= line.height;
2329
+ }
2330
+ this.lines.splice(at, n);
2331
+ },
2332
+ collapse: function(lines) {
2333
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
2334
+ },
2335
+ insertHeight: function(at, lines, height) {
2336
+ this.height += height;
2337
+ this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2338
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2339
+ },
2340
+ iterN: function(at, n, op) {
2341
+ for (var e = at + n; at < e; ++at)
2342
+ if (op(this.lines[at])) return true;
2343
+ }
2344
+ };
2345
+ function BranchChunk(children) {
2346
+ this.children = children;
2347
+ var size = 0, height = 0;
2348
+ for (var i = 0, e = children.length; i < e; ++i) {
2349
+ var ch = children[i];
2350
+ size += ch.chunkSize(); height += ch.height;
2351
+ ch.parent = this;
2352
+ }
2353
+ this.size = size;
2354
+ this.height = height;
2355
+ this.parent = null;
2356
+ }
2357
+ BranchChunk.prototype = {
2358
+ chunkSize: function() { return this.size; },
2359
+ remove: function(at, n) {
2360
+ this.size -= n;
2361
+ for (var i = 0; i < this.children.length; ++i) {
2362
+ var child = this.children[i], sz = child.chunkSize();
2363
+ if (at < sz) {
2364
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
2365
+ child.remove(at, rm);
2366
+ this.height -= oldHeight - child.height;
2367
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
2368
+ if ((n -= rm) == 0) break;
2369
+ at = 0;
2370
+ } else at -= sz;
2371
+ }
2372
+ if (this.size - n < 25) {
2373
+ var lines = [];
2374
+ this.collapse(lines);
2375
+ this.children = [new LeafChunk(lines)];
2376
+ }
2377
+ },
2378
+ collapse: function(lines) {
2379
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
2380
+ },
2381
+ insert: function(at, lines) {
2382
+ var height = 0;
2383
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
2384
+ this.insertHeight(at, lines, height);
2385
+ },
2386
+ insertHeight: function(at, lines, height) {
2387
+ this.size += lines.length;
2388
+ this.height += height;
2389
+ for (var i = 0, e = this.children.length; i < e; ++i) {
2390
+ var child = this.children[i], sz = child.chunkSize();
2391
+ if (at <= sz) {
2392
+ child.insertHeight(at, lines, height);
2393
+ if (child.lines && child.lines.length > 50) {
2394
+ while (child.lines.length > 50) {
2395
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
2396
+ var newleaf = new LeafChunk(spilled);
2397
+ child.height -= newleaf.height;
2398
+ this.children.splice(i + 1, 0, newleaf);
2399
+ newleaf.parent = this;
2400
+ }
2401
+ this.maybeSpill();
2402
+ }
2403
+ break;
2404
+ }
2405
+ at -= sz;
2406
+ }
2407
+ },
2408
+ maybeSpill: function() {
2409
+ if (this.children.length <= 10) return;
2410
+ var me = this;
2411
+ do {
2412
+ var spilled = me.children.splice(me.children.length - 5, 5);
2413
+ var sibling = new BranchChunk(spilled);
2414
+ if (!me.parent) { // Become the parent node
2415
+ var copy = new BranchChunk(me.children);
2416
+ copy.parent = me;
2417
+ me.children = [copy, sibling];
2418
+ me = copy;
2419
+ } else {
2420
+ me.size -= sibling.size;
2421
+ me.height -= sibling.height;
2422
+ var myIndex = indexOf(me.parent.children, me);
2423
+ me.parent.children.splice(myIndex + 1, 0, sibling);
2424
+ }
2425
+ sibling.parent = me.parent;
2426
+ } while (me.children.length > 10);
2427
+ me.parent.maybeSpill();
2428
+ },
2429
+ iter: function(from, to, op) { this.iterN(from, to - from, op); },
2430
+ iterN: function(at, n, op) {
2431
+ for (var i = 0, e = this.children.length; i < e; ++i) {
2432
+ var child = this.children[i], sz = child.chunkSize();
2433
+ if (at < sz) {
2434
+ var used = Math.min(n, sz - at);
2435
+ if (child.iterN(at, used, op)) return true;
2436
+ if ((n -= used) == 0) break;
2437
+ at = 0;
2438
+ } else at -= sz;
2439
+ }
2440
+ }
2441
+ };
2442
+
2443
+ function getLineAt(chunk, n) {
2444
+ for (;;) {
2445
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
2446
+ var child = chunk.children[i], sz = child.chunkSize();
2447
+ if (n < sz) { chunk = child; break; }
2448
+ n -= sz;
2449
+ }
2450
+ if (chunk.lines) return chunk.lines[n];
2451
+ }
2452
+ }
2453
+ function lineNo(line) {
2454
+ if (line.parent == null) return null;
2455
+ var cur = line.parent, no = indexOf(cur.lines, line);
2456
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2457
+ for (var i = 0, e = chunk.children.length; ; ++i) {
2458
+ if (chunk.children[i] == cur) break;
2459
+ no += chunk.children[i].chunkSize();
2460
+ }
2461
+ }
2462
+ return no;
2463
+ }
2464
+ function lineAtHeight(chunk, h) {
2465
+ var n = 0;
2466
+ outer: do {
2467
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
2468
+ var child = chunk.children[i], ch = child.height;
2469
+ if (h < ch) { chunk = child; continue outer; }
2470
+ h -= ch;
2471
+ n += child.chunkSize();
2472
+ }
2473
+ return n;
2474
+ } while (!chunk.lines);
2475
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
2476
+ var line = chunk.lines[i], lh = line.height;
2477
+ if (h < lh) break;
2478
+ h -= lh;
2479
+ }
2480
+ return n + i;
2481
+ }
2482
+ function heightAtLine(chunk, n) {
2483
+ var h = 0;
2484
+ outer: do {
2485
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
2486
+ var child = chunk.children[i], sz = child.chunkSize();
2487
+ if (n < sz) { chunk = child; continue outer; }
2488
+ n -= sz;
2489
+ h += child.height;
2490
+ }
2491
+ return h;
2492
+ } while (!chunk.lines);
2493
+ for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
2494
+ return h;
2495
+ }
2496
+
2061
2497
  // The history object 'chunks' changes that are made close together
2062
2498
  // and at almost the same time into bigger undoable units.
2063
2499
  function History() {
@@ -2132,25 +2568,18 @@ var CodeMirror = (function() {
2132
2568
  function Delayed() {this.id = null;}
2133
2569
  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2134
2570
 
2135
- // Some IE versions don't preserve whitespace when setting the
2136
- // innerHTML of a PRE tag.
2137
- var badInnerHTML = (function() {
2138
- var pre = document.createElement("pre");
2139
- pre.innerHTML = " "; return !pre.innerHTML;
2140
- })();
2141
-
2142
2571
  // Detect drag-and-drop
2143
- var dragAndDrop = (function() {
2572
+ var dragAndDrop = function() {
2144
2573
  // IE8 has ondragstart and ondrop properties, but doesn't seem to
2145
2574
  // actually support ondragstart the way it's supposed to work.
2146
2575
  if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2147
2576
  var div = document.createElement('div');
2148
- return "ondragstart" in div && "ondrop" in div;
2149
- })();
2577
+ return "draggable" in div;
2578
+ }();
2150
2579
 
2151
2580
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2152
2581
  var ie = /MSIE \d/.test(navigator.userAgent);
2153
- var safari = /Apple Computer/.test(navigator.vendor);
2582
+ var webkit = /WebKit\//.test(navigator.userAgent);
2154
2583
 
2155
2584
  var lineSep = "\n";
2156
2585
  // Feature-detect whether newlines in textareas are converted to \r\n
@@ -2162,6 +2591,7 @@ var CodeMirror = (function() {
2162
2591
 
2163
2592
  var tabSize = 8;
2164
2593
  var mac = /Mac/.test(navigator.platform);
2594
+ var win = /Win/.test(navigator.platform);
2165
2595
  var movementKeys = {};
2166
2596
  for (var i = 35; i <= 40; ++i)
2167
2597
  movementKeys[i] = movementKeys["c" + i] = true;
@@ -2184,21 +2614,44 @@ var CodeMirror = (function() {
2184
2614
  if (elt.currentStyle) return elt.currentStyle;
2185
2615
  return window.getComputedStyle(elt, null);
2186
2616
  }
2617
+
2187
2618
  // Find the position of an element by following the offsetParent chain.
2188
2619
  // If screen==true, it returns screen (rather than page) coordinates.
2189
2620
  function eltOffset(node, screen) {
2190
- var doc = node.ownerDocument.body;
2191
- var x = 0, y = 0, skipDoc = false;
2621
+ var bod = node.ownerDocument.body;
2622
+ var x = 0, y = 0, skipBody = false;
2192
2623
  for (var n = node; n; n = n.offsetParent) {
2193
- x += n.offsetLeft; y += n.offsetTop;
2624
+ var ol = n.offsetLeft, ot = n.offsetTop;
2625
+ // Firefox reports weird inverted offsets when the body has a border.
2626
+ if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2627
+ else { x += ol, y += ot; }
2194
2628
  if (screen && computedStyle(n).position == "fixed")
2195
- skipDoc = true;
2629
+ skipBody = true;
2196
2630
  }
2197
- var e = screen && !skipDoc ? null : doc;
2631
+ var e = screen && !skipBody ? null : bod;
2198
2632
  for (var n = node.parentNode; n != e; n = n.parentNode)
2199
2633
  if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2200
2634
  return {left: x, top: y};
2201
2635
  }
2636
+ // Use the faster and saner getBoundingClientRect method when possible.
2637
+ if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2638
+ // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2639
+ // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2640
+ try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
2641
+ catch(e) { box = {top: 0, left: 0}; }
2642
+ if (!screen) {
2643
+ // Get the toplevel scroll, working around browser differences.
2644
+ if (window.pageYOffset == null) {
2645
+ var t = document.documentElement || document.body.parentNode;
2646
+ if (t.scrollTop == null) t = document.body;
2647
+ box.top += t.scrollTop; box.left += t.scrollLeft;
2648
+ } else {
2649
+ box.top += window.pageYOffset; box.left += window.pageXOffset;
2650
+ }
2651
+ }
2652
+ return box;
2653
+ };
2654
+
2202
2655
  // Get a node's text content.
2203
2656
  function eltText(node) {
2204
2657
  return node.textContent || node.innerText || node.nodeValue || "";
@@ -2262,7 +2715,7 @@ var CodeMirror = (function() {
2262
2715
  try {return {start: te.selectionStart, end: te.selectionEnd};}
2263
2716
  catch(e) {return null;}
2264
2717
  };
2265
- if (safari)
2718
+ if (webkit)
2266
2719
  // On Safari, selection set with setSelectionRange are in a sort
2267
2720
  // of limbo wrt their anchor. If you press shift-left in them,
2268
2721
  // the anchor is put at the end, and the selection expanded to