codemirror-rails 2.33 → 2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. data/lib/codemirror/rails/version.rb +2 -2
  2. data/vendor/assets/javascripts/codemirror.js +359 -453
  3. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +4 -1
  4. data/vendor/assets/javascripts/codemirror/modes/clojure.js +7 -8
  5. data/vendor/assets/javascripts/codemirror/modes/commonlisp.js +101 -0
  6. data/vendor/assets/javascripts/codemirror/modes/css.js +336 -62
  7. data/vendor/assets/javascripts/codemirror/modes/gfm.js +6 -1
  8. data/vendor/assets/javascripts/codemirror/modes/haxe.js +0 -3
  9. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +5 -1
  10. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +11 -12
  11. data/vendor/assets/javascripts/codemirror/modes/javascript.js +1 -1
  12. data/vendor/assets/javascripts/codemirror/modes/markdown.js +131 -17
  13. data/vendor/assets/javascripts/codemirror/modes/php.js +4 -6
  14. data/vendor/assets/javascripts/codemirror/modes/shell.js +1 -1
  15. data/vendor/assets/javascripts/codemirror/modes/tiki.js +0 -7
  16. data/vendor/assets/javascripts/codemirror/modes/vb.js +2 -2
  17. data/vendor/assets/javascripts/codemirror/modes/xml.js +0 -8
  18. data/vendor/assets/javascripts/codemirror/utils/closetag.js +35 -35
  19. data/vendor/assets/javascripts/codemirror/utils/formatting.js +147 -253
  20. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +4 -8
  21. data/vendor/assets/javascripts/codemirror/utils/overlay.js +3 -1
  22. data/vendor/assets/javascripts/codemirror/utils/runmode-standalone.js +90 -0
  23. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +1 -1
  24. data/vendor/assets/stylesheets/codemirror.css +2 -2
  25. metadata +14 -12
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '2.33'
4
- CODEMIRROR_VERSION = '2.33'
3
+ VERSION = '2.34'
4
+ CODEMIRROR_VERSION = '2.34'
5
5
  end
6
6
  end
@@ -1,5 +1,5 @@
1
- // CodeMirror version 2.33
2
- //
1
+ // CodeMirror version 2.34
2
+
3
3
  // All functions that need access to the editor's state live inside
4
4
  // the CodeMirror function. Below that, at the bottom of the file,
5
5
  // some utilities are defined.
@@ -62,22 +62,13 @@ window.CodeMirror = (function() {
62
62
  // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
63
63
  else if (ie_lt8) scrollbar.style.minWidth = "18px";
64
64
 
65
- // Check for problem with IE innerHTML not working when we have a
66
- // P (or similar) parent node.
67
- try { charWidth(); }
68
- catch (e) {
69
- if (e.message.match(/runtime/i))
70
- e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
71
- throw e;
72
- }
73
-
74
65
  // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
75
66
  var poll = new Delayed(), highlight = new Delayed(), blinker;
76
67
 
77
68
  // mode holds a mode API object. doc is the tree of Line objects,
78
- // work an array of lines that should be parsed, and history the
79
- // undo history (instance of History constructor).
80
- var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
69
+ // frontier is the point up to which the content has been parsed,
70
+ // and history the undo history (instance of History constructor).
71
+ var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), frontier = 0, focused;
81
72
  loadMode();
82
73
  // The selection. These are always maintained to point at valid
83
74
  // positions. Inverted is used to remember that the user is
@@ -89,7 +80,7 @@ window.CodeMirror = (function() {
89
80
  overwrite = false, suppressEdits = false;
90
81
  // Variables used by startOperation/endOperation to track what
91
82
  // happened during the operation.
92
- var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
83
+ var updateInput, userSelChange, changes, textChanged, selectionChanged,
93
84
  gutterDirty, callbacks;
94
85
  // Current visible range (may be bigger than the view window).
95
86
  var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
@@ -99,7 +90,6 @@ window.CodeMirror = (function() {
99
90
  // Tracks the maximum line length so that the horizontal scrollbar
100
91
  // can be kept static when scrolling.
101
92
  var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
102
- var tabCache = {};
103
93
  var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
104
94
  var goalColumn = null;
105
95
 
@@ -186,6 +176,7 @@ window.CodeMirror = (function() {
186
176
  }
187
177
  },
188
178
  getOption: function(option) {return options[option];},
179
+ getMode: function() {return mode;},
189
180
  undo: operation(undo),
190
181
  redo: operation(redo),
191
182
  indentLine: operation(function(n, dir) {
@@ -204,8 +195,18 @@ window.CodeMirror = (function() {
204
195
  history.undone = histData.undone;
205
196
  },
206
197
  getHistory: function() {
207
- history.time = 0;
208
- return {done: history.done.concat([]), undone: history.undone.concat([])};
198
+ function cp(arr) {
199
+ for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
200
+ nw.push(nwelt = []);
201
+ for (var j = 0, elt = arr[i]; j < elt.length; ++j) {
202
+ var old = [], cur = elt[j];
203
+ nwelt.push({start: cur.start, added: cur.added, old: old});
204
+ for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
205
+ }
206
+ }
207
+ return nw;
208
+ }
209
+ return {done: cp(history.done), undone: cp(history.undone)};
209
210
  },
210
211
  matchBrackets: operation(function(){matchBrackets(true);}),
211
212
  getTokenAt: operation(function(pos) {
@@ -374,6 +375,12 @@ window.CodeMirror = (function() {
374
375
  for (var n = line; n; n = n.parent) n.height += diff;
375
376
  }
376
377
 
378
+ function lineContent(line, wrapAt) {
379
+ if (!line.styles)
380
+ line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize);
381
+ return line.getContent(options.tabSize, wrapAt, options.lineWrapping);
382
+ }
383
+
377
384
  function setValue(code) {
378
385
  var top = {line: 0, ch: 0};
379
386
  updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
@@ -572,11 +579,8 @@ window.CodeMirror = (function() {
572
579
  e.dataTransfer.setData("Text", txt);
573
580
 
574
581
  // Use dummy image instead of default browsers image.
575
- if (gecko || chrome || opera) {
576
- var img = elt('img');
577
- img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
578
- e.dataTransfer.setDragImage(img, 0, 0);
579
- }
582
+ if (e.dataTransfer.setDragImage)
583
+ e.dataTransfer.setDragImage(elt('img'), 0, 0);
580
584
  }
581
585
 
582
586
  function doHandleBinding(bound, dropShift) {
@@ -610,10 +614,11 @@ window.CodeMirror = (function() {
610
614
  }, 50);
611
615
 
612
616
  var name = keyNames[e_prop(e, "keyCode")], handled = false;
617
+ var flipCtrlCmd = opera && mac;
613
618
  if (name == null || e.altGraphKey) return false;
614
619
  if (e_prop(e, "altKey")) name = "Alt-" + name;
615
- if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
616
- if (e_prop(e, "metaKey")) name = "Cmd-" + name;
620
+ if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
621
+ if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
617
622
 
618
623
  var stopped = false;
619
624
  function stop() { stopped = true; }
@@ -689,7 +694,6 @@ window.CodeMirror = (function() {
689
694
  focused = true;
690
695
  if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
691
696
  scroller.className += " CodeMirror-focused";
692
- if (!leaveInputAlone) resetInput(true);
693
697
  }
694
698
  slowPoll();
695
699
  restartBlink();
@@ -712,13 +716,16 @@ window.CodeMirror = (function() {
712
716
  // Afterwards, set the selection to selFrom, selTo.
713
717
  function updateLines(from, to, newText, selFrom, selTo) {
714
718
  if (suppressEdits) return;
719
+ var old = [];
720
+ doc.iter(from.line, to.line + 1, function(line) {
721
+ old.push(newHL(line.text, line.markedSpans));
722
+ });
715
723
  if (history) {
716
- var old = [];
717
- doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
718
724
  history.addChange(from.line, newText.length, old);
719
725
  while (history.done.length > options.undoDepth) history.done.shift();
720
726
  }
721
- updateLinesNoUndo(from, to, newText, selFrom, selTo);
727
+ var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
728
+ updateLinesNoUndo(from, to, lines, selFrom, selTo);
722
729
  }
723
730
  function unredoHelper(from, to) {
724
731
  if (!from.length) return;
@@ -726,11 +733,12 @@ window.CodeMirror = (function() {
726
733
  for (var i = set.length - 1; i >= 0; i -= 1) {
727
734
  var change = set[i];
728
735
  var replaced = [], end = change.start + change.added;
729
- doc.iter(change.start, end, function(line) { replaced.push(line.text); });
736
+ doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
730
737
  out.push({start: change.start, added: change.old.length, old: replaced});
731
738
  var pos = {line: change.start + change.old.length - 1,
732
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
733
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
739
+ ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))};
740
+ updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length},
741
+ change.old, pos, pos);
734
742
  }
735
743
  updateInput = true;
736
744
  to.push(out);
@@ -738,66 +746,59 @@ window.CodeMirror = (function() {
738
746
  function undo() {unredoHelper(history.done, history.undone);}
739
747
  function redo() {unredoHelper(history.undone, history.done);}
740
748
 
741
- function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
749
+ function updateLinesNoUndo(from, to, lines, selFrom, selTo) {
742
750
  if (suppressEdits) return;
743
751
  var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
744
752
  if (!options.lineWrapping)
745
753
  doc.iter(from.line, to.line + 1, function(line) {
746
754
  if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
747
755
  });
748
- if (from.line != to.line || newText.length > 1) gutterDirty = true;
756
+ if (from.line != to.line || lines.length > 1) gutterDirty = true;
749
757
 
750
758
  var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
751
- // First adjust the line structure, taking some care to leave highlighting intact.
752
- if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
759
+ var lastHL = lst(lines);
760
+
761
+ // First adjust the line structure
762
+ if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
753
763
  // This is a whole-line replace. Treated specially to make
754
764
  // sure line objects move the way they are supposed to.
755
765
  var added = [], prevLine = null;
756
- if (from.line) {
757
- prevLine = getLine(from.line - 1);
758
- prevLine.fixMarkEnds(lastLine);
759
- } else lastLine.fixMarkStarts();
760
- for (var i = 0, e = newText.length - 1; i < e; ++i)
761
- added.push(Line.inheritMarks(newText[i], prevLine));
766
+ for (var i = 0, e = lines.length - 1; i < e; ++i)
767
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
768
+ lastLine.update(lastLine.text, hlSpans(lastHL));
762
769
  if (nlines) doc.remove(from.line, nlines, callbacks);
763
770
  if (added.length) doc.insert(from.line, added);
764
771
  } else if (firstLine == lastLine) {
765
- if (newText.length == 1)
766
- firstLine.replace(from.ch, to.ch, newText[0]);
767
- else {
768
- lastLine = firstLine.split(to.ch, newText[newText.length-1]);
769
- firstLine.replace(from.ch, null, newText[0]);
770
- firstLine.fixMarkEnds(lastLine);
771
- var added = [];
772
- for (var i = 1, e = newText.length - 1; i < e; ++i)
773
- added.push(Line.inheritMarks(newText[i], firstLine));
774
- added.push(lastLine);
772
+ if (lines.length == 1) {
773
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), hlSpans(lines[0]));
774
+ } else {
775
+ for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
776
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
777
+ added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL)));
778
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
775
779
  doc.insert(from.line + 1, added);
776
780
  }
777
- } else if (newText.length == 1) {
778
- firstLine.replace(from.ch, null, newText[0]);
779
- lastLine.replace(null, to.ch, "");
780
- firstLine.append(lastLine);
781
+ } else if (lines.length == 1) {
782
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), hlSpans(lines[0]));
781
783
  doc.remove(from.line + 1, nlines, callbacks);
782
784
  } else {
783
785
  var added = [];
784
- firstLine.replace(from.ch, null, newText[0]);
785
- lastLine.replace(null, to.ch, newText[newText.length-1]);
786
- firstLine.fixMarkEnds(lastLine);
787
- for (var i = 1, e = newText.length - 1; i < e; ++i)
788
- added.push(Line.inheritMarks(newText[i], firstLine));
786
+ firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
787
+ lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
788
+ for (var i = 1, e = lines.length - 1; i < e; ++i)
789
+ added.push(new Line(hlText(lines[i]), hlSpans(lines[i])));
789
790
  if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
790
791
  doc.insert(from.line + 1, added);
791
792
  }
792
793
  if (options.lineWrapping) {
793
794
  var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
794
- doc.iter(from.line, from.line + newText.length, function(line) {
795
+ doc.iter(from.line, from.line + lines.length, function(line) {
795
796
  if (line.hidden) return;
796
797
  var guess = Math.ceil(line.text.length / perLine) || 1;
797
798
  if (guess != line.height) updateLineHeight(line, guess);
798
799
  });
799
800
  } else {
800
- doc.iter(from.line, from.line + newText.length, function(line) {
801
+ doc.iter(from.line, from.line + lines.length, function(line) {
801
802
  var l = line.text;
802
803
  if (!line.hidden && l.length > maxLineLength) {
803
804
  maxLine = line; maxLineLength = l.length; maxLineChanged = true;
@@ -807,26 +808,24 @@ window.CodeMirror = (function() {
807
808
  if (recomputeMaxLength) updateMaxLine = true;
808
809
  }
809
810
 
810
- // Add these lines to the work array, so that they will be
811
- // highlighted. Adjust work lines if lines were added/removed.
812
- var newWork = [], lendiff = newText.length - nlines - 1;
813
- for (var i = 0, l = work.length; i < l; ++i) {
814
- var task = work[i];
815
- if (task < from.line) newWork.push(task);
816
- else if (task > to.line) newWork.push(task + lendiff);
817
- }
818
- var hlEnd = from.line + Math.min(newText.length, 500);
819
- highlightLines(from.line, hlEnd);
820
- newWork.push(hlEnd);
821
- work = newWork;
822
- startWorker(100);
811
+ // Adjust frontier, schedule worker
812
+ frontier = Math.min(frontier, from.line);
813
+ startWorker(400);
814
+
815
+ var lendiff = lines.length - nlines - 1;
823
816
  // Remember that these lines changed, for updating the display
824
817
  changes.push({from: from.line, to: to.line + 1, diff: lendiff});
825
- var changeObj = {from: from, to: to, text: newText};
826
- if (textChanged) {
827
- for (var cur = textChanged; cur.next; cur = cur.next) {}
828
- cur.next = changeObj;
829
- } else textChanged = changeObj;
818
+ if (options.onChange) {
819
+ // Normalize lines to contain only strings, since that's what
820
+ // the change event handler expects
821
+ for (var i = 0; i < lines.length; ++i)
822
+ if (typeof lines[i] != "string") lines[i] = lines[i].text;
823
+ var changeObj = {from: from, to: to, text: lines};
824
+ if (textChanged) {
825
+ for (var cur = textChanged; cur.next; cur = cur.next) {}
826
+ cur.next = changeObj;
827
+ } else textChanged = changeObj;
828
+ }
830
829
 
831
830
  // Update the selection
832
831
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
@@ -888,7 +887,7 @@ window.CodeMirror = (function() {
888
887
  var line = pos.line + code.length - (to.line - from.line) - 1;
889
888
  var ch = pos.ch;
890
889
  if (pos.line == to.line)
891
- ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
890
+ ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0));
892
891
  return {line: line, ch: ch};
893
892
  }
894
893
  var end;
@@ -906,7 +905,7 @@ window.CodeMirror = (function() {
906
905
  });
907
906
  }
908
907
  function replaceRange1(code, from, to, computeSel) {
909
- var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
908
+ var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length;
910
909
  var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
911
910
  updateLines(from, to, code, newSel.from, newSel.to);
912
911
  }
@@ -926,21 +925,17 @@ window.CodeMirror = (function() {
926
925
  function slowPoll() {
927
926
  if (pollingFast) return;
928
927
  poll.set(options.pollInterval, function() {
929
- startOperation();
930
928
  readInput();
931
929
  if (focused) slowPoll();
932
- endOperation();
933
930
  });
934
931
  }
935
932
  function fastPoll() {
936
933
  var missed = false;
937
934
  pollingFast = true;
938
935
  function p() {
939
- startOperation();
940
936
  var changed = readInput();
941
937
  if (!changed && !missed) {missed = true; poll.set(60, p);}
942
938
  else {pollingFast = false; slowPoll();}
943
- endOperation();
944
939
  }
945
940
  poll.set(20, p);
946
941
  }
@@ -952,9 +947,10 @@ window.CodeMirror = (function() {
952
947
  // supported or compatible enough yet to rely on.)
953
948
  var prevInput = "";
954
949
  function readInput() {
955
- if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
950
+ if (!focused || hasSelection(input) || options.readOnly) return false;
956
951
  var text = input.value;
957
952
  if (text == prevInput) return false;
953
+ if (!nestedOperation) startOperation();
958
954
  shiftSelecting = null;
959
955
  var same = 0, l = Math.min(prevInput.length, text.length);
960
956
  while (same < l && prevInput[same] == text[same]) ++same;
@@ -965,6 +961,7 @@ window.CodeMirror = (function() {
965
961
  replaceSelection(text.slice(same), "end");
966
962
  if (text.length > 1000) { input.value = prevInput = ""; }
967
963
  else prevInput = text;
964
+ if (!nestedOperation) endOperation();
968
965
  return true;
969
966
  }
970
967
  function resetInput(user) {
@@ -1090,6 +1087,7 @@ window.CodeMirror = (function() {
1090
1087
  });
1091
1088
  showingFrom = from; showingTo = to;
1092
1089
  displayOffset = heightAtLine(doc, from);
1090
+ startWorker(100);
1093
1091
 
1094
1092
  // Since this is all rather error prone, it is honoured with the
1095
1093
  // only assertion in the whole file.
@@ -1176,7 +1174,7 @@ window.CodeMirror = (function() {
1176
1174
  if (!nextIntact || nextIntact.from > j) {
1177
1175
  if (line.hidden) var lineElement = elt("pre");
1178
1176
  else {
1179
- var lineElement = line.getElement(makeTab);
1177
+ var lineElement = lineContent(line);
1180
1178
  if (line.className) lineElement.className = line.className;
1181
1179
  // Kludge to make sure the styled element lies behind the selection (by z-index)
1182
1180
  if (line.bgClassName) {
@@ -1460,7 +1458,7 @@ window.CodeMirror = (function() {
1460
1458
  var indentString = "", pos = 0;
1461
1459
  if (options.indentWithTabs)
1462
1460
  for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1463
- while (pos < indentation) {++pos; indentString += " ";}
1461
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
1464
1462
 
1465
1463
  if (indentString != curSpaceString)
1466
1464
  replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
@@ -1469,8 +1467,8 @@ window.CodeMirror = (function() {
1469
1467
  function loadMode() {
1470
1468
  mode = CodeMirror.getMode(options, options.mode);
1471
1469
  doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1472
- work = [0];
1473
- startWorker();
1470
+ frontier = 0;
1471
+ startWorker(100);
1474
1472
  }
1475
1473
  function gutterChanged() {
1476
1474
  var visible = options.gutter || options.lineNumbers;
@@ -1497,13 +1495,6 @@ window.CodeMirror = (function() {
1497
1495
  }
1498
1496
  changes.push({from: 0, to: doc.size});
1499
1497
  }
1500
- function makeTab(col) {
1501
- var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1502
- if (cached) return cached;
1503
- for (var str = "", i = 0; i < w; ++i) str += " ";
1504
- var span = elt("span", str, "cm-tab");
1505
- return (tabCache[w] = {element: span, width: w});
1506
- }
1507
1498
  function themeChanged() {
1508
1499
  scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1509
1500
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
@@ -1514,74 +1505,71 @@ window.CodeMirror = (function() {
1514
1505
  (style ? " cm-keymap-" + style : "");
1515
1506
  }
1516
1507
 
1517
- function TextMarker() { this.set = []; }
1508
+ function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; }
1518
1509
  TextMarker.prototype.clear = operation(function() {
1519
1510
  var min = Infinity, max = -Infinity;
1520
- for (var i = 0, e = this.set.length; i < e; ++i) {
1521
- var line = this.set[i], mk = line.marked;
1522
- if (!mk || !line.parent) continue;
1523
- var lineN = lineNo(line);
1524
- min = Math.min(min, lineN); max = Math.max(max, lineN);
1525
- for (var j = 0; j < mk.length; ++j)
1526
- if (mk[j].marker == this) mk.splice(j--, 1);
1511
+ for (var i = 0; i < this.lines.length; ++i) {
1512
+ var line = this.lines[i];
1513
+ var span = getMarkedSpanFor(line.markedSpans, this, true);
1514
+ if (span.from != null || span.to != null) {
1515
+ var lineN = lineNo(line);
1516
+ min = Math.min(min, lineN); max = Math.max(max, lineN);
1517
+ }
1527
1518
  }
1528
1519
  if (min != Infinity)
1529
1520
  changes.push({from: min, to: max + 1});
1521
+ this.lines.length = 0;
1530
1522
  });
1531
1523
  TextMarker.prototype.find = function() {
1532
1524
  var from, to;
1533
- for (var i = 0, e = this.set.length; i < e; ++i) {
1534
- var line = this.set[i], mk = line.marked;
1535
- for (var j = 0; j < mk.length; ++j) {
1536
- var mark = mk[j];
1537
- if (mark.marker == this) {
1538
- if (mark.from != null || mark.to != null) {
1539
- var found = lineNo(line);
1540
- if (found != null) {
1541
- if (mark.from != null) from = {line: found, ch: mark.from};
1542
- if (mark.to != null) to = {line: found, ch: mark.to};
1543
- }
1544
- }
1545
- }
1525
+ for (var i = 0; i < this.lines.length; ++i) {
1526
+ var line = this.lines[i];
1527
+ var span = getMarkedSpanFor(line.markedSpans, this);
1528
+ if (span.from != null || span.to != null) {
1529
+ var found = lineNo(line);
1530
+ if (span.from != null) from = {line: found, ch: span.from};
1531
+ if (span.to != null) to = {line: found, ch: span.to};
1546
1532
  }
1547
1533
  }
1548
- return {from: from, to: to};
1534
+ if (this.type == "bookmark") return from;
1535
+ return from && {from: from, to: to};
1549
1536
  };
1550
1537
 
1551
- function markText(from, to, className) {
1538
+ function markText(from, to, className, options) {
1552
1539
  from = clipPos(from); to = clipPos(to);
1553
- var tm = new TextMarker();
1554
- if (!posLess(from, to)) return tm;
1555
- function add(line, from, to, className) {
1556
- getLine(line).addMark(new MarkedText(from, to, className, tm));
1557
- }
1558
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1559
- else {
1560
- add(from.line, from.ch, null, className);
1561
- for (var i = from.line + 1, e = to.line; i < e; ++i)
1562
- add(i, null, null, className);
1563
- add(to.line, null, to.ch, className);
1564
- }
1540
+ var marker = new TextMarker("range", className);
1541
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
1542
+ marker[opt] = options[opt];
1543
+ var curLine = from.line;
1544
+ doc.iter(curLine, to.line + 1, function(line) {
1545
+ var span = {from: curLine == from.line ? from.ch : null,
1546
+ to: curLine == to.line ? to.ch : null,
1547
+ marker: marker};
1548
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1549
+ marker.lines.push(line);
1550
+ ++curLine;
1551
+ });
1565
1552
  changes.push({from: from.line, to: to.line + 1});
1566
- return tm;
1553
+ return marker;
1567
1554
  }
1568
1555
 
1569
1556
  function setBookmark(pos) {
1570
1557
  pos = clipPos(pos);
1571
- var bm = new Bookmark(pos.ch);
1572
- getLine(pos.line).addMark(bm);
1573
- return bm;
1558
+ var marker = new TextMarker("bookmark"), line = getLine(pos.line);
1559
+ var span = {from: pos.ch, to: pos.ch, marker: marker};
1560
+ (line.markedSpans || (line.markedSpans = [])).push(span);
1561
+ marker.lines.push(line);
1562
+ return marker;
1574
1563
  }
1575
1564
 
1576
1565
  function findMarksAt(pos) {
1577
1566
  pos = clipPos(pos);
1578
- var markers = [], marked = getLine(pos.line).marked;
1579
- if (!marked) return markers;
1580
- for (var i = 0, e = marked.length; i < e; ++i) {
1581
- var m = marked[i];
1582
- if ((m.from == null || m.from <= pos.ch) &&
1583
- (m.to == null || m.to >= pos.ch))
1584
- markers.push(m.marker || m);
1567
+ var markers = [], spans = getLine(pos.line).markedSpans;
1568
+ if (spans) for (var i = 0; i < spans.length; ++i) {
1569
+ var span = spans[i];
1570
+ if ((span.from == null || span.from <= pos.ch) &&
1571
+ (span.to == null || span.to >= pos.ch))
1572
+ markers.push(span.marker);
1585
1573
  }
1586
1574
  return markers;
1587
1575
  }
@@ -1656,40 +1644,11 @@ window.CodeMirror = (function() {
1656
1644
  markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1657
1645
  }
1658
1646
 
1659
- // These are used to go from pixel positions to character
1660
- // positions, taking varying character widths into account.
1661
- function charFromX(line, x) {
1662
- if (x <= 0) return 0;
1663
- var lineObj = getLine(line), text = lineObj.text;
1664
- function getX(len) {
1665
- return measureLine(lineObj, len).left;
1666
- }
1667
- var from = 0, fromX = 0, to = text.length, toX;
1668
- // Guess a suitable upper bound for our search.
1669
- var estimated = Math.min(to, Math.ceil(x / charWidth()));
1670
- for (;;) {
1671
- var estX = getX(estimated);
1672
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1673
- else {toX = estX; to = estimated; break;}
1674
- }
1675
- if (x > toX) return to;
1676
- // Try to guess a suitable lower bound as well.
1677
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
1678
- if (estX < x) {from = estimated; fromX = estX;}
1679
- // Do a binary search between these bounds.
1680
- for (;;) {
1681
- if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1682
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1683
- if (middleX > x) {to = middle; toX = middleX;}
1684
- else {from = middle; fromX = middleX;}
1685
- }
1686
- }
1687
-
1688
1647
  function measureLine(line, ch) {
1689
1648
  if (ch == 0) return {top: 0, left: 0};
1690
1649
  var wbr = options.lineWrapping && ch < line.text.length &&
1691
1650
  spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1692
- var pre = line.getElement(makeTab, ch, wbr);
1651
+ var pre = lineContent(line, ch);
1693
1652
  removeChildrenAndAdd(measure, pre);
1694
1653
  var anchor = pre.anchor;
1695
1654
  var top = anchor.offsetTop, left = anchor.offsetLeft;
@@ -1800,6 +1759,7 @@ window.CodeMirror = (function() {
1800
1759
  var offL = eltOffset(lineSpace, true);
1801
1760
  return coordsChar(x - offL.left, y - offL.top);
1802
1761
  }
1762
+ var detectingSelectAll;
1803
1763
  function onContextMenu(e) {
1804
1764
  var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1805
1765
  if (!pos || opera) return; // Opera is difficult.
@@ -1811,19 +1771,30 @@ window.CodeMirror = (function() {
1811
1771
  input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1812
1772
  "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1813
1773
  "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1814
- leaveInputAlone = true;
1815
- var val = input.value = getSelection();
1816
1774
  focusInput();
1817
- selectInput(input);
1775
+ resetInput(true);
1776
+ // Adds "Select all" to context menu in FF
1777
+ if (posEq(sel.from, sel.to)) input.value = prevInput = " ";
1778
+
1818
1779
  function rehide() {
1819
- var newVal = splitLines(input.value).join("\n");
1820
- if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
1821
1780
  inputDiv.style.position = "relative";
1822
1781
  input.style.cssText = oldCSS;
1823
1782
  if (ie_lt9) scrollbar.scrollTop = scrollPos;
1824
- leaveInputAlone = false;
1825
- resetInput(true);
1826
1783
  slowPoll();
1784
+
1785
+ // Try to detect the user choosing select-all
1786
+ if (input.selectionStart != null) {
1787
+ clearTimeout(detectingSelectAll);
1788
+ var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0;
1789
+ prevInput = " ";
1790
+ input.selectionStart = 1; input.selectionEnd = extval.length;
1791
+ detectingSelectAll = setTimeout(function poll(){
1792
+ if (prevInput == " " && input.selectionStart == 0)
1793
+ operation(commands.selectAll)(instance);
1794
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1795
+ else resetInput();
1796
+ }, 200);
1797
+ }
1827
1798
  }
1828
1799
 
1829
1800
  if (gecko) {
@@ -1907,70 +1878,39 @@ window.CodeMirror = (function() {
1907
1878
  return minline;
1908
1879
  }
1909
1880
  function getStateBefore(n) {
1910
- var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1881
+ var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter;
1911
1882
  if (!state) state = startState(mode);
1912
1883
  else state = copyState(mode, state);
1913
- doc.iter(start, n, function(line) {
1914
- line.highlight(mode, state, options.tabSize);
1915
- line.stateAfter = copyState(mode, state);
1884
+ doc.iter(pos, n, function(line) {
1885
+ line.process(mode, state, options.tabSize);
1886
+ line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null;
1916
1887
  });
1917
- if (start < n) changes.push({from: start, to: n});
1918
- if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1919
1888
  return state;
1920
1889
  }
1921
- function highlightLines(start, end) {
1922
- var state = getStateBefore(start);
1923
- doc.iter(start, end, function(line) {
1924
- line.highlight(mode, state, options.tabSize);
1925
- line.stateAfter = copyState(mode, state);
1926
- });
1927
- }
1928
1890
  function highlightWorker() {
1929
- var end = +new Date + options.workTime;
1930
- var foundWork = work.length;
1931
- while (work.length) {
1932
- if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1933
- else var task = work.pop();
1934
- if (task >= doc.size) continue;
1935
- var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1936
- if (state) state = copyState(mode, state);
1937
- else state = startState(mode);
1938
-
1939
- var unchanged = 0, compare = mode.compareStates, realChange = false,
1940
- i = start, bail = false;
1941
- doc.iter(i, doc.size, function(line) {
1942
- var hadState = line.stateAfter;
1943
- if (+new Date > end) {
1944
- work.push(i);
1945
- startWorker(options.workDelay);
1946
- if (realChange) changes.push({from: task, to: i + 1});
1947
- return (bail = true);
1948
- }
1949
- var changed = line.highlight(mode, state, options.tabSize);
1950
- if (changed) realChange = true;
1891
+ if (frontier >= showingTo) return;
1892
+ var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier));
1893
+ var startFrontier = frontier;
1894
+ doc.iter(frontier, showingTo, function(line) {
1895
+ if (frontier >= showingFrom) { // Visible
1896
+ line.highlight(mode, state, options.tabSize);
1951
1897
  line.stateAfter = copyState(mode, state);
1952
- var done = null;
1953
- if (compare) {
1954
- var same = hadState && compare(hadState, state);
1955
- if (same != Pass) done = !!same;
1956
- }
1957
- if (done == null) {
1958
- if (changed !== false || !hadState) unchanged = 0;
1959
- else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1960
- done = true;
1961
- }
1962
- if (done) return true;
1963
- ++i;
1964
- });
1965
- if (bail) return;
1966
- if (realChange) changes.push({from: task, to: i + 1});
1967
- }
1968
- if (foundWork && options.onHighlightComplete)
1969
- options.onHighlightComplete(instance);
1898
+ } else {
1899
+ line.process(mode, state, options.tabSize);
1900
+ line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null;
1901
+ }
1902
+ ++frontier;
1903
+ if (+new Date > end) {
1904
+ startWorker(options.workDelay);
1905
+ return true;
1906
+ }
1907
+ });
1908
+ if (showingTo > startFrontier && frontier >= showingFrom)
1909
+ operation(function() {changes.push({from: startFrontier, to: frontier});})();
1970
1910
  }
1971
1911
  function startWorker(time) {
1972
- if (!work.length) return;
1973
- highlight.set(time, operation(highlightWorker));
1912
+ if (frontier < showingTo)
1913
+ highlight.set(time, highlightWorker);
1974
1914
  }
1975
1915
 
1976
1916
  // Operations are used to wrap changes in such a way that each
@@ -2005,8 +1945,7 @@ window.CodeMirror = (function() {
2005
1945
  if (newScrollPos) scrollCursorIntoView();
2006
1946
  if (selectionChanged) restartBlink();
2007
1947
 
2008
- if (focused && !leaveInputAlone &&
2009
- (updateInput === true || (updateInput !== false && selectionChanged)))
1948
+ if (focused && (updateInput === true || (updateInput !== false && selectionChanged)))
2010
1949
  resetInput(userSelChange);
2011
1950
 
2012
1951
  if (selectionChanged && options.matchBrackets)
@@ -2070,7 +2009,6 @@ window.CodeMirror = (function() {
2070
2009
  onCursorActivity: null,
2071
2010
  onViewportChange: null,
2072
2011
  onGutterClick: null,
2073
- onHighlightComplete: null,
2074
2012
  onUpdate: null,
2075
2013
  onFocus: null, onBlur: null, onScroll: null,
2076
2014
  matchBrackets: false,
@@ -2113,7 +2051,13 @@ window.CodeMirror = (function() {
2113
2051
  var spec = CodeMirror.resolveMode(spec);
2114
2052
  var mfactory = modes[spec.name];
2115
2053
  if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2116
- return mfactory(options, spec);
2054
+ var modeObj = mfactory(options, spec);
2055
+ if (modeExtensions.hasOwnProperty(spec.name)) {
2056
+ var exts = modeExtensions[spec.name];
2057
+ for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop];
2058
+ }
2059
+ modeObj.name = spec.name;
2060
+ return modeObj;
2117
2061
  };
2118
2062
  CodeMirror.listModes = function() {
2119
2063
  var list = [];
@@ -2133,6 +2077,13 @@ window.CodeMirror = (function() {
2133
2077
  extensions[name] = func;
2134
2078
  };
2135
2079
 
2080
+ var modeExtensions = CodeMirror.modeExtensions = {};
2081
+ CodeMirror.extendMode = function(mode, properties) {
2082
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2083
+ for (var prop in properties) if (properties.hasOwnProperty(prop))
2084
+ exts[prop] = properties[prop];
2085
+ };
2086
+
2136
2087
  var commands = CodeMirror.commands = {
2137
2088
  selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
2138
2089
  killLine: function(cm) {
@@ -2335,6 +2286,14 @@ window.CodeMirror = (function() {
2335
2286
  return mode.startState ? mode.startState(a1, a2) : true;
2336
2287
  }
2337
2288
  CodeMirror.startState = startState;
2289
+ CodeMirror.innerMode = function(mode, state) {
2290
+ while (mode.innerMode) {
2291
+ var info = mode.innerMode(state);
2292
+ state = info.state;
2293
+ mode = info.mode;
2294
+ }
2295
+ return info || {mode: mode, state: state};
2296
+ };
2338
2297
 
2339
2298
  // The character stream used by a mode's parser.
2340
2299
  function StringStream(string, tabSize) {
@@ -2383,6 +2342,7 @@ window.CodeMirror = (function() {
2383
2342
  }
2384
2343
  } else {
2385
2344
  var match = this.string.slice(this.pos).match(pattern);
2345
+ if (match && match.index > 0) return null;
2386
2346
  if (match && consume !== false) this.pos += match[0].length;
2387
2347
  return match;
2388
2348
  }
@@ -2391,69 +2351,123 @@ window.CodeMirror = (function() {
2391
2351
  };
2392
2352
  CodeMirror.StringStream = StringStream;
2393
2353
 
2394
- function MarkedText(from, to, className, marker) {
2395
- this.from = from; this.to = to; this.style = className; this.marker = marker;
2354
+ function MarkedSpan(from, to, marker) {
2355
+ this.from = from; this.to = to; this.marker = marker;
2396
2356
  }
2397
- MarkedText.prototype = {
2398
- attach: function(line) { this.marker.set.push(line); },
2399
- detach: function(line) {
2400
- var ix = indexOf(this.marker.set, line);
2401
- if (ix > -1) this.marker.set.splice(ix, 1);
2402
- },
2403
- split: function(pos, lenBefore) {
2404
- if (this.to <= pos && this.to != null) return null;
2405
- var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2406
- var to = this.to == null ? null : this.to - pos + lenBefore;
2407
- return new MarkedText(from, to, this.style, this.marker);
2408
- },
2409
- dup: function() { return new MarkedText(null, null, this.style, this.marker); },
2410
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2411
- if (fromOpen && to > this.from && (to < this.to || this.to == null))
2412
- this.from = null;
2413
- else if (this.from != null && this.from >= from)
2414
- this.from = Math.max(to, this.from) + diff;
2415
- if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2416
- this.to = null;
2417
- else if (this.to != null && this.to > from)
2418
- this.to = to < this.to ? this.to + diff : from;
2419
- },
2420
- isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2421
- sameSet: function(x) { return this.marker == x.marker; }
2422
- };
2423
2357
 
2424
- function Bookmark(pos) {
2425
- this.from = pos; this.to = pos; this.line = null;
2358
+ function getMarkedSpanFor(spans, marker, del) {
2359
+ if (spans) for (var i = 0; i < spans.length; ++i) {
2360
+ var span = spans[i];
2361
+ if (span.marker == marker) {
2362
+ if (del) spans.splice(i, 1);
2363
+ return span;
2364
+ }
2365
+ }
2426
2366
  }
2427
- Bookmark.prototype = {
2428
- attach: function(line) { this.line = line; },
2429
- detach: function(line) { if (this.line == line) this.line = null; },
2430
- split: function(pos, lenBefore) {
2431
- if (pos < this.from) {
2432
- this.from = this.to = (this.from - pos) + lenBefore;
2433
- return this;
2367
+
2368
+ function markedSpansBefore(old, startCh, endCh) {
2369
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2370
+ var span = old[i], marker = span.marker;
2371
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
2372
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) {
2373
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
2374
+ (nw || (nw = [])).push({from: span.from,
2375
+ to: endsAfter ? null : span.to,
2376
+ marker: marker});
2434
2377
  }
2435
- },
2436
- isDead: function() { return this.from > this.to; },
2437
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2438
- if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2439
- this.from = 0; this.to = -1;
2440
- } else if (this.from > from) {
2441
- this.from = this.to = Math.max(to, this.from) + diff;
2378
+ }
2379
+ return nw;
2380
+ }
2381
+
2382
+ function markedSpansAfter(old, endCh) {
2383
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
2384
+ var span = old[i], marker = span.marker;
2385
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
2386
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh) {
2387
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
2388
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
2389
+ to: span.to == null ? null : span.to - endCh,
2390
+ marker: marker});
2442
2391
  }
2443
- },
2444
- sameSet: function(x) { return false; },
2445
- find: function() {
2446
- if (!this.line || !this.line.parent) return null;
2447
- return {line: lineNo(this.line), ch: this.from};
2448
- },
2449
- clear: function() {
2450
- if (this.line) {
2451
- var found = indexOf(this.line.marked, this);
2452
- if (found != -1) this.line.marked.splice(found, 1);
2453
- this.line = null;
2392
+ }
2393
+ return nw;
2394
+ }
2395
+
2396
+ function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
2397
+ if (!oldFirst && !oldLast) return newText;
2398
+ // Get the spans that 'stick out' on both sides
2399
+ var first = markedSpansBefore(oldFirst, startCh);
2400
+ var last = markedSpansAfter(oldLast, endCh);
2401
+
2402
+ // Next, merge those two ends
2403
+ var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
2404
+ if (first) {
2405
+ // Fix up .to properties of first
2406
+ for (var i = 0; i < first.length; ++i) {
2407
+ var span = first[i];
2408
+ if (span.to == null) {
2409
+ var found = getMarkedSpanFor(last, span.marker);
2410
+ if (!found) span.to = startCh;
2411
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
2412
+ }
2454
2413
  }
2455
2414
  }
2456
- };
2415
+ if (last) {
2416
+ // Fix up .from in last (or move them into first in case of sameLine)
2417
+ for (var i = 0; i < last.length; ++i) {
2418
+ var span = last[i];
2419
+ if (span.to != null) span.to += offset;
2420
+ if (span.from == null) {
2421
+ var found = getMarkedSpanFor(first, span.marker);
2422
+ if (!found) {
2423
+ span.from = offset;
2424
+ if (sameLine) (first || (first = [])).push(span);
2425
+ }
2426
+ } else {
2427
+ span.from += offset;
2428
+ if (sameLine) (first || (first = [])).push(span);
2429
+ }
2430
+ }
2431
+ }
2432
+
2433
+ var newMarkers = [newHL(newText[0], first)];
2434
+ if (!sameLine) {
2435
+ // Fill gap with whole-line-spans
2436
+ var gap = newText.length - 2, gapMarkers;
2437
+ if (gap > 0 && first)
2438
+ for (var i = 0; i < first.length; ++i)
2439
+ if (first[i].to == null)
2440
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
2441
+ for (var i = 0; i < gap; ++i)
2442
+ newMarkers.push(newHL(newText[i+1], gapMarkers));
2443
+ newMarkers.push(newHL(lst(newText), last));
2444
+ }
2445
+ return newMarkers;
2446
+ }
2447
+
2448
+ // hl stands for history-line, a data structure that can be either a
2449
+ // string (line without markers) or a {text, markedSpans} object.
2450
+ function hlText(val) { return typeof val == "string" ? val : val.text; }
2451
+ function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; }
2452
+ function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
2453
+
2454
+ function detachMarkedSpans(line) {
2455
+ var spans = line.markedSpans;
2456
+ if (!spans) return;
2457
+ for (var i = 0; i < spans.length; ++i) {
2458
+ var lines = spans[i].marker.lines;
2459
+ var ix = indexOf(lines, line);
2460
+ lines.splice(ix, 1);
2461
+ }
2462
+ line.markedSpans = null;
2463
+ }
2464
+
2465
+ function attachMarkedSpans(line, spans) {
2466
+ if (!spans) return;
2467
+ for (var i = 0; i < spans.length; ++i)
2468
+ var marker = spans[i].marker.lines.push(line);
2469
+ line.markedSpans = spans;
2470
+ }
2457
2471
 
2458
2472
  // When measuring the position of the end of a line, different
2459
2473
  // browsers require different approaches. If an empty span is added,
@@ -2467,142 +2481,32 @@ window.CodeMirror = (function() {
2467
2481
 
2468
2482
  // Line objects. These hold state related to a line, including
2469
2483
  // highlighting info (the styles array).
2470
- function Line(text, styles) {
2471
- this.styles = styles || [text, null];
2484
+ function Line(text, markedSpans) {
2472
2485
  this.text = text;
2473
2486
  this.height = 1;
2487
+ attachMarkedSpans(this, markedSpans);
2474
2488
  }
2475
- Line.inheritMarks = function(text, orig) {
2476
- var ln = new Line(text), mk = orig && orig.marked;
2477
- if (mk) {
2478
- for (var i = 0; i < mk.length; ++i) {
2479
- if (mk[i].to == null && mk[i].style) {
2480
- var newmk = ln.marked || (ln.marked = []), mark = mk[i];
2481
- var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
2482
- }
2483
- }
2484
- }
2485
- return ln;
2486
- };
2487
2489
  Line.prototype = {
2488
- // Replace a piece of a line, keeping the styles around it intact.
2489
- replace: function(from, to_, text) {
2490
- var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
2491
- copyStyles(0, from, this.styles, st);
2492
- if (text) st.push(text, null);
2493
- copyStyles(to, this.text.length, this.styles, st);
2494
- this.styles = st;
2495
- this.text = this.text.slice(0, from) + text + this.text.slice(to);
2496
- this.stateAfter = null;
2497
- if (mk) {
2498
- var diff = text.length - (to - from);
2499
- for (var i = 0; i < mk.length; ++i) {
2500
- var mark = mk[i];
2501
- mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2502
- if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2503
- }
2504
- }
2505
- },
2506
- // Split a part off a line, keeping styles and markers intact.
2507
- split: function(pos, textBefore) {
2508
- var st = [textBefore, null], mk = this.marked;
2509
- copyStyles(pos, this.text.length, this.styles, st);
2510
- var taken = new Line(textBefore + this.text.slice(pos), st);
2511
- if (mk) {
2512
- for (var i = 0; i < mk.length; ++i) {
2513
- var mark = mk[i];
2514
- var newmark = mark.split(pos, textBefore.length);
2515
- if (newmark) {
2516
- if (!taken.marked) taken.marked = [];
2517
- taken.marked.push(newmark); newmark.attach(taken);
2518
- if (newmark == mark) mk.splice(i--, 1);
2519
- }
2520
- }
2521
- }
2522
- return taken;
2523
- },
2524
- append: function(line) {
2525
- var mylen = this.text.length, mk = line.marked, mymk = this.marked;
2526
- this.text += line.text;
2527
- copyStyles(0, line.text.length, line.styles, this.styles);
2528
- if (mymk) {
2529
- for (var i = 0; i < mymk.length; ++i)
2530
- if (mymk[i].to == null) mymk[i].to = mylen;
2531
- }
2532
- if (mk && mk.length) {
2533
- if (!mymk) this.marked = mymk = [];
2534
- outer: for (var i = 0; i < mk.length; ++i) {
2535
- var mark = mk[i];
2536
- if (!mark.from) {
2537
- for (var j = 0; j < mymk.length; ++j) {
2538
- var mymark = mymk[j];
2539
- if (mymark.to == mylen && mymark.sameSet(mark)) {
2540
- mymark.to = mark.to == null ? null : mark.to + mylen;
2541
- if (mymark.isDead()) {
2542
- mymark.detach(this);
2543
- mk.splice(i--, 1);
2544
- }
2545
- continue outer;
2546
- }
2547
- }
2548
- }
2549
- mymk.push(mark);
2550
- mark.attach(this);
2551
- mark.from += mylen;
2552
- if (mark.to != null) mark.to += mylen;
2553
- }
2554
- }
2555
- },
2556
- fixMarkEnds: function(other) {
2557
- var mk = this.marked, omk = other.marked;
2558
- if (!mk) return;
2559
- outer: for (var i = 0; i < mk.length; ++i) {
2560
- var mark = mk[i], close = mark.to == null;
2561
- if (close && omk) {
2562
- for (var j = 0; j < omk.length; ++j) {
2563
- var om = omk[j];
2564
- if (!om.sameSet(mark) || om.from != null) continue;
2565
- if (mark.from == this.text.length && om.to == 0) {
2566
- omk.splice(j, 1);
2567
- mk.splice(i--, 1);
2568
- continue outer;
2569
- } else {
2570
- close = false; break;
2571
- }
2572
- }
2573
- }
2574
- if (close) mark.to = this.text.length;
2575
- }
2576
- },
2577
- fixMarkStarts: function() {
2578
- var mk = this.marked;
2579
- if (!mk) return;
2580
- for (var i = 0; i < mk.length; ++i)
2581
- if (mk[i].from == null) mk[i].from = 0;
2582
- },
2583
- addMark: function(mark) {
2584
- mark.attach(this);
2585
- if (this.marked == null) this.marked = [];
2586
- this.marked.push(mark);
2587
- this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
2490
+ update: function(text, markedSpans) {
2491
+ this.text = text;
2492
+ this.stateAfter = this.styles = null;
2493
+ detachMarkedSpans(this);
2494
+ attachMarkedSpans(this, markedSpans);
2588
2495
  },
2589
2496
  // Run the given mode's parser over a line, update the styles
2590
2497
  // array, which contains alternating fragments of text and CSS
2591
2498
  // classes.
2592
2499
  highlight: function(mode, state, tabSize) {
2593
- var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
2594
- var changed = false, curWord = st[0], prevWord;
2500
+ var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []);
2501
+ var pos = st.length = 0;
2595
2502
  if (this.text == "" && mode.blankLine) mode.blankLine(state);
2596
2503
  while (!stream.eol()) {
2597
- var style = mode.token(stream, state);
2598
- var substr = this.text.slice(stream.start, stream.pos);
2504
+ var style = mode.token(stream, state), substr = stream.current();
2599
2505
  stream.start = stream.pos;
2600
- if (pos && st[pos-1] == style)
2506
+ if (pos && st[pos-1] == style) {
2601
2507
  st[pos-2] += substr;
2602
- else if (substr) {
2603
- if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
2508
+ } else if (substr) {
2604
2509
  st[pos++] = substr; st[pos++] = style;
2605
- prevWord = curWord; curWord = st[pos];
2606
2510
  }
2607
2511
  // Give up when line is ridiculously long
2608
2512
  if (stream.pos > 5000) {
@@ -2610,12 +2514,14 @@ window.CodeMirror = (function() {
2610
2514
  break;
2611
2515
  }
2612
2516
  }
2613
- if (st.length != pos) {st.length = pos; changed = true;}
2614
- if (pos && st[pos-2] != prevWord) changed = true;
2615
- // Short lines with simple highlights return null, and are
2616
- // counted as changed by the driver because they are likely to
2617
- // highlight the same way in various contexts.
2618
- return changed || (st.length < 5 && this.text.length < 10 ? null : false);
2517
+ },
2518
+ process: function(mode, state, tabSize) {
2519
+ var stream = new StringStream(this.text, tabSize);
2520
+ if (this.text == "" && mode.blankLine) mode.blankLine(state);
2521
+ while (!stream.eol() && stream.pos <= 5000) {
2522
+ mode.token(stream, state);
2523
+ stream.start = stream.pos;
2524
+ }
2619
2525
  },
2620
2526
  // Fetch the parser token for a given character. Useful for hacks
2621
2527
  // that want to inspect the mode state (say, for completion).
@@ -2634,7 +2540,7 @@ window.CodeMirror = (function() {
2634
2540
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2635
2541
  // Produces an HTML fragment for the line, taking selection,
2636
2542
  // marking, and highlighting into account.
2637
- getElement: function(makeTab, wrapAt, wrapWBR) {
2543
+ getContent: function(tabSize, wrapAt, compensateForWrapping) {
2638
2544
  var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
2639
2545
  var pre = elt("pre");
2640
2546
  function span_(html, text, style) {
@@ -2658,9 +2564,9 @@ window.CodeMirror = (function() {
2658
2564
  if (!m) break;
2659
2565
  pos += skipped + 1;
2660
2566
  if (m[0] == "\t") {
2661
- var tab = makeTab(col);
2662
- content.appendChild(tab.element.cloneNode(true));
2663
- col += tab.width;
2567
+ var tabWidth = tabSize - col % tabSize;
2568
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
2569
+ col += tabWidth;
2664
2570
  } else {
2665
2571
  var token = elt("span", "\u2022", "cm-invalidchar");
2666
2572
  token.title = "\\u" + m[0].charCodeAt(0).toString(16);
@@ -2681,7 +2587,7 @@ window.CodeMirror = (function() {
2681
2587
  if (wrapAt > outPos) {
2682
2588
  span_(html, text.slice(0, wrapAt - outPos), style);
2683
2589
  // See comment at the definition of spanAffectsWrapping
2684
- if (wrapWBR) html.appendChild(elt("wbr"));
2590
+ if (compensateForWrapping) html.appendChild(elt("wbr"));
2685
2591
  }
2686
2592
  html.appendChild(anchor);
2687
2593
  var cut = wrapAt - outPos;
@@ -2702,7 +2608,7 @@ window.CodeMirror = (function() {
2702
2608
  };
2703
2609
  }
2704
2610
 
2705
- var st = this.styles, allText = this.text, marked = this.marked;
2611
+ var st = this.styles, allText = this.text, marked = this.markedSpans;
2706
2612
  var len = allText.length;
2707
2613
  function styleToClass(style) {
2708
2614
  if (!style) return null;
@@ -2718,13 +2624,14 @@ window.CodeMirror = (function() {
2718
2624
  span(pre, str, styleToClass(style));
2719
2625
  }
2720
2626
  } else {
2627
+ marked.sort(function(a, b) { return a.from - b.from; });
2721
2628
  var pos = 0, i = 0, text = "", style, sg = 0;
2722
2629
  var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2723
2630
  var advanceMarks = function() {
2724
2631
  var m;
2725
2632
  while (markpos < marked.length &&
2726
2633
  ((m = marked[markpos]).from == pos || m.from == null)) {
2727
- if (m.style != null) marks.push(m);
2634
+ if (m.marker.type == "range") marks.push(m);
2728
2635
  ++markpos;
2729
2636
  }
2730
2637
  nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
@@ -2743,8 +2650,12 @@ window.CodeMirror = (function() {
2743
2650
  if (text) {
2744
2651
  var end = pos + text.length;
2745
2652
  var appliedStyle = style;
2746
- for (var j = 0; j < marks.length; ++j)
2747
- appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2653
+ for (var j = 0; j < marks.length; ++j) {
2654
+ var mark = marks[j];
2655
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style;
2656
+ if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle;
2657
+ if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle;
2658
+ }
2748
2659
  span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2749
2660
  if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2750
2661
  pos = end;
@@ -2757,24 +2668,9 @@ window.CodeMirror = (function() {
2757
2668
  },
2758
2669
  cleanUp: function() {
2759
2670
  this.parent = null;
2760
- if (this.marked)
2761
- for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
2671
+ detachMarkedSpans(this);
2762
2672
  }
2763
2673
  };
2764
- // Utility used by replace and split above
2765
- function copyStyles(from, to, source, dest) {
2766
- for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
2767
- var part = source[i], end = pos + part.length;
2768
- if (state == 0) {
2769
- if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2770
- if (end >= from) state = 1;
2771
- } else if (state == 1) {
2772
- if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2773
- else dest.push(part, source[i+1]);
2774
- }
2775
- pos = end;
2776
- }
2777
- }
2778
2674
 
2779
2675
  // Data structure that holds the sequence of lines.
2780
2676
  function LeafChunk(lines) {
@@ -2975,7 +2871,7 @@ window.CodeMirror = (function() {
2975
2871
  History.prototype = {
2976
2872
  addChange: function(start, added, old) {
2977
2873
  this.undone.length = 0;
2978
- var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2874
+ var time = +new Date, cur = lst(this.done), last = cur && lst(cur);
2979
2875
  var dtime = time - this.time;
2980
2876
 
2981
2877
  if (this.compound && cur && !this.closed) {
@@ -3121,10 +3017,19 @@ window.CodeMirror = (function() {
3121
3017
  return box;
3122
3018
  }
3123
3019
 
3124
- // Get a node's text content.
3125
3020
  function eltText(node) {
3126
3021
  return node.textContent || node.innerText || node.nodeValue || "";
3127
3022
  }
3023
+
3024
+ var spaceStrs = [""];
3025
+ function spaceStr(n) {
3026
+ while (spaceStrs.length <= n)
3027
+ spaceStrs.push(lst(spaceStrs) + " ");
3028
+ return spaceStrs[n];
3029
+ }
3030
+
3031
+ function lst(arr) { return arr[arr.length-1]; }
3032
+
3128
3033
  function selectInput(node) {
3129
3034
  if (ios) { // Mobile Safari apparently has a bug where select() is broken.
3130
3035
  node.selectionStart = 0;
@@ -3158,7 +3063,6 @@ window.CodeMirror = (function() {
3158
3063
  e.appendChild(document.createTextNode(str));
3159
3064
  } else e.textContent = str;
3160
3065
  }
3161
- CodeMirror.setTextContent = setTextContent;
3162
3066
 
3163
3067
  // Used to position the cursor after an undo/redo by finding the
3164
3068
  // last edited character.
@@ -3233,5 +3137,7 @@ window.CodeMirror = (function() {
3233
3137
  for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
3234
3138
  })();
3235
3139
 
3140
+ CodeMirror.version = "2.34";
3141
+
3236
3142
  return CodeMirror;
3237
3143
  })();