codemirror-rails 0.3.1 → 0.3.2
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.
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/README.md +18 -7
- data/codemirror-rails.gemspec +2 -0
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +852 -399
- data/vendor/assets/javascripts/codemirror/modes/gfm.js +108 -0
- data/vendor/assets/javascripts/codemirror/modes/groovy.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +8 -2
- data/vendor/assets/javascripts/codemirror/modes/lua.js +1 -1
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +43 -31
- data/vendor/assets/javascripts/codemirror/modes/perl.js +10 -4
- data/vendor/assets/javascripts/codemirror/modes/rpm-changes.js +19 -0
- data/vendor/assets/javascripts/codemirror/modes/rpm-spec.js +66 -0
- data/vendor/assets/javascripts/codemirror/modes/rust.js +38 -22
- data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +10 -0
- data/vendor/assets/javascripts/codemirror/modes/xml.js +2 -1
- data/vendor/assets/stylesheets/codemirror.css +9 -0
- data/vendor/assets/stylesheets/codemirror/modes/rpm-spec.css +5 -0
- data/vendor/assets/stylesheets/codemirror/themes/default.css +1 -1
- data/vendor/assets/stylesheets/codemirror/themes/monokai.css +27 -0
- data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +20 -0
- metadata +21 -3
data/.gitignore
CHANGED
data/Gemfile
ADDED
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
|
-
|
10
|
+
```ruby
|
11
|
+
gem 'codemirror-rails'
|
12
|
+
```
|
11
13
|
|
12
14
|
Or manually install the codemirror-rails gem:
|
13
15
|
|
14
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
51
|
+
```shell
|
52
|
+
rails generate codemirror:install
|
53
|
+
```
|
data/codemirror-rails.gemspec
CHANGED
@@ -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"
|
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"> </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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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.
|
59
|
-
//
|
60
|
-
//
|
61
|
-
|
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 <
|
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 == "
|
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
|
157
|
-
},
|
169
|
+
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
|
170
|
+
}),
|
158
171
|
getStateAfter: function(line) {
|
159
|
-
line = clipLine(line == null ?
|
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
|
-
|
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,
|
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
|
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
|
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:
|
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(
|
228
|
-
var
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
}
|
235
|
-
return clipPos({line:
|
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:
|
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
|
-
|
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
|
-
//
|
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
|
-
|
365
|
-
|
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
|
-
|
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
|
-
|
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:
|
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
|
-
|
512
|
-
|
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 =
|
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
|
-
|
526
|
-
|
527
|
-
|
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
|
-
|
568
|
+
doc.remove(from.line + 1, nlines);
|
535
569
|
}
|
536
570
|
else {
|
537
|
-
var
|
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
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
}
|
552
|
-
}
|
553
|
-
|
554
|
-
|
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;
|
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
|
-
|
572
|
-
|
573
|
-
|
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 = (
|
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
|
627
|
-
var code = [
|
628
|
-
|
629
|
-
code.push(
|
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(
|
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(
|
738
|
-
|
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
|
-
|
742
|
-
startch += lineSep.length +
|
743
|
-
|
744
|
-
|
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
|
-
|
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 =
|
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 =
|
787
|
-
|
788
|
-
|
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
|
-
//
|
799
|
-
|
800
|
-
|
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,
|
807
|
-
|
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
|
-
|
821
|
-
//
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
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
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
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
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
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 (
|
891
|
-
}
|
892
|
-
|
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
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
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
|
-
|
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
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
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> ");
|
995
|
+
html.push("</pre>");
|
996
|
+
}
|
997
|
+
++i;
|
998
|
+
});
|
958
999
|
gutter.style.display = "none";
|
959
1000
|
gutterText.innerHTML = html.join("");
|
960
|
-
var minwidth = String(
|
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 =
|
969
|
-
var
|
970
|
-
var
|
971
|
-
inputDiv.style.top = Math.max(Math.min(
|
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 =
|
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,
|
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 >=
|
1037
|
-
var ch = pos.ch, linelen =
|
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 /
|
1045
|
-
|
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:
|
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 =
|
1053
|
-
setSelection({line: 0, ch: 0}, {line: endLine, ch:
|
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 =
|
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:
|
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,
|
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 =
|
1170
|
+
var line = getLine(n), curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
1107
1171
|
if (how == "prev") {
|
1108
|
-
if (n) 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
|
-
|
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
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
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:
|
1159
|
-
return new TextMarker(set);
|
1225
|
+
changes.push({from: 0, to: doc.size});
|
1160
1226
|
}
|
1161
1227
|
|
1162
|
-
function TextMarker(
|
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
|
-
|
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:
|
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 =
|
1183
|
-
if (found
|
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 =
|
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 =
|
1291
|
+
if (typeof line == "number") line = getLine(clipLine(line));
|
1202
1292
|
line.gutterMarker = null;
|
1203
1293
|
gutterDirty = true;
|
1204
1294
|
}
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
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 =
|
1329
|
+
line = getLine(line);
|
1225
1330
|
if (!line) return null;
|
1226
1331
|
}
|
1227
1332
|
else {
|
1228
|
-
var n =
|
1229
|
-
if (n ==
|
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,
|
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 =
|
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 /
|
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 =
|
1277
|
-
|
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
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
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
|
-
|
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 =
|
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,
|
1380
|
-
var 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 =
|
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 &&
|
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
|
-
|
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 <
|
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
|
-
|
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 (!
|
1608
|
+
if (!getLine(showingFrom).stateAfter) var task = showingFrom;
|
1438
1609
|
else var task = work.pop();
|
1439
|
-
if (task >=
|
1440
|
-
var start = findStartLine(task), state = start &&
|
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
|
-
|
1446
|
-
|
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))
|
1629
|
+
if (hadState && compare(hadState, state)) return true;
|
1458
1630
|
} else {
|
1459
1631
|
if (changed !== false || !hadState) unchanged = 0;
|
1460
|
-
else if (++unchanged > 3)
|
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 =
|
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 =
|
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(
|
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(
|
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 ==
|
1575
|
-
line = fold(
|
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:
|
1785
|
+
pos = {line: pos.line-1, ch: getLine(pos.line-1).text.length};
|
1612
1786
|
}
|
1613
1787
|
else {
|
1614
|
-
if (pos.line ==
|
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.
|
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.
|
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
|
-
|
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)
|
1857
|
-
var
|
1858
|
-
|
1859
|
-
|
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
|
-
|
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
|
-
|
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 (
|
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.
|
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.
|
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
|
-
|
1925
|
-
|
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(
|
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 =
|
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 "
|
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
|
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
|
2191
|
-
var x = 0, y = 0,
|
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
|
-
|
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
|
-
|
2629
|
+
skipBody = true;
|
2196
2630
|
}
|
2197
|
-
var e = screen && !
|
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 (
|
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
|