##// END OF EJS Templates
codemirror: updated to version 4.7...
Christian Oyarzun -
r4563:c384703b default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,19 b''
1 Copyright (C) 2014 by Marijn Haverbeke <marijnh@gmail.com> and others
2
3 Permission is hereby granted, free of charge, to any person obtaining a copy
4 of this software and associated documentation files (the "Software"), to deal
5 in the Software without restriction, including without limitation the rights
6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 copies of the Software, and to permit persons to whom the Software is
8 furnished to do so, subject to the following conditions:
9
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 THE SOFTWARE.
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -43,9 +43,9 b' Codemirror'
43 ----------
43 ----------
44
44
45 Kallithea incorporates parts of the Javascript system called
45 Kallithea incorporates parts of the Javascript system called
46 [Codemirror](http://codemirror.net/), which is primarily:
46 [Codemirror](http://codemirror.net/), version 4.7.0, which is primarily:
47
47
48 Copyright &copy; 2013 by Marijn Haverbeke <marijnh@gmail.com>
48 Copyright &copy; 2013-2014 by Marijn Haverbeke <marijnh@gmail.com>
49
49
50 and licensed under the MIT-permissive license, which is
50 and licensed under the MIT-permissive license, which is
51 [included in this distribution](MIT-Permissive-License.txt).
51 [included in this distribution](MIT-Permissive-License.txt).
@@ -36,13 +36,17 b''
36 min-width: 20px;
36 min-width: 20px;
37 text-align: right;
37 text-align: right;
38 color: #999;
38 color: #999;
39 -moz-box-sizing: content-box;
40 box-sizing: content-box;
39 }
41 }
40
42
43 .CodeMirror-guttermarker { color: black; }
44 .CodeMirror-guttermarker-subtle { color: #999; }
45
41 /* CURSOR */
46 /* CURSOR */
42
47
43 .CodeMirror div.CodeMirror-cursor {
48 .CodeMirror div.CodeMirror-cursor {
44 border-left: 1px solid black;
49 border-left: 1px solid black;
45 z-index: 3;
46 }
50 }
47 /* Shown when moving in bi-directional text */
51 /* Shown when moving in bi-directional text */
48 .CodeMirror div.CodeMirror-secondarycursor {
52 .CodeMirror div.CodeMirror-secondarycursor {
@@ -52,12 +56,43 b''
52 width: auto;
56 width: auto;
53 border: 0;
57 border: 0;
54 background: #7e7;
58 background: #7e7;
59 }
60 .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursors {
55 z-index: 1;
61 z-index: 1;
56 }
62 }
63
64 .cm-animate-fat-cursor {
65 width: auto;
66 border: 0;
67 -webkit-animation: blink 1.06s steps(1) infinite;
68 -moz-animation: blink 1.06s steps(1) infinite;
69 animation: blink 1.06s steps(1) infinite;
70 }
71 @-moz-keyframes blink {
72 0% { background: #7e7; }
73 50% { background: none; }
74 100% { background: #7e7; }
75 }
76 @-webkit-keyframes blink {
77 0% { background: #7e7; }
78 50% { background: none; }
79 100% { background: #7e7; }
80 }
81 @keyframes blink {
82 0% { background: #7e7; }
83 50% { background: none; }
84 100% { background: #7e7; }
85 }
86
57 /* Can style cursor different in overwrite (non-insert) mode */
87 /* Can style cursor different in overwrite (non-insert) mode */
58 .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
88 div.CodeMirror-overwrite div.CodeMirror-cursor {}
89
90 .cm-tab { display: inline-block; text-decoration: inherit; }
59
91
60 .cm-tab { display: inline-block; }
92 .CodeMirror-ruler {
93 border-left: 1px solid #ccc;
94 position: absolute;
95 }
61
96
62 /* DEFAULT THEME */
97 /* DEFAULT THEME */
63
98
@@ -65,16 +100,16 b''
65 .cm-s-default .cm-atom {color: #219;}
100 .cm-s-default .cm-atom {color: #219;}
66 .cm-s-default .cm-number {color: #164;}
101 .cm-s-default .cm-number {color: #164;}
67 .cm-s-default .cm-def {color: #00f;}
102 .cm-s-default .cm-def {color: #00f;}
68 .cm-s-default .cm-variable {color: black;}
103 .cm-s-default .cm-variable,
104 .cm-s-default .cm-punctuation,
105 .cm-s-default .cm-property,
106 .cm-s-default .cm-operator {}
69 .cm-s-default .cm-variable-2 {color: #05a;}
107 .cm-s-default .cm-variable-2 {color: #05a;}
70 .cm-s-default .cm-variable-3 {color: #085;}
108 .cm-s-default .cm-variable-3 {color: #085;}
71 .cm-s-default .cm-property {color: black;}
72 .cm-s-default .cm-operator {color: black;}
73 .cm-s-default .cm-comment {color: #a50;}
109 .cm-s-default .cm-comment {color: #a50;}
74 .cm-s-default .cm-string {color: #a11;}
110 .cm-s-default .cm-string {color: #a11;}
75 .cm-s-default .cm-string-2 {color: #f50;}
111 .cm-s-default .cm-string-2 {color: #f50;}
76 .cm-s-default .cm-meta {color: #555;}
112 .cm-s-default .cm-meta {color: #555;}
77 .cm-s-default .cm-error {color: #f00;}
78 .cm-s-default .cm-qualifier {color: #555;}
113 .cm-s-default .cm-qualifier {color: #555;}
79 .cm-s-default .cm-builtin {color: #30a;}
114 .cm-s-default .cm-builtin {color: #30a;}
80 .cm-s-default .cm-bracket {color: #997;}
115 .cm-s-default .cm-bracket {color: #997;}
@@ -91,10 +126,15 b''
91 .cm-em {font-style: italic;}
126 .cm-em {font-style: italic;}
92 .cm-link {text-decoration: underline;}
127 .cm-link {text-decoration: underline;}
93
128
129 .cm-s-default .cm-error {color: #f00;}
94 .cm-invalidchar {color: #f00;}
130 .cm-invalidchar {color: #f00;}
95
131
132 /* Default styles for common addons */
133
96 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
134 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
97 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
135 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
136 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
137 .CodeMirror-activeline-background {background: #e8f2ff;}
98
138
99 /* STOP */
139 /* STOP */
100
140
@@ -113,13 +153,18 b' div.CodeMirror span.CodeMirror-nonmatchi'
113 /* 30px is the magic margin used to hide the element's real scrollbars */
153 /* 30px is the magic margin used to hide the element's real scrollbars */
114 /* See overflow: hidden in .CodeMirror */
154 /* See overflow: hidden in .CodeMirror */
115 margin-bottom: -30px; margin-right: -30px;
155 margin-bottom: -30px; margin-right: -30px;
116 padding-bottom: 30px; padding-right: 30px;
156 padding-bottom: 30px;
117 height: 100%;
157 height: 100%;
118 outline: none; /* Prevent dragging from highlighting the element */
158 outline: none; /* Prevent dragging from highlighting the element */
119 position: relative;
159 position: relative;
160 -moz-box-sizing: content-box;
161 box-sizing: content-box;
120 }
162 }
121 .CodeMirror-sizer {
163 .CodeMirror-sizer {
122 position: relative;
164 position: relative;
165 border-right: 30px solid transparent;
166 -moz-box-sizing: content-box;
167 box-sizing: content-box;
123 }
168 }
124
169
125 /* The fake, visible scrollbars. Used to force redraw during scrolling
170 /* The fake, visible scrollbars. Used to force redraw during scrolling
@@ -155,6 +200,8 b' div.CodeMirror span.CodeMirror-nonmatchi'
155 .CodeMirror-gutter {
200 .CodeMirror-gutter {
156 white-space: normal;
201 white-space: normal;
157 height: 100%;
202 height: 100%;
203 -moz-box-sizing: content-box;
204 box-sizing: content-box;
158 padding-bottom: 30px;
205 padding-bottom: 30px;
159 margin-bottom: -32px;
206 margin-bottom: -32px;
160 display: inline-block;
207 display: inline-block;
@@ -170,6 +217,7 b' div.CodeMirror span.CodeMirror-nonmatchi'
170
217
171 .CodeMirror-lines {
218 .CodeMirror-lines {
172 cursor: text;
219 cursor: text;
220 min-height: 1px; /* prevents collapsing before first draw */
173 }
221 }
174 .CodeMirror pre {
222 .CodeMirror pre {
175 /* Reset some styles that the rest of the page might have set */
223 /* Reset some styles that the rest of the page might have set */
@@ -192,16 +240,7 b' div.CodeMirror span.CodeMirror-nonmatchi'
192 white-space: pre-wrap;
240 white-space: pre-wrap;
193 word-break: normal;
241 word-break: normal;
194 }
242 }
195 .CodeMirror-code pre {
243
196 border-right: 30px solid transparent;
197 width: -webkit-fit-content;
198 width: -moz-fit-content;
199 width: fit-content;
200 }
201 .CodeMirror-wrap .CodeMirror-code pre {
202 border-right: none;
203 width: auto;
204 }
205 .CodeMirror-linebackground {
244 .CodeMirror-linebackground {
206 position: absolute;
245 position: absolute;
207 left: 0; right: 0; top: 0; bottom: 0;
246 left: 0; right: 0; top: 0; bottom: 0;
@@ -214,8 +253,7 b' div.CodeMirror span.CodeMirror-nonmatchi'
214 overflow: auto;
253 overflow: auto;
215 }
254 }
216
255
217 .CodeMirror-widget {
256 .CodeMirror-widget {}
218 }
219
257
220 .CodeMirror-wrap .CodeMirror-scroll {
258 .CodeMirror-wrap .CodeMirror-scroll {
221 overflow-x: hidden;
259 overflow-x: hidden;
@@ -223,7 +261,8 b' div.CodeMirror span.CodeMirror-nonmatchi'
223
261
224 .CodeMirror-measure {
262 .CodeMirror-measure {
225 position: absolute;
263 position: absolute;
226 width: 100%; height: 0px;
264 width: 100%;
265 height: 0;
227 overflow: hidden;
266 overflow: hidden;
228 visibility: hidden;
267 visibility: hidden;
229 }
268 }
@@ -231,16 +270,22 b' div.CodeMirror span.CodeMirror-nonmatchi'
231
270
232 .CodeMirror div.CodeMirror-cursor {
271 .CodeMirror div.CodeMirror-cursor {
233 position: absolute;
272 position: absolute;
234 visibility: hidden;
235 border-right: none;
273 border-right: none;
236 width: 0;
274 width: 0;
237 }
275 }
238 .CodeMirror-focused div.CodeMirror-cursor {
276
277 div.CodeMirror-cursors {
278 visibility: hidden;
279 position: relative;
280 z-index: 3;
281 }
282 .CodeMirror-focused div.CodeMirror-cursors {
239 visibility: visible;
283 visibility: visible;
240 }
284 }
241
285
242 .CodeMirror-selected { background: #d9d9d9; }
286 .CodeMirror-selected { background: #d9d9d9; }
243 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
287 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
288 .CodeMirror-crosshair { cursor: crosshair; }
244
289
245 .cm-searching {
290 .cm-searching {
246 background: #ffa;
291 background: #ffa;
@@ -250,9 +295,15 b' div.CodeMirror span.CodeMirror-nonmatchi'
250 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
295 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
251 .CodeMirror span { *vertical-align: text-bottom; }
296 .CodeMirror span { *vertical-align: text-bottom; }
252
297
298 /* Used to force a border model for a node */
299 .cm-force-border { padding-right: .1px; }
300
253 @media print {
301 @media print {
254 /* Hide the cursor when printing */
302 /* Hide the cursor when printing */
255 .CodeMirror div.CodeMirror-cursor {
303 .CodeMirror div.CodeMirror-cursors {
256 visibility: hidden;
304 visibility: hidden;
257 }
305 }
258 }
306 }
307
308 /* Help users use markselection to safely style text background */
309 span.CodeMirror-selectedtext { background: none; }
This diff has been collapsed as it changes many lines, (6399 lines changed) Show them Hide them
@@ -1,24 +1,39 b''
1 // CodeMirror version 3.15
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM.
2 //
6 //
3 // CodeMirror is the only global var we claim
7 // You can find some technical background for some of the code below
4 window.CodeMirror = (function() {
8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
10 (function(mod) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS
12 module.exports = mod();
13 else if (typeof define == "function" && define.amd) // AMD
14 return define([], mod);
15 else // Plain browser env
16 this.CodeMirror = mod();
17 })(function() {
5 "use strict";
18 "use strict";
6
19
7 // BROWSER SNIFFING
20 // BROWSER SNIFFING
8
21
9 // Crude, but necessary to handle a number of hard-to-feature-detect
22 // Kludges for bugs and behavior differences that can't be feature
10 // bugs and behavior differences.
23 // detected are enabled based on userAgent etc sniffing.
24
11 var gecko = /gecko\/\d/i.test(navigator.userAgent);
25 var gecko = /gecko\/\d/i.test(navigator.userAgent);
12 var ie = /MSIE \d/.test(navigator.userAgent);
26 // ie_uptoN means Internet Explorer version N or lower
13 var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
27 var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
14 var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
28 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
29 var ie = ie_upto10 || ie_11up;
30 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
15 var webkit = /WebKit\//.test(navigator.userAgent);
31 var webkit = /WebKit\//.test(navigator.userAgent);
16 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
32 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17 var chrome = /Chrome\//.test(navigator.userAgent);
33 var chrome = /Chrome\//.test(navigator.userAgent);
18 var opera = /Opera\//.test(navigator.userAgent);
34 var presto = /Opera\//.test(navigator.userAgent);
19 var safari = /Apple Computer/.test(navigator.vendor);
35 var safari = /Apple Computer/.test(navigator.vendor);
20 var khtml = /KHTML\//.test(navigator.userAgent);
36 var khtml = /KHTML\//.test(navigator.userAgent);
21 var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
22 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
37 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
23 var phantom = /PhantomJS/.test(navigator.userAgent);
38 var phantom = /PhantomJS/.test(navigator.userAgent);
24
39
@@ -26,153 +41,184 b' window.CodeMirror = (function() {'
26 // This is woefully incomplete. Suggestions for alternative methods welcome.
41 // This is woefully incomplete. Suggestions for alternative methods welcome.
27 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
42 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28 var mac = ios || /Mac/.test(navigator.platform);
43 var mac = ios || /Mac/.test(navigator.platform);
29 var windows = /windows/i.test(navigator.platform);
44 var windows = /win/i.test(navigator.platform);
30
45
31 var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
46 var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32 if (opera_version) opera_version = Number(opera_version[1]);
47 if (presto_version) presto_version = Number(presto_version[1]);
33 if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
48 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
34 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
49 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
35 var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
50 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
36 var captureMiddleClick = gecko || (ie && !ie_lt9);
51 var captureRightClick = gecko || (ie && ie_version >= 9);
37
52
38 // Optimize some code when these features are not used
53 // Optimize some code when these features are not used.
39 var sawReadOnlySpans = false, sawCollapsedSpans = false;
54 var sawReadOnlySpans = false, sawCollapsedSpans = false;
40
55
41 // CONSTRUCTOR
56 // EDITOR CONSTRUCTOR
57
58 // A CodeMirror instance represents an editor. This is the object
59 // that user code is usually dealing with.
42
60
43 function CodeMirror(place, options) {
61 function CodeMirror(place, options) {
44 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
62 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
45
63
46 this.options = options = options || {};
64 this.options = options = options ? copyObj(options) : {};
47 // Determine effective options based on given values and defaults.
65 // Determine effective options based on given values and defaults.
48 for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
66 copyObj(defaults, options, false);
49 options[opt] = defaults[opt];
50 setGuttersForLineNumbers(options);
67 setGuttersForLineNumbers(options);
51
68
52 var docStart = typeof options.value == "string" ? 0 : options.value.first;
69 var doc = options.value;
53 var display = this.display = makeDisplay(place, docStart);
70 if (typeof doc == "string") doc = new Doc(doc, options.mode);
71 this.doc = doc;
72
73 var display = this.display = new Display(place, doc);
54 display.wrapper.CodeMirror = this;
74 display.wrapper.CodeMirror = this;
55 updateGutters(this);
75 updateGutters(this);
56 if (options.autofocus && !mobile) focusInput(this);
57
58 this.state = {keyMaps: [],
59 overlays: [],
60 modeGen: 0,
61 overwrite: false, focused: false,
62 suppressEdits: false, pasteIncoming: false,
63 draggingText: false,
64 highlight: new Delayed()};
65
66 themeChanged(this);
76 themeChanged(this);
67 if (options.lineWrapping)
77 if (options.lineWrapping)
68 this.display.wrapper.className += " CodeMirror-wrap";
78 this.display.wrapper.className += " CodeMirror-wrap";
69
79 if (options.autofocus && !mobile) focusInput(this);
70 var doc = options.value;
80
71 if (typeof doc == "string") doc = new Doc(options.value, options.mode);
81 this.state = {
72 operation(this, attachDoc)(this, doc);
82 keyMaps: [], // stores maps added by addKeyMap
83 overlays: [], // highlighting overlays, as added by addOverlay
84 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
85 overwrite: false, focused: false,
86 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
87 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
88 draggingText: false,
89 highlight: new Delayed() // stores highlight worker timeout
90 };
73
91
74 // Override magic textarea content restore that IE sometimes does
92 // Override magic textarea content restore that IE sometimes does
75 // on our hidden textarea on reload
93 // on our hidden textarea on reload
76 if (ie) setTimeout(bind(resetInput, this, true), 20);
94 if (ie && ie_version < 11) setTimeout(bind(resetInput, this, true), 20);
77
95
78 registerEventHandlers(this);
96 registerEventHandlers(this);
79 // IE throws unspecified error in certain cases, when
97 ensureGlobalHandlers();
80 // trying to access activeElement before onload
98
81 var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
99 startOperation(this);
82 if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
100 this.curOp.forceUpdate = true;
83 else onBlur(this);
101 attachDoc(this, doc);
84
102
85 operation(this, function() {
103 if ((options.autofocus && !mobile) || activeElt() == display.input)
86 for (var opt in optionHandlers)
104 setTimeout(bind(onFocus, this), 20);
87 if (optionHandlers.propertyIsEnumerable(opt))
105 else
88 optionHandlers[opt](this, options[opt], Init);
106 onBlur(this);
89 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
107
90 })();
108 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
109 optionHandlers[opt](this, options[opt], Init);
110 maybeUpdateLineNumberWidth(this);
111 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
112 endOperation(this);
91 }
113 }
92
114
93 // DISPLAY CONSTRUCTOR
115 // DISPLAY CONSTRUCTOR
94
116
95 function makeDisplay(place, docStart) {
117 // The display handles the DOM integration, both for input reading
96 var d = {};
118 // and content drawing. It holds references to DOM nodes and
97
119 // display-related state.
98 var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
120
121 function Display(place, doc) {
122 var d = this;
123
124 // The semihidden textarea that is focused when the editor is
125 // focused, and receives input.
126 var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
127 // The textarea is kept positioned near the cursor to prevent the
128 // fact that it'll be scrolled into view on input from scrolling
129 // our fake cursor out of view. On webkit, when wrap=off, paste is
130 // very slow. So make the area wide instead.
99 if (webkit) input.style.width = "1000px";
131 if (webkit) input.style.width = "1000px";
100 else input.setAttribute("wrap", "off");
132 else input.setAttribute("wrap", "off");
101 // if border: 0; -- iOS fails to open keyboard (issue #1287)
133 // If border: 0; -- iOS fails to open keyboard (issue #1287)
102 if (ios) input.style.border = "1px solid black";
134 if (ios) input.style.border = "1px solid black";
103 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
135 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
104
136
105 // Wraps and hides input textarea
137 // Wraps and hides input textarea
106 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
138 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
107 // The actual fake scrollbars.
139 // The fake scrollbar elements.
108 d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
140 d.scrollbarH = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
109 d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
141 d.scrollbarV = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
142 // Covers bottom-right square when both scrollbars are present.
110 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
143 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
144 // Covers bottom of gutter when coverGutterNextToScrollbar is on
145 // and h scrollbar is present.
111 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
146 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
112 // DIVs containing the selection and the actual code
147 // Will contain the actual code, positioned to cover the viewport.
113 d.lineDiv = elt("div", null, "CodeMirror-code");
148 d.lineDiv = elt("div", null, "CodeMirror-code");
149 // Elements are added to these to represent selection and cursors.
114 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
150 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
115 // Blinky cursor, and element used to ensure cursor fits at the end of a line
151 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
116 d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
152 // A visibility: hidden element used to find the size of things.
117 // Secondary cursor, shown when on a 'jump' in bi-directional text
118 d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
119 // Used to measure text size
120 d.measure = elt("div", null, "CodeMirror-measure");
153 d.measure = elt("div", null, "CodeMirror-measure");
154 // When lines outside of the viewport are measured, they are drawn in this.
155 d.lineMeasure = elt("div", null, "CodeMirror-measure");
121 // Wraps everything that needs to exist inside the vertically-padded coordinate system
156 // Wraps everything that needs to exist inside the vertically-padded coordinate system
122 d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
157 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
123 null, "position: relative; outline: none");
158 null, "position: relative; outline: none");
124 // Moved around its parent to cover visible view
159 // Moved around its parent to cover visible view.
125 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
160 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
126 // Set to the height of the text, causes scrolling
161 // Set to the height of the document, allowing scrolling.
127 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
162 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
128 // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
163 // Behavior of elts with overflow: auto and padding is
164 // inconsistent across browsers. This is used to ensure the
165 // scrollable area is big enough.
129 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
166 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
130 // Will contain the gutters, if any
167 // Will contain the gutters, if any.
131 d.gutters = elt("div", null, "CodeMirror-gutters");
168 d.gutters = elt("div", null, "CodeMirror-gutters");
132 d.lineGutter = null;
169 d.lineGutter = null;
133 // Provides scrolling
170 // Actual scrollable element.
134 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
171 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
135 d.scroller.setAttribute("tabIndex", "-1");
172 d.scroller.setAttribute("tabIndex", "-1");
136 // The element in which the editor lives.
173 // The element in which the editor lives.
137 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
174 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
138 d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
175 d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
139 // Work around IE7 z-index bug
176
140 if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
177 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
141 if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
178 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
142
143 // Needed to hide big blue blinking cursor on Mobile Safari
179 // Needed to hide big blue blinking cursor on Mobile Safari
144 if (ios) input.style.width = "0px";
180 if (ios) input.style.width = "0px";
145 if (!webkit) d.scroller.draggable = true;
181 if (!webkit) d.scroller.draggable = true;
146 // Needed to handle Tab key in KHTML
182 // Needed to handle Tab key in KHTML
147 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
183 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
148 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
184 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
149 else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
185 if (ie && ie_version < 8) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px";
150
186
151 // Current visible range (may be bigger than the view window).
187 if (place.appendChild) place.appendChild(d.wrapper);
152 d.viewOffset = d.lastSizeC = 0;
188 else place(d.wrapper);
153 d.showingFrom = d.showingTo = docStart;
189
190 // Current rendered range (may be bigger than the view window).
191 d.viewFrom = d.viewTo = doc.first;
192 // Information about the rendered lines.
193 d.view = [];
194 // Holds info about a single rendered line when it was rendered
195 // for measurement, while not in view.
196 d.externalMeasured = null;
197 // Empty space (in pixels) above the view
198 d.viewOffset = 0;
199 d.lastSizeC = 0;
200 d.updateLineNumbers = null;
154
201
155 // Used to only resize the line number gutter when necessary (when
202 // Used to only resize the line number gutter when necessary (when
156 // the amount of lines crosses a boundary that makes its width change)
203 // the amount of lines crosses a boundary that makes its width change)
157 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
204 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
158 // See readInput and resetInput
205 // See readInput and resetInput
159 d.prevInput = "";
206 d.prevInput = "";
160 // Set to true when a non-horizontal-scrolling widget is added. As
207 // Set to true when a non-horizontal-scrolling line widget is
161 // an optimization, widget aligning is skipped when d is false.
208 // added. As an optimization, line widget aligning is skipped when
209 // this is false.
162 d.alignWidgets = false;
210 d.alignWidgets = false;
163 // Flag that indicates whether we currently expect input to appear
211 // Flag that indicates whether we expect input to appear real soon
164 // (after some event like 'keypress' or 'input') and are polling
212 // now (after some event like 'keypress' or 'input') and are
165 // intensively.
213 // polling intensively.
166 d.pollingFast = false;
214 d.pollingFast = false;
167 // Self-resetting timeout for the poller
215 // Self-resetting timeout for the poller
168 d.poll = new Delayed();
216 d.poll = new Delayed();
169
217
170 d.cachedCharWidth = d.cachedTextHeight = null;
218 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
171 d.measureLineCache = [];
172 d.measureLineCachePos = 0;
173
219
174 // Tracks when resetInput has punted to just putting a short
220 // Tracks when resetInput has punted to just putting a short
175 // string instead of the (large) selection.
221 // string into the textarea instead of the full selection.
176 d.inaccurateSelection = false;
222 d.inaccurateSelection = false;
177
223
178 // Tracks the maximum line length so that the horizontal scrollbar
224 // Tracks the maximum line length so that the horizontal scrollbar
@@ -184,7 +230,12 b' window.CodeMirror = (function() {'
184 // Used for measuring wheel scrolling granularity
230 // Used for measuring wheel scrolling granularity
185 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
231 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
186
232
187 return d;
233 // True when shift is held down.
234 d.shift = false;
235
236 // Used to track whether anything happened since the context menu
237 // was opened.
238 d.selForContextMenu = null;
188 }
239 }
189
240
190 // STATE UPDATES
241 // STATE UPDATES
@@ -193,6 +244,10 b' window.CodeMirror = (function() {'
193
244
194 function loadMode(cm) {
245 function loadMode(cm) {
195 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
246 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
247 resetModeState(cm);
248 }
249
250 function resetModeState(cm) {
196 cm.doc.iter(function(line) {
251 cm.doc.iter(function(line) {
197 if (line.stateAfter) line.stateAfter = null;
252 if (line.stateAfter) line.stateAfter = null;
198 if (line.styles) line.styles = null;
253 if (line.styles) line.styles = null;
@@ -205,11 +260,11 b' window.CodeMirror = (function() {'
205
260
206 function wrappingChanged(cm) {
261 function wrappingChanged(cm) {
207 if (cm.options.lineWrapping) {
262 if (cm.options.lineWrapping) {
208 cm.display.wrapper.className += " CodeMirror-wrap";
263 addClass(cm.display.wrapper, "CodeMirror-wrap");
209 cm.display.sizer.style.minWidth = "";
264 cm.display.sizer.style.minWidth = "";
210 } else {
265 } else {
211 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
266 rmClass(cm.display.wrapper, "CodeMirror-wrap");
212 computeMaxLength(cm);
267 findMaxLine(cm);
213 }
268 }
214 estimateLineHeights(cm);
269 estimateLineHeights(cm);
215 regChange(cm);
270 regChange(cm);
@@ -217,16 +272,24 b' window.CodeMirror = (function() {'
217 setTimeout(function(){updateScrollbars(cm);}, 100);
272 setTimeout(function(){updateScrollbars(cm);}, 100);
218 }
273 }
219
274
275 // Returns a function that estimates the height of a line, to use as
276 // first approximation until the line becomes visible (and is thus
277 // properly measurable).
220 function estimateHeight(cm) {
278 function estimateHeight(cm) {
221 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
279 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
222 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
280 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
223 return function(line) {
281 return function(line) {
224 if (lineIsHidden(cm.doc, line))
282 if (lineIsHidden(cm.doc, line)) return 0;
225 return 0;
283
226 else if (wrapping)
284 var widgetsHeight = 0;
227 return (Math.ceil(line.text.length / perLine) || 1) * th;
285 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
286 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
287 }
288
289 if (wrapping)
290 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
228 else
291 else
229 return th;
292 return widgetsHeight + th;
230 };
293 };
231 }
294 }
232
295
@@ -242,7 +305,6 b' window.CodeMirror = (function() {'
242 var map = keyMap[cm.options.keyMap], style = map.style;
305 var map = keyMap[cm.options.keyMap], style = map.style;
243 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
306 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
244 (style ? " cm-keymap-" + style : "");
307 (style ? " cm-keymap-" + style : "");
245 cm.state.disableInput = map.disableInput;
246 }
308 }
247
309
248 function themeChanged(cm) {
310 function themeChanged(cm) {
@@ -257,6 +319,8 b' window.CodeMirror = (function() {'
257 setTimeout(function(){alignHorizontally(cm);}, 20);
319 setTimeout(function(){alignHorizontally(cm);}, 20);
258 }
320 }
259
321
322 // Rebuild the gutter elements, ensure the margin to the left of the
323 // code matches their width.
260 function updateGutters(cm) {
324 function updateGutters(cm) {
261 var gutters = cm.display.gutters, specs = cm.options.gutters;
325 var gutters = cm.display.gutters, specs = cm.options.gutters;
262 removeChildren(gutters);
326 removeChildren(gutters);
@@ -269,33 +333,44 b' window.CodeMirror = (function() {'
269 }
333 }
270 }
334 }
271 gutters.style.display = i ? "" : "none";
335 gutters.style.display = i ? "" : "none";
272 }
336 updateGutterSpace(cm);
273
337 }
274 function lineLength(doc, line) {
338
339 function updateGutterSpace(cm) {
340 var width = cm.display.gutters.offsetWidth;
341 cm.display.sizer.style.marginLeft = width + "px";
342 cm.display.scrollbarH.style.left = cm.options.fixedGutter ? width + "px" : 0;
343 }
344
345 // Compute the character length of a line, taking into account
346 // collapsed ranges (see markText) that might hide parts, and join
347 // other lines onto it.
348 function lineLength(line) {
275 if (line.height == 0) return 0;
349 if (line.height == 0) return 0;
276 var len = line.text.length, merged, cur = line;
350 var len = line.text.length, merged, cur = line;
277 while (merged = collapsedSpanAtStart(cur)) {
351 while (merged = collapsedSpanAtStart(cur)) {
278 var found = merged.find();
352 var found = merged.find(0, true);
279 cur = getLine(doc, found.from.line);
353 cur = found.from.line;
280 len += found.from.ch - found.to.ch;
354 len += found.from.ch - found.to.ch;
281 }
355 }
282 cur = line;
356 cur = line;
283 while (merged = collapsedSpanAtEnd(cur)) {
357 while (merged = collapsedSpanAtEnd(cur)) {
284 var found = merged.find();
358 var found = merged.find(0, true);
285 len -= cur.text.length - found.from.ch;
359 len -= cur.text.length - found.from.ch;
286 cur = getLine(doc, found.to.line);
360 cur = found.to.line;
287 len += cur.text.length - found.to.ch;
361 len += cur.text.length - found.to.ch;
288 }
362 }
289 return len;
363 return len;
290 }
364 }
291
365
292 function computeMaxLength(cm) {
366 // Find the longest line in the document.
367 function findMaxLine(cm) {
293 var d = cm.display, doc = cm.doc;
368 var d = cm.display, doc = cm.doc;
294 d.maxLine = getLine(doc, doc.first);
369 d.maxLine = getLine(doc, doc.first);
295 d.maxLineLength = lineLength(doc, d.maxLine);
370 d.maxLineLength = lineLength(d.maxLine);
296 d.maxLineChanged = true;
371 d.maxLineChanged = true;
297 doc.iter(function(line) {
372 doc.iter(function(line) {
298 var len = lineLength(doc, line);
373 var len = lineLength(line);
299 if (len > d.maxLineLength) {
374 if (len > d.maxLineLength) {
300 d.maxLineLength = len;
375 d.maxLineLength = len;
301 d.maxLine = line;
376 d.maxLine = line;
@@ -306,78 +381,137 b' window.CodeMirror = (function() {'
306 // Make sure the gutters options contains the element
381 // Make sure the gutters options contains the element
307 // "CodeMirror-linenumbers" when the lineNumbers option is true.
382 // "CodeMirror-linenumbers" when the lineNumbers option is true.
308 function setGuttersForLineNumbers(options) {
383 function setGuttersForLineNumbers(options) {
309 var found = false;
384 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
310 for (var i = 0; i < options.gutters.length; ++i) {
385 if (found == -1 && options.lineNumbers) {
311 if (options.gutters[i] == "CodeMirror-linenumbers") {
386 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
312 if (options.lineNumbers) found = true;
387 } else if (found > -1 && !options.lineNumbers) {
313 else options.gutters.splice(i--, 1);
388 options.gutters = options.gutters.slice(0);
314 }
389 options.gutters.splice(found, 1);
315 }
390 }
316 if (!found && options.lineNumbers)
317 options.gutters.push("CodeMirror-linenumbers");
318 }
391 }
319
392
320 // SCROLLBARS
393 // SCROLLBARS
321
394
395 function hScrollbarTakesSpace(cm) {
396 return cm.display.scroller.clientHeight - cm.display.wrapper.clientHeight < scrollerCutOff - 3;
397 }
398
399 // Prepare DOM reads needed to update the scrollbars. Done in one
400 // shot to minimize update/measure roundtrips.
401 function measureForScrollbars(cm) {
402 var scroll = cm.display.scroller;
403 return {
404 clientHeight: scroll.clientHeight,
405 barHeight: cm.display.scrollbarV.clientHeight,
406 scrollWidth: scroll.scrollWidth, clientWidth: scroll.clientWidth,
407 hScrollbarTakesSpace: hScrollbarTakesSpace(cm),
408 barWidth: cm.display.scrollbarH.clientWidth,
409 docHeight: Math.round(cm.doc.height + paddingVert(cm.display))
410 };
411 }
412
322 // Re-synchronize the fake scrollbars with the actual size of the
413 // Re-synchronize the fake scrollbars with the actual size of the
323 // content. Optionally force a scrollTop.
414 // content.
324 function updateScrollbars(cm) {
415 function updateScrollbars(cm, measure) {
325 var d = cm.display, docHeight = cm.doc.height;
416 if (!measure) measure = measureForScrollbars(cm);
326 var totalHeight = docHeight + paddingVert(d);
417 var d = cm.display, sWidth = scrollbarWidth(d.measure);
327 d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
418 var scrollHeight = measure.docHeight + scrollerCutOff;
328 d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
419 var needsH = measure.scrollWidth > measure.clientWidth;
329 var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
420 if (needsH && measure.scrollWidth <= measure.clientWidth + 1 &&
330 var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
421 sWidth > 0 && !measure.hScrollbarTakesSpace)
331 var needsV = scrollHeight > (d.scroller.clientHeight + 1);
422 needsH = false; // (Issue #2562)
423 var needsV = scrollHeight > measure.clientHeight;
424
332 if (needsV) {
425 if (needsV) {
333 d.scrollbarV.style.display = "block";
426 d.scrollbarV.style.display = "block";
334 d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
427 d.scrollbarV.style.bottom = needsH ? sWidth + "px" : "0";
428 // A bug in IE8 can cause this value to be negative, so guard it.
335 d.scrollbarV.firstChild.style.height =
429 d.scrollbarV.firstChild.style.height =
336 (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
430 Math.max(0, scrollHeight - measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + "px";
337 } else d.scrollbarV.style.display = "";
431 } else {
432 d.scrollbarV.style.display = "";
433 d.scrollbarV.firstChild.style.height = "0";
434 }
338 if (needsH) {
435 if (needsH) {
339 d.scrollbarH.style.display = "block";
436 d.scrollbarH.style.display = "block";
340 d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
437 d.scrollbarH.style.right = needsV ? sWidth + "px" : "0";
341 d.scrollbarH.firstChild.style.width =
438 d.scrollbarH.firstChild.style.width =
342 (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
439 (measure.scrollWidth - measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + "px";
343 } else d.scrollbarH.style.display = "";
440 } else {
441 d.scrollbarH.style.display = "";
442 d.scrollbarH.firstChild.style.width = "0";
443 }
344 if (needsH && needsV) {
444 if (needsH && needsV) {
345 d.scrollbarFiller.style.display = "block";
445 d.scrollbarFiller.style.display = "block";
346 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
446 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = sWidth + "px";
347 } else d.scrollbarFiller.style.display = "";
447 } else d.scrollbarFiller.style.display = "";
348 if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
448 if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
349 d.gutterFiller.style.display = "block";
449 d.gutterFiller.style.display = "block";
350 d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
450 d.gutterFiller.style.height = sWidth + "px";
351 d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
451 d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
352 } else d.gutterFiller.style.display = "";
452 } else d.gutterFiller.style.display = "";
353
453
354 if (mac_geLion && scrollbarWidth(d.measure) === 0)
454 if (!cm.state.checkedOverlayScrollbar && measure.clientHeight > 0) {
355 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
455 if (sWidth === 0) {
356 }
456 var w = mac && !mac_geMountainLion ? "12px" : "18px";
357
457 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = w;
358 function visibleLines(display, doc, viewPort) {
458 var barMouseDown = function(e) {
359 var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
459 if (e_target(e) != d.scrollbarV && e_target(e) != d.scrollbarH)
360 if (typeof viewPort == "number") top = viewPort;
460 operation(cm, onMouseDown)(e);
361 else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
461 };
462 on(d.scrollbarV, "mousedown", barMouseDown);
463 on(d.scrollbarH, "mousedown", barMouseDown);
464 }
465 cm.state.checkedOverlayScrollbar = true;
466 }
467 }
468
469 // Compute the lines that are visible in a given viewport (defaults
470 // the the current scroll position). viewport may contain top,
471 // height, and ensure (see op.scrollToPos) properties.
472 function visibleLines(display, doc, viewport) {
473 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
362 top = Math.floor(top - paddingTop(display));
474 top = Math.floor(top - paddingTop(display));
363 var bottom = Math.ceil(top + height);
475 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
364 return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
476
477 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
478 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
479 // forces those lines into the viewport (if possible).
480 if (viewport && viewport.ensure) {
481 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
482 if (ensureFrom < from)
483 return {from: ensureFrom,
484 to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
485 if (Math.min(ensureTo, doc.lastLine()) >= to)
486 return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight),
487 to: ensureTo};
488 }
489 return {from: from, to: Math.max(to, from + 1)};
365 }
490 }
366
491
367 // LINE NUMBERS
492 // LINE NUMBERS
368
493
494 // Re-align line numbers and gutter marks to compensate for
495 // horizontal scrolling.
369 function alignHorizontally(cm) {
496 function alignHorizontally(cm) {
370 var display = cm.display;
497 var display = cm.display, view = display.view;
371 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
498 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
372 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
499 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
373 var gutterW = display.gutters.offsetWidth, l = comp + "px";
500 var gutterW = display.gutters.offsetWidth, left = comp + "px";
374 for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
501 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
375 for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
502 if (cm.options.fixedGutter && view[i].gutter)
503 view[i].gutter.style.left = left;
504 var align = view[i].alignable;
505 if (align) for (var j = 0; j < align.length; j++)
506 align[j].style.left = left;
376 }
507 }
377 if (cm.options.fixedGutter)
508 if (cm.options.fixedGutter)
378 display.gutters.style.left = (comp + gutterW) + "px";
509 display.gutters.style.left = (comp + gutterW) + "px";
379 }
510 }
380
511
512 // Used to ensure that the line number gutter is still the right
513 // size for the current document size. Returns true when an update
514 // is needed.
381 function maybeUpdateLineNumberWidth(cm) {
515 function maybeUpdateLineNumberWidth(cm) {
382 if (!cm.options.lineNumbers) return false;
516 if (!cm.options.lineNumbers) return false;
383 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
517 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
@@ -390,6 +524,7 b' window.CodeMirror = (function() {'
390 display.lineNumWidth = display.lineNumInnerWidth + padding;
524 display.lineNumWidth = display.lineNumInnerWidth + padding;
391 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
525 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
392 display.lineGutter.style.width = display.lineNumWidth + "px";
526 display.lineGutter.style.width = display.lineNumWidth + "px";
527 updateGutterSpace(cm);
393 return true;
528 return true;
394 }
529 }
395 return false;
530 return false;
@@ -398,191 +533,199 b' window.CodeMirror = (function() {'
398 function lineNumberFor(options, i) {
533 function lineNumberFor(options, i) {
399 return String(options.lineNumberFormatter(i + options.firstLineNumber));
534 return String(options.lineNumberFormatter(i + options.firstLineNumber));
400 }
535 }
536
537 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
538 // but using getBoundingClientRect to get a sub-pixel-accurate
539 // result.
401 function compensateForHScroll(display) {
540 function compensateForHScroll(display) {
402 return getRect(display.scroller).left - getRect(display.sizer).left;
541 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
403 }
542 }
404
543
405 // DISPLAY DRAWING
544 // DISPLAY DRAWING
406
545
407 function updateDisplay(cm, changes, viewPort, forced) {
546 function DisplayUpdate(cm, viewport, force) {
408 var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
547 var display = cm.display;
409 var visible = visibleLines(cm.display, cm.doc, viewPort);
548
410 for (;;) {
549 this.viewport = viewport;
411 if (!updateDisplayInner(cm, changes, visible, forced)) break;
550 // Store some values that we'll need later (but don't want to force a relayout for)
412 forced = false;
551 this.visible = visibleLines(display, cm.doc, viewport);
413 updated = true;
552 this.editorIsHidden = !display.wrapper.offsetWidth;
414 updateSelection(cm);
553 this.wrapperHeight = display.wrapper.clientHeight;
415 updateScrollbars(cm);
554 this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
416
555 this.oldScrollerWidth = display.scroller.clientWidth;
417 // Clip forced viewport to actual scrollable area
556 this.force = force;
418 if (viewPort)
557 this.dims = getDimensions(cm);
419 viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
558 }
420 typeof viewPort == "number" ? viewPort : viewPort.top);
559
421 visible = visibleLines(cm.display, cm.doc, viewPort);
560 // Does the actual updating of the line display. Bails out
422 if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
561 // (returning false) when there is nothing to be done and forced is
423 break;
562 // false.
424 changes = [];
563 function updateDisplayIfNeeded(cm, update) {
425 }
426
427 if (updated) {
428 signalLater(cm, "update", cm);
429 if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
430 signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
431 }
432 return updated;
433 }
434
435 // Uses a set of changes plus the current scroll position to
436 // determine which DOM updates have to be made, and makes the
437 // updates.
438 function updateDisplayInner(cm, changes, visible, forced) {
439 var display = cm.display, doc = cm.doc;
564 var display = cm.display, doc = cm.doc;
440 if (!display.wrapper.clientWidth) {
565 if (update.editorIsHidden) {
441 display.showingFrom = display.showingTo = doc.first;
566 resetView(cm);
442 display.viewOffset = 0;
567 return false;
443 return;
444 }
568 }
445
569
446 // Bail out if the visible area is already rendered and nothing changed.
570 // Bail out if the visible area is already rendered and nothing changed.
447 if (!forced && changes.length == 0 &&
571 if (!update.force &&
448 visible.from > display.showingFrom && visible.to < display.showingTo)
572 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
449 return;
573 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
450
574 countDirtyView(cm) == 0)
451 if (maybeUpdateLineNumberWidth(cm))
575 return false;
452 changes = [{from: doc.first, to: doc.first + doc.size}];
576
453 var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
577 if (maybeUpdateLineNumberWidth(cm)) {
454 display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
578 resetView(cm);
455
579 update.dims = getDimensions(cm);
456 // Used to determine which lines need their line numbers updated
580 }
457 var positionsChangedFrom = Infinity;
581
458 if (cm.options.lineNumbers)
582 // Compute a suitable new viewport (from & to)
459 for (var i = 0; i < changes.length; ++i)
460 if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
461
462 var end = doc.first + doc.size;
583 var end = doc.first + doc.size;
463 var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
584 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
464 var to = Math.min(end, visible.to + cm.options.viewportMargin);
585 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
465 if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
586 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
466 if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
587 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
467 if (sawCollapsedSpans) {
588 if (sawCollapsedSpans) {
468 from = lineNo(visualLine(doc, getLine(doc, from)));
589 from = visualLineNo(cm.doc, from);
469 while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
590 to = visualLineEndNo(cm.doc, to);
470 }
591 }
471
592
472 // Create a range of theoretically intact lines, and punch holes
593 var different = from != display.viewFrom || to != display.viewTo ||
473 // in that using the change info.
594 display.lastSizeC != update.wrapperHeight;
474 var intact = [{from: Math.max(display.showingFrom, doc.first),
595 adjustView(cm, from, to);
475 to: Math.min(display.showingTo, end)}];
596
476 if (intact[0].from >= intact[0].to) intact = [];
597 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
477 else intact = computeIntact(intact, changes);
598 // Position the mover div to align with the current scroll position
478 // When merged lines are present, we might have to reduce the
599 cm.display.mover.style.top = display.viewOffset + "px";
479 // intact ranges because changes in continued fragments of the
600
480 // intact lines do require the lines to be redrawn.
601 var toUpdate = countDirtyView(cm);
481 if (sawCollapsedSpans)
602 if (!different && toUpdate == 0 && !update.force &&
482 for (var i = 0; i < intact.length; ++i) {
603 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
483 var range = intact[i], merged;
604 return false;
484 while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
605
485 var newTo = merged.find().from.line;
606 // For big changes, we hide the enclosing element during the
486 if (newTo > range.from) range.to = newTo;
607 // update, since that speeds up the operations on most browsers.
487 else { intact.splice(i--, 1); break; }
608 var focused = activeElt();
488 }
609 if (toUpdate > 4) display.lineDiv.style.display = "none";
489 }
610 patchDisplay(cm, display.updateLineNumbers, update.dims);
490
611 if (toUpdate > 4) display.lineDiv.style.display = "";
491 // Clip off the parts that won't be visible
612 // There might have been a widget with a focused element that got
492 var intactLines = 0;
613 // hidden or updated, if so re-focus it.
493 for (var i = 0; i < intact.length; ++i) {
614 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
494 var range = intact[i];
615
495 if (range.from < from) range.from = from;
616 // Prevent selection and cursors from interfering with the scroll
496 if (range.to > to) range.to = to;
617 // width.
497 if (range.from >= range.to) intact.splice(i--, 1);
618 removeChildren(display.cursorDiv);
498 else intactLines += range.to - range.from;
619 removeChildren(display.selectionDiv);
499 }
620
500 if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
501 updateViewOffset(cm);
502 return;
503 }
504 intact.sort(function(a, b) {return a.from - b.from;});
505
506 // Avoid crashing on IE's "unspecified error" when in iframes
507 try {
508 var focused = document.activeElement;
509 } catch(e) {}
510 if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
511 patchDisplay(cm, from, to, intact, positionsChangedFrom);
512 display.lineDiv.style.display = "";
513 if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
514
515 var different = from != display.showingFrom || to != display.showingTo ||
516 display.lastSizeC != display.wrapper.clientHeight;
517 // This is just a bogus formula that detects when the editor is
518 // resized or the font size changes.
519 if (different) {
621 if (different) {
520 display.lastSizeC = display.wrapper.clientHeight;
622 display.lastSizeC = update.wrapperHeight;
521 startWorker(cm, 400);
623 startWorker(cm, 400);
522 }
624 }
523 display.showingFrom = from; display.showingTo = to;
625
524
626 display.updateLineNumbers = null;
525 updateHeightsInViewport(cm);
526 updateViewOffset(cm);
527
627
528 return true;
628 return true;
529 }
629 }
530
630
631 function postUpdateDisplay(cm, update) {
632 var force = update.force, viewport = update.viewport;
633 for (var first = true;; first = false) {
634 if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) {
635 force = true;
636 } else {
637 force = false;
638 // Clip forced viewport to actual scrollable area.
639 if (viewport && viewport.top != null)
640 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - scrollerCutOff -
641 cm.display.scroller.clientHeight, viewport.top)};
642 // Updated line heights might result in the drawn area not
643 // actually covering the viewport. Keep looping until it does.
644 update.visible = visibleLines(cm.display, cm.doc, viewport);
645 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
646 break;
647 }
648 if (!updateDisplayIfNeeded(cm, update)) break;
649 updateHeightsInViewport(cm);
650 var barMeasure = measureForScrollbars(cm);
651 updateSelection(cm);
652 setDocumentHeight(cm, barMeasure);
653 updateScrollbars(cm, barMeasure);
654 }
655
656 signalLater(cm, "update", cm);
657 if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update.oldViewTo)
658 signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
659 }
660
661 function updateDisplaySimple(cm, viewport) {
662 var update = new DisplayUpdate(cm, viewport);
663 if (updateDisplayIfNeeded(cm, update)) {
664 updateHeightsInViewport(cm);
665 postUpdateDisplay(cm, update);
666 var barMeasure = measureForScrollbars(cm);
667 updateSelection(cm);
668 setDocumentHeight(cm, barMeasure);
669 updateScrollbars(cm, barMeasure);
670 }
671 }
672
673 function setDocumentHeight(cm, measure) {
674 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measure.docHeight + "px";
675 cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + "px";
676 }
677
678 function checkForWebkitWidthBug(cm, measure) {
679 // Work around Webkit bug where it sometimes reserves space for a
680 // non-existing phantom scrollbar in the scroller (Issue #2420)
681 if (cm.display.sizer.offsetWidth + cm.display.gutters.offsetWidth < cm.display.scroller.clientWidth - 1) {
682 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = "0px";
683 cm.display.gutters.style.height = measure.docHeight + "px";
684 }
685 }
686
687 // Read the actual heights of the rendered lines, and update their
688 // stored heights to match.
531 function updateHeightsInViewport(cm) {
689 function updateHeightsInViewport(cm) {
532 var display = cm.display;
690 var display = cm.display;
533 var prevBottom = display.lineDiv.offsetTop;
691 var prevBottom = display.lineDiv.offsetTop;
534 for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
692 for (var i = 0; i < display.view.length; i++) {
535 if (ie_lt8) {
693 var cur = display.view[i], height;
536 var bot = node.offsetTop + node.offsetHeight;
694 if (cur.hidden) continue;
695 if (ie && ie_version < 8) {
696 var bot = cur.node.offsetTop + cur.node.offsetHeight;
537 height = bot - prevBottom;
697 height = bot - prevBottom;
538 prevBottom = bot;
698 prevBottom = bot;
539 } else {
699 } else {
540 var box = getRect(node);
700 var box = cur.node.getBoundingClientRect();
541 height = box.bottom - box.top;
701 height = box.bottom - box.top;
542 }
702 }
543 var diff = node.lineObj.height - height;
703 var diff = cur.line.height - height;
544 if (height < 2) height = textHeight(display);
704 if (height < 2) height = textHeight(display);
545 if (diff > .001 || diff < -.001) {
705 if (diff > .001 || diff < -.001) {
546 updateLineHeight(node.lineObj, height);
706 updateLineHeight(cur.line, height);
547 var widgets = node.lineObj.widgets;
707 updateWidgetHeight(cur.line);
548 if (widgets) for (var i = 0; i < widgets.length; ++i)
708 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
549 widgets[i].height = widgets[i].node.offsetHeight;
709 updateWidgetHeight(cur.rest[j]);
550 }
710 }
551 }
711 }
552 }
712 }
553
713
554 function updateViewOffset(cm) {
714 // Read and store the height of line widgets associated with the
555 var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
715 // given line.
556 // Position the mover div to align with the current virtual scroll position
716 function updateWidgetHeight(line) {
557 cm.display.mover.style.top = off + "px";
717 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
558 }
718 line.widgets[i].height = line.widgets[i].node.offsetHeight;
559
719 }
560 function computeIntact(intact, changes) {
720
561 for (var i = 0, l = changes.length || 0; i < l; ++i) {
721 // Do a bulk-read of the DOM positions and sizes needed to draw the
562 var change = changes[i], intact2 = [], diff = change.diff || 0;
722 // view, so that we don't interleave reading and writing to the DOM.
563 for (var j = 0, l2 = intact.length; j < l2; ++j) {
564 var range = intact[j];
565 if (change.to <= range.from && change.diff) {
566 intact2.push({from: range.from + diff, to: range.to + diff});
567 } else if (change.to <= range.from || change.from >= range.to) {
568 intact2.push(range);
569 } else {
570 if (change.from > range.from)
571 intact2.push({from: range.from, to: change.from});
572 if (change.to < range.to)
573 intact2.push({from: change.to + diff, to: range.to + diff});
574 }
575 }
576 intact = intact2;
577 }
578 return intact;
579 }
580
581 function getDimensions(cm) {
723 function getDimensions(cm) {
582 var d = cm.display, left = {}, width = {};
724 var d = cm.display, left = {}, width = {};
725 var gutterLeft = d.gutters.clientLeft;
583 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
726 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
584 left[cm.options.gutters[i]] = n.offsetLeft;
727 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
585 width[cm.options.gutters[i]] = n.offsetWidth;
728 width[cm.options.gutters[i]] = n.clientWidth;
586 }
729 }
587 return {fixedPos: compensateForHScroll(d),
730 return {fixedPos: compensateForHScroll(d),
588 gutterTotalWidth: d.gutters.offsetWidth,
731 gutterTotalWidth: d.gutters.offsetWidth,
@@ -591,153 +734,207 b' window.CodeMirror = (function() {'
591 wrapperWidth: d.wrapper.clientWidth};
734 wrapperWidth: d.wrapper.clientWidth};
592 }
735 }
593
736
594 function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
737 // Sync the actual display DOM structure with display.view, removing
595 var dims = getDimensions(cm);
738 // nodes for lines that are no longer in view, and creating the ones
739 // that are not there yet, and updating the ones that are out of
740 // date.
741 function patchDisplay(cm, updateNumbersFrom, dims) {
596 var display = cm.display, lineNumbers = cm.options.lineNumbers;
742 var display = cm.display, lineNumbers = cm.options.lineNumbers;
597 if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
598 removeChildren(display.lineDiv);
599 var container = display.lineDiv, cur = container.firstChild;
743 var container = display.lineDiv, cur = container.firstChild;
600
744
601 function rm(node) {
745 function rm(node) {
602 var next = node.nextSibling;
746 var next = node.nextSibling;
603 if (webkit && mac && cm.display.currentWheelTarget == node) {
747 // Works around a throw-scroll bug in OS X Webkit
748 if (webkit && mac && cm.display.currentWheelTarget == node)
604 node.style.display = "none";
749 node.style.display = "none";
605 node.lineObj = null;
750 else
606 } else {
607 node.parentNode.removeChild(node);
751 node.parentNode.removeChild(node);
608 }
609 return next;
752 return next;
610 }
753 }
611
754
612 var nextIntact = intact.shift(), lineN = from;
755 var view = display.view, lineN = display.viewFrom;
613 cm.doc.iter(from, to, function(line) {
756 // Loop over the elements in the view, syncing cur (the DOM nodes
614 if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
757 // in display.lineDiv) with the view as we go.
615 if (lineIsHidden(cm.doc, line)) {
758 for (var i = 0; i < view.length; i++) {
616 if (line.height != 0) updateLineHeight(line, 0);
759 var lineView = view[i];
617 if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
760 if (lineView.hidden) {
618 var w = line.widgets[i];
761 } else if (!lineView.node) { // Not drawn yet
619 if (w.showIfHidden) {
762 var node = buildLineElement(cm, lineView, lineN, dims);
620 var prev = cur.previousSibling;
763 container.insertBefore(node, cur);
621 if (/pre/i.test(prev.nodeName)) {
764 } else { // Already drawn
622 var wrap = elt("div", null, null, "position: relative");
765 while (cur != lineView.node) cur = rm(cur);
623 prev.parentNode.replaceChild(wrap, prev);
766 var updateNumber = lineNumbers && updateNumbersFrom != null &&
624 wrap.appendChild(prev);
767 updateNumbersFrom <= lineN && lineView.lineNumber;
625 prev = wrap;
768 if (lineView.changes) {
626 }
769 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
627 var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
770 updateLineForChanges(cm, lineView, lineN, dims);
628 if (!w.handleMouseEvents) wnode.ignoreEvents = true;
629 positionLineWidget(w, wnode, prev, dims);
630 }
631 }
771 }
632 } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
772 if (updateNumber) {
633 // This line is intact. Skip to the actual node. Update its
773 removeChildren(lineView.lineNumber);
634 // line number if needed.
774 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
635 while (cur.lineObj != line) cur = rm(cur);
636 if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
637 setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
638 cur = cur.nextSibling;
639 } else {
640 // For lines with widgets, make an attempt to find and reuse
641 // the existing element, so that widgets aren't needlessly
642 // removed and re-inserted into the dom
643 if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
644 if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
645 // This line needs to be generated.
646 var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
647 if (lineNode != reuse) {
648 container.insertBefore(lineNode, cur);
649 } else {
650 while (cur != reuse) cur = rm(cur);
651 cur = cur.nextSibling;
652 }
775 }
653
776 cur = lineView.node.nextSibling;
654 lineNode.lineObj = line;
777 }
655 }
778 lineN += lineView.size;
656 ++lineN;
779 }
657 });
658 while (cur) cur = rm(cur);
780 while (cur) cur = rm(cur);
659 }
781 }
660
782
661 function buildLineElement(cm, line, lineNo, dims, reuse) {
783 // When an aspect of a line changes, a string is added to
662 var lineElement = lineContent(cm, line);
784 // lineView.changes. This updates the relevant part of the line's
663 var markers = line.gutterMarkers, display = cm.display, wrap;
785 // DOM structure.
664
786 function updateLineForChanges(cm, lineView, lineN, dims) {
665 if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
787 for (var j = 0; j < lineView.changes.length; j++) {
666 return lineElement;
788 var type = lineView.changes[j];
667
789 if (type == "text") updateLineText(cm, lineView);
668 // Lines with gutter elements, widgets or a background class need
790 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
669 // to be wrapped again, and have the extra elements added to the
791 else if (type == "class") updateLineClasses(lineView);
670 // wrapper div
792 else if (type == "widget") updateLineWidgets(lineView, dims);
671
793 }
672 if (reuse) {
794 lineView.changes = null;
673 reuse.alignable = null;
795 }
674 var isOk = true, widgetsSeen = 0, insertBefore = null;
796
675 for (var n = reuse.firstChild, next; n; n = next) {
797 // Lines with gutter elements, widgets or a background class need to
676 next = n.nextSibling;
798 // be wrapped, and have the extra elements added to the wrapper div
677 if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
799 function ensureLineWrapped(lineView) {
678 reuse.removeChild(n);
800 if (lineView.node == lineView.text) {
679 } else {
801 lineView.node = elt("div", null, null, "position: relative");
680 for (var i = 0; i < line.widgets.length; ++i) {
802 if (lineView.text.parentNode)
681 var widget = line.widgets[i];
803 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
682 if (widget.node == n.firstChild) {
804 lineView.node.appendChild(lineView.text);
683 if (!widget.above && !insertBefore) insertBefore = n;
805 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
684 positionLineWidget(widget, n, reuse, dims);
806 }
685 ++widgetsSeen;
807 return lineView.node;
686 break;
808 }
687 }
809
688 }
810 function updateLineBackground(lineView) {
689 if (i == line.widgets.length) { isOk = false; break; }
811 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
690 }
812 if (cls) cls += " CodeMirror-linebackground";
691 }
813 if (lineView.background) {
692 reuse.insertBefore(lineElement, insertBefore);
814 if (cls) lineView.background.className = cls;
693 if (isOk && widgetsSeen == line.widgets.length) {
815 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
694 wrap = reuse;
816 } else if (cls) {
695 reuse.className = line.wrapClass || "";
817 var wrap = ensureLineWrapped(lineView);
696 }
818 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
697 }
819 }
698 if (!wrap) {
820 }
699 wrap = elt("div", null, line.wrapClass, "position: relative");
821
700 wrap.appendChild(lineElement);
822 // Wrapper around buildLineContent which will reuse the structure
701 }
823 // in display.externalMeasured when possible.
702 // Kludge to make sure the styled element lies behind the selection (by z-index)
824 function getLineContent(cm, lineView) {
703 if (line.bgClass)
825 var ext = cm.display.externalMeasured;
704 wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
826 if (ext && ext.line == lineView.line) {
827 cm.display.externalMeasured = null;
828 lineView.measure = ext.measure;
829 return ext.built;
830 }
831 return buildLineContent(cm, lineView);
832 }
833
834 // Redraw the line's text. Interacts with the background and text
835 // classes because the mode may output tokens that influence these
836 // classes.
837 function updateLineText(cm, lineView) {
838 var cls = lineView.text.className;
839 var built = getLineContent(cm, lineView);
840 if (lineView.text == lineView.node) lineView.node = built.pre;
841 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
842 lineView.text = built.pre;
843 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
844 lineView.bgClass = built.bgClass;
845 lineView.textClass = built.textClass;
846 updateLineClasses(lineView);
847 } else if (cls) {
848 lineView.text.className = cls;
849 }
850 }
851
852 function updateLineClasses(lineView) {
853 updateLineBackground(lineView);
854 if (lineView.line.wrapClass)
855 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
856 else if (lineView.node != lineView.text)
857 lineView.node.className = "";
858 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
859 lineView.text.className = textClass || "";
860 }
861
862 function updateLineGutter(cm, lineView, lineN, dims) {
863 if (lineView.gutter) {
864 lineView.node.removeChild(lineView.gutter);
865 lineView.gutter = null;
866 }
867 var markers = lineView.line.gutterMarkers;
705 if (cm.options.lineNumbers || markers) {
868 if (cm.options.lineNumbers || markers) {
706 var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
869 var wrap = ensureLineWrapped(lineView);
707 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
870 var gutterWrap = lineView.gutter =
708 wrap.firstChild);
871 wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " +
709 if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
872 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
873 lineView.text);
710 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
874 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
711 wrap.lineNumber = gutterWrap.appendChild(
875 lineView.lineNumber = gutterWrap.appendChild(
712 elt("div", lineNumberFor(cm.options, lineNo),
876 elt("div", lineNumberFor(cm.options, lineN),
713 "CodeMirror-linenumber CodeMirror-gutter-elt",
877 "CodeMirror-linenumber CodeMirror-gutter-elt",
714 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
878 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
715 + display.lineNumInnerWidth + "px"));
879 + cm.display.lineNumInnerWidth + "px"));
716 if (markers)
880 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
717 for (var k = 0; k < cm.options.gutters.length; ++k) {
881 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
718 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
882 if (found)
719 if (found)
883 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
720 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
884 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
721 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
885 }
722 }
886 }
723 }
887 }
724 if (ie_lt8) wrap.style.zIndex = 2;
888
725 if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
889 function updateLineWidgets(lineView, dims) {
890 if (lineView.alignable) lineView.alignable = null;
891 for (var node = lineView.node.firstChild, next; node; node = next) {
892 var next = node.nextSibling;
893 if (node.className == "CodeMirror-linewidget")
894 lineView.node.removeChild(node);
895 }
896 insertLineWidgets(lineView, dims);
897 }
898
899 // Build a line's DOM representation from scratch
900 function buildLineElement(cm, lineView, lineN, dims) {
901 var built = getLineContent(cm, lineView);
902 lineView.text = lineView.node = built.pre;
903 if (built.bgClass) lineView.bgClass = built.bgClass;
904 if (built.textClass) lineView.textClass = built.textClass;
905
906 updateLineClasses(lineView);
907 updateLineGutter(cm, lineView, lineN, dims);
908 insertLineWidgets(lineView, dims);
909 return lineView.node;
910 }
911
912 // A lineView may contain multiple logical lines (when merged by
913 // collapsed spans). The widgets for all of them need to be drawn.
914 function insertLineWidgets(lineView, dims) {
915 insertLineWidgetsFor(lineView.line, lineView, dims, true);
916 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
917 insertLineWidgetsFor(lineView.rest[i], lineView, dims, false);
918 }
919
920 function insertLineWidgetsFor(line, lineView, dims, allowAbove) {
921 if (!line.widgets) return;
922 var wrap = ensureLineWrapped(lineView);
923 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
726 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
924 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
727 if (!widget.handleMouseEvents) node.ignoreEvents = true;
925 if (!widget.handleMouseEvents) node.ignoreEvents = true;
728 positionLineWidget(widget, node, wrap, dims);
926 positionLineWidget(widget, node, lineView, dims);
729 if (widget.above)
927 if (allowAbove && widget.above)
730 wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
928 wrap.insertBefore(node, lineView.gutter || lineView.text);
731 else
929 else
732 wrap.appendChild(node);
930 wrap.appendChild(node);
733 signalLater(widget, "redraw");
931 signalLater(widget, "redraw");
734 }
932 }
735 return wrap;
933 }
736 }
934
737
935 function positionLineWidget(widget, node, lineView, dims) {
738 function positionLineWidget(widget, node, wrap, dims) {
739 if (widget.noHScroll) {
936 if (widget.noHScroll) {
740 (wrap.alignable || (wrap.alignable = [])).push(node);
937 (lineView.alignable || (lineView.alignable = [])).push(node);
741 var width = dims.wrapperWidth;
938 var width = dims.wrapperWidth;
742 node.style.left = dims.fixedPos + "px";
939 node.style.left = dims.fixedPos + "px";
743 if (!widget.coverGutter) {
940 if (!widget.coverGutter) {
@@ -753,57 +950,381 b' window.CodeMirror = (function() {'
753 }
950 }
754 }
951 }
755
952
953 // POSITION OBJECT
954
955 // A Pos instance represents a position within the text.
956 var Pos = CodeMirror.Pos = function(line, ch) {
957 if (!(this instanceof Pos)) return new Pos(line, ch);
958 this.line = line; this.ch = ch;
959 };
960
961 // Compare two positions, return 0 if they are the same, a negative
962 // number when a is less, and a positive number otherwise.
963 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
964
965 function copyPos(x) {return Pos(x.line, x.ch);}
966 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
967 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
968
756 // SELECTION / CURSOR
969 // SELECTION / CURSOR
757
970
758 function updateSelection(cm) {
971 // Selection objects are immutable. A new one is created every time
759 var display = cm.display;
972 // the selection changes. A selection is one or more non-overlapping
760 var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
973 // (and non-touching) ranges, sorted, and an integer that indicates
761 if (collapsed || cm.options.showCursorWhenSelecting)
974 // which one is the primary selection (the one that's scrolled into
762 updateSelectionCursor(cm);
975 // view, that getCursor returns, etc).
763 else
976 function Selection(ranges, primIndex) {
764 display.cursor.style.display = display.otherCursor.style.display = "none";
977 this.ranges = ranges;
765 if (!collapsed)
978 this.primIndex = primIndex;
766 updateSelectionRange(cm);
979 }
767 else
980
768 display.selectionDiv.style.display = "none";
981 Selection.prototype = {
982 primary: function() { return this.ranges[this.primIndex]; },
983 equals: function(other) {
984 if (other == this) return true;
985 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
986 for (var i = 0; i < this.ranges.length; i++) {
987 var here = this.ranges[i], there = other.ranges[i];
988 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
989 }
990 return true;
991 },
992 deepCopy: function() {
993 for (var out = [], i = 0; i < this.ranges.length; i++)
994 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
995 return new Selection(out, this.primIndex);
996 },
997 somethingSelected: function() {
998 for (var i = 0; i < this.ranges.length; i++)
999 if (!this.ranges[i].empty()) return true;
1000 return false;
1001 },
1002 contains: function(pos, end) {
1003 if (!end) end = pos;
1004 for (var i = 0; i < this.ranges.length; i++) {
1005 var range = this.ranges[i];
1006 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
1007 return i;
1008 }
1009 return -1;
1010 }
1011 };
1012
1013 function Range(anchor, head) {
1014 this.anchor = anchor; this.head = head;
1015 }
1016
1017 Range.prototype = {
1018 from: function() { return minPos(this.anchor, this.head); },
1019 to: function() { return maxPos(this.anchor, this.head); },
1020 empty: function() {
1021 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
1022 }
1023 };
1024
1025 // Take an unsorted, potentially overlapping set of ranges, and
1026 // build a selection out of it. 'Consumes' ranges array (modifying
1027 // it).
1028 function normalizeSelection(ranges, primIndex) {
1029 var prim = ranges[primIndex];
1030 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
1031 primIndex = indexOf(ranges, prim);
1032 for (var i = 1; i < ranges.length; i++) {
1033 var cur = ranges[i], prev = ranges[i - 1];
1034 if (cmp(prev.to(), cur.from()) >= 0) {
1035 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
1036 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
1037 if (i <= primIndex) --primIndex;
1038 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
1039 }
1040 }
1041 return new Selection(ranges, primIndex);
1042 }
1043
1044 function simpleSelection(anchor, head) {
1045 return new Selection([new Range(anchor, head || anchor)], 0);
1046 }
1047
1048 // Most of the external API clips given positions to make sure they
1049 // actually exist within the document.
1050 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
1051 function clipPos(doc, pos) {
1052 if (pos.line < doc.first) return Pos(doc.first, 0);
1053 var last = doc.first + doc.size - 1;
1054 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
1055 return clipToLen(pos, getLine(doc, pos.line).text.length);
1056 }
1057 function clipToLen(pos, linelen) {
1058 var ch = pos.ch;
1059 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
1060 else if (ch < 0) return Pos(pos.line, 0);
1061 else return pos;
1062 }
1063 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
1064 function clipPosArray(doc, array) {
1065 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
1066 return out;
1067 }
1068
1069 // SELECTION UPDATES
1070
1071 // The 'scroll' parameter given to many of these indicated whether
1072 // the new cursor position should be scrolled into view after
1073 // modifying the selection.
1074
1075 // If shift is held or the extend flag is set, extends a range to
1076 // include a given position (and optionally a second position).
1077 // Otherwise, simply returns the range between the given positions.
1078 // Used for cursor motion and such.
1079 function extendRange(doc, range, head, other) {
1080 if (doc.cm && doc.cm.display.shift || doc.extend) {
1081 var anchor = range.anchor;
1082 if (other) {
1083 var posBefore = cmp(head, anchor) < 0;
1084 if (posBefore != (cmp(other, anchor) < 0)) {
1085 anchor = head;
1086 head = other;
1087 } else if (posBefore != (cmp(head, other) < 0)) {
1088 head = other;
1089 }
1090 }
1091 return new Range(anchor, head);
1092 } else {
1093 return new Range(other || head, head);
1094 }
1095 }
1096
1097 // Extend the primary selection range, discard the rest.
1098 function extendSelection(doc, head, other, options) {
1099 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
1100 }
1101
1102 // Extend all selections (pos is an array of selections with length
1103 // equal the number of selections)
1104 function extendSelections(doc, heads, options) {
1105 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
1106 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
1107 var newSel = normalizeSelection(out, doc.sel.primIndex);
1108 setSelection(doc, newSel, options);
1109 }
1110
1111 // Updates a single range in the selection.
1112 function replaceOneSelection(doc, i, range, options) {
1113 var ranges = doc.sel.ranges.slice(0);
1114 ranges[i] = range;
1115 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
1116 }
1117
1118 // Reset the selection to a single range.
1119 function setSimpleSelection(doc, anchor, head, options) {
1120 setSelection(doc, simpleSelection(anchor, head), options);
1121 }
1122
1123 // Give beforeSelectionChange handlers a change to influence a
1124 // selection update.
1125 function filterSelectionChange(doc, sel) {
1126 var obj = {
1127 ranges: sel.ranges,
1128 update: function(ranges) {
1129 this.ranges = [];
1130 for (var i = 0; i < ranges.length; i++)
1131 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
1132 clipPos(doc, ranges[i].head));
1133 }
1134 };
1135 signal(doc, "beforeSelectionChange", doc, obj);
1136 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
1137 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
1138 else return sel;
1139 }
1140
1141 function setSelectionReplaceHistory(doc, sel, options) {
1142 var done = doc.history.done, last = lst(done);
1143 if (last && last.ranges) {
1144 done[done.length - 1] = sel;
1145 setSelectionNoUndo(doc, sel, options);
1146 } else {
1147 setSelection(doc, sel, options);
1148 }
1149 }
1150
1151 // Set a new selection.
1152 function setSelection(doc, sel, options) {
1153 setSelectionNoUndo(doc, sel, options);
1154 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
1155 }
1156
1157 function setSelectionNoUndo(doc, sel, options) {
1158 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
1159 sel = filterSelectionChange(doc, sel);
1160
1161 var bias = options && options.bias ||
1162 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
1163 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
1164
1165 if (!(options && options.scroll === false) && doc.cm)
1166 ensureCursorVisible(doc.cm);
1167 }
1168
1169 function setSelectionInner(doc, sel) {
1170 if (sel.equals(doc.sel)) return;
1171
1172 doc.sel = sel;
1173
1174 if (doc.cm) {
1175 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
1176 signalCursorActivity(doc.cm);
1177 }
1178 signalLater(doc, "cursorActivity", doc);
1179 }
1180
1181 // Verify that the selection does not partially select any atomic
1182 // marked ranges.
1183 function reCheckSelection(doc) {
1184 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
1185 }
1186
1187 // Return a selection that does not partially select any atomic
1188 // ranges.
1189 function skipAtomicInSelection(doc, sel, bias, mayClear) {
1190 var out;
1191 for (var i = 0; i < sel.ranges.length; i++) {
1192 var range = sel.ranges[i];
1193 var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
1194 var newHead = skipAtomic(doc, range.head, bias, mayClear);
1195 if (out || newAnchor != range.anchor || newHead != range.head) {
1196 if (!out) out = sel.ranges.slice(0, i);
1197 out[i] = new Range(newAnchor, newHead);
1198 }
1199 }
1200 return out ? normalizeSelection(out, sel.primIndex) : sel;
1201 }
1202
1203 // Ensure a given position is not inside an atomic range.
1204 function skipAtomic(doc, pos, bias, mayClear) {
1205 var flipped = false, curPos = pos;
1206 var dir = bias || 1;
1207 doc.cantEdit = false;
1208 search: for (;;) {
1209 var line = getLine(doc, curPos.line);
1210 if (line.markedSpans) {
1211 for (var i = 0; i < line.markedSpans.length; ++i) {
1212 var sp = line.markedSpans[i], m = sp.marker;
1213 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
1214 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
1215 if (mayClear) {
1216 signal(m, "beforeCursorEnter");
1217 if (m.explicitlyCleared) {
1218 if (!line.markedSpans) break;
1219 else {--i; continue;}
1220 }
1221 }
1222 if (!m.atomic) continue;
1223 var newPos = m.find(dir < 0 ? -1 : 1);
1224 if (cmp(newPos, curPos) == 0) {
1225 newPos.ch += dir;
1226 if (newPos.ch < 0) {
1227 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
1228 else newPos = null;
1229 } else if (newPos.ch > line.text.length) {
1230 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
1231 else newPos = null;
1232 }
1233 if (!newPos) {
1234 if (flipped) {
1235 // Driven in a corner -- no valid cursor position found at all
1236 // -- try again *with* clearing, if we didn't already
1237 if (!mayClear) return skipAtomic(doc, pos, bias, true);
1238 // Otherwise, turn off editing until further notice, and return the start of the doc
1239 doc.cantEdit = true;
1240 return Pos(doc.first, 0);
1241 }
1242 flipped = true; newPos = pos; dir = -dir;
1243 }
1244 }
1245 curPos = newPos;
1246 continue search;
1247 }
1248 }
1249 }
1250 return curPos;
1251 }
1252 }
1253
1254 // SELECTION DRAWING
1255
1256 // Redraw the selection and/or cursor
1257 function drawSelection(cm) {
1258 var display = cm.display, doc = cm.doc, result = {};
1259 var curFragment = result.cursors = document.createDocumentFragment();
1260 var selFragment = result.selection = document.createDocumentFragment();
1261
1262 for (var i = 0; i < doc.sel.ranges.length; i++) {
1263 var range = doc.sel.ranges[i];
1264 var collapsed = range.empty();
1265 if (collapsed || cm.options.showCursorWhenSelecting)
1266 drawSelectionCursor(cm, range, curFragment);
1267 if (!collapsed)
1268 drawSelectionRange(cm, range, selFragment);
1269 }
769
1270
770 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1271 // Move the hidden textarea near the cursor to prevent scrolling artifacts
771 if (cm.options.moveInputWithCursor) {
1272 if (cm.options.moveInputWithCursor) {
772 var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
1273 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
773 var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
1274 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
774 display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1275 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
775 headPos.top + lineOff.top - wrapOff.top)) + "px";
1276 headPos.top + lineOff.top - wrapOff.top));
776 display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1277 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
777 headPos.left + lineOff.left - wrapOff.left)) + "px";
1278 headPos.left + lineOff.left - wrapOff.left));
778 }
1279 }
779 }
1280
780
1281 return result;
781 // No selection, plain cursor
1282 }
782 function updateSelectionCursor(cm) {
1283
783 var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
1284 function showSelection(cm, drawn) {
784 display.cursor.style.left = pos.left + "px";
1285 removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
785 display.cursor.style.top = pos.top + "px";
1286 removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
786 display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
1287 if (drawn.teTop != null) {
787 display.cursor.style.display = "";
1288 cm.display.inputDiv.style.top = drawn.teTop + "px";
1289 cm.display.inputDiv.style.left = drawn.teLeft + "px";
1290 }
1291 }
1292
1293 function updateSelection(cm) {
1294 showSelection(cm, drawSelection(cm));
1295 }
1296
1297 // Draws a cursor for the given range
1298 function drawSelectionCursor(cm, range, output) {
1299 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
1300
1301 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
1302 cursor.style.left = pos.left + "px";
1303 cursor.style.top = pos.top + "px";
1304 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
788
1305
789 if (pos.other) {
1306 if (pos.other) {
790 display.otherCursor.style.display = "";
1307 // Secondary cursor, shown when on a 'jump' in bi-directional text
791 display.otherCursor.style.left = pos.other.left + "px";
1308 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
792 display.otherCursor.style.top = pos.other.top + "px";
1309 otherCursor.style.display = "";
793 display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
1310 otherCursor.style.left = pos.other.left + "px";
794 } else { display.otherCursor.style.display = "none"; }
1311 otherCursor.style.top = pos.other.top + "px";
795 }
1312 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
796
1313 }
797 // Highlight selection
1314 }
798 function updateSelectionRange(cm) {
1315
799 var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
1316 // Draws the given range as a highlighted selection
1317 function drawSelectionRange(cm, range, output) {
1318 var display = cm.display, doc = cm.doc;
800 var fragment = document.createDocumentFragment();
1319 var fragment = document.createDocumentFragment();
801 var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
1320 var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right;
802
1321
803 function add(left, top, width, bottom) {
1322 function add(left, top, width, bottom) {
804 if (top < 0) top = 0;
1323 if (top < 0) top = 0;
1324 top = Math.round(top);
1325 bottom = Math.round(bottom);
805 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1326 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
806 "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
1327 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
807 "px; height: " + (bottom - top) + "px"));
1328 "px; height: " + (bottom - top) + "px"));
808 }
1329 }
809
1330
@@ -826,44 +1347,44 b' window.CodeMirror = (function() {'
826 left = leftPos.left;
1347 left = leftPos.left;
827 right = rightPos.right;
1348 right = rightPos.right;
828 }
1349 }
829 if (fromArg == null && from == 0) left = pl;
1350 if (fromArg == null && from == 0) left = leftSide;
830 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
1351 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
831 add(left, leftPos.top, null, leftPos.bottom);
1352 add(left, leftPos.top, null, leftPos.bottom);
832 left = pl;
1353 left = leftSide;
833 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
1354 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
834 }
1355 }
835 if (toArg == null && to == lineLen) right = clientWidth;
1356 if (toArg == null && to == lineLen) right = rightSide;
836 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
1357 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
837 start = leftPos;
1358 start = leftPos;
838 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
1359 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
839 end = rightPos;
1360 end = rightPos;
840 if (left < pl + 1) left = pl;
1361 if (left < leftSide + 1) left = leftSide;
841 add(left, rightPos.top, right - left, rightPos.bottom);
1362 add(left, rightPos.top, right - left, rightPos.bottom);
842 });
1363 });
843 return {start: start, end: end};
1364 return {start: start, end: end};
844 }
1365 }
845
1366
846 if (sel.from.line == sel.to.line) {
1367 var sFrom = range.from(), sTo = range.to();
847 drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
1368 if (sFrom.line == sTo.line) {
1369 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
848 } else {
1370 } else {
849 var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
1371 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
850 var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
1372 var singleVLine = visualLine(fromLine) == visualLine(toLine);
851 var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
1373 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
852 var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
1374 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
853 if (singleVLine) {
1375 if (singleVLine) {
854 if (leftEnd.top < rightStart.top - 2) {
1376 if (leftEnd.top < rightStart.top - 2) {
855 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
1377 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
856 add(pl, rightStart.top, rightStart.left, rightStart.bottom);
1378 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
857 } else {
1379 } else {
858 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
1380 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
859 }
1381 }
860 }
1382 }
861 if (leftEnd.bottom < rightStart.top)
1383 if (leftEnd.bottom < rightStart.top)
862 add(pl, leftEnd.bottom, null, rightStart.top);
1384 add(leftSide, leftEnd.bottom, null, rightStart.top);
863 }
1385 }
864
1386
865 removeChildrenAndAdd(display.selectionDiv, fragment);
1387 output.appendChild(fragment);
866 display.selectionDiv.style.display = "";
867 }
1388 }
868
1389
869 // Cursor-blinking
1390 // Cursor-blinking
@@ -872,39 +1393,45 b' window.CodeMirror = (function() {'
872 var display = cm.display;
1393 var display = cm.display;
873 clearInterval(display.blinker);
1394 clearInterval(display.blinker);
874 var on = true;
1395 var on = true;
875 display.cursor.style.visibility = display.otherCursor.style.visibility = "";
1396 display.cursorDiv.style.visibility = "";
876 display.blinker = setInterval(function() {
1397 if (cm.options.cursorBlinkRate > 0)
877 display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
1398 display.blinker = setInterval(function() {
878 }, cm.options.cursorBlinkRate);
1399 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
1400 }, cm.options.cursorBlinkRate);
1401 else if (cm.options.cursorBlinkRate < 0)
1402 display.cursorDiv.style.visibility = "hidden";
879 }
1403 }
880
1404
881 // HIGHLIGHT WORKER
1405 // HIGHLIGHT WORKER
882
1406
883 function startWorker(cm, time) {
1407 function startWorker(cm, time) {
884 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
1408 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
885 cm.state.highlight.set(time, bind(highlightWorker, cm));
1409 cm.state.highlight.set(time, bind(highlightWorker, cm));
886 }
1410 }
887
1411
888 function highlightWorker(cm) {
1412 function highlightWorker(cm) {
889 var doc = cm.doc;
1413 var doc = cm.doc;
890 if (doc.frontier < doc.first) doc.frontier = doc.first;
1414 if (doc.frontier < doc.first) doc.frontier = doc.first;
891 if (doc.frontier >= cm.display.showingTo) return;
1415 if (doc.frontier >= cm.display.viewTo) return;
892 var end = +new Date + cm.options.workTime;
1416 var end = +new Date + cm.options.workTime;
893 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
1417 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
894 var changed = [], prevChange;
1418 var changedLines = [];
895 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
1419
896 if (doc.frontier >= cm.display.showingFrom) { // Visible
1420 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
1421 if (doc.frontier >= cm.display.viewFrom) { // Visible
897 var oldStyles = line.styles;
1422 var oldStyles = line.styles;
898 line.styles = highlightLine(cm, line, state);
1423 var highlighted = highlightLine(cm, line, state, true);
899 var ischange = !oldStyles || oldStyles.length != line.styles.length;
1424 line.styles = highlighted.styles;
1425 var oldCls = line.styleClasses, newCls = highlighted.classes;
1426 if (newCls) line.styleClasses = newCls;
1427 else if (oldCls) line.styleClasses = null;
1428 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
1429 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
900 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
1430 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
901 if (ischange) {
1431 if (ischange) changedLines.push(doc.frontier);
902 if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
903 else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
904 }
905 line.stateAfter = copyState(doc.mode, state);
1432 line.stateAfter = copyState(doc.mode, state);
906 } else {
1433 } else {
907 processLine(cm, line, state);
1434 processLine(cm, line.text, state);
908 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
1435 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
909 }
1436 }
910 ++doc.frontier;
1437 ++doc.frontier;
@@ -913,11 +1440,10 b' window.CodeMirror = (function() {'
913 return true;
1440 return true;
914 }
1441 }
915 });
1442 });
916 if (changed.length)
1443 if (changedLines.length) runInOp(cm, function() {
917 operation(cm, function() {
1444 for (var i = 0; i < changedLines.length; i++)
918 for (var i = 0; i < changed.length; ++i)
1445 regLineChange(cm, changedLines[i], "text");
919 regChange(this, changed[i].start, changed[i].end);
1446 });
920 })();
921 }
1447 }
922
1448
923 // Finds the line to start with when starting a parse. Tries to
1449 // Finds the line to start with when starting a parse. Tries to
@@ -927,7 +1453,8 b' window.CodeMirror = (function() {'
927 // parse correctly.
1453 // parse correctly.
928 function findStartLine(cm, n, precise) {
1454 function findStartLine(cm, n, precise) {
929 var minindent, minline, doc = cm.doc;
1455 var minindent, minline, doc = cm.doc;
930 for (var search = n, lim = n - 100; search > lim; --search) {
1456 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1457 for (var search = n; search > lim; --search) {
931 if (search <= doc.first) return doc.first;
1458 if (search <= doc.first) return doc.first;
932 var line = getLine(doc, search - 1);
1459 var line = getLine(doc, search - 1);
933 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
1460 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
@@ -942,16 +1469,17 b' window.CodeMirror = (function() {'
942
1469
943 function getStateBefore(cm, n, precise) {
1470 function getStateBefore(cm, n, precise) {
944 var doc = cm.doc, display = cm.display;
1471 var doc = cm.doc, display = cm.display;
945 if (!doc.mode.startState) return true;
1472 if (!doc.mode.startState) return true;
946 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
1473 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
947 if (!state) state = startState(doc.mode);
1474 if (!state) state = startState(doc.mode);
948 else state = copyState(doc.mode, state);
1475 else state = copyState(doc.mode, state);
949 doc.iter(pos, n, function(line) {
1476 doc.iter(pos, n, function(line) {
950 processLine(cm, line, state);
1477 processLine(cm, line.text, state);
951 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
1478 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
952 line.stateAfter = save ? copyState(doc.mode, state) : null;
1479 line.stateAfter = save ? copyState(doc.mode, state) : null;
953 ++pos;
1480 ++pos;
954 });
1481 });
1482 if (precise) doc.frontier = pos;
955 return state;
1483 return state;
956 }
1484 }
957
1485
@@ -959,165 +1487,247 b' window.CodeMirror = (function() {'
959
1487
960 function paddingTop(display) {return display.lineSpace.offsetTop;}
1488 function paddingTop(display) {return display.lineSpace.offsetTop;}
961 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
1489 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
962 function paddingLeft(display) {
1490 function paddingH(display) {
963 var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
1491 if (display.cachedPaddingH) return display.cachedPaddingH;
964 return e.offsetLeft;
1492 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
965 }
1493 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
966
1494 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
967 function measureChar(cm, line, ch, data, bias) {
1495 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
968 var dir = -1;
1496 return data;
969 data = data || measureLine(cm, line);
1497 }
970
1498
971 for (var pos = ch;; pos += dir) {
1499 // Ensure the lineView.wrapping.heights array is populated. This is
972 var r = data[pos];
1500 // an array of bottom offsets for the lines that make up a drawn
973 if (r) break;
1501 // line. When lineWrapping is on, there might be more than one
974 if (dir < 0 && pos == 0) dir = 1;
1502 // height.
975 }
1503 function ensureLineHeights(cm, lineView, rect) {
976 bias = pos > ch ? "left" : pos < ch ? "right" : bias;
1504 var wrapping = cm.options.lineWrapping;
977 if (bias == "left" && r.leftSide) r = r.leftSide;
1505 var curWidth = wrapping && cm.display.scroller.clientWidth;
978 else if (bias == "right" && r.rightSide) r = r.rightSide;
1506 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
979 return {left: pos < ch ? r.right : r.left,
1507 var heights = lineView.measure.heights = [];
980 right: pos > ch ? r.left : r.right,
1508 if (wrapping) {
981 top: r.top,
1509 lineView.measure.width = curWidth;
982 bottom: r.bottom};
1510 var rects = lineView.text.firstChild.getClientRects();
983 }
1511 for (var i = 0; i < rects.length - 1; i++) {
984
1512 var cur = rects[i], next = rects[i + 1];
985 function findCachedMeasurement(cm, line) {
1513 if (Math.abs(cur.bottom - next.bottom) > 2)
986 var cache = cm.display.measureLineCache;
1514 heights.push((cur.bottom + next.top) / 2 - rect.top);
987 for (var i = 0; i < cache.length; ++i) {
988 var memo = cache[i];
989 if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
990 cm.display.scroller.clientWidth == memo.width &&
991 memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
992 return memo;
993 }
994 }
995
996 function clearCachedMeasurement(cm, line) {
997 var exists = findCachedMeasurement(cm, line);
998 if (exists) exists.text = exists.measure = exists.markedSpans = null;
999 }
1000
1001 function measureLine(cm, line) {
1002 // First look in the cache
1003 var cached = findCachedMeasurement(cm, line);
1004 if (cached) return cached.measure;
1005
1006 // Failing that, recompute and store result in cache
1007 var measure = measureLineInner(cm, line);
1008 var cache = cm.display.measureLineCache;
1009 var memo = {text: line.text, width: cm.display.scroller.clientWidth,
1010 markedSpans: line.markedSpans, measure: measure,
1011 classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
1012 if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
1013 else cache.push(memo);
1014 return measure;
1015 }
1016
1017 function measureLineInner(cm, line) {
1018 var display = cm.display, measure = emptyArray(line.text.length);
1019 var pre = lineContent(cm, line, measure, true);
1020
1021 // IE does not cache element positions of inline elements between
1022 // calls to getBoundingClientRect. This makes the loop below,
1023 // which gathers the positions of all the characters on the line,
1024 // do an amount of layout work quadratic to the number of
1025 // characters. When line wrapping is off, we try to improve things
1026 // by first subdividing the line into a bunch of inline blocks, so
1027 // that IE can reuse most of the layout information from caches
1028 // for those blocks. This does interfere with line wrapping, so it
1029 // doesn't work when wrapping is on, but in that case the
1030 // situation is slightly better, since IE does cache line-wrapping
1031 // information and only recomputes per-line.
1032 if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1033 var fragment = document.createDocumentFragment();
1034 var chunk = 10, n = pre.childNodes.length;
1035 for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1036 var wrap = elt("div", null, null, "display: inline-block");
1037 for (var j = 0; j < chunk && n; ++j) {
1038 wrap.appendChild(pre.firstChild);
1039 --n;
1040 }
1515 }
1041 fragment.appendChild(wrap);
1516 }
1042 }
1517 heights.push(rect.bottom - rect.top);
1043 pre.appendChild(fragment);
1518 }
1044 }
1519 }
1045
1520
1046 removeChildrenAndAdd(display.measure, pre);
1521 // Find a line map (mapping character offsets to text nodes) and a
1047
1522 // measurement cache for the given line number. (A line view might
1048 var outer = getRect(display.lineDiv);
1523 // contain multiple lines when collapsed ranges are present.)
1049 var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1524 function mapFromLineView(lineView, line, lineN) {
1050 // Work around an IE7/8 bug where it will sometimes have randomly
1525 if (lineView.line == line)
1051 // replaced our pre with a clone at this point.
1526 return {map: lineView.measure.map, cache: lineView.measure.cache};
1052 if (ie_lt9 && display.measure.first != pre)
1527 for (var i = 0; i < lineView.rest.length; i++)
1053 removeChildrenAndAdd(display.measure, pre);
1528 if (lineView.rest[i] == line)
1054
1529 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
1055 function measureRect(rect) {
1530 for (var i = 0; i < lineView.rest.length; i++)
1056 var top = rect.top - outer.top, bot = rect.bottom - outer.top;
1531 if (lineNo(lineView.rest[i]) > lineN)
1057 if (bot > maxBot) bot = maxBot;
1532 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
1058 if (top < 0) top = 0;
1533 }
1059 for (var i = vranges.length - 2; i >= 0; i -= 2) {
1534
1060 var rtop = vranges[i], rbot = vranges[i+1];
1535 // Render a line into the hidden node display.externalMeasured. Used
1061 if (rtop > bot || rbot < top) continue;
1536 // when measurement is needed for a line that's not in the viewport.
1062 if (rtop <= top && rbot >= bot ||
1537 function updateExternalMeasurement(cm, line) {
1063 top <= rtop && bot >= rbot ||
1538 line = visualLine(line);
1064 Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1539 var lineN = lineNo(line);
1065 vranges[i] = Math.min(top, rtop);
1540 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
1066 vranges[i+1] = Math.max(bot, rbot);
1541 view.lineN = lineN;
1067 break;
1542 var built = view.built = buildLineContent(cm, view);
1543 view.text = built.pre;
1544 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
1545 return view;
1546 }
1547
1548 // Get a {top, bottom, left, right} box (in line-local coordinates)
1549 // for a given character.
1550 function measureChar(cm, line, ch, bias) {
1551 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
1552 }
1553
1554 // Find a line view that corresponds to the given line number.
1555 function findViewForLine(cm, lineN) {
1556 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
1557 return cm.display.view[findViewIndex(cm, lineN)];
1558 var ext = cm.display.externalMeasured;
1559 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
1560 return ext;
1561 }
1562
1563 // Measurement can be split in two steps, the set-up work that
1564 // applies to the whole line, and the measurement of the actual
1565 // character. Functions like coordsChar, that need to do a lot of
1566 // measurements in a row, can thus ensure that the set-up work is
1567 // only done once.
1568 function prepareMeasureForLine(cm, line) {
1569 var lineN = lineNo(line);
1570 var view = findViewForLine(cm, lineN);
1571 if (view && !view.text)
1572 view = null;
1573 else if (view && view.changes)
1574 updateLineForChanges(cm, view, lineN, getDimensions(cm));
1575 if (!view)
1576 view = updateExternalMeasurement(cm, line);
1577
1578 var info = mapFromLineView(view, line, lineN);
1579 return {
1580 line: line, view: view, rect: null,
1581 map: info.map, cache: info.cache, before: info.before,
1582 hasHeights: false
1583 };
1584 }
1585
1586 // Given a prepared measurement object, measures the position of an
1587 // actual character (or fetches it from the cache).
1588 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
1589 if (prepared.before) ch = -1;
1590 var key = ch + (bias || ""), found;
1591 if (prepared.cache.hasOwnProperty(key)) {
1592 found = prepared.cache[key];
1593 } else {
1594 if (!prepared.rect)
1595 prepared.rect = prepared.view.text.getBoundingClientRect();
1596 if (!prepared.hasHeights) {
1597 ensureLineHeights(cm, prepared.view, prepared.rect);
1598 prepared.hasHeights = true;
1599 }
1600 found = measureCharInner(cm, prepared, ch, bias);
1601 if (!found.bogus) prepared.cache[key] = found;
1602 }
1603 return {left: found.left, right: found.right,
1604 top: varHeight ? found.rtop : found.top,
1605 bottom: varHeight ? found.rbottom : found.bottom};
1606 }
1607
1608 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
1609
1610 function measureCharInner(cm, prepared, ch, bias) {
1611 var map = prepared.map;
1612
1613 var node, start, end, collapse;
1614 // First, search the line map for the text node corresponding to,
1615 // or closest to, the target character.
1616 for (var i = 0; i < map.length; i += 3) {
1617 var mStart = map[i], mEnd = map[i + 1];
1618 if (ch < mStart) {
1619 start = 0; end = 1;
1620 collapse = "left";
1621 } else if (ch < mEnd) {
1622 start = ch - mStart;
1623 end = start + 1;
1624 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
1625 end = mEnd - mStart;
1626 start = end - 1;
1627 if (ch >= mEnd) collapse = "right";
1628 }
1629 if (start != null) {
1630 node = map[i + 2];
1631 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
1632 collapse = bias;
1633 if (bias == "left" && start == 0)
1634 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
1635 node = map[(i -= 3) + 2];
1636 collapse = "left";
1637 }
1638 if (bias == "right" && start == mEnd - mStart)
1639 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
1640 node = map[(i += 3) + 2];
1641 collapse = "right";
1642 }
1643 break;
1644 }
1645 }
1646
1647 var rect;
1648 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
1649 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
1650 while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
1651 while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
1652 if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) {
1653 rect = node.parentNode.getBoundingClientRect();
1654 } else if (ie && cm.options.lineWrapping) {
1655 var rects = range(node, start, end).getClientRects();
1656 if (rects.length)
1657 rect = rects[bias == "right" ? rects.length - 1 : 0];
1658 else
1659 rect = nullRect;
1660 } else {
1661 rect = range(node, start, end).getBoundingClientRect() || nullRect;
1068 }
1662 }
1069 }
1663 if (rect.left || rect.right || start == 0) break;
1070 if (i < 0) { i = vranges.length; vranges.push(top, bot); }
1664 end = start;
1071 return {left: rect.left - outer.left,
1665 start = start - 1;
1072 right: rect.right - outer.left,
1666 collapse = "right";
1073 top: i, bottom: null};
1667 }
1074 }
1668 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
1075 function finishRect(rect) {
1669 } else { // If it is a widget, simply get the box for the whole widget.
1076 rect.bottom = vranges[rect.top+1];
1670 if (start > 0) collapse = bias = "right";
1077 rect.top = vranges[rect.top];
1671 var rects;
1078 }
1672 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
1079
1673 rect = rects[bias == "right" ? rects.length - 1 : 0];
1080 for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1674 else
1081 var node = cur, rect = null;
1675 rect = node.getBoundingClientRect();
1082 // A widget might wrap, needs special care
1676 }
1083 if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
1677 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
1084 if (cur.firstChild.nodeType == 1) node = cur.firstChild;
1678 var rSpan = node.parentNode.getClientRects()[0];
1085 var rects = node.getClientRects();
1679 if (rSpan)
1086 if (rects.length > 1) {
1680 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
1087 rect = data[i] = measureRect(rects[0]);
1681 else
1088 rect.rightSide = measureRect(rects[rects.length - 1]);
1682 rect = nullRect;
1089 }
1683 }
1090 }
1684
1091 if (!rect) rect = data[i] = measureRect(getRect(node));
1685 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
1092 if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
1686 var mid = (rtop + rbot) / 2;
1093 if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
1687 var heights = prepared.view.measure.heights;
1094 }
1688 for (var i = 0; i < heights.length - 1; i++)
1095 for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1689 if (mid < heights[i]) break;
1096 finishRect(cur);
1690 var top = i ? heights[i - 1] : 0, bot = heights[i];
1097 if (cur.leftSide) finishRect(cur.leftSide);
1691 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
1098 if (cur.rightSide) finishRect(cur.rightSide);
1692 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
1099 }
1693 top: top, bottom: bot};
1100 return data;
1694 if (!rect.left && !rect.right) result.bogus = true;
1101 }
1695 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
1102
1696
1103 function measureLineWidth(cm, line) {
1697 return result;
1104 var hasBadSpan = false;
1698 }
1105 if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1699
1106 var sp = line.markedSpans[i];
1700 // Work around problem with bounding client rects on ranges being
1107 if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1701 // returned incorrectly when zoomed on IE10 and below.
1108 }
1702 function maybeUpdateRectForZooming(measure, rect) {
1109 var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1703 if (!window.screen || screen.logicalXDPI == null ||
1110 if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
1704 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
1111
1705 return rect;
1112 var pre = lineContent(cm, line, null, true);
1706 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
1113 var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1707 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
1114 removeChildrenAndAdd(cm.display.measure, pre);
1708 return {left: rect.left * scaleX, right: rect.right * scaleX,
1115 return getRect(end).right - getRect(cm.display.lineDiv).left;
1709 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
1710 }
1711
1712 function clearLineMeasurementCacheFor(lineView) {
1713 if (lineView.measure) {
1714 lineView.measure.cache = {};
1715 lineView.measure.heights = null;
1716 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1717 lineView.measure.caches[i] = {};
1718 }
1719 }
1720
1721 function clearLineMeasurementCache(cm) {
1722 cm.display.externalMeasure = null;
1723 removeChildren(cm.display.lineMeasure);
1724 for (var i = 0; i < cm.display.view.length; i++)
1725 clearLineMeasurementCacheFor(cm.display.view[i]);
1116 }
1726 }
1117
1727
1118 function clearCaches(cm) {
1728 function clearCaches(cm) {
1119 cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1729 clearLineMeasurementCache(cm);
1120 cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1730 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
1121 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1731 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1122 cm.display.lineNumChars = null;
1732 cm.display.lineNumChars = null;
1123 }
1733 }
@@ -1125,7 +1735,9 b' window.CodeMirror = (function() {'
1125 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1735 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1126 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1736 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1127
1737
1128 // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1738 // Converts a {top, bottom, left, right} box from line-local
1739 // coordinates into another coordinate system. Context may be one of
1740 // "line", "div" (display.lineDiv), "local"/null (editor), or "page".
1129 function intoCoordSystem(cm, lineObj, rect, context) {
1741 function intoCoordSystem(cm, lineObj, rect, context) {
1130 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1742 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1131 var size = widgetHeight(lineObj.widgets[i]);
1743 var size = widgetHeight(lineObj.widgets[i]);
@@ -1133,11 +1745,11 b' window.CodeMirror = (function() {'
1133 }
1745 }
1134 if (context == "line") return rect;
1746 if (context == "line") return rect;
1135 if (!context) context = "local";
1747 if (!context) context = "local";
1136 var yOff = heightAtLine(cm, lineObj);
1748 var yOff = heightAtLine(lineObj);
1137 if (context == "local") yOff += paddingTop(cm.display);
1749 if (context == "local") yOff += paddingTop(cm.display);
1138 else yOff -= cm.display.viewOffset;
1750 else yOff -= cm.display.viewOffset;
1139 if (context == "page" || context == "window") {
1751 if (context == "page" || context == "window") {
1140 var lOff = getRect(cm.display.lineSpace);
1752 var lOff = cm.display.lineSpace.getBoundingClientRect();
1141 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1753 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1142 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1754 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1143 rect.left += xOff; rect.right += xOff;
1755 rect.left += xOff; rect.right += xOff;
@@ -1146,8 +1758,8 b' window.CodeMirror = (function() {'
1146 return rect;
1758 return rect;
1147 }
1759 }
1148
1760
1149 // Context may be "window", "page", "div", or "local"/null
1761 // Coverts a box from "div" coords to another coordinate system.
1150 // Result is in "div" coords
1762 // Context may be "window", "page", "div", or "local"/null.
1151 function fromCoordSystem(cm, coords, context) {
1763 function fromCoordSystem(cm, coords, context) {
1152 if (context == "div") return coords;
1764 if (context == "div") return coords;
1153 var left = coords.left, top = coords.top;
1765 var left = coords.left, top = coords.top;
@@ -1156,25 +1768,28 b' window.CodeMirror = (function() {'
1156 left -= pageScrollX();
1768 left -= pageScrollX();
1157 top -= pageScrollY();
1769 top -= pageScrollY();
1158 } else if (context == "local" || !context) {
1770 } else if (context == "local" || !context) {
1159 var localBox = getRect(cm.display.sizer);
1771 var localBox = cm.display.sizer.getBoundingClientRect();
1160 left += localBox.left;
1772 left += localBox.left;
1161 top += localBox.top;
1773 top += localBox.top;
1162 }
1774 }
1163
1775
1164 var lineSpaceBox = getRect(cm.display.lineSpace);
1776 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
1165 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1777 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1166 }
1778 }
1167
1779
1168 function charCoords(cm, pos, context, lineObj, bias) {
1780 function charCoords(cm, pos, context, lineObj, bias) {
1169 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1781 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1170 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
1782 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
1171 }
1783 }
1172
1784
1173 function cursorCoords(cm, pos, context, lineObj, measurement) {
1785 // Returns a box for a given cursor position, which may have an
1786 // 'other' property containing the position of the secondary cursor
1787 // on a bidi boundary.
1788 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
1174 lineObj = lineObj || getLine(cm.doc, pos.line);
1789 lineObj = lineObj || getLine(cm.doc, pos.line);
1175 if (!measurement) measurement = measureLine(cm, lineObj);
1790 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
1176 function get(ch, right) {
1791 function get(ch, right) {
1177 var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
1792 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
1178 if (right) m.left = m.right; else m.right = m.left;
1793 if (right) m.left = m.right; else m.right = m.left;
1179 return intoCoordSystem(cm, lineObj, m, context);
1794 return intoCoordSystem(cm, lineObj, m, context);
1180 }
1795 }
@@ -1200,43 +1815,59 b' window.CodeMirror = (function() {'
1200 return val;
1815 return val;
1201 }
1816 }
1202
1817
1818 // Used to cheaply estimate the coordinates for a position. Used for
1819 // intermediate scroll updates.
1820 function estimateCoords(cm, pos) {
1821 var left = 0, pos = clipPos(cm.doc, pos);
1822 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
1823 var lineObj = getLine(cm.doc, pos.line);
1824 var top = heightAtLine(lineObj) + paddingTop(cm.display);
1825 return {left: left, right: left, top: top, bottom: top + lineObj.height};
1826 }
1827
1828 // Positions returned by coordsChar contain some extra information.
1829 // xRel is the relative x position of the input coordinates compared
1830 // to the found position (so xRel > 0 means the coordinates are to
1831 // the right of the character position, for example). When outside
1832 // is true, that means the coordinates lie outside the line's
1833 // vertical range.
1203 function PosWithInfo(line, ch, outside, xRel) {
1834 function PosWithInfo(line, ch, outside, xRel) {
1204 var pos = new Pos(line, ch);
1835 var pos = Pos(line, ch);
1205 pos.xRel = xRel;
1836 pos.xRel = xRel;
1206 if (outside) pos.outside = true;
1837 if (outside) pos.outside = true;
1207 return pos;
1838 return pos;
1208 }
1839 }
1209
1840
1210 // Coords must be lineSpace-local
1841 // Compute the character position closest to the given coordinates.
1842 // Input must be lineSpace-local ("div" coordinate system).
1211 function coordsChar(cm, x, y) {
1843 function coordsChar(cm, x, y) {
1212 var doc = cm.doc;
1844 var doc = cm.doc;
1213 y += cm.display.viewOffset;
1845 y += cm.display.viewOffset;
1214 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1846 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1215 var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1847 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1216 if (lineNo > last)
1848 if (lineN > last)
1217 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1849 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1218 if (x < 0) x = 0;
1850 if (x < 0) x = 0;
1219
1851
1852 var lineObj = getLine(doc, lineN);
1220 for (;;) {
1853 for (;;) {
1221 var lineObj = getLine(doc, lineNo);
1854 var found = coordsCharInner(cm, lineObj, lineN, x, y);
1222 var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1223 var merged = collapsedSpanAtEnd(lineObj);
1855 var merged = collapsedSpanAtEnd(lineObj);
1224 var mergedPos = merged && merged.find();
1856 var mergedPos = merged && merged.find(0, true);
1225 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1857 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1226 lineNo = mergedPos.to.line;
1858 lineN = lineNo(lineObj = mergedPos.to.line);
1227 else
1859 else
1228 return found;
1860 return found;
1229 }
1861 }
1230 }
1862 }
1231
1863
1232 function coordsCharInner(cm, lineObj, lineNo, x, y) {
1864 function coordsCharInner(cm, lineObj, lineNo, x, y) {
1233 var innerOff = y - heightAtLine(cm, lineObj);
1865 var innerOff = y - heightAtLine(lineObj);
1234 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1866 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1235 var measurement = measureLine(cm, lineObj);
1867 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
1236
1868
1237 function getX(ch) {
1869 function getX(ch) {
1238 var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1870 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
1239 lineObj, measurement);
1240 wrongLine = true;
1871 wrongLine = true;
1241 if (innerOff > sp.bottom) return sp.left - adjust;
1872 if (innerOff > sp.bottom) return sp.left - adjust;
1242 else if (innerOff < sp.top) return sp.left + adjust;
1873 else if (innerOff < sp.top) return sp.left + adjust;
@@ -1254,9 +1885,9 b' window.CodeMirror = (function() {'
1254 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1885 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1255 var ch = x < fromX || x - fromX <= toX - x ? from : to;
1886 var ch = x < fromX || x - fromX <= toX - x ? from : to;
1256 var xDiff = x - (ch == from ? fromX : toX);
1887 var xDiff = x - (ch == from ? fromX : toX);
1257 while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1888 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
1258 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1889 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1259 xDiff < 0 ? -1 : xDiff ? 1 : 0);
1890 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
1260 return pos;
1891 return pos;
1261 }
1892 }
1262 var step = Math.ceil(dist / 2), middle = from + step;
1893 var step = Math.ceil(dist / 2), middle = from + step;
@@ -1271,6 +1902,7 b' window.CodeMirror = (function() {'
1271 }
1902 }
1272
1903
1273 var measureText;
1904 var measureText;
1905 // Compute the default text height.
1274 function textHeight(display) {
1906 function textHeight(display) {
1275 if (display.cachedTextHeight != null) return display.cachedTextHeight;
1907 if (display.cachedTextHeight != null) return display.cachedTextHeight;
1276 if (measureText == null) {
1908 if (measureText == null) {
@@ -1290,133 +1922,442 b' window.CodeMirror = (function() {'
1290 return height || 1;
1922 return height || 1;
1291 }
1923 }
1292
1924
1925 // Compute the default character width.
1293 function charWidth(display) {
1926 function charWidth(display) {
1294 if (display.cachedCharWidth != null) return display.cachedCharWidth;
1927 if (display.cachedCharWidth != null) return display.cachedCharWidth;
1295 var anchor = elt("span", "x");
1928 var anchor = elt("span", "xxxxxxxxxx");
1296 var pre = elt("pre", [anchor]);
1929 var pre = elt("pre", [anchor]);
1297 removeChildrenAndAdd(display.measure, pre);
1930 removeChildrenAndAdd(display.measure, pre);
1298 var width = anchor.offsetWidth;
1931 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
1299 if (width > 2) display.cachedCharWidth = width;
1932 if (width > 2) display.cachedCharWidth = width;
1300 return width || 10;
1933 return width || 10;
1301 }
1934 }
1302
1935
1303 // OPERATIONS
1936 // OPERATIONS
1304
1937
1305 // Operations are used to wrap changes in such a way that each
1938 // Operations are used to wrap a series of changes to the editor
1306 // change won't have to update the cursor and display (which would
1939 // state in such a way that each change won't have to update the
1307 // be awkward, slow, and error-prone), but instead updates are
1940 // cursor and display (which would be awkward, slow, and
1308 // batched and then all combined and executed at once.
1941 // error-prone). Instead, display updates are batched and then all
1942 // combined and executed at once.
1943
1944 var operationGroup = null;
1309
1945
1310 var nextOpId = 0;
1946 var nextOpId = 0;
1947 // Start a new operation.
1311 function startOperation(cm) {
1948 function startOperation(cm) {
1312 cm.curOp = {
1949 cm.curOp = {
1313 // An array of ranges of lines that have to be updated. See
1950 cm: cm,
1314 // updateDisplay.
1951 viewChanged: false, // Flag that indicates that lines might need to be redrawn
1315 changes: [],
1952 startHeight: cm.doc.height, // Used to detect need to update scrollbar
1316 forceUpdate: false,
1953 forceUpdate: false, // Used to force a redraw
1317 updateInput: null,
1954 updateInput: null, // Whether to reset the input textarea
1318 userSelChange: null,
1955 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
1319 textChanged: null,
1956 changeObjs: null, // Accumulated changes, for firing change events
1320 selectionChanged: false,
1957 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
1321 cursorActivity: false,
1958 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
1322 updateMaxLine: false,
1959 selectionChanged: false, // Whether the selection needs to be redrawn
1323 updateScrollPos: false,
1960 updateMaxLine: false, // Set when the widest line needs to be determined anew
1324 id: ++nextOpId
1961 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
1962 scrollToPos: null, // Used to scroll to a specific position
1963 id: ++nextOpId // Unique ID
1325 };
1964 };
1326 if (!delayedCallbackDepth++) delayedCallbacks = [];
1965 if (operationGroup) {
1327 }
1966 operationGroup.ops.push(cm.curOp);
1328
1967 } else {
1968 cm.curOp.ownsGroup = operationGroup = {
1969 ops: [cm.curOp],
1970 delayedCallbacks: []
1971 };
1972 }
1973 }
1974
1975 function fireCallbacksForOps(group) {
1976 // Calls delayed callbacks and cursorActivity handlers until no
1977 // new ones appear
1978 var callbacks = group.delayedCallbacks, i = 0;
1979 do {
1980 for (; i < callbacks.length; i++)
1981 callbacks[i]();
1982 for (var j = 0; j < group.ops.length; j++) {
1983 var op = group.ops[j];
1984 if (op.cursorActivityHandlers)
1985 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
1986 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
1987 }
1988 } while (i < callbacks.length);
1989 }
1990
1991 // Finish an operation, updating the display and signalling delayed events
1329 function endOperation(cm) {
1992 function endOperation(cm) {
1330 var op = cm.curOp, doc = cm.doc, display = cm.display;
1993 var op = cm.curOp, group = op.ownsGroup;
1331 cm.curOp = null;
1994 if (!group) return;
1332
1995
1333 if (op.updateMaxLine) computeMaxLength(cm);
1996 try { fireCallbacksForOps(group); }
1334 if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
1997 finally {
1335 var width = measureLineWidth(cm, display.maxLine);
1998 operationGroup = null;
1336 display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1999 for (var i = 0; i < group.ops.length; i++)
1337 display.maxLineChanged = false;
2000 group.ops[i].cm.curOp = null;
1338 var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
2001 endOperations(group);
1339 if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
2002 }
1340 setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
2003 }
1341 }
2004
1342 var newScrollPos, updated;
2005 // The DOM updates done when an operation finishes are batched so
1343 if (op.updateScrollPos) {
2006 // that the minimum number of relayouts are required.
1344 newScrollPos = op.updateScrollPos;
2007 function endOperations(group) {
1345 } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
2008 var ops = group.ops;
1346 var coords = cursorCoords(cm, doc.sel.head);
2009 for (var i = 0; i < ops.length; i++) // Read DOM
1347 newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
2010 endOperation_R1(ops[i]);
1348 }
2011 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
1349 if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
2012 endOperation_W1(ops[i]);
1350 updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate);
2013 for (var i = 0; i < ops.length; i++) // Read DOM
1351 if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
2014 endOperation_R2(ops[i]);
1352 }
2015 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
1353 if (!updated && op.selectionChanged) updateSelection(cm);
2016 endOperation_W2(ops[i]);
1354 if (op.updateScrollPos) {
2017 for (var i = 0; i < ops.length; i++) // Read DOM
1355 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
2018 endOperation_finish(ops[i]);
1356 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
2019 }
1357 alignHorizontally(cm);
2020
1358 if (op.scrollToPos)
2021 function endOperation_R1(op) {
1359 scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
2022 var cm = op.cm, display = cm.display;
1360 } else if (newScrollPos) {
2023 if (op.updateMaxLine) findMaxLine(cm);
1361 scrollCursorIntoView(cm);
2024
1362 }
2025 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
2026 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
2027 op.scrollToPos.to.line >= display.viewTo) ||
2028 display.maxLineChanged && cm.options.lineWrapping;
2029 op.update = op.mustUpdate &&
2030 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
2031 }
2032
2033 function endOperation_W1(op) {
2034 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
2035 }
2036
2037 function endOperation_R2(op) {
2038 var cm = op.cm, display = cm.display;
2039 if (op.updatedDisplay) updateHeightsInViewport(cm);
2040
2041 op.barMeasure = measureForScrollbars(cm);
2042
2043 // If the max line changed since it was last measured, measure it,
2044 // and ensure the document's width matches it.
2045 // updateDisplay_W2 will use these properties to do the actual resizing
2046 if (display.maxLineChanged && !cm.options.lineWrapping) {
2047 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
2048 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
2049 scrollerCutOff - display.scroller.clientWidth);
2050 }
2051
2052 if (op.updatedDisplay || op.selectionChanged)
2053 op.newSelectionNodes = drawSelection(cm);
2054 }
2055
2056 function endOperation_W2(op) {
2057 var cm = op.cm;
2058
2059 if (op.adjustWidthTo != null) {
2060 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
2061 if (op.maxScrollLeft < cm.doc.scrollLeft)
2062 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
2063 cm.display.maxLineChanged = false;
2064 }
2065
2066 if (op.newSelectionNodes)
2067 showSelection(cm, op.newSelectionNodes);
2068 if (op.updatedDisplay)
2069 setDocumentHeight(cm, op.barMeasure);
2070 if (op.updatedDisplay || op.startHeight != cm.doc.height)
2071 updateScrollbars(cm, op.barMeasure);
2072
1363 if (op.selectionChanged) restartBlink(cm);
2073 if (op.selectionChanged) restartBlink(cm);
1364
2074
1365 if (cm.state.focused && op.updateInput)
2075 if (cm.state.focused && op.updateInput)
1366 resetInput(cm, op.userSelChange);
2076 resetInput(cm, op.typing);
1367
2077 }
2078
2079 function endOperation_finish(op) {
2080 var cm = op.cm, display = cm.display, doc = cm.doc;
2081
2082 if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.display.scroller.scrollWidth) > 1)
2083 updateScrollbars(cm);
2084
2085 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
2086
2087 // Abort mouse wheel delta measurement, when scrolling explicitly
2088 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
2089 display.wheelStartX = display.wheelStartY = null;
2090
2091 // Propagate the scroll position to the actual DOM scroller
2092 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
2093 var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
2094 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
2095 }
2096 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
2097 var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
2098 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
2099 alignHorizontally(cm);
2100 }
2101 // If we need to scroll a specific position into view, do so.
2102 if (op.scrollToPos) {
2103 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
2104 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
2105 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
2106 }
2107
2108 // Fire events for markers that are hidden/unidden by editing or
2109 // undoing
1368 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
2110 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1369 if (hidden) for (var i = 0; i < hidden.length; ++i)
2111 if (hidden) for (var i = 0; i < hidden.length; ++i)
1370 if (!hidden[i].lines.length) signal(hidden[i], "hide");
2112 if (!hidden[i].lines.length) signal(hidden[i], "hide");
1371 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
2113 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1372 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
2114 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1373
2115
1374 var delayed;
2116 if (display.wrapper.offsetHeight)
1375 if (!--delayedCallbackDepth) {
2117 doc.scrollTop = cm.display.scroller.scrollTop;
1376 delayed = delayedCallbacks;
2118
1377 delayedCallbacks = null;
2119 // Apply workaround for two webkit bugs
1378 }
2120 if (op.updatedDisplay && webkit) {
1379 if (op.textChanged)
2121 if (cm.options.lineWrapping)
1380 signal(cm, "change", cm, op.textChanged);
2122 checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420)
1381 if (op.cursorActivity) signal(cm, "cursorActivity", cm);
2123 if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth &&
1382 if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
2124 op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 &&
1383 }
2125 !hScrollbarTakesSpace(cm))
1384
2126 updateScrollbars(cm); // (Issue #2562)
2127 }
2128
2129 // Fire change events, and delayed event handlers
2130 if (op.changeObjs)
2131 signal(cm, "changes", cm, op.changeObjs);
2132 }
2133
2134 // Run the given function in an operation
2135 function runInOp(cm, f) {
2136 if (cm.curOp) return f();
2137 startOperation(cm);
2138 try { return f(); }
2139 finally { endOperation(cm); }
2140 }
1385 // Wraps a function in an operation. Returns the wrapped function.
2141 // Wraps a function in an operation. Returns the wrapped function.
1386 function operation(cm1, f) {
2142 function operation(cm, f) {
2143 return function() {
2144 if (cm.curOp) return f.apply(cm, arguments);
2145 startOperation(cm);
2146 try { return f.apply(cm, arguments); }
2147 finally { endOperation(cm); }
2148 };
2149 }
2150 // Used to add methods to editor and doc instances, wrapping them in
2151 // operations.
2152 function methodOp(f) {
1387 return function() {
2153 return function() {
1388 var cm = cm1 || this, withOp = !cm.curOp;
2154 if (this.curOp) return f.apply(this, arguments);
1389 if (withOp) startOperation(cm);
2155 startOperation(this);
1390 try { var result = f.apply(cm, arguments); }
2156 try { return f.apply(this, arguments); }
1391 finally { if (withOp) endOperation(cm); }
2157 finally { endOperation(this); }
1392 return result;
1393 };
2158 };
1394 }
2159 }
1395 function docOperation(f) {
2160 function docMethodOp(f) {
1396 return function() {
2161 return function() {
1397 var withOp = this.cm && !this.cm.curOp, result;
2162 var cm = this.cm;
1398 if (withOp) startOperation(this.cm);
2163 if (!cm || cm.curOp) return f.apply(this, arguments);
1399 try { result = f.apply(this, arguments); }
2164 startOperation(cm);
1400 finally { if (withOp) endOperation(this.cm); }
2165 try { return f.apply(this, arguments); }
1401 return result;
2166 finally { endOperation(cm); }
1402 };
2167 };
1403 }
2168 }
1404 function runInOp(cm, f) {
2169
1405 var withOp = !cm.curOp, result;
2170 // VIEW TRACKING
1406 if (withOp) startOperation(cm);
2171
1407 try { result = f(); }
2172 // These objects are used to represent the visible (currently drawn)
1408 finally { if (withOp) endOperation(cm); }
2173 // part of the document. A LineView may correspond to multiple
1409 return result;
2174 // logical lines, if those are connected by collapsed ranges.
1410 }
2175 function LineView(doc, line, lineN) {
1411
2176 // The starting line
2177 this.line = line;
2178 // Continuing lines, if any
2179 this.rest = visualLineContinued(line);
2180 // Number of logical lines in this visual line
2181 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
2182 this.node = this.text = null;
2183 this.hidden = lineIsHidden(doc, line);
2184 }
2185
2186 // Create a range of LineView objects for the given lines.
2187 function buildViewArray(cm, from, to) {
2188 var array = [], nextPos;
2189 for (var pos = from; pos < to; pos = nextPos) {
2190 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
2191 nextPos = pos + view.size;
2192 array.push(view);
2193 }
2194 return array;
2195 }
2196
2197 // Updates the display.view data structure for a given change to the
2198 // document. From and to are in pre-change coordinates. Lendiff is
2199 // the amount of lines added or subtracted by the change. This is
2200 // used for changes that span multiple lines, or change the way
2201 // lines are divided into visual lines. regLineChange (below)
2202 // registers single-line changes.
1412 function regChange(cm, from, to, lendiff) {
2203 function regChange(cm, from, to, lendiff) {
1413 if (from == null) from = cm.doc.first;
2204 if (from == null) from = cm.doc.first;
1414 if (to == null) to = cm.doc.first + cm.doc.size;
2205 if (to == null) to = cm.doc.first + cm.doc.size;
1415 cm.curOp.changes.push({from: from, to: to, diff: lendiff});
2206 if (!lendiff) lendiff = 0;
2207
2208 var display = cm.display;
2209 if (lendiff && to < display.viewTo &&
2210 (display.updateLineNumbers == null || display.updateLineNumbers > from))
2211 display.updateLineNumbers = from;
2212
2213 cm.curOp.viewChanged = true;
2214
2215 if (from >= display.viewTo) { // Change after
2216 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
2217 resetView(cm);
2218 } else if (to <= display.viewFrom) { // Change before
2219 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
2220 resetView(cm);
2221 } else {
2222 display.viewFrom += lendiff;
2223 display.viewTo += lendiff;
2224 }
2225 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
2226 resetView(cm);
2227 } else if (from <= display.viewFrom) { // Top overlap
2228 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
2229 if (cut) {
2230 display.view = display.view.slice(cut.index);
2231 display.viewFrom = cut.lineN;
2232 display.viewTo += lendiff;
2233 } else {
2234 resetView(cm);
2235 }
2236 } else if (to >= display.viewTo) { // Bottom overlap
2237 var cut = viewCuttingPoint(cm, from, from, -1);
2238 if (cut) {
2239 display.view = display.view.slice(0, cut.index);
2240 display.viewTo = cut.lineN;
2241 } else {
2242 resetView(cm);
2243 }
2244 } else { // Gap in the middle
2245 var cutTop = viewCuttingPoint(cm, from, from, -1);
2246 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
2247 if (cutTop && cutBot) {
2248 display.view = display.view.slice(0, cutTop.index)
2249 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
2250 .concat(display.view.slice(cutBot.index));
2251 display.viewTo += lendiff;
2252 } else {
2253 resetView(cm);
2254 }
2255 }
2256
2257 var ext = display.externalMeasured;
2258 if (ext) {
2259 if (to < ext.lineN)
2260 ext.lineN += lendiff;
2261 else if (from < ext.lineN + ext.size)
2262 display.externalMeasured = null;
2263 }
2264 }
2265
2266 // Register a change to a single line. Type must be one of "text",
2267 // "gutter", "class", "widget"
2268 function regLineChange(cm, line, type) {
2269 cm.curOp.viewChanged = true;
2270 var display = cm.display, ext = cm.display.externalMeasured;
2271 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
2272 display.externalMeasured = null;
2273
2274 if (line < display.viewFrom || line >= display.viewTo) return;
2275 var lineView = display.view[findViewIndex(cm, line)];
2276 if (lineView.node == null) return;
2277 var arr = lineView.changes || (lineView.changes = []);
2278 if (indexOf(arr, type) == -1) arr.push(type);
2279 }
2280
2281 // Clear the view.
2282 function resetView(cm) {
2283 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
2284 cm.display.view = [];
2285 cm.display.viewOffset = 0;
2286 }
2287
2288 // Find the view element corresponding to a given line. Return null
2289 // when the line isn't visible.
2290 function findViewIndex(cm, n) {
2291 if (n >= cm.display.viewTo) return null;
2292 n -= cm.display.viewFrom;
2293 if (n < 0) return null;
2294 var view = cm.display.view;
2295 for (var i = 0; i < view.length; i++) {
2296 n -= view[i].size;
2297 if (n < 0) return i;
2298 }
2299 }
2300
2301 function viewCuttingPoint(cm, oldN, newN, dir) {
2302 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
2303 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
2304 return {index: index, lineN: newN};
2305 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
2306 n += view[i].size;
2307 if (n != oldN) {
2308 if (dir > 0) {
2309 if (index == view.length - 1) return null;
2310 diff = (n + view[index].size) - oldN;
2311 index++;
2312 } else {
2313 diff = n - oldN;
2314 }
2315 oldN += diff; newN += diff;
2316 }
2317 while (visualLineNo(cm.doc, newN) != newN) {
2318 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
2319 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
2320 index += dir;
2321 }
2322 return {index: index, lineN: newN};
2323 }
2324
2325 // Force the view to cover a given range, adding empty view element
2326 // or clipping off existing ones as needed.
2327 function adjustView(cm, from, to) {
2328 var display = cm.display, view = display.view;
2329 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
2330 display.view = buildViewArray(cm, from, to);
2331 display.viewFrom = from;
2332 } else {
2333 if (display.viewFrom > from)
2334 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
2335 else if (display.viewFrom < from)
2336 display.view = display.view.slice(findViewIndex(cm, from));
2337 display.viewFrom = from;
2338 if (display.viewTo < to)
2339 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
2340 else if (display.viewTo > to)
2341 display.view = display.view.slice(0, findViewIndex(cm, to));
2342 }
2343 display.viewTo = to;
2344 }
2345
2346 // Count the number of lines in the view whose DOM representation is
2347 // out of date (or nonexistent).
2348 function countDirtyView(cm) {
2349 var view = cm.display.view, dirty = 0;
2350 for (var i = 0; i < view.length; i++) {
2351 var lineView = view[i];
2352 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
2353 }
2354 return dirty;
1416 }
2355 }
1417
2356
1418 // INPUT HANDLING
2357 // INPUT HANDLING
1419
2358
2359 // Poll for input changes, using the normal rate of polling. This
2360 // runs as long as the editor is focused.
1420 function slowPoll(cm) {
2361 function slowPoll(cm) {
1421 if (cm.display.pollingFast) return;
2362 if (cm.display.pollingFast) return;
1422 cm.display.poll.set(cm.options.pollInterval, function() {
2363 cm.display.poll.set(cm.options.pollInterval, function() {
@@ -1425,6 +2366,9 b' window.CodeMirror = (function() {'
1425 });
2366 });
1426 }
2367 }
1427
2368
2369 // When an event has just come in that is likely to add or change
2370 // something in the input textarea, we poll faster, to ensure that
2371 // the change appears on the screen quickly.
1428 function fastPoll(cm) {
2372 function fastPoll(cm) {
1429 var missed = false;
2373 var missed = false;
1430 cm.display.pollingFast = true;
2374 cm.display.pollingFast = true;
@@ -1436,96 +2380,169 b' window.CodeMirror = (function() {'
1436 cm.display.poll.set(20, p);
2380 cm.display.poll.set(20, p);
1437 }
2381 }
1438
2382
1439 // prevInput is a hack to work with IME. If we reset the textarea
2383 // This will be set to an array of strings when copying, so that,
1440 // on every change, that breaks IME. So we look for changes
2384 // when pasting, we know what kind of selections the copied text
1441 // compared to the previous content instead. (Modern browsers have
2385 // was made out of.
1442 // events that indicate IME taking place, but these are not widely
2386 var lastCopied = null;
1443 // supported or compatible enough yet to rely on.)
2387
2388 // Read input from the textarea, and update the document to match.
2389 // When something is selected, it is present in the textarea, and
2390 // selected (unless it is huge, in which case a placeholder is
2391 // used). When nothing is selected, the cursor sits after previously
2392 // seen text (can be empty), which is stored in prevInput (we must
2393 // not reset the textarea when typing, because that breaks IME).
1444 function readInput(cm) {
2394 function readInput(cm) {
1445 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
2395 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc;
1446 if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
2396 // Since this is called a *lot*, try to bail out as cheaply as
2397 // possible when it is clear that nothing happened. hasSelection
2398 // will be the case when there is a lot of text in the textarea,
2399 // in which case reading its value would be expensive.
2400 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput)
2401 return false;
2402 // See paste handler for more on the fakedLastChar kludge
2403 if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
2404 input.value = input.value.substring(0, input.value.length - 1);
2405 cm.state.fakedLastChar = false;
2406 }
1447 var text = input.value;
2407 var text = input.value;
1448 if (text == prevInput && posEq(sel.from, sel.to)) return false;
2408 // If nothing changed, bail.
1449 if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
2409 if (text == prevInput && !cm.somethingSelected()) return false;
1450 resetInput(cm, true);
2410 // Work around nonsensical selection resetting in IE9/10, and
2411 // inexplicable appearance of private area unicode characters on
2412 // some key combos in Mac (#2689).
2413 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
2414 mac && /[\uf700-\uf7ff]/.test(text)) {
2415 resetInput(cm);
1451 return false;
2416 return false;
1452 }
2417 }
1453
2418
1454 var withOp = !cm.curOp;
2419 var withOp = !cm.curOp;
1455 if (withOp) startOperation(cm);
2420 if (withOp) startOperation(cm);
1456 sel.shift = false;
2421 cm.display.shift = false;
2422
2423 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
2424 prevInput = "\u200b";
2425 // Find the part of the input that is actually new
1457 var same = 0, l = Math.min(prevInput.length, text.length);
2426 var same = 0, l = Math.min(prevInput.length, text.length);
1458 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
2427 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1459 var from = sel.from, to = sel.to;
2428 var inserted = text.slice(same), textLines = splitLines(inserted);
1460 if (same < prevInput.length)
2429
1461 from = Pos(from.line, from.ch - (prevInput.length - same));
2430 // When pasing N lines into N selections, insert one line per selection
1462 else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
2431 var multiPaste = null;
1463 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
2432 if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
1464
2433 if (lastCopied && lastCopied.join("\n") == inserted)
1465 var updateInput = cm.curOp.updateInput;
2434 multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
1466 var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
2435 else if (textLines.length == doc.sel.ranges.length)
1467 origin: cm.state.pasteIncoming ? "paste" : "+input"};
2436 multiPaste = map(textLines, function(l) { return [l]; });
1468 makeChange(cm.doc, changeEvent, "end");
2437 }
2438
2439 // Normal behavior is to insert the new text into every selection
2440 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
2441 var range = doc.sel.ranges[i];
2442 var from = range.from(), to = range.to();
2443 // Handle deletion
2444 if (same < prevInput.length)
2445 from = Pos(from.line, from.ch - (prevInput.length - same));
2446 // Handle overwrite
2447 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
2448 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
2449 var updateInput = cm.curOp.updateInput;
2450 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
2451 origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
2452 makeChange(cm.doc, changeEvent);
2453 signalLater(cm, "inputRead", cm, changeEvent);
2454 // When an 'electric' character is inserted, immediately trigger a reindent
2455 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
2456 cm.options.smartIndent && range.head.ch < 100 &&
2457 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
2458 var mode = cm.getModeAt(range.head);
2459 var end = changeEnd(changeEvent);
2460 if (mode.electricChars) {
2461 for (var j = 0; j < mode.electricChars.length; j++)
2462 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
2463 indentLine(cm, end.line, "smart");
2464 break;
2465 }
2466 } else if (mode.electricInput) {
2467 if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
2468 indentLine(cm, end.line, "smart");
2469 }
2470 }
2471 }
2472 ensureCursorVisible(cm);
1469 cm.curOp.updateInput = updateInput;
2473 cm.curOp.updateInput = updateInput;
1470 signalLater(cm, "inputRead", cm, changeEvent);
2474 cm.curOp.typing = true;
1471
2475
2476 // Don't leave long text in the textarea, since it makes further polling slow
1472 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
2477 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1473 else cm.display.prevInput = text;
2478 else cm.display.prevInput = text;
1474 if (withOp) endOperation(cm);
2479 if (withOp) endOperation(cm);
1475 cm.state.pasteIncoming = false;
2480 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1476 return true;
2481 return true;
1477 }
2482 }
1478
2483
1479 function resetInput(cm, user) {
2484 // Reset the input to correspond to the selection (or to be empty,
2485 // when not typing and nothing is selected)
2486 function resetInput(cm, typing) {
1480 var minimal, selected, doc = cm.doc;
2487 var minimal, selected, doc = cm.doc;
1481 if (!posEq(doc.sel.from, doc.sel.to)) {
2488 if (cm.somethingSelected()) {
1482 cm.display.prevInput = "";
2489 cm.display.prevInput = "";
2490 var range = doc.sel.primary();
1483 minimal = hasCopyEvent &&
2491 minimal = hasCopyEvent &&
1484 (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
2492 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1485 var content = minimal ? "-" : selected || cm.getSelection();
2493 var content = minimal ? "-" : selected || cm.getSelection();
1486 cm.display.input.value = content;
2494 cm.display.input.value = content;
1487 if (cm.state.focused) selectInput(cm.display.input);
2495 if (cm.state.focused) selectInput(cm.display.input);
1488 if (ie && !ie_lt9) cm.display.inputHasSelection = content;
2496 if (ie && ie_version >= 9) cm.display.inputHasSelection = content;
1489 } else if (user) {
2497 } else if (!typing) {
1490 cm.display.prevInput = cm.display.input.value = "";
2498 cm.display.prevInput = cm.display.input.value = "";
1491 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
2499 if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
1492 }
2500 }
1493 cm.display.inaccurateSelection = minimal;
2501 cm.display.inaccurateSelection = minimal;
1494 }
2502 }
1495
2503
1496 function focusInput(cm) {
2504 function focusInput(cm) {
1497 if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
2505 if (cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.display.input))
1498 cm.display.input.focus();
2506 cm.display.input.focus();
1499 }
2507 }
1500
2508
2509 function ensureFocus(cm) {
2510 if (!cm.state.focused) { focusInput(cm); onFocus(cm); }
2511 }
2512
1501 function isReadOnly(cm) {
2513 function isReadOnly(cm) {
1502 return cm.options.readOnly || cm.doc.cantEdit;
2514 return cm.options.readOnly || cm.doc.cantEdit;
1503 }
2515 }
1504
2516
1505 // EVENT HANDLERS
2517 // EVENT HANDLERS
1506
2518
2519 // Attach the necessary event handlers when initializing the editor
1507 function registerEventHandlers(cm) {
2520 function registerEventHandlers(cm) {
1508 var d = cm.display;
2521 var d = cm.display;
1509 on(d.scroller, "mousedown", operation(cm, onMouseDown));
2522 on(d.scroller, "mousedown", operation(cm, onMouseDown));
1510 if (ie)
2523 // Older IE's will not fire a second mousedown for a double click
2524 if (ie && ie_version < 11)
1511 on(d.scroller, "dblclick", operation(cm, function(e) {
2525 on(d.scroller, "dblclick", operation(cm, function(e) {
1512 if (signalDOMEvent(cm, e)) return;
2526 if (signalDOMEvent(cm, e)) return;
1513 var pos = posFromMouse(cm, e);
2527 var pos = posFromMouse(cm, e);
1514 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
2528 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1515 e_preventDefault(e);
2529 e_preventDefault(e);
1516 var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
2530 var word = cm.findWordAt(pos);
1517 extendSelection(cm.doc, word.from, word.to);
2531 extendSelection(cm.doc, word.anchor, word.head);
1518 }));
2532 }));
1519 else
2533 else
1520 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
2534 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
2535 // Prevent normal selection in the editor (we handle our own)
1521 on(d.lineSpace, "selectstart", function(e) {
2536 on(d.lineSpace, "selectstart", function(e) {
1522 if (!eventInWidget(d, e)) e_preventDefault(e);
2537 if (!eventInWidget(d, e)) e_preventDefault(e);
1523 });
2538 });
1524 // Gecko browsers fire contextmenu *after* opening the menu, at
2539 // Some browsers fire contextmenu *after* opening the menu, at
1525 // which point we can't mess with it anymore. Context menu is
2540 // which point we can't mess with it anymore. Context menu is
1526 // handled in onMouseDown for Gecko.
2541 // handled in onMouseDown for these browsers.
1527 if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
2542 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1528
2543
2544 // Sync scrolling between fake scrollbars and real scrollable
2545 // area, ensure viewport is updated when scrolling.
1529 on(d.scroller, "scroll", function() {
2546 on(d.scroller, "scroll", function() {
1530 if (d.scroller.clientHeight) {
2547 if (d.scroller.clientHeight) {
1531 setScrollTop(cm, d.scroller.scrollTop);
2548 setScrollTop(cm, d.scroller.scrollTop);
@@ -1540,49 +2557,29 b' window.CodeMirror = (function() {'
1540 if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
2557 if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1541 });
2558 });
1542
2559
2560 // Listen to wheel events in order to try and update the viewport on time.
1543 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
2561 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1544 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
2562 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1545
2563
2564 // Prevent clicks in the scrollbars from killing focus
1546 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
2565 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1547 on(d.scrollbarH, "mousedown", reFocus);
2566 on(d.scrollbarH, "mousedown", reFocus);
1548 on(d.scrollbarV, "mousedown", reFocus);
2567 on(d.scrollbarV, "mousedown", reFocus);
1549 // Prevent wrapper from ever scrolling
2568 // Prevent wrapper from ever scrolling
1550 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
2569 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1551
2570
1552 var resizeTimer;
2571 on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
1553 function onResize() {
2572 on(d.input, "input", function() {
1554 if (resizeTimer == null) resizeTimer = setTimeout(function() {
2573 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
1555 resizeTimer = null;
2574 fastPoll(cm);
1556 // Might be a text scaling operation, clear size caches.
2575 });
1557 d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
1558 clearCaches(cm);
1559 runInOp(cm, bind(regChange, cm));
1560 }, 100);
1561 }
1562 on(window, "resize", onResize);
1563 // Above handler holds on to the editor and its data structures.
1564 // Here we poll to unregister it when the editor is no longer in
1565 // the document, so that it can be garbage-collected.
1566 function unregister() {
1567 for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1568 if (p) setTimeout(unregister, 5000);
1569 else off(window, "resize", onResize);
1570 }
1571 setTimeout(unregister, 5000);
1572
1573 on(d.input, "keyup", operation(cm, function(e) {
1574 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1575 if (e.keyCode == 16) cm.doc.sel.shift = false;
1576 }));
1577 on(d.input, "input", bind(fastPoll, cm));
1578 on(d.input, "keydown", operation(cm, onKeyDown));
2576 on(d.input, "keydown", operation(cm, onKeyDown));
1579 on(d.input, "keypress", operation(cm, onKeyPress));
2577 on(d.input, "keypress", operation(cm, onKeyPress));
1580 on(d.input, "focus", bind(onFocus, cm));
2578 on(d.input, "focus", bind(onFocus, cm));
1581 on(d.input, "blur", bind(onBlur, cm));
2579 on(d.input, "blur", bind(onBlur, cm));
1582
2580
1583 function drag_(e) {
2581 function drag_(e) {
1584 if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
2582 if (!signalDOMEvent(cm, e)) e_stop(e);
1585 e_stop(e);
1586 }
2583 }
1587 if (cm.options.dragDrop) {
2584 if (cm.options.dragDrop) {
1588 on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
2585 on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
@@ -1590,62 +2587,124 b' window.CodeMirror = (function() {'
1590 on(d.scroller, "dragover", drag_);
2587 on(d.scroller, "dragover", drag_);
1591 on(d.scroller, "drop", operation(cm, onDrop));
2588 on(d.scroller, "drop", operation(cm, onDrop));
1592 }
2589 }
1593 on(d.scroller, "paste", function(e){
2590 on(d.scroller, "paste", function(e) {
1594 if (eventInWidget(d, e)) return;
2591 if (eventInWidget(d, e)) return;
2592 cm.state.pasteIncoming = true;
1595 focusInput(cm);
2593 focusInput(cm);
1596 fastPoll(cm);
2594 fastPoll(cm);
1597 });
2595 });
1598 on(d.input, "paste", function() {
2596 on(d.input, "paste", function() {
2597 // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
2598 // Add a char to the end of textarea before paste occur so that
2599 // selection doesn't span to the end of textarea.
2600 if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
2601 var start = d.input.selectionStart, end = d.input.selectionEnd;
2602 d.input.value += "$";
2603 // The selection end needs to be set before the start, otherwise there
2604 // can be an intermediate non-empty selection between the two, which
2605 // can override the middle-click paste buffer on linux and cause the
2606 // wrong thing to get pasted.
2607 d.input.selectionEnd = end;
2608 d.input.selectionStart = start;
2609 cm.state.fakedLastChar = true;
2610 }
1599 cm.state.pasteIncoming = true;
2611 cm.state.pasteIncoming = true;
1600 fastPoll(cm);
2612 fastPoll(cm);
1601 });
2613 });
1602
2614
1603 function prepareCopy() {
2615 function prepareCopyCut(e) {
1604 if (d.inaccurateSelection) {
2616 if (cm.somethingSelected()) {
1605 d.prevInput = "";
2617 lastCopied = cm.getSelections();
1606 d.inaccurateSelection = false;
2618 if (d.inaccurateSelection) {
1607 d.input.value = cm.getSelection();
2619 d.prevInput = "";
1608 selectInput(d.input);
2620 d.inaccurateSelection = false;
1609 }
2621 d.input.value = lastCopied.join("\n");
1610 }
2622 selectInput(d.input);
1611 on(d.input, "cut", prepareCopy);
2623 }
1612 on(d.input, "copy", prepareCopy);
2624 } else {
2625 var text = [], ranges = [];
2626 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
2627 var line = cm.doc.sel.ranges[i].head.line;
2628 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
2629 ranges.push(lineRange);
2630 text.push(cm.getRange(lineRange.anchor, lineRange.head));
2631 }
2632 if (e.type == "cut") {
2633 cm.setSelections(ranges, null, sel_dontScroll);
2634 } else {
2635 d.prevInput = "";
2636 d.input.value = text.join("\n");
2637 selectInput(d.input);
2638 }
2639 lastCopied = text;
2640 }
2641 if (e.type == "cut") cm.state.cutIncoming = true;
2642 }
2643 on(d.input, "cut", prepareCopyCut);
2644 on(d.input, "copy", prepareCopyCut);
1613
2645
1614 // Needed to handle Tab key in KHTML
2646 // Needed to handle Tab key in KHTML
1615 if (khtml) on(d.sizer, "mouseup", function() {
2647 if (khtml) on(d.sizer, "mouseup", function() {
1616 if (document.activeElement == d.input) d.input.blur();
2648 if (activeElt() == d.input) d.input.blur();
1617 focusInput(cm);
2649 focusInput(cm);
1618 });
2650 });
1619 }
2651 }
1620
2652
2653 // Called when the window resizes
2654 function onResize(cm) {
2655 // Might be a text scaling operation, clear size caches.
2656 var d = cm.display;
2657 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
2658 cm.setSize();
2659 }
2660
2661 // MOUSE EVENTS
2662
2663 // Return true when the given mouse event happened in a widget
1621 function eventInWidget(display, e) {
2664 function eventInWidget(display, e) {
1622 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2665 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1623 if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
2666 if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
1624 }
2667 }
1625 }
2668 }
1626
2669
1627 function posFromMouse(cm, e, liberal) {
2670 // Given a mouse event, find the corresponding position. If liberal
2671 // is false, it checks whether a gutter or scrollbar was clicked,
2672 // and returns null if it was. forRect is used by rectangular
2673 // selections, and tries to estimate a character position even for
2674 // coordinates beyond the right of the text.
2675 function posFromMouse(cm, e, liberal, forRect) {
1628 var display = cm.display;
2676 var display = cm.display;
1629 if (!liberal) {
2677 if (!liberal) {
1630 var target = e_target(e);
2678 var target = e_target(e);
1631 if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
2679 if (target == display.scrollbarH || target == display.scrollbarV ||
1632 target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1633 target == display.scrollbarFiller || target == display.gutterFiller) return null;
2680 target == display.scrollbarFiller || target == display.gutterFiller) return null;
1634 }
2681 }
1635 var x, y, space = getRect(display.lineSpace);
2682 var x, y, space = display.lineSpace.getBoundingClientRect();
1636 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2683 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1637 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
2684 try { x = e.clientX - space.left; y = e.clientY - space.top; }
1638 return coordsChar(cm, x - space.left, y - space.top);
2685 catch (e) { return null; }
1639 }
2686 var coords = coordsChar(cm, x, y), line;
1640
2687 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
1641 var lastClick, lastDoubleClick;
2688 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
2689 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
2690 }
2691 return coords;
2692 }
2693
2694 // A mouse down can be a single click, double click, triple click,
2695 // start of selection drag, start of text drag, new cursor
2696 // (ctrl-click), rectangle drag (alt-drag), or xwin
2697 // middle-click-paste. Or it might be a click on something we should
2698 // not interfere with, such as a scrollbar or widget.
1642 function onMouseDown(e) {
2699 function onMouseDown(e) {
1643 if (signalDOMEvent(this, e)) return;
2700 if (signalDOMEvent(this, e)) return;
1644 var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
2701 var cm = this, display = cm.display;
1645 sel.shift = e.shiftKey;
2702 display.shift = e.shiftKey;
1646
2703
1647 if (eventInWidget(display, e)) {
2704 if (eventInWidget(display, e)) {
1648 if (!webkit) {
2705 if (!webkit) {
2706 // Briefly turn off draggability, to allow widgets to do
2707 // normal dragging things.
1649 display.scroller.draggable = false;
2708 display.scroller.draggable = false;
1650 setTimeout(function(){display.scroller.draggable = true;}, 100);
2709 setTimeout(function(){display.scroller.draggable = true;}, 100);
1651 }
2710 }
@@ -1653,88 +2712,172 b' window.CodeMirror = (function() {'
1653 }
2712 }
1654 if (clickInGutter(cm, e)) return;
2713 if (clickInGutter(cm, e)) return;
1655 var start = posFromMouse(cm, e);
2714 var start = posFromMouse(cm, e);
2715 window.focus();
1656
2716
1657 switch (e_button(e)) {
2717 switch (e_button(e)) {
1658 case 3:
2718 case 1:
1659 if (captureMiddleClick) onContextMenu.call(cm, cm, e);
2719 if (start)
1660 return;
2720 leftButtonDown(cm, e, start);
2721 else if (e_target(e) == display.scroller)
2722 e_preventDefault(e);
2723 break;
1661 case 2:
2724 case 2:
2725 if (webkit) cm.state.lastMiddleDown = +new Date;
1662 if (start) extendSelection(cm.doc, start);
2726 if (start) extendSelection(cm.doc, start);
1663 setTimeout(bind(focusInput, cm), 20);
2727 setTimeout(bind(focusInput, cm), 20);
1664 e_preventDefault(e);
2728 e_preventDefault(e);
1665 return;
2729 break;
1666 }
2730 case 3:
1667 // For button 1, if it was clicked inside the editor
2731 if (captureRightClick) onContextMenu(cm, e);
1668 // (posFromMouse returning non-null), we have to adjust the
2732 break;
1669 // selection.
2733 }
1670 if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
2734 }
1671
2735
1672 if (!cm.state.focused) onFocus(cm);
2736 var lastClick, lastDoubleClick;
1673
2737 function leftButtonDown(cm, e, start) {
1674 var now = +new Date, type = "single";
2738 setTimeout(bind(ensureFocus, cm), 0);
1675 if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
2739
2740 var now = +new Date, type;
2741 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
1676 type = "triple";
2742 type = "triple";
1677 e_preventDefault(e);
2743 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
1678 setTimeout(bind(focusInput, cm), 20);
1679 selectLine(cm, start.line);
1680 } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1681 type = "double";
2744 type = "double";
1682 lastDoubleClick = {time: now, pos: start};
2745 lastDoubleClick = {time: now, pos: start};
1683 e_preventDefault(e);
2746 } else {
1684 var word = findWordAt(getLine(doc, start.line).text, start);
2747 type = "single";
1685 extendSelection(cm.doc, word.from, word.to);
2748 lastClick = {time: now, pos: start};
1686 } else { lastClick = {time: now, pos: start}; }
2749 }
1687
2750
1688 var last = start;
2751 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey;
1689 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
2752 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
1690 !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
2753 type == "single" && sel.contains(start) > -1 && sel.somethingSelected())
1691 var dragEnd = operation(cm, function(e2) {
2754 leftButtonStartDrag(cm, e, start, modifier);
1692 if (webkit) display.scroller.draggable = false;
2755 else
1693 cm.state.draggingText = false;
2756 leftButtonSelect(cm, e, start, type, modifier);
1694 off(document, "mouseup", dragEnd);
2757 }
1695 off(display.scroller, "drop", dragEnd);
2758
1696 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
2759 // Start a text drag. When it ends, see if any dragging actually
1697 e_preventDefault(e2);
2760 // happen, and treat as a click if it didn't.
2761 function leftButtonStartDrag(cm, e, start, modifier) {
2762 var display = cm.display;
2763 var dragEnd = operation(cm, function(e2) {
2764 if (webkit) display.scroller.draggable = false;
2765 cm.state.draggingText = false;
2766 off(document, "mouseup", dragEnd);
2767 off(display.scroller, "drop", dragEnd);
2768 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
2769 e_preventDefault(e2);
2770 if (!modifier)
1698 extendSelection(cm.doc, start);
2771 extendSelection(cm.doc, start);
1699 focusInput(cm);
2772 focusInput(cm);
2773 // Work around unexplainable focus problem in IE9 (#2127)
2774 if (ie && ie_version == 9)
2775 setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
2776 }
2777 });
2778 // Let the drag handler handle this.
2779 if (webkit) display.scroller.draggable = true;
2780 cm.state.draggingText = dragEnd;
2781 // IE's approach to draggable
2782 if (display.scroller.dragDrop) display.scroller.dragDrop();
2783 on(document, "mouseup", dragEnd);
2784 on(display.scroller, "drop", dragEnd);
2785 }
2786
2787 // Normal selection, as opposed to text dragging.
2788 function leftButtonSelect(cm, e, start, type, addNew) {
2789 var display = cm.display, doc = cm.doc;
2790 e_preventDefault(e);
2791
2792 var ourRange, ourIndex, startSel = doc.sel;
2793 if (addNew && !e.shiftKey) {
2794 ourIndex = doc.sel.contains(start);
2795 if (ourIndex > -1)
2796 ourRange = doc.sel.ranges[ourIndex];
2797 else
2798 ourRange = new Range(start, start);
2799 } else {
2800 ourRange = doc.sel.primary();
2801 }
2802
2803 if (e.altKey) {
2804 type = "rect";
2805 if (!addNew) ourRange = new Range(start, start);
2806 start = posFromMouse(cm, e, true, true);
2807 ourIndex = -1;
2808 } else if (type == "double") {
2809 var word = cm.findWordAt(start);
2810 if (cm.display.shift || doc.extend)
2811 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
2812 else
2813 ourRange = word;
2814 } else if (type == "triple") {
2815 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
2816 if (cm.display.shift || doc.extend)
2817 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
2818 else
2819 ourRange = line;
2820 } else {
2821 ourRange = extendRange(doc, ourRange, start);
2822 }
2823
2824 if (!addNew) {
2825 ourIndex = 0;
2826 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
2827 startSel = doc.sel;
2828 } else if (ourIndex > -1) {
2829 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
2830 } else {
2831 ourIndex = doc.sel.ranges.length;
2832 setSelection(doc, normalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
2833 {scroll: false, origin: "*mouse"});
2834 }
2835
2836 var lastPos = start;
2837 function extendTo(pos) {
2838 if (cmp(lastPos, pos) == 0) return;
2839 lastPos = pos;
2840
2841 if (type == "rect") {
2842 var ranges = [], tabSize = cm.options.tabSize;
2843 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
2844 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
2845 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
2846 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
2847 line <= end; line++) {
2848 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
2849 if (left == right)
2850 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
2851 else if (text.length > leftPos)
2852 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
1700 }
2853 }
1701 });
2854 if (!ranges.length) ranges.push(new Range(start, start));
1702 // Let the drag handler handle this.
2855 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
1703 if (webkit) display.scroller.draggable = true;
2856 {origin: "*mouse", scroll: false});
1704 cm.state.draggingText = dragEnd;
2857 cm.scrollIntoView(pos);
1705 // IE's approach to draggable
2858 } else {
1706 if (display.scroller.dragDrop) display.scroller.dragDrop();
2859 var oldRange = ourRange;
1707 on(document, "mouseup", dragEnd);
2860 var anchor = oldRange.anchor, head = pos;
1708 on(display.scroller, "drop", dragEnd);
2861 if (type != "single") {
1709 return;
2862 if (type == "double")
1710 }
2863 var range = cm.findWordAt(pos);
1711 e_preventDefault(e);
2864 else
1712 if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
2865 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
1713
2866 if (cmp(range.anchor, anchor) > 0) {
1714 var startstart = sel.from, startend = sel.to, lastPos = start;
2867 head = range.head;
1715
2868 anchor = minPos(oldRange.from(), range.anchor);
1716 function doSelect(cur) {
2869 } else {
1717 if (posEq(lastPos, cur)) return;
2870 head = range.anchor;
1718 lastPos = cur;
2871 anchor = maxPos(oldRange.to(), range.head);
1719
2872 }
1720 if (type == "single") {
2873 }
1721 extendSelection(cm.doc, clipPos(doc, start), cur);
2874 var ranges = startSel.ranges.slice(0);
1722 return;
2875 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
1723 }
2876 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
1724
2877 }
1725 startstart = clipPos(doc, startstart);
2878 }
1726 startend = clipPos(doc, startend);
2879
1727 if (type == "double") {
2880 var editorSize = display.wrapper.getBoundingClientRect();
1728 var word = findWordAt(getLine(doc, cur.line).text, cur);
1729 if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1730 else extendSelection(cm.doc, startstart, word.to);
1731 } else if (type == "triple") {
1732 if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1733 else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1734 }
1735 }
1736
1737 var editorSize = getRect(display.wrapper);
1738 // Used to ensure timeout re-tries don't fire when another extend
2881 // Used to ensure timeout re-tries don't fire when another extend
1739 // happened in the meantime (clearTimeout isn't reliable -- at
2882 // happened in the meantime (clearTimeout isn't reliable -- at
1740 // least on Chrome, the timeouts still happen even when cleared,
2883 // least on Chrome, the timeouts still happen even when cleared,
@@ -1743,12 +2886,11 b' window.CodeMirror = (function() {'
1743
2886
1744 function extend(e) {
2887 function extend(e) {
1745 var curCount = ++counter;
2888 var curCount = ++counter;
1746 var cur = posFromMouse(cm, e, true);
2889 var cur = posFromMouse(cm, e, true, type == "rect");
1747 if (!cur) return;
2890 if (!cur) return;
1748 if (!posEq(cur, last)) {
2891 if (cmp(cur, lastPos) != 0) {
1749 if (!cm.state.focused) onFocus(cm);
2892 ensureFocus(cm);
1750 last = cur;
2893 extendTo(cur);
1751 doSelect(cur);
1752 var visible = visibleLines(display, doc);
2894 var visible = visibleLines(display, doc);
1753 if (cur.line >= visible.to || cur.line < visible.from)
2895 if (cur.line >= visible.to || cur.line < visible.from)
1754 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
2896 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
@@ -1768,10 +2910,11 b' window.CodeMirror = (function() {'
1768 focusInput(cm);
2910 focusInput(cm);
1769 off(document, "mousemove", move);
2911 off(document, "mousemove", move);
1770 off(document, "mouseup", up);
2912 off(document, "mouseup", up);
2913 doc.history.lastSelOrigin = null;
1771 }
2914 }
1772
2915
1773 var move = operation(cm, function(e) {
2916 var move = operation(cm, function(e) {
1774 if (!ie && !e_button(e)) done(e);
2917 if (!e_button(e)) done(e);
1775 else extend(e);
2918 else extend(e);
1776 });
2919 });
1777 var up = operation(cm, done);
2920 var up = operation(cm, done);
@@ -1779,29 +2922,33 b' window.CodeMirror = (function() {'
1779 on(document, "mouseup", up);
2922 on(document, "mouseup", up);
1780 }
2923 }
1781
2924
1782 function clickInGutter(cm, e) {
2925 // Determines whether an event happened in the gutter, and fires the
1783 var display = cm.display;
2926 // handlers for the corresponding event.
2927 function gutterEvent(cm, e, type, prevent, signalfn) {
1784 try { var mX = e.clientX, mY = e.clientY; }
2928 try { var mX = e.clientX, mY = e.clientY; }
1785 catch(e) { return false; }
2929 catch(e) { return false; }
1786
2930 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
1787 if (mX >= Math.floor(getRect(display.gutters).right)) return false;
2931 if (prevent) e_preventDefault(e);
1788 e_preventDefault(e);
2932
1789 if (!hasHandler(cm, "gutterClick")) return true;
2933 var display = cm.display;
1790
2934 var lineBox = display.lineDiv.getBoundingClientRect();
1791 var lineBox = getRect(display.lineDiv);
2935
1792 if (mY > lineBox.bottom) return true;
2936 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
1793 mY -= lineBox.top - display.viewOffset;
2937 mY -= lineBox.top - display.viewOffset;
1794
2938
1795 for (var i = 0; i < cm.options.gutters.length; ++i) {
2939 for (var i = 0; i < cm.options.gutters.length; ++i) {
1796 var g = display.gutters.childNodes[i];
2940 var g = display.gutters.childNodes[i];
1797 if (g && getRect(g).right >= mX) {
2941 if (g && g.getBoundingClientRect().right >= mX) {
1798 var line = lineAtHeight(cm.doc, mY);
2942 var line = lineAtHeight(cm.doc, mY);
1799 var gutter = cm.options.gutters[i];
2943 var gutter = cm.options.gutters[i];
1800 signalLater(cm, "gutterClick", cm, line, gutter, e);
2944 signalfn(cm, type, cm, line, gutter, e);
1801 break;
2945 return e_defaultPrevented(e);
1802 }
2946 }
1803 }
2947 }
1804 return true;
2948 }
2949
2950 function clickInGutter(cm, e) {
2951 return gutterEvent(cm, e, "gutterClick", true, signalLater);
1805 }
2952 }
1806
2953
1807 // Kludge to work around strange IE behavior where it'll sometimes
2954 // Kludge to work around strange IE behavior where it'll sometimes
@@ -1810,29 +2957,33 b' window.CodeMirror = (function() {'
1810
2957
1811 function onDrop(e) {
2958 function onDrop(e) {
1812 var cm = this;
2959 var cm = this;
1813 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
2960 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
1814 return;
2961 return;
1815 e_preventDefault(e);
2962 e_preventDefault(e);
1816 if (ie) lastDrop = +new Date;
2963 if (ie) lastDrop = +new Date;
1817 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
2964 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1818 if (!pos || isReadOnly(cm)) return;
2965 if (!pos || isReadOnly(cm)) return;
2966 // Might be a file drop, in which case we simply extract the text
2967 // and insert it.
1819 if (files && files.length && window.FileReader && window.File) {
2968 if (files && files.length && window.FileReader && window.File) {
1820 var n = files.length, text = Array(n), read = 0;
2969 var n = files.length, text = Array(n), read = 0;
1821 var loadFile = function(file, i) {
2970 var loadFile = function(file, i) {
1822 var reader = new FileReader;
2971 var reader = new FileReader;
1823 reader.onload = function() {
2972 reader.onload = operation(cm, function() {
1824 text[i] = reader.result;
2973 text[i] = reader.result;
1825 if (++read == n) {
2974 if (++read == n) {
1826 pos = clipPos(cm.doc, pos);
2975 pos = clipPos(cm.doc, pos);
1827 makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
2976 var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"};
2977 makeChange(cm.doc, change);
2978 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
1828 }
2979 }
1829 };
2980 });
1830 reader.readAsText(file);
2981 reader.readAsText(file);
1831 };
2982 };
1832 for (var i = 0; i < n; ++i) loadFile(files[i], i);
2983 for (var i = 0; i < n; ++i) loadFile(files[i], i);
1833 } else {
2984 } else { // Normal drop
1834 // Don't do a replace if the drop happened inside of the selected text.
2985 // Don't do a replace if the drop happened inside of the selected text.
1835 if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
2986 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
1836 cm.state.draggingText(e);
2987 cm.state.draggingText(e);
1837 // Ensure the editor is re-focused
2988 // Ensure the editor is re-focused
1838 setTimeout(bind(focusInput, cm), 20);
2989 setTimeout(bind(focusInput, cm), 20);
@@ -1841,12 +2992,13 b' window.CodeMirror = (function() {'
1841 try {
2992 try {
1842 var text = e.dataTransfer.getData("Text");
2993 var text = e.dataTransfer.getData("Text");
1843 if (text) {
2994 if (text) {
1844 var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
2995 if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey))
1845 setSelection(cm.doc, pos, pos);
2996 var selected = cm.listSelections();
1846 if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
2997 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
1847 cm.replaceSelection(text, null, "paste");
2998 if (selected) for (var i = 0; i < selected.length; ++i)
2999 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3000 cm.replaceSelection(text, "around", "paste");
1848 focusInput(cm);
3001 focusInput(cm);
1849 onFocus(cm);
1850 }
3002 }
1851 }
3003 }
1852 catch(e){}
3004 catch(e){}
@@ -1857,33 +3009,39 b' window.CodeMirror = (function() {'
1857 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3009 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
1858 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3010 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
1859
3011
1860 var txt = cm.getSelection();
3012 e.dataTransfer.setData("Text", cm.getSelection());
1861 e.dataTransfer.setData("Text", txt);
1862
3013
1863 // Use dummy image instead of default browsers image.
3014 // Use dummy image instead of default browsers image.
1864 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3015 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1865 if (e.dataTransfer.setDragImage && !safari) {
3016 if (e.dataTransfer.setDragImage && !safari) {
1866 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3017 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1867 if (opera) {
3018 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3019 if (presto) {
1868 img.width = img.height = 1;
3020 img.width = img.height = 1;
1869 cm.display.wrapper.appendChild(img);
3021 cm.display.wrapper.appendChild(img);
1870 // Force a relayout, or Opera won't use our image for some obscure reason
3022 // Force a relayout, or Opera won't use our image for some obscure reason
1871 img._top = img.offsetTop;
3023 img._top = img.offsetTop;
1872 }
3024 }
1873 e.dataTransfer.setDragImage(img, 0, 0);
3025 e.dataTransfer.setDragImage(img, 0, 0);
1874 if (opera) img.parentNode.removeChild(img);
3026 if (presto) img.parentNode.removeChild(img);
1875 }
3027 }
1876 }
3028 }
1877
3029
3030 // SCROLL EVENTS
3031
3032 // Sync the scrollable area and scrollbars, ensure the viewport
3033 // covers the visible area.
1878 function setScrollTop(cm, val) {
3034 function setScrollTop(cm, val) {
1879 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3035 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1880 cm.doc.scrollTop = val;
3036 cm.doc.scrollTop = val;
1881 if (!gecko) updateDisplay(cm, [], val);
3037 if (!gecko) updateDisplaySimple(cm, {top: val});
1882 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3038 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1883 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
3039 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1884 if (gecko) updateDisplay(cm, []);
3040 if (gecko) updateDisplaySimple(cm);
1885 startWorker(cm, 100);
3041 startWorker(cm, 100);
1886 }
3042 }
3043 // Sync scroller and scrollbar, ensure the gutter elements are
3044 // aligned.
1887 function setScrollLeft(cm, val, isScroller) {
3045 function setScrollLeft(cm, val, isScroller) {
1888 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3046 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1889 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3047 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
@@ -1930,10 +3088,12 b' window.CodeMirror = (function() {'
1930 // This hack (see related code in patchDisplay) makes sure the
3088 // This hack (see related code in patchDisplay) makes sure the
1931 // element is kept around.
3089 // element is kept around.
1932 if (dy && mac && webkit) {
3090 if (dy && mac && webkit) {
1933 for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
3091 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
1934 if (cur.lineObj) {
3092 for (var i = 0; i < view.length; i++) {
1935 cm.display.currentWheelTarget = cur;
3093 if (view[i].node == cur) {
1936 break;
3094 cm.display.currentWheelTarget = cur;
3095 break outer;
3096 }
1937 }
3097 }
1938 }
3098 }
1939 }
3099 }
@@ -1944,7 +3104,7 b' window.CodeMirror = (function() {'
1944 // estimated pixels/delta value, we just handle horizontal
3104 // estimated pixels/delta value, we just handle horizontal
1945 // scrolling entirely here. It'll be slightly off from native, but
3105 // scrolling entirely here. It'll be slightly off from native, but
1946 // better than glitching out.
3106 // better than glitching out.
1947 if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
3107 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
1948 if (dy)
3108 if (dy)
1949 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3109 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1950 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3110 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
@@ -1953,12 +3113,14 b' window.CodeMirror = (function() {'
1953 return;
3113 return;
1954 }
3114 }
1955
3115
3116 // 'Project' the visible viewport to cover the area that is being
3117 // scrolled into view (if we know enough to estimate it).
1956 if (dy && wheelPixelsPerUnit != null) {
3118 if (dy && wheelPixelsPerUnit != null) {
1957 var pixels = dy * wheelPixelsPerUnit;
3119 var pixels = dy * wheelPixelsPerUnit;
1958 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3120 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1959 if (pixels < 0) top = Math.max(0, top + pixels - 50);
3121 if (pixels < 0) top = Math.max(0, top + pixels - 50);
1960 else bot = Math.min(cm.doc.height, bot + pixels + 50);
3122 else bot = Math.min(cm.doc.height, bot + pixels + 50);
1961 updateDisplay(cm, [], {top: top, bottom: bot});
3123 updateDisplaySimple(cm, {top: top, bottom: bot});
1962 }
3124 }
1963
3125
1964 if (wheelSamples < 20) {
3126 if (wheelSamples < 20) {
@@ -1982,6 +3144,9 b' window.CodeMirror = (function() {'
1982 }
3144 }
1983 }
3145 }
1984
3146
3147 // KEY EVENTS
3148
3149 // Run a handler that was bound to a key.
1985 function doHandleBinding(cm, bound, dropShift) {
3150 function doHandleBinding(cm, bound, dropShift) {
1986 if (typeof bound == "string") {
3151 if (typeof bound == "string") {
1987 bound = commands[bound];
3152 bound = commands[bound];
@@ -1990,18 +3155,19 b' window.CodeMirror = (function() {'
1990 // Ensure previous input has been read, so that the handler sees a
3155 // Ensure previous input has been read, so that the handler sees a
1991 // consistent view of the document
3156 // consistent view of the document
1992 if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
3157 if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1993 var doc = cm.doc, prevShift = doc.sel.shift, done = false;
3158 var prevShift = cm.display.shift, done = false;
1994 try {
3159 try {
1995 if (isReadOnly(cm)) cm.state.suppressEdits = true;
3160 if (isReadOnly(cm)) cm.state.suppressEdits = true;
1996 if (dropShift) doc.sel.shift = false;
3161 if (dropShift) cm.display.shift = false;
1997 done = bound(cm) != Pass;
3162 done = bound(cm) != Pass;
1998 } finally {
3163 } finally {
1999 doc.sel.shift = prevShift;
3164 cm.display.shift = prevShift;
2000 cm.state.suppressEdits = false;
3165 cm.state.suppressEdits = false;
2001 }
3166 }
2002 return done;
3167 return done;
2003 }
3168 }
2004
3169
3170 // Collect the currently active keymaps.
2005 function allKeyMaps(cm) {
3171 function allKeyMaps(cm) {
2006 var maps = cm.state.keyMaps.slice(0);
3172 var maps = cm.state.keyMaps.slice(0);
2007 if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
3173 if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
@@ -2010,8 +3176,9 b' window.CodeMirror = (function() {'
2010 }
3176 }
2011
3177
2012 var maybeTransition;
3178 var maybeTransition;
3179 // Handle a key from the keydown event.
2013 function handleKeyBinding(cm, e) {
3180 function handleKeyBinding(cm, e) {
2014 // Handle auto keymap transitions
3181 // Handle automatic keymap transitions
2015 var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
3182 var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
2016 clearTimeout(maybeTransition);
3183 clearTimeout(maybeTransition);
2017 if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
3184 if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
@@ -2041,12 +3208,12 b' window.CodeMirror = (function() {'
2041 if (handled) {
3208 if (handled) {
2042 e_preventDefault(e);
3209 e_preventDefault(e);
2043 restartBlink(cm);
3210 restartBlink(cm);
2044 if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2045 signalLater(cm, "keyHandled", cm, name, e);
3211 signalLater(cm, "keyHandled", cm, name, e);
2046 }
3212 }
2047 return handled;
3213 return handled;
2048 }
3214 }
2049
3215
3216 // Handle a key from the keypress event
2050 function handleCharBinding(cm, e, ch) {
3217 function handleCharBinding(cm, e, ch) {
2051 var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
3218 var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
2052 function(b) { return doHandleBinding(cm, b, true); });
3219 function(b) { return doHandleBinding(cm, b, true); });
@@ -2061,46 +3228,72 b' window.CodeMirror = (function() {'
2061 var lastStoppedKey = null;
3228 var lastStoppedKey = null;
2062 function onKeyDown(e) {
3229 function onKeyDown(e) {
2063 var cm = this;
3230 var cm = this;
2064 if (!cm.state.focused) onFocus(cm);
3231 ensureFocus(cm);
2065 if (ie && e.keyCode == 27) { e.returnValue = false; }
3232 if (signalDOMEvent(cm, e)) return;
2066 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2067 var code = e.keyCode;
2068 // IE does strange things with escape.
3233 // IE does strange things with escape.
2069 cm.doc.sel.shift = code == 16 || e.shiftKey;
3234 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
2070 // First give onKeyEvent option a chance to handle this.
3235 var code = e.keyCode;
3236 cm.display.shift = code == 16 || e.shiftKey;
2071 var handled = handleKeyBinding(cm, e);
3237 var handled = handleKeyBinding(cm, e);
2072 if (opera) {
3238 if (presto) {
2073 lastStoppedKey = handled ? code : null;
3239 lastStoppedKey = handled ? code : null;
2074 // Opera has no cut event... we try to at least catch the key combo
3240 // Opera has no cut event... we try to at least catch the key combo
2075 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
3241 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
2076 cm.replaceSelection("");
3242 cm.replaceSelection("", null, "cut");
2077 }
3243 }
3244
3245 // Turn mouse into crosshair when Alt is held on Mac.
3246 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
3247 showCrossHair(cm);
3248 }
3249
3250 function showCrossHair(cm) {
3251 var lineDiv = cm.display.lineDiv;
3252 addClass(lineDiv, "CodeMirror-crosshair");
3253
3254 function up(e) {
3255 if (e.keyCode == 18 || !e.altKey) {
3256 rmClass(lineDiv, "CodeMirror-crosshair");
3257 off(document, "keyup", up);
3258 off(document, "mouseover", up);
3259 }
3260 }
3261 on(document, "keyup", up);
3262 on(document, "mouseover", up);
3263 }
3264
3265 function onKeyUp(e) {
3266 if (e.keyCode == 16) this.doc.sel.shift = false;
3267 signalDOMEvent(this, e);
2078 }
3268 }
2079
3269
2080 function onKeyPress(e) {
3270 function onKeyPress(e) {
2081 var cm = this;
3271 var cm = this;
2082 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
3272 if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
2083 var keyCode = e.keyCode, charCode = e.charCode;
3273 var keyCode = e.keyCode, charCode = e.charCode;
2084 if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
3274 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2085 if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
3275 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2086 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
3276 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2087 if (this.options.electricChars && this.doc.mode.electricChars &&
2088 this.options.smartIndent && !isReadOnly(this) &&
2089 this.doc.mode.electricChars.indexOf(ch) > -1)
2090 setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2091 if (handleCharBinding(cm, e, ch)) return;
3277 if (handleCharBinding(cm, e, ch)) return;
2092 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
3278 if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
2093 fastPoll(cm);
3279 fastPoll(cm);
2094 }
3280 }
2095
3281
3282 // FOCUS/BLUR EVENTS
3283
2096 function onFocus(cm) {
3284 function onFocus(cm) {
2097 if (cm.options.readOnly == "nocursor") return;
3285 if (cm.options.readOnly == "nocursor") return;
2098 if (!cm.state.focused) {
3286 if (!cm.state.focused) {
2099 signal(cm, "focus", cm);
3287 signal(cm, "focus", cm);
2100 cm.state.focused = true;
3288 cm.state.focused = true;
2101 if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
3289 addClass(cm.display.wrapper, "CodeMirror-focused");
2102 cm.display.wrapper.className += " CodeMirror-focused";
3290 // The prevInput test prevents this from firing when a context
2103 resetInput(cm, true);
3291 // menu is closed (since the resetInput would kill the
3292 // select-all detection hack)
3293 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3294 resetInput(cm);
3295 if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
3296 }
2104 }
3297 }
2105 slowPoll(cm);
3298 slowPoll(cm);
2106 restartBlink(cm);
3299 restartBlink(cm);
@@ -2109,62 +3302,81 b' window.CodeMirror = (function() {'
2109 if (cm.state.focused) {
3302 if (cm.state.focused) {
2110 signal(cm, "blur", cm);
3303 signal(cm, "blur", cm);
2111 cm.state.focused = false;
3304 cm.state.focused = false;
2112 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
3305 rmClass(cm.display.wrapper, "CodeMirror-focused");
2113 }
3306 }
2114 clearInterval(cm.display.blinker);
3307 clearInterval(cm.display.blinker);
2115 setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
3308 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
2116 }
3309 }
2117
3310
2118 var detectingSelectAll;
3311 // CONTEXT MENU HANDLING
3312
3313 // To make the context menu work, we need to briefly unhide the
3314 // textarea (making it as unobtrusive as possible) to let the
3315 // right-click take effect on it.
2119 function onContextMenu(cm, e) {
3316 function onContextMenu(cm, e) {
2120 if (signalDOMEvent(cm, e, "contextmenu")) return;
3317 if (signalDOMEvent(cm, e, "contextmenu")) return;
2121 var display = cm.display, sel = cm.doc.sel;
3318 var display = cm.display;
2122 if (eventInWidget(display, e)) return;
3319 if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
2123
3320
2124 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
3321 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2125 if (!pos || opera) return; // Opera is difficult.
3322 if (!pos || presto) return; // Opera is difficult.
2126 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
3323
2127 operation(cm, setSelection)(cm.doc, pos, pos);
3324 // Reset the current text selection only if the click is done outside of the selection
3325 // and 'resetSelectionOnContextMenu' option is true.
3326 var reset = cm.options.resetSelectionOnContextMenu;
3327 if (reset && cm.doc.sel.contains(pos) == -1)
3328 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
2128
3329
2129 var oldCSS = display.input.style.cssText;
3330 var oldCSS = display.input.style.cssText;
2130 display.inputDiv.style.position = "absolute";
3331 display.inputDiv.style.position = "absolute";
2131 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
3332 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2132 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
3333 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
2133 "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
3334 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
3335 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
3336 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
2134 focusInput(cm);
3337 focusInput(cm);
2135 resetInput(cm, true);
3338 if (webkit) window.scrollTo(null, oldScrollY);
3339 resetInput(cm);
2136 // Adds "Select all" to context menu in FF
3340 // Adds "Select all" to context menu in FF
2137 if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
3341 if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
2138
3342 display.selForContextMenu = cm.doc.sel;
3343 clearTimeout(display.detectingSelectAll);
3344
3345 // Select-all will be greyed out if there's nothing to select, so
3346 // this adds a zero-width space so that we can later check whether
3347 // it got selected.
2139 function prepareSelectAllHack() {
3348 function prepareSelectAllHack() {
2140 if (display.input.selectionStart != null) {
3349 if (display.input.selectionStart != null) {
2141 var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
3350 var selected = cm.somethingSelected();
2142 display.prevInput = " ";
3351 var extval = display.input.value = "\u200b" + (selected ? display.input.value : "");
3352 display.prevInput = selected ? "" : "\u200b";
2143 display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
3353 display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
3354 // Re-set this, in case some other handler touched the
3355 // selection in the meantime.
3356 display.selForContextMenu = cm.doc.sel;
2144 }
3357 }
2145 }
3358 }
2146 function rehide() {
3359 function rehide() {
2147 display.inputDiv.style.position = "relative";
3360 display.inputDiv.style.position = "relative";
2148 display.input.style.cssText = oldCSS;
3361 display.input.style.cssText = oldCSS;
2149 if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
3362 if (ie && ie_version < 9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2150 slowPoll(cm);
3363 slowPoll(cm);
2151
3364
2152 // Try to detect the user choosing select-all
3365 // Try to detect the user choosing select-all
2153 if (display.input.selectionStart != null) {
3366 if (display.input.selectionStart != null) {
2154 if (!ie || ie_lt9) prepareSelectAllHack();
3367 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
2155 clearTimeout(detectingSelectAll);
3368 var i = 0, poll = function() {
2156 var i = 0, poll = function(){
3369 if (display.selForContextMenu == cm.doc.sel && display.input.selectionStart == 0)
2157 if (display.prevInput == " " && display.input.selectionStart == 0)
2158 operation(cm, commands.selectAll)(cm);
3370 operation(cm, commands.selectAll)(cm);
2159 else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
3371 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
2160 else resetInput(cm);
3372 else resetInput(cm);
2161 };
3373 };
2162 detectingSelectAll = setTimeout(poll, 200);
3374 display.detectingSelectAll = setTimeout(poll, 200);
2163 }
3375 }
2164 }
3376 }
2165
3377
2166 if (ie && !ie_lt9) prepareSelectAllHack();
3378 if (ie && ie_version >= 9) prepareSelectAllHack();
2167 if (captureMiddleClick) {
3379 if (captureRightClick) {
2168 e_stop(e);
3380 e_stop(e);
2169 var mouseup = function() {
3381 var mouseup = function() {
2170 off(window, "mouseup", mouseup);
3382 off(window, "mouseup", mouseup);
@@ -2176,54 +3388,71 b' window.CodeMirror = (function() {'
2176 }
3388 }
2177 }
3389 }
2178
3390
3391 function contextMenuInGutter(cm, e) {
3392 if (!hasHandler(cm, "gutterContextMenu")) return false;
3393 return gutterEvent(cm, e, "gutterContextMenu", false, signal);
3394 }
3395
2179 // UPDATING
3396 // UPDATING
2180
3397
3398 // Compute the position of the end of a change (its 'to' property
3399 // refers to the pre-change end).
2181 var changeEnd = CodeMirror.changeEnd = function(change) {
3400 var changeEnd = CodeMirror.changeEnd = function(change) {
2182 if (!change.text) return change.to;
3401 if (!change.text) return change.to;
2183 return Pos(change.from.line + change.text.length - 1,
3402 return Pos(change.from.line + change.text.length - 1,
2184 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
3403 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2185 };
3404 };
2186
3405
2187 // Make sure a position will be valid after the given change.
3406 // Adjust a position to refer to the post-change position of the
2188 function clipPostChange(doc, change, pos) {
3407 // same text, or the end of the change if the change covers it.
2189 if (!posLess(change.from, pos)) return clipPos(doc, pos);
3408 function adjustForChange(pos, change) {
2190 var diff = (change.text.length - 1) - (change.to.line - change.from.line);
3409 if (cmp(pos, change.from) < 0) return pos;
2191 if (pos.line > change.to.line + diff) {
3410 if (cmp(pos, change.to) <= 0) return changeEnd(change);
2192 var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
3411
2193 if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
3412 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2194 return clipToLen(pos, getLine(doc, preLine).text.length);
3413 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
2195 }
3414 return Pos(line, ch);
2196 if (pos.line == change.to.line + diff)
3415 }
2197 return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
3416
2198 getLine(doc, change.to.line).text.length - change.to.ch);
3417 function computeSelAfterChange(doc, change) {
2199 var inside = pos.line - change.from.line;
3418 var out = [];
2200 return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
3419 for (var i = 0; i < doc.sel.ranges.length; i++) {
2201 }
3420 var range = doc.sel.ranges[i];
2202
3421 out.push(new Range(adjustForChange(range.anchor, change),
2203 // Hint can be null|"end"|"start"|"around"|{anchor,head}
3422 adjustForChange(range.head, change)));
2204 function computeSelAfterChange(doc, change, hint) {
3423 }
2205 if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
3424 return normalizeSelection(out, doc.sel.primIndex);
2206 return {anchor: clipPostChange(doc, change, hint.anchor),
3425 }
2207 head: clipPostChange(doc, change, hint.head)};
3426
2208
3427 function offsetPos(pos, old, nw) {
2209 if (hint == "start") return {anchor: change.from, head: change.from};
3428 if (pos.line == old.line)
2210
3429 return Pos(nw.line, pos.ch - old.ch + nw.ch);
2211 var end = changeEnd(change);
3430 else
2212 if (hint == "around") return {anchor: change.from, head: end};
3431 return Pos(nw.line + (pos.line - old.line), pos.ch);
2213 if (hint == "end") return {anchor: end, head: end};
3432 }
2214
3433
2215 // hint is null, leave the selection alone as much as possible
3434 // Used by replaceSelections to allow moving the selection to the
2216 var adjustPos = function(pos) {
3435 // start or around the replaced test. Hint may be "start" or "around".
2217 if (posLess(pos, change.from)) return pos;
3436 function computeReplacedSel(doc, changes, hint) {
2218 if (!posLess(change.to, pos)) return end;
3437 var out = [];
2219
3438 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
2220 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
3439 for (var i = 0; i < changes.length; i++) {
2221 if (pos.line == change.to.line) ch += end.ch - change.to.ch;
3440 var change = changes[i];
2222 return Pos(line, ch);
3441 var from = offsetPos(change.from, oldPrev, newPrev);
2223 };
3442 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
2224 return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
3443 oldPrev = change.to;
2225 }
3444 newPrev = to;
2226
3445 if (hint == "around") {
3446 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
3447 out[i] = new Range(inv ? to : from, inv ? from : to);
3448 } else {
3449 out[i] = new Range(from, from);
3450 }
3451 }
3452 return new Selection(out, doc.sel.primIndex);
3453 }
3454
3455 // Allow "beforeChange" event handlers to influence a change
2227 function filterChange(doc, change, update) {
3456 function filterChange(doc, change, update) {
2228 var obj = {
3457 var obj = {
2229 canceled: false,
3458 canceled: false,
@@ -2246,11 +3475,11 b' window.CodeMirror = (function() {'
2246 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
3475 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2247 }
3476 }
2248
3477
2249 // Replace the range from from to to by the strings in replacement.
3478 // Apply a change to a document, and add it to the document's
2250 // change is a {from, to, text [, origin]} object
3479 // history, and propagating it to all linked documents.
2251 function makeChange(doc, change, selUpdate, ignoreReadOnly) {
3480 function makeChange(doc, change, ignoreReadOnly) {
2252 if (doc.cm) {
3481 if (doc.cm) {
2253 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
3482 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
2254 if (doc.cm.state.suppressEdits) return;
3483 if (doc.cm.state.suppressEdits) return;
2255 }
3484 }
2256
3485
@@ -2263,18 +3492,17 b' window.CodeMirror = (function() {'
2263 // of read-only spans in its range.
3492 // of read-only spans in its range.
2264 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
3493 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2265 if (split) {
3494 if (split) {
2266 for (var i = split.length - 1; i >= 1; --i)
3495 for (var i = split.length - 1; i >= 0; --i)
2267 makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
3496 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
2268 if (split.length)
2269 makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2270 } else {
3497 } else {
2271 makeChangeNoReadonly(doc, change, selUpdate);
3498 makeChangeInner(doc, change);
2272 }
3499 }
2273 }
3500 }
2274
3501
2275 function makeChangeNoReadonly(doc, change, selUpdate) {
3502 function makeChangeInner(doc, change) {
2276 var selAfter = computeSelAfterChange(doc, change, selUpdate);
3503 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
2277 addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
3504 var selAfter = computeSelAfterChange(doc, change);
3505 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2278
3506
2279 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
3507 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2280 var rebased = [];
3508 var rebased = [];
@@ -2288,17 +3516,41 b' window.CodeMirror = (function() {'
2288 });
3516 });
2289 }
3517 }
2290
3518
2291 function makeChangeFromHistory(doc, type) {
3519 // Revert a change stored in a document's history.
3520 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
2292 if (doc.cm && doc.cm.state.suppressEdits) return;
3521 if (doc.cm && doc.cm.state.suppressEdits) return;
2293
3522
2294 var hist = doc.history;
3523 var hist = doc.history, event, selAfter = doc.sel;
2295 var event = (type == "undo" ? hist.done : hist.undone).pop();
3524 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
2296 if (!event) return;
3525
2297
3526 // Verify that there is a useable event (so that ctrl-z won't
2298 var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
3527 // needlessly clear selection events)
2299 anchorAfter: event.anchorBefore, headAfter: event.headBefore,
3528 for (var i = 0; i < source.length; i++) {
2300 generation: hist.generation};
3529 event = source[i];
2301 (type == "undo" ? hist.undone : hist.done).push(anti);
3530 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
3531 break;
3532 }
3533 if (i == source.length) return;
3534 hist.lastOrigin = hist.lastSelOrigin = null;
3535
3536 for (;;) {
3537 event = source.pop();
3538 if (event.ranges) {
3539 pushSelectionToHistory(event, dest);
3540 if (allowSelectionOnly && !event.equals(doc.sel)) {
3541 setSelection(doc, event, {clearRedo: false});
3542 return;
3543 }
3544 selAfter = event;
3545 }
3546 else break;
3547 }
3548
3549 // Build up a reverse change object to add to the opposite history
3550 // stack (redo when undoing, and vice versa).
3551 var antiChanges = [];
3552 pushSelectionToHistory(selAfter, dest);
3553 dest.push({changes: antiChanges, generation: hist.generation});
2302 hist.generation = event.generation || ++hist.maxGeneration;
3554 hist.generation = event.generation || ++hist.maxGeneration;
2303
3555
2304 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
3556 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
@@ -2307,17 +3559,18 b' window.CodeMirror = (function() {'
2307 var change = event.changes[i];
3559 var change = event.changes[i];
2308 change.origin = type;
3560 change.origin = type;
2309 if (filter && !filterChange(doc, change, false)) {
3561 if (filter && !filterChange(doc, change, false)) {
2310 (type == "undo" ? hist.done : hist.undone).length = 0;
3562 source.length = 0;
2311 return;
3563 return;
2312 }
3564 }
2313
3565
2314 anti.changes.push(historyChangeFromChange(doc, change));
3566 antiChanges.push(historyChangeFromChange(doc, change));
2315
3567
2316 var after = i ? computeSelAfterChange(doc, change, null)
3568 var after = i ? computeSelAfterChange(doc, change) : lst(source);
2317 : {anchor: event.anchorBefore, head: event.headBefore};
2318 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
3569 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
3570 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
2319 var rebased = [];
3571 var rebased = [];
2320
3572
3573 // Propagate to the linked documents
2321 linkedDocs(doc, function(doc, sharedHist) {
3574 linkedDocs(doc, function(doc, sharedHist) {
2322 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
3575 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2323 rebaseHist(doc.history, change);
3576 rebaseHist(doc.history, change);
@@ -2328,14 +3581,24 b' window.CodeMirror = (function() {'
2328 }
3581 }
2329 }
3582 }
2330
3583
3584 // Sub-views need their line numbers shifted when text is added
3585 // above or below them in the parent document.
2331 function shiftDoc(doc, distance) {
3586 function shiftDoc(doc, distance) {
2332 function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
3587 if (distance == 0) return;
2333 doc.first += distance;
3588 doc.first += distance;
2334 if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
3589 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
2335 doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
3590 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
2336 doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
3591 Pos(range.head.line + distance, range.head.ch));
2337 }
3592 }), doc.sel.primIndex);
2338
3593 if (doc.cm) {
3594 regChange(doc.cm, doc.first, doc.first - distance, distance);
3595 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
3596 regLineChange(doc.cm, l, "gutter");
3597 }
3598 }
3599
3600 // More lower-level change function, handling only a single document
3601 // (not linked ones).
2339 function makeChangeSingleDoc(doc, change, selAfter, spans) {
3602 function makeChangeSingleDoc(doc, change, selAfter, spans) {
2340 if (doc.cm && !doc.cm.curOp)
3603 if (doc.cm && !doc.cm.curOp)
2341 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
3604 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
@@ -2361,17 +3624,20 b' window.CodeMirror = (function() {'
2361
3624
2362 change.removed = getBetween(doc, change.from, change.to);
3625 change.removed = getBetween(doc, change.from, change.to);
2363
3626
2364 if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
3627 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
2365 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
3628 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
2366 else updateDoc(doc, change, spans, selAfter);
3629 else updateDoc(doc, change, spans);
2367 }
3630 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
2368
3631 }
2369 function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
3632
3633 // Handle the interaction of a change to a document with the editor
3634 // that this document is part of.
3635 function makeChangeSingleDocInEditor(cm, change, spans) {
2370 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
3636 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2371
3637
2372 var recomputeMaxLength = false, checkWidthStart = from.line;
3638 var recomputeMaxLength = false, checkWidthStart = from.line;
2373 if (!cm.options.lineWrapping) {
3639 if (!cm.options.lineWrapping) {
2374 checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
3640 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
2375 doc.iter(checkWidthStart, to.line + 1, function(line) {
3641 doc.iter(checkWidthStart, to.line + 1, function(line) {
2376 if (line == display.maxLine) {
3642 if (line == display.maxLine) {
2377 recomputeMaxLength = true;
3643 recomputeMaxLength = true;
@@ -2380,14 +3646,14 b' window.CodeMirror = (function() {'
2380 });
3646 });
2381 }
3647 }
2382
3648
2383 if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
3649 if (doc.sel.contains(change.from, change.to) > -1)
2384 cm.curOp.cursorActivity = true;
3650 signalCursorActivity(cm);
2385
3651
2386 updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
3652 updateDoc(doc, change, spans, estimateHeight(cm));
2387
3653
2388 if (!cm.options.lineWrapping) {
3654 if (!cm.options.lineWrapping) {
2389 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
3655 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2390 var len = lineLength(doc, line);
3656 var len = lineLength(line);
2391 if (len > display.maxLineLength) {
3657 if (len > display.maxLineLength) {
2392 display.maxLine = line;
3658 display.maxLine = line;
2393 display.maxLineLength = len;
3659 display.maxLineLength = len;
@@ -2404,196 +3670,63 b' window.CodeMirror = (function() {'
2404
3670
2405 var lendiff = change.text.length - (to.line - from.line) - 1;
3671 var lendiff = change.text.length - (to.line - from.line) - 1;
2406 // Remember that these lines changed, for updating the display
3672 // Remember that these lines changed, for updating the display
2407 regChange(cm, from.line, to.line + 1, lendiff);
3673 if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
2408
3674 regLineChange(cm, from.line, "text");
2409 if (hasHandler(cm, "change")) {
3675 else
2410 var changeObj = {from: from, to: to,
3676 regChange(cm, from.line, to.line + 1, lendiff);
2411 text: change.text,
3677
2412 removed: change.removed,
3678 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
2413 origin: change.origin};
3679 if (changeHandler || changesHandler) {
2414 if (cm.curOp.textChanged) {
3680 var obj = {
2415 for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
3681 from: from, to: to,
2416 cur.next = changeObj;
3682 text: change.text,
2417 } else cm.curOp.textChanged = changeObj;
3683 removed: change.removed,
2418 }
3684 origin: change.origin
3685 };
3686 if (changeHandler) signalLater(cm, "change", cm, obj);
3687 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
3688 }
3689 cm.display.selForContextMenu = null;
2419 }
3690 }
2420
3691
2421 function replaceRange(doc, code, from, to, origin) {
3692 function replaceRange(doc, code, from, to, origin) {
2422 if (!to) to = from;
3693 if (!to) to = from;
2423 if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
3694 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
2424 if (typeof code == "string") code = splitLines(code);
3695 if (typeof code == "string") code = splitLines(code);
2425 makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
3696 makeChange(doc, {from: from, to: to, text: code, origin: origin});
2426 }
3697 }
2427
3698
2428 // POSITION OBJECT
3699 // SCROLLING THINGS INTO VIEW
2429
3700
2430 function Pos(line, ch) {
3701 // If an editor sits on the top or bottom of the window, partially
2431 if (!(this instanceof Pos)) return new Pos(line, ch);
3702 // scrolled out of view, this ensures that the cursor is visible.
2432 this.line = line; this.ch = ch;
3703 function maybeScrollWindow(cm, coords) {
2433 }
3704 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
2434 CodeMirror.Pos = Pos;
2435
2436 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2437 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2438 function copyPos(x) {return Pos(x.line, x.ch);}
2439
2440 // SELECTION
2441
2442 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2443 function clipPos(doc, pos) {
2444 if (pos.line < doc.first) return Pos(doc.first, 0);
2445 var last = doc.first + doc.size - 1;
2446 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2447 return clipToLen(pos, getLine(doc, pos.line).text.length);
2448 }
2449 function clipToLen(pos, linelen) {
2450 var ch = pos.ch;
2451 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2452 else if (ch < 0) return Pos(pos.line, 0);
2453 else return pos;
2454 }
2455 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2456
2457 // If shift is held, this will move the selection anchor. Otherwise,
2458 // it'll set the whole selection.
2459 function extendSelection(doc, pos, other, bias) {
2460 if (doc.sel.shift || doc.sel.extend) {
2461 var anchor = doc.sel.anchor;
2462 if (other) {
2463 var posBefore = posLess(pos, anchor);
2464 if (posBefore != posLess(other, anchor)) {
2465 anchor = pos;
2466 pos = other;
2467 } else if (posBefore != posLess(pos, other)) {
2468 pos = other;
2469 }
2470 }
2471 setSelection(doc, anchor, pos, bias);
2472 } else {
2473 setSelection(doc, pos, other || pos, bias);
2474 }
2475 if (doc.cm) doc.cm.curOp.userSelChange = true;
2476 }
2477
2478 function filterSelectionChange(doc, anchor, head) {
2479 var obj = {anchor: anchor, head: head};
2480 signal(doc, "beforeSelectionChange", doc, obj);
2481 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2482 obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2483 return obj;
2484 }
2485
2486 // Update the selection. Last two args are only used by
2487 // updateDoc, since they have to be expressed in the line
2488 // numbers before the update.
2489 function setSelection(doc, anchor, head, bias, checkAtomic) {
2490 if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2491 var filtered = filterSelectionChange(doc, anchor, head);
2492 head = filtered.head;
2493 anchor = filtered.anchor;
2494 }
2495
2496 var sel = doc.sel;
2497 sel.goalColumn = null;
2498 // Skip over atomic spans.
2499 if (checkAtomic || !posEq(anchor, sel.anchor))
2500 anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2501 if (checkAtomic || !posEq(head, sel.head))
2502 head = skipAtomic(doc, head, bias, checkAtomic != "push");
2503
2504 if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2505
2506 sel.anchor = anchor; sel.head = head;
2507 var inv = posLess(head, anchor);
2508 sel.from = inv ? head : anchor;
2509 sel.to = inv ? anchor : head;
2510
2511 if (doc.cm)
2512 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
2513 doc.cm.curOp.cursorActivity = true;
2514
2515 signalLater(doc, "cursorActivity", doc);
2516 }
2517
2518 function reCheckSelection(cm) {
2519 setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2520 }
2521
2522 function skipAtomic(doc, pos, bias, mayClear) {
2523 var flipped = false, curPos = pos;
2524 var dir = bias || 1;
2525 doc.cantEdit = false;
2526 search: for (;;) {
2527 var line = getLine(doc, curPos.line);
2528 if (line.markedSpans) {
2529 for (var i = 0; i < line.markedSpans.length; ++i) {
2530 var sp = line.markedSpans[i], m = sp.marker;
2531 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2532 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2533 if (mayClear) {
2534 signal(m, "beforeCursorEnter");
2535 if (m.explicitlyCleared) {
2536 if (!line.markedSpans) break;
2537 else {--i; continue;}
2538 }
2539 }
2540 if (!m.atomic) continue;
2541 var newPos = m.find()[dir < 0 ? "from" : "to"];
2542 if (posEq(newPos, curPos)) {
2543 newPos.ch += dir;
2544 if (newPos.ch < 0) {
2545 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2546 else newPos = null;
2547 } else if (newPos.ch > line.text.length) {
2548 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2549 else newPos = null;
2550 }
2551 if (!newPos) {
2552 if (flipped) {
2553 // Driven in a corner -- no valid cursor position found at all
2554 // -- try again *with* clearing, if we didn't already
2555 if (!mayClear) return skipAtomic(doc, pos, bias, true);
2556 // Otherwise, turn off editing until further notice, and return the start of the doc
2557 doc.cantEdit = true;
2558 return Pos(doc.first, 0);
2559 }
2560 flipped = true; newPos = pos; dir = -dir;
2561 }
2562 }
2563 curPos = newPos;
2564 continue search;
2565 }
2566 }
2567 }
2568 return curPos;
2569 }
2570 }
2571
2572 // SCROLLING
2573
2574 function scrollCursorIntoView(cm) {
2575 var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
2576 if (!cm.state.focused) return;
2577 var display = cm.display, box = getRect(display.sizer), doScroll = null;
2578 if (coords.top + box.top < 0) doScroll = true;
3705 if (coords.top + box.top < 0) doScroll = true;
2579 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
3706 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2580 if (doScroll != null && !phantom) {
3707 if (doScroll != null && !phantom) {
2581 var hidden = display.cursor.style.display == "none";
3708 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
2582 if (hidden) {
3709 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
2583 display.cursor.style.display = "";
3710 (coords.bottom - coords.top + scrollerCutOff) + "px; left: " +
2584 display.cursor.style.left = coords.left + "px";
3711 coords.left + "px; width: 2px;");
2585 display.cursor.style.top = (coords.top - display.viewOffset) + "px";
3712 cm.display.lineSpace.appendChild(scrollNode);
2586 }
3713 scrollNode.scrollIntoView(doScroll);
2587 display.cursor.scrollIntoView(doScroll);
3714 cm.display.lineSpace.removeChild(scrollNode);
2588 if (hidden) display.cursor.style.display = "none";
3715 }
2589 }
3716 }
2590 }
3717
2591
3718 // Scroll a given position into view (immediately), verifying that
2592 function scrollPosIntoView(cm, pos, margin) {
3719 // it actually became visible (as line heights are accurately
3720 // measured, the position of something may 'drift' during drawing).
3721 function scrollPosIntoView(cm, pos, end, margin) {
2593 if (margin == null) margin = 0;
3722 if (margin == null) margin = 0;
2594 for (;;) {
3723 for (var limit = 0; limit < 5; limit++) {
2595 var changed = false, coords = cursorCoords(cm, pos);
3724 var changed = false, coords = cursorCoords(cm, pos);
2596 var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
3725 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3726 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
3727 Math.min(coords.top, endCoords.top) - margin,
3728 Math.max(coords.left, endCoords.left),
3729 Math.max(coords.bottom, endCoords.bottom) + margin);
2597 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3730 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2598 if (scrollPos.scrollTop != null) {
3731 if (scrollPos.scrollTop != null) {
2599 setScrollTop(cm, scrollPos.scrollTop);
3732 setScrollTop(cm, scrollPos.scrollTop);
@@ -2607,16 +3740,23 b' window.CodeMirror = (function() {'
2607 }
3740 }
2608 }
3741 }
2609
3742
3743 // Scroll a given set of coordinates into view (immediately).
2610 function scrollIntoView(cm, x1, y1, x2, y2) {
3744 function scrollIntoView(cm, x1, y1, x2, y2) {
2611 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
3745 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2612 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
3746 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2613 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
3747 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2614 }
3748 }
2615
3749
3750 // Calculate a new scroll position needed to scroll the given
3751 // rectangle into view. Returns an object with scrollTop and
3752 // scrollLeft properties. When these are undefined, the
3753 // vertical/horizontal position does not need to be adjusted.
2616 function calculateScrollPos(cm, x1, y1, x2, y2) {
3754 function calculateScrollPos(cm, x1, y1, x2, y2) {
2617 var display = cm.display, snapMargin = textHeight(cm.display);
3755 var display = cm.display, snapMargin = textHeight(cm.display);
2618 if (y1 < 0) y1 = 0;
3756 if (y1 < 0) y1 = 0;
2619 var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
3757 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3758 var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
3759 if (y2 - y1 > screen) y2 = y1 + screen;
2620 var docBottom = cm.doc.height + paddingVert(display);
3760 var docBottom = cm.doc.height + paddingVert(display);
2621 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
3761 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
2622 if (y1 < screentop) {
3762 if (y1 < screentop) {
@@ -2626,47 +3766,86 b' window.CodeMirror = (function() {'
2626 if (newTop != screentop) result.scrollTop = newTop;
3766 if (newTop != screentop) result.scrollTop = newTop;
2627 }
3767 }
2628
3768
2629 var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
3769 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
2630 x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
3770 var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutters.offsetWidth;
2631 var gutterw = display.gutters.offsetWidth;
3771 var tooWide = x2 - x1 > screenw;
2632 var atLeft = x1 < gutterw + 10;
3772 if (tooWide) x2 = x1 + screenw;
2633 if (x1 < screenleft + gutterw || atLeft) {
3773 if (x1 < 10)
2634 if (atLeft) x1 = 0;
3774 result.scrollLeft = 0;
2635 result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
3775 else if (x1 < screenleft)
2636 } else if (x2 > screenw + screenleft - 3) {
3776 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
2637 result.scrollLeft = x2 + 10 - screenw;
3777 else if (x2 > screenw + screenleft - 3)
2638 }
3778 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
3779
2639 return result;
3780 return result;
2640 }
3781 }
2641
3782
2642 function updateScrollPos(cm, left, top) {
3783 // Store a relative adjustment to the scroll position in the current
2643 cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
3784 // operation (to be applied when the operation finishes).
2644 scrollTop: top == null ? cm.doc.scrollTop : top};
2645 }
2646
2647 function addToScrollPos(cm, left, top) {
3785 function addToScrollPos(cm, left, top) {
2648 var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
3786 if (left != null || top != null) resolveScrollToPos(cm);
2649 var scroll = cm.display.scroller;
3787 if (left != null)
2650 pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
3788 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
2651 pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
3789 if (top != null)
3790 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3791 }
3792
3793 // Make sure that at the end of the operation the current cursor is
3794 // shown.
3795 function ensureCursorVisible(cm) {
3796 resolveScrollToPos(cm);
3797 var cur = cm.getCursor(), from = cur, to = cur;
3798 if (!cm.options.lineWrapping) {
3799 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
3800 to = Pos(cur.line, cur.ch + 1);
3801 }
3802 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
3803 }
3804
3805 // When an operation has its scrollToPos property set, and another
3806 // scroll action is applied before the end of the operation, this
3807 // 'simulates' scrolling that position into view in a cheap way, so
3808 // that the effect of intermediate scroll commands is not ignored.
3809 function resolveScrollToPos(cm) {
3810 var range = cm.curOp.scrollToPos;
3811 if (range) {
3812 cm.curOp.scrollToPos = null;
3813 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
3814 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
3815 Math.min(from.top, to.top) - range.margin,
3816 Math.max(from.right, to.right),
3817 Math.max(from.bottom, to.bottom) + range.margin);
3818 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
3819 }
2652 }
3820 }
2653
3821
2654 // API UTILITIES
3822 // API UTILITIES
2655
3823
3824 // Indent the given line. The how parameter can be "smart",
3825 // "add"/null, "subtract", or "prev". When aggressive is false
3826 // (typically set to true for forced single-line indents), empty
3827 // lines are not indented, and places where the mode returns Pass
3828 // are left alone.
2656 function indentLine(cm, n, how, aggressive) {
3829 function indentLine(cm, n, how, aggressive) {
2657 var doc = cm.doc;
3830 var doc = cm.doc, state;
2658 if (how == null) how = "add";
3831 if (how == null) how = "add";
2659 if (how == "smart") {
3832 if (how == "smart") {
2660 if (!cm.doc.mode.indent) how = "prev";
3833 // Fall back to "prev" when the mode doesn't have an indentation
2661 else var state = getStateBefore(cm, n);
3834 // method.
3835 if (!doc.mode.indent) how = "prev";
3836 else state = getStateBefore(cm, n);
2662 }
3837 }
2663
3838
2664 var tabSize = cm.options.tabSize;
3839 var tabSize = cm.options.tabSize;
2665 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
3840 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
3841 if (line.stateAfter) line.stateAfter = null;
2666 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
3842 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2667 if (how == "smart") {
3843 if (!aggressive && !/\S/.test(line.text)) {
2668 indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
3844 indentation = 0;
2669 if (indentation == Pass) {
3845 how = "not";
3846 } else if (how == "smart") {
3847 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
3848 if (indentation == Pass || indentation > 150) {
2670 if (!aggressive) return;
3849 if (!aggressive) return;
2671 how = "prev";
3850 how = "prev";
2672 }
3851 }
@@ -2688,21 +3867,69 b' window.CodeMirror = (function() {'
2688 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
3867 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2689 if (pos < indentation) indentString += spaceStr(indentation - pos);
3868 if (pos < indentation) indentString += spaceStr(indentation - pos);
2690
3869
2691 if (indentString != curSpaceString)
3870 if (indentString != curSpaceString) {
2692 replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
3871 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
3872 } else {
3873 // Ensure that, if the cursor was in the whitespace at the start
3874 // of the line, it is moved to the end of that space.
3875 for (var i = 0; i < doc.sel.ranges.length; i++) {
3876 var range = doc.sel.ranges[i];
3877 if (range.head.line == n && range.head.ch < curSpaceString.length) {
3878 var pos = Pos(n, curSpaceString.length);
3879 replaceOneSelection(doc, i, new Range(pos, pos));
3880 break;
3881 }
3882 }
3883 }
2693 line.stateAfter = null;
3884 line.stateAfter = null;
2694 }
3885 }
2695
3886
2696 function changeLine(cm, handle, op) {
3887 // Utility for applying a change to a line by handle or number,
2697 var no = handle, line = handle, doc = cm.doc;
3888 // returning the number and optionally registering the line as
3889 // changed.
3890 function changeLine(doc, handle, changeType, op) {
3891 var no = handle, line = handle;
2698 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
3892 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2699 else no = lineNo(handle);
3893 else no = lineNo(handle);
2700 if (no == null) return null;
3894 if (no == null) return null;
2701 if (op(line, no)) regChange(cm, no, no + 1);
3895 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
2702 else return null;
2703 return line;
3896 return line;
2704 }
3897 }
2705
3898
3899 // Helper for deleting text near the selection(s), used to implement
3900 // backspace, delete, and similar functionality.
3901 function deleteNearSelection(cm, compute) {
3902 var ranges = cm.doc.sel.ranges, kill = [];
3903 // Build up a set of ranges to kill first, merging overlapping
3904 // ranges.
3905 for (var i = 0; i < ranges.length; i++) {
3906 var toKill = compute(ranges[i]);
3907 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
3908 var replaced = kill.pop();
3909 if (cmp(replaced.from, toKill.from) < 0) {
3910 toKill.from = replaced.from;
3911 break;
3912 }
3913 }
3914 kill.push(toKill);
3915 }
3916 // Next, remove those actual ranges.
3917 runInOp(cm, function() {
3918 for (var i = kill.length - 1; i >= 0; i--)
3919 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
3920 ensureCursorVisible(cm);
3921 });
3922 }
3923
3924 // Used for horizontal relative motion. Dir is -1 or 1 (left or
3925 // right), unit can be "char", "column" (like char, but doesn't
3926 // cross line boundaries), "word" (across next word), or "group" (to
3927 // the start of next group of word or non-word-non-whitespace
3928 // chars). The visually param controls whether, in right-to-left
3929 // text, direction 1 means to move towards the next index in the
3930 // string, or towards the character to the right of the current
3931 // position. The resulting position will have a hitSide=true
3932 // property if it reached the end of the document.
2706 function findPosH(doc, pos, dir, unit, visually) {
3933 function findPosH(doc, pos, dir, unit, visually) {
2707 var line = pos.line, ch = pos.ch, origDir = dir;
3934 var line = pos.line, ch = pos.ch, origDir = dir;
2708 var lineObj = getLine(doc, line);
3935 var lineObj = getLine(doc, line);
@@ -2728,17 +3955,20 b' window.CodeMirror = (function() {'
2728 else if (unit == "column") moveOnce(true);
3955 else if (unit == "column") moveOnce(true);
2729 else if (unit == "word" || unit == "group") {
3956 else if (unit == "word" || unit == "group") {
2730 var sawType = null, group = unit == "group";
3957 var sawType = null, group = unit == "group";
3958 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
2731 for (var first = true;; first = false) {
3959 for (var first = true;; first = false) {
2732 if (dir < 0 && !moveOnce(!first)) break;
3960 if (dir < 0 && !moveOnce(!first)) break;
2733 var cur = lineObj.text.charAt(ch) || "\n";
3961 var cur = lineObj.text.charAt(ch) || "\n";
2734 var type = isWordChar(cur) ? "w"
3962 var type = isWordChar(cur, helper) ? "w"
2735 : !group ? null
3963 : group && cur == "\n" ? "n"
2736 : /\s/.test(cur) ? null
3964 : !group || /\s/.test(cur) ? null
2737 : "p";
3965 : "p";
3966 if (group && !first && !type) type = "s";
2738 if (sawType && sawType != type) {
3967 if (sawType && sawType != type) {
2739 if (dir < 0) {dir = 1; moveOnce();}
3968 if (dir < 0) {dir = 1; moveOnce();}
2740 break;
3969 break;
2741 }
3970 }
3971
2742 if (type) sawType = type;
3972 if (type) sawType = type;
2743 if (dir > 0 && !moveOnce(!first)) break;
3973 if (dir > 0 && !moveOnce(!first)) break;
2744 }
3974 }
@@ -2748,6 +3978,9 b' window.CodeMirror = (function() {'
2748 return result;
3978 return result;
2749 }
3979 }
2750
3980
3981 // For relative vertical movement. Dir may be -1 or 1. Unit can be
3982 // "page" or "line". The resulting position will have a hitSide=true
3983 // property if it reached the end of the document.
2751 function findPosV(cm, pos, dir, unit) {
3984 function findPosV(cm, pos, dir, unit) {
2752 var doc = cm.doc, x = pos.left, y;
3985 var doc = cm.doc, x = pos.left, y;
2753 if (unit == "page") {
3986 if (unit == "page") {
@@ -2765,32 +3998,19 b' window.CodeMirror = (function() {'
2765 return target;
3998 return target;
2766 }
3999 }
2767
4000
2768 function findWordAt(line, pos) {
4001 // EDITOR METHODS
2769 var start = pos.ch, end = pos.ch;
4002
2770 if (line) {
4003 // The publicly visible API. Note that methodOp(f) means
2771 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
4004 // 'wrap f in an operation, performed on its `this` parameter'.
2772 var startChar = line.charAt(start);
4005
2773 var check = isWordChar(startChar) ? isWordChar
4006 // This is not the complete set of editor methods. Most of the
2774 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
4007 // methods defined on the Doc type are also injected into
2775 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
4008 // CodeMirror.prototype, for backwards compatibility and
2776 while (start > 0 && check(line.charAt(start - 1))) --start;
4009 // convenience.
2777 while (end < line.length && check(line.charAt(end))) ++end;
2778 }
2779 return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2780 }
2781
2782 function selectLine(cm, line) {
2783 extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2784 }
2785
2786 // PROTOTYPE
2787
2788 // The publicly visible API. Note that operation(null, f) means
2789 // 'wrap f in an operation, performed on its `this` parameter'
2790
4010
2791 CodeMirror.prototype = {
4011 CodeMirror.prototype = {
2792 constructor: CodeMirror,
4012 constructor: CodeMirror,
2793 focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
4013 focus: function(){window.focus(); focusInput(this); fastPoll(this);},
2794
4014
2795 setOption: function(option, value) {
4015 setOption: function(option, value) {
2796 var options = this.options, old = options[option];
4016 var options = this.options, old = options[option];
@@ -2815,14 +4035,14 b' window.CodeMirror = (function() {'
2815 }
4035 }
2816 },
4036 },
2817
4037
2818 addOverlay: operation(null, function(spec, options) {
4038 addOverlay: methodOp(function(spec, options) {
2819 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4039 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2820 if (mode.startState) throw new Error("Overlays may not be stateful.");
4040 if (mode.startState) throw new Error("Overlays may not be stateful.");
2821 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4041 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2822 this.state.modeGen++;
4042 this.state.modeGen++;
2823 regChange(this);
4043 regChange(this);
2824 }),
4044 }),
2825 removeOverlay: operation(null, function(spec) {
4045 removeOverlay: methodOp(function(spec) {
2826 var overlays = this.state.overlays;
4046 var overlays = this.state.overlays;
2827 for (var i = 0; i < overlays.length; ++i) {
4047 for (var i = 0; i < overlays.length; ++i) {
2828 var cur = overlays[i].modeSpec;
4048 var cur = overlays[i].modeSpec;
@@ -2835,18 +4055,32 b' window.CodeMirror = (function() {'
2835 }
4055 }
2836 }),
4056 }),
2837
4057
2838 indentLine: operation(null, function(n, dir, aggressive) {
4058 indentLine: methodOp(function(n, dir, aggressive) {
2839 if (typeof dir != "string" && typeof dir != "number") {
4059 if (typeof dir != "string" && typeof dir != "number") {
2840 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4060 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2841 else dir = dir ? "add" : "subtract";
4061 else dir = dir ? "add" : "subtract";
2842 }
4062 }
2843 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4063 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2844 }),
4064 }),
2845 indentSelection: operation(null, function(how) {
4065 indentSelection: methodOp(function(how) {
2846 var sel = this.doc.sel;
4066 var ranges = this.doc.sel.ranges, end = -1;
2847 if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
4067 for (var i = 0; i < ranges.length; i++) {
2848 var e = sel.to.line - (sel.to.ch ? 0 : 1);
4068 var range = ranges[i];
2849 for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
4069 if (!range.empty()) {
4070 var from = range.from(), to = range.to();
4071 var start = Math.max(end, from.line);
4072 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4073 for (var j = start; j < end; ++j)
4074 indentLine(this, j, how);
4075 var newRanges = this.doc.sel.ranges;
4076 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4077 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4078 } else if (range.head.line > end) {
4079 indentLine(this, range.head.line, how, true);
4080 end = range.head.line;
4081 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4082 }
4083 }
2850 }),
4084 }),
2851
4085
2852 // Fetch the parser token for a given character. Useful for hacks
4086 // Fetch the parser token for a given character. Useful for hacks
@@ -2859,12 +4093,11 b' window.CodeMirror = (function() {'
2859 var stream = new StringStream(line.text, this.options.tabSize);
4093 var stream = new StringStream(line.text, this.options.tabSize);
2860 while (stream.pos < pos.ch && !stream.eol()) {
4094 while (stream.pos < pos.ch && !stream.eol()) {
2861 stream.start = stream.pos;
4095 stream.start = stream.pos;
2862 var style = mode.token(stream, state);
4096 var style = readToken(mode, stream, state);
2863 }
4097 }
2864 return {start: stream.start,
4098 return {start: stream.start,
2865 end: stream.pos,
4099 end: stream.pos,
2866 string: stream.current(),
4100 string: stream.current(),
2867 className: style || null, // Deprecated, use 'type' instead
2868 type: style || null,
4101 type: style || null,
2869 state: state};
4102 state: state};
2870 },
4103 },
@@ -2873,13 +4106,16 b' window.CodeMirror = (function() {'
2873 pos = clipPos(this.doc, pos);
4106 pos = clipPos(this.doc, pos);
2874 var styles = getLineStyles(this, getLine(this.doc, pos.line));
4107 var styles = getLineStyles(this, getLine(this.doc, pos.line));
2875 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4108 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
2876 if (ch == 0) return styles[2];
4109 var type;
2877 for (;;) {
4110 if (ch == 0) type = styles[2];
4111 else for (;;) {
2878 var mid = (before + after) >> 1;
4112 var mid = (before + after) >> 1;
2879 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4113 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
2880 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4114 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
2881 else return styles[mid * 2 + 2];
4115 else { type = styles[mid * 2 + 2]; break; }
2882 }
4116 }
4117 var cut = type ? type.indexOf("cm-overlay ") : -1;
4118 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
2883 },
4119 },
2884
4120
2885 getModeAt: function(pos) {
4121 getModeAt: function(pos) {
@@ -2889,11 +4125,31 b' window.CodeMirror = (function() {'
2889 },
4125 },
2890
4126
2891 getHelper: function(pos, type) {
4127 getHelper: function(pos, type) {
2892 if (!helpers.hasOwnProperty(type)) return;
4128 return this.getHelpers(pos, type)[0];
4129 },
4130
4131 getHelpers: function(pos, type) {
4132 var found = [];
4133 if (!helpers.hasOwnProperty(type)) return helpers;
2893 var help = helpers[type], mode = this.getModeAt(pos);
4134 var help = helpers[type], mode = this.getModeAt(pos);
2894 return mode[type] && help[mode[type]] ||
4135 if (typeof mode[type] == "string") {
2895 mode.helperType && help[mode.helperType] ||
4136 if (help[mode[type]]) found.push(help[mode[type]]);
2896 help[mode.name];
4137 } else if (mode[type]) {
4138 for (var i = 0; i < mode[type].length; i++) {
4139 var val = help[mode[type][i]];
4140 if (val) found.push(val);
4141 }
4142 } else if (mode.helperType && help[mode.helperType]) {
4143 found.push(help[mode.helperType]);
4144 } else if (help[mode.name]) {
4145 found.push(help[mode.name]);
4146 }
4147 for (var i = 0; i < help._global.length; i++) {
4148 var cur = help._global[i];
4149 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
4150 found.push(cur.val);
4151 }
4152 return found;
2897 },
4153 },
2898
4154
2899 getStateAfter: function(line, precise) {
4155 getStateAfter: function(line, precise) {
@@ -2903,10 +4159,10 b' window.CodeMirror = (function() {'
2903 },
4159 },
2904
4160
2905 cursorCoords: function(start, mode) {
4161 cursorCoords: function(start, mode) {
2906 var pos, sel = this.doc.sel;
4162 var pos, range = this.doc.sel.primary();
2907 if (start == null) pos = sel.head;
4163 if (start == null) pos = range.head;
2908 else if (typeof start == "object") pos = clipPos(this.doc, start);
4164 else if (typeof start == "object") pos = clipPos(this.doc, start);
2909 else pos = start ? sel.from : sel.to;
4165 else pos = start ? range.from() : range.to();
2910 return cursorCoords(this, pos, mode || "page");
4166 return cursorCoords(this, pos, mode || "page");
2911 },
4167 },
2912
4168
@@ -2928,15 +4184,15 b' window.CodeMirror = (function() {'
2928 if (line < this.doc.first) line = this.doc.first;
4184 if (line < this.doc.first) line = this.doc.first;
2929 else if (line > last) { line = last; end = true; }
4185 else if (line > last) { line = last; end = true; }
2930 var lineObj = getLine(this.doc, line);
4186 var lineObj = getLine(this.doc, line);
2931 return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
4187 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
2932 (end ? lineObj.height : 0);
4188 (end ? this.doc.height - heightAtLine(lineObj) : 0);
2933 },
4189 },
2934
4190
2935 defaultTextHeight: function() { return textHeight(this.display); },
4191 defaultTextHeight: function() { return textHeight(this.display); },
2936 defaultCharWidth: function() { return charWidth(this.display); },
4192 defaultCharWidth: function() { return charWidth(this.display); },
2937
4193
2938 setGutterMarker: operation(null, function(line, gutterID, value) {
4194 setGutterMarker: methodOp(function(line, gutterID, value) {
2939 return changeLine(this, line, function(line) {
4195 return changeLine(this.doc, line, "gutter", function(line) {
2940 var markers = line.gutterMarkers || (line.gutterMarkers = {});
4196 var markers = line.gutterMarkers || (line.gutterMarkers = {});
2941 markers[gutterID] = value;
4197 markers[gutterID] = value;
2942 if (!value && isEmpty(markers)) line.gutterMarkers = null;
4198 if (!value && isEmpty(markers)) line.gutterMarkers = null;
@@ -2944,45 +4200,19 b' window.CodeMirror = (function() {'
2944 });
4200 });
2945 }),
4201 }),
2946
4202
2947 clearGutter: operation(null, function(gutterID) {
4203 clearGutter: methodOp(function(gutterID) {
2948 var cm = this, doc = cm.doc, i = doc.first;
4204 var cm = this, doc = cm.doc, i = doc.first;
2949 doc.iter(function(line) {
4205 doc.iter(function(line) {
2950 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
4206 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2951 line.gutterMarkers[gutterID] = null;
4207 line.gutterMarkers[gutterID] = null;
2952 regChange(cm, i, i + 1);
4208 regLineChange(cm, i, "gutter");
2953 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
4209 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2954 }
4210 }
2955 ++i;
4211 ++i;
2956 });
4212 });
2957 }),
4213 }),
2958
4214
2959 addLineClass: operation(null, function(handle, where, cls) {
4215 addLineWidget: methodOp(function(handle, node, options) {
2960 return changeLine(this, handle, function(line) {
2961 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2962 if (!line[prop]) line[prop] = cls;
2963 else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
2964 else line[prop] += " " + cls;
2965 return true;
2966 });
2967 }),
2968
2969 removeLineClass: operation(null, function(handle, where, cls) {
2970 return changeLine(this, handle, function(line) {
2971 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2972 var cur = line[prop];
2973 if (!cur) return false;
2974 else if (cls == null) line[prop] = null;
2975 else {
2976 var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
2977 if (!found) return false;
2978 var end = found.index + found[0].length;
2979 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
2980 }
2981 return true;
2982 });
2983 }),
2984
2985 addLineWidget: operation(null, function(handle, node, options) {
2986 return addLineWidget(this, handle, node, options);
4216 return addLineWidget(this, handle, node, options);
2987 }),
4217 }),
2988
4218
@@ -3003,7 +4233,7 b' window.CodeMirror = (function() {'
3003 widgets: line.widgets};
4233 widgets: line.widgets};
3004 },
4234 },
3005
4235
3006 getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
4236 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
3007
4237
3008 addWidget: function(pos, node, scroll, vert, horiz) {
4238 addWidget: function(pos, node, scroll, vert, horiz) {
3009 var display = this.display;
4239 var display = this.display;
@@ -3038,9 +4268,14 b' window.CodeMirror = (function() {'
3038 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
4268 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
3039 },
4269 },
3040
4270
3041 triggerOnKeyDown: operation(null, onKeyDown),
4271 triggerOnKeyDown: methodOp(onKeyDown),
3042
4272 triggerOnKeyPress: methodOp(onKeyPress),
3043 execCommand: function(cmd) {return commands[cmd](this);},
4273 triggerOnKeyUp: onKeyUp,
4274
4275 execCommand: function(cmd) {
4276 if (commands.hasOwnProperty(cmd))
4277 return commands[cmd](this);
4278 },
3044
4279
3045 findPosH: function(from, amount, unit, visually) {
4280 findPosH: function(from, amount, unit, visually) {
3046 var dir = 1;
4281 var dir = 1;
@@ -3052,20 +4287,25 b' window.CodeMirror = (function() {'
3052 return cur;
4287 return cur;
3053 },
4288 },
3054
4289
3055 moveH: operation(null, function(dir, unit) {
4290 moveH: methodOp(function(dir, unit) {
3056 var sel = this.doc.sel, pos;
4291 var cm = this;
3057 if (sel.shift || sel.extend || posEq(sel.from, sel.to))
4292 cm.extendSelectionsBy(function(range) {
3058 pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
4293 if (cm.display.shift || cm.doc.extend || range.empty())
4294 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
4295 else
4296 return dir < 0 ? range.from() : range.to();
4297 }, sel_move);
4298 }),
4299
4300 deleteH: methodOp(function(dir, unit) {
4301 var sel = this.doc.sel, doc = this.doc;
4302 if (sel.somethingSelected())
4303 doc.replaceSelection("", null, "+delete");
3059 else
4304 else
3060 pos = dir < 0 ? sel.from : sel.to;
4305 deleteNearSelection(this, function(range) {
3061 extendSelection(this.doc, pos, pos, dir);
4306 var other = findPosH(doc, range.head, dir, unit, false);
3062 }),
4307 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
3063
4308 });
3064 deleteH: operation(null, function(dir, unit) {
3065 var sel = this.doc.sel;
3066 if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
3067 else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
3068 this.curOp.userSelChange = true;
3069 }),
4309 }),
3070
4310
3071 findPosV: function(from, amount, unit, goalColumn) {
4311 findPosV: function(from, amount, unit, goalColumn) {
@@ -3081,28 +4321,57 b' window.CodeMirror = (function() {'
3081 return cur;
4321 return cur;
3082 },
4322 },
3083
4323
3084 moveV: operation(null, function(dir, unit) {
4324 moveV: methodOp(function(dir, unit) {
3085 var sel = this.doc.sel;
4325 var cm = this, doc = this.doc, goals = [];
3086 var pos = cursorCoords(this, sel.head, "div");
4326 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
3087 if (sel.goalColumn != null) pos.left = sel.goalColumn;
4327 doc.extendSelectionsBy(function(range) {
3088 var target = findPosV(this, pos, dir, unit);
4328 if (collapse)
3089
4329 return dir < 0 ? range.from() : range.to();
3090 if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
4330 var headPos = cursorCoords(cm, range.head, "div");
3091 extendSelection(this.doc, target, target, dir);
4331 if (range.goalColumn != null) headPos.left = range.goalColumn;
3092 sel.goalColumn = pos.left;
4332 goals.push(headPos.left);
4333 var pos = findPosV(cm, headPos, dir, unit);
4334 if (unit == "page" && range == doc.sel.primary())
4335 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
4336 return pos;
4337 }, sel_move);
4338 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
4339 doc.sel.ranges[i].goalColumn = goals[i];
3093 }),
4340 }),
3094
4341
4342 // Find the word at the given position (as returned by coordsChar).
4343 findWordAt: function(pos) {
4344 var doc = this.doc, line = getLine(doc, pos.line).text;
4345 var start = pos.ch, end = pos.ch;
4346 if (line) {
4347 var helper = this.getHelper(pos, "wordChars");
4348 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
4349 var startChar = line.charAt(start);
4350 var check = isWordChar(startChar, helper)
4351 ? function(ch) { return isWordChar(ch, helper); }
4352 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
4353 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
4354 while (start > 0 && check(line.charAt(start - 1))) --start;
4355 while (end < line.length && check(line.charAt(end))) ++end;
4356 }
4357 return new Range(Pos(pos.line, start), Pos(pos.line, end));
4358 },
4359
3095 toggleOverwrite: function(value) {
4360 toggleOverwrite: function(value) {
3096 if (value != null && value == this.state.overwrite) return;
4361 if (value != null && value == this.state.overwrite) return;
3097 if (this.state.overwrite = !this.state.overwrite)
4362 if (this.state.overwrite = !this.state.overwrite)
3098 this.display.cursor.className += " CodeMirror-overwrite";
4363 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
3099 else
4364 else
3100 this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
4365 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
4366
4367 signal(this, "overwriteToggle", this, this.state.overwrite);
3101 },
4368 },
3102 hasFocus: function() { return this.state.focused; },
4369 hasFocus: function() { return activeElt() == this.display.input; },
3103
4370
3104 scrollTo: operation(null, function(x, y) {
4371 scrollTo: methodOp(function(x, y) {
3105 updateScrollPos(this, x, y);
4372 if (x != null || y != null) resolveScrollToPos(this);
4373 if (x != null) this.curOp.scrollLeft = x;
4374 if (y != null) this.curOp.scrollTop = y;
3106 }),
4375 }),
3107 getScrollInfo: function() {
4376 getScrollInfo: function() {
3108 var scroller = this.display.scroller, co = scrollerCutOff;
4377 var scroller = this.display.scroller, co = scrollerCutOff;
@@ -3111,46 +4380,71 b' window.CodeMirror = (function() {'
3111 clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
4380 clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
3112 },
4381 },
3113
4382
3114 scrollIntoView: operation(null, function(pos, margin) {
4383 scrollIntoView: methodOp(function(range, margin) {
3115 if (typeof pos == "number") pos = Pos(pos, 0);
4384 if (range == null) {
3116 if (!margin) margin = 0;
4385 range = {from: this.doc.sel.primary().head, to: null};
3117 var coords = pos;
4386 if (margin == null) margin = this.options.cursorScrollMargin;
3118
4387 } else if (typeof range == "number") {
3119 if (!pos || pos.line != null) {
4388 range = {from: Pos(range, 0), to: null};
3120 this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
4389 } else if (range.from == null) {
3121 this.curOp.scrollToPosMargin = margin;
4390 range = {from: range, to: null};
3122 coords = cursorCoords(this, this.curOp.scrollToPos);
4391 }
3123 }
4392 if (!range.to) range.to = range.from;
3124 var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
4393 range.margin = margin || 0;
3125 updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
4394
4395 if (range.from.line != null) {
4396 resolveScrollToPos(this);
4397 this.curOp.scrollToPos = range;
4398 } else {
4399 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
4400 Math.min(range.from.top, range.to.top) - range.margin,
4401 Math.max(range.from.right, range.to.right),
4402 Math.max(range.from.bottom, range.to.bottom) + range.margin);
4403 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4404 }
3126 }),
4405 }),
3127
4406
3128 setSize: operation(null, function(width, height) {
4407 setSize: methodOp(function(width, height) {
4408 var cm = this;
3129 function interpret(val) {
4409 function interpret(val) {
3130 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
4410 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
3131 }
4411 }
3132 if (width != null) this.display.wrapper.style.width = interpret(width);
4412 if (width != null) cm.display.wrapper.style.width = interpret(width);
3133 if (height != null) this.display.wrapper.style.height = interpret(height);
4413 if (height != null) cm.display.wrapper.style.height = interpret(height);
3134 if (this.options.lineWrapping)
4414 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
3135 this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
4415 var lineNo = cm.display.viewFrom;
3136 this.curOp.forceUpdate = true;
4416 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
4417 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
4418 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
4419 ++lineNo;
4420 });
4421 cm.curOp.forceUpdate = true;
4422 signal(cm, "refresh", this);
3137 }),
4423 }),
3138
4424
3139 operation: function(f){return runInOp(this, f);},
4425 operation: function(f){return runInOp(this, f);},
3140
4426
3141 refresh: operation(null, function() {
4427 refresh: methodOp(function() {
3142 clearCaches(this);
4428 var oldHeight = this.display.cachedTextHeight;
3143 updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
3144 regChange(this);
4429 regChange(this);
4430 this.curOp.forceUpdate = true;
4431 clearCaches(this);
4432 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
4433 updateGutterSpace(this);
4434 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
4435 estimateLineHeights(this);
4436 signal(this, "refresh", this);
3145 }),
4437 }),
3146
4438
3147 swapDoc: operation(null, function(doc) {
4439 swapDoc: methodOp(function(doc) {
3148 var old = this.doc;
4440 var old = this.doc;
3149 old.cm = null;
4441 old.cm = null;
3150 attachDoc(this, doc);
4442 attachDoc(this, doc);
3151 clearCaches(this);
4443 clearCaches(this);
3152 resetInput(this, true);
4444 resetInput(this);
3153 updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
4445 this.scrollTo(doc.scrollLeft, doc.scrollTop);
4446 this.curOp.forceScroll = true;
4447 signalLater(this, "swapDoc", this, old);
3154 return old;
4448 return old;
3155 }),
4449 }),
3156
4450
@@ -3163,10 +4457,10 b' window.CodeMirror = (function() {'
3163
4457
3164 // OPTION DEFAULTS
4458 // OPTION DEFAULTS
3165
4459
3166 var optionHandlers = CodeMirror.optionHandlers = {};
3167
3168 // The default configuration options.
4460 // The default configuration options.
3169 var defaults = CodeMirror.defaults = {};
4461 var defaults = CodeMirror.defaults = {};
4462 // Functions to run when options are changed.
4463 var optionHandlers = CodeMirror.optionHandlers = {};
3170
4464
3171 function option(name, deflt, handle, notOnInit) {
4465 function option(name, deflt, handle, notOnInit) {
3172 CodeMirror.defaults[name] = deflt;
4466 CodeMirror.defaults[name] = deflt;
@@ -3174,6 +4468,7 b' window.CodeMirror = (function() {'
3174 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
4468 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3175 }
4469 }
3176
4470
4471 // Passed to option handlers when there is no old value.
3177 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
4472 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3178
4473
3179 // These two are, on init, called from the constructor because they
4474 // These two are, on init, called from the constructor because they
@@ -3190,12 +4485,18 b' window.CodeMirror = (function() {'
3190 option("indentWithTabs", false);
4485 option("indentWithTabs", false);
3191 option("smartIndent", true);
4486 option("smartIndent", true);
3192 option("tabSize", 4, function(cm) {
4487 option("tabSize", 4, function(cm) {
3193 loadMode(cm);
4488 resetModeState(cm);
3194 clearCaches(cm);
4489 clearCaches(cm);
3195 regChange(cm);
4490 regChange(cm);
3196 }, true);
4491 }, true);
4492 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val) {
4493 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
4494 cm.refresh();
4495 }, true);
4496 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
3197 option("electricChars", true);
4497 option("electricChars", true);
3198 option("rtlMoveVisually", !windows);
4498 option("rtlMoveVisually", !windows);
4499 option("wholeLineUpdateBefore", true);
3199
4500
3200 option("theme", "default", function(cm) {
4501 option("theme", "default", function(cm) {
3201 themeChanged(cm);
4502 themeChanged(cm);
@@ -3204,9 +4505,6 b' window.CodeMirror = (function() {'
3204 option("keyMap", "default", keyMapChanged);
4505 option("keyMap", "default", keyMapChanged);
3205 option("extraKeys", null);
4506 option("extraKeys", null);
3206
4507
3207 option("onKeyEvent", null);
3208 option("onDragEvent", null);
3209
3210 option("lineWrapping", false, wrappingChanged, true);
4508 option("lineWrapping", false, wrappingChanged, true);
3211 option("gutters", [], function(cm) {
4509 option("gutters", [], function(cm) {
3212 setGuttersForLineNumbers(cm.options);
4510 setGuttersForLineNumbers(cm.options);
@@ -3225,23 +4523,34 b' window.CodeMirror = (function() {'
3225 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
4523 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3226 option("showCursorWhenSelecting", false, updateSelection, true);
4524 option("showCursorWhenSelecting", false, updateSelection, true);
3227
4525
4526 option("resetSelectionOnContextMenu", true);
4527
3228 option("readOnly", false, function(cm, val) {
4528 option("readOnly", false, function(cm, val) {
3229 if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
4529 if (val == "nocursor") {
3230 else if (!val) resetInput(cm, true);
4530 onBlur(cm);
4531 cm.display.input.blur();
4532 cm.display.disabled = true;
4533 } else {
4534 cm.display.disabled = false;
4535 if (!val) resetInput(cm);
4536 }
3231 });
4537 });
4538 option("disableInput", false, function(cm, val) {if (!val) resetInput(cm);}, true);
3232 option("dragDrop", true);
4539 option("dragDrop", true);
3233
4540
3234 option("cursorBlinkRate", 530);
4541 option("cursorBlinkRate", 530);
3235 option("cursorScrollMargin", 0);
4542 option("cursorScrollMargin", 0);
3236 option("cursorHeight", 1);
4543 option("cursorHeight", 1, updateSelection, true);
4544 option("singleCursorHeightPerLine", true, updateSelection, true);
3237 option("workTime", 100);
4545 option("workTime", 100);
3238 option("workDelay", 100);
4546 option("workDelay", 100);
3239 option("flattenSpans", true);
4547 option("flattenSpans", true, resetModeState, true);
4548 option("addModeClass", false, resetModeState, true);
3240 option("pollInterval", 100);
4549 option("pollInterval", 100);
3241 option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
4550 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
3242 option("historyEventDelay", 500);
4551 option("historyEventDelay", 1250);
3243 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
4552 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3244 option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
4553 option("maxHighlightLength", 10000, resetModeState, true);
3245 option("moveInputWithCursor", true, function(cm, val) {
4554 option("moveInputWithCursor", true, function(cm, val) {
3246 if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
4555 if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
3247 });
4556 });
@@ -3256,12 +4565,13 b' window.CodeMirror = (function() {'
3256 // Known modes, by name and by MIME
4565 // Known modes, by name and by MIME
3257 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
4566 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3258
4567
4568 // Extra arguments are stored as the mode's dependencies, which is
4569 // used by (legacy) mechanisms like loadmode.js to automatically
4570 // load a mode. (Preferred mechanism is the require/define calls.)
3259 CodeMirror.defineMode = function(name, mode) {
4571 CodeMirror.defineMode = function(name, mode) {
3260 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
4572 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3261 if (arguments.length > 2) {
4573 if (arguments.length > 2)
3262 mode.dependencies = [];
4574 mode.dependencies = Array.prototype.slice.call(arguments, 2);
3263 for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3264 }
3265 modes[name] = mode;
4575 modes[name] = mode;
3266 };
4576 };
3267
4577
@@ -3269,11 +4579,14 b' window.CodeMirror = (function() {'
3269 mimeModes[mime] = spec;
4579 mimeModes[mime] = spec;
3270 };
4580 };
3271
4581
4582 // Given a MIME type, a {name, ...options} config object, or a name
4583 // string, return a mode config object.
3272 CodeMirror.resolveMode = function(spec) {
4584 CodeMirror.resolveMode = function(spec) {
3273 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
4585 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
3274 spec = mimeModes[spec];
4586 spec = mimeModes[spec];
3275 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
4587 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
3276 var found = mimeModes[spec.name];
4588 var found = mimeModes[spec.name];
4589 if (typeof found == "string") found = {name: found};
3277 spec = createObj(found, spec);
4590 spec = createObj(found, spec);
3278 spec.name = found.name;
4591 spec.name = found.name;
3279 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
4592 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
@@ -3283,6 +4596,8 b' window.CodeMirror = (function() {'
3283 else return spec || {name: "null"};
4596 else return spec || {name: "null"};
3284 };
4597 };
3285
4598
4599 // Given a mode spec (anything that resolveMode accepts), find and
4600 // initialize an actual mode object.
3286 CodeMirror.getMode = function(options, spec) {
4601 CodeMirror.getMode = function(options, spec) {
3287 var spec = CodeMirror.resolveMode(spec);
4602 var spec = CodeMirror.resolveMode(spec);
3288 var mfactory = modes[spec.name];
4603 var mfactory = modes[spec.name];
@@ -3297,15 +4612,21 b' window.CodeMirror = (function() {'
3297 }
4612 }
3298 }
4613 }
3299 modeObj.name = spec.name;
4614 modeObj.name = spec.name;
4615 if (spec.helperType) modeObj.helperType = spec.helperType;
4616 if (spec.modeProps) for (var prop in spec.modeProps)
4617 modeObj[prop] = spec.modeProps[prop];
3300
4618
3301 return modeObj;
4619 return modeObj;
3302 };
4620 };
3303
4621
4622 // Minimal default mode.
3304 CodeMirror.defineMode("null", function() {
4623 CodeMirror.defineMode("null", function() {
3305 return {token: function(stream) {stream.skipToEnd();}};
4624 return {token: function(stream) {stream.skipToEnd();}};
3306 });
4625 });
3307 CodeMirror.defineMIME("text/plain", "null");
4626 CodeMirror.defineMIME("text/plain", "null");
3308
4627
4628 // This can be used to attach properties to mode objects from
4629 // outside the actual mode definition.
3309 var modeExtensions = CodeMirror.modeExtensions = {};
4630 var modeExtensions = CodeMirror.modeExtensions = {};
3310 CodeMirror.extendMode = function(mode, properties) {
4631 CodeMirror.extendMode = function(mode, properties) {
3311 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
4632 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
@@ -3327,19 +4648,20 b' window.CodeMirror = (function() {'
3327
4648
3328 var helpers = CodeMirror.helpers = {};
4649 var helpers = CodeMirror.helpers = {};
3329 CodeMirror.registerHelper = function(type, name, value) {
4650 CodeMirror.registerHelper = function(type, name, value) {
3330 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
4651 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
3331 helpers[type][name] = value;
4652 helpers[type][name] = value;
3332 };
4653 };
3333
4654 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
3334 // UTILITIES
4655 CodeMirror.registerHelper(type, name, value);
3335
4656 helpers[type]._global.push({pred: predicate, val: value});
3336 CodeMirror.isWordChar = isWordChar;
4657 };
3337
4658
3338 // MODE STATE HANDLING
4659 // MODE STATE HANDLING
3339
4660
3340 // Utility functions for working with state. Exported because modes
4661 // Utility functions for working with state. Exported because nested
3341 // sometimes need to do this.
4662 // modes need to do this for their inner modes.
3342 function copyState(mode, state) {
4663
4664 var copyState = CodeMirror.copyState = function(mode, state) {
3343 if (state === true) return state;
4665 if (state === true) return state;
3344 if (mode.copyState) return mode.copyState(state);
4666 if (mode.copyState) return mode.copyState(state);
3345 var nstate = {};
4667 var nstate = {};
@@ -3349,14 +4671,14 b' window.CodeMirror = (function() {'
3349 nstate[n] = val;
4671 nstate[n] = val;
3350 }
4672 }
3351 return nstate;
4673 return nstate;
3352 }
4674 };
3353 CodeMirror.copyState = copyState;
4675
3354
4676 var startState = CodeMirror.startState = function(mode, a1, a2) {
3355 function startState(mode, a1, a2) {
3356 return mode.startState ? mode.startState(a1, a2) : true;
4677 return mode.startState ? mode.startState(a1, a2) : true;
3357 }
4678 };
3358 CodeMirror.startState = startState;
4679
3359
4680 // Given a mode and a state (for that mode), find the inner mode and
4681 // state at the position that the state refers to.
3360 CodeMirror.innerMode = function(mode, state) {
4682 CodeMirror.innerMode = function(mode, state) {
3361 while (mode.innerMode) {
4683 while (mode.innerMode) {
3362 var info = mode.innerMode(state);
4684 var info = mode.innerMode(state);
@@ -3369,49 +4691,89 b' window.CodeMirror = (function() {'
3369
4691
3370 // STANDARD COMMANDS
4692 // STANDARD COMMANDS
3371
4693
4694 // Commands are parameter-less actions that can be performed on an
4695 // editor, mostly used for keybindings.
3372 var commands = CodeMirror.commands = {
4696 var commands = CodeMirror.commands = {
3373 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
4697 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
4698 singleSelection: function(cm) {
4699 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
4700 },
3374 killLine: function(cm) {
4701 killLine: function(cm) {
3375 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
4702 deleteNearSelection(cm, function(range) {
3376 if (!sel && cm.getLine(from.line).length == from.ch)
4703 if (range.empty()) {
3377 cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
4704 var len = getLine(cm.doc, range.head.line).text.length;
3378 else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
4705 if (range.head.ch == len && range.head.line < cm.lastLine())
4706 return {from: range.head, to: Pos(range.head.line + 1, 0)};
4707 else
4708 return {from: range.head, to: Pos(range.head.line, len)};
4709 } else {
4710 return {from: range.from(), to: range.to()};
4711 }
4712 });
3379 },
4713 },
3380 deleteLine: function(cm) {
4714 deleteLine: function(cm) {
3381 var l = cm.getCursor().line;
4715 deleteNearSelection(cm, function(range) {
3382 cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
4716 return {from: Pos(range.from().line, 0),
4717 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
4718 });
3383 },
4719 },
3384 delLineLeft: function(cm) {
4720 delLineLeft: function(cm) {
3385 var cur = cm.getCursor();
4721 deleteNearSelection(cm, function(range) {
3386 cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
4722 return {from: Pos(range.from().line, 0), to: range.from()};
4723 });
4724 },
4725 delWrappedLineLeft: function(cm) {
4726 deleteNearSelection(cm, function(range) {
4727 var top = cm.charCoords(range.head, "div").top + 5;
4728 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
4729 return {from: leftPos, to: range.from()};
4730 });
4731 },
4732 delWrappedLineRight: function(cm) {
4733 deleteNearSelection(cm, function(range) {
4734 var top = cm.charCoords(range.head, "div").top + 5;
4735 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
4736 return {from: range.from(), to: rightPos };
4737 });
3387 },
4738 },
3388 undo: function(cm) {cm.undo();},
4739 undo: function(cm) {cm.undo();},
3389 redo: function(cm) {cm.redo();},
4740 redo: function(cm) {cm.redo();},
4741 undoSelection: function(cm) {cm.undoSelection();},
4742 redoSelection: function(cm) {cm.redoSelection();},
3390 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
4743 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3391 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
4744 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3392 goLineStart: function(cm) {
4745 goLineStart: function(cm) {
3393 cm.extendSelection(lineStart(cm, cm.getCursor().line));
4746 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
4747 {origin: "+move", bias: 1});
3394 },
4748 },
3395 goLineStartSmart: function(cm) {
4749 goLineStartSmart: function(cm) {
3396 var cur = cm.getCursor(), start = lineStart(cm, cur.line);
4750 cm.extendSelectionsBy(function(range) {
3397 var line = cm.getLineHandle(start.line);
4751 return lineStartSmart(cm, range.head);
3398 var order = getOrder(line);
4752 }, {origin: "+move", bias: 1});
3399 if (!order || order[0].level == 0) {
3400 var firstNonWS = Math.max(0, line.text.search(/\S/));
3401 var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3402 cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3403 } else cm.extendSelection(start);
3404 },
4753 },
3405 goLineEnd: function(cm) {
4754 goLineEnd: function(cm) {
3406 cm.extendSelection(lineEnd(cm, cm.getCursor().line));
4755 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
4756 {origin: "+move", bias: -1});
3407 },
4757 },
3408 goLineRight: function(cm) {
4758 goLineRight: function(cm) {
3409 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
4759 cm.extendSelectionsBy(function(range) {
3410 cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
4760 var top = cm.charCoords(range.head, "div").top + 5;
4761 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
4762 }, sel_move);
3411 },
4763 },
3412 goLineLeft: function(cm) {
4764 goLineLeft: function(cm) {
3413 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
4765 cm.extendSelectionsBy(function(range) {
3414 cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
4766 var top = cm.charCoords(range.head, "div").top + 5;
4767 return cm.coordsChar({left: 0, top: top}, "div");
4768 }, sel_move);
4769 },
4770 goLineLeftSmart: function(cm) {
4771 cm.extendSelectionsBy(function(range) {
4772 var top = cm.charCoords(range.head, "div").top + 5;
4773 var pos = cm.coordsChar({left: 0, top: top}, "div");
4774 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
4775 return pos;
4776 }, sel_move);
3415 },
4777 },
3416 goLineUp: function(cm) {cm.moveV(-1, "line");},
4778 goLineUp: function(cm) {cm.moveV(-1, "line");},
3417 goLineDown: function(cm) {cm.moveV(1, "line");},
4779 goLineDown: function(cm) {cm.moveV(1, "line");},
@@ -3434,22 +4796,53 b' window.CodeMirror = (function() {'
3434 indentAuto: function(cm) {cm.indentSelection("smart");},
4796 indentAuto: function(cm) {cm.indentSelection("smart");},
3435 indentMore: function(cm) {cm.indentSelection("add");},
4797 indentMore: function(cm) {cm.indentSelection("add");},
3436 indentLess: function(cm) {cm.indentSelection("subtract");},
4798 indentLess: function(cm) {cm.indentSelection("subtract");},
3437 insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
4799 insertTab: function(cm) {cm.replaceSelection("\t");},
4800 insertSoftTab: function(cm) {
4801 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
4802 for (var i = 0; i < ranges.length; i++) {
4803 var pos = ranges[i].from();
4804 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
4805 spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
4806 }
4807 cm.replaceSelections(spaces);
4808 },
3438 defaultTab: function(cm) {
4809 defaultTab: function(cm) {
3439 if (cm.somethingSelected()) cm.indentSelection("add");
4810 if (cm.somethingSelected()) cm.indentSelection("add");
3440 else cm.replaceSelection("\t", "end", "+input");
4811 else cm.execCommand("insertTab");
3441 },
4812 },
3442 transposeChars: function(cm) {
4813 transposeChars: function(cm) {
3443 var cur = cm.getCursor(), line = cm.getLine(cur.line);
4814 runInOp(cm, function() {
3444 if (cur.ch > 0 && cur.ch < line.length - 1)
4815 var ranges = cm.listSelections(), newSel = [];
3445 cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
4816 for (var i = 0; i < ranges.length; i++) {
3446 Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
4817 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
4818 if (line) {
4819 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
4820 if (cur.ch > 0) {
4821 cur = new Pos(cur.line, cur.ch + 1);
4822 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
4823 Pos(cur.line, cur.ch - 2), cur, "+transpose");
4824 } else if (cur.line > cm.doc.first) {
4825 var prev = getLine(cm.doc, cur.line - 1).text;
4826 if (prev)
4827 cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
4828 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
4829 }
4830 }
4831 newSel.push(new Range(cur, cur));
4832 }
4833 cm.setSelections(newSel);
4834 });
3447 },
4835 },
3448 newlineAndIndent: function(cm) {
4836 newlineAndIndent: function(cm) {
3449 operation(cm, function() {
4837 runInOp(cm, function() {
3450 cm.replaceSelection("\n", "end", "+input");
4838 var len = cm.listSelections().length;
3451 cm.indentLine(cm.getCursor().line, null, true);
4839 for (var i = 0; i < len; i++) {
3452 })();
4840 var range = cm.listSelections()[i];
4841 cm.replaceRange("\n", range.anchor, range.head, "+input");
4842 cm.indentLine(range.from().line + 1, null, true);
4843 ensureCursorVisible(cm);
4844 }
4845 });
3453 },
4846 },
3454 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
4847 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3455 };
4848 };
@@ -3460,36 +4853,42 b' window.CodeMirror = (function() {'
3460 keyMap.basic = {
4853 keyMap.basic = {
3461 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
4854 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3462 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
4855 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3463 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
4856 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
3464 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
4857 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
4858 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
4859 "Esc": "singleSelection"
3465 };
4860 };
3466 // Note that the save and find-related commands aren't defined by
4861 // Note that the save and find-related commands aren't defined by
3467 // default. Unknown commands are simply ignored.
4862 // default. User code or addons can define them. Unknown commands
4863 // are simply ignored.
3468 keyMap.pcDefault = {
4864 keyMap.pcDefault = {
3469 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
4865 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3470 "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
4866 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
3471 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
4867 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3472 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
4868 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3473 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
4869 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3474 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
4870 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
4871 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
3475 fallthrough: "basic"
4872 fallthrough: "basic"
3476 };
4873 };
3477 keyMap.macDefault = {
4874 keyMap.macDefault = {
3478 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
4875 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3479 "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
4876 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3480 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
4877 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
3481 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
4878 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3482 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
4879 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3483 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
4880 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
4881 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
3484 fallthrough: ["basic", "emacsy"]
4882 fallthrough: ["basic", "emacsy"]
3485 };
4883 };
3486 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
4884 // Very basic readline/emacs-style bindings, which are standard on Mac.
3487 keyMap.emacsy = {
4885 keyMap.emacsy = {
3488 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
4886 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3489 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
4887 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3490 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
4888 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3491 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
4889 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3492 };
4890 };
4891 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3493
4892
3494 // KEYMAP DISPATCH
4893 // KEYMAP DISPATCH
3495
4894
@@ -3498,7 +4897,11 b' window.CodeMirror = (function() {'
3498 else return val;
4897 else return val;
3499 }
4898 }
3500
4899
3501 function lookupKey(name, maps, handle) {
4900 // Given an array of keymaps and a key name, call handle on any
4901 // bindings found, until that returns a truthy value, at which point
4902 // we consider the key handled. Implements things like binding a key
4903 // to false stopping further handling and keymap fallthrough.
4904 var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
3502 function lookup(map) {
4905 function lookup(map) {
3503 map = getKeyMap(map);
4906 map = getKeyMap(map);
3504 var found = map[name];
4907 var found = map[name];
@@ -3510,7 +4913,7 b' window.CodeMirror = (function() {'
3510 if (fallthrough == null) return false;
4913 if (fallthrough == null) return false;
3511 if (Object.prototype.toString.call(fallthrough) != "[object Array]")
4914 if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3512 return lookup(fallthrough);
4915 return lookup(fallthrough);
3513 for (var i = 0, e = fallthrough.length; i < e; ++i) {
4916 for (var i = 0; i < fallthrough.length; ++i) {
3514 var done = lookup(fallthrough[i]);
4917 var done = lookup(fallthrough[i]);
3515 if (done) return done;
4918 if (done) return done;
3516 }
4919 }
@@ -3521,13 +4924,18 b' window.CodeMirror = (function() {'
3521 var done = lookup(maps[i]);
4924 var done = lookup(maps[i]);
3522 if (done) return done != "stop";
4925 if (done) return done != "stop";
3523 }
4926 }
3524 }
4927 };
3525 function isModifierKey(event) {
4928
4929 // Modifier key presses don't count as 'real' key presses for the
4930 // purpose of keymap fallthrough.
4931 var isModifierKey = CodeMirror.isModifierKey = function(event) {
3526 var name = keyNames[event.keyCode];
4932 var name = keyNames[event.keyCode];
3527 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
4933 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3528 }
4934 };
3529 function keyName(event, noShift) {
4935
3530 if (opera && event.keyCode == 34 && event["char"]) return false;
4936 // Look up the name of a key as indicated by an event object.
4937 var keyName = CodeMirror.keyName = function(event, noShift) {
4938 if (presto && event.keyCode == 34 && event["char"]) return false;
3531 var name = keyNames[event.keyCode];
4939 var name = keyNames[event.keyCode];
3532 if (name == null || event.altGraphKey) return false;
4940 if (name == null || event.altGraphKey) return false;
3533 if (event.altKey) name = "Alt-" + name;
4941 if (event.altKey) name = "Alt-" + name;
@@ -3535,10 +4943,7 b' window.CodeMirror = (function() {'
3535 if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
4943 if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3536 if (!noShift && event.shiftKey) name = "Shift-" + name;
4944 if (!noShift && event.shiftKey) name = "Shift-" + name;
3537 return name;
4945 return name;
3538 }
4946 };
3539 CodeMirror.lookupKey = lookupKey;
3540 CodeMirror.isModifierKey = isModifierKey;
3541 CodeMirror.keyName = keyName;
3542
4947
3543 // FROMTEXTAREA
4948 // FROMTEXTAREA
3544
4949
@@ -3552,9 +4957,7 b' window.CodeMirror = (function() {'
3552 // Set autofocus to true if this textarea is focused, or if it has
4957 // Set autofocus to true if this textarea is focused, or if it has
3553 // autofocus and no other element is focused.
4958 // autofocus and no other element is focused.
3554 if (options.autofocus == null) {
4959 if (options.autofocus == null) {
3555 var hasFocus = document.body;
4960 var hasFocus = activeElt();
3556 // doc.activeElement occasionally throws on IE
3557 try { hasFocus = document.activeElement; } catch(e) {}
3558 options.autofocus = hasFocus == textarea ||
4961 options.autofocus = hasFocus == textarea ||
3559 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
4962 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3560 }
4963 }
@@ -3583,6 +4986,7 b' window.CodeMirror = (function() {'
3583 cm.save = save;
4986 cm.save = save;
3584 cm.getTextArea = function() { return textarea; };
4987 cm.getTextArea = function() { return textarea; };
3585 cm.toTextArea = function() {
4988 cm.toTextArea = function() {
4989 cm.toTextArea = isNaN; // Prevent this from being ran twice
3586 save();
4990 save();
3587 textarea.parentNode.removeChild(cm.getWrapperElement());
4991 textarea.parentNode.removeChild(cm.getWrapperElement());
3588 textarea.style.display = "";
4992 textarea.style.display = "";
@@ -3600,17 +5004,17 b' window.CodeMirror = (function() {'
3600 // Fed to the mode parsers, provides helper functions to make
5004 // Fed to the mode parsers, provides helper functions to make
3601 // parsers more succinct.
5005 // parsers more succinct.
3602
5006
3603 // The character stream used by a mode's parser.
5007 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
3604 function StringStream(string, tabSize) {
3605 this.pos = this.start = 0;
5008 this.pos = this.start = 0;
3606 this.string = string;
5009 this.string = string;
3607 this.tabSize = tabSize || 8;
5010 this.tabSize = tabSize || 8;
3608 this.lastColumnPos = this.lastColumnValue = 0;
5011 this.lastColumnPos = this.lastColumnValue = 0;
3609 }
5012 this.lineStart = 0;
5013 };
3610
5014
3611 StringStream.prototype = {
5015 StringStream.prototype = {
3612 eol: function() {return this.pos >= this.string.length;},
5016 eol: function() {return this.pos >= this.string.length;},
3613 sol: function() {return this.pos == 0;},
5017 sol: function() {return this.pos == this.lineStart;},
3614 peek: function() {return this.string.charAt(this.pos) || undefined;},
5018 peek: function() {return this.string.charAt(this.pos) || undefined;},
3615 next: function() {
5019 next: function() {
3616 if (this.pos < this.string.length)
5020 if (this.pos < this.string.length)
@@ -3643,9 +5047,12 b' window.CodeMirror = (function() {'
3643 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
5047 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3644 this.lastColumnPos = this.start;
5048 this.lastColumnPos = this.start;
3645 }
5049 }
3646 return this.lastColumnValue;
5050 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
3647 },
5051 },
3648 indentation: function() {return countColumn(this.string, null, this.tabSize);},
5052 indentation: function() {
5053 return countColumn(this.string, null, this.tabSize) -
5054 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5055 },
3649 match: function(pattern, consume, caseInsensitive) {
5056 match: function(pattern, consume, caseInsensitive) {
3650 if (typeof pattern == "string") {
5057 if (typeof pattern == "string") {
3651 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
5058 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
@@ -3661,20 +5068,34 b' window.CodeMirror = (function() {'
3661 return match;
5068 return match;
3662 }
5069 }
3663 },
5070 },
3664 current: function(){return this.string.slice(this.start, this.pos);}
5071 current: function(){return this.string.slice(this.start, this.pos);},
5072 hideFirstChars: function(n, inner) {
5073 this.lineStart += n;
5074 try { return inner(); }
5075 finally { this.lineStart -= n; }
5076 }
3665 };
5077 };
3666 CodeMirror.StringStream = StringStream;
3667
5078
3668 // TEXTMARKERS
5079 // TEXTMARKERS
3669
5080
3670 function TextMarker(doc, type) {
5081 // Created with markText and setBookmark methods. A TextMarker is a
5082 // handle that can be used to clear or find a marked position in the
5083 // document. Line objects hold arrays (markedSpans) containing
5084 // {from, to, marker} object pointing to such marker objects, and
5085 // indicating that such a marker is present on that line. Multiple
5086 // lines may point to the same marker when it spans across lines.
5087 // The spans will have null for their from/to properties when the
5088 // marker continues beyond the start/end of the line. Markers have
5089 // links back to the lines they currently touch.
5090
5091 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
3671 this.lines = [];
5092 this.lines = [];
3672 this.type = type;
5093 this.type = type;
3673 this.doc = doc;
5094 this.doc = doc;
3674 }
5095 };
3675 CodeMirror.TextMarker = TextMarker;
3676 eventMixin(TextMarker);
5096 eventMixin(TextMarker);
3677
5097
5098 // Clear the marker.
3678 TextMarker.prototype.clear = function() {
5099 TextMarker.prototype.clear = function() {
3679 if (this.explicitlyCleared) return;
5100 if (this.explicitlyCleared) return;
3680 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5101 var cm = this.doc.cm, withOp = cm && !cm.curOp;
@@ -3687,15 +5108,17 b' window.CodeMirror = (function() {'
3687 for (var i = 0; i < this.lines.length; ++i) {
5108 for (var i = 0; i < this.lines.length; ++i) {
3688 var line = this.lines[i];
5109 var line = this.lines[i];
3689 var span = getMarkedSpanFor(line.markedSpans, this);
5110 var span = getMarkedSpanFor(line.markedSpans, this);
3690 if (span.to != null) max = lineNo(line);
5111 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
5112 else if (cm) {
5113 if (span.to != null) max = lineNo(line);
5114 if (span.from != null) min = lineNo(line);
5115 }
3691 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5116 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3692 if (span.from != null)
5117 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
3693 min = lineNo(line);
3694 else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3695 updateLineHeight(line, textHeight(cm.display));
5118 updateLineHeight(line, textHeight(cm.display));
3696 }
5119 }
3697 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
5120 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3698 var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
5121 var visual = visualLine(this.lines[i]), len = lineLength(visual);
3699 if (len > cm.display.maxLineLength) {
5122 if (len > cm.display.maxLineLength) {
3700 cm.display.maxLine = visual;
5123 cm.display.maxLine = visual;
3701 cm.display.maxLineLength = len;
5124 cm.display.maxLineLength = len;
@@ -3703,45 +5126,62 b' window.CodeMirror = (function() {'
3703 }
5126 }
3704 }
5127 }
3705
5128
3706 if (min != null && cm) regChange(cm, min, max + 1);
5129 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
3707 this.lines.length = 0;
5130 this.lines.length = 0;
3708 this.explicitlyCleared = true;
5131 this.explicitlyCleared = true;
3709 if (this.atomic && this.doc.cantEdit) {
5132 if (this.atomic && this.doc.cantEdit) {
3710 this.doc.cantEdit = false;
5133 this.doc.cantEdit = false;
3711 if (cm) reCheckSelection(cm);
5134 if (cm) reCheckSelection(cm.doc);
3712 }
5135 }
5136 if (cm) signalLater(cm, "markerCleared", cm, this);
3713 if (withOp) endOperation(cm);
5137 if (withOp) endOperation(cm);
5138 if (this.parent) this.parent.clear();
3714 };
5139 };
3715
5140
3716 TextMarker.prototype.find = function() {
5141 // Find the position of the marker in the document. Returns a {from,
5142 // to} object by default. Side can be passed to get a specific side
5143 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5144 // Pos objects returned contain a line object, rather than a line
5145 // number (used to prevent looking up the same line twice).
5146 TextMarker.prototype.find = function(side, lineObj) {
5147 if (side == null && this.type == "bookmark") side = 1;
3717 var from, to;
5148 var from, to;
3718 for (var i = 0; i < this.lines.length; ++i) {
5149 for (var i = 0; i < this.lines.length; ++i) {
3719 var line = this.lines[i];
5150 var line = this.lines[i];
3720 var span = getMarkedSpanFor(line.markedSpans, this);
5151 var span = getMarkedSpanFor(line.markedSpans, this);
3721 if (span.from != null || span.to != null) {
5152 if (span.from != null) {
3722 var found = lineNo(line);
5153 from = Pos(lineObj ? line : lineNo(line), span.from);
3723 if (span.from != null) from = Pos(found, span.from);
5154 if (side == -1) return from;
3724 if (span.to != null) to = Pos(found, span.to);
5155 }
3725 }
5156 if (span.to != null) {
3726 }
5157 to = Pos(lineObj ? line : lineNo(line), span.to);
3727 if (this.type == "bookmark") return from;
5158 if (side == 1) return to;
5159 }
5160 }
3728 return from && {from: from, to: to};
5161 return from && {from: from, to: to};
3729 };
5162 };
3730
5163
5164 // Signals that the marker's widget changed, and surrounding layout
5165 // should be recomputed.
3731 TextMarker.prototype.changed = function() {
5166 TextMarker.prototype.changed = function() {
3732 var pos = this.find(), cm = this.doc.cm;
5167 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
3733 if (!pos || !cm) return;
5168 if (!pos || !cm) return;
3734 var line = getLine(this.doc, pos.from.line);
5169 runInOp(cm, function() {
3735 clearCachedMeasurement(cm, line);
5170 var line = pos.line, lineN = lineNo(pos.line);
3736 if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
5171 var view = findViewForLine(cm, lineN);
3737 for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
5172 if (view) {
3738 if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
5173 clearLineMeasurementCacheFor(view);
3739 break;
5174 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
3740 }
5175 }
3741 runInOp(cm, function() {
5176 cm.curOp.updateMaxLine = true;
3742 cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
5177 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
3743 });
5178 var oldHeight = widget.height;
3744 }
5179 widget.height = null;
5180 var dHeight = widgetHeight(widget) - oldHeight;
5181 if (dHeight)
5182 updateLineHeight(line, line.height + dHeight);
5183 }
5184 });
3745 };
5185 };
3746
5186
3747 TextMarker.prototype.attachLine = function(line) {
5187 TextMarker.prototype.attachLine = function(line) {
@@ -3760,40 +5200,53 b' window.CodeMirror = (function() {'
3760 }
5200 }
3761 };
5201 };
3762
5202
5203 // Collapsed markers have unique ids, in order to be able to order
5204 // them, which is needed for uniquely determining an outer marker
5205 // when they overlap (they may nest, but not partially overlap).
5206 var nextMarkerId = 0;
5207
5208 // Create a marker, wire it up to the right lines, and
3763 function markText(doc, from, to, options, type) {
5209 function markText(doc, from, to, options, type) {
5210 // Shared markers (across linked documents) are handled separately
5211 // (markTextShared will call out to this again, once per
5212 // document).
3764 if (options && options.shared) return markTextShared(doc, from, to, options, type);
5213 if (options && options.shared) return markTextShared(doc, from, to, options, type);
5214 // Ensure we are in an operation.
3765 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
5215 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3766
5216
3767 var marker = new TextMarker(doc, type);
5217 var marker = new TextMarker(doc, type), diff = cmp(from, to);
3768 if (type == "range" && !posLess(from, to)) return marker;
5218 if (options) copyObj(options, marker, false);
3769 if (options) copyObj(options, marker);
5219 // Don't connect empty markers unless clearWhenEmpty is false
5220 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5221 return marker;
3770 if (marker.replacedWith) {
5222 if (marker.replacedWith) {
5223 // Showing up as a widget implies collapsed (widget replaces text)
3771 marker.collapsed = true;
5224 marker.collapsed = true;
3772 marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
5225 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
3773 if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
5226 if (!options.handleMouseEvents) marker.widgetNode.ignoreEvents = true;
3774 }
5227 if (options.insertLeft) marker.widgetNode.insertLeft = true;
3775 if (marker.collapsed) sawCollapsedSpans = true;
5228 }
5229 if (marker.collapsed) {
5230 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5231 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5232 throw new Error("Inserting collapsed marker partially overlapping an existing one");
5233 sawCollapsedSpans = true;
5234 }
3776
5235
3777 if (marker.addToHistory)
5236 if (marker.addToHistory)
3778 addToHistory(doc, {from: from, to: to, origin: "markText"},
5237 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
3779 {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
5238
3780
5239 var curLine = from.line, cm = doc.cm, updateMaxLine;
3781 var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3782 doc.iter(curLine, to.line + 1, function(line) {
5240 doc.iter(curLine, to.line + 1, function(line) {
3783 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
5241 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
3784 updateMaxLine = true;
5242 updateMaxLine = true;
3785 var span = {from: null, to: null, marker: marker};
5243 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
3786 size += line.text.length;
5244 addMarkedSpan(line, new MarkedSpan(marker,
3787 if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
5245 curLine == from.line ? from.ch : null,
3788 if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
5246 curLine == to.line ? to.ch : null));
3789 if (marker.collapsed) {
3790 if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3791 if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3792 else updateLineHeight(line, 0);
3793 }
3794 addMarkedSpan(line, span);
3795 ++curLine;
5247 ++curLine;
3796 });
5248 });
5249 // lineIsHidden depends on the presence of the spans, so needs a second pass
3797 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
5250 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3798 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
5251 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3799 });
5252 });
@@ -3806,31 +5259,33 b' window.CodeMirror = (function() {'
3806 doc.clearHistory();
5259 doc.clearHistory();
3807 }
5260 }
3808 if (marker.collapsed) {
5261 if (marker.collapsed) {
3809 if (collapsedAtStart != collapsedAtEnd)
5262 marker.id = ++nextMarkerId;
3810 throw new Error("Inserting collapsed marker overlapping an existing one");
3811 marker.size = size;
3812 marker.atomic = true;
5263 marker.atomic = true;
3813 }
5264 }
3814 if (cm) {
5265 if (cm) {
5266 // Sync editor state
3815 if (updateMaxLine) cm.curOp.updateMaxLine = true;
5267 if (updateMaxLine) cm.curOp.updateMaxLine = true;
3816 if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
5268 if (marker.collapsed)
3817 regChange(cm, from.line, to.line + 1);
5269 regChange(cm, from.line, to.line + 1);
3818 if (marker.atomic) reCheckSelection(cm);
5270 else if (marker.className || marker.title || marker.startStyle || marker.endStyle)
5271 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
5272 if (marker.atomic) reCheckSelection(cm.doc);
5273 signalLater(cm, "markerAdded", cm, marker);
3819 }
5274 }
3820 return marker;
5275 return marker;
3821 }
5276 }
3822
5277
3823 // SHARED TEXTMARKERS
5278 // SHARED TEXTMARKERS
3824
5279
3825 function SharedTextMarker(markers, primary) {
5280 // A shared marker spans multiple linked documents. It is
5281 // implemented as a meta-marker-object controlling multiple normal
5282 // markers.
5283 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
3826 this.markers = markers;
5284 this.markers = markers;
3827 this.primary = primary;
5285 this.primary = primary;
3828 for (var i = 0, me = this; i < markers.length; ++i) {
5286 for (var i = 0; i < markers.length; ++i)
3829 markers[i].parent = this;
5287 markers[i].parent = this;
3830 on(markers[i], "clear", function(){me.clear();});
5288 };
3831 }
3832 }
3833 CodeMirror.SharedTextMarker = SharedTextMarker;
3834 eventMixin(SharedTextMarker);
5289 eventMixin(SharedTextMarker);
3835
5290
3836 SharedTextMarker.prototype.clear = function() {
5291 SharedTextMarker.prototype.clear = function() {
@@ -3840,17 +5295,17 b' window.CodeMirror = (function() {'
3840 this.markers[i].clear();
5295 this.markers[i].clear();
3841 signalLater(this, "clear");
5296 signalLater(this, "clear");
3842 };
5297 };
3843 SharedTextMarker.prototype.find = function() {
5298 SharedTextMarker.prototype.find = function(side, lineObj) {
3844 return this.primary.find();
5299 return this.primary.find(side, lineObj);
3845 };
5300 };
3846
5301
3847 function markTextShared(doc, from, to, options, type) {
5302 function markTextShared(doc, from, to, options, type) {
3848 options = copyObj(options);
5303 options = copyObj(options);
3849 options.shared = false;
5304 options.shared = false;
3850 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
5305 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3851 var widget = options.replacedWith;
5306 var widget = options.widgetNode;
3852 linkedDocs(doc, function(doc) {
5307 linkedDocs(doc, function(doc) {
3853 if (widget) options.replacedWith = widget.cloneNode(true);
5308 if (widget) options.widgetNode = widget.cloneNode(true);
3854 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
5309 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3855 for (var i = 0; i < doc.linked.length; ++i)
5310 for (var i = 0; i < doc.linked.length; ++i)
3856 if (doc.linked[i].isParent) return;
5311 if (doc.linked[i].isParent) return;
@@ -3859,58 +5314,104 b' window.CodeMirror = (function() {'
3859 return new SharedTextMarker(markers, primary);
5314 return new SharedTextMarker(markers, primary);
3860 }
5315 }
3861
5316
5317 function findSharedMarkers(doc) {
5318 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
5319 function(m) { return m.parent; });
5320 }
5321
5322 function copySharedMarkers(doc, markers) {
5323 for (var i = 0; i < markers.length; i++) {
5324 var marker = markers[i], pos = marker.find();
5325 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
5326 if (cmp(mFrom, mTo)) {
5327 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
5328 marker.markers.push(subMark);
5329 subMark.parent = marker;
5330 }
5331 }
5332 }
5333
5334 function detachSharedMarkers(markers) {
5335 for (var i = 0; i < markers.length; i++) {
5336 var marker = markers[i], linked = [marker.primary.doc];;
5337 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
5338 for (var j = 0; j < marker.markers.length; j++) {
5339 var subMarker = marker.markers[j];
5340 if (indexOf(linked, subMarker.doc) == -1) {
5341 subMarker.parent = null;
5342 marker.markers.splice(j--, 1);
5343 }
5344 }
5345 }
5346 }
5347
3862 // TEXTMARKER SPANS
5348 // TEXTMARKER SPANS
3863
5349
5350 function MarkedSpan(marker, from, to) {
5351 this.marker = marker;
5352 this.from = from; this.to = to;
5353 }
5354
5355 // Search an array of spans for a span matching the given marker.
3864 function getMarkedSpanFor(spans, marker) {
5356 function getMarkedSpanFor(spans, marker) {
3865 if (spans) for (var i = 0; i < spans.length; ++i) {
5357 if (spans) for (var i = 0; i < spans.length; ++i) {
3866 var span = spans[i];
5358 var span = spans[i];
3867 if (span.marker == marker) return span;
5359 if (span.marker == marker) return span;
3868 }
5360 }
3869 }
5361 }
5362 // Remove a span from an array, returning undefined if no spans are
5363 // left (we don't store arrays for lines without spans).
3870 function removeMarkedSpan(spans, span) {
5364 function removeMarkedSpan(spans, span) {
3871 for (var r, i = 0; i < spans.length; ++i)
5365 for (var r, i = 0; i < spans.length; ++i)
3872 if (spans[i] != span) (r || (r = [])).push(spans[i]);
5366 if (spans[i] != span) (r || (r = [])).push(spans[i]);
3873 return r;
5367 return r;
3874 }
5368 }
5369 // Add a span to a line.
3875 function addMarkedSpan(line, span) {
5370 function addMarkedSpan(line, span) {
3876 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
5371 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3877 span.marker.attachLine(line);
5372 span.marker.attachLine(line);
3878 }
5373 }
3879
5374
5375 // Used for the algorithm that adjusts markers for a change in the
5376 // document. These functions cut an array of spans at a given
5377 // character position, returning an array of remaining chunks (or
5378 // undefined if nothing remains).
3880 function markedSpansBefore(old, startCh, isInsert) {
5379 function markedSpansBefore(old, startCh, isInsert) {
3881 if (old) for (var i = 0, nw; i < old.length; ++i) {
5380 if (old) for (var i = 0, nw; i < old.length; ++i) {
3882 var span = old[i], marker = span.marker;
5381 var span = old[i], marker = span.marker;
3883 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
5382 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3884 if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
5383 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
3885 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
5384 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3886 (nw || (nw = [])).push({from: span.from,
5385 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
3887 to: endsAfter ? null : span.to,
3888 marker: marker});
3889 }
5386 }
3890 }
5387 }
3891 return nw;
5388 return nw;
3892 }
5389 }
3893
3894 function markedSpansAfter(old, endCh, isInsert) {
5390 function markedSpansAfter(old, endCh, isInsert) {
3895 if (old) for (var i = 0, nw; i < old.length; ++i) {
5391 if (old) for (var i = 0, nw; i < old.length; ++i) {
3896 var span = old[i], marker = span.marker;
5392 var span = old[i], marker = span.marker;
3897 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
5393 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3898 if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
5394 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
3899 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
5395 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3900 (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
5396 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
3901 to: span.to == null ? null : span.to - endCh,
5397 span.to == null ? null : span.to - endCh));
3902 marker: marker});
3903 }
5398 }
3904 }
5399 }
3905 return nw;
5400 return nw;
3906 }
5401 }
3907
5402
5403 // Given a change object, compute the new set of marker spans that
5404 // cover the line in which the change took place. Removes spans
5405 // entirely within the change, reconnects spans belonging to the
5406 // same marker that appear on both sides of the change, and cuts off
5407 // spans partially within the change. Returns an array of span
5408 // arrays with one element for each line in (after) the change.
3908 function stretchSpansOverChange(doc, change) {
5409 function stretchSpansOverChange(doc, change) {
3909 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
5410 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3910 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
5411 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3911 if (!oldFirst && !oldLast) return null;
5412 if (!oldFirst && !oldLast) return null;
3912
5413
3913 var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
5414 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
3914 // Get the spans that 'stick out' on both sides
5415 // Get the spans that 'stick out' on both sides
3915 var first = markedSpansBefore(oldFirst, startCh, isInsert);
5416 var first = markedSpansBefore(oldFirst, startCh, isInsert);
3916 var last = markedSpansAfter(oldLast, endCh, isInsert);
5417 var last = markedSpansAfter(oldLast, endCh, isInsert);
@@ -3945,13 +5446,9 b' window.CodeMirror = (function() {'
3945 }
5446 }
3946 }
5447 }
3947 }
5448 }
3948 if (sameLine && first) {
5449 // Make sure we didn't create any zero-length spans
3949 // Make sure we didn't create any zero-length spans
5450 if (first) first = clearEmptySpans(first);
3950 for (var i = 0; i < first.length; ++i)
5451 if (last && last != first) last = clearEmptySpans(last);
3951 if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
3952 first.splice(i--, 1);
3953 if (!first.length) first = null;
3954 }
3955
5452
3956 var newMarkers = [first];
5453 var newMarkers = [first];
3957 if (!sameLine) {
5454 if (!sameLine) {
@@ -3960,7 +5457,7 b' window.CodeMirror = (function() {'
3960 if (gap > 0 && first)
5457 if (gap > 0 && first)
3961 for (var i = 0; i < first.length; ++i)
5458 for (var i = 0; i < first.length; ++i)
3962 if (first[i].to == null)
5459 if (first[i].to == null)
3963 (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
5460 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
3964 for (var i = 0; i < gap; ++i)
5461 for (var i = 0; i < gap; ++i)
3965 newMarkers.push(gapMarkers);
5462 newMarkers.push(gapMarkers);
3966 newMarkers.push(last);
5463 newMarkers.push(last);
@@ -3968,6 +5465,22 b' window.CodeMirror = (function() {'
3968 return newMarkers;
5465 return newMarkers;
3969 }
5466 }
3970
5467
5468 // Remove spans that are empty and don't have a clearWhenEmpty
5469 // option of false.
5470 function clearEmptySpans(spans) {
5471 for (var i = 0; i < spans.length; ++i) {
5472 var span = spans[i];
5473 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
5474 spans.splice(i--, 1);
5475 }
5476 if (!spans.length) return null;
5477 return spans;
5478 }
5479
5480 // Used for un/re-doing changes from the history. Combines the
5481 // result of computing the existing spans with the set of spans that
5482 // existed in the history (so that deleting around a span and then
5483 // undoing brings back the span).
3971 function mergeOldSpans(doc, change) {
5484 function mergeOldSpans(doc, change) {
3972 var old = getOldSpans(doc, change);
5485 var old = getOldSpans(doc, change);
3973 var stretched = stretchSpansOverChange(doc, change);
5486 var stretched = stretchSpansOverChange(doc, change);
@@ -3990,6 +5503,7 b' window.CodeMirror = (function() {'
3990 return old;
5503 return old;
3991 }
5504 }
3992
5505
5506 // Used to 'clip' out readOnly ranges when making a change.
3993 function removeReadOnlyRanges(doc, from, to) {
5507 function removeReadOnlyRanges(doc, from, to) {
3994 var markers = null;
5508 var markers = null;
3995 doc.iter(from.line, to.line + 1, function(line) {
5509 doc.iter(from.line, to.line + 1, function(line) {
@@ -4002,14 +5516,14 b' window.CodeMirror = (function() {'
4002 if (!markers) return null;
5516 if (!markers) return null;
4003 var parts = [{from: from, to: to}];
5517 var parts = [{from: from, to: to}];
4004 for (var i = 0; i < markers.length; ++i) {
5518 for (var i = 0; i < markers.length; ++i) {
4005 var mk = markers[i], m = mk.find();
5519 var mk = markers[i], m = mk.find(0);
4006 for (var j = 0; j < parts.length; ++j) {
5520 for (var j = 0; j < parts.length; ++j) {
4007 var p = parts[j];
5521 var p = parts[j];
4008 if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
5522 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
4009 var newParts = [j, 1];
5523 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
4010 if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
5524 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
4011 newParts.push({from: p.from, to: m.from});
5525 newParts.push({from: p.from, to: m.from});
4012 if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
5526 if (dto > 0 || !mk.inclusiveRight && !dto)
4013 newParts.push({from: m.to, to: p.to});
5527 newParts.push({from: m.to, to: p.to});
4014 parts.splice.apply(parts, newParts);
5528 parts.splice.apply(parts, newParts);
4015 j += newParts.length - 1;
5529 j += newParts.length - 1;
@@ -4018,71 +5532,148 b' window.CodeMirror = (function() {'
4018 return parts;
5532 return parts;
4019 }
5533 }
4020
5534
4021 function collapsedSpanAt(line, ch) {
5535 // Connect or disconnect spans from a line.
5536 function detachMarkedSpans(line) {
5537 var spans = line.markedSpans;
5538 if (!spans) return;
5539 for (var i = 0; i < spans.length; ++i)
5540 spans[i].marker.detachLine(line);
5541 line.markedSpans = null;
5542 }
5543 function attachMarkedSpans(line, spans) {
5544 if (!spans) return;
5545 for (var i = 0; i < spans.length; ++i)
5546 spans[i].marker.attachLine(line);
5547 line.markedSpans = spans;
5548 }
5549
5550 // Helpers used when computing which overlapping collapsed span
5551 // counts as the larger one.
5552 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
5553 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
5554
5555 // Returns a number indicating which of two overlapping collapsed
5556 // spans is larger (and thus includes the other). Falls back to
5557 // comparing ids when the spans cover exactly the same range.
5558 function compareCollapsedMarkers(a, b) {
5559 var lenDiff = a.lines.length - b.lines.length;
5560 if (lenDiff != 0) return lenDiff;
5561 var aPos = a.find(), bPos = b.find();
5562 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
5563 if (fromCmp) return -fromCmp;
5564 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
5565 if (toCmp) return toCmp;
5566 return b.id - a.id;
5567 }
5568
5569 // Find out whether a line ends or starts in a collapsed span. If
5570 // so, return the marker for that span.
5571 function collapsedSpanAtSide(line, start) {
4022 var sps = sawCollapsedSpans && line.markedSpans, found;
5572 var sps = sawCollapsedSpans && line.markedSpans, found;
4023 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
5573 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
4024 sp = sps[i];
5574 sp = sps[i];
4025 if (!sp.marker.collapsed) continue;
5575 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
4026 if ((sp.from == null || sp.from < ch) &&
5576 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
4027 (sp.to == null || sp.to > ch) &&
4028 (!found || found.width < sp.marker.width))
4029 found = sp.marker;
5577 found = sp.marker;
4030 }
5578 }
4031 return found;
5579 return found;
4032 }
5580 }
4033 function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
5581 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
4034 function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
5582 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
4035
5583
4036 function visualLine(doc, line) {
5584 // Test whether there exists a collapsed span that partially
5585 // overlaps (covers the start or end, but not both) of a new span.
5586 // Such overlap is not allowed.
5587 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
5588 var line = getLine(doc, lineNo);
5589 var sps = sawCollapsedSpans && line.markedSpans;
5590 if (sps) for (var i = 0; i < sps.length; ++i) {
5591 var sp = sps[i];
5592 if (!sp.marker.collapsed) continue;
5593 var found = sp.marker.find(0);
5594 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
5595 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
5596 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
5597 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
5598 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
5599 return true;
5600 }
5601 }
5602
5603 // A visual line is a line as drawn on the screen. Folding, for
5604 // example, can cause multiple logical lines to appear on the same
5605 // visual line. This finds the start of the visual line that the
5606 // given line is part of (usually that is the line itself).
5607 function visualLine(line) {
4037 var merged;
5608 var merged;
4038 while (merged = collapsedSpanAtStart(line))
5609 while (merged = collapsedSpanAtStart(line))
4039 line = getLine(doc, merged.find().from.line);
5610 line = merged.find(-1, true).line;
4040 return line;
5611 return line;
4041 }
5612 }
4042
5613
5614 // Returns an array of logical lines that continue the visual line
5615 // started by the argument, or undefined if there are no such lines.
5616 function visualLineContinued(line) {
5617 var merged, lines;
5618 while (merged = collapsedSpanAtEnd(line)) {
5619 line = merged.find(1, true).line;
5620 (lines || (lines = [])).push(line);
5621 }
5622 return lines;
5623 }
5624
5625 // Get the line number of the start of the visual line that the
5626 // given line number is part of.
5627 function visualLineNo(doc, lineN) {
5628 var line = getLine(doc, lineN), vis = visualLine(line);
5629 if (line == vis) return lineN;
5630 return lineNo(vis);
5631 }
5632 // Get the line number of the start of the next visual line after
5633 // the given line.
5634 function visualLineEndNo(doc, lineN) {
5635 if (lineN > doc.lastLine()) return lineN;
5636 var line = getLine(doc, lineN), merged;
5637 if (!lineIsHidden(doc, line)) return lineN;
5638 while (merged = collapsedSpanAtEnd(line))
5639 line = merged.find(1, true).line;
5640 return lineNo(line) + 1;
5641 }
5642
5643 // Compute whether a line is hidden. Lines count as hidden when they
5644 // are part of a visual line that starts with another line, or when
5645 // they are entirely covered by collapsed, non-widget span.
4043 function lineIsHidden(doc, line) {
5646 function lineIsHidden(doc, line) {
4044 var sps = sawCollapsedSpans && line.markedSpans;
5647 var sps = sawCollapsedSpans && line.markedSpans;
4045 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
5648 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
4046 sp = sps[i];
5649 sp = sps[i];
4047 if (!sp.marker.collapsed) continue;
5650 if (!sp.marker.collapsed) continue;
4048 if (sp.from == null) return true;
5651 if (sp.from == null) return true;
4049 if (sp.marker.replacedWith) continue;
5652 if (sp.marker.widgetNode) continue;
4050 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
5653 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
4051 return true;
5654 return true;
4052 }
5655 }
4053 }
5656 }
4054 function lineIsHiddenInner(doc, line, span) {
5657 function lineIsHiddenInner(doc, line, span) {
4055 if (span.to == null) {
5658 if (span.to == null) {
4056 var end = span.marker.find().to, endLine = getLine(doc, end.line);
5659 var end = span.marker.find(1, true);
4057 return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
5660 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
4058 }
5661 }
4059 if (span.marker.inclusiveRight && span.to == line.text.length)
5662 if (span.marker.inclusiveRight && span.to == line.text.length)
4060 return true;
5663 return true;
4061 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
5664 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
4062 sp = line.markedSpans[i];
5665 sp = line.markedSpans[i];
4063 if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
5666 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
5667 (sp.to == null || sp.to != span.from) &&
4064 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
5668 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
4065 lineIsHiddenInner(doc, line, sp)) return true;
5669 lineIsHiddenInner(doc, line, sp)) return true;
4066 }
5670 }
4067 }
5671 }
4068
5672
4069 function detachMarkedSpans(line) {
4070 var spans = line.markedSpans;
4071 if (!spans) return;
4072 for (var i = 0; i < spans.length; ++i)
4073 spans[i].marker.detachLine(line);
4074 line.markedSpans = null;
4075 }
4076
4077 function attachMarkedSpans(line, spans) {
4078 if (!spans) return;
4079 for (var i = 0; i < spans.length; ++i)
4080 spans[i].marker.attachLine(line);
4081 line.markedSpans = spans;
4082 }
4083
4084 // LINE WIDGETS
5673 // LINE WIDGETS
4085
5674
5675 // Line widgets are block elements displayed above or below a line.
5676
4086 var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
5677 var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
4087 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
5678 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
4088 this[opt] = options[opt];
5679 this[opt] = options[opt];
@@ -4090,54 +5681,60 b' window.CodeMirror = (function() {'
4090 this.node = node;
5681 this.node = node;
4091 };
5682 };
4092 eventMixin(LineWidget);
5683 eventMixin(LineWidget);
4093 function widgetOperation(f) {
5684
4094 return function() {
5685 function adjustScrollWhenAboveVisible(cm, line, diff) {
4095 var withOp = !this.cm.curOp;
5686 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
4096 if (withOp) startOperation(this.cm);
5687 addToScrollPos(cm, null, diff);
4097 try {var result = f.apply(this, arguments);}
5688 }
4098 finally {if (withOp) endOperation(this.cm);}
5689
4099 return result;
5690 LineWidget.prototype.clear = function() {
4100 };
5691 var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
4101 }
4102 LineWidget.prototype.clear = widgetOperation(function() {
4103 var ws = this.line.widgets, no = lineNo(this.line);
4104 if (no == null || !ws) return;
5692 if (no == null || !ws) return;
4105 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
5693 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
4106 if (!ws.length) this.line.widgets = null;
5694 if (!ws.length) line.widgets = null;
4107 var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
5695 var height = widgetHeight(this);
4108 updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
5696 runInOp(cm, function() {
4109 if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
5697 adjustScrollWhenAboveVisible(cm, line, -height);
4110 regChange(this.cm, no, no + 1);
5698 regLineChange(cm, no, "widget");
4111 });
5699 updateLineHeight(line, Math.max(0, line.height - height));
4112 LineWidget.prototype.changed = widgetOperation(function() {
5700 });
4113 var oldH = this.height;
5701 };
5702 LineWidget.prototype.changed = function() {
5703 var oldH = this.height, cm = this.cm, line = this.line;
4114 this.height = null;
5704 this.height = null;
4115 var diff = widgetHeight(this) - oldH;
5705 var diff = widgetHeight(this) - oldH;
4116 if (!diff) return;
5706 if (!diff) return;
4117 updateLineHeight(this.line, this.line.height + diff);
5707 runInOp(cm, function() {
4118 var no = lineNo(this.line);
5708 cm.curOp.forceUpdate = true;
4119 regChange(this.cm, no, no + 1);
5709 adjustScrollWhenAboveVisible(cm, line, diff);
4120 });
5710 updateLineHeight(line, line.height + diff);
5711 });
5712 };
4121
5713
4122 function widgetHeight(widget) {
5714 function widgetHeight(widget) {
4123 if (widget.height != null) return widget.height;
5715 if (widget.height != null) return widget.height;
4124 if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
5716 if (!contains(document.body, widget.node)) {
4125 removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
5717 var parentStyle = "position: relative;";
5718 if (widget.coverGutter)
5719 parentStyle += "margin-left: -" + widget.cm.getGutterElement().offsetWidth + "px;";
5720 removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle));
5721 }
4126 return widget.height = widget.node.offsetHeight;
5722 return widget.height = widget.node.offsetHeight;
4127 }
5723 }
4128
5724
4129 function addLineWidget(cm, handle, node, options) {
5725 function addLineWidget(cm, handle, node, options) {
4130 var widget = new LineWidget(cm, node, options);
5726 var widget = new LineWidget(cm, node, options);
4131 if (widget.noHScroll) cm.display.alignWidgets = true;
5727 if (widget.noHScroll) cm.display.alignWidgets = true;
4132 changeLine(cm, handle, function(line) {
5728 changeLine(cm.doc, handle, "widget", function(line) {
4133 var widgets = line.widgets || (line.widgets = []);
5729 var widgets = line.widgets || (line.widgets = []);
4134 if (widget.insertAt == null) widgets.push(widget);
5730 if (widget.insertAt == null) widgets.push(widget);
4135 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
5731 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
4136 widget.line = line;
5732 widget.line = line;
4137 if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
5733 if (!lineIsHidden(cm.doc, line)) {
4138 var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
5734 var aboveVisible = heightAtLine(line) < cm.doc.scrollTop;
4139 updateLineHeight(line, line.height + widgetHeight(widget));
5735 updateLineHeight(line, line.height + widgetHeight(widget));
4140 if (aboveVisible) addToScrollPos(cm, 0, widget.height);
5736 if (aboveVisible) addToScrollPos(cm, null, widget.height);
5737 cm.curOp.forceUpdate = true;
4141 }
5738 }
4142 return true;
5739 return true;
4143 });
5740 });
@@ -4154,7 +5751,11 b' window.CodeMirror = (function() {'
4154 this.height = estimateHeight ? estimateHeight(this) : 1;
5751 this.height = estimateHeight ? estimateHeight(this) : 1;
4155 };
5752 };
4156 eventMixin(Line);
5753 eventMixin(Line);
4157
5754 Line.prototype.lineNo = function() { return lineNo(this); };
5755
5756 // Change the content (text, markers) of a line. Automatically
5757 // invalidates cached information and tries to re-estimate the
5758 // line's height.
4158 function updateLine(line, text, markedSpans, estimateHeight) {
5759 function updateLine(line, text, markedSpans, estimateHeight) {
4159 line.text = text;
5760 line.text = text;
4160 if (line.stateAfter) line.stateAfter = null;
5761 if (line.stateAfter) line.stateAfter = null;
@@ -4166,28 +5767,60 b' window.CodeMirror = (function() {'
4166 if (estHeight != line.height) updateLineHeight(line, estHeight);
5767 if (estHeight != line.height) updateLineHeight(line, estHeight);
4167 }
5768 }
4168
5769
5770 // Detach a line from the document tree and its markers.
4169 function cleanUpLine(line) {
5771 function cleanUpLine(line) {
4170 line.parent = null;
5772 line.parent = null;
4171 detachMarkedSpans(line);
5773 detachMarkedSpans(line);
4172 }
5774 }
4173
5775
4174 // Run the given mode's parser over a line, update the styles
5776 function extractLineClasses(type, output) {
4175 // array, which contains alternating fragments of text and CSS
5777 if (type) for (;;) {
4176 // classes.
5778 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
4177 function runMode(cm, text, mode, state, f) {
5779 if (!lineClass) break;
5780 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
5781 var prop = lineClass[1] ? "bgClass" : "textClass";
5782 if (output[prop] == null)
5783 output[prop] = lineClass[2];
5784 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
5785 output[prop] += " " + lineClass[2];
5786 }
5787 return type;
5788 }
5789
5790 function callBlankLine(mode, state) {
5791 if (mode.blankLine) return mode.blankLine(state);
5792 if (!mode.innerMode) return;
5793 var inner = CodeMirror.innerMode(mode, state);
5794 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
5795 }
5796
5797 function readToken(mode, stream, state) {
5798 for (var i = 0; i < 10; i++) {
5799 var style = mode.token(stream, state);
5800 if (stream.pos > stream.start) return style;
5801 }
5802 throw new Error("Mode " + mode.name + " failed to advance stream.");
5803 }
5804
5805 // Run the given mode's parser over a line, calling f for each token.
5806 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
4178 var flattenSpans = mode.flattenSpans;
5807 var flattenSpans = mode.flattenSpans;
4179 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
5808 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
4180 var curStart = 0, curStyle = null;
5809 var curStart = 0, curStyle = null;
4181 var stream = new StringStream(text, cm.options.tabSize), style;
5810 var stream = new StringStream(text, cm.options.tabSize), style;
4182 if (text == "" && mode.blankLine) mode.blankLine(state);
5811 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
4183 while (!stream.eol()) {
5812 while (!stream.eol()) {
4184 if (stream.pos > cm.options.maxHighlightLength) {
5813 if (stream.pos > cm.options.maxHighlightLength) {
4185 flattenSpans = false;
5814 flattenSpans = false;
4186 // Webkit seems to refuse to render text nodes longer than 57444 characters
5815 if (forceToEnd) processLine(cm, text, state, stream.pos);
4187 stream.pos = Math.min(text.length, stream.start + 50000);
5816 stream.pos = text.length;
4188 style = null;
5817 style = null;
4189 } else {
5818 } else {
4190 style = mode.token(stream, state);
5819 style = extractLineClasses(readToken(mode, stream, state), lineClasses);
5820 }
5821 if (cm.options.addModeClass) {
5822 var mName = CodeMirror.innerMode(mode, state).mode.name;
5823 if (mName) style = "m-" + (style ? mName + " " + style : mName);
4191 }
5824 }
4192 if (!flattenSpans || curStyle != style) {
5825 if (!flattenSpans || curStyle != style) {
4193 if (curStart < stream.start) f(stream.start, curStyle);
5826 if (curStart < stream.start) f(stream.start, curStyle);
@@ -4195,15 +5828,26 b' window.CodeMirror = (function() {'
4195 }
5828 }
4196 stream.start = stream.pos;
5829 stream.start = stream.pos;
4197 }
5830 }
4198 if (curStart < stream.pos) f(stream.pos, curStyle);
5831 while (curStart < stream.pos) {
4199 }
5832 // Webkit seems to refuse to render text nodes longer than 57444 characters
4200
5833 var pos = Math.min(stream.pos, curStart + 50000);
4201 function highlightLine(cm, line, state) {
5834 f(pos, curStyle);
5835 curStart = pos;
5836 }
5837 }
5838
5839 // Compute a style array (an array starting with a mode generation
5840 // -- for invalidation -- followed by pairs of end positions and
5841 // style strings), which is used to highlight the tokens on the
5842 // line.
5843 function highlightLine(cm, line, state, forceToEnd) {
4202 // A styles array always starts with a number identifying the
5844 // A styles array always starts with a number identifying the
4203 // mode/overlays that it is based on (for easy invalidation).
5845 // mode/overlays that it is based on (for easy invalidation).
4204 var st = [cm.state.modeGen];
5846 var st = [cm.state.modeGen], lineClasses = {};
4205 // Compute the base array of styles
5847 // Compute the base array of styles
4206 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
5848 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
5849 st.push(end, style);
5850 }, lineClasses, forceToEnd);
4207
5851
4208 // Run overlays, adjust style array.
5852 // Run overlays, adjust style array.
4209 for (var o = 0; o < cm.state.overlays.length; ++o) {
5853 for (var o = 0; o < cm.state.overlays.length; ++o) {
@@ -4220,159 +5864,164 b' window.CodeMirror = (function() {'
4220 }
5864 }
4221 if (!style) return;
5865 if (!style) return;
4222 if (overlay.opaque) {
5866 if (overlay.opaque) {
4223 st.splice(start, i - start, end, style);
5867 st.splice(start, i - start, end, "cm-overlay " + style);
4224 i = start + 2;
5868 i = start + 2;
4225 } else {
5869 } else {
4226 for (; start < i; start += 2) {
5870 for (; start < i; start += 2) {
4227 var cur = st[start+1];
5871 var cur = st[start+1];
4228 st[start+1] = cur ? cur + " " + style : style;
5872 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
4229 }
5873 }
4230 }
5874 }
4231 });
5875 }, lineClasses);
4232 }
5876 }
4233
5877
4234 return st;
5878 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
4235 }
5879 }
4236
5880
4237 function getLineStyles(cm, line) {
5881 function getLineStyles(cm, line) {
4238 if (!line.styles || line.styles[0] != cm.state.modeGen)
5882 if (!line.styles || line.styles[0] != cm.state.modeGen) {
4239 line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
5883 var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
5884 line.styles = result.styles;
5885 if (result.classes) line.styleClasses = result.classes;
5886 else if (line.styleClasses) line.styleClasses = null;
5887 }
4240 return line.styles;
5888 return line.styles;
4241 }
5889 }
4242
5890
4243 // Lightweight form of highlight -- proceed over this line and
5891 // Lightweight form of highlight -- proceed over this line and
4244 // update state, but don't save a style array.
5892 // update state, but don't save a style array. Used for lines that
4245 function processLine(cm, line, state) {
5893 // aren't currently visible.
5894 function processLine(cm, text, state, startAt) {
4246 var mode = cm.doc.mode;
5895 var mode = cm.doc.mode;
4247 var stream = new StringStream(line.text, cm.options.tabSize);
5896 var stream = new StringStream(text, cm.options.tabSize);
4248 if (line.text == "" && mode.blankLine) mode.blankLine(state);
5897 stream.start = stream.pos = startAt || 0;
5898 if (text == "") callBlankLine(mode, state);
4249 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
5899 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
4250 mode.token(stream, state);
5900 readToken(mode, stream, state);
4251 stream.start = stream.pos;
5901 stream.start = stream.pos;
4252 }
5902 }
4253 }
5903 }
4254
5904
4255 var styleToClassCache = {};
5905 // Convert a style as returned by a mode (either null, or a string
4256 function styleToClass(style) {
5906 // containing one or more styles) to a CSS style. This is cached,
4257 if (!style) return null;
5907 // and also looks for line-wide styles.
4258 return styleToClassCache[style] ||
5908 var styleToClassCache = {}, styleToClassCacheWithMode = {};
4259 (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
5909 function interpretTokenStyle(style, options) {
4260 }
5910 if (!style || /^\s*$/.test(style)) return null;
4261
5911 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
4262 function lineContent(cm, realLine, measure, copyWidgets) {
5912 return cache[style] ||
4263 var merged, line = realLine, empty = true;
5913 (cache[style] = style.replace(/\S+/g, "cm-$&"));
4264 while (merged = collapsedSpanAtStart(line))
5914 }
4265 line = getLine(cm.doc, merged.find().from.line);
5915
4266
5916 // Render the DOM representation of the text of a line. Also builds
4267 var builder = {pre: elt("pre"), col: 0, pos: 0,
5917 // up a 'line map', which points at the DOM nodes that represent
4268 measure: null, measuredSomething: false, cm: cm,
5918 // specific stretches of text, and is used by the measuring code.
4269 copyWidgets: copyWidgets};
5919 // The returned object contains the DOM node, this map, and
4270 if (line.textClass) builder.pre.className = line.textClass;
5920 // information about line-wide styles that were set by the mode.
4271
5921 function buildLineContent(cm, lineView) {
4272 do {
5922 // The padding-right forces the element to have a 'border', which
4273 if (line.text) empty = false;
5923 // is needed on Webkit to be able to get line-level bounding
4274 builder.measure = line == realLine && measure;
5924 // rectangles for it (in measureChar).
5925 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
5926 var builder = {pre: elt("pre", [content]), content: content, col: 0, pos: 0, cm: cm};
5927 lineView.measure = {};
5928
5929 // Iterate over the logical lines that make up this visual line.
5930 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
5931 var line = i ? lineView.rest[i - 1] : lineView.line, order;
4275 builder.pos = 0;
5932 builder.pos = 0;
4276 builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
5933 builder.addToken = buildToken;
5934 // Optionally wire in some hacks into the token-rendering
5935 // algorithm, to deal with browser quirks.
4277 if ((ie || webkit) && cm.getOption("lineWrapping"))
5936 if ((ie || webkit) && cm.getOption("lineWrapping"))
4278 builder.addToken = buildTokenSplitSpaces(builder.addToken);
5937 builder.addToken = buildTokenSplitSpaces(builder.addToken);
4279 var next = insertLineContent(line, builder, getLineStyles(cm, line));
5938 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
4280 if (measure && line == realLine && !builder.measuredSomething) {
5939 builder.addToken = buildTokenBadBidi(builder.addToken, order);
4281 measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
5940 builder.map = [];
4282 builder.measuredSomething = true;
5941 insertLineContent(line, builder, getLineStyles(cm, line));
4283 }
5942 if (line.styleClasses) {
4284 if (next) line = getLine(cm.doc, next.to.line);
5943 if (line.styleClasses.bgClass)
4285 } while (next);
5944 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
4286
5945 if (line.styleClasses.textClass)
4287 if (measure && !builder.measuredSomething && !measure[0])
5946 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
4288 measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
5947 }
4289 if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
5948
4290 builder.pre.appendChild(document.createTextNode("\u00a0"));
5949 // Ensure at least a single node is present, for measuring.
4291
5950 if (builder.map.length == 0)
4292 var order;
5951 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
4293 // Work around problem with the reported dimensions of single-char
5952
4294 // direction spans on IE (issue #1129). See also the comment in
5953 // Store the map and a cache object for the current logical line
4295 // cursorCoords.
5954 if (i == 0) {
4296 if (measure && ie && (order = getOrder(line))) {
5955 lineView.measure.map = builder.map;
4297 var l = order.length - 1;
5956 lineView.measure.cache = {};
4298 if (order[l].from == order[l].to) --l;
5957 } else {
4299 var last = order[l], prev = order[l - 1];
5958 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
4300 if (last.from + 1 == last.to && prev && last.level < prev.level) {
5959 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
4301 var span = measure[builder.pos - 1];
5960 }
4302 if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
5961 }
4303 span.nextSibling);
5962
4304 }
5963 signal(cm, "renderLine", cm, lineView.line, builder.pre);
4305 }
5964 if (builder.pre.className)
4306
5965 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
4307 signal(cm, "renderLine", cm, realLine, builder.pre);
5966 return builder;
4308 return builder.pre;
5967 }
4309 }
5968
4310
5969 function defaultSpecialCharPlaceholder(ch) {
4311 var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
5970 var token = elt("span", "\u2022", "cm-invalidchar");
5971 token.title = "\\u" + ch.charCodeAt(0).toString(16);
5972 return token;
5973 }
5974
5975 // Build up the DOM representation for a single token, and add it to
5976 // the line map. Takes care to render special characters separately.
4312 function buildToken(builder, text, style, startStyle, endStyle, title) {
5977 function buildToken(builder, text, style, startStyle, endStyle, title) {
4313 if (!text) return;
5978 if (!text) return;
4314 if (!tokenSpecialChars.test(text)) {
5979 var special = builder.cm.options.specialChars, mustWrap = false;
5980 if (!special.test(text)) {
4315 builder.col += text.length;
5981 builder.col += text.length;
4316 var content = document.createTextNode(text);
5982 var content = document.createTextNode(text);
5983 builder.map.push(builder.pos, builder.pos + text.length, content);
5984 if (ie && ie_version < 9) mustWrap = true;
5985 builder.pos += text.length;
4317 } else {
5986 } else {
4318 var content = document.createDocumentFragment(), pos = 0;
5987 var content = document.createDocumentFragment(), pos = 0;
4319 while (true) {
5988 while (true) {
4320 tokenSpecialChars.lastIndex = pos;
5989 special.lastIndex = pos;
4321 var m = tokenSpecialChars.exec(text);
5990 var m = special.exec(text);
4322 var skipped = m ? m.index - pos : text.length - pos;
5991 var skipped = m ? m.index - pos : text.length - pos;
4323 if (skipped) {
5992 if (skipped) {
4324 content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
5993 var txt = document.createTextNode(text.slice(pos, pos + skipped));
5994 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
5995 else content.appendChild(txt);
5996 builder.map.push(builder.pos, builder.pos + skipped, txt);
4325 builder.col += skipped;
5997 builder.col += skipped;
5998 builder.pos += skipped;
4326 }
5999 }
4327 if (!m) break;
6000 if (!m) break;
4328 pos += skipped + 1;
6001 pos += skipped + 1;
4329 if (m[0] == "\t") {
6002 if (m[0] == "\t") {
4330 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
6003 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4331 content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
6004 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4332 builder.col += tabWidth;
6005 builder.col += tabWidth;
4333 } else {
6006 } else {
4334 var token = elt("span", "\u2022", "cm-invalidchar");
6007 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
4335 token.title = "\\u" + m[0].charCodeAt(0).toString(16);
6008 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
4336 content.appendChild(token);
6009 else content.appendChild(txt);
4337 builder.col += 1;
6010 builder.col += 1;
4338 }
6011 }
4339 }
6012 builder.map.push(builder.pos, builder.pos + 1, txt);
4340 }
6013 builder.pos++;
4341 if (style || startStyle || endStyle || builder.measure) {
6014 }
6015 }
6016 if (style || startStyle || endStyle || mustWrap) {
4342 var fullStyle = style || "";
6017 var fullStyle = style || "";
4343 if (startStyle) fullStyle += startStyle;
6018 if (startStyle) fullStyle += startStyle;
4344 if (endStyle) fullStyle += endStyle;
6019 if (endStyle) fullStyle += endStyle;
4345 var token = elt("span", [content], fullStyle);
6020 var token = elt("span", [content], fullStyle);
4346 if (title) token.title = title;
6021 if (title) token.title = title;
4347 return builder.pre.appendChild(token);
6022 return builder.content.appendChild(token);
4348 }
6023 }
4349 builder.pre.appendChild(content);
6024 builder.content.appendChild(content);
4350 }
4351
4352 function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4353 var wrapping = builder.cm.options.lineWrapping;
4354 for (var i = 0; i < text.length; ++i) {
4355 var ch = text.charAt(i), start = i == 0;
4356 if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4357 ch = text.slice(i, i + 2);
4358 ++i;
4359 } else if (i && wrapping && spanAffectsWrapping(text, i)) {
4360 builder.pre.appendChild(elt("wbr"));
4361 }
4362 var old = builder.measure[builder.pos];
4363 var span = builder.measure[builder.pos] =
4364 buildToken(builder, ch, style,
4365 start && startStyle, i == text.length - 1 && endStyle);
4366 if (old) span.leftSide = old.leftSide || old;
4367 // In IE single-space nodes wrap differently than spaces
4368 // embedded in larger text nodes, except when set to
4369 // white-space: normal (issue #1268).
4370 if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4371 i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4372 span.style.whiteSpace = "normal";
4373 builder.pos += ch.length;
4374 }
4375 if (text.length) builder.measuredSomething = true;
4376 }
6025 }
4377
6026
4378 function buildTokenSplitSpaces(inner) {
6027 function buildTokenSplitSpaces(inner) {
@@ -4383,27 +6032,36 b' window.CodeMirror = (function() {'
4383 return out;
6032 return out;
4384 }
6033 }
4385 return function(builder, text, style, startStyle, endStyle, title) {
6034 return function(builder, text, style, startStyle, endStyle, title) {
4386 return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
6035 inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
6036 };
6037 }
6038
6039 // Work around nonsense dimensions being reported for stretches of
6040 // right-to-left text.
6041 function buildTokenBadBidi(inner, order) {
6042 return function(builder, text, style, startStyle, endStyle, title) {
6043 style = style ? style + " cm-force-border" : "cm-force-border";
6044 var start = builder.pos, end = start + text.length;
6045 for (;;) {
6046 // Find the part that overlaps with the start of this text
6047 for (var i = 0; i < order.length; i++) {
6048 var part = order[i];
6049 if (part.to > start && part.from <= start) break;
6050 }
6051 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title);
6052 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title);
6053 startStyle = null;
6054 text = text.slice(part.to - start);
6055 start = part.to;
6056 }
4387 };
6057 };
4388 }
6058 }
4389
6059
4390 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6060 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
4391 var widget = !ignoreWidget && marker.replacedWith;
6061 var widget = !ignoreWidget && marker.widgetNode;
4392 if (widget) {
6062 if (widget) {
4393 if (builder.copyWidgets) widget = widget.cloneNode(true);
6063 builder.map.push(builder.pos, builder.pos + size, widget);
4394 builder.pre.appendChild(widget);
6064 builder.content.appendChild(widget);
4395 if (builder.measure) {
4396 if (size) {
4397 builder.measure[builder.pos] = widget;
4398 } else {
4399 var elt = builder.measure[builder.pos] = zeroWidthElement(builder.cm.display.measure);
4400 if (marker.type != "bookmark" || marker.insertLeft)
4401 builder.pre.insertBefore(elt, widget);
4402 else
4403 builder.pre.appendChild(elt);
4404 }
4405 builder.measuredSomething = true;
4406 }
4407 }
6065 }
4408 builder.pos += size;
6066 builder.pos += size;
4409 }
6067 }
@@ -4414,7 +6072,7 b' window.CodeMirror = (function() {'
4414 var spans = line.markedSpans, allText = line.text, at = 0;
6072 var spans = line.markedSpans, allText = line.text, at = 0;
4415 if (!spans) {
6073 if (!spans) {
4416 for (var i = 1; i < styles.length; i+=2)
6074 for (var i = 1; i < styles.length; i+=2)
4417 builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
6075 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
4418 return;
6076 return;
4419 }
6077 }
4420
6078
@@ -4424,7 +6082,7 b' window.CodeMirror = (function() {'
4424 if (nextChange == pos) { // Update current marker set
6082 if (nextChange == pos) { // Update current marker set
4425 spanStyle = spanEndStyle = spanStartStyle = title = "";
6083 spanStyle = spanEndStyle = spanStartStyle = title = "";
4426 collapsed = null; nextChange = Infinity;
6084 collapsed = null; nextChange = Infinity;
4427 var foundBookmark = null;
6085 var foundBookmarks = [];
4428 for (var j = 0; j < spans.length; ++j) {
6086 for (var j = 0; j < spans.length; ++j) {
4429 var sp = spans[j], m = sp.marker;
6087 var sp = spans[j], m = sp.marker;
4430 if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
6088 if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
@@ -4433,19 +6091,20 b' window.CodeMirror = (function() {'
4433 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
6091 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4434 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
6092 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4435 if (m.title && !title) title = m.title;
6093 if (m.title && !title) title = m.title;
4436 if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
6094 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
4437 collapsed = sp;
6095 collapsed = sp;
4438 } else if (sp.from > pos && nextChange > sp.from) {
6096 } else if (sp.from > pos && nextChange > sp.from) {
4439 nextChange = sp.from;
6097 nextChange = sp.from;
4440 }
6098 }
4441 if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmark = m;
6099 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookmarks.push(m);
4442 }
6100 }
4443 if (collapsed && (collapsed.from || 0) == pos) {
6101 if (collapsed && (collapsed.from || 0) == pos) {
4444 buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
6102 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
4445 collapsed.marker, collapsed.from == null);
6103 collapsed.marker, collapsed.from == null);
4446 if (collapsed.to == null) return collapsed.marker.find();
6104 if (collapsed.to == null) return;
4447 }
6105 }
4448 if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
6106 if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
6107 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
4449 }
6108 }
4450 if (pos >= len) break;
6109 if (pos >= len) break;
4451
6110
@@ -4463,14 +6122,23 b' window.CodeMirror = (function() {'
4463 spanStartStyle = "";
6122 spanStartStyle = "";
4464 }
6123 }
4465 text = allText.slice(at, at = styles[i++]);
6124 text = allText.slice(at, at = styles[i++]);
4466 style = styleToClass(styles[i++]);
6125 style = interpretTokenStyle(styles[i++], builder.cm.options);
4467 }
6126 }
4468 }
6127 }
4469 }
6128 }
4470
6129
4471 // DOCUMENT DATA STRUCTURE
6130 // DOCUMENT DATA STRUCTURE
4472
6131
4473 function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
6132 // By default, updates that start and end at the beginning of a line
6133 // are treated specially, in order to make the association of line
6134 // widgets and marker elements with the text behave more intuitive.
6135 function isWholeLineUpdate(doc, change) {
6136 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
6137 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
6138 }
6139
6140 // Perform a change on the document data structure.
6141 function updateDoc(doc, change, markedSpans, estimateHeight) {
4474 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
6142 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4475 function update(line, text, spans) {
6143 function update(line, text, spans) {
4476 updateLine(line, text, spans, estimateHeight);
6144 updateLine(line, text, spans, estimateHeight);
@@ -4481,11 +6149,11 b' window.CodeMirror = (function() {'
4481 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
6149 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4482 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
6150 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4483
6151
4484 // First adjust the line structure
6152 // Adjust the line structure
4485 if (from.ch == 0 && to.ch == 0 && lastText == "") {
6153 if (isWholeLineUpdate(doc, change)) {
4486 // This is a whole-line replace. Treated specially to make
6154 // This is a whole-line replace. Treated specially to make
4487 // sure line objects move the way they are supposed to.
6155 // sure line objects move the way they are supposed to.
4488 for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
6156 for (var i = 0, added = []; i < text.length - 1; ++i)
4489 added.push(new Line(text[i], spansFor(i), estimateHeight));
6157 added.push(new Line(text[i], spansFor(i), estimateHeight));
4490 update(lastLine, lastLine.text, lastSpans);
6158 update(lastLine, lastLine.text, lastSpans);
4491 if (nlines) doc.remove(from.line, nlines);
6159 if (nlines) doc.remove(from.line, nlines);
@@ -4494,7 +6162,7 b' window.CodeMirror = (function() {'
4494 if (text.length == 1) {
6162 if (text.length == 1) {
4495 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
6163 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4496 } else {
6164 } else {
4497 for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
6165 for (var added = [], i = 1; i < text.length - 1; ++i)
4498 added.push(new Line(text[i], spansFor(i), estimateHeight));
6166 added.push(new Line(text[i], spansFor(i), estimateHeight));
4499 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
6167 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4500 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6168 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
@@ -4506,20 +6174,32 b' window.CodeMirror = (function() {'
4506 } else {
6174 } else {
4507 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
6175 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4508 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
6176 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4509 for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
6177 for (var i = 1, added = []; i < text.length - 1; ++i)
4510 added.push(new Line(text[i], spansFor(i), estimateHeight));
6178 added.push(new Line(text[i], spansFor(i), estimateHeight));
4511 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
6179 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4512 doc.insert(from.line + 1, added);
6180 doc.insert(from.line + 1, added);
4513 }
6181 }
4514
6182
4515 signalLater(doc, "change", doc, change);
6183 signalLater(doc, "change", doc, change);
4516 setSelection(doc, selAfter.anchor, selAfter.head, null, true);
6184 }
4517 }
6185
6186 // The document is represented as a BTree consisting of leaves, with
6187 // chunk of lines in them, and branches, with up to ten leaves or
6188 // other branch nodes below them. The top node is always a branch
6189 // node, and is the document object itself (meaning it has
6190 // additional methods and properties).
6191 //
6192 // All nodes have parent links. The tree is used both to go from
6193 // line numbers to line objects, and to go from objects to numbers.
6194 // It also indexes by height, and is used to convert between height
6195 // and line object, and to find the total height of the document.
6196 //
6197 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
4518
6198
4519 function LeafChunk(lines) {
6199 function LeafChunk(lines) {
4520 this.lines = lines;
6200 this.lines = lines;
4521 this.parent = null;
6201 this.parent = null;
4522 for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
6202 for (var i = 0, height = 0; i < lines.length; ++i) {
4523 lines[i].parent = this;
6203 lines[i].parent = this;
4524 height += lines[i].height;
6204 height += lines[i].height;
4525 }
6205 }
@@ -4528,6 +6208,7 b' window.CodeMirror = (function() {'
4528
6208
4529 LeafChunk.prototype = {
6209 LeafChunk.prototype = {
4530 chunkSize: function() { return this.lines.length; },
6210 chunkSize: function() { return this.lines.length; },
6211 // Remove the n lines at offset 'at'.
4531 removeInner: function(at, n) {
6212 removeInner: function(at, n) {
4532 for (var i = at, e = at + n; i < e; ++i) {
6213 for (var i = at, e = at + n; i < e; ++i) {
4533 var line = this.lines[i];
6214 var line = this.lines[i];
@@ -4537,14 +6218,18 b' window.CodeMirror = (function() {'
4537 }
6218 }
4538 this.lines.splice(at, n);
6219 this.lines.splice(at, n);
4539 },
6220 },
6221 // Helper used to collapse a small branch into a single leaf.
4540 collapse: function(lines) {
6222 collapse: function(lines) {
4541 lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
6223 lines.push.apply(lines, this.lines);
4542 },
6224 },
6225 // Insert the given array of lines at offset 'at', count them as
6226 // having the given height.
4543 insertInner: function(at, lines, height) {
6227 insertInner: function(at, lines, height) {
4544 this.height += height;
6228 this.height += height;
4545 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
6229 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4546 for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
6230 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
4547 },
6231 },
6232 // Used to iterate over a part of the tree.
4548 iterN: function(at, n, op) {
6233 iterN: function(at, n, op) {
4549 for (var e = at + n; at < e; ++at)
6234 for (var e = at + n; at < e; ++at)
4550 if (op(this.lines[at])) return true;
6235 if (op(this.lines[at])) return true;
@@ -4554,7 +6239,7 b' window.CodeMirror = (function() {'
4554 function BranchChunk(children) {
6239 function BranchChunk(children) {
4555 this.children = children;
6240 this.children = children;
4556 var size = 0, height = 0;
6241 var size = 0, height = 0;
4557 for (var i = 0, e = children.length; i < e; ++i) {
6242 for (var i = 0; i < children.length; ++i) {
4558 var ch = children[i];
6243 var ch = children[i];
4559 size += ch.chunkSize(); height += ch.height;
6244 size += ch.chunkSize(); height += ch.height;
4560 ch.parent = this;
6245 ch.parent = this;
@@ -4579,7 +6264,10 b' window.CodeMirror = (function() {'
4579 at = 0;
6264 at = 0;
4580 } else at -= sz;
6265 } else at -= sz;
4581 }
6266 }
4582 if (this.size - n < 25) {
6267 // If the result is smaller than 25 lines, ensure that it is a
6268 // single leaf node.
6269 if (this.size - n < 25 &&
6270 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
4583 var lines = [];
6271 var lines = [];
4584 this.collapse(lines);
6272 this.collapse(lines);
4585 this.children = [new LeafChunk(lines)];
6273 this.children = [new LeafChunk(lines)];
@@ -4587,12 +6275,12 b' window.CodeMirror = (function() {'
4587 }
6275 }
4588 },
6276 },
4589 collapse: function(lines) {
6277 collapse: function(lines) {
4590 for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
6278 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
4591 },
6279 },
4592 insertInner: function(at, lines, height) {
6280 insertInner: function(at, lines, height) {
4593 this.size += lines.length;
6281 this.size += lines.length;
4594 this.height += height;
6282 this.height += height;
4595 for (var i = 0, e = this.children.length; i < e; ++i) {
6283 for (var i = 0; i < this.children.length; ++i) {
4596 var child = this.children[i], sz = child.chunkSize();
6284 var child = this.children[i], sz = child.chunkSize();
4597 if (at <= sz) {
6285 if (at <= sz) {
4598 child.insertInner(at, lines, height);
6286 child.insertInner(at, lines, height);
@@ -4611,6 +6299,7 b' window.CodeMirror = (function() {'
4611 at -= sz;
6299 at -= sz;
4612 }
6300 }
4613 },
6301 },
6302 // When a node has grown, check whether it should be split.
4614 maybeSpill: function() {
6303 maybeSpill: function() {
4615 if (this.children.length <= 10) return;
6304 if (this.children.length <= 10) return;
4616 var me = this;
6305 var me = this;
@@ -4633,7 +6322,7 b' window.CodeMirror = (function() {'
4633 me.parent.maybeSpill();
6322 me.parent.maybeSpill();
4634 },
6323 },
4635 iterN: function(at, n, op) {
6324 iterN: function(at, n, op) {
4636 for (var i = 0, e = this.children.length; i < e; ++i) {
6325 for (var i = 0; i < this.children.length; ++i) {
4637 var child = this.children[i], sz = child.chunkSize();
6326 var child = this.children[i], sz = child.chunkSize();
4638 if (at < sz) {
6327 if (at < sz) {
4639 var used = Math.min(n, sz - at);
6328 var used = Math.min(n, sz - at);
@@ -4654,43 +6343,52 b' window.CodeMirror = (function() {'
4654 this.first = firstLine;
6343 this.first = firstLine;
4655 this.scrollTop = this.scrollLeft = 0;
6344 this.scrollTop = this.scrollLeft = 0;
4656 this.cantEdit = false;
6345 this.cantEdit = false;
4657 this.history = makeHistory();
4658 this.cleanGeneration = 1;
6346 this.cleanGeneration = 1;
4659 this.frontier = firstLine;
6347 this.frontier = firstLine;
4660 var start = Pos(firstLine, 0);
6348 var start = Pos(firstLine, 0);
4661 this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
6349 this.sel = simpleSelection(start);
6350 this.history = new History(null);
4662 this.id = ++nextDocId;
6351 this.id = ++nextDocId;
4663 this.modeOption = mode;
6352 this.modeOption = mode;
4664
6353
4665 if (typeof text == "string") text = splitLines(text);
6354 if (typeof text == "string") text = splitLines(text);
4666 updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
6355 updateDoc(this, {from: start, to: start, text: text});
6356 setSelection(this, simpleSelection(start), sel_dontScroll);
4667 };
6357 };
4668
6358
4669 Doc.prototype = createObj(BranchChunk.prototype, {
6359 Doc.prototype = createObj(BranchChunk.prototype, {
4670 constructor: Doc,
6360 constructor: Doc,
6361 // Iterate over the document. Supports two forms -- with only one
6362 // argument, it calls that for each line in the document. With
6363 // three, it iterates over the range given by the first two (with
6364 // the second being non-inclusive).
4671 iter: function(from, to, op) {
6365 iter: function(from, to, op) {
4672 if (op) this.iterN(from - this.first, to - from, op);
6366 if (op) this.iterN(from - this.first, to - from, op);
4673 else this.iterN(this.first, this.first + this.size, from);
6367 else this.iterN(this.first, this.first + this.size, from);
4674 },
6368 },
4675
6369
6370 // Non-public interface for adding and removing lines.
4676 insert: function(at, lines) {
6371 insert: function(at, lines) {
4677 var height = 0;
6372 var height = 0;
4678 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
6373 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
4679 this.insertInner(at - this.first, lines, height);
6374 this.insertInner(at - this.first, lines, height);
4680 },
6375 },
4681 remove: function(at, n) { this.removeInner(at - this.first, n); },
6376 remove: function(at, n) { this.removeInner(at - this.first, n); },
4682
6377
6378 // From here, the methods are part of the public interface. Most
6379 // are also available from CodeMirror (editor) instances.
6380
4683 getValue: function(lineSep) {
6381 getValue: function(lineSep) {
4684 var lines = getLines(this, this.first, this.first + this.size);
6382 var lines = getLines(this, this.first, this.first + this.size);
4685 if (lineSep === false) return lines;
6383 if (lineSep === false) return lines;
4686 return lines.join(lineSep || "\n");
6384 return lines.join(lineSep || "\n");
4687 },
6385 },
4688 setValue: function(code) {
6386 setValue: docMethodOp(function(code) {
4689 var top = Pos(this.first, 0), last = this.first + this.size - 1;
6387 var top = Pos(this.first, 0), last = this.first + this.size - 1;
4690 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6388 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4691 text: splitLines(code), origin: "setValue"},
6389 text: splitLines(code), origin: "setValue"}, true);
4692 {head: top, anchor: top}, true);
6390 setSelection(this, simpleSelection(top));
4693 },
6391 }),
4694 replaceRange: function(code, from, to, origin) {
6392 replaceRange: function(code, from, to, origin) {
4695 from = clipPos(this, from);
6393 from = clipPos(this, from);
4696 to = to ? clipPos(this, to) : from;
6394 to = to ? clipPos(this, to) : from;
@@ -4703,21 +6401,13 b' window.CodeMirror = (function() {'
4703 },
6401 },
4704
6402
4705 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
6403 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4706 setLine: function(line, text) {
4707 if (isLine(this, line))
4708 replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4709 },
4710 removeLine: function(line) {
4711 if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
4712 else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
4713 },
4714
6404
4715 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
6405 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4716 getLineNumber: function(line) {return lineNo(line);},
6406 getLineNumber: function(line) {return lineNo(line);},
4717
6407
4718 getLineHandleVisualStart: function(line) {
6408 getLineHandleVisualStart: function(line) {
4719 if (typeof line == "number") line = getLine(this, line);
6409 if (typeof line == "number") line = getLine(this, line);
4720 return visualLine(this, line);
6410 return visualLine(line);
4721 },
6411 },
4722
6412
4723 lineCount: function() {return this.size;},
6413 lineCount: function() {return this.size;},
@@ -4727,47 +6417,103 b' window.CodeMirror = (function() {'
4727 clipPos: function(pos) {return clipPos(this, pos);},
6417 clipPos: function(pos) {return clipPos(this, pos);},
4728
6418
4729 getCursor: function(start) {
6419 getCursor: function(start) {
4730 var sel = this.sel, pos;
6420 var range = this.sel.primary(), pos;
4731 if (start == null || start == "head") pos = sel.head;
6421 if (start == null || start == "head") pos = range.head;
4732 else if (start == "anchor") pos = sel.anchor;
6422 else if (start == "anchor") pos = range.anchor;
4733 else if (start == "end" || start === false) pos = sel.to;
6423 else if (start == "end" || start == "to" || start === false) pos = range.to();
4734 else pos = sel.from;
6424 else pos = range.from();
4735 return copyPos(pos);
6425 return pos;
4736 },
6426 },
4737 somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
6427 listSelections: function() { return this.sel.ranges; },
4738
6428 somethingSelected: function() {return this.sel.somethingSelected();},
4739 setCursor: docOperation(function(line, ch, extend) {
6429
4740 var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
6430 setCursor: docMethodOp(function(line, ch, options) {
4741 if (extend) extendSelection(this, pos);
6431 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
4742 else setSelection(this, pos, pos);
6432 }),
6433 setSelection: docMethodOp(function(anchor, head, options) {
6434 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
6435 }),
6436 extendSelection: docMethodOp(function(head, other, options) {
6437 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6438 }),
6439 extendSelections: docMethodOp(function(heads, options) {
6440 extendSelections(this, clipPosArray(this, heads, options));
6441 }),
6442 extendSelectionsBy: docMethodOp(function(f, options) {
6443 extendSelections(this, map(this.sel.ranges, f), options);
6444 }),
6445 setSelections: docMethodOp(function(ranges, primary, options) {
6446 if (!ranges.length) return;
6447 for (var i = 0, out = []; i < ranges.length; i++)
6448 out[i] = new Range(clipPos(this, ranges[i].anchor),
6449 clipPos(this, ranges[i].head));
6450 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
6451 setSelection(this, normalizeSelection(out, primary), options);
6452 }),
6453 addSelection: docMethodOp(function(anchor, head, options) {
6454 var ranges = this.sel.ranges.slice(0);
6455 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
6456 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
4743 }),
6457 }),
4744 setSelection: docOperation(function(anchor, head) {
6458
4745 setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
6459 getSelection: function(lineSep) {
4746 }),
6460 var ranges = this.sel.ranges, lines;
4747 extendSelection: docOperation(function(from, to) {
6461 for (var i = 0; i < ranges.length; i++) {
4748 extendSelection(this, clipPos(this, from), to && clipPos(this, to));
6462 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6463 lines = lines ? lines.concat(sel) : sel;
6464 }
6465 if (lineSep === false) return lines;
6466 else return lines.join(lineSep || "\n");
6467 },
6468 getSelections: function(lineSep) {
6469 var parts = [], ranges = this.sel.ranges;
6470 for (var i = 0; i < ranges.length; i++) {
6471 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6472 if (lineSep !== false) sel = sel.join(lineSep || "\n");
6473 parts[i] = sel;
6474 }
6475 return parts;
6476 },
6477 replaceSelection: function(code, collapse, origin) {
6478 var dup = [];
6479 for (var i = 0; i < this.sel.ranges.length; i++)
6480 dup[i] = code;
6481 this.replaceSelections(dup, collapse, origin || "+input");
6482 },
6483 replaceSelections: docMethodOp(function(code, collapse, origin) {
6484 var changes = [], sel = this.sel;
6485 for (var i = 0; i < sel.ranges.length; i++) {
6486 var range = sel.ranges[i];
6487 changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
6488 }
6489 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
6490 for (var i = changes.length - 1; i >= 0; i--)
6491 makeChange(this, changes[i]);
6492 if (newSel) setSelectionReplaceHistory(this, newSel);
6493 else if (this.cm) ensureCursorVisible(this.cm);
4749 }),
6494 }),
4750
6495 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
4751 getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
6496 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
4752 replaceSelection: function(code, collapse, origin) {
6497 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
4753 makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
6498 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
4754 },
6499
4755 undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
6500 setExtending: function(val) {this.extend = val;},
4756 redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
6501 getExtending: function() {return this.extend;},
4757
4758 setExtending: function(val) {this.sel.extend = val;},
4759
6502
4760 historySize: function() {
6503 historySize: function() {
4761 var hist = this.history;
6504 var hist = this.history, done = 0, undone = 0;
4762 return {undo: hist.done.length, redo: hist.undone.length};
6505 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
6506 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
6507 return {undo: done, redo: undone};
4763 },
6508 },
4764 clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
6509 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
4765
6510
4766 markClean: function() {
6511 markClean: function() {
4767 this.cleanGeneration = this.changeGeneration();
6512 this.cleanGeneration = this.changeGeneration(true);
4768 },
6513 },
4769 changeGeneration: function() {
6514 changeGeneration: function(forceSplit) {
4770 this.history.lastOp = this.history.lastOrigin = null;
6515 if (forceSplit)
6516 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
4771 return this.history.generation;
6517 return this.history.generation;
4772 },
6518 },
4773 isClean: function (gen) {
6519 isClean: function (gen) {
@@ -4779,17 +6525,43 b' window.CodeMirror = (function() {'
4779 undone: copyHistoryArray(this.history.undone)};
6525 undone: copyHistoryArray(this.history.undone)};
4780 },
6526 },
4781 setHistory: function(histData) {
6527 setHistory: function(histData) {
4782 var hist = this.history = makeHistory(this.history.maxGeneration);
6528 var hist = this.history = new History(this.history.maxGeneration);
4783 hist.done = histData.done.slice(0);
6529 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
4784 hist.undone = histData.undone.slice(0);
6530 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
4785 },
6531 },
4786
6532
6533 addLineClass: docMethodOp(function(handle, where, cls) {
6534 return changeLine(this, handle, "class", function(line) {
6535 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
6536 if (!line[prop]) line[prop] = cls;
6537 else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
6538 else line[prop] += " " + cls;
6539 return true;
6540 });
6541 }),
6542 removeLineClass: docMethodOp(function(handle, where, cls) {
6543 return changeLine(this, handle, "class", function(line) {
6544 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
6545 var cur = line[prop];
6546 if (!cur) return false;
6547 else if (cls == null) line[prop] = null;
6548 else {
6549 var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
6550 if (!found) return false;
6551 var end = found.index + found[0].length;
6552 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
6553 }
6554 return true;
6555 });
6556 }),
6557
4787 markText: function(from, to, options) {
6558 markText: function(from, to, options) {
4788 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
6559 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4789 },
6560 },
4790 setBookmark: function(pos, options) {
6561 setBookmark: function(pos, options) {
4791 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6562 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4792 insertLeft: options && options.insertLeft};
6563 insertLeft: options && options.insertLeft,
6564 clearWhenEmpty: false, shared: options && options.shared};
4793 pos = clipPos(this, pos);
6565 pos = clipPos(this, pos);
4794 return markText(this, pos, pos, realOpts, "bookmark");
6566 return markText(this, pos, pos, realOpts, "bookmark");
4795 },
6567 },
@@ -4804,6 +6576,23 b' window.CodeMirror = (function() {'
4804 }
6576 }
4805 return markers;
6577 return markers;
4806 },
6578 },
6579 findMarks: function(from, to, filter) {
6580 from = clipPos(this, from); to = clipPos(this, to);
6581 var found = [], lineNo = from.line;
6582 this.iter(from.line, to.line + 1, function(line) {
6583 var spans = line.markedSpans;
6584 if (spans) for (var i = 0; i < spans.length; i++) {
6585 var span = spans[i];
6586 if (!(lineNo == from.line && from.ch > span.to ||
6587 span.from == null && lineNo != from.line||
6588 lineNo == to.line && span.from > to.ch) &&
6589 (!filter || filter(span.marker)))
6590 found.push(span.marker.parent || span.marker);
6591 }
6592 ++lineNo;
6593 });
6594 return found;
6595 },
4807 getAllMarks: function() {
6596 getAllMarks: function() {
4808 var markers = [];
6597 var markers = [];
4809 this.iter(function(line) {
6598 this.iter(function(line) {
@@ -4837,8 +6626,8 b' window.CodeMirror = (function() {'
4837 copy: function(copyHistory) {
6626 copy: function(copyHistory) {
4838 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
6627 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4839 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6628 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4840 doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
6629 doc.sel = this.sel;
4841 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
6630 doc.extend = false;
4842 if (copyHistory) {
6631 if (copyHistory) {
4843 doc.history.undoDepth = this.history.undoDepth;
6632 doc.history.undoDepth = this.history.undoDepth;
4844 doc.setHistory(this.getHistory());
6633 doc.setHistory(this.getHistory());
@@ -4855,6 +6644,7 b' window.CodeMirror = (function() {'
4855 if (options.sharedHist) copy.history = this.history;
6644 if (options.sharedHist) copy.history = this.history;
4856 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
6645 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4857 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6646 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6647 copySharedMarkers(copy, findSharedMarkers(this));
4858 return copy;
6648 return copy;
4859 },
6649 },
4860 unlinkDoc: function(other) {
6650 unlinkDoc: function(other) {
@@ -4864,13 +6654,14 b' window.CodeMirror = (function() {'
4864 if (link.doc != other) continue;
6654 if (link.doc != other) continue;
4865 this.linked.splice(i, 1);
6655 this.linked.splice(i, 1);
4866 other.unlinkDoc(this);
6656 other.unlinkDoc(this);
6657 detachSharedMarkers(findSharedMarkers(this));
4867 break;
6658 break;
4868 }
6659 }
4869 // If the histories were shared, split them again
6660 // If the histories were shared, split them again
4870 if (other.history == this.history) {
6661 if (other.history == this.history) {
4871 var splitIds = [other.id];
6662 var splitIds = [other.id];
4872 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
6663 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4873 other.history = makeHistory();
6664 other.history = new History(null);
4874 other.history.done = copyHistoryArray(this.history.done, splitIds);
6665 other.history.done = copyHistoryArray(this.history.done, splitIds);
4875 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6666 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4876 }
6667 }
@@ -4881,9 +6672,10 b' window.CodeMirror = (function() {'
4881 getEditor: function() {return this.cm;}
6672 getEditor: function() {return this.cm;}
4882 });
6673 });
4883
6674
6675 // Public alias.
4884 Doc.prototype.eachLine = Doc.prototype.iter;
6676 Doc.prototype.eachLine = Doc.prototype.iter;
4885
6677
4886 // The Doc methods that should be available on CodeMirror instances
6678 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
4887 var dontDelegate = "iter insert remove copy getEditor".split(" ");
6679 var dontDelegate = "iter insert remove copy getEditor".split(" ");
4888 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
6680 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4889 CodeMirror.prototype[prop] = (function(method) {
6681 CodeMirror.prototype[prop] = (function(method) {
@@ -4892,6 +6684,7 b' window.CodeMirror = (function() {'
4892
6684
4893 eventMixin(Doc);
6685 eventMixin(Doc);
4894
6686
6687 // Call f for all linked documents.
4895 function linkedDocs(doc, f, sharedHistOnly) {
6688 function linkedDocs(doc, f, sharedHistOnly) {
4896 function propagate(doc, skip, sharedHist) {
6689 function propagate(doc, skip, sharedHist) {
4897 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
6690 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
@@ -4906,22 +6699,25 b' window.CodeMirror = (function() {'
4906 propagate(doc, null, true);
6699 propagate(doc, null, true);
4907 }
6700 }
4908
6701
6702 // Attach a document to an editor.
4909 function attachDoc(cm, doc) {
6703 function attachDoc(cm, doc) {
4910 if (doc.cm) throw new Error("This document is already in use.");
6704 if (doc.cm) throw new Error("This document is already in use.");
4911 cm.doc = doc;
6705 cm.doc = doc;
4912 doc.cm = cm;
6706 doc.cm = cm;
4913 estimateLineHeights(cm);
6707 estimateLineHeights(cm);
4914 loadMode(cm);
6708 loadMode(cm);
4915 if (!cm.options.lineWrapping) computeMaxLength(cm);
6709 if (!cm.options.lineWrapping) findMaxLine(cm);
4916 cm.options.mode = doc.modeOption;
6710 cm.options.mode = doc.modeOption;
4917 regChange(cm);
6711 regChange(cm);
4918 }
6712 }
4919
6713
4920 // LINE UTILITIES
6714 // LINE UTILITIES
4921
6715
4922 function getLine(chunk, n) {
6716 // Find the line object corresponding to the given line number.
4923 n -= chunk.first;
6717 function getLine(doc, n) {
4924 while (!chunk.lines) {
6718 n -= doc.first;
6719 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
6720 for (var chunk = doc; !chunk.lines;) {
4925 for (var i = 0;; ++i) {
6721 for (var i = 0;; ++i) {
4926 var child = chunk.children[i], sz = child.chunkSize();
6722 var child = chunk.children[i], sz = child.chunkSize();
4927 if (n < sz) { chunk = child; break; }
6723 if (n < sz) { chunk = child; break; }
@@ -4931,6 +6727,8 b' window.CodeMirror = (function() {'
4931 return chunk.lines[n];
6727 return chunk.lines[n];
4932 }
6728 }
4933
6729
6730 // Get the part of a document between two positions, as an array of
6731 // strings.
4934 function getBetween(doc, start, end) {
6732 function getBetween(doc, start, end) {
4935 var out = [], n = start.line;
6733 var out = [], n = start.line;
4936 doc.iter(start.line, end.line + 1, function(line) {
6734 doc.iter(start.line, end.line + 1, function(line) {
@@ -4942,17 +6740,22 b' window.CodeMirror = (function() {'
4942 });
6740 });
4943 return out;
6741 return out;
4944 }
6742 }
6743 // Get the lines between from and to, as array of strings.
4945 function getLines(doc, from, to) {
6744 function getLines(doc, from, to) {
4946 var out = [];
6745 var out = [];
4947 doc.iter(from, to, function(line) { out.push(line.text); });
6746 doc.iter(from, to, function(line) { out.push(line.text); });
4948 return out;
6747 return out;
4949 }
6748 }
4950
6749
6750 // Update the height of a line, propagating the height change
6751 // upwards to parent nodes.
4951 function updateLineHeight(line, height) {
6752 function updateLineHeight(line, height) {
4952 var diff = height - line.height;
6753 var diff = height - line.height;
4953 for (var n = line; n; n = n.parent) n.height += diff;
6754 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
4954 }
6755 }
4955
6756
6757 // Given a line object, find its line number by walking up through
6758 // its parent links.
4956 function lineNo(line) {
6759 function lineNo(line) {
4957 if (line.parent == null) return null;
6760 if (line.parent == null) return null;
4958 var cur = line.parent, no = indexOf(cur.lines, line);
6761 var cur = line.parent, no = indexOf(cur.lines, line);
@@ -4965,10 +6768,12 b' window.CodeMirror = (function() {'
4965 return no + cur.first;
6768 return no + cur.first;
4966 }
6769 }
4967
6770
6771 // Find the line at the given vertical position, using the height
6772 // information in the document tree.
4968 function lineAtHeight(chunk, h) {
6773 function lineAtHeight(chunk, h) {
4969 var n = chunk.first;
6774 var n = chunk.first;
4970 outer: do {
6775 outer: do {
4971 for (var i = 0, e = chunk.children.length; i < e; ++i) {
6776 for (var i = 0; i < chunk.children.length; ++i) {
4972 var child = chunk.children[i], ch = child.height;
6777 var child = chunk.children[i], ch = child.height;
4973 if (h < ch) { chunk = child; continue outer; }
6778 if (h < ch) { chunk = child; continue outer; }
4974 h -= ch;
6779 h -= ch;
@@ -4976,7 +6781,7 b' window.CodeMirror = (function() {'
4976 }
6781 }
4977 return n;
6782 return n;
4978 } while (!chunk.lines);
6783 } while (!chunk.lines);
4979 for (var i = 0, e = chunk.lines.length; i < e; ++i) {
6784 for (var i = 0; i < chunk.lines.length; ++i) {
4980 var line = chunk.lines[i], lh = line.height;
6785 var line = chunk.lines[i], lh = line.height;
4981 if (h < lh) break;
6786 if (h < lh) break;
4982 h -= lh;
6787 h -= lh;
@@ -4984,8 +6789,10 b' window.CodeMirror = (function() {'
4984 return n + i;
6789 return n + i;
4985 }
6790 }
4986
6791
4987 function heightAtLine(cm, lineObj) {
6792
4988 lineObj = visualLine(cm.doc, lineObj);
6793 // Find the height above the given line.
6794 function heightAtLine(lineObj) {
6795 lineObj = visualLine(lineObj);
4989
6796
4990 var h = 0, chunk = lineObj.parent;
6797 var h = 0, chunk = lineObj.parent;
4991 for (var i = 0; i < chunk.lines.length; ++i) {
6798 for (var i = 0; i < chunk.lines.length; ++i) {
@@ -5003,6 +6810,9 b' window.CodeMirror = (function() {'
5003 return h;
6810 return h;
5004 }
6811 }
5005
6812
6813 // Get the bidi ordering for the given line (and cache it). Returns
6814 // false for lines that are fully left-to-right, and an array of
6815 // BidiSpan objects otherwise.
5006 function getOrder(line) {
6816 function getOrder(line) {
5007 var order = line.order;
6817 var order = line.order;
5008 if (order == null) order = line.order = bidiOrdering(line.text);
6818 if (order == null) order = line.order = bidiOrdering(line.text);
@@ -5011,20 +6821,141 b' window.CodeMirror = (function() {'
5011
6821
5012 // HISTORY
6822 // HISTORY
5013
6823
5014 function makeHistory(startGen) {
6824 function History(startGen) {
5015 return {
6825 // Arrays of change events and selections. Doing something adds an
5016 // Arrays of history events. Doing something adds an event to
6826 // event to done and clears undo. Undoing moves events from done
5017 // done and clears undo. Undoing moves events from done to
6827 // to undone, redoing moves them in the other direction.
5018 // undone, redoing moves them in the other direction.
6828 this.done = []; this.undone = [];
5019 done: [], undone: [], undoDepth: Infinity,
6829 this.undoDepth = Infinity;
5020 // Used to track when changes can be merged into a single undo
6830 // Used to track when changes can be merged into a single undo
5021 // event
6831 // event
5022 lastTime: 0, lastOp: null, lastOrigin: null,
6832 this.lastModTime = this.lastSelTime = 0;
5023 // Used by the isClean() method
6833 this.lastOp = this.lastSelOp = null;
5024 generation: startGen || 1, maxGeneration: startGen || 1
6834 this.lastOrigin = this.lastSelOrigin = null;
5025 };
6835 // Used by the isClean() method
5026 }
6836 this.generation = this.maxGeneration = startGen || 1;
5027
6837 }
6838
6839 // Create a history change event from an updateDoc-style change
6840 // object.
6841 function historyChangeFromChange(doc, change) {
6842 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
6843 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
6844 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
6845 return histChange;
6846 }
6847
6848 // Pop all selection events off the end of a history array. Stop at
6849 // a change event.
6850 function clearSelectionEvents(array) {
6851 while (array.length) {
6852 var last = lst(array);
6853 if (last.ranges) array.pop();
6854 else break;
6855 }
6856 }
6857
6858 // Find the top change event in the history. Pop off selection
6859 // events that are in the way.
6860 function lastChangeEvent(hist, force) {
6861 if (force) {
6862 clearSelectionEvents(hist.done);
6863 return lst(hist.done);
6864 } else if (hist.done.length && !lst(hist.done).ranges) {
6865 return lst(hist.done);
6866 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
6867 hist.done.pop();
6868 return lst(hist.done);
6869 }
6870 }
6871
6872 // Register a change in the history. Merges changes that are within
6873 // a single operation, ore are close together with an origin that
6874 // allows merging (starting with "+") into a single event.
6875 function addChangeToHistory(doc, change, selAfter, opId) {
6876 var hist = doc.history;
6877 hist.undone.length = 0;
6878 var time = +new Date, cur;
6879
6880 if ((hist.lastOp == opId ||
6881 hist.lastOrigin == change.origin && change.origin &&
6882 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
6883 change.origin.charAt(0) == "*")) &&
6884 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
6885 // Merge this change into the last event
6886 var last = lst(cur.changes);
6887 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
6888 // Optimized case for simple insertion -- don't want to add
6889 // new changesets for every character typed
6890 last.to = changeEnd(change);
6891 } else {
6892 // Add new sub-event
6893 cur.changes.push(historyChangeFromChange(doc, change));
6894 }
6895 } else {
6896 // Can not be merged, start a new event.
6897 var before = lst(hist.done);
6898 if (!before || !before.ranges)
6899 pushSelectionToHistory(doc.sel, hist.done);
6900 cur = {changes: [historyChangeFromChange(doc, change)],
6901 generation: hist.generation};
6902 hist.done.push(cur);
6903 while (hist.done.length > hist.undoDepth) {
6904 hist.done.shift();
6905 if (!hist.done[0].ranges) hist.done.shift();
6906 }
6907 }
6908 hist.done.push(selAfter);
6909 hist.generation = ++hist.maxGeneration;
6910 hist.lastModTime = hist.lastSelTime = time;
6911 hist.lastOp = hist.lastSelOp = opId;
6912 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6913
6914 if (!last) signal(doc, "historyAdded");
6915 }
6916
6917 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6918 var ch = origin.charAt(0);
6919 return ch == "*" ||
6920 ch == "+" &&
6921 prev.ranges.length == sel.ranges.length &&
6922 prev.somethingSelected() == sel.somethingSelected() &&
6923 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
6924 }
6925
6926 // Called whenever the selection changes, sets the new selection as
6927 // the pending selection in the history, and pushes the old pending
6928 // selection into the 'done' array when it was significantly
6929 // different (in number of selected ranges, emptiness, or time).
6930 function addSelectionToHistory(doc, sel, opId, options) {
6931 var hist = doc.history, origin = options && options.origin;
6932
6933 // A new event is started when the previous origin does not match
6934 // the current, or the origins don't allow matching. Origins
6935 // starting with * are always merged, those starting with + are
6936 // merged when similar and close together in time.
6937 if (opId == hist.lastSelOp ||
6938 (origin && hist.lastSelOrigin == origin &&
6939 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6940 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6941 hist.done[hist.done.length - 1] = sel;
6942 else
6943 pushSelectionToHistory(sel, hist.done);
6944
6945 hist.lastSelTime = +new Date;
6946 hist.lastSelOrigin = origin;
6947 hist.lastSelOp = opId;
6948 if (options && options.clearRedo !== false)
6949 clearSelectionEvents(hist.undone);
6950 }
6951
6952 function pushSelectionToHistory(sel, dest) {
6953 var top = lst(dest);
6954 if (!(top && top.ranges && top.equals(sel)))
6955 dest.push(sel);
6956 }
6957
6958 // Used to store marked span information in the history.
5028 function attachLocalSpans(doc, change, from, to) {
6959 function attachLocalSpans(doc, change, from, to) {
5029 var existing = change["spans_" + doc.id], n = 0;
6960 var existing = change["spans_" + doc.id], n = 0;
5030 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
6961 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
@@ -5034,51 +6965,8 b' window.CodeMirror = (function() {'
5034 });
6965 });
5035 }
6966 }
5036
6967
5037 function historyChangeFromChange(doc, change) {
6968 // When un/re-doing restores text containing marked spans, those
5038 var from = { line: change.from.line, ch: change.from.ch };
6969 // that have been explicitly cleared should not be restored.
5039 var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
5040 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
5041 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
5042 return histChange;
5043 }
5044
5045 function addToHistory(doc, change, selAfter, opId) {
5046 var hist = doc.history;
5047 hist.undone.length = 0;
5048 var time = +new Date, cur = lst(hist.done);
5049
5050 if (cur &&
5051 (hist.lastOp == opId ||
5052 hist.lastOrigin == change.origin && change.origin &&
5053 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
5054 change.origin.charAt(0) == "*"))) {
5055 // Merge this change into the last event
5056 var last = lst(cur.changes);
5057 if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
5058 // Optimized case for simple insertion -- don't want to add
5059 // new changesets for every character typed
5060 last.to = changeEnd(change);
5061 } else {
5062 // Add new sub-event
5063 cur.changes.push(historyChangeFromChange(doc, change));
5064 }
5065 cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
5066 } else {
5067 // Can not be merged, start a new event.
5068 cur = {changes: [historyChangeFromChange(doc, change)],
5069 generation: hist.generation,
5070 anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
5071 anchorAfter: selAfter.anchor, headAfter: selAfter.head};
5072 hist.done.push(cur);
5073 hist.generation = ++hist.maxGeneration;
5074 while (hist.done.length > hist.undoDepth)
5075 hist.done.shift();
5076 }
5077 hist.lastTime = time;
5078 hist.lastOp = opId;
5079 hist.lastOrigin = change.origin;
5080 }
5081
5082 function removeClearedSpans(spans) {
6970 function removeClearedSpans(spans) {
5083 if (!spans) return null;
6971 if (!spans) return null;
5084 for (var i = 0, out; i < spans.length; ++i) {
6972 for (var i = 0, out; i < spans.length; ++i) {
@@ -5088,6 +6976,7 b' window.CodeMirror = (function() {'
5088 return !out ? spans : out.length ? out : null;
6976 return !out ? spans : out.length ? out : null;
5089 }
6977 }
5090
6978
6979 // Retrieve and filter the old marked spans stored in a change event.
5091 function getOldSpans(doc, change) {
6980 function getOldSpans(doc, change) {
5092 var found = change["spans_" + doc.id];
6981 var found = change["spans_" + doc.id];
5093 if (!found) return null;
6982 if (!found) return null;
@@ -5098,11 +6987,15 b' window.CodeMirror = (function() {'
5098
6987
5099 // Used both to provide a JSON-safe object in .getHistory, and, when
6988 // Used both to provide a JSON-safe object in .getHistory, and, when
5100 // detaching a document, to split the history in two
6989 // detaching a document, to split the history in two
5101 function copyHistoryArray(events, newGroup) {
6990 function copyHistoryArray(events, newGroup, instantiateSel) {
5102 for (var i = 0, copy = []; i < events.length; ++i) {
6991 for (var i = 0, copy = []; i < events.length; ++i) {
5103 var event = events[i], changes = event.changes, newChanges = [];
6992 var event = events[i];
5104 copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
6993 if (event.ranges) {
5105 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
6994 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
6995 continue;
6996 }
6997 var changes = event.changes, newChanges = [];
6998 copy.push({changes: newChanges});
5106 for (var j = 0; j < changes.length; ++j) {
6999 for (var j = 0; j < changes.length; ++j) {
5107 var change = changes[j], m;
7000 var change = changes[j], m;
5108 newChanges.push({from: change.from, to: change.to, text: change.text});
7001 newChanges.push({from: change.from, to: change.to, text: change.text});
@@ -5119,7 +7012,7 b' window.CodeMirror = (function() {'
5119
7012
5120 // Rebasing/resetting history to deal with externally-sourced changes
7013 // Rebasing/resetting history to deal with externally-sourced changes
5121
7014
5122 function rebaseHistSel(pos, from, to, diff) {
7015 function rebaseHistSelSingle(pos, from, to, diff) {
5123 if (to < pos.line) {
7016 if (to < pos.line) {
5124 pos.line += diff;
7017 pos.line += diff;
5125 } else if (from < pos.line) {
7018 } else if (from < pos.line) {
@@ -5138,28 +7031,27 b' window.CodeMirror = (function() {'
5138 function rebaseHistArray(array, from, to, diff) {
7031 function rebaseHistArray(array, from, to, diff) {
5139 for (var i = 0; i < array.length; ++i) {
7032 for (var i = 0; i < array.length; ++i) {
5140 var sub = array[i], ok = true;
7033 var sub = array[i], ok = true;
7034 if (sub.ranges) {
7035 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7036 for (var j = 0; j < sub.ranges.length; j++) {
7037 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7038 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7039 }
7040 continue;
7041 }
5141 for (var j = 0; j < sub.changes.length; ++j) {
7042 for (var j = 0; j < sub.changes.length; ++j) {
5142 var cur = sub.changes[j];
7043 var cur = sub.changes[j];
5143 if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
5144 if (to < cur.from.line) {
7044 if (to < cur.from.line) {
5145 cur.from.line += diff;
7045 cur.from = Pos(cur.from.line + diff, cur.from.ch);
5146 cur.to.line += diff;
7046 cur.to = Pos(cur.to.line + diff, cur.to.ch);
5147 } else if (from <= cur.to.line) {
7047 } else if (from <= cur.to.line) {
5148 ok = false;
7048 ok = false;
5149 break;
7049 break;
5150 }
7050 }
5151 }
7051 }
5152 if (!sub.copied) {
5153 sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
5154 sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
5155 sub.copied = true;
5156 }
5157 if (!ok) {
7052 if (!ok) {
5158 array.splice(0, i + 1);
7053 array.splice(0, i + 1);
5159 i = 0;
7054 i = 0;
5160 } else {
5161 rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
5162 rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
5163 }
7055 }
5164 }
7056 }
5165 }
7057 }
@@ -5170,30 +7062,23 b' window.CodeMirror = (function() {'
5170 rebaseHistArray(hist.undone, from, to, diff);
7062 rebaseHistArray(hist.undone, from, to, diff);
5171 }
7063 }
5172
7064
5173 // EVENT OPERATORS
7065 // EVENT UTILITIES
5174
7066
5175 function stopMethod() {e_stop(this);}
7067 // Due to the fact that we still support jurassic IE versions, some
5176 // Ensure an event has a stop method.
7068 // compatibility wrappers are needed.
5177 function addStop(event) {
7069
5178 if (!event.stop) event.stop = stopMethod;
7070 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
5179 return event;
5180 }
5181
5182 function e_preventDefault(e) {
5183 if (e.preventDefault) e.preventDefault();
7071 if (e.preventDefault) e.preventDefault();
5184 else e.returnValue = false;
7072 else e.returnValue = false;
5185 }
7073 };
5186 function e_stopPropagation(e) {
7074 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
5187 if (e.stopPropagation) e.stopPropagation();
7075 if (e.stopPropagation) e.stopPropagation();
5188 else e.cancelBubble = true;
7076 else e.cancelBubble = true;
5189 }
7077 };
5190 function e_defaultPrevented(e) {
7078 function e_defaultPrevented(e) {
5191 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
7079 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
5192 }
7080 }
5193 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
7081 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
5194 CodeMirror.e_stop = e_stop;
5195 CodeMirror.e_preventDefault = e_preventDefault;
5196 CodeMirror.e_stopPropagation = e_stopPropagation;
5197
7082
5198 function e_target(e) {return e.target || e.srcElement;}
7083 function e_target(e) {return e.target || e.srcElement;}
5199 function e_button(e) {
7084 function e_button(e) {
@@ -5209,7 +7094,10 b' window.CodeMirror = (function() {'
5209
7094
5210 // EVENT HANDLING
7095 // EVENT HANDLING
5211
7096
5212 function on(emitter, type, f) {
7097 // Lightweight event framework. on/off also work on DOM nodes,
7098 // registering native DOM handlers.
7099
7100 var on = CodeMirror.on = function(emitter, type, f) {
5213 if (emitter.addEventListener)
7101 if (emitter.addEventListener)
5214 emitter.addEventListener(type, f, false);
7102 emitter.addEventListener(type, f, false);
5215 else if (emitter.attachEvent)
7103 else if (emitter.attachEvent)
@@ -5219,9 +7107,9 b' window.CodeMirror = (function() {'
5219 var arr = map[type] || (map[type] = []);
7107 var arr = map[type] || (map[type] = []);
5220 arr.push(f);
7108 arr.push(f);
5221 }
7109 }
5222 }
7110 };
5223
7111
5224 function off(emitter, type, f) {
7112 var off = CodeMirror.off = function(emitter, type, f) {
5225 if (emitter.removeEventListener)
7113 if (emitter.removeEventListener)
5226 emitter.removeEventListener(type, f, false);
7114 emitter.removeEventListener(type, f, false);
5227 else if (emitter.detachEvent)
7115 else if (emitter.detachEvent)
@@ -5232,40 +7120,61 b' window.CodeMirror = (function() {'
5232 for (var i = 0; i < arr.length; ++i)
7120 for (var i = 0; i < arr.length; ++i)
5233 if (arr[i] == f) { arr.splice(i, 1); break; }
7121 if (arr[i] == f) { arr.splice(i, 1); break; }
5234 }
7122 }
5235 }
7123 };
5236
7124
5237 function signal(emitter, type /*, values...*/) {
7125 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
5238 var arr = emitter._handlers && emitter._handlers[type];
7126 var arr = emitter._handlers && emitter._handlers[type];
5239 if (!arr) return;
7127 if (!arr) return;
5240 var args = Array.prototype.slice.call(arguments, 2);
7128 var args = Array.prototype.slice.call(arguments, 2);
5241 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
7129 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5242 }
7130 };
5243
7131
5244 var delayedCallbacks, delayedCallbackDepth = 0;
7132 var orphanDelayedCallbacks = null;
7133
7134 // Often, we want to signal events at a point where we are in the
7135 // middle of some work, but don't want the handler to start calling
7136 // other methods on the editor, which might be in an inconsistent
7137 // state or simply not expect any other events to happen.
7138 // signalLater looks whether there are any handlers, and schedules
7139 // them to be executed when the last operation ends, or, if no
7140 // operation is active, when a timeout fires.
5245 function signalLater(emitter, type /*, values...*/) {
7141 function signalLater(emitter, type /*, values...*/) {
5246 var arr = emitter._handlers && emitter._handlers[type];
7142 var arr = emitter._handlers && emitter._handlers[type];
5247 if (!arr) return;
7143 if (!arr) return;
5248 var args = Array.prototype.slice.call(arguments, 2);
7144 var args = Array.prototype.slice.call(arguments, 2), list;
5249 if (!delayedCallbacks) {
7145 if (operationGroup) {
5250 ++delayedCallbackDepth;
7146 list = operationGroup.delayedCallbacks;
5251 delayedCallbacks = [];
7147 } else if (orphanDelayedCallbacks) {
5252 setTimeout(fireDelayed, 0);
7148 list = orphanDelayedCallbacks;
7149 } else {
7150 list = orphanDelayedCallbacks = [];
7151 setTimeout(fireOrphanDelayed, 0);
5253 }
7152 }
5254 function bnd(f) {return function(){f.apply(null, args);};};
7153 function bnd(f) {return function(){f.apply(null, args);};};
5255 for (var i = 0; i < arr.length; ++i)
7154 for (var i = 0; i < arr.length; ++i)
5256 delayedCallbacks.push(bnd(arr[i]));
7155 list.push(bnd(arr[i]));
5257 }
7156 }
5258
7157
7158 function fireOrphanDelayed() {
7159 var delayed = orphanDelayedCallbacks;
7160 orphanDelayedCallbacks = null;
7161 for (var i = 0; i < delayed.length; ++i) delayed[i]();
7162 }
7163
7164 // The DOM events that CodeMirror handles can be overridden by
7165 // registering a (non-DOM) handler on the editor for the event name,
7166 // and preventDefault-ing the event in that handler.
5259 function signalDOMEvent(cm, e, override) {
7167 function signalDOMEvent(cm, e, override) {
5260 signal(cm, override || e.type, cm, e);
7168 signal(cm, override || e.type, cm, e);
5261 return e_defaultPrevented(e) || e.codemirrorIgnore;
7169 return e_defaultPrevented(e) || e.codemirrorIgnore;
5262 }
7170 }
5263
7171
5264 function fireDelayed() {
7172 function signalCursorActivity(cm) {
5265 --delayedCallbackDepth;
7173 var arr = cm._handlers && cm._handlers.cursorActivity;
5266 var delayed = delayedCallbacks;
7174 if (!arr) return;
5267 delayedCallbacks = null;
7175 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
5268 for (var i = 0; i < delayed.length; ++i) delayed[i]();
7176 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
7177 set.push(arr[i]);
5269 }
7178 }
5270
7179
5271 function hasHandler(emitter, type) {
7180 function hasHandler(emitter, type) {
@@ -5273,8 +7182,8 b' window.CodeMirror = (function() {'
5273 return arr && arr.length > 0;
7182 return arr && arr.length > 0;
5274 }
7183 }
5275
7184
5276 CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
7185 // Add on and off methods to a constructor's prototype, to make
5277
7186 // registering events on such objects more convenient.
5278 function eventMixin(ctor) {
7187 function eventMixin(ctor) {
5279 ctor.prototype.on = function(type, f) {on(this, type, f);};
7188 ctor.prototype.on = function(type, f) {on(this, type, f);};
5280 ctor.prototype.off = function(type, f) {off(this, type, f);};
7189 ctor.prototype.off = function(type, f) {off(this, type, f);};
@@ -5289,23 +7198,47 b' window.CodeMirror = (function() {'
5289 // handling this'.
7198 // handling this'.
5290 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
7199 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5291
7200
7201 // Reused option objects for setSelection & friends
7202 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
7203
5292 function Delayed() {this.id = null;}
7204 function Delayed() {this.id = null;}
5293 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
7205 Delayed.prototype.set = function(ms, f) {
7206 clearTimeout(this.id);
7207 this.id = setTimeout(f, ms);
7208 };
5294
7209
5295 // Counts the column offset in a string, taking tabs into account.
7210 // Counts the column offset in a string, taking tabs into account.
5296 // Used mostly to find indentation.
7211 // Used mostly to find indentation.
5297 function countColumn(string, end, tabSize, startIndex, startValue) {
7212 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
5298 if (end == null) {
7213 if (end == null) {
5299 end = string.search(/[^\s\u00a0]/);
7214 end = string.search(/[^\s\u00a0]/);
5300 if (end == -1) end = string.length;
7215 if (end == -1) end = string.length;
5301 }
7216 }
5302 for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
7217 for (var i = startIndex || 0, n = startValue || 0;;) {
5303 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
7218 var nextTab = string.indexOf("\t", i);
5304 else ++n;
7219 if (nextTab < 0 || nextTab >= end)
5305 }
7220 return n + (end - i);
5306 return n;
7221 n += nextTab - i;
5307 }
7222 n += tabSize - (n % tabSize);
5308 CodeMirror.countColumn = countColumn;
7223 i = nextTab + 1;
7224 }
7225 };
7226
7227 // The inverse of countColumn -- find the offset that corresponds to
7228 // a particular column.
7229 function findColumn(string, goal, tabSize) {
7230 for (var pos = 0, col = 0;;) {
7231 var nextTab = string.indexOf("\t", pos);
7232 if (nextTab == -1) nextTab = string.length;
7233 var skipped = nextTab - pos;
7234 if (nextTab == string.length || col + skipped >= goal)
7235 return pos + Math.min(skipped, goal - col);
7236 col += nextTab - pos;
7237 col += tabSize - (col % tabSize);
7238 pos = nextTab + 1;
7239 if (col >= goal) return pos;
7240 }
7241 }
5309
7242
5310 var spaceStrs = [""];
7243 var spaceStrs = [""];
5311 function spaceStr(n) {
7244 function spaceStr(n) {
@@ -5316,52 +7249,60 b' window.CodeMirror = (function() {'
5316
7249
5317 function lst(arr) { return arr[arr.length-1]; }
7250 function lst(arr) { return arr[arr.length-1]; }
5318
7251
5319 function selectInput(node) {
7252 var selectInput = function(node) { node.select(); };
5320 if (ios) { // Mobile Safari apparently has a bug where select() is broken.
7253 if (ios) // Mobile Safari apparently has a bug where select() is broken.
5321 node.selectionStart = 0;
7254 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
5322 node.selectionEnd = node.value.length;
7255 else if (ie) // Suppress mysterious IE10 errors
5323 } else {
7256 selectInput = function(node) { try { node.select(); } catch(_e) {} };
5324 // Suppress mysterious IE10 errors
7257
5325 try { node.select(); }
7258 function indexOf(array, elt) {
5326 catch(_e) {}
7259 for (var i = 0; i < array.length; ++i)
5327 }
7260 if (array[i] == elt) return i;
5328 }
5329
5330 function indexOf(collection, elt) {
5331 if (collection.indexOf) return collection.indexOf(elt);
5332 for (var i = 0, e = collection.length; i < e; ++i)
5333 if (collection[i] == elt) return i;
5334 return -1;
7261 return -1;
5335 }
7262 }
7263 if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); };
7264 function map(array, f) {
7265 var out = [];
7266 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
7267 return out;
7268 }
7269 if ([].map) map = function(array, f) { return array.map(f); };
5336
7270
5337 function createObj(base, props) {
7271 function createObj(base, props) {
5338 function Obj() {}
7272 var inst;
5339 Obj.prototype = base;
7273 if (Object.create) {
5340 var inst = new Obj();
7274 inst = Object.create(base);
7275 } else {
7276 var ctor = function() {};
7277 ctor.prototype = base;
7278 inst = new ctor();
7279 }
5341 if (props) copyObj(props, inst);
7280 if (props) copyObj(props, inst);
5342 return inst;
7281 return inst;
5343 }
7282 };
5344
7283
5345 function copyObj(obj, target) {
7284 function copyObj(obj, target, overwrite) {
5346 if (!target) target = {};
7285 if (!target) target = {};
5347 for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
7286 for (var prop in obj)
7287 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
7288 target[prop] = obj[prop];
5348 return target;
7289 return target;
5349 }
7290 }
5350
7291
5351 function emptyArray(size) {
5352 for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5353 return a;
5354 }
5355
5356 function bind(f) {
7292 function bind(f) {
5357 var args = Array.prototype.slice.call(arguments, 1);
7293 var args = Array.prototype.slice.call(arguments, 1);
5358 return function(){return f.apply(null, args);};
7294 return function(){return f.apply(null, args);};
5359 }
7295 }
5360
7296
5361 var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
7297 var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
5362 function isWordChar(ch) {
7298 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
5363 return /\w/.test(ch) || ch > "\x80" &&
7299 return /\w/.test(ch) || ch > "\x80" &&
5364 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
7300 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
7301 };
7302 function isWordChar(ch, helper) {
7303 if (!helper) return isWordCharBasic(ch);
7304 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
7305 return helper.test(ch);
5365 }
7306 }
5366
7307
5367 function isEmpty(obj) {
7308 function isEmpty(obj) {
@@ -5369,7 +7310,13 b' window.CodeMirror = (function() {'
5369 return true;
7310 return true;
5370 }
7311 }
5371
7312
5372 var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
7313 // Extending unicode characters. A series of a non-extending char +
7314 // any number of extending chars is treated as a single unit as far
7315 // as editing and measuring is concerned. This is not fully correct,
7316 // since some scripts/fonts/browsers also treat other configurations
7317 // of code points as a group.
7318 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
7319 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
5373
7320
5374 // DOM UTILITIES
7321 // DOM UTILITIES
5375
7322
@@ -5377,11 +7324,27 b' window.CodeMirror = (function() {'
5377 var e = document.createElement(tag);
7324 var e = document.createElement(tag);
5378 if (className) e.className = className;
7325 if (className) e.className = className;
5379 if (style) e.style.cssText = style;
7326 if (style) e.style.cssText = style;
5380 if (typeof content == "string") setTextContent(e, content);
7327 if (typeof content == "string") e.appendChild(document.createTextNode(content));
5381 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
7328 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5382 return e;
7329 return e;
5383 }
7330 }
5384
7331
7332 var range;
7333 if (document.createRange) range = function(node, start, end) {
7334 var r = document.createRange();
7335 r.setEnd(node, end);
7336 r.setStart(node, start);
7337 return r;
7338 };
7339 else range = function(node, start, end) {
7340 var r = document.body.createTextRange();
7341 r.moveToElementText(node.parentNode);
7342 r.collapse(true);
7343 r.moveEnd("character", end);
7344 r.moveStart("character", start);
7345 return r;
7346 };
7347
5385 function removeChildren(e) {
7348 function removeChildren(e) {
5386 for (var count = e.childNodes.length; count > 0; --count)
7349 for (var count = e.childNodes.length; count > 0; --count)
5387 e.removeChild(e.firstChild);
7350 e.removeChild(e.firstChild);
@@ -5392,17 +7355,72 b' window.CodeMirror = (function() {'
5392 return removeChildren(parent).appendChild(e);
7355 return removeChildren(parent).appendChild(e);
5393 }
7356 }
5394
7357
5395 function setTextContent(e, str) {
7358 function contains(parent, child) {
5396 if (ie_lt9) {
7359 if (parent.contains)
5397 e.innerHTML = "";
7360 return parent.contains(child);
5398 e.appendChild(document.createTextNode(str));
7361 while (child = child.parentNode)
5399 } else e.textContent = str;
7362 if (child == parent) return true;
5400 }
7363 }
5401
7364
5402 function getRect(node) {
7365 function activeElt() { return document.activeElement; }
5403 return node.getBoundingClientRect();
7366 // Older versions of IE throws unspecified error when touching
5404 }
7367 // document.activeElement in some cases (during loading, in iframe)
5405 CodeMirror.replaceGetRect = function(f) { getRect = f; };
7368 if (ie && ie_version < 11) activeElt = function() {
7369 try { return document.activeElement; }
7370 catch(e) { return document.body; }
7371 };
7372
7373 function classTest(cls) { return new RegExp("\\b" + cls + "\\b\\s*"); }
7374 function rmClass(node, cls) {
7375 var test = classTest(cls);
7376 if (test.test(node.className)) node.className = node.className.replace(test, "");
7377 }
7378 function addClass(node, cls) {
7379 if (!classTest(cls).test(node.className)) node.className += " " + cls;
7380 }
7381 function joinClasses(a, b) {
7382 var as = a.split(" ");
7383 for (var i = 0; i < as.length; i++)
7384 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
7385 return b;
7386 }
7387
7388 // WINDOW-WIDE EVENTS
7389
7390 // These must be handled carefully, because naively registering a
7391 // handler for each editor will cause the editors to never be
7392 // garbage collected.
7393
7394 function forEachCodeMirror(f) {
7395 if (!document.body.getElementsByClassName) return;
7396 var byClass = document.body.getElementsByClassName("CodeMirror");
7397 for (var i = 0; i < byClass.length; i++) {
7398 var cm = byClass[i].CodeMirror;
7399 if (cm) f(cm);
7400 }
7401 }
7402
7403 var globalsRegistered = false;
7404 function ensureGlobalHandlers() {
7405 if (globalsRegistered) return;
7406 registerGlobalHandlers();
7407 globalsRegistered = true;
7408 }
7409 function registerGlobalHandlers() {
7410 // When the window resizes, we need to refresh active editors.
7411 var resizeTimer;
7412 on(window, "resize", function() {
7413 if (resizeTimer == null) resizeTimer = setTimeout(function() {
7414 resizeTimer = null;
7415 knownScrollbarWidth = null;
7416 forEachCodeMirror(onResize);
7417 }, 100);
7418 });
7419 // When the window loses focus, we want to show the editor as blurred
7420 on(window, "blur", function() {
7421 forEachCodeMirror(onBlur);
7422 });
7423 }
5406
7424
5407 // FEATURE DETECTION
7425 // FEATURE DETECTION
5408
7426
@@ -5410,36 +7428,11 b' window.CodeMirror = (function() {'
5410 var dragAndDrop = function() {
7428 var dragAndDrop = function() {
5411 // There is *some* kind of drag-and-drop support in IE6-8, but I
7429 // There is *some* kind of drag-and-drop support in IE6-8, but I
5412 // couldn't get it to work yet.
7430 // couldn't get it to work yet.
5413 if (ie_lt9) return false;
7431 if (ie && ie_version < 9) return false;
5414 var div = elt('div');
7432 var div = elt('div');
5415 return "draggable" in div || "dragDrop" in div;
7433 return "draggable" in div || "dragDrop" in div;
5416 }();
7434 }();
5417
7435
5418 // For a reason I have yet to figure out, some browsers disallow
5419 // word wrapping between certain characters *only* if a new inline
5420 // element is started between them. This makes it hard to reliably
5421 // measure the position of things, since that requires inserting an
5422 // extra span. This terribly fragile set of tests matches the
5423 // character combinations that suffer from this phenomenon on the
5424 // various browsers.
5425 function spanAffectsWrapping() { return false; }
5426 if (gecko) // Only for "$'"
5427 spanAffectsWrapping = function(str, i) {
5428 return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
5429 };
5430 else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
5431 spanAffectsWrapping = function(str, i) {
5432 return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
5433 };
5434 else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
5435 spanAffectsWrapping = function(str, i) {
5436 if (i > 1 && str.charCodeAt(i - 1) == 45) {
5437 if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
5438 if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
5439 }
5440 return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
5441 };
5442
5443 var knownScrollbarWidth;
7436 var knownScrollbarWidth;
5444 function scrollbarWidth(measure) {
7437 function scrollbarWidth(measure) {
5445 if (knownScrollbarWidth != null) return knownScrollbarWidth;
7438 if (knownScrollbarWidth != null) return knownScrollbarWidth;
@@ -5456,15 +7449,26 b' window.CodeMirror = (function() {'
5456 var test = elt("span", "\u200b");
7449 var test = elt("span", "\u200b");
5457 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
7450 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5458 if (measure.firstChild.offsetHeight != 0)
7451 if (measure.firstChild.offsetHeight != 0)
5459 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
7452 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
5460 }
7453 }
5461 if (zwspSupported) return elt("span", "\u200b");
7454 if (zwspSupported) return elt("span", "\u200b");
5462 else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
7455 else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5463 }
7456 }
5464
7457
7458 // Feature-detect IE's crummy client rect reporting for bidi text
7459 var badBidiRects;
7460 function hasBadBidiRects(measure) {
7461 if (badBidiRects != null) return badBidiRects;
7462 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
7463 var r0 = range(txt, 0, 1).getBoundingClientRect();
7464 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
7465 var r1 = range(txt, 1, 2).getBoundingClientRect();
7466 return badBidiRects = (r1.right - r0.right < 3);
7467 }
7468
5465 // See if "".split is the broken IE version, if so, provide an
7469 // See if "".split is the broken IE version, if so, provide an
5466 // alternative way to split lines.
7470 // alternative way to split lines.
5467 var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
7471 var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5468 var pos = 0, result = [], l = string.length;
7472 var pos = 0, result = [], l = string.length;
5469 while (pos <= l) {
7473 while (pos <= l) {
5470 var nl = string.indexOf("\n", pos);
7474 var nl = string.indexOf("\n", pos);
@@ -5481,7 +7485,6 b' window.CodeMirror = (function() {'
5481 }
7485 }
5482 return result;
7486 return result;
5483 } : function(string){return string.split(/\r\n?|\n/);};
7487 } : function(string){return string.split(/\r\n?|\n/);};
5484 CodeMirror.splitLines = splitLines;
5485
7488
5486 var hasSelection = window.getSelection ? function(te) {
7489 var hasSelection = window.getSelection ? function(te) {
5487 try { return te.selectionStart != te.selectionEnd; }
7490 try { return te.selectionStart != te.selectionEnd; }
@@ -5497,22 +7500,31 b' window.CodeMirror = (function() {'
5497 var e = elt("div");
7500 var e = elt("div");
5498 if ("oncopy" in e) return true;
7501 if ("oncopy" in e) return true;
5499 e.setAttribute("oncopy", "return;");
7502 e.setAttribute("oncopy", "return;");
5500 return typeof e.oncopy == 'function';
7503 return typeof e.oncopy == "function";
5501 })();
7504 })();
5502
7505
5503 // KEY NAMING
7506 var badZoomedRects = null;
7507 function hasBadZoomedRects(measure) {
7508 if (badZoomedRects != null) return badZoomedRects;
7509 var node = removeChildrenAndAdd(measure, elt("span", "x"));
7510 var normal = node.getBoundingClientRect();
7511 var fromRange = range(node, 0, 1).getBoundingClientRect();
7512 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
7513 }
7514
7515 // KEY NAMES
5504
7516
5505 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
7517 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5506 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
7518 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5507 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
7519 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5508 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
7520 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
5509 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
7521 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5510 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
7522 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
5511 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
7523 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
5512 CodeMirror.keyNames = keyNames;
7524 CodeMirror.keyNames = keyNames;
5513 (function() {
7525 (function() {
5514 // Number keys
7526 // Number keys
5515 for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
7527 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
5516 // Alphabetic keys
7528 // Alphabetic keys
5517 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
7529 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5518 // Function keys
7530 // Function keys
@@ -5546,19 +7558,32 b' window.CodeMirror = (function() {'
5546
7558
5547 function lineStart(cm, lineN) {
7559 function lineStart(cm, lineN) {
5548 var line = getLine(cm.doc, lineN);
7560 var line = getLine(cm.doc, lineN);
5549 var visual = visualLine(cm.doc, line);
7561 var visual = visualLine(line);
5550 if (visual != line) lineN = lineNo(visual);
7562 if (visual != line) lineN = lineNo(visual);
5551 var order = getOrder(visual);
7563 var order = getOrder(visual);
5552 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
7564 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5553 return Pos(lineN, ch);
7565 return Pos(lineN, ch);
5554 }
7566 }
5555 function lineEnd(cm, lineN) {
7567 function lineEnd(cm, lineN) {
5556 var merged, line;
7568 var merged, line = getLine(cm.doc, lineN);
5557 while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
7569 while (merged = collapsedSpanAtEnd(line)) {
5558 lineN = merged.find().to.line;
7570 line = merged.find(1, true).line;
7571 lineN = null;
7572 }
5559 var order = getOrder(line);
7573 var order = getOrder(line);
5560 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
7574 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5561 return Pos(lineN, ch);
7575 return Pos(lineN == null ? lineNo(line) : lineN, ch);
7576 }
7577 function lineStartSmart(cm, pos) {
7578 var start = lineStart(cm, pos.line);
7579 var line = getLine(cm.doc, start.line);
7580 var order = getOrder(line);
7581 if (!order || order[0].level == 0) {
7582 var firstNonWS = Math.max(0, line.text.search(/\S/));
7583 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
7584 return Pos(start.line, inWS ? 0 : firstNonWS);
7585 }
7586 return start;
5562 }
7587 }
5563
7588
5564 function compareBidiLevel(order, a, b) {
7589 function compareBidiLevel(order, a, b) {
@@ -5569,38 +7594,37 b' window.CodeMirror = (function() {'
5569 }
7594 }
5570 var bidiOther;
7595 var bidiOther;
5571 function getBidiPartAt(order, pos) {
7596 function getBidiPartAt(order, pos) {
7597 bidiOther = null;
5572 for (var i = 0, found; i < order.length; ++i) {
7598 for (var i = 0, found; i < order.length; ++i) {
5573 var cur = order[i];
7599 var cur = order[i];
5574 if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
7600 if (cur.from < pos && cur.to > pos) return i;
5575 if (cur.from == pos || cur.to == pos) {
7601 if ((cur.from == pos || cur.to == pos)) {
5576 if (found == null) {
7602 if (found == null) {
5577 found = i;
7603 found = i;
5578 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
7604 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
5579 bidiOther = found;
7605 if (cur.from != cur.to) bidiOther = found;
5580 return i;
7606 return i;
5581 } else {
7607 } else {
5582 bidiOther = i;
7608 if (cur.from != cur.to) bidiOther = i;
5583 return found;
7609 return found;
5584 }
7610 }
5585 }
7611 }
5586 }
7612 }
5587 bidiOther = null;
5588 return found;
7613 return found;
5589 }
7614 }
5590
7615
5591 function moveInLine(line, pos, dir, byUnit) {
7616 function moveInLine(line, pos, dir, byUnit) {
5592 if (!byUnit) return pos + dir;
7617 if (!byUnit) return pos + dir;
5593 do pos += dir;
7618 do pos += dir;
5594 while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
7619 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
5595 return pos;
7620 return pos;
5596 }
7621 }
5597
7622
5598 // This is somewhat involved. It is needed in order to move
7623 // This is needed in order to move 'visually' through bi-directional
5599 // 'visually' through bi-directional text -- i.e., pressing left
7624 // text -- i.e., pressing left should make the cursor go left, even
5600 // should make the cursor go left, even when in RTL text. The
7625 // when in RTL text. The tricky part is the 'jumps', where RTL and
5601 // tricky part is the 'jumps', where RTL and LTR text touch each
7626 // LTR text touch each other. This often requires the cursor offset
5602 // other. This often requires the cursor offset to move more than
7627 // to move more than one unit, in order to visually move one unit.
5603 // one unit, in order to visually move one unit.
5604 function moveVisually(line, start, dir, byUnit) {
7628 function moveVisually(line, start, dir, byUnit) {
5605 var bidi = getOrder(line);
7629 var bidi = getOrder(line);
5606 if (!bidi) return moveLogically(line, start, dir, byUnit);
7630 if (!bidi) return moveLogically(line, start, dir, byUnit);
@@ -5626,7 +7650,7 b' window.CodeMirror = (function() {'
5626
7650
5627 function moveLogically(line, start, dir, byUnit) {
7651 function moveLogically(line, start, dir, byUnit) {
5628 var target = start + dir;
7652 var target = start + dir;
5629 if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
7653 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
5630 return target < 0 || target > line.text.length ? null : target;
7654 return target < 0 || target > line.text.length ? null : target;
5631 }
7655 }
5632
7656
@@ -5655,14 +7679,16 b' window.CodeMirror = (function() {'
5655 // objects) in the order in which they occur visually.
7679 // objects) in the order in which they occur visually.
5656 var bidiOrdering = (function() {
7680 var bidiOrdering = (function() {
5657 // Character types for codepoints 0 to 0xff
7681 // Character types for codepoints 0 to 0xff
5658 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
7682 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
5659 // Character types for codepoints 0x600 to 0x6ff
7683 // Character types for codepoints 0x600 to 0x6ff
5660 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
7684 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
5661 function charType(code) {
7685 function charType(code) {
5662 if (code <= 0xff) return lowTypes.charAt(code);
7686 if (code <= 0xf7) return lowTypes.charAt(code);
5663 else if (0x590 <= code && code <= 0x5f4) return "R";
7687 else if (0x590 <= code && code <= 0x5f4) return "R";
5664 else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
7688 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
5665 else if (0x700 <= code && code <= 0x8ac) return "r";
7689 else if (0x6ee <= code && code <= 0x8ac) return "r";
7690 else if (0x2000 <= code && code <= 0x200b) return "w";
7691 else if (code == 0x200c) return "b";
5666 else return "L";
7692 else return "L";
5667 }
7693 }
5668
7694
@@ -5671,6 +7697,11 b' window.CodeMirror = (function() {'
5671 // Browsers seem to always treat the boundaries of block elements as being L.
7697 // Browsers seem to always treat the boundaries of block elements as being L.
5672 var outerType = "L";
7698 var outerType = "L";
5673
7699
7700 function BidiSpan(level, from, to) {
7701 this.level = level;
7702 this.from = from; this.to = to;
7703 }
7704
5674 return function(str) {
7705 return function(str) {
5675 if (!bidiRE.test(str)) return false;
7706 if (!bidiRE.test(str)) return false;
5676 var len = str.length, types = [];
7707 var len = str.length, types = [];
@@ -5718,7 +7749,7 b' window.CodeMirror = (function() {'
5718 if (type == ",") types[i] = "N";
7749 if (type == ",") types[i] = "N";
5719 else if (type == "%") {
7750 else if (type == "%") {
5720 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
7751 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5721 var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
7752 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
5722 for (var j = i; j < end; ++j) types[j] = replace;
7753 for (var j = i; j < end; ++j) types[j] = replace;
5723 i = end - 1;
7754 i = end - 1;
5724 }
7755 }
@@ -5743,7 +7774,7 b' window.CodeMirror = (function() {'
5743 if (isNeutral.test(types[i])) {
7774 if (isNeutral.test(types[i])) {
5744 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
7775 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5745 var before = (i ? types[i-1] : outerType) == "L";
7776 var before = (i ? types[i-1] : outerType) == "L";
5746 var after = (end < len - 1 ? types[end] : outerType) == "L";
7777 var after = (end < len ? types[end] : outerType) == "L";
5747 var replace = before || after ? "L" : "R";
7778 var replace = before || after ? "L" : "R";
5748 for (var j = i; j < end; ++j) types[j] = replace;
7779 for (var j = i; j < end; ++j) types[j] = replace;
5749 i = end - 1;
7780 i = end - 1;
@@ -5760,32 +7791,32 b' window.CodeMirror = (function() {'
5760 if (countsAsLeft.test(types[i])) {
7791 if (countsAsLeft.test(types[i])) {
5761 var start = i;
7792 var start = i;
5762 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
7793 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5763 order.push({from: start, to: i, level: 0});
7794 order.push(new BidiSpan(0, start, i));
5764 } else {
7795 } else {
5765 var pos = i, at = order.length;
7796 var pos = i, at = order.length;
5766 for (++i; i < len && types[i] != "L"; ++i) {}
7797 for (++i; i < len && types[i] != "L"; ++i) {}
5767 for (var j = pos; j < i;) {
7798 for (var j = pos; j < i;) {
5768 if (countsAsNum.test(types[j])) {
7799 if (countsAsNum.test(types[j])) {
5769 if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
7800 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
5770 var nstart = j;
7801 var nstart = j;
5771 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
7802 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5772 order.splice(at, 0, {from: nstart, to: j, level: 2});
7803 order.splice(at, 0, new BidiSpan(2, nstart, j));
5773 pos = j;
7804 pos = j;
5774 } else ++j;
7805 } else ++j;
5775 }
7806 }
5776 if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
7807 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
5777 }
7808 }
5778 }
7809 }
5779 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
7810 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5780 order[0].from = m[0].length;
7811 order[0].from = m[0].length;
5781 order.unshift({from: 0, to: m[0].length, level: 0});
7812 order.unshift(new BidiSpan(0, 0, m[0].length));
5782 }
7813 }
5783 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
7814 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5784 lst(order).to -= m[0].length;
7815 lst(order).to -= m[0].length;
5785 order.push({from: len - m[0].length, to: len, level: 0});
7816 order.push(new BidiSpan(0, len - m[0].length, len));
5786 }
7817 }
5787 if (order[0].level != lst(order).level)
7818 if (order[0].level != lst(order).level)
5788 order.push({from: len, to: len, level: order[0].level});
7819 order.push(new BidiSpan(order[0].level, len, len));
5789
7820
5790 return order;
7821 return order;
5791 };
7822 };
@@ -5793,7 +7824,7 b' window.CodeMirror = (function() {'
5793
7824
5794 // THE END
7825 // THE END
5795
7826
5796 CodeMirror.version = "3.15.0";
7827 CodeMirror.version = "4.7.0";
5797
7828
5798 return CodeMirror;
7829 return CodeMirror;
5799 })();
7830 });
@@ -1,3 +1,16 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
1 CodeMirror.defineMode("apl", function() {
14 CodeMirror.defineMode("apl", function() {
2 var builtInOps = {
15 var builtInOps = {
3 ".": "innerProduct",
16 ".": "innerProduct",
@@ -158,3 +171,5 b' CodeMirror.defineMode("apl", function() '
158 });
171 });
159
172
160 CodeMirror.defineMIME("text/apl", "apl");
173 CodeMirror.defineMIME("text/apl", "apl");
174
175 });
@@ -1,3 +1,6 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
1 /*
4 /*
2 * =====================================================================================
5 * =====================================================================================
3 *
6 *
@@ -14,6 +17,16 b''
14 * =====================================================================================
17 * =====================================================================================
15 */
18 */
16
19
20 (function(mod) {
21 if (typeof exports == "object" && typeof module == "object") // CommonJS
22 mod(require("../../lib/codemirror"));
23 else if (typeof define == "function" && define.amd) // AMD
24 define(["../../lib/codemirror"], mod);
25 else // Plain browser env
26 mod(CodeMirror);
27 })(function(CodeMirror) {
28 "use strict";
29
17 CodeMirror.defineMode("asterisk", function() {
30 CodeMirror.defineMode("asterisk", function() {
18 var atoms = ["exten", "same", "include","ignorepat","switch"],
31 var atoms = ["exten", "same", "include","ignorepat","switch"],
19 dpcmd = ["#include","#exec"],
32 dpcmd = ["#include","#exec"],
@@ -181,3 +194,5 b' CodeMirror.defineMode("asterisk", functi'
181 });
194 });
182
195
183 CodeMirror.defineMIME("text/x-asterisk", "asterisk");
196 CodeMirror.defineMIME("text/x-asterisk", "asterisk");
197
198 });
@@ -1,3 +1,16 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
1 CodeMirror.defineMode("clike", function(config, parserConfig) {
14 CodeMirror.defineMode("clike", function(config, parserConfig) {
2 var indentUnit = config.indentUnit,
15 var indentUnit = config.indentUnit,
3 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
16 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
@@ -7,7 +20,8 b' CodeMirror.defineMode("clike", function('
7 blockKeywords = parserConfig.blockKeywords || {},
20 blockKeywords = parserConfig.blockKeywords || {},
8 atoms = parserConfig.atoms || {},
21 atoms = parserConfig.atoms || {},
9 hooks = parserConfig.hooks || {},
22 hooks = parserConfig.hooks || {},
10 multiLineStrings = parserConfig.multiLineStrings;
23 multiLineStrings = parserConfig.multiLineStrings,
24 indentStatements = parserConfig.indentStatements !== false;
11 var isOperatorChar = /[+\-*&%=<>!?|\/]/;
25 var isOperatorChar = /[+\-*&%=<>!?|\/]/;
12
26
13 var curPunc;
27 var curPunc;
@@ -44,7 +58,7 b' CodeMirror.defineMode("clike", function('
44 stream.eatWhile(isOperatorChar);
58 stream.eatWhile(isOperatorChar);
45 return "operator";
59 return "operator";
46 }
60 }
47 stream.eatWhile(/[\w\$_]/);
61 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
48 var cur = stream.current();
62 var cur = stream.current();
49 if (keywords.propertyIsEnumerable(cur)) {
63 if (keywords.propertyIsEnumerable(cur)) {
50 if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
64 if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
@@ -138,7 +152,9 b' CodeMirror.defineMode("clike", function('
138 while (ctx.type == "statement") ctx = popContext(state);
152 while (ctx.type == "statement") ctx = popContext(state);
139 }
153 }
140 else if (curPunc == ctx.type) popContext(state);
154 else if (curPunc == ctx.type) popContext(state);
141 else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
155 else if (indentStatements &&
156 (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') ||
157 (ctx.type == "statement" && curPunc == "newstatement")))
142 pushContext(state, stream.column(), "statement");
158 pushContext(state, stream.column(), "statement");
143 state.startOfLine = false;
159 state.startOfLine = false;
144 return style;
160 return style;
@@ -163,7 +179,6 b' CodeMirror.defineMode("clike", function('
163 };
179 };
164 });
180 });
165
181
166 (function() {
167 function words(str) {
182 function words(str) {
168 var obj = {}, words = str.split(" ");
183 var obj = {}, words = str.split(" ");
169 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
184 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
@@ -191,6 +206,30 b' CodeMirror.defineMode("clike", function('
191 return "meta";
206 return "meta";
192 }
207 }
193
208
209 function cpp11StringHook(stream, state) {
210 stream.backUp(1);
211 // Raw strings.
212 if (stream.match(/(R|u8R|uR|UR|LR)/)) {
213 var match = stream.match(/"([^\s\\()]{0,16})\(/);
214 if (!match) {
215 return false;
216 }
217 state.cpp11RawStringDelim = match[1];
218 state.tokenize = tokenRawString;
219 return tokenRawString(stream, state);
220 }
221 // Unicode strings/chars.
222 if (stream.match(/(u8|u|U|L)/)) {
223 if (stream.match(/["']/, /* eat */ false)) {
224 return "string";
225 }
226 return false;
227 }
228 // Ignore this hook.
229 stream.next();
230 return false;
231 }
232
194 // C#-style strings where "" escapes a quote.
233 // C#-style strings where "" escapes a quote.
195 function tokenAtString(stream, state) {
234 function tokenAtString(stream, state) {
196 var next;
235 var next;
@@ -203,28 +242,67 b' CodeMirror.defineMode("clike", function('
203 return "string";
242 return "string";
204 }
243 }
205
244
206 function mimes(ms, mode) {
245 // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
207 for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
246 // <delim> can be a string up to 16 characters long.
247 function tokenRawString(stream, state) {
248 // Escape characters that have special regex meanings.
249 var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
250 var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
251 if (match)
252 state.tokenize = null;
253 else
254 stream.skipToEnd();
255 return "string";
208 }
256 }
209
257
210 mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
258 function def(mimes, mode) {
259 if (typeof mimes == "string") mimes = [mimes];
260 var words = [];
261 function add(obj) {
262 if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
263 words.push(prop);
264 }
265 add(mode.keywords);
266 add(mode.builtin);
267 add(mode.atoms);
268 if (words.length) {
269 mode.helperType = mimes[0];
270 CodeMirror.registerHelper("hintWords", mimes[0], words);
271 }
272
273 for (var i = 0; i < mimes.length; ++i)
274 CodeMirror.defineMIME(mimes[i], mode);
275 }
276
277 def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
211 name: "clike",
278 name: "clike",
212 keywords: words(cKeywords),
279 keywords: words(cKeywords),
213 blockKeywords: words("case do else for if switch while struct"),
280 blockKeywords: words("case do else for if switch while struct"),
214 atoms: words("null"),
281 atoms: words("null"),
215 hooks: {"#": cppHook}
282 hooks: {"#": cppHook},
283 modeProps: {fold: ["brace", "include"]}
216 });
284 });
217 mimes(["text/x-c++src", "text/x-c++hdr"], {
285
286 def(["text/x-c++src", "text/x-c++hdr"], {
218 name: "clike",
287 name: "clike",
219 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
288 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
220 "static_cast typeid catch operator template typename class friend private " +
289 "static_cast typeid catch operator template typename class friend private " +
221 "this using const_cast inline public throw virtual delete mutable protected " +
290 "this using const_cast inline public throw virtual delete mutable protected " +
222 "wchar_t"),
291 "wchar_t alignas alignof constexpr decltype nullptr noexcept thread_local final " +
292 "static_assert override"),
223 blockKeywords: words("catch class do else finally for if struct switch try while"),
293 blockKeywords: words("catch class do else finally for if struct switch try while"),
224 atoms: words("true false null"),
294 atoms: words("true false null"),
225 hooks: {"#": cppHook}
295 hooks: {
296 "#": cppHook,
297 "u": cpp11StringHook,
298 "U": cpp11StringHook,
299 "L": cpp11StringHook,
300 "R": cpp11StringHook
301 },
302 modeProps: {fold: ["brace", "include"]}
226 });
303 });
227 CodeMirror.defineMIME("text/x-java", {
304
305 def("text/x-java", {
228 name: "clike",
306 name: "clike",
229 keywords: words("abstract assert boolean break byte case catch char class const continue default " +
307 keywords: words("abstract assert boolean break byte case catch char class const continue default " +
230 "do double else enum extends final finally float for goto if implements import " +
308 "do double else enum extends final finally float for goto if implements import " +
@@ -238,9 +316,11 b' CodeMirror.defineMode("clike", function('
238 stream.eatWhile(/[\w\$_]/);
316 stream.eatWhile(/[\w\$_]/);
239 return "meta";
317 return "meta";
240 }
318 }
241 }
319 },
320 modeProps: {fold: ["brace", "import"]}
242 });
321 });
243 CodeMirror.defineMIME("text/x-csharp", {
322
323 def("text/x-csharp", {
244 name: "clike",
324 name: "clike",
245 keywords: words("abstract as base break case catch checked class const continue" +
325 keywords: words("abstract as base break case catch checked class const continue" +
246 " default delegate do else enum event explicit extern finally fixed for" +
326 " default delegate do else enum event explicit extern finally fixed for" +
@@ -266,7 +346,20 b' CodeMirror.defineMode("clike", function('
266 }
346 }
267 }
347 }
268 });
348 });
269 CodeMirror.defineMIME("text/x-scala", {
349
350 function tokenTripleString(stream, state) {
351 var escaped = false;
352 while (!stream.eol()) {
353 if (!escaped && stream.match('"""')) {
354 state.tokenize = null;
355 break;
356 }
357 escaped = stream.next() != "\\" && !escaped;
358 }
359 return "string";
360 }
361
362 def("text/x-scala", {
270 name: "clike",
363 name: "clike",
271 keywords: words(
364 keywords: words(
272
365
@@ -291,19 +384,25 b' CodeMirror.defineMode("clike", function('
291 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
384 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
292 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
385 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
293 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
386 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
294
295
296 ),
387 ),
388 multiLineStrings: true,
297 blockKeywords: words("catch class do else finally for forSome if match switch try while"),
389 blockKeywords: words("catch class do else finally for forSome if match switch try while"),
298 atoms: words("true false null"),
390 atoms: words("true false null"),
391 indentStatements: false,
299 hooks: {
392 hooks: {
300 "@": function(stream) {
393 "@": function(stream) {
301 stream.eatWhile(/[\w\$_]/);
394 stream.eatWhile(/[\w\$_]/);
302 return "meta";
395 return "meta";
396 },
397 '"': function(stream, state) {
398 if (!stream.match('""')) return false;
399 state.tokenize = tokenTripleString;
400 return state.tokenize(stream, state);
303 }
401 }
304 }
402 }
305 });
403 });
306 mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
404
405 def(["x-shader/x-vertex", "x-shader/x-fragment"], {
307 name: "clike",
406 name: "clike",
308 keywords: words("float int bool void " +
407 keywords: words("float int bool void " +
309 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
408 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
@@ -357,6 +456,19 b' CodeMirror.defineMode("clike", function('
357 "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
456 "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
358 "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
457 "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
359 "gl_MaxDrawBuffers"),
458 "gl_MaxDrawBuffers"),
360 hooks: {"#": cppHook}
459 hooks: {"#": cppHook},
460 modeProps: {fold: ["brace", "include"]}
361 });
461 });
362 }());
462
463 def("text/x-nesc", {
464 name: "clike",
465 keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
466 "implementation includes interface module new norace nx_struct nx_union post provides " +
467 "signal task uses abstract extends"),
468 blockKeywords: words("case do else for if switch while struct"),
469 atoms: words("null"),
470 hooks: {"#": cppHook},
471 modeProps: {fold: ["brace", "include"]}
472 });
473
474 });
@@ -1,11 +1,26 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
1 /**
4 /**
2 * Author: Hans Engel
5 * Author: Hans Engel
3 * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
6 * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
4 */
7 */
5 CodeMirror.defineMode("clojure", function () {
8
9 (function(mod) {
10 if (typeof exports == "object" && typeof module == "object") // CommonJS
11 mod(require("../../lib/codemirror"));
12 else if (typeof define == "function" && define.amd) // AMD
13 define(["../../lib/codemirror"], mod);
14 else // Plain browser env
15 mod(CodeMirror);
16 })(function(CodeMirror) {
17 "use strict";
18
19 CodeMirror.defineMode("clojure", function (options) {
6 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
20 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
7 ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword";
21 ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
8 var INDENT_WORD_SKIP = 2;
22 var INDENT_WORD_SKIP = options.indentUnit || 2;
23 var NORMAL_INDENT_UNIT = options.indentUnit || 2;
9
24
10 function makeKeywords(str) {
25 function makeKeywords(str) {
11 var obj = {}, words = str.split(" ");
26 var obj = {}, words = str.split(" ");
@@ -44,7 +59,7 b' CodeMirror.defineMode("clojure", functio'
44 sign: /[+-]/,
59 sign: /[+-]/,
45 exponent: /e/i,
60 exponent: /e/i,
46 keyword_char: /[^\s\(\[\;\)\]]/,
61 keyword_char: /[^\s\(\[\;\)\]]/,
47 symbol: /[\w*+!\-\._?:\/]/
62 symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/
48 };
63 };
49
64
50 function stateStack(indent, type, prev) { // represents a state stack object
65 function stateStack(indent, type, prev) { // represents a state stack object
@@ -99,7 +114,7 b' CodeMirror.defineMode("clojure", functio'
99 var first = stream.next();
114 var first = stream.next();
100 // Read special literals: backspace, newline, space, return.
115 // Read special literals: backspace, newline, space, return.
101 // Just read all lowercase letters.
116 // Just read all lowercase letters.
102 if (first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
117 if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
103 return;
118 return;
104 }
119 }
105 // Read unicode character: \u1000 \uA0a1
120 // Read unicode character: \u1000 \uA0a1
@@ -179,8 +194,8 b' CodeMirror.defineMode("clojure", functio'
179 stream.eatSpace();
194 stream.eatSpace();
180 if (stream.eol() || stream.peek() == ";") {
195 if (stream.eol() || stream.peek() == ";") {
181 // nothing significant after
196 // nothing significant after
182 // we restart indentation 1 space after
197 // we restart indentation the user defined spaces after
183 pushStack(state, indentTemp + 1, ch);
198 pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
184 } else {
199 } else {
185 pushStack(state, indentTemp + stream.current().length, ch); // else we match
200 pushStack(state, indentTemp + stream.current().length, ch); // else we match
186 }
201 }
@@ -205,7 +220,9 b' CodeMirror.defineMode("clojure", functio'
205 returnType = BUILTIN;
220 returnType = BUILTIN;
206 } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
221 } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
207 returnType = ATOM;
222 returnType = ATOM;
208 } else returnType = null;
223 } else {
224 returnType = VAR;
225 }
209 }
226 }
210 }
227 }
211
228
@@ -222,3 +239,5 b' CodeMirror.defineMode("clojure", functio'
222 });
239 });
223
240
224 CodeMirror.defineMIME("text/x-clojure", "clojure");
241 CodeMirror.defineMIME("text/x-clojure", "clojure");
242
243 });
@@ -1,7 +1,20 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
1 /**
4 /**
2 * Author: Gautam Mehta
5 * Author: Gautam Mehta
3 * Branched from CodeMirror's Scheme mode
6 * Branched from CodeMirror's Scheme mode
4 */
7 */
8 (function(mod) {
9 if (typeof exports == "object" && typeof module == "object") // CommonJS
10 mod(require("../../lib/codemirror"));
11 else if (typeof define == "function" && define.amd) // AMD
12 define(["../../lib/codemirror"], mod);
13 else // Plain browser env
14 mod(CodeMirror);
15 })(function(CodeMirror) {
16 "use strict";
17
5 CodeMirror.defineMode("cobol", function () {
18 CodeMirror.defineMode("cobol", function () {
6 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string",
19 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string",
7 ATOM = "atom", NUMBER = "number", KEYWORD = "keyword", MODTAG = "header",
20 ATOM = "atom", NUMBER = "number", KEYWORD = "keyword", MODTAG = "header",
@@ -238,3 +251,5 b' CodeMirror.defineMode("cobol", function '
238 });
251 });
239
252
240 CodeMirror.defineMIME("text/x-cobol", "cobol");
253 CodeMirror.defineMIME("text/x-cobol", "cobol");
254
255 });
This diff has been collapsed as it changes many lines, (619 lines changed) Show them Hide them
@@ -1,348 +1,369 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
1 /**
4 /**
2 * Link to the project's GitHub page:
5 * Link to the project's GitHub page:
3 * https://github.com/pickhardt/coffeescript-codemirror-mode
6 * https://github.com/pickhardt/coffeescript-codemirror-mode
4 */
7 */
5 CodeMirror.defineMode('coffeescript', function(conf) {
8 (function(mod) {
6 var ERRORCLASS = 'error';
9 if (typeof exports == "object" && typeof module == "object") // CommonJS
10 mod(require("../../lib/codemirror"));
11 else if (typeof define == "function" && define.amd) // AMD
12 define(["../../lib/codemirror"], mod);
13 else // Plain browser env
14 mod(CodeMirror);
15 })(function(CodeMirror) {
16 "use strict";
7
17
8 function wordRegexp(words) {
18 CodeMirror.defineMode("coffeescript", function(conf) {
9 return new RegExp("^((" + words.join(")|(") + "))\\b");
19 var ERRORCLASS = "error";
10 }
20
21 function wordRegexp(words) {
22 return new RegExp("^((" + words.join(")|(") + "))\\b");
23 }
11
24
12 var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
13 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\},:`=;\\.]');
26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
14 var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
15 var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
28 var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;
16 var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
17 var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
18 var properties = new RegExp("^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*");
19
29
20 var wordOperators = wordRegexp(['and', 'or', 'not',
30 var wordOperators = wordRegexp(["and", "or", "not",
21 'is', 'isnt', 'in',
31 "is", "isnt", "in",
22 'instanceof', 'typeof']);
32 "instanceof", "typeof"]);
23 var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
24 'switch', 'try', 'catch', 'finally', 'class'];
34 "switch", "try", "catch", "finally", "class"];
25 var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
35 var commonKeywords = ["break", "by", "continue", "debugger", "delete",
26 'do', 'in', 'of', 'new', 'return', 'then',
36 "do", "in", "of", "new", "return", "then",
27 'this', 'throw', 'when', 'until'];
37 "this", "@", "throw", "when", "until", "extends"];
28
38
29 var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
30
40
31 indentKeywords = wordRegexp(indentKeywords);
41 indentKeywords = wordRegexp(indentKeywords);
32
42
33
43
34 var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
44 var stringPrefixes = /^('{3}|\"{3}|['\"])/;
35 var regexPrefixes = new RegExp("^(/{3}|/)");
45 var regexPrefixes = /^(\/{3}|\/)/;
36 var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
46 var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
37 var constants = wordRegexp(commonConstants);
47 var constants = wordRegexp(commonConstants);
38
48
39 // Tokenizers
49 // Tokenizers
40 function tokenBase(stream, state) {
50 function tokenBase(stream, state) {
41 // Handle scope changes
51 // Handle scope changes
42 if (stream.sol()) {
52 if (stream.sol()) {
43 var scopeOffset = state.scopes[0].offset;
53 if (state.scope.align === null) state.scope.align = false;
44 if (stream.eatSpace()) {
54 var scopeOffset = state.scope.offset;
45 var lineOffset = stream.indentation();
55 if (stream.eatSpace()) {
46 if (lineOffset > scopeOffset) {
56 var lineOffset = stream.indentation();
47 return 'indent';
57 if (lineOffset > scopeOffset && state.scope.type == "coffee") {
48 } else if (lineOffset < scopeOffset) {
58 return "indent";
49 return 'dedent';
59 } else if (lineOffset < scopeOffset) {
50 }
60 return "dedent";
51 return null;
52 } else {
53 if (scopeOffset > 0) {
54 dedent(stream, state);
55 }
56 }
57 }
61 }
58 if (stream.eatSpace()) {
62 return null;
59 return null;
63 } else {
64 if (scopeOffset > 0) {
65 dedent(stream, state);
60 }
66 }
67 }
68 }
69 if (stream.eatSpace()) {
70 return null;
71 }
61
72
62 var ch = stream.peek();
73 var ch = stream.peek();
63
74
64 // Handle docco title comment (single line)
75 // Handle docco title comment (single line)
65 if (stream.match("####")) {
76 if (stream.match("####")) {
66 stream.skipToEnd();
77 stream.skipToEnd();
67 return 'comment';
78 return "comment";
68 }
79 }
69
80
70 // Handle multi line comments
81 // Handle multi line comments
71 if (stream.match("###")) {
82 if (stream.match("###")) {
72 state.tokenize = longComment;
83 state.tokenize = longComment;
73 return state.tokenize(stream, state);
84 return state.tokenize(stream, state);
74 }
85 }
75
86
76 // Single line comment
87 // Single line comment
77 if (ch === '#') {
88 if (ch === "#") {
78 stream.skipToEnd();
89 stream.skipToEnd();
79 return 'comment';
90 return "comment";
80 }
91 }
81
92
82 // Handle number literals
93 // Handle number literals
83 if (stream.match(/^-?[0-9\.]/, false)) {
94 if (stream.match(/^-?[0-9\.]/, false)) {
84 var floatLiteral = false;
95 var floatLiteral = false;
85 // Floats
96 // Floats
86 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
97 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
87 floatLiteral = true;
98 floatLiteral = true;
88 }
99 }
89 if (stream.match(/^-?\d+\.\d*/)) {
100 if (stream.match(/^-?\d+\.\d*/)) {
90 floatLiteral = true;
101 floatLiteral = true;
91 }
102 }
92 if (stream.match(/^-?\.\d+/)) {
103 if (stream.match(/^-?\.\d+/)) {
93 floatLiteral = true;
104 floatLiteral = true;
94 }
105 }
95
106
96 if (floatLiteral) {
107 if (floatLiteral) {
97 // prevent from getting extra . on 1..
108 // prevent from getting extra . on 1..
98 if (stream.peek() == "."){
109 if (stream.peek() == "."){
99 stream.backUp(1);
110 stream.backUp(1);
100 }
101 return 'number';
102 }
103 // Integers
104 var intLiteral = false;
105 // Hex
106 if (stream.match(/^-?0x[0-9a-f]+/i)) {
107 intLiteral = true;
108 }
109 // Decimal
110 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
111 intLiteral = true;
112 }
113 // Zero by itself with no other piece of number.
114 if (stream.match(/^-?0(?![\dx])/i)) {
115 intLiteral = true;
116 }
117 if (intLiteral) {
118 return 'number';
119 }
120 }
121
122 // Handle strings
123 if (stream.match(stringPrefixes)) {
124 state.tokenize = tokenFactory(stream.current(), 'string');
125 return state.tokenize(stream, state);
126 }
111 }
127 // Handle regex literals
112 return "number";
128 if (stream.match(regexPrefixes)) {
113 }
129 if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
114 // Integers
130 state.tokenize = tokenFactory(stream.current(), 'string-2');
115 var intLiteral = false;
131 return state.tokenize(stream, state);
116 // Hex
132 } else {
117 if (stream.match(/^-?0x[0-9a-f]+/i)) {
133 stream.backUp(1);
118 intLiteral = true;
134 }
119 }
135 }
120 // Decimal
121 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
122 intLiteral = true;
123 }
124 // Zero by itself with no other piece of number.
125 if (stream.match(/^-?0(?![\dx])/i)) {
126 intLiteral = true;
127 }
128 if (intLiteral) {
129 return "number";
130 }
131 }
136
132
137 // Handle operators and delimiters
133 // Handle strings
138 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
134 if (stream.match(stringPrefixes)) {
139 return 'punctuation';
135 state.tokenize = tokenFactory(stream.current(), false, "string");
140 }
136 return state.tokenize(stream, state);
141 if (stream.match(doubleOperators)
137 }
142 || stream.match(singleOperators)
138 // Handle regex literals
143 || stream.match(wordOperators)) {
139 if (stream.match(regexPrefixes)) {
144 return 'operator';
140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
145 }
141 state.tokenize = tokenFactory(stream.current(), true, "string-2");
146 if (stream.match(singleDelimiters)) {
142 return state.tokenize(stream, state);
147 return 'punctuation';
143 } else {
148 }
144 stream.backUp(1);
145 }
146 }
149
147
150 if (stream.match(constants)) {
148 // Handle operators and delimiters
151 return 'atom';
149 if (stream.match(operators) || stream.match(wordOperators)) {
152 }
150 return "operator";
153
151 }
154 if (stream.match(keywords)) {
152 if (stream.match(delimiters)) {
155 return 'keyword';
153 return "punctuation";
156 }
154 }
157
155
158 if (stream.match(identifiers)) {
156 if (stream.match(constants)) {
159 return 'variable';
157 return "atom";
160 }
158 }
161
159
162 if (stream.match(properties)) {
160 if (stream.match(keywords)) {
163 return 'property';
161 return "keyword";
164 }
162 }
165
163
166 // Handle non-detected items
164 if (stream.match(identifiers)) {
167 stream.next();
165 return "variable";
168 return ERRORCLASS;
166 }
167
168 if (stream.match(properties)) {
169 return "property";
169 }
170 }
170
171
171 function tokenFactory(delimiter, outclass) {
172 // Handle non-detected items
172 var singleline = delimiter.length == 1;
173 stream.next();
173 return function(stream, state) {
174 return ERRORCLASS;
174 while (!stream.eol()) {
175 }
175 stream.eatWhile(/[^'"\/\\]/);
176
176 if (stream.eat('\\')) {
177 function tokenFactory(delimiter, singleline, outclass) {
177 stream.next();
178 return function(stream, state) {
178 if (singleline && stream.eol()) {
179 while (!stream.eol()) {
179 return outclass;
180 stream.eatWhile(/[^'"\/\\]/);
180 }
181 if (stream.eat("\\")) {
181 } else if (stream.match(delimiter)) {
182 stream.next();
182 state.tokenize = tokenBase;
183 if (singleline && stream.eol()) {
183 return outclass;
184 } else {
185 stream.eat(/['"\/]/);
186 }
187 }
188 if (singleline) {
189 if (conf.mode.singleLineStringErrors) {
190 outclass = ERRORCLASS;
191 } else {
192 state.tokenize = tokenBase;
193 }
194 }
195 return outclass;
184 return outclass;
196 };
185 }
197 }
186 } else if (stream.match(delimiter)) {
187 state.tokenize = tokenBase;
188 return outclass;
189 } else {
190 stream.eat(/['"\/]/);
191 }
192 }
193 if (singleline) {
194 if (conf.mode.singleLineStringErrors) {
195 outclass = ERRORCLASS;
196 } else {
197 state.tokenize = tokenBase;
198 }
199 }
200 return outclass;
201 };
202 }
198
203
199 function longComment(stream, state) {
204 function longComment(stream, state) {
200 while (!stream.eol()) {
205 while (!stream.eol()) {
201 stream.eatWhile(/[^#]/);
206 stream.eatWhile(/[^#]/);
202 if (stream.match("###")) {
207 if (stream.match("###")) {
203 state.tokenize = tokenBase;
208 state.tokenize = tokenBase;
204 break;
209 break;
205 }
210 }
206 stream.eatWhile("#");
211 stream.eatWhile("#");
207 }
208 return "comment";
209 }
212 }
213 return "comment";
214 }
210
215
211 function indent(stream, state, type) {
216 function indent(stream, state, type) {
212 type = type || 'coffee';
217 type = type || "coffee";
213 var indentUnit = 0;
218 var offset = 0, align = false, alignOffset = null;
214 if (type === 'coffee') {
219 for (var scope = state.scope; scope; scope = scope.prev) {
215 for (var i = 0; i < state.scopes.length; i++) {
220 if (scope.type === "coffee" || scope.type == "}") {
216 if (state.scopes[i].type === 'coffee') {
221 offset = scope.offset + conf.indentUnit;
217 indentUnit = state.scopes[i].offset + conf.indentUnit;
222 break;
218 break;
223 }
219 }
220 }
221 } else {
222 indentUnit = stream.column() + stream.current().length;
223 }
224 state.scopes.unshift({
225 offset: indentUnit,
226 type: type
227 });
228 }
224 }
225 if (type !== "coffee") {
226 align = null;
227 alignOffset = stream.column() + stream.current().length;
228 } else if (state.scope.align) {
229 state.scope.align = false;
230 }
231 state.scope = {
232 offset: offset,
233 type: type,
234 prev: state.scope,
235 align: align,
236 alignOffset: alignOffset
237 };
238 }
229
239
230 function dedent(stream, state) {
240 function dedent(stream, state) {
231 if (state.scopes.length == 1) return;
241 if (!state.scope.prev) return;
232 if (state.scopes[0].type === 'coffee') {
242 if (state.scope.type === "coffee") {
233 var _indent = stream.indentation();
243 var _indent = stream.indentation();
234 var _indent_index = -1;
244 var matched = false;
235 for (var i = 0; i < state.scopes.length; ++i) {
245 for (var scope = state.scope; scope; scope = scope.prev) {
236 if (_indent === state.scopes[i].offset) {
246 if (_indent === scope.offset) {
237 _indent_index = i;
247 matched = true;
238 break;
248 break;
239 }
240 }
241 if (_indent_index === -1) {
242 return true;
243 }
244 while (state.scopes[0].offset !== _indent) {
245 state.scopes.shift();
246 }
247 return false;
248 } else {
249 state.scopes.shift();
250 return false;
251 }
249 }
250 }
251 if (!matched) {
252 return true;
253 }
254 while (state.scope.prev && state.scope.offset !== _indent) {
255 state.scope = state.scope.prev;
256 }
257 return false;
258 } else {
259 state.scope = state.scope.prev;
260 return false;
261 }
262 }
263
264 function tokenLexer(stream, state) {
265 var style = state.tokenize(stream, state);
266 var current = stream.current();
267
268 // Handle "." connected identifiers
269 if (current === ".") {
270 style = state.tokenize(stream, state);
271 current = stream.current();
272 if (/^\.[\w$]+$/.test(current)) {
273 return "variable";
274 } else {
275 return ERRORCLASS;
276 }
252 }
277 }
253
278
254 function tokenLexer(stream, state) {
279 // Handle scope changes.
255 var style = state.tokenize(stream, state);
280 if (current === "return") {
256 var current = stream.current();
281 state.dedent = true;
257
282 }
258 // Handle '.' connected identifiers
283 if (((current === "->" || current === "=>") &&
259 if (current === '.') {
284 !state.lambda &&
260 style = state.tokenize(stream, state);
285 !stream.peek())
261 current = stream.current();
286 || style === "indent") {
262 if (style === 'variable') {
287 indent(stream, state);
263 return 'variable';
288 }
264 } else {
289 var delimiter_index = "[({".indexOf(current);
265 return ERRORCLASS;
290 if (delimiter_index !== -1) {
266 }
291 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
267 }
292 }
268
293 if (indentKeywords.exec(current)){
269 // Handle scope changes.
294 indent(stream, state);
270 if (current === 'return') {
295 }
271 state.dedent += 1;
296 if (current == "then"){
272 }
297 dedent(stream, state);
273 if (((current === '->' || current === '=>') &&
298 }
274 !state.lambda &&
275 state.scopes[0].type == 'coffee' &&
276 stream.peek() === '')
277 || style === 'indent') {
278 indent(stream, state);
279 }
280 var delimiter_index = '[({'.indexOf(current);
281 if (delimiter_index !== -1) {
282 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
283 }
284 if (indentKeywords.exec(current)){
285 indent(stream, state);
286 }
287 if (current == 'then'){
288 dedent(stream, state);
289 }
290
299
291
300
292 if (style === 'dedent') {
301 if (style === "dedent") {
293 if (dedent(stream, state)) {
302 if (dedent(stream, state)) {
294 return ERRORCLASS;
303 return ERRORCLASS;
295 }
304 }
296 }
305 }
297 delimiter_index = '])}'.indexOf(current);
306 delimiter_index = "])}".indexOf(current);
298 if (delimiter_index !== -1) {
307 if (delimiter_index !== -1) {
299 if (dedent(stream, state)) {
308 while (state.scope.type == "coffee" && state.scope.prev)
300 return ERRORCLASS;
309 state.scope = state.scope.prev;
301 }
310 if (state.scope.type == current)
302 }
311 state.scope = state.scope.prev;
303 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
312 }
304 if (state.scopes.length > 1) state.scopes.shift();
313 if (state.dedent && stream.eol()) {
305 state.dedent -= 1;
314 if (state.scope.type == "coffee" && state.scope.prev)
306 }
315 state.scope = state.scope.prev;
307
316 state.dedent = false;
308 return style;
309 }
317 }
310
318
311 var external = {
319 return style;
312 startState: function(basecolumn) {
320 }
313 return {
314 tokenize: tokenBase,
315 scopes: [{offset:basecolumn || 0, type:'coffee'}],
316 lastToken: null,
317 lambda: false,
318 dedent: 0
319 };
320 },
321
321
322 token: function(stream, state) {
322 var external = {
323 var style = tokenLexer(stream, state);
323 startState: function(basecolumn) {
324 return {
325 tokenize: tokenBase,
326 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
327 lastToken: null,
328 lambda: false,
329 dedent: 0
330 };
331 },
324
332
325 state.lastToken = {style:style, content: stream.current()};
333 token: function(stream, state) {
334 var fillAlign = state.scope.align === null && state.scope;
335 if (fillAlign && stream.sol()) fillAlign.align = false;
336
337 var style = tokenLexer(stream, state);
338 if (fillAlign && style && style != "comment") fillAlign.align = true;
339
340 state.lastToken = {style:style, content: stream.current()};
326
341
327 if (stream.eol() && stream.lambda) {
342 if (stream.eol() && stream.lambda) {
328 state.lambda = false;
343 state.lambda = false;
329 }
344 }
330
345
331 return style;
346 return style;
332 },
347 },
333
348
334 indent: function(state) {
349 indent: function(state, text) {
335 if (state.tokenize != tokenBase) {
350 if (state.tokenize != tokenBase) return 0;
336 return 0;
351 var scope = state.scope;
337 }
352 var closer = text && "])}".indexOf(text.charAt(0)) > -1;
353 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
354 var closes = closer && scope.type === text.charAt(0);
355 if (scope.align)
356 return scope.alignOffset - (closes ? 1 : 0);
357 else
358 return (closes ? scope.prev : scope).offset;
359 },
338
360
339 return state.scopes[0].offset;
361 lineComment: "#",
340 },
362 fold: "indent"
341
363 };
342 lineComment: "#",
364 return external;
343 fold: "indent"
344 };
345 return external;
346 });
365 });
347
366
348 CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');
367 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
368
369 });
@@ -1,3 +1,16 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
1 CodeMirror.defineMode("commonlisp", function (config) {
14 CodeMirror.defineMode("commonlisp", function (config) {
2 var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/;
15 var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/;
3 var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/;
16 var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/;
@@ -103,3 +116,5 b' CodeMirror.defineMode("commonlisp", func'
103 });
116 });
104
117
105 CodeMirror.defineMIME("text/x-common-lisp", "commonlisp");
118 CodeMirror.defineMIME("text/x-common-lisp", "commonlisp");
119
120 });
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file chmod 100755 => 100644
NO CONTENT: modified file chmod 100755 => 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (767 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (728 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now