##// END OF EJS Templates
codemirror: update from 5.4.0 to 5.11.0, fixes #3154
marcink -
r346:1a48416d default
parent child Browse files
Show More
@@ -1,405 +1,416 b''
1 /* BASICS */
1 /* BASICS */
2
2
3 .CodeMirror {
3 .CodeMirror {
4 /* Set height, width, borders, and global font properties here */
4 /* Set height, width, borders, and global font properties here */
5 font-family: monospace;
5 font-family: monospace;
6 height: 300px;
6 height: 300px;
7 color: black;
7 color: black;
8 border-radius: @border-radius;
8 border-radius: @border-radius;
9 border: @border-thickness solid @grey6;
9 border: @border-thickness solid @grey6;
10 margin: 0 0 @padding;
10 margin: 0 0 @padding;
11 }
11 }
12
12
13 /* PADDING */
13 /* PADDING */
14
14
15 .CodeMirror-lines {
15 .CodeMirror-lines {
16 padding: 4px 0; /* Vertical padding around content */
16 padding: 4px 0; /* Vertical padding around content */
17 }
17 }
18 .CodeMirror pre {
18 .CodeMirror pre {
19 padding: 0 4px; /* Horizontal padding of content */
19 padding: 0 4px; /* Horizontal padding of content */
20 }
20 }
21
21
22 .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
22 .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
23 background-color: white; /* The little square between H and V scrollbars */
23 background-color: white; /* The little square between H and V scrollbars */
24 }
24 }
25
25
26 /* GUTTER */
26 /* GUTTER */
27
27
28 .CodeMirror-gutters {
28 .CodeMirror-gutters {
29 border-right: 1px solid #ddd;
29 border-right: 1px solid #ddd;
30 background-color: @grey6;
30 background-color: @grey6;
31 white-space: nowrap;
31 white-space: nowrap;
32 }
32 }
33 .CodeMirror-linenumbers {}
33 .CodeMirror-linenumbers {}
34 .CodeMirror-linenumber {
34 .CodeMirror-linenumber {
35 padding: 0 3px 0 5px;
35 padding: 0 3px 0 5px;
36 min-width: 20px;
36 min-width: 20px;
37 text-align: right;
37 text-align: right;
38 color: @grey4;
38 color: @grey4;
39 white-space: nowrap;
39 white-space: nowrap;
40 }
40 }
41
41
42 .CodeMirror-guttermarker { color: black; }
42 .CodeMirror-guttermarker { color: black; }
43 .CodeMirror-guttermarker-subtle { color: #999; }
43 .CodeMirror-guttermarker-subtle { color: #999; }
44
44
45 /* CURSOR */
45 /* CURSOR */
46
46
47 .CodeMirror div.CodeMirror-cursor {
47 .CodeMirror div.CodeMirror-cursor {
48 border-left: 1px solid black;
48 border-left: 1px solid black;
49 }
49 }
50 /* Shown when moving in bi-directional text */
50 /* Shown when moving in bi-directional text */
51 .CodeMirror div.CodeMirror-secondarycursor {
51 .CodeMirror div.CodeMirror-secondarycursor {
52 border-left: 1px solid silver;
52 border-left: 1px solid silver;
53 }
53 }
54 .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
54 .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
55 width: auto;
55 width: auto;
56 border: 0;
56 border: 0;
57 background: @grey6;
57 background: @grey6;
58 }
58 }
59 .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
59 .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
60 z-index: 1;
60 z-index: 1;
61 }
61 }
62
62
63 .cm-animate-fat-cursor {
63 .cm-animate-fat-cursor {
64 width: auto;
64 width: auto;
65 border: 0;
65 border: 0;
66 -webkit-animation: blink 1.06s steps(1) infinite;
66 -webkit-animation: blink 1.06s steps(1) infinite;
67 -moz-animation: blink 1.06s steps(1) infinite;
67 -moz-animation: blink 1.06s steps(1) infinite;
68 animation: blink 1.06s steps(1) infinite;
68 animation: blink 1.06s steps(1) infinite;
69 }
69 }
70 @-moz-keyframes blink {
70 @-moz-keyframes blink {
71 0% { background: #7e7; }
71 0% { background: #7e7; }
72 50% { background: none; }
72 50% { background: none; }
73 100% { background: #7e7; }
73 100% { background: #7e7; }
74 }
74 }
75 @-webkit-keyframes blink {
75 @-webkit-keyframes blink {
76 0% { background: #7e7; }
76 0% { background: #7e7; }
77 50% { background: none; }
77 50% { background: none; }
78 100% { background: #7e7; }
78 100% { background: #7e7; }
79 }
79 }
80 @keyframes blink {
80 @keyframes blink {
81 0% { background: #7e7; }
81 0% { background: #7e7; }
82 50% { background: none; }
82 50% { background: none; }
83 100% { background: #7e7; }
83 100% { background: #7e7; }
84 }
84 }
85
85
86 /* Can style cursor different in overwrite (non-insert) mode */
86 /* Can style cursor different in overwrite (non-insert) mode */
87 div.CodeMirror-overwrite div.CodeMirror-cursor {}
87 div.CodeMirror-overwrite div.CodeMirror-cursor {}
88
88
89 .cm-tab { display: inline-block; text-decoration: inherit; }
89 .cm-tab { display: inline-block; text-decoration: inherit; }
90
90
91 .CodeMirror-ruler {
91 .CodeMirror-ruler {
92 border-left: 1px solid #ccc;
92 border-left: 1px solid #ccc;
93 position: absolute;
93 position: absolute;
94 }
94 }
95
95
96 /* DEFAULT THEME */
96 /* DEFAULT THEME */
97
97
98 .cm-s-default .cm-header {color: blue;}
98 .cm-s-default .cm-header {color: blue;}
99 .cm-s-default .cm-quote {color: #090;}
99 .cm-s-default .cm-quote {color: #090;}
100 .cm-negative {color: #d44;}
100 .cm-negative {color: #d44;}
101 .cm-positive {color: #292;}
101 .cm-positive {color: #292;}
102 .cm-header, .cm-strong {font-weight: bold;}
102 .cm-header, .cm-strong {font-weight: bold;}
103 .cm-em {font-style: italic;}
103 .cm-em {font-style: italic;}
104 .cm-link {text-decoration: underline;}
104 .cm-link {text-decoration: underline;}
105 .cm-strikethrough {text-decoration: line-through;}
105 .cm-strikethrough {text-decoration: line-through;}
106
106
107 .cm-s-default .cm-keyword {color: #708;}
107 .cm-s-default .cm-keyword {color: #708;}
108 .cm-s-default .cm-atom {color: #219;}
108 .cm-s-default .cm-atom {color: #219;}
109 .cm-s-default .cm-number {color: #164;}
109 .cm-s-default .cm-number {color: #164;}
110 .cm-s-default .cm-def {color: #00f;}
110 .cm-s-default .cm-def {color: #00f;}
111 .cm-s-default .cm-variable,
111 .cm-s-default .cm-variable,
112 .cm-s-default .cm-punctuation,
112 .cm-s-default .cm-punctuation,
113 .cm-s-default .cm-property,
113 .cm-s-default .cm-property,
114 .cm-s-default .cm-operator {}
114 .cm-s-default .cm-operator {}
115 .cm-s-default .cm-variable-2 {color: #05a;}
115 .cm-s-default .cm-variable-2 {color: #05a;}
116 .cm-s-default .cm-variable-3 {color: #085;}
116 .cm-s-default .cm-variable-3 {color: #085;}
117 .cm-s-default .cm-comment {color: #a50;}
117 .cm-s-default .cm-comment {color: #a50;}
118 .cm-s-default .cm-string {color: #a11;}
118 .cm-s-default .cm-string {color: #a11;}
119 .cm-s-default .cm-string-2 {color: #f50;}
119 .cm-s-default .cm-string-2 {color: #f50;}
120 .cm-s-default .cm-meta {color: #555;}
120 .cm-s-default .cm-meta {color: #555;}
121 .cm-s-default .cm-qualifier {color: #555;}
121 .cm-s-default .cm-qualifier {color: #555;}
122 .cm-s-default .cm-builtin {color: #30a;}
122 .cm-s-default .cm-builtin {color: #30a;}
123 .cm-s-default .cm-bracket {color: #997;}
123 .cm-s-default .cm-bracket {color: #997;}
124 .cm-s-default .cm-tag {color: #170;}
124 .cm-s-default .cm-tag {color: #170;}
125 .cm-s-default .cm-attribute {color: #00c;}
125 .cm-s-default .cm-attribute {color: #00c;}
126 .cm-s-default .cm-hr {color: #999;}
126 .cm-s-default .cm-hr {color: #999;}
127 .cm-s-default .cm-link {color: #00c;}
127 .cm-s-default .cm-link {color: #00c;}
128
128
129 .cm-s-default .cm-error {color: #f00;}
129 .cm-s-default .cm-error {color: #f00;}
130 .cm-invalidchar {color: #f00;}
130 .cm-invalidchar {color: #f00;}
131
131
132 .CodeMirror-composing { border-bottom: 2px solid; }
132 .CodeMirror-composing { border-bottom: 2px solid; }
133
133
134 /* Default styles for common addons */
134 /* Default styles for common addons */
135
135
136 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
136 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
137 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
137 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
138 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
138 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
139 .CodeMirror-activeline-background {background: #e8f2ff;}
139 .CodeMirror-activeline-background {background: #e8f2ff;}
140
140
141 /* STOP */
141 /* STOP */
142
142
143 /* The rest of this file contains styles related to the mechanics of
143 /* The rest of this file contains styles related to the mechanics of
144 the editor. You probably shouldn't touch them. */
144 the editor. You probably shouldn't touch them. */
145
145
146 .CodeMirror {
146 .CodeMirror {
147 position: relative;
147 position: relative;
148 overflow: hidden;
148 overflow: hidden;
149 background: white;
149 background: white;
150 }
150 }
151
151
152 .CodeMirror-scroll {
152 .CodeMirror-scroll {
153 overflow: scroll !important; /* Things will break if this is overridden */
153 overflow: scroll !important; /* Things will break if this is overridden */
154 /* 30px is the magic margin used to hide the element's real scrollbars */
154 /* 30px is the magic margin used to hide the element's real scrollbars */
155 /* See overflow: hidden in .CodeMirror */
155 /* See overflow: hidden in .CodeMirror */
156 margin-bottom: -30px; margin-right: -30px;
156 margin-bottom: -30px; margin-right: -30px;
157 padding-bottom: 30px;
157 padding-bottom: 30px;
158 height: 100%;
158 height: 100%;
159 outline: none; /* Prevent dragging from highlighting the element */
159 outline: none; /* Prevent dragging from highlighting the element */
160 position: relative;
160 position: relative;
161 }
161 }
162 .CodeMirror-sizer {
162 .CodeMirror-sizer {
163 position: relative;
163 position: relative;
164 border-right: 30px solid transparent;
164 border-right: 30px solid transparent;
165 }
165 }
166
166
167 /* The fake, visible scrollbars. Used to force redraw during scrolling
167 /* The fake, visible scrollbars. Used to force redraw during scrolling
168 before actuall scrolling happens, thus preventing shaking and
168 before actual scrolling happens, thus preventing shaking and
169 flickering artifacts. */
169 flickering artifacts. */
170 .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
170 .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
171 position: absolute;
171 position: absolute;
172 z-index: 6;
172 z-index: 6;
173 display: none;
173 display: none;
174 }
174 }
175 .CodeMirror-vscrollbar {
175 .CodeMirror-vscrollbar {
176 right: 0; top: 0;
176 right: 0; top: 0;
177 overflow-x: hidden;
177 overflow-x: hidden;
178 overflow-y: scroll;
178 overflow-y: scroll;
179 }
179 }
180 .CodeMirror-hscrollbar {
180 .CodeMirror-hscrollbar {
181 bottom: 0; left: 0;
181 bottom: 0; left: 0;
182 overflow-y: hidden;
182 overflow-y: hidden;
183 overflow-x: scroll;
183 overflow-x: scroll;
184 }
184 }
185 .CodeMirror-scrollbar-filler {
185 .CodeMirror-scrollbar-filler {
186 right: 0; bottom: 0;
186 right: 0; bottom: 0;
187 }
187 }
188 .CodeMirror-gutter-filler {
188 .CodeMirror-gutter-filler {
189 left: 0; bottom: 0;
189 left: 0; bottom: 0;
190 }
190 }
191
191
192 .CodeMirror-gutters {
192 .CodeMirror-gutters {
193 position: absolute; left: 0; top: 0;
193 position: absolute; left: 0; top: 0;
194 z-index: 3;
194 z-index: 3;
195 }
195 }
196 .CodeMirror-gutter {
196 .CodeMirror-gutter {
197 white-space: normal;
197 white-space: normal;
198 height: 100%;
198 height: 100%;
199 display: inline-block;
199 display: inline-block;
200 margin-bottom: -30px;
200 margin-bottom: -30px;
201 /* Hack to make IE7 behave */
201 /* Hack to make IE7 behave */
202 *zoom:1;
202 *zoom:1;
203 *display:inline;
203 *display:inline;
204 }
204 }
205 .CodeMirror-gutter-wrapper {
205 .CodeMirror-gutter-wrapper {
206 position: absolute;
206 position: absolute;
207 z-index: 4;
207 z-index: 4;
208 height: 100%;
208 height: 100%;
209 }
209 }
210 .CodeMirror-gutter-background {
211 position: absolute;
212 top: 0; bottom: 0;
213 z-index: 4;
214 }
210 .CodeMirror-gutter-elt {
215 .CodeMirror-gutter-elt {
211 position: absolute;
216 position: absolute;
212 cursor: default;
217 cursor: default;
213 z-index: 4;
218 z-index: 4;
214 }
219 }
215 .CodeMirror-gutter-wrapper {
220 .CodeMirror-gutter-wrapper {
216 -webkit-user-select: none;
221 -webkit-user-select: none;
217 -moz-user-select: none;
222 -moz-user-select: none;
218 user-select: none;
223 user-select: none;
219 }
224 }
220
225
221 .CodeMirror-lines {
226 .CodeMirror-lines {
222 cursor: text;
227 cursor: text;
223 min-height: 1px; /* prevents collapsing before first draw */
228 min-height: 1px; /* prevents collapsing before first draw */
224 }
229 }
225 .CodeMirror pre {
230 .CodeMirror pre {
226 /* Reset some styles that the rest of the page might have set */
231 /* Reset some styles that the rest of the page might have set */
227 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
232 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
228 border-width: 0;
233 border-width: 0;
229 background: transparent;
234 background: transparent;
230 font-family: inherit;
235 font-family: inherit;
231 font-size: inherit;
236 font-size: inherit;
232 margin: 0;
237 margin: 0;
233 white-space: pre;
238 white-space: pre;
234 word-wrap: normal;
239 word-wrap: normal;
235 line-height: inherit;
240 line-height: inherit;
236 color: inherit;
241 color: inherit;
237 z-index: 2;
242 z-index: 2;
238 position: relative;
243 position: relative;
239 overflow: visible;
244 overflow: visible;
240 -webkit-tap-highlight-color: transparent;
245 -webkit-tap-highlight-color: transparent;
241 }
246 }
242 .CodeMirror-wrap pre {
247 .CodeMirror-wrap pre {
243 word-wrap: break-word;
248 word-wrap: break-word;
244 white-space: pre-wrap;
249 white-space: pre-wrap;
245 word-break: normal;
250 word-break: normal;
246 }
251 }
247
252
248 .CodeMirror-linebackground {
253 .CodeMirror-linebackground {
249 position: absolute;
254 position: absolute;
250 left: 0; right: 0; top: 0; bottom: 0;
255 left: 0; right: 0; top: 0; bottom: 0;
251 z-index: 0;
256 z-index: 0;
252 }
257 }
253
258
254 .CodeMirror-linewidget {
259 .CodeMirror-linewidget {
255 position: relative;
260 position: relative;
256 z-index: 2;
261 z-index: 2;
257 overflow: auto;
262 overflow: auto;
258 }
263 }
259
264
260 .CodeMirror-widget {}
265 .CodeMirror-widget {}
261
266
262 .CodeMirror-code {
267 .CodeMirror-code {
263 outline: none;
268 outline: none;
264 }
269 }
265
270
266 /* Force content-box sizing for the elements where we expect it */
271 /* Force content-box sizing for the elements where we expect it */
267 .CodeMirror-scroll,
272 .CodeMirror-scroll,
268 .CodeMirror-sizer,
273 .CodeMirror-sizer,
269 .CodeMirror-gutter,
274 .CodeMirror-gutter,
270 .CodeMirror-gutters,
275 .CodeMirror-gutters,
271 .CodeMirror-linenumber {
276 .CodeMirror-linenumber {
272 -moz-box-sizing: content-box;
277 -moz-box-sizing: content-box;
273 box-sizing: content-box;
278 box-sizing: content-box;
274 }
279 }
275
280
276 .CodeMirror-measure {
281 .CodeMirror-measure {
277 position: absolute;
282 position: absolute;
278 width: 100%;
283 width: 100%;
279 height: 0;
284 height: 0;
280 overflow: hidden;
285 overflow: hidden;
281 visibility: hidden;
286 visibility: hidden;
282 }
287 }
283 .CodeMirror-measure pre { position: static; }
288
284
289
285 .CodeMirror div.CodeMirror-cursor {
290 .CodeMirror div.CodeMirror-cursor {
286 position: absolute;
291 position: absolute;
287 border-right: none;
292 border-right: none;
288 width: 0;
293 width: 0;
289 }
294 }
290
295
296 .CodeMirror-measure pre { position: static; }
297
291 div.CodeMirror-cursors {
298 div.CodeMirror-cursors {
292 visibility: hidden;
299 visibility: hidden;
293 position: relative;
300 position: relative;
294 z-index: 3;
301 z-index: 3;
295 }
302 }
303 div.CodeMirror-dragcursors {
304 visibility: visible;
305 }
306
296 .CodeMirror-focused div.CodeMirror-cursors {
307 .CodeMirror-focused div.CodeMirror-cursors {
297 visibility: visible;
308 visibility: visible;
298 }
309 }
299
310
300 .CodeMirror-selected { background: #d9d9d9; }
311 .CodeMirror-selected { background: #d9d9d9; }
301 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
312 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
302 .CodeMirror-crosshair { cursor: crosshair; }
313 .CodeMirror-crosshair { cursor: crosshair; }
303 .CodeMirror ::selection { background: #d7d4f0; }
314 .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
304 .CodeMirror ::-moz-selection { background: #d7d4f0; }
315 .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
305
316
306 .cm-searching {
317 .cm-searching {
307 background: #ffa;
318 background: #ffa;
308 background: rgba(255, 255, 0, .4);
319 background: rgba(255, 255, 0, .4);
309 }
320 }
310
321
311 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
322 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
312 .CodeMirror span { *vertical-align: text-bottom; }
323 .CodeMirror span { *vertical-align: text-bottom; }
313
324
314 /* Used to force a border model for a node */
325 /* Used to force a border model for a node */
315 .cm-force-border { padding-right: .1px; }
326 .cm-force-border { padding-right: .1px; }
316
327
317 @media print {
328 @media print {
318 /* Hide the cursor when printing */
329 /* Hide the cursor when printing */
319 .CodeMirror div.CodeMirror-cursors {
330 .CodeMirror div.CodeMirror-cursors {
320 visibility: hidden;
331 visibility: hidden;
321 }
332 }
322 }
333 }
323
334
324 /* See issue #2901 */
335 /* See issue #2901 */
325 .cm-tab-wrap-hack:after { content: ''; }
336 .cm-tab-wrap-hack:after { content: ''; }
326
337
327 /* Help users use markselection to safely style text background */
338 /* Help users use markselection to safely style text background */
328 span.CodeMirror-selectedtext { background: none; }
339 span.CodeMirror-selectedtext { background: none; }
329
340
330 /* codemirror autocomplete widget */
341 /* codemirror autocomplete widget */
331 .CodeMirror-hints {
342 .CodeMirror-hints {
332 position: absolute;
343 position: absolute;
333 z-index: 10;
344 z-index: 10;
334 overflow: hidden;
345 overflow: hidden;
335 list-style: none;
346 list-style: none;
336
347
337 margin: 0;
348 margin: 0;
338 padding: 0;
349 padding: 0;
339
350
340 border-radius: @border-radius;
351 border-radius: @border-radius;
341 border: @border-thickness solid @rcblue;
352 border: @border-thickness solid @rcblue;
342
353
343 color: @rcblue;
354 color: @rcblue;
344 background-color: white;
355 background-color: white;
345 font-size: 95%;
356 font-size: 95%;
346
357
347 max-height: 20em;
358 max-height: 20em;
348 overflow-y: auto;
359 overflow-y: auto;
349 }
360 }
350
361
351 .CodeMirror-hint {
362 .CodeMirror-hint {
352 margin: 0;
363 margin: 0;
353 padding: 4px 8px;
364 padding: 4px 8px;
354 max-width: 40em;
365 max-width: 40em;
355 white-space: pre;
366 white-space: pre;
356 color: @rcblue;
367 color: @rcblue;
357 cursor: pointer;
368 cursor: pointer;
358 }
369 }
359
370
360 .CodeMirror-hint-active {
371 .CodeMirror-hint-active {
361 background: @rclightblue;
372 background: @rclightblue;
362 color: @rcblue;
373 color: @rcblue;
363 }
374 }
364
375
365 .CodeMirror-hint-entry {
376 .CodeMirror-hint-entry {
366 width: 38em;
377 width: 38em;
367 color: @rcblue;
378 color: @rcblue;
368 }
379 }
369
380
370 .CodeMirror-hint-entry .gravatar {
381 .CodeMirror-hint-entry .gravatar {
371 margin-right: 4px;
382 margin-right: 4px;
372 }
383 }
373
384
374 .CodeMirror-empty {
385 .CodeMirror-empty {
375 border: @border-thickness solid @grey5;
386 border: @border-thickness solid @grey5;
376 }
387 }
377
388
378 .CodeMirror-focused {
389 .CodeMirror-focused {
379 border: @border-thickness solid @grey5;
390 border: @border-thickness solid @grey5;
380 }
391 }
381
392
382 .CodeMirror-empty.CodeMirror-focused {
393 .CodeMirror-empty.CodeMirror-focused {
383 border: @border-thickness solid @grey5;
394 border: @border-thickness solid @grey5;
384 }
395 }
385
396
386 .CodeMirror pre.CodeMirror-placeholder {
397 .CodeMirror pre.CodeMirror-placeholder {
387 color: @grey4;
398 color: @grey4;
388 }
399 }
389
400
390 /** RhodeCode Customizations **/
401 /** RhodeCode Customizations **/
391
402
392 .CodeMirror.cm-s-rc-input {
403 .CodeMirror.cm-s-rc-input {
393 border: @border-thickness solid @grey4;
404 border: @border-thickness solid @grey4;
394 }
405 }
395
406
396 .CodeMirror-code pre {
407 .CodeMirror-code pre {
397 border-right: 30px solid transparent;
408 border-right: 30px solid transparent;
398 width: -webkit-fit-content;
409 width: -webkit-fit-content;
399 width: -moz-fit-content;
410 width: -moz-fit-content;
400 width: fit-content;
411 width: fit-content;
401 }
412 }
402 .CodeMirror-wrap .CodeMirror-code pre {
413 .CodeMirror-wrap .CodeMirror-code pre {
403 border-right: none;
414 border-right: none;
404 width: auto;
415 width: auto;
405 }
416 }
@@ -1,591 +1,772 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("clike", function(config, parserConfig) {
14 CodeMirror.defineMode("clike", function(config, parserConfig) {
15 var indentUnit = config.indentUnit,
15 var indentUnit = config.indentUnit,
16 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
16 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
17 dontAlignCalls = parserConfig.dontAlignCalls,
17 dontAlignCalls = parserConfig.dontAlignCalls,
18 keywords = parserConfig.keywords || {},
18 keywords = parserConfig.keywords || {},
19 types = parserConfig.types || {},
19 types = parserConfig.types || {},
20 builtin = parserConfig.builtin || {},
20 builtin = parserConfig.builtin || {},
21 blockKeywords = parserConfig.blockKeywords || {},
21 blockKeywords = parserConfig.blockKeywords || {},
22 defKeywords = parserConfig.defKeywords || {},
22 defKeywords = parserConfig.defKeywords || {},
23 atoms = parserConfig.atoms || {},
23 atoms = parserConfig.atoms || {},
24 hooks = parserConfig.hooks || {},
24 hooks = parserConfig.hooks || {},
25 multiLineStrings = parserConfig.multiLineStrings,
25 multiLineStrings = parserConfig.multiLineStrings,
26 indentStatements = parserConfig.indentStatements !== false,
26 indentStatements = parserConfig.indentStatements !== false,
27 indentSwitch = parserConfig.indentSwitch !== false,
27 indentSwitch = parserConfig.indentSwitch !== false,
28 namespaceSeparator = parserConfig.namespaceSeparator;
28 namespaceSeparator = parserConfig.namespaceSeparator,
29 var isOperatorChar = /[+\-*&%=<>!?|\/]/;
29 isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
30 numberStart = parserConfig.numberStart || /[\d\.]/,
31 number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
32 isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
33 endStatement = parserConfig.endStatement || /^[;:,]$/;
30
34
31 var curPunc, isDefKeyword;
35 var curPunc, isDefKeyword;
32
36
33 function tokenBase(stream, state) {
37 function tokenBase(stream, state) {
34 var ch = stream.next();
38 var ch = stream.next();
35 if (hooks[ch]) {
39 if (hooks[ch]) {
36 var result = hooks[ch](stream, state);
40 var result = hooks[ch](stream, state);
37 if (result !== false) return result;
41 if (result !== false) return result;
38 }
42 }
39 if (ch == '"' || ch == "'") {
43 if (ch == '"' || ch == "'") {
40 state.tokenize = tokenString(ch);
44 state.tokenize = tokenString(ch);
41 return state.tokenize(stream, state);
45 return state.tokenize(stream, state);
42 }
46 }
43 if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
47 if (isPunctuationChar.test(ch)) {
44 curPunc = ch;
48 curPunc = ch;
45 return null;
49 return null;
46 }
50 }
47 if (/\d/.test(ch)) {
51 if (numberStart.test(ch)) {
48 stream.eatWhile(/[\w\.]/);
52 stream.backUp(1)
49 return "number";
53 if (stream.match(number)) return "number"
54 stream.next()
50 }
55 }
51 if (ch == "/") {
56 if (ch == "/") {
52 if (stream.eat("*")) {
57 if (stream.eat("*")) {
53 state.tokenize = tokenComment;
58 state.tokenize = tokenComment;
54 return tokenComment(stream, state);
59 return tokenComment(stream, state);
55 }
60 }
56 if (stream.eat("/")) {
61 if (stream.eat("/")) {
57 stream.skipToEnd();
62 stream.skipToEnd();
58 return "comment";
63 return "comment";
59 }
64 }
60 }
65 }
61 if (isOperatorChar.test(ch)) {
66 if (isOperatorChar.test(ch)) {
62 stream.eatWhile(isOperatorChar);
67 stream.eatWhile(isOperatorChar);
63 return "operator";
68 return "operator";
64 }
69 }
65 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
70 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
66 if (namespaceSeparator) while (stream.match(namespaceSeparator))
71 if (namespaceSeparator) while (stream.match(namespaceSeparator))
67 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
72 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
68
73
69 var cur = stream.current();
74 var cur = stream.current();
70 if (keywords.propertyIsEnumerable(cur)) {
75 if (contains(keywords, cur)) {
71 if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
76 if (contains(blockKeywords, cur)) curPunc = "newstatement";
72 if (defKeywords.propertyIsEnumerable(cur)) isDefKeyword = true;
77 if (contains(defKeywords, cur)) isDefKeyword = true;
73 return "keyword";
78 return "keyword";
74 }
79 }
75 if (types.propertyIsEnumerable(cur)) return "variable-3";
80 if (contains(types, cur)) return "variable-3";
76 if (builtin.propertyIsEnumerable(cur)) {
81 if (contains(builtin, cur)) {
77 if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
82 if (contains(blockKeywords, cur)) curPunc = "newstatement";
78 return "builtin";
83 return "builtin";
79 }
84 }
80 if (atoms.propertyIsEnumerable(cur)) return "atom";
85 if (contains(atoms, cur)) return "atom";
81 return "variable";
86 return "variable";
82 }
87 }
83
88
84 function tokenString(quote) {
89 function tokenString(quote) {
85 return function(stream, state) {
90 return function(stream, state) {
86 var escaped = false, next, end = false;
91 var escaped = false, next, end = false;
87 while ((next = stream.next()) != null) {
92 while ((next = stream.next()) != null) {
88 if (next == quote && !escaped) {end = true; break;}
93 if (next == quote && !escaped) {end = true; break;}
89 escaped = !escaped && next == "\\";
94 escaped = !escaped && next == "\\";
90 }
95 }
91 if (end || !(escaped || multiLineStrings))
96 if (end || !(escaped || multiLineStrings))
92 state.tokenize = null;
97 state.tokenize = null;
93 return "string";
98 return "string";
94 };
99 };
95 }
100 }
96
101
97 function tokenComment(stream, state) {
102 function tokenComment(stream, state) {
98 var maybeEnd = false, ch;
103 var maybeEnd = false, ch;
99 while (ch = stream.next()) {
104 while (ch = stream.next()) {
100 if (ch == "/" && maybeEnd) {
105 if (ch == "/" && maybeEnd) {
101 state.tokenize = null;
106 state.tokenize = null;
102 break;
107 break;
103 }
108 }
104 maybeEnd = (ch == "*");
109 maybeEnd = (ch == "*");
105 }
110 }
106 return "comment";
111 return "comment";
107 }
112 }
108
113
109 function Context(indented, column, type, align, prev) {
114 function Context(indented, column, type, align, prev) {
110 this.indented = indented;
115 this.indented = indented;
111 this.column = column;
116 this.column = column;
112 this.type = type;
117 this.type = type;
113 this.align = align;
118 this.align = align;
114 this.prev = prev;
119 this.prev = prev;
115 }
120 }
116 function isStatement(type) {
121 function isStatement(type) {
117 return type == "statement" || type == "switchstatement" || type == "namespace";
122 return type == "statement" || type == "switchstatement" || type == "namespace";
118 }
123 }
119 function pushContext(state, col, type) {
124 function pushContext(state, col, type) {
120 var indent = state.indented;
125 var indent = state.indented;
121 if (state.context && isStatement(state.context.type) && !isStatement(type))
126 if (state.context && isStatement(state.context.type) && !isStatement(type))
122 indent = state.context.indented;
127 indent = state.context.indented;
123 return state.context = new Context(indent, col, type, null, state.context);
128 return state.context = new Context(indent, col, type, null, state.context);
124 }
129 }
125 function popContext(state) {
130 function popContext(state) {
126 var t = state.context.type;
131 var t = state.context.type;
127 if (t == ")" || t == "]" || t == "}")
132 if (t == ")" || t == "]" || t == "}")
128 state.indented = state.context.indented;
133 state.indented = state.context.indented;
129 return state.context = state.context.prev;
134 return state.context = state.context.prev;
130 }
135 }
131
136
132 function typeBefore(stream, state) {
137 function typeBefore(stream, state) {
133 if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
138 if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
134 if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
139 if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
135 }
140 }
136
141
137 function isTopScope(context) {
142 function isTopScope(context) {
138 for (;;) {
143 for (;;) {
139 if (!context || context.type == "top") return true;
144 if (!context || context.type == "top") return true;
140 if (context.type == "}" && context.prev.type != "namespace") return false;
145 if (context.type == "}" && context.prev.type != "namespace") return false;
141 context = context.prev;
146 context = context.prev;
142 }
147 }
143 }
148 }
144
149
145 // Interface
150 // Interface
146
151
147 return {
152 return {
148 startState: function(basecolumn) {
153 startState: function(basecolumn) {
149 return {
154 return {
150 tokenize: null,
155 tokenize: null,
151 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
156 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
152 indented: 0,
157 indented: 0,
153 startOfLine: true,
158 startOfLine: true,
154 prevToken: null
159 prevToken: null
155 };
160 };
156 },
161 },
157
162
158 token: function(stream, state) {
163 token: function(stream, state) {
159 var ctx = state.context;
164 var ctx = state.context;
160 if (stream.sol()) {
165 if (stream.sol()) {
161 if (ctx.align == null) ctx.align = false;
166 if (ctx.align == null) ctx.align = false;
162 state.indented = stream.indentation();
167 state.indented = stream.indentation();
163 state.startOfLine = true;
168 state.startOfLine = true;
164 }
169 }
165 if (stream.eatSpace()) return null;
170 if (stream.eatSpace()) return null;
166 curPunc = isDefKeyword = null;
171 curPunc = isDefKeyword = null;
167 var style = (state.tokenize || tokenBase)(stream, state);
172 var style = (state.tokenize || tokenBase)(stream, state);
168 if (style == "comment" || style == "meta") return style;
173 if (style == "comment" || style == "meta") return style;
169 if (ctx.align == null) ctx.align = true;
174 if (ctx.align == null) ctx.align = true;
170
175
171 if ((curPunc == ";" || curPunc == ":" || curPunc == ","))
176 if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state);
172 while (isStatement(state.context.type)) popContext(state);
173 else if (curPunc == "{") pushContext(state, stream.column(), "}");
177 else if (curPunc == "{") pushContext(state, stream.column(), "}");
174 else if (curPunc == "[") pushContext(state, stream.column(), "]");
178 else if (curPunc == "[") pushContext(state, stream.column(), "]");
175 else if (curPunc == "(") pushContext(state, stream.column(), ")");
179 else if (curPunc == "(") pushContext(state, stream.column(), ")");
176 else if (curPunc == "}") {
180 else if (curPunc == "}") {
177 while (isStatement(ctx.type)) ctx = popContext(state);
181 while (isStatement(ctx.type)) ctx = popContext(state);
178 if (ctx.type == "}") ctx = popContext(state);
182 if (ctx.type == "}") ctx = popContext(state);
179 while (isStatement(ctx.type)) ctx = popContext(state);
183 while (isStatement(ctx.type)) ctx = popContext(state);
180 }
184 }
181 else if (curPunc == ctx.type) popContext(state);
185 else if (curPunc == ctx.type) popContext(state);
182 else if (indentStatements &&
186 else if (indentStatements &&
183 (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
187 (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
184 (isStatement(ctx.type) && curPunc == "newstatement"))) {
188 (isStatement(ctx.type) && curPunc == "newstatement"))) {
185 var type = "statement";
189 var type = "statement";
186 if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
190 if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
187 type = "switchstatement";
191 type = "switchstatement";
188 else if (style == "keyword" && stream.current() == "namespace")
192 else if (style == "keyword" && stream.current() == "namespace")
189 type = "namespace";
193 type = "namespace";
190 pushContext(state, stream.column(), type);
194 pushContext(state, stream.column(), type);
191 }
195 }
192
196
193 if (style == "variable" &&
197 if (style == "variable" &&
194 ((state.prevToken == "def" ||
198 ((state.prevToken == "def" ||
195 (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
199 (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
196 isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
200 isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
197 style = "def";
201 style = "def";
198
202
199 if (hooks.token) {
203 if (hooks.token) {
200 var result = hooks.token(stream, state, style);
204 var result = hooks.token(stream, state, style);
201 if (result !== undefined) style = result;
205 if (result !== undefined) style = result;
202 }
206 }
203
207
204 if (style == "def" && parserConfig.styleDefs === false) style = "variable";
208 if (style == "def" && parserConfig.styleDefs === false) style = "variable";
205
209
206 state.startOfLine = false;
210 state.startOfLine = false;
207 state.prevToken = isDefKeyword ? "def" : style || curPunc;
211 state.prevToken = isDefKeyword ? "def" : style || curPunc;
208 return style;
212 return style;
209 },
213 },
210
214
211 indent: function(state, textAfter) {
215 indent: function(state, textAfter) {
212 if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
216 if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
213 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
217 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
214 if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
218 if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
219 if (hooks.indent) {
220 var hook = hooks.indent(state, ctx, textAfter);
221 if (typeof hook == "number") return hook
222 }
215 var closing = firstChar == ctx.type;
223 var closing = firstChar == ctx.type;
216 var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
224 var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
225 if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
226 while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
227 return ctx.indented
228 }
217 if (isStatement(ctx.type))
229 if (isStatement(ctx.type))
218 return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
230 return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
219 if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
231 if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
220 return ctx.column + (closing ? 0 : 1);
232 return ctx.column + (closing ? 0 : 1);
221 if (ctx.type == ")" && !closing)
233 if (ctx.type == ")" && !closing)
222 return ctx.indented + statementIndentUnit;
234 return ctx.indented + statementIndentUnit;
223
235
224 return ctx.indented + (closing ? 0 : indentUnit) +
236 return ctx.indented + (closing ? 0 : indentUnit) +
225 (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
237 (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
226 },
238 },
227
239
228 electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
240 electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
229 blockCommentStart: "/*",
241 blockCommentStart: "/*",
230 blockCommentEnd: "*/",
242 blockCommentEnd: "*/",
231 lineComment: "//",
243 lineComment: "//",
232 fold: "brace"
244 fold: "brace"
233 };
245 };
234 });
246 });
235
247
236 function words(str) {
248 function words(str) {
237 var obj = {}, words = str.split(" ");
249 var obj = {}, words = str.split(" ");
238 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
250 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
239 return obj;
251 return obj;
240 }
252 }
253 function contains(words, word) {
254 if (typeof words === "function") {
255 return words(word);
256 } else {
257 return words.propertyIsEnumerable(word);
258 }
259 }
241 var cKeywords = "auto if break case register continue return default do sizeof " +
260 var cKeywords = "auto if break case register continue return default do sizeof " +
242 "static else struct switch extern typedef float union for " +
261 "static else struct switch extern typedef union for goto while enum const volatile";
243 "goto while enum const volatile";
244 var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
262 var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
245
263
246 function cppHook(stream, state) {
264 function cppHook(stream, state) {
247 if (!state.startOfLine) return false;
265 if (!state.startOfLine) return false
248 for (;;) {
266 for (var ch, next = null; ch = stream.peek();) {
249 if (stream.skipTo("\\")) {
267 if (ch == "\\" && stream.match(/^.$/)) {
250 stream.next();
268 next = cppHook
251 if (stream.eol()) {
269 break
252 state.tokenize = cppHook;
270 } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
253 break;
271 break
254 }
255 } else {
256 stream.skipToEnd();
257 state.tokenize = null;
258 break;
259 }
272 }
273 stream.next()
260 }
274 }
261 return "meta";
275 state.tokenize = next
276 return "meta"
262 }
277 }
263
278
264 function pointerHook(_stream, state) {
279 function pointerHook(_stream, state) {
265 if (state.prevToken == "variable-3") return "variable-3";
280 if (state.prevToken == "variable-3") return "variable-3";
266 return false;
281 return false;
267 }
282 }
268
283
284 function cpp14Literal(stream) {
285 stream.eatWhile(/[\w\.']/);
286 return "number";
287 }
288
269 function cpp11StringHook(stream, state) {
289 function cpp11StringHook(stream, state) {
270 stream.backUp(1);
290 stream.backUp(1);
271 // Raw strings.
291 // Raw strings.
272 if (stream.match(/(R|u8R|uR|UR|LR)/)) {
292 if (stream.match(/(R|u8R|uR|UR|LR)/)) {
273 var match = stream.match(/"([^\s\\()]{0,16})\(/);
293 var match = stream.match(/"([^\s\\()]{0,16})\(/);
274 if (!match) {
294 if (!match) {
275 return false;
295 return false;
276 }
296 }
277 state.cpp11RawStringDelim = match[1];
297 state.cpp11RawStringDelim = match[1];
278 state.tokenize = tokenRawString;
298 state.tokenize = tokenRawString;
279 return tokenRawString(stream, state);
299 return tokenRawString(stream, state);
280 }
300 }
281 // Unicode strings/chars.
301 // Unicode strings/chars.
282 if (stream.match(/(u8|u|U|L)/)) {
302 if (stream.match(/(u8|u|U|L)/)) {
283 if (stream.match(/["']/, /* eat */ false)) {
303 if (stream.match(/["']/, /* eat */ false)) {
284 return "string";
304 return "string";
285 }
305 }
286 return false;
306 return false;
287 }
307 }
288 // Ignore this hook.
308 // Ignore this hook.
289 stream.next();
309 stream.next();
290 return false;
310 return false;
291 }
311 }
292
312
293 function cppLooksLikeConstructor(word) {
313 function cppLooksLikeConstructor(word) {
294 var lastTwo = /(\w+)::(\w+)$/.exec(word);
314 var lastTwo = /(\w+)::(\w+)$/.exec(word);
295 return lastTwo && lastTwo[1] == lastTwo[2];
315 return lastTwo && lastTwo[1] == lastTwo[2];
296 }
316 }
297
317
298 // C#-style strings where "" escapes a quote.
318 // C#-style strings where "" escapes a quote.
299 function tokenAtString(stream, state) {
319 function tokenAtString(stream, state) {
300 var next;
320 var next;
301 while ((next = stream.next()) != null) {
321 while ((next = stream.next()) != null) {
302 if (next == '"' && !stream.eat('"')) {
322 if (next == '"' && !stream.eat('"')) {
303 state.tokenize = null;
323 state.tokenize = null;
304 break;
324 break;
305 }
325 }
306 }
326 }
307 return "string";
327 return "string";
308 }
328 }
309
329
310 // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
330 // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
311 // <delim> can be a string up to 16 characters long.
331 // <delim> can be a string up to 16 characters long.
312 function tokenRawString(stream, state) {
332 function tokenRawString(stream, state) {
313 // Escape characters that have special regex meanings.
333 // Escape characters that have special regex meanings.
314 var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
334 var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
315 var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
335 var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
316 if (match)
336 if (match)
317 state.tokenize = null;
337 state.tokenize = null;
318 else
338 else
319 stream.skipToEnd();
339 stream.skipToEnd();
320 return "string";
340 return "string";
321 }
341 }
322
342
323 function def(mimes, mode) {
343 function def(mimes, mode) {
324 if (typeof mimes == "string") mimes = [mimes];
344 if (typeof mimes == "string") mimes = [mimes];
325 var words = [];
345 var words = [];
326 function add(obj) {
346 function add(obj) {
327 if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
347 if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
328 words.push(prop);
348 words.push(prop);
329 }
349 }
330 add(mode.keywords);
350 add(mode.keywords);
331 add(mode.types);
351 add(mode.types);
332 add(mode.builtin);
352 add(mode.builtin);
333 add(mode.atoms);
353 add(mode.atoms);
334 if (words.length) {
354 if (words.length) {
335 mode.helperType = mimes[0];
355 mode.helperType = mimes[0];
336 CodeMirror.registerHelper("hintWords", mimes[0], words);
356 CodeMirror.registerHelper("hintWords", mimes[0], words);
337 }
357 }
338
358
339 for (var i = 0; i < mimes.length; ++i)
359 for (var i = 0; i < mimes.length; ++i)
340 CodeMirror.defineMIME(mimes[i], mode);
360 CodeMirror.defineMIME(mimes[i], mode);
341 }
361 }
342
362
343 def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
363 def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
344 name: "clike",
364 name: "clike",
345 keywords: words(cKeywords),
365 keywords: words(cKeywords),
346 types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
366 types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
347 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
367 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
348 "uint32_t uint64_t"),
368 "uint32_t uint64_t"),
349 blockKeywords: words("case do else for if switch while struct"),
369 blockKeywords: words("case do else for if switch while struct"),
350 defKeywords: words("struct"),
370 defKeywords: words("struct"),
351 typeFirstDefinitions: true,
371 typeFirstDefinitions: true,
352 atoms: words("null true false"),
372 atoms: words("null true false"),
353 hooks: {"#": cppHook, "*": pointerHook},
373 hooks: {"#": cppHook, "*": pointerHook},
354 modeProps: {fold: ["brace", "include"]}
374 modeProps: {fold: ["brace", "include"]}
355 });
375 });
356
376
357 def(["text/x-c++src", "text/x-c++hdr"], {
377 def(["text/x-c++src", "text/x-c++hdr"], {
358 name: "clike",
378 name: "clike",
359 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
379 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
360 "static_cast typeid catch operator template typename class friend private " +
380 "static_cast typeid catch operator template typename class friend private " +
361 "this using const_cast inline public throw virtual delete mutable protected " +
381 "this using const_cast inline public throw virtual delete mutable protected " +
362 "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
382 "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
363 "static_assert override"),
383 "static_assert override"),
364 types: words(cTypes + " bool wchar_t"),
384 types: words(cTypes + " bool wchar_t"),
365 blockKeywords: words("catch class do else finally for if struct switch try while"),
385 blockKeywords: words("catch class do else finally for if struct switch try while"),
366 defKeywords: words("class namespace struct enum union"),
386 defKeywords: words("class namespace struct enum union"),
367 typeFirstDefinitions: true,
387 typeFirstDefinitions: true,
368 atoms: words("true false null"),
388 atoms: words("true false null"),
369 hooks: {
389 hooks: {
370 "#": cppHook,
390 "#": cppHook,
371 "*": pointerHook,
391 "*": pointerHook,
372 "u": cpp11StringHook,
392 "u": cpp11StringHook,
373 "U": cpp11StringHook,
393 "U": cpp11StringHook,
374 "L": cpp11StringHook,
394 "L": cpp11StringHook,
375 "R": cpp11StringHook,
395 "R": cpp11StringHook,
396 "0": cpp14Literal,
397 "1": cpp14Literal,
398 "2": cpp14Literal,
399 "3": cpp14Literal,
400 "4": cpp14Literal,
401 "5": cpp14Literal,
402 "6": cpp14Literal,
403 "7": cpp14Literal,
404 "8": cpp14Literal,
405 "9": cpp14Literal,
376 token: function(stream, state, style) {
406 token: function(stream, state, style) {
377 if (style == "variable" && stream.peek() == "(" &&
407 if (style == "variable" && stream.peek() == "(" &&
378 (state.prevToken == ";" || state.prevToken == null ||
408 (state.prevToken == ";" || state.prevToken == null ||
379 state.prevToken == "}") &&
409 state.prevToken == "}") &&
380 cppLooksLikeConstructor(stream.current()))
410 cppLooksLikeConstructor(stream.current()))
381 return "def";
411 return "def";
382 }
412 }
383 },
413 },
384 namespaceSeparator: "::",
414 namespaceSeparator: "::",
385 modeProps: {fold: ["brace", "include"]}
415 modeProps: {fold: ["brace", "include"]}
386 });
416 });
387
417
388 def("text/x-java", {
418 def("text/x-java", {
389 name: "clike",
419 name: "clike",
390 keywords: words("abstract assert break case catch class const continue default " +
420 keywords: words("abstract assert break case catch class const continue default " +
391 "do else enum extends final finally float for goto if implements import " +
421 "do else enum extends final finally float for goto if implements import " +
392 "instanceof interface native new package private protected public " +
422 "instanceof interface native new package private protected public " +
393 "return static strictfp super switch synchronized this throw throws transient " +
423 "return static strictfp super switch synchronized this throw throws transient " +
394 "try volatile while"),
424 "try volatile while"),
395 types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
425 types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
396 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
426 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
397 blockKeywords: words("catch class do else finally for if switch try while"),
427 blockKeywords: words("catch class do else finally for if switch try while"),
398 defKeywords: words("class interface package enum"),
428 defKeywords: words("class interface package enum"),
399 typeFirstDefinitions: true,
429 typeFirstDefinitions: true,
400 atoms: words("true false null"),
430 atoms: words("true false null"),
431 endStatement: /^[;:]$/,
401 hooks: {
432 hooks: {
402 "@": function(stream) {
433 "@": function(stream) {
403 stream.eatWhile(/[\w\$_]/);
434 stream.eatWhile(/[\w\$_]/);
404 return "meta";
435 return "meta";
405 }
436 }
406 },
437 },
407 modeProps: {fold: ["brace", "import"]}
438 modeProps: {fold: ["brace", "import"]}
408 });
439 });
409
440
410 def("text/x-csharp", {
441 def("text/x-csharp", {
411 name: "clike",
442 name: "clike",
412 keywords: words("abstract as async await base break case catch checked class const continue" +
443 keywords: words("abstract as async await base break case catch checked class const continue" +
413 " default delegate do else enum event explicit extern finally fixed for" +
444 " default delegate do else enum event explicit extern finally fixed for" +
414 " foreach goto if implicit in interface internal is lock namespace new" +
445 " foreach goto if implicit in interface internal is lock namespace new" +
415 " operator out override params private protected public readonly ref return sealed" +
446 " operator out override params private protected public readonly ref return sealed" +
416 " sizeof stackalloc static struct switch this throw try typeof unchecked" +
447 " sizeof stackalloc static struct switch this throw try typeof unchecked" +
417 " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
448 " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
418 " global group into join let orderby partial remove select set value var yield"),
449 " global group into join let orderby partial remove select set value var yield"),
419 types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
450 types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
420 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
451 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
421 " UInt64 bool byte char decimal double short int long object" +
452 " UInt64 bool byte char decimal double short int long object" +
422 " sbyte float string ushort uint ulong"),
453 " sbyte float string ushort uint ulong"),
423 blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
454 blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
424 defKeywords: words("class interface namespace struct var"),
455 defKeywords: words("class interface namespace struct var"),
425 typeFirstDefinitions: true,
456 typeFirstDefinitions: true,
426 atoms: words("true false null"),
457 atoms: words("true false null"),
427 hooks: {
458 hooks: {
428 "@": function(stream, state) {
459 "@": function(stream, state) {
429 if (stream.eat('"')) {
460 if (stream.eat('"')) {
430 state.tokenize = tokenAtString;
461 state.tokenize = tokenAtString;
431 return tokenAtString(stream, state);
462 return tokenAtString(stream, state);
432 }
463 }
433 stream.eatWhile(/[\w\$_]/);
464 stream.eatWhile(/[\w\$_]/);
434 return "meta";
465 return "meta";
435 }
466 }
436 }
467 }
437 });
468 });
438
469
439 function tokenTripleString(stream, state) {
470 function tokenTripleString(stream, state) {
440 var escaped = false;
471 var escaped = false;
441 while (!stream.eol()) {
472 while (!stream.eol()) {
442 if (!escaped && stream.match('"""')) {
473 if (!escaped && stream.match('"""')) {
443 state.tokenize = null;
474 state.tokenize = null;
444 break;
475 break;
445 }
476 }
446 escaped = stream.next() == "\\" && !escaped;
477 escaped = stream.next() == "\\" && !escaped;
447 }
478 }
448 return "string";
479 return "string";
449 }
480 }
450
481
451 def("text/x-scala", {
482 def("text/x-scala", {
452 name: "clike",
483 name: "clike",
453 keywords: words(
484 keywords: words(
454
485
455 /* scala */
486 /* scala */
456 "abstract case catch class def do else extends false final finally for forSome if " +
487 "abstract case catch class def do else extends final finally for forSome if " +
457 "implicit import lazy match new null object override package private protected return " +
488 "implicit import lazy match new null object override package private protected return " +
458 "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
489 "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
459 "<% >: # @ " +
490 "<% >: # @ " +
460
491
461 /* package scala */
492 /* package scala */
462 "assert assume require print println printf readLine readBoolean readByte readShort " +
493 "assert assume require print println printf readLine readBoolean readByte readShort " +
463 "readChar readInt readLong readFloat readDouble " +
494 "readChar readInt readLong readFloat readDouble " +
464
495
465 ":: #:: "
496 ":: #:: "
466 ),
497 ),
467 types: words(
498 types: words(
468 "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
499 "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
469 "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
500 "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
470 "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
501 "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
471 "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
502 "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
472 "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
503 "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
473
504
474 /* package java.lang */
505 /* package java.lang */
475 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
506 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
476 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
507 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
477 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
508 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
478 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
509 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
479 ),
510 ),
480 multiLineStrings: true,
511 multiLineStrings: true,
481 blockKeywords: words("catch class do else finally for forSome if match switch try while"),
512 blockKeywords: words("catch class do else finally for forSome if match switch try while"),
482 defKeywords: words("class def object package trait type val var"),
513 defKeywords: words("class def object package trait type val var"),
483 atoms: words("true false null"),
514 atoms: words("true false null"),
484 indentStatements: false,
515 indentStatements: false,
485 indentSwitch: false,
516 indentSwitch: false,
486 hooks: {
517 hooks: {
487 "@": function(stream) {
518 "@": function(stream) {
488 stream.eatWhile(/[\w\$_]/);
519 stream.eatWhile(/[\w\$_]/);
489 return "meta";
520 return "meta";
490 },
521 },
491 '"': function(stream, state) {
522 '"': function(stream, state) {
492 if (!stream.match('""')) return false;
523 if (!stream.match('""')) return false;
493 state.tokenize = tokenTripleString;
524 state.tokenize = tokenTripleString;
494 return state.tokenize(stream, state);
525 return state.tokenize(stream, state);
495 },
526 },
496 "'": function(stream) {
527 "'": function(stream) {
497 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
528 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
498 return "atom";
529 return "atom";
499 }
530 }
500 },
531 },
501 modeProps: {closeBrackets: {triples: '"'}}
532 modeProps: {closeBrackets: {triples: '"'}}
502 });
533 });
503
534
535 function tokenKotlinString(tripleString){
536 return function (stream, state) {
537 var escaped = false, next, end = false;
538 while (!stream.eol()) {
539 if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
540 if (tripleString && stream.match('"""')) {end = true; break;}
541 next = stream.next();
542 if(!escaped && next == "$" && stream.match('{'))
543 stream.skipTo("}");
544 escaped = !escaped && next == "\\" && !tripleString;
545 }
546 if (end || !tripleString)
547 state.tokenize = null;
548 return "string";
549 }
550 }
551
552 def("text/x-kotlin", {
553 name: "clike",
554 keywords: words(
555 /*keywords*/
556 "package as typealias class interface this super val " +
557 "var fun for is in This throw return " +
558 "break continue object if else while do try when !in !is as? " +
559
560 /*soft keywords*/
561 "file import where by get set abstract enum open inner override private public internal " +
562 "protected catch finally out final vararg reified dynamic companion constructor init " +
563 "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
564 "external annotation crossinline const operator infix"
565 ),
566 types: words(
567 /* package java.lang */
568 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
569 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
570 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
571 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
572 ),
573 intendSwitch: false,
574 indentStatements: false,
575 multiLineStrings: true,
576 blockKeywords: words("catch class do else finally for if where try while enum"),
577 defKeywords: words("class val var object package interface fun"),
578 atoms: words("true false null this"),
579 hooks: {
580 '"': function(stream, state) {
581 state.tokenize = tokenKotlinString(stream.match('""'));
582 return state.tokenize(stream, state);
583 }
584 },
585 modeProps: {closeBrackets: {triples: '"'}}
586 });
587
504 def(["x-shader/x-vertex", "x-shader/x-fragment"], {
588 def(["x-shader/x-vertex", "x-shader/x-fragment"], {
505 name: "clike",
589 name: "clike",
506 keywords: words("sampler1D sampler2D sampler3D samplerCube " +
590 keywords: words("sampler1D sampler2D sampler3D samplerCube " +
507 "sampler1DShadow sampler2DShadow " +
591 "sampler1DShadow sampler2DShadow " +
508 "const attribute uniform varying " +
592 "const attribute uniform varying " +
509 "break continue discard return " +
593 "break continue discard return " +
510 "for while do if else struct " +
594 "for while do if else struct " +
511 "in out inout"),
595 "in out inout"),
512 types: words("float int bool void " +
596 types: words("float int bool void " +
513 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
597 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
514 "mat2 mat3 mat4"),
598 "mat2 mat3 mat4"),
515 blockKeywords: words("for while do if else struct"),
599 blockKeywords: words("for while do if else struct"),
516 builtin: words("radians degrees sin cos tan asin acos atan " +
600 builtin: words("radians degrees sin cos tan asin acos atan " +
517 "pow exp log exp2 sqrt inversesqrt " +
601 "pow exp log exp2 sqrt inversesqrt " +
518 "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
602 "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
519 "length distance dot cross normalize ftransform faceforward " +
603 "length distance dot cross normalize ftransform faceforward " +
520 "reflect refract matrixCompMult " +
604 "reflect refract matrixCompMult " +
521 "lessThan lessThanEqual greaterThan greaterThanEqual " +
605 "lessThan lessThanEqual greaterThan greaterThanEqual " +
522 "equal notEqual any all not " +
606 "equal notEqual any all not " +
523 "texture1D texture1DProj texture1DLod texture1DProjLod " +
607 "texture1D texture1DProj texture1DLod texture1DProjLod " +
524 "texture2D texture2DProj texture2DLod texture2DProjLod " +
608 "texture2D texture2DProj texture2DLod texture2DProjLod " +
525 "texture3D texture3DProj texture3DLod texture3DProjLod " +
609 "texture3D texture3DProj texture3DLod texture3DProjLod " +
526 "textureCube textureCubeLod " +
610 "textureCube textureCubeLod " +
527 "shadow1D shadow2D shadow1DProj shadow2DProj " +
611 "shadow1D shadow2D shadow1DProj shadow2DProj " +
528 "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
612 "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
529 "dFdx dFdy fwidth " +
613 "dFdx dFdy fwidth " +
530 "noise1 noise2 noise3 noise4"),
614 "noise1 noise2 noise3 noise4"),
531 atoms: words("true false " +
615 atoms: words("true false " +
532 "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
616 "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
533 "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
617 "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
534 "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
618 "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
535 "gl_FogCoord gl_PointCoord " +
619 "gl_FogCoord gl_PointCoord " +
536 "gl_Position gl_PointSize gl_ClipVertex " +
620 "gl_Position gl_PointSize gl_ClipVertex " +
537 "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
621 "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
538 "gl_TexCoord gl_FogFragCoord " +
622 "gl_TexCoord gl_FogFragCoord " +
539 "gl_FragCoord gl_FrontFacing " +
623 "gl_FragCoord gl_FrontFacing " +
540 "gl_FragData gl_FragDepth " +
624 "gl_FragData gl_FragDepth " +
541 "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
625 "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
542 "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
626 "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
543 "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
627 "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
544 "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
628 "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
545 "gl_ProjectionMatrixInverseTranspose " +
629 "gl_ProjectionMatrixInverseTranspose " +
546 "gl_ModelViewProjectionMatrixInverseTranspose " +
630 "gl_ModelViewProjectionMatrixInverseTranspose " +
547 "gl_TextureMatrixInverseTranspose " +
631 "gl_TextureMatrixInverseTranspose " +
548 "gl_NormalScale gl_DepthRange gl_ClipPlane " +
632 "gl_NormalScale gl_DepthRange gl_ClipPlane " +
549 "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
633 "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
550 "gl_FrontLightModelProduct gl_BackLightModelProduct " +
634 "gl_FrontLightModelProduct gl_BackLightModelProduct " +
551 "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
635 "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
552 "gl_FogParameters " +
636 "gl_FogParameters " +
553 "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
637 "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
554 "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
638 "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
555 "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
639 "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
556 "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
640 "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
557 "gl_MaxDrawBuffers"),
641 "gl_MaxDrawBuffers"),
558 indentSwitch: false,
642 indentSwitch: false,
559 hooks: {"#": cppHook},
643 hooks: {"#": cppHook},
560 modeProps: {fold: ["brace", "include"]}
644 modeProps: {fold: ["brace", "include"]}
561 });
645 });
562
646
563 def("text/x-nesc", {
647 def("text/x-nesc", {
564 name: "clike",
648 name: "clike",
565 keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
649 keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
566 "implementation includes interface module new norace nx_struct nx_union post provides " +
650 "implementation includes interface module new norace nx_struct nx_union post provides " +
567 "signal task uses abstract extends"),
651 "signal task uses abstract extends"),
568 types: words(cTypes),
652 types: words(cTypes),
569 blockKeywords: words("case do else for if switch while struct"),
653 blockKeywords: words("case do else for if switch while struct"),
570 atoms: words("null true false"),
654 atoms: words("null true false"),
571 hooks: {"#": cppHook},
655 hooks: {"#": cppHook},
572 modeProps: {fold: ["brace", "include"]}
656 modeProps: {fold: ["brace", "include"]}
573 });
657 });
574
658
575 def("text/x-objectivec", {
659 def("text/x-objectivec", {
576 name: "clike",
660 name: "clike",
577 keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
661 keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
578 "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
662 "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
579 types: words(cTypes),
663 types: words(cTypes),
580 atoms: words("YES NO NULL NILL ON OFF true false"),
664 atoms: words("YES NO NULL NILL ON OFF true false"),
581 hooks: {
665 hooks: {
582 "@": function(stream) {
666 "@": function(stream) {
583 stream.eatWhile(/[\w\$]/);
667 stream.eatWhile(/[\w\$]/);
584 return "keyword";
668 return "keyword";
585 },
669 },
586 "#": cppHook
670 "#": cppHook,
671 indent: function(_state, ctx, textAfter) {
672 if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
673 }
587 },
674 },
588 modeProps: {fold: "brace"}
675 modeProps: {fold: "brace"}
589 });
676 });
590
677
678 def("text/x-squirrel", {
679 name: "clike",
680 keywords: words("base break clone continue const default delete enum extends function in class" +
681 " foreach local resume return this throw typeof yield constructor instanceof static"),
682 types: words(cTypes),
683 blockKeywords: words("case catch class else for foreach if switch try while"),
684 defKeywords: words("function local class"),
685 typeFirstDefinitions: true,
686 atoms: words("true false null"),
687 hooks: {"#": cppHook},
688 modeProps: {fold: ["brace", "include"]}
689 });
690
691 // Ceylon Strings need to deal with interpolation
692 var stringTokenizer = null;
693 function tokenCeylonString(type) {
694 return function(stream, state) {
695 var escaped = false, next, end = false;
696 while (!stream.eol()) {
697 if (!escaped && stream.match('"') &&
698 (type == "single" || stream.match('""'))) {
699 end = true;
700 break;
701 }
702 if (!escaped && stream.match('``')) {
703 stringTokenizer = tokenCeylonString(type);
704 end = true;
705 break;
706 }
707 next = stream.next();
708 escaped = type == "single" && !escaped && next == "\\";
709 }
710 if (end)
711 state.tokenize = null;
712 return "string";
713 }
714 }
715
716 def("text/x-ceylon", {
717 name: "clike",
718 keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
719 " exists extends finally for function given if import in interface is let module new" +
720 " nonempty object of out outer package return satisfies super switch then this throw" +
721 " try value void while"),
722 types: function(word) {
723 // In Ceylon all identifiers that start with an uppercase are types
724 var first = word.charAt(0);
725 return (first === first.toUpperCase() && first !== first.toLowerCase());
726 },
727 blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
728 defKeywords: words("class dynamic function interface module object package value"),
729 builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
730 " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
731 isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
732 isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
733 numberStart: /[\d#$]/,
734 number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
735 multiLineStrings: true,
736 typeFirstDefinitions: true,
737 atoms: words("true false null larger smaller equal empty finished"),
738 indentSwitch: false,
739 styleDefs: false,
740 hooks: {
741 "@": function(stream) {
742 stream.eatWhile(/[\w\$_]/);
743 return "meta";
744 },
745 '"': function(stream, state) {
746 state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
747 return state.tokenize(stream, state);
748 },
749 '`': function(stream, state) {
750 if (!stringTokenizer || !stream.match('`')) return false;
751 state.tokenize = stringTokenizer;
752 stringTokenizer = null;
753 return state.tokenize(stream, state);
754 },
755 "'": function(stream) {
756 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
757 return "atom";
758 },
759 token: function(_stream, state, style) {
760 if ((style == "variable" || style == "variable-3") &&
761 state.prevToken == ".") {
762 return "variable-2";
763 }
764 }
765 },
766 modeProps: {
767 fold: ["brace", "import"],
768 closeBrackets: {triples: '"'}
769 }
770 });
771
591 });
772 });
@@ -1,244 +1,249 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 /**
4 /**
5 * Author: Hans Engel
5 * Author: Hans Engel
6 * 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)
7 */
7 */
8
8
9 (function(mod) {
9 (function(mod) {
10 if (typeof exports == "object" && typeof module == "object") // CommonJS
10 if (typeof exports == "object" && typeof module == "object") // CommonJS
11 mod(require("../../lib/codemirror"));
11 mod(require("../../lib/codemirror"));
12 else if (typeof define == "function" && define.amd) // AMD
12 else if (typeof define == "function" && define.amd) // AMD
13 define(["../../lib/codemirror"], mod);
13 define(["../../lib/codemirror"], mod);
14 else // Plain browser env
14 else // Plain browser env
15 mod(CodeMirror);
15 mod(CodeMirror);
16 })(function(CodeMirror) {
16 })(function(CodeMirror) {
17 "use strict";
17 "use strict";
18
18
19 CodeMirror.defineMode("clojure", function (options) {
19 CodeMirror.defineMode("clojure", function (options) {
20 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
20 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
21 ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
21 ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
22 var INDENT_WORD_SKIP = options.indentUnit || 2;
22 var INDENT_WORD_SKIP = options.indentUnit || 2;
23 var NORMAL_INDENT_UNIT = options.indentUnit || 2;
23 var NORMAL_INDENT_UNIT = options.indentUnit || 2;
24
24
25 function makeKeywords(str) {
25 function makeKeywords(str) {
26 var obj = {}, words = str.split(" ");
26 var obj = {}, words = str.split(" ");
27 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
27 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
28 return obj;
28 return obj;
29 }
29 }
30
30
31 var atoms = makeKeywords("true false nil");
31 var atoms = makeKeywords("true false nil");
32
32
33 var keywords = makeKeywords(
33 var keywords = makeKeywords(
34 "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle");
34 "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle");
35
35
36 var builtins = makeKeywords(
36 var builtins = makeKeywords(
37 "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>");
37 "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>");
38
38
39 var indentKeys = makeKeywords(
39 var indentKeys = makeKeywords(
40 // Built-ins
40 // Built-ins
41 "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " +
41 "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " +
42
42
43 // Binding forms
43 // Binding forms
44 "let letfn binding loop for doseq dotimes when-let if-let " +
44 "let letfn binding loop for doseq dotimes when-let if-let " +
45
45
46 // Data structures
46 // Data structures
47 "defstruct struct-map assoc " +
47 "defstruct struct-map assoc " +
48
48
49 // clojure.test
49 // clojure.test
50 "testing deftest " +
50 "testing deftest " +
51
51
52 // contrib
52 // contrib
53 "handler-case handle dotrace deftrace");
53 "handler-case handle dotrace deftrace");
54
54
55 var tests = {
55 var tests = {
56 digit: /\d/,
56 digit: /\d/,
57 digit_or_colon: /[\d:]/,
57 digit_or_colon: /[\d:]/,
58 hex: /[0-9a-f]/i,
58 hex: /[0-9a-f]/i,
59 sign: /[+-]/,
59 sign: /[+-]/,
60 exponent: /e/i,
60 exponent: /e/i,
61 keyword_char: /[^\s\(\[\;\)\]]/,
61 keyword_char: /[^\s\(\[\;\)\]]/,
62 symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/
62 symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/,
63 block_indent: /^(?:def|with)[^\/]+$|\/(?:def|with)/
63 };
64 };
64
65
65 function stateStack(indent, type, prev) { // represents a state stack object
66 function stateStack(indent, type, prev) { // represents a state stack object
66 this.indent = indent;
67 this.indent = indent;
67 this.type = type;
68 this.type = type;
68 this.prev = prev;
69 this.prev = prev;
69 }
70 }
70
71
71 function pushStack(state, indent, type) {
72 function pushStack(state, indent, type) {
72 state.indentStack = new stateStack(indent, type, state.indentStack);
73 state.indentStack = new stateStack(indent, type, state.indentStack);
73 }
74 }
74
75
75 function popStack(state) {
76 function popStack(state) {
76 state.indentStack = state.indentStack.prev;
77 state.indentStack = state.indentStack.prev;
77 }
78 }
78
79
79 function isNumber(ch, stream){
80 function isNumber(ch, stream){
80 // hex
81 // hex
81 if ( ch === '0' && stream.eat(/x/i) ) {
82 if ( ch === '0' && stream.eat(/x/i) ) {
82 stream.eatWhile(tests.hex);
83 stream.eatWhile(tests.hex);
83 return true;
84 return true;
84 }
85 }
85
86
86 // leading sign
87 // leading sign
87 if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {
88 if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {
88 stream.eat(tests.sign);
89 stream.eat(tests.sign);
89 ch = stream.next();
90 ch = stream.next();
90 }
91 }
91
92
92 if ( tests.digit.test(ch) ) {
93 if ( tests.digit.test(ch) ) {
93 stream.eat(ch);
94 stream.eat(ch);
94 stream.eatWhile(tests.digit);
95 stream.eatWhile(tests.digit);
95
96
96 if ( '.' == stream.peek() ) {
97 if ( '.' == stream.peek() ) {
97 stream.eat('.');
98 stream.eat('.');
98 stream.eatWhile(tests.digit);
99 stream.eatWhile(tests.digit);
100 } else if ('/' == stream.peek() ) {
101 stream.eat('/');
102 stream.eatWhile(tests.digit);
99 }
103 }
100
104
101 if ( stream.eat(tests.exponent) ) {
105 if ( stream.eat(tests.exponent) ) {
102 stream.eat(tests.sign);
106 stream.eat(tests.sign);
103 stream.eatWhile(tests.digit);
107 stream.eatWhile(tests.digit);
104 }
108 }
105
109
106 return true;
110 return true;
107 }
111 }
108
112
109 return false;
113 return false;
110 }
114 }
111
115
112 // Eat character that starts after backslash \
116 // Eat character that starts after backslash \
113 function eatCharacter(stream) {
117 function eatCharacter(stream) {
114 var first = stream.next();
118 var first = stream.next();
115 // Read special literals: backspace, newline, space, return.
119 // Read special literals: backspace, newline, space, return.
116 // Just read all lowercase letters.
120 // Just read all lowercase letters.
117 if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
121 if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
118 return;
122 return;
119 }
123 }
120 // Read unicode character: \u1000 \uA0a1
124 // Read unicode character: \u1000 \uA0a1
121 if (first === "u") {
125 if (first === "u") {
122 stream.match(/[0-9a-z]{4}/i, true);
126 stream.match(/[0-9a-z]{4}/i, true);
123 }
127 }
124 }
128 }
125
129
126 return {
130 return {
127 startState: function () {
131 startState: function () {
128 return {
132 return {
129 indentStack: null,
133 indentStack: null,
130 indentation: 0,
134 indentation: 0,
131 mode: false
135 mode: false
132 };
136 };
133 },
137 },
134
138
135 token: function (stream, state) {
139 token: function (stream, state) {
136 if (state.indentStack == null && stream.sol()) {
140 if (state.indentStack == null && stream.sol()) {
137 // update indentation, but only if indentStack is empty
141 // update indentation, but only if indentStack is empty
138 state.indentation = stream.indentation();
142 state.indentation = stream.indentation();
139 }
143 }
140
144
141 // skip spaces
145 // skip spaces
142 if (stream.eatSpace()) {
146 if (state.mode != "string" && stream.eatSpace()) {
143 return null;
147 return null;
144 }
148 }
145 var returnType = null;
149 var returnType = null;
146
150
147 switch(state.mode){
151 switch(state.mode){
148 case "string": // multi-line string parsing mode
152 case "string": // multi-line string parsing mode
149 var next, escaped = false;
153 var next, escaped = false;
150 while ((next = stream.next()) != null) {
154 while ((next = stream.next()) != null) {
151 if (next == "\"" && !escaped) {
155 if (next == "\"" && !escaped) {
152
156
153 state.mode = false;
157 state.mode = false;
154 break;
158 break;
155 }
159 }
156 escaped = !escaped && next == "\\";
160 escaped = !escaped && next == "\\";
157 }
161 }
158 returnType = STRING; // continue on in string mode
162 returnType = STRING; // continue on in string mode
159 break;
163 break;
160 default: // default parsing mode
164 default: // default parsing mode
161 var ch = stream.next();
165 var ch = stream.next();
162
166
163 if (ch == "\"") {
167 if (ch == "\"") {
164 state.mode = "string";
168 state.mode = "string";
165 returnType = STRING;
169 returnType = STRING;
166 } else if (ch == "\\") {
170 } else if (ch == "\\") {
167 eatCharacter(stream);
171 eatCharacter(stream);
168 returnType = CHARACTER;
172 returnType = CHARACTER;
169 } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
173 } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
170 returnType = ATOM;
174 returnType = ATOM;
171 } else if (ch == ";") { // comment
175 } else if (ch == ";") { // comment
172 stream.skipToEnd(); // rest of the line is a comment
176 stream.skipToEnd(); // rest of the line is a comment
173 returnType = COMMENT;
177 returnType = COMMENT;
174 } else if (isNumber(ch,stream)){
178 } else if (isNumber(ch,stream)){
175 returnType = NUMBER;
179 returnType = NUMBER;
176 } else if (ch == "(" || ch == "[" || ch == "{" ) {
180 } else if (ch == "(" || ch == "[" || ch == "{" ) {
177 var keyWord = '', indentTemp = stream.column(), letter;
181 var keyWord = '', indentTemp = stream.column(), letter;
178 /**
182 /**
179 Either
183 Either
180 (indent-word ..
184 (indent-word ..
181 (non-indent-word ..
185 (non-indent-word ..
182 (;something else, bracket, etc.
186 (;something else, bracket, etc.
183 */
187 */
184
188
185 if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) {
189 if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) {
186 keyWord += letter;
190 keyWord += letter;
187 }
191 }
188
192
189 if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) ||
193 if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) ||
190 /^(?:def|with)/.test(keyWord))) { // indent-word
194 tests.block_indent.test(keyWord))) { // indent-word
191 pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
195 pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
192 } else { // non-indent word
196 } else { // non-indent word
193 // we continue eating the spaces
197 // we continue eating the spaces
194 stream.eatSpace();
198 stream.eatSpace();
195 if (stream.eol() || stream.peek() == ";") {
199 if (stream.eol() || stream.peek() == ";") {
196 // nothing significant after
200 // nothing significant after
197 // we restart indentation the user defined spaces after
201 // we restart indentation the user defined spaces after
198 pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
202 pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
199 } else {
203 } else {
200 pushStack(state, indentTemp + stream.current().length, ch); // else we match
204 pushStack(state, indentTemp + stream.current().length, ch); // else we match
201 }
205 }
202 }
206 }
203 stream.backUp(stream.current().length - 1); // undo all the eating
207 stream.backUp(stream.current().length - 1); // undo all the eating
204
208
205 returnType = BRACKET;
209 returnType = BRACKET;
206 } else if (ch == ")" || ch == "]" || ch == "}") {
210 } else if (ch == ")" || ch == "]" || ch == "}") {
207 returnType = BRACKET;
211 returnType = BRACKET;
208 if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) {
212 if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) {
209 popStack(state);
213 popStack(state);
210 }
214 }
211 } else if ( ch == ":" ) {
215 } else if ( ch == ":" ) {
212 stream.eatWhile(tests.symbol);
216 stream.eatWhile(tests.symbol);
213 return ATOM;
217 return ATOM;
214 } else {
218 } else {
215 stream.eatWhile(tests.symbol);
219 stream.eatWhile(tests.symbol);
216
220
217 if (keywords && keywords.propertyIsEnumerable(stream.current())) {
221 if (keywords && keywords.propertyIsEnumerable(stream.current())) {
218 returnType = KEYWORD;
222 returnType = KEYWORD;
219 } else if (builtins && builtins.propertyIsEnumerable(stream.current())) {
223 } else if (builtins && builtins.propertyIsEnumerable(stream.current())) {
220 returnType = BUILTIN;
224 returnType = BUILTIN;
221 } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
225 } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
222 returnType = ATOM;
226 returnType = ATOM;
223 } else {
227 } else {
224 returnType = VAR;
228 returnType = VAR;
225 }
229 }
226 }
230 }
227 }
231 }
228
232
229 return returnType;
233 return returnType;
230 },
234 },
231
235
232 indent: function (state) {
236 indent: function (state) {
233 if (state.indentStack == null) return state.indentation;
237 if (state.indentStack == null) return state.indentation;
234 return state.indentStack.indent;
238 return state.indentStack.indent;
235 },
239 },
236
240
237 closeBrackets: {pairs: "()[]{}\"\""},
241 closeBrackets: {pairs: "()[]{}\"\""},
238 lineComment: ";;"
242 lineComment: ";;"
239 };
243 };
240 });
244 });
241
245
242 CodeMirror.defineMIME("text/x-clojure", "clojure");
246 CodeMirror.defineMIME("text/x-clojure", "clojure");
247 CodeMirror.defineMIME("text/x-clojurescript", "clojure");
243
248
244 });
249 });
@@ -1,369 +1,355 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 /**
4 /**
5 * Link to the project's GitHub page:
5 * Link to the project's GitHub page:
6 * https://github.com/pickhardt/coffeescript-codemirror-mode
6 * https://github.com/pickhardt/coffeescript-codemirror-mode
7 */
7 */
8 (function(mod) {
8 (function(mod) {
9 if (typeof exports == "object" && typeof module == "object") // CommonJS
9 if (typeof exports == "object" && typeof module == "object") // CommonJS
10 mod(require("../../lib/codemirror"));
10 mod(require("../../lib/codemirror"));
11 else if (typeof define == "function" && define.amd) // AMD
11 else if (typeof define == "function" && define.amd) // AMD
12 define(["../../lib/codemirror"], mod);
12 define(["../../lib/codemirror"], mod);
13 else // Plain browser env
13 else // Plain browser env
14 mod(CodeMirror);
14 mod(CodeMirror);
15 })(function(CodeMirror) {
15 })(function(CodeMirror) {
16 "use strict";
16 "use strict";
17
17
18 CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
18 CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
19 var ERRORCLASS = "error";
19 var ERRORCLASS = "error";
20
20
21 function wordRegexp(words) {
21 function wordRegexp(words) {
22 return new RegExp("^((" + words.join(")|(") + "))\\b");
22 return new RegExp("^((" + words.join(")|(") + "))\\b");
23 }
23 }
24
24
25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
28 var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;
28 var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;
29
29
30 var wordOperators = wordRegexp(["and", "or", "not",
30 var wordOperators = wordRegexp(["and", "or", "not",
31 "is", "isnt", "in",
31 "is", "isnt", "in",
32 "instanceof", "typeof"]);
32 "instanceof", "typeof"]);
33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
34 "switch", "try", "catch", "finally", "class"];
34 "switch", "try", "catch", "finally", "class"];
35 var commonKeywords = ["break", "by", "continue", "debugger", "delete",
35 var commonKeywords = ["break", "by", "continue", "debugger", "delete",
36 "do", "in", "of", "new", "return", "then",
36 "do", "in", "of", "new", "return", "then",
37 "this", "@", "throw", "when", "until", "extends"];
37 "this", "@", "throw", "when", "until", "extends"];
38
38
39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
40
40
41 indentKeywords = wordRegexp(indentKeywords);
41 indentKeywords = wordRegexp(indentKeywords);
42
42
43
43
44 var stringPrefixes = /^('{3}|\"{3}|['\"])/;
44 var stringPrefixes = /^('{3}|\"{3}|['\"])/;
45 var regexPrefixes = /^(\/{3}|\/)/;
45 var regexPrefixes = /^(\/{3}|\/)/;
46 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"];
47 var constants = wordRegexp(commonConstants);
47 var constants = wordRegexp(commonConstants);
48
48
49 // Tokenizers
49 // Tokenizers
50 function tokenBase(stream, state) {
50 function tokenBase(stream, state) {
51 // Handle scope changes
51 // Handle scope changes
52 if (stream.sol()) {
52 if (stream.sol()) {
53 if (state.scope.align === null) state.scope.align = false;
53 if (state.scope.align === null) state.scope.align = false;
54 var scopeOffset = state.scope.offset;
54 var scopeOffset = state.scope.offset;
55 if (stream.eatSpace()) {
55 if (stream.eatSpace()) {
56 var lineOffset = stream.indentation();
56 var lineOffset = stream.indentation();
57 if (lineOffset > scopeOffset && state.scope.type == "coffee") {
57 if (lineOffset > scopeOffset && state.scope.type == "coffee") {
58 return "indent";
58 return "indent";
59 } else if (lineOffset < scopeOffset) {
59 } else if (lineOffset < scopeOffset) {
60 return "dedent";
60 return "dedent";
61 }
61 }
62 return null;
62 return null;
63 } else {
63 } else {
64 if (scopeOffset > 0) {
64 if (scopeOffset > 0) {
65 dedent(stream, state);
65 dedent(stream, state);
66 }
66 }
67 }
67 }
68 }
68 }
69 if (stream.eatSpace()) {
69 if (stream.eatSpace()) {
70 return null;
70 return null;
71 }
71 }
72
72
73 var ch = stream.peek();
73 var ch = stream.peek();
74
74
75 // Handle docco title comment (single line)
75 // Handle docco title comment (single line)
76 if (stream.match("####")) {
76 if (stream.match("####")) {
77 stream.skipToEnd();
77 stream.skipToEnd();
78 return "comment";
78 return "comment";
79 }
79 }
80
80
81 // Handle multi line comments
81 // Handle multi line comments
82 if (stream.match("###")) {
82 if (stream.match("###")) {
83 state.tokenize = longComment;
83 state.tokenize = longComment;
84 return state.tokenize(stream, state);
84 return state.tokenize(stream, state);
85 }
85 }
86
86
87 // Single line comment
87 // Single line comment
88 if (ch === "#") {
88 if (ch === "#") {
89 stream.skipToEnd();
89 stream.skipToEnd();
90 return "comment";
90 return "comment";
91 }
91 }
92
92
93 // Handle number literals
93 // Handle number literals
94 if (stream.match(/^-?[0-9\.]/, false)) {
94 if (stream.match(/^-?[0-9\.]/, false)) {
95 var floatLiteral = false;
95 var floatLiteral = false;
96 // Floats
96 // Floats
97 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
97 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
98 floatLiteral = true;
98 floatLiteral = true;
99 }
99 }
100 if (stream.match(/^-?\d+\.\d*/)) {
100 if (stream.match(/^-?\d+\.\d*/)) {
101 floatLiteral = true;
101 floatLiteral = true;
102 }
102 }
103 if (stream.match(/^-?\.\d+/)) {
103 if (stream.match(/^-?\.\d+/)) {
104 floatLiteral = true;
104 floatLiteral = true;
105 }
105 }
106
106
107 if (floatLiteral) {
107 if (floatLiteral) {
108 // prevent from getting extra . on 1..
108 // prevent from getting extra . on 1..
109 if (stream.peek() == "."){
109 if (stream.peek() == "."){
110 stream.backUp(1);
110 stream.backUp(1);
111 }
111 }
112 return "number";
112 return "number";
113 }
113 }
114 // Integers
114 // Integers
115 var intLiteral = false;
115 var intLiteral = false;
116 // Hex
116 // Hex
117 if (stream.match(/^-?0x[0-9a-f]+/i)) {
117 if (stream.match(/^-?0x[0-9a-f]+/i)) {
118 intLiteral = true;
118 intLiteral = true;
119 }
119 }
120 // Decimal
120 // Decimal
121 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
121 if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
122 intLiteral = true;
122 intLiteral = true;
123 }
123 }
124 // Zero by itself with no other piece of number.
124 // Zero by itself with no other piece of number.
125 if (stream.match(/^-?0(?![\dx])/i)) {
125 if (stream.match(/^-?0(?![\dx])/i)) {
126 intLiteral = true;
126 intLiteral = true;
127 }
127 }
128 if (intLiteral) {
128 if (intLiteral) {
129 return "number";
129 return "number";
130 }
130 }
131 }
131 }
132
132
133 // Handle strings
133 // Handle strings
134 if (stream.match(stringPrefixes)) {
134 if (stream.match(stringPrefixes)) {
135 state.tokenize = tokenFactory(stream.current(), false, "string");
135 state.tokenize = tokenFactory(stream.current(), false, "string");
136 return state.tokenize(stream, state);
136 return state.tokenize(stream, state);
137 }
137 }
138 // Handle regex literals
138 // Handle regex literals
139 if (stream.match(regexPrefixes)) {
139 if (stream.match(regexPrefixes)) {
140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
141 state.tokenize = tokenFactory(stream.current(), true, "string-2");
141 state.tokenize = tokenFactory(stream.current(), true, "string-2");
142 return state.tokenize(stream, state);
142 return state.tokenize(stream, state);
143 } else {
143 } else {
144 stream.backUp(1);
144 stream.backUp(1);
145 }
145 }
146 }
146 }
147
147
148
149
148 // Handle operators and delimiters
150 // Handle operators and delimiters
149 if (stream.match(operators) || stream.match(wordOperators)) {
151 if (stream.match(operators) || stream.match(wordOperators)) {
150 return "operator";
152 return "operator";
151 }
153 }
152 if (stream.match(delimiters)) {
154 if (stream.match(delimiters)) {
153 return "punctuation";
155 return "punctuation";
154 }
156 }
155
157
156 if (stream.match(constants)) {
158 if (stream.match(constants)) {
157 return "atom";
159 return "atom";
158 }
160 }
159
161
162 if (stream.match(atProp) || state.prop && stream.match(identifiers)) {
163 return "property";
164 }
165
160 if (stream.match(keywords)) {
166 if (stream.match(keywords)) {
161 return "keyword";
167 return "keyword";
162 }
168 }
163
169
164 if (stream.match(identifiers)) {
170 if (stream.match(identifiers)) {
165 return "variable";
171 return "variable";
166 }
172 }
167
173
168 if (stream.match(properties)) {
169 return "property";
170 }
171
172 // Handle non-detected items
174 // Handle non-detected items
173 stream.next();
175 stream.next();
174 return ERRORCLASS;
176 return ERRORCLASS;
175 }
177 }
176
178
177 function tokenFactory(delimiter, singleline, outclass) {
179 function tokenFactory(delimiter, singleline, outclass) {
178 return function(stream, state) {
180 return function(stream, state) {
179 while (!stream.eol()) {
181 while (!stream.eol()) {
180 stream.eatWhile(/[^'"\/\\]/);
182 stream.eatWhile(/[^'"\/\\]/);
181 if (stream.eat("\\")) {
183 if (stream.eat("\\")) {
182 stream.next();
184 stream.next();
183 if (singleline && stream.eol()) {
185 if (singleline && stream.eol()) {
184 return outclass;
186 return outclass;
185 }
187 }
186 } else if (stream.match(delimiter)) {
188 } else if (stream.match(delimiter)) {
187 state.tokenize = tokenBase;
189 state.tokenize = tokenBase;
188 return outclass;
190 return outclass;
189 } else {
191 } else {
190 stream.eat(/['"\/]/);
192 stream.eat(/['"\/]/);
191 }
193 }
192 }
194 }
193 if (singleline) {
195 if (singleline) {
194 if (parserConf.singleLineStringErrors) {
196 if (parserConf.singleLineStringErrors) {
195 outclass = ERRORCLASS;
197 outclass = ERRORCLASS;
196 } else {
198 } else {
197 state.tokenize = tokenBase;
199 state.tokenize = tokenBase;
198 }
200 }
199 }
201 }
200 return outclass;
202 return outclass;
201 };
203 };
202 }
204 }
203
205
204 function longComment(stream, state) {
206 function longComment(stream, state) {
205 while (!stream.eol()) {
207 while (!stream.eol()) {
206 stream.eatWhile(/[^#]/);
208 stream.eatWhile(/[^#]/);
207 if (stream.match("###")) {
209 if (stream.match("###")) {
208 state.tokenize = tokenBase;
210 state.tokenize = tokenBase;
209 break;
211 break;
210 }
212 }
211 stream.eatWhile("#");
213 stream.eatWhile("#");
212 }
214 }
213 return "comment";
215 return "comment";
214 }
216 }
215
217
216 function indent(stream, state, type) {
218 function indent(stream, state, type) {
217 type = type || "coffee";
219 type = type || "coffee";
218 var offset = 0, align = false, alignOffset = null;
220 var offset = 0, align = false, alignOffset = null;
219 for (var scope = state.scope; scope; scope = scope.prev) {
221 for (var scope = state.scope; scope; scope = scope.prev) {
220 if (scope.type === "coffee" || scope.type == "}") {
222 if (scope.type === "coffee" || scope.type == "}") {
221 offset = scope.offset + conf.indentUnit;
223 offset = scope.offset + conf.indentUnit;
222 break;
224 break;
223 }
225 }
224 }
226 }
225 if (type !== "coffee") {
227 if (type !== "coffee") {
226 align = null;
228 align = null;
227 alignOffset = stream.column() + stream.current().length;
229 alignOffset = stream.column() + stream.current().length;
228 } else if (state.scope.align) {
230 } else if (state.scope.align) {
229 state.scope.align = false;
231 state.scope.align = false;
230 }
232 }
231 state.scope = {
233 state.scope = {
232 offset: offset,
234 offset: offset,
233 type: type,
235 type: type,
234 prev: state.scope,
236 prev: state.scope,
235 align: align,
237 align: align,
236 alignOffset: alignOffset
238 alignOffset: alignOffset
237 };
239 };
238 }
240 }
239
241
240 function dedent(stream, state) {
242 function dedent(stream, state) {
241 if (!state.scope.prev) return;
243 if (!state.scope.prev) return;
242 if (state.scope.type === "coffee") {
244 if (state.scope.type === "coffee") {
243 var _indent = stream.indentation();
245 var _indent = stream.indentation();
244 var matched = false;
246 var matched = false;
245 for (var scope = state.scope; scope; scope = scope.prev) {
247 for (var scope = state.scope; scope; scope = scope.prev) {
246 if (_indent === scope.offset) {
248 if (_indent === scope.offset) {
247 matched = true;
249 matched = true;
248 break;
250 break;
249 }
251 }
250 }
252 }
251 if (!matched) {
253 if (!matched) {
252 return true;
254 return true;
253 }
255 }
254 while (state.scope.prev && state.scope.offset !== _indent) {
256 while (state.scope.prev && state.scope.offset !== _indent) {
255 state.scope = state.scope.prev;
257 state.scope = state.scope.prev;
256 }
258 }
257 return false;
259 return false;
258 } else {
260 } else {
259 state.scope = state.scope.prev;
261 state.scope = state.scope.prev;
260 return false;
262 return false;
261 }
263 }
262 }
264 }
263
265
264 function tokenLexer(stream, state) {
266 function tokenLexer(stream, state) {
265 var style = state.tokenize(stream, state);
267 var style = state.tokenize(stream, state);
266 var current = stream.current();
268 var current = stream.current();
267
269
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 }
277 }
278
279 // Handle scope changes.
270 // Handle scope changes.
280 if (current === "return") {
271 if (current === "return") {
281 state.dedent = true;
272 state.dedent = true;
282 }
273 }
283 if (((current === "->" || current === "=>") &&
274 if (((current === "->" || current === "=>") && stream.eol())
284 !state.lambda &&
285 !stream.peek())
286 || style === "indent") {
275 || style === "indent") {
287 indent(stream, state);
276 indent(stream, state);
288 }
277 }
289 var delimiter_index = "[({".indexOf(current);
278 var delimiter_index = "[({".indexOf(current);
290 if (delimiter_index !== -1) {
279 if (delimiter_index !== -1) {
291 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
280 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
292 }
281 }
293 if (indentKeywords.exec(current)){
282 if (indentKeywords.exec(current)){
294 indent(stream, state);
283 indent(stream, state);
295 }
284 }
296 if (current == "then"){
285 if (current == "then"){
297 dedent(stream, state);
286 dedent(stream, state);
298 }
287 }
299
288
300
289
301 if (style === "dedent") {
290 if (style === "dedent") {
302 if (dedent(stream, state)) {
291 if (dedent(stream, state)) {
303 return ERRORCLASS;
292 return ERRORCLASS;
304 }
293 }
305 }
294 }
306 delimiter_index = "])}".indexOf(current);
295 delimiter_index = "])}".indexOf(current);
307 if (delimiter_index !== -1) {
296 if (delimiter_index !== -1) {
308 while (state.scope.type == "coffee" && state.scope.prev)
297 while (state.scope.type == "coffee" && state.scope.prev)
309 state.scope = state.scope.prev;
298 state.scope = state.scope.prev;
310 if (state.scope.type == current)
299 if (state.scope.type == current)
311 state.scope = state.scope.prev;
300 state.scope = state.scope.prev;
312 }
301 }
313 if (state.dedent && stream.eol()) {
302 if (state.dedent && stream.eol()) {
314 if (state.scope.type == "coffee" && state.scope.prev)
303 if (state.scope.type == "coffee" && state.scope.prev)
315 state.scope = state.scope.prev;
304 state.scope = state.scope.prev;
316 state.dedent = false;
305 state.dedent = false;
317 }
306 }
318
307
319 return style;
308 return style;
320 }
309 }
321
310
322 var external = {
311 var external = {
323 startState: function(basecolumn) {
312 startState: function(basecolumn) {
324 return {
313 return {
325 tokenize: tokenBase,
314 tokenize: tokenBase,
326 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
315 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
327 lastToken: null,
316 prop: false,
328 lambda: false,
329 dedent: 0
317 dedent: 0
330 };
318 };
331 },
319 },
332
320
333 token: function(stream, state) {
321 token: function(stream, state) {
334 var fillAlign = state.scope.align === null && state.scope;
322 var fillAlign = state.scope.align === null && state.scope;
335 if (fillAlign && stream.sol()) fillAlign.align = false;
323 if (fillAlign && stream.sol()) fillAlign.align = false;
336
324
337 var style = tokenLexer(stream, state);
325 var style = tokenLexer(stream, state);
338 if (fillAlign && style && style != "comment") fillAlign.align = true;
326 if (style && style != "comment") {
339
327 if (fillAlign) fillAlign.align = true;
340 state.lastToken = {style:style, content: stream.current()};
328 state.prop = style == "punctuation" && stream.current() == "."
341
342 if (stream.eol() && stream.lambda) {
343 state.lambda = false;
344 }
329 }
345
330
346 return style;
331 return style;
347 },
332 },
348
333
349 indent: function(state, text) {
334 indent: function(state, text) {
350 if (state.tokenize != tokenBase) return 0;
335 if (state.tokenize != tokenBase) return 0;
351 var scope = state.scope;
336 var scope = state.scope;
352 var closer = text && "])}".indexOf(text.charAt(0)) > -1;
337 var closer = text && "])}".indexOf(text.charAt(0)) > -1;
353 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
338 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
354 var closes = closer && scope.type === text.charAt(0);
339 var closes = closer && scope.type === text.charAt(0);
355 if (scope.align)
340 if (scope.align)
356 return scope.alignOffset - (closes ? 1 : 0);
341 return scope.alignOffset - (closes ? 1 : 0);
357 else
342 else
358 return (closes ? scope.prev : scope).offset;
343 return (closes ? scope.prev : scope).offset;
359 },
344 },
360
345
361 lineComment: "#",
346 lineComment: "#",
362 fold: "indent"
347 fold: "indent"
363 };
348 };
364 return external;
349 return external;
365 });
350 });
366
351
367 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
352 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
353 CodeMirror.defineMIME("text/coffeescript", "coffeescript");
368
354
369 });
355 });
@@ -1,754 +1,825 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("css", function(config, parserConfig) {
14 CodeMirror.defineMode("css", function(config, parserConfig) {
15 var inline = parserConfig.inline
15 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
16 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
16
17
17 var indentUnit = config.indentUnit,
18 var indentUnit = config.indentUnit,
18 tokenHooks = parserConfig.tokenHooks,
19 tokenHooks = parserConfig.tokenHooks,
19 documentTypes = parserConfig.documentTypes || {},
20 documentTypes = parserConfig.documentTypes || {},
20 mediaTypes = parserConfig.mediaTypes || {},
21 mediaTypes = parserConfig.mediaTypes || {},
21 mediaFeatures = parserConfig.mediaFeatures || {},
22 mediaFeatures = parserConfig.mediaFeatures || {},
23 mediaValueKeywords = parserConfig.mediaValueKeywords || {},
22 propertyKeywords = parserConfig.propertyKeywords || {},
24 propertyKeywords = parserConfig.propertyKeywords || {},
23 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
25 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
24 fontProperties = parserConfig.fontProperties || {},
26 fontProperties = parserConfig.fontProperties || {},
25 counterDescriptors = parserConfig.counterDescriptors || {},
27 counterDescriptors = parserConfig.counterDescriptors || {},
26 colorKeywords = parserConfig.colorKeywords || {},
28 colorKeywords = parserConfig.colorKeywords || {},
27 valueKeywords = parserConfig.valueKeywords || {},
29 valueKeywords = parserConfig.valueKeywords || {},
28 allowNested = parserConfig.allowNested;
30 allowNested = parserConfig.allowNested,
31 supportsAtComponent = parserConfig.supportsAtComponent === true;
29
32
30 var type, override;
33 var type, override;
31 function ret(style, tp) { type = tp; return style; }
34 function ret(style, tp) { type = tp; return style; }
32
35
33 // Tokenizers
36 // Tokenizers
34
37
35 function tokenBase(stream, state) {
38 function tokenBase(stream, state) {
36 var ch = stream.next();
39 var ch = stream.next();
37 if (tokenHooks[ch]) {
40 if (tokenHooks[ch]) {
38 var result = tokenHooks[ch](stream, state);
41 var result = tokenHooks[ch](stream, state);
39 if (result !== false) return result;
42 if (result !== false) return result;
40 }
43 }
41 if (ch == "@") {
44 if (ch == "@") {
42 stream.eatWhile(/[\w\\\-]/);
45 stream.eatWhile(/[\w\\\-]/);
43 return ret("def", stream.current());
46 return ret("def", stream.current());
44 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
47 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
45 return ret(null, "compare");
48 return ret(null, "compare");
46 } else if (ch == "\"" || ch == "'") {
49 } else if (ch == "\"" || ch == "'") {
47 state.tokenize = tokenString(ch);
50 state.tokenize = tokenString(ch);
48 return state.tokenize(stream, state);
51 return state.tokenize(stream, state);
49 } else if (ch == "#") {
52 } else if (ch == "#") {
50 stream.eatWhile(/[\w\\\-]/);
53 stream.eatWhile(/[\w\\\-]/);
51 return ret("atom", "hash");
54 return ret("atom", "hash");
52 } else if (ch == "!") {
55 } else if (ch == "!") {
53 stream.match(/^\s*\w*/);
56 stream.match(/^\s*\w*/);
54 return ret("keyword", "important");
57 return ret("keyword", "important");
55 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
58 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
56 stream.eatWhile(/[\w.%]/);
59 stream.eatWhile(/[\w.%]/);
57 return ret("number", "unit");
60 return ret("number", "unit");
58 } else if (ch === "-") {
61 } else if (ch === "-") {
59 if (/[\d.]/.test(stream.peek())) {
62 if (/[\d.]/.test(stream.peek())) {
60 stream.eatWhile(/[\w.%]/);
63 stream.eatWhile(/[\w.%]/);
61 return ret("number", "unit");
64 return ret("number", "unit");
62 } else if (stream.match(/^-[\w\\\-]+/)) {
65 } else if (stream.match(/^-[\w\\\-]+/)) {
63 stream.eatWhile(/[\w\\\-]/);
66 stream.eatWhile(/[\w\\\-]/);
64 if (stream.match(/^\s*:/, false))
67 if (stream.match(/^\s*:/, false))
65 return ret("variable-2", "variable-definition");
68 return ret("variable-2", "variable-definition");
66 return ret("variable-2", "variable");
69 return ret("variable-2", "variable");
67 } else if (stream.match(/^\w+-/)) {
70 } else if (stream.match(/^\w+-/)) {
68 return ret("meta", "meta");
71 return ret("meta", "meta");
69 }
72 }
70 } else if (/[,+>*\/]/.test(ch)) {
73 } else if (/[,+>*\/]/.test(ch)) {
71 return ret(null, "select-op");
74 return ret(null, "select-op");
72 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
75 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
73 return ret("qualifier", "qualifier");
76 return ret("qualifier", "qualifier");
74 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
77 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
75 return ret(null, ch);
78 return ret(null, ch);
76 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
79 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
77 (ch == "d" && stream.match("omain(")) ||
80 (ch == "d" && stream.match("omain(")) ||
78 (ch == "r" && stream.match("egexp("))) {
81 (ch == "r" && stream.match("egexp("))) {
79 stream.backUp(1);
82 stream.backUp(1);
80 state.tokenize = tokenParenthesized;
83 state.tokenize = tokenParenthesized;
81 return ret("property", "word");
84 return ret("property", "word");
82 } else if (/[\w\\\-]/.test(ch)) {
85 } else if (/[\w\\\-]/.test(ch)) {
83 stream.eatWhile(/[\w\\\-]/);
86 stream.eatWhile(/[\w\\\-]/);
84 return ret("property", "word");
87 return ret("property", "word");
85 } else {
88 } else {
86 return ret(null, null);
89 return ret(null, null);
87 }
90 }
88 }
91 }
89
92
90 function tokenString(quote) {
93 function tokenString(quote) {
91 return function(stream, state) {
94 return function(stream, state) {
92 var escaped = false, ch;
95 var escaped = false, ch;
93 while ((ch = stream.next()) != null) {
96 while ((ch = stream.next()) != null) {
94 if (ch == quote && !escaped) {
97 if (ch == quote && !escaped) {
95 if (quote == ")") stream.backUp(1);
98 if (quote == ")") stream.backUp(1);
96 break;
99 break;
97 }
100 }
98 escaped = !escaped && ch == "\\";
101 escaped = !escaped && ch == "\\";
99 }
102 }
100 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
103 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
101 return ret("string", "string");
104 return ret("string", "string");
102 };
105 };
103 }
106 }
104
107
105 function tokenParenthesized(stream, state) {
108 function tokenParenthesized(stream, state) {
106 stream.next(); // Must be '('
109 stream.next(); // Must be '('
107 if (!stream.match(/\s*[\"\')]/, false))
110 if (!stream.match(/\s*[\"\')]/, false))
108 state.tokenize = tokenString(")");
111 state.tokenize = tokenString(")");
109 else
112 else
110 state.tokenize = null;
113 state.tokenize = null;
111 return ret(null, "(");
114 return ret(null, "(");
112 }
115 }
113
116
114 // Context management
117 // Context management
115
118
116 function Context(type, indent, prev) {
119 function Context(type, indent, prev) {
117 this.type = type;
120 this.type = type;
118 this.indent = indent;
121 this.indent = indent;
119 this.prev = prev;
122 this.prev = prev;
120 }
123 }
121
124
122 function pushContext(state, stream, type) {
125 function pushContext(state, stream, type, indent) {
123 state.context = new Context(type, stream.indentation() + indentUnit, state.context);
126 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
124 return type;
127 return type;
125 }
128 }
126
129
127 function popContext(state) {
130 function popContext(state) {
128 state.context = state.context.prev;
131 if (state.context.prev)
132 state.context = state.context.prev;
129 return state.context.type;
133 return state.context.type;
130 }
134 }
131
135
132 function pass(type, stream, state) {
136 function pass(type, stream, state) {
133 return states[state.context.type](type, stream, state);
137 return states[state.context.type](type, stream, state);
134 }
138 }
135 function popAndPass(type, stream, state, n) {
139 function popAndPass(type, stream, state, n) {
136 for (var i = n || 1; i > 0; i--)
140 for (var i = n || 1; i > 0; i--)
137 state.context = state.context.prev;
141 state.context = state.context.prev;
138 return pass(type, stream, state);
142 return pass(type, stream, state);
139 }
143 }
140
144
141 // Parser
145 // Parser
142
146
143 function wordAsValue(stream) {
147 function wordAsValue(stream) {
144 var word = stream.current().toLowerCase();
148 var word = stream.current().toLowerCase();
145 if (valueKeywords.hasOwnProperty(word))
149 if (valueKeywords.hasOwnProperty(word))
146 override = "atom";
150 override = "atom";
147 else if (colorKeywords.hasOwnProperty(word))
151 else if (colorKeywords.hasOwnProperty(word))
148 override = "keyword";
152 override = "keyword";
149 else
153 else
150 override = "variable";
154 override = "variable";
151 }
155 }
152
156
153 var states = {};
157 var states = {};
154
158
155 states.top = function(type, stream, state) {
159 states.top = function(type, stream, state) {
156 if (type == "{") {
160 if (type == "{") {
157 return pushContext(state, stream, "block");
161 return pushContext(state, stream, "block");
158 } else if (type == "}" && state.context.prev) {
162 } else if (type == "}" && state.context.prev) {
159 return popContext(state);
163 return popContext(state);
160 } else if (/@(media|supports|(-moz-)?document)/.test(type)) {
164 } else if (supportsAtComponent && /@component/.test(type)) {
165 return pushContext(state, stream, "atComponentBlock");
166 } else if (/^@(-moz-)?document$/.test(type)) {
167 return pushContext(state, stream, "documentTypes");
168 } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
161 return pushContext(state, stream, "atBlock");
169 return pushContext(state, stream, "atBlock");
162 } else if (/@(font-face|counter-style)/.test(type)) {
170 } else if (/^@(font-face|counter-style)/.test(type)) {
163 state.stateArg = type;
171 state.stateArg = type;
164 return "restricted_atBlock_before";
172 return "restricted_atBlock_before";
165 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
173 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
166 return "keyframes";
174 return "keyframes";
167 } else if (type && type.charAt(0) == "@") {
175 } else if (type && type.charAt(0) == "@") {
168 return pushContext(state, stream, "at");
176 return pushContext(state, stream, "at");
169 } else if (type == "hash") {
177 } else if (type == "hash") {
170 override = "builtin";
178 override = "builtin";
171 } else if (type == "word") {
179 } else if (type == "word") {
172 override = "tag";
180 override = "tag";
173 } else if (type == "variable-definition") {
181 } else if (type == "variable-definition") {
174 return "maybeprop";
182 return "maybeprop";
175 } else if (type == "interpolation") {
183 } else if (type == "interpolation") {
176 return pushContext(state, stream, "interpolation");
184 return pushContext(state, stream, "interpolation");
177 } else if (type == ":") {
185 } else if (type == ":") {
178 return "pseudo";
186 return "pseudo";
179 } else if (allowNested && type == "(") {
187 } else if (allowNested && type == "(") {
180 return pushContext(state, stream, "parens");
188 return pushContext(state, stream, "parens");
181 }
189 }
182 return state.context.type;
190 return state.context.type;
183 };
191 };
184
192
185 states.block = function(type, stream, state) {
193 states.block = function(type, stream, state) {
186 if (type == "word") {
194 if (type == "word") {
187 var word = stream.current().toLowerCase();
195 var word = stream.current().toLowerCase();
188 if (propertyKeywords.hasOwnProperty(word)) {
196 if (propertyKeywords.hasOwnProperty(word)) {
189 override = "property";
197 override = "property";
190 return "maybeprop";
198 return "maybeprop";
191 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
199 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
192 override = "string-2";
200 override = "string-2";
193 return "maybeprop";
201 return "maybeprop";
194 } else if (allowNested) {
202 } else if (allowNested) {
195 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
203 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
196 return "block";
204 return "block";
197 } else {
205 } else {
198 override += " error";
206 override += " error";
199 return "maybeprop";
207 return "maybeprop";
200 }
208 }
201 } else if (type == "meta") {
209 } else if (type == "meta") {
202 return "block";
210 return "block";
203 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
211 } else if (!allowNested && (type == "hash" || type == "qualifier")) {
204 override = "error";
212 override = "error";
205 return "block";
213 return "block";
206 } else {
214 } else {
207 return states.top(type, stream, state);
215 return states.top(type, stream, state);
208 }
216 }
209 };
217 };
210
218
211 states.maybeprop = function(type, stream, state) {
219 states.maybeprop = function(type, stream, state) {
212 if (type == ":") return pushContext(state, stream, "prop");
220 if (type == ":") return pushContext(state, stream, "prop");
213 return pass(type, stream, state);
221 return pass(type, stream, state);
214 };
222 };
215
223
216 states.prop = function(type, stream, state) {
224 states.prop = function(type, stream, state) {
217 if (type == ";") return popContext(state);
225 if (type == ";") return popContext(state);
218 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
226 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
219 if (type == "}" || type == "{") return popAndPass(type, stream, state);
227 if (type == "}" || type == "{") return popAndPass(type, stream, state);
220 if (type == "(") return pushContext(state, stream, "parens");
228 if (type == "(") return pushContext(state, stream, "parens");
221
229
222 if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
230 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
223 override += " error";
231 override += " error";
224 } else if (type == "word") {
232 } else if (type == "word") {
225 wordAsValue(stream);
233 wordAsValue(stream);
226 } else if (type == "interpolation") {
234 } else if (type == "interpolation") {
227 return pushContext(state, stream, "interpolation");
235 return pushContext(state, stream, "interpolation");
228 }
236 }
229 return "prop";
237 return "prop";
230 };
238 };
231
239
232 states.propBlock = function(type, _stream, state) {
240 states.propBlock = function(type, _stream, state) {
233 if (type == "}") return popContext(state);
241 if (type == "}") return popContext(state);
234 if (type == "word") { override = "property"; return "maybeprop"; }
242 if (type == "word") { override = "property"; return "maybeprop"; }
235 return state.context.type;
243 return state.context.type;
236 };
244 };
237
245
238 states.parens = function(type, stream, state) {
246 states.parens = function(type, stream, state) {
239 if (type == "{" || type == "}") return popAndPass(type, stream, state);
247 if (type == "{" || type == "}") return popAndPass(type, stream, state);
240 if (type == ")") return popContext(state);
248 if (type == ")") return popContext(state);
241 if (type == "(") return pushContext(state, stream, "parens");
249 if (type == "(") return pushContext(state, stream, "parens");
242 if (type == "interpolation") return pushContext(state, stream, "interpolation");
250 if (type == "interpolation") return pushContext(state, stream, "interpolation");
243 if (type == "word") wordAsValue(stream);
251 if (type == "word") wordAsValue(stream);
244 return "parens";
252 return "parens";
245 };
253 };
246
254
247 states.pseudo = function(type, stream, state) {
255 states.pseudo = function(type, stream, state) {
248 if (type == "word") {
256 if (type == "word") {
249 override = "variable-3";
257 override = "variable-3";
250 return state.context.type;
258 return state.context.type;
251 }
259 }
252 return pass(type, stream, state);
260 return pass(type, stream, state);
253 };
261 };
254
262
263 states.documentTypes = function(type, stream, state) {
264 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
265 override = "tag";
266 return state.context.type;
267 } else {
268 return states.atBlock(type, stream, state);
269 }
270 };
271
255 states.atBlock = function(type, stream, state) {
272 states.atBlock = function(type, stream, state) {
256 if (type == "(") return pushContext(state, stream, "atBlock_parens");
273 if (type == "(") return pushContext(state, stream, "atBlock_parens");
257 if (type == "}") return popAndPass(type, stream, state);
274 if (type == "}" || type == ";") return popAndPass(type, stream, state);
258 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
275 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
259
276
277 if (type == "interpolation") return pushContext(state, stream, "interpolation");
278
260 if (type == "word") {
279 if (type == "word") {
261 var word = stream.current().toLowerCase();
280 var word = stream.current().toLowerCase();
262 if (word == "only" || word == "not" || word == "and" || word == "or")
281 if (word == "only" || word == "not" || word == "and" || word == "or")
263 override = "keyword";
282 override = "keyword";
264 else if (documentTypes.hasOwnProperty(word))
265 override = "tag";
266 else if (mediaTypes.hasOwnProperty(word))
283 else if (mediaTypes.hasOwnProperty(word))
267 override = "attribute";
284 override = "attribute";
268 else if (mediaFeatures.hasOwnProperty(word))
285 else if (mediaFeatures.hasOwnProperty(word))
269 override = "property";
286 override = "property";
287 else if (mediaValueKeywords.hasOwnProperty(word))
288 override = "keyword";
270 else if (propertyKeywords.hasOwnProperty(word))
289 else if (propertyKeywords.hasOwnProperty(word))
271 override = "property";
290 override = "property";
272 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
291 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
273 override = "string-2";
292 override = "string-2";
274 else if (valueKeywords.hasOwnProperty(word))
293 else if (valueKeywords.hasOwnProperty(word))
275 override = "atom";
294 override = "atom";
295 else if (colorKeywords.hasOwnProperty(word))
296 override = "keyword";
276 else
297 else
277 override = "error";
298 override = "error";
278 }
299 }
279 return state.context.type;
300 return state.context.type;
280 };
301 };
281
302
303 states.atComponentBlock = function(type, stream, state) {
304 if (type == "}")
305 return popAndPass(type, stream, state);
306 if (type == "{")
307 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
308 if (type == "word")
309 override = "error";
310 return state.context.type;
311 };
312
282 states.atBlock_parens = function(type, stream, state) {
313 states.atBlock_parens = function(type, stream, state) {
283 if (type == ")") return popContext(state);
314 if (type == ")") return popContext(state);
284 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
315 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
285 return states.atBlock(type, stream, state);
316 return states.atBlock(type, stream, state);
286 };
317 };
287
318
288 states.restricted_atBlock_before = function(type, stream, state) {
319 states.restricted_atBlock_before = function(type, stream, state) {
289 if (type == "{")
320 if (type == "{")
290 return pushContext(state, stream, "restricted_atBlock");
321 return pushContext(state, stream, "restricted_atBlock");
291 if (type == "word" && state.stateArg == "@counter-style") {
322 if (type == "word" && state.stateArg == "@counter-style") {
292 override = "variable";
323 override = "variable";
293 return "restricted_atBlock_before";
324 return "restricted_atBlock_before";
294 }
325 }
295 return pass(type, stream, state);
326 return pass(type, stream, state);
296 };
327 };
297
328
298 states.restricted_atBlock = function(type, stream, state) {
329 states.restricted_atBlock = function(type, stream, state) {
299 if (type == "}") {
330 if (type == "}") {
300 state.stateArg = null;
331 state.stateArg = null;
301 return popContext(state);
332 return popContext(state);
302 }
333 }
303 if (type == "word") {
334 if (type == "word") {
304 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
335 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
305 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
336 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
306 override = "error";
337 override = "error";
307 else
338 else
308 override = "property";
339 override = "property";
309 return "maybeprop";
340 return "maybeprop";
310 }
341 }
311 return "restricted_atBlock";
342 return "restricted_atBlock";
312 };
343 };
313
344
314 states.keyframes = function(type, stream, state) {
345 states.keyframes = function(type, stream, state) {
315 if (type == "word") { override = "variable"; return "keyframes"; }
346 if (type == "word") { override = "variable"; return "keyframes"; }
316 if (type == "{") return pushContext(state, stream, "top");
347 if (type == "{") return pushContext(state, stream, "top");
317 return pass(type, stream, state);
348 return pass(type, stream, state);
318 };
349 };
319
350
320 states.at = function(type, stream, state) {
351 states.at = function(type, stream, state) {
321 if (type == ";") return popContext(state);
352 if (type == ";") return popContext(state);
322 if (type == "{" || type == "}") return popAndPass(type, stream, state);
353 if (type == "{" || type == "}") return popAndPass(type, stream, state);
323 if (type == "word") override = "tag";
354 if (type == "word") override = "tag";
324 else if (type == "hash") override = "builtin";
355 else if (type == "hash") override = "builtin";
325 return "at";
356 return "at";
326 };
357 };
327
358
328 states.interpolation = function(type, stream, state) {
359 states.interpolation = function(type, stream, state) {
329 if (type == "}") return popContext(state);
360 if (type == "}") return popContext(state);
330 if (type == "{" || type == ";") return popAndPass(type, stream, state);
361 if (type == "{" || type == ";") return popAndPass(type, stream, state);
331 if (type == "word") override = "variable";
362 if (type == "word") override = "variable";
332 else if (type != "variable" && type != "(" && type != ")") override = "error";
363 else if (type != "variable" && type != "(" && type != ")") override = "error";
333 return "interpolation";
364 return "interpolation";
334 };
365 };
335
366
336 return {
367 return {
337 startState: function(base) {
368 startState: function(base) {
338 return {tokenize: null,
369 return {tokenize: null,
339 state: "top",
370 state: inline ? "block" : "top",
340 stateArg: null,
371 stateArg: null,
341 context: new Context("top", base || 0, null)};
372 context: new Context(inline ? "block" : "top", base || 0, null)};
342 },
373 },
343
374
344 token: function(stream, state) {
375 token: function(stream, state) {
345 if (!state.tokenize && stream.eatSpace()) return null;
376 if (!state.tokenize && stream.eatSpace()) return null;
346 var style = (state.tokenize || tokenBase)(stream, state);
377 var style = (state.tokenize || tokenBase)(stream, state);
347 if (style && typeof style == "object") {
378 if (style && typeof style == "object") {
348 type = style[1];
379 type = style[1];
349 style = style[0];
380 style = style[0];
350 }
381 }
351 override = style;
382 override = style;
352 state.state = states[state.state](type, stream, state);
383 state.state = states[state.state](type, stream, state);
353 return override;
384 return override;
354 },
385 },
355
386
356 indent: function(state, textAfter) {
387 indent: function(state, textAfter) {
357 var cx = state.context, ch = textAfter && textAfter.charAt(0);
388 var cx = state.context, ch = textAfter && textAfter.charAt(0);
358 var indent = cx.indent;
389 var indent = cx.indent;
359 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
390 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
360 if (cx.prev &&
391 if (cx.prev) {
361 (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "restricted_atBlock") ||
392 if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
362 ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
393 cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
363 ch == "{" && (cx.type == "at" || cx.type == "atBlock"))) {
394 // Resume indentation from parent context.
364 indent = cx.indent - indentUnit;
395 cx = cx.prev;
365 cx = cx.prev;
396 indent = cx.indent;
397 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
398 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
399 // Dedent relative to current context.
400 indent = Math.max(0, cx.indent - indentUnit);
401 cx = cx.prev;
402 }
366 }
403 }
367 return indent;
404 return indent;
368 },
405 },
369
406
370 electricChars: "}",
407 electricChars: "}",
371 blockCommentStart: "/*",
408 blockCommentStart: "/*",
372 blockCommentEnd: "*/",
409 blockCommentEnd: "*/",
373 fold: "brace"
410 fold: "brace"
374 };
411 };
375 });
412 });
376
413
377 function keySet(array) {
414 function keySet(array) {
378 var keys = {};
415 var keys = {};
379 for (var i = 0; i < array.length; ++i) {
416 for (var i = 0; i < array.length; ++i) {
380 keys[array[i]] = true;
417 keys[array[i]] = true;
381 }
418 }
382 return keys;
419 return keys;
383 }
420 }
384
421
385 var documentTypes_ = [
422 var documentTypes_ = [
386 "domain", "regexp", "url", "url-prefix"
423 "domain", "regexp", "url", "url-prefix"
387 ], documentTypes = keySet(documentTypes_);
424 ], documentTypes = keySet(documentTypes_);
388
425
389 var mediaTypes_ = [
426 var mediaTypes_ = [
390 "all", "aural", "braille", "handheld", "print", "projection", "screen",
427 "all", "aural", "braille", "handheld", "print", "projection", "screen",
391 "tty", "tv", "embossed"
428 "tty", "tv", "embossed"
392 ], mediaTypes = keySet(mediaTypes_);
429 ], mediaTypes = keySet(mediaTypes_);
393
430
394 var mediaFeatures_ = [
431 var mediaFeatures_ = [
395 "width", "min-width", "max-width", "height", "min-height", "max-height",
432 "width", "min-width", "max-width", "height", "min-height", "max-height",
396 "device-width", "min-device-width", "max-device-width", "device-height",
433 "device-width", "min-device-width", "max-device-width", "device-height",
397 "min-device-height", "max-device-height", "aspect-ratio",
434 "min-device-height", "max-device-height", "aspect-ratio",
398 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
435 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
399 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
436 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
400 "max-color", "color-index", "min-color-index", "max-color-index",
437 "max-color", "color-index", "min-color-index", "max-color-index",
401 "monochrome", "min-monochrome", "max-monochrome", "resolution",
438 "monochrome", "min-monochrome", "max-monochrome", "resolution",
402 "min-resolution", "max-resolution", "scan", "grid"
439 "min-resolution", "max-resolution", "scan", "grid", "orientation",
440 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
441 "pointer", "any-pointer", "hover", "any-hover"
403 ], mediaFeatures = keySet(mediaFeatures_);
442 ], mediaFeatures = keySet(mediaFeatures_);
404
443
444 var mediaValueKeywords_ = [
445 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
446 "interlace", "progressive"
447 ], mediaValueKeywords = keySet(mediaValueKeywords_);
448
405 var propertyKeywords_ = [
449 var propertyKeywords_ = [
406 "align-content", "align-items", "align-self", "alignment-adjust",
450 "align-content", "align-items", "align-self", "alignment-adjust",
407 "alignment-baseline", "anchor-point", "animation", "animation-delay",
451 "alignment-baseline", "anchor-point", "animation", "animation-delay",
408 "animation-direction", "animation-duration", "animation-fill-mode",
452 "animation-direction", "animation-duration", "animation-fill-mode",
409 "animation-iteration-count", "animation-name", "animation-play-state",
453 "animation-iteration-count", "animation-name", "animation-play-state",
410 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
454 "animation-timing-function", "appearance", "azimuth", "backface-visibility",
411 "background", "background-attachment", "background-clip", "background-color",
455 "background", "background-attachment", "background-blend-mode", "background-clip",
412 "background-image", "background-origin", "background-position",
456 "background-color", "background-image", "background-origin", "background-position",
413 "background-repeat", "background-size", "baseline-shift", "binding",
457 "background-repeat", "background-size", "baseline-shift", "binding",
414 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
458 "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
415 "bookmark-target", "border", "border-bottom", "border-bottom-color",
459 "bookmark-target", "border", "border-bottom", "border-bottom-color",
416 "border-bottom-left-radius", "border-bottom-right-radius",
460 "border-bottom-left-radius", "border-bottom-right-radius",
417 "border-bottom-style", "border-bottom-width", "border-collapse",
461 "border-bottom-style", "border-bottom-width", "border-collapse",
418 "border-color", "border-image", "border-image-outset",
462 "border-color", "border-image", "border-image-outset",
419 "border-image-repeat", "border-image-slice", "border-image-source",
463 "border-image-repeat", "border-image-slice", "border-image-source",
420 "border-image-width", "border-left", "border-left-color",
464 "border-image-width", "border-left", "border-left-color",
421 "border-left-style", "border-left-width", "border-radius", "border-right",
465 "border-left-style", "border-left-width", "border-radius", "border-right",
422 "border-right-color", "border-right-style", "border-right-width",
466 "border-right-color", "border-right-style", "border-right-width",
423 "border-spacing", "border-style", "border-top", "border-top-color",
467 "border-spacing", "border-style", "border-top", "border-top-color",
424 "border-top-left-radius", "border-top-right-radius", "border-top-style",
468 "border-top-left-radius", "border-top-right-radius", "border-top-style",
425 "border-top-width", "border-width", "bottom", "box-decoration-break",
469 "border-top-width", "border-width", "bottom", "box-decoration-break",
426 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
470 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
427 "caption-side", "clear", "clip", "color", "color-profile", "column-count",
471 "caption-side", "clear", "clip", "color", "color-profile", "column-count",
428 "column-fill", "column-gap", "column-rule", "column-rule-color",
472 "column-fill", "column-gap", "column-rule", "column-rule-color",
429 "column-rule-style", "column-rule-width", "column-span", "column-width",
473 "column-rule-style", "column-rule-width", "column-span", "column-width",
430 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
474 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
431 "cue-after", "cue-before", "cursor", "direction", "display",
475 "cue-after", "cue-before", "cursor", "direction", "display",
432 "dominant-baseline", "drop-initial-after-adjust",
476 "dominant-baseline", "drop-initial-after-adjust",
433 "drop-initial-after-align", "drop-initial-before-adjust",
477 "drop-initial-after-align", "drop-initial-before-adjust",
434 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
478 "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
435 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
479 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
436 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
480 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
437 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
481 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
438 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
482 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
439 "font-stretch", "font-style", "font-synthesis", "font-variant",
483 "font-stretch", "font-style", "font-synthesis", "font-variant",
440 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
484 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
441 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
485 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
442 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
486 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
443 "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
487 "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
444 "grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
488 "grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
445 "grid-template", "grid-template-areas", "grid-template-columns",
489 "grid-template", "grid-template-areas", "grid-template-columns",
446 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
490 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
447 "icon", "image-orientation", "image-rendering", "image-resolution",
491 "icon", "image-orientation", "image-rendering", "image-resolution",
448 "inline-box-align", "justify-content", "left", "letter-spacing",
492 "inline-box-align", "justify-content", "left", "letter-spacing",
449 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
493 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
450 "line-stacking-shift", "line-stacking-strategy", "list-style",
494 "line-stacking-shift", "line-stacking-strategy", "list-style",
451 "list-style-image", "list-style-position", "list-style-type", "margin",
495 "list-style-image", "list-style-position", "list-style-type", "margin",
452 "margin-bottom", "margin-left", "margin-right", "margin-top",
496 "margin-bottom", "margin-left", "margin-right", "margin-top",
453 "marker-offset", "marks", "marquee-direction", "marquee-loop",
497 "marker-offset", "marks", "marquee-direction", "marquee-loop",
454 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
498 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
455 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
499 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
456 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
500 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
457 "opacity", "order", "orphans", "outline",
501 "opacity", "order", "orphans", "outline",
458 "outline-color", "outline-offset", "outline-style", "outline-width",
502 "outline-color", "outline-offset", "outline-style", "outline-width",
459 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
503 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
460 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
504 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
461 "page", "page-break-after", "page-break-before", "page-break-inside",
505 "page", "page-break-after", "page-break-before", "page-break-inside",
462 "page-policy", "pause", "pause-after", "pause-before", "perspective",
506 "page-policy", "pause", "pause-after", "pause-before", "perspective",
463 "perspective-origin", "pitch", "pitch-range", "play-during", "position",
507 "perspective-origin", "pitch", "pitch-range", "play-during", "position",
464 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
508 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
465 "region-break-before", "region-break-inside", "region-fragment",
509 "region-break-before", "region-break-inside", "region-fragment",
466 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
510 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
467 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
511 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
468 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
512 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
469 "shape-outside", "size", "speak", "speak-as", "speak-header",
513 "shape-outside", "size", "speak", "speak-as", "speak-header",
470 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
514 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
471 "tab-size", "table-layout", "target", "target-name", "target-new",
515 "tab-size", "table-layout", "target", "target-name", "target-new",
472 "target-position", "text-align", "text-align-last", "text-decoration",
516 "target-position", "text-align", "text-align-last", "text-decoration",
473 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
517 "text-decoration-color", "text-decoration-line", "text-decoration-skip",
474 "text-decoration-style", "text-emphasis", "text-emphasis-color",
518 "text-decoration-style", "text-emphasis", "text-emphasis-color",
475 "text-emphasis-position", "text-emphasis-style", "text-height",
519 "text-emphasis-position", "text-emphasis-style", "text-height",
476 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
520 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
477 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
521 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
478 "text-wrap", "top", "transform", "transform-origin", "transform-style",
522 "text-wrap", "top", "transform", "transform-origin", "transform-style",
479 "transition", "transition-delay", "transition-duration",
523 "transition", "transition-delay", "transition-duration",
480 "transition-property", "transition-timing-function", "unicode-bidi",
524 "transition-property", "transition-timing-function", "unicode-bidi",
481 "vertical-align", "visibility", "voice-balance", "voice-duration",
525 "vertical-align", "visibility", "voice-balance", "voice-duration",
482 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
526 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
483 "voice-volume", "volume", "white-space", "widows", "width", "word-break",
527 "voice-volume", "volume", "white-space", "widows", "width", "word-break",
484 "word-spacing", "word-wrap", "z-index",
528 "word-spacing", "word-wrap", "z-index",
485 // SVG-specific
529 // SVG-specific
486 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
530 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
487 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
531 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
488 "color-interpolation", "color-interpolation-filters",
532 "color-interpolation", "color-interpolation-filters",
489 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
533 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
490 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
534 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
491 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
535 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
492 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
536 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
493 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
537 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
494 "glyph-orientation-vertical", "text-anchor", "writing-mode"
538 "glyph-orientation-vertical", "text-anchor", "writing-mode"
495 ], propertyKeywords = keySet(propertyKeywords_);
539 ], propertyKeywords = keySet(propertyKeywords_);
496
540
497 var nonStandardPropertyKeywords_ = [
541 var nonStandardPropertyKeywords_ = [
498 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
542 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
499 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
543 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
500 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
544 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
501 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
545 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
502 "searchfield-results-decoration", "zoom"
546 "searchfield-results-decoration", "zoom"
503 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
547 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
504
548
505 var fontProperties_ = [
549 var fontProperties_ = [
506 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
550 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
507 "font-stretch", "font-weight", "font-style"
551 "font-stretch", "font-weight", "font-style"
508 ], fontProperties = keySet(fontProperties_);
552 ], fontProperties = keySet(fontProperties_);
509
553
510 var counterDescriptors_ = [
554 var counterDescriptors_ = [
511 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
555 "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
512 "speak-as", "suffix", "symbols", "system"
556 "speak-as", "suffix", "symbols", "system"
513 ], counterDescriptors = keySet(counterDescriptors_);
557 ], counterDescriptors = keySet(counterDescriptors_);
514
558
515 var colorKeywords_ = [
559 var colorKeywords_ = [
516 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
560 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
517 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
561 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
518 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
562 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
519 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
563 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
520 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
564 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
521 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
565 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
522 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
566 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
523 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
567 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
524 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
568 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
525 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
569 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
526 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
570 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
527 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
571 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
528 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
572 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
529 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
573 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
530 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
574 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
531 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
575 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
532 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
576 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
533 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
577 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
534 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
578 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
535 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
579 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
536 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
580 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
537 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
581 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
538 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
582 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
539 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
583 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
540 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
584 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
541 "whitesmoke", "yellow", "yellowgreen"
585 "whitesmoke", "yellow", "yellowgreen"
542 ], colorKeywords = keySet(colorKeywords_);
586 ], colorKeywords = keySet(colorKeywords_);
543
587
544 var valueKeywords_ = [
588 var valueKeywords_ = [
545 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
589 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
546 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
590 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
547 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
591 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
548 "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
592 "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
549 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
593 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
550 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
594 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
551 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
595 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
552 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
596 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
553 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
597 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
554 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
598 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
555 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
599 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
556 "col-resize", "collapse", "column", "compact", "condensed", "contain", "content",
600 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
601 "compact", "condensed", "contain", "content",
557 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
602 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
558 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "dashed", "decimal",
603 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
559 "decimal-leading-zero", "default", "default-button", "destination-atop",
604 "decimal-leading-zero", "default", "default-button", "destination-atop",
560 "destination-in", "destination-out", "destination-over", "devanagari",
605 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
561 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
606 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
562 "dot-dash", "dot-dot-dash",
607 "dot-dash", "dot-dot-dash",
563 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
608 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
564 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
609 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
565 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
610 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
566 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
611 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
567 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
612 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
568 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
613 "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
569 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
614 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
570 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
615 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
571 "ethiopic-numeric", "ew-resize", "expanded", "extends", "extra-condensed",
616 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
572 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "footnotes",
617 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
573 "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
618 "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
574 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
619 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
575 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
620 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
576 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
621 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
577 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
622 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
578 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
623 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
579 "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
624 "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
580 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
625 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
581 "katakana", "katakana-iroha", "keep-all", "khmer",
626 "katakana", "katakana-iroha", "keep-all", "khmer",
582 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
627 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
583 "landscape", "lao", "large", "larger", "left", "level", "lighter",
628 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
584 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
629 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
585 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
630 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
586 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
631 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
587 "lower-roman", "lowercase", "ltr", "malayalam", "match", "matrix", "matrix3d",
632 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
588 "media-controls-background", "media-current-time-display",
633 "media-controls-background", "media-current-time-display",
589 "media-fullscreen-button", "media-mute-button", "media-play-button",
634 "media-fullscreen-button", "media-mute-button", "media-play-button",
590 "media-return-to-realtime-button", "media-rewind-button",
635 "media-return-to-realtime-button", "media-rewind-button",
591 "media-seek-back-button", "media-seek-forward-button", "media-slider",
636 "media-seek-back-button", "media-seek-forward-button", "media-slider",
592 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
637 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
593 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
638 "media-volume-slider-container", "media-volume-sliderthumb", "medium",
594 "menu", "menulist", "menulist-button", "menulist-text",
639 "menu", "menulist", "menulist-button", "menulist-text",
595 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
640 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
596 "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
641 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
597 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
642 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
598 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
643 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
599 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
644 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
600 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
645 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
601 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
646 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
602 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
647 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
603 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
648 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
604 "progress", "push-button", "radial-gradient", "radio", "read-only",
649 "progress", "push-button", "radial-gradient", "radio", "read-only",
605 "read-write", "read-write-plaintext-only", "rectangle", "region",
650 "read-write", "read-write-plaintext-only", "rectangle", "region",
606 "relative", "repeat", "repeating-linear-gradient",
651 "relative", "repeat", "repeating-linear-gradient",
607 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
652 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
608 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
653 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
609 "rotateZ", "round", "row-resize", "rtl", "run-in", "running",
654 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
610 "s-resize", "sans-serif", "scale", "scale3d", "scaleX", "scaleY", "scaleZ",
655 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
611 "scroll", "scrollbar", "se-resize", "searchfield",
656 "scroll", "scrollbar", "se-resize", "searchfield",
612 "searchfield-cancel-button", "searchfield-decoration",
657 "searchfield-cancel-button", "searchfield-decoration",
613 "searchfield-results-button", "searchfield-results-decoration",
658 "searchfield-results-button", "searchfield-results-decoration",
614 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
659 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
615 "simp-chinese-formal", "simp-chinese-informal", "single",
660 "simp-chinese-formal", "simp-chinese-informal", "single",
616 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
661 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
617 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
662 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
618 "small", "small-caps", "small-caption", "smaller", "solid", "somali",
663 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
619 "source-atop", "source-in", "source-out", "source-over", "space", "spell-out", "square",
664 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
620 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
665 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
621 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
666 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
622 "table-caption", "table-cell", "table-column", "table-column-group",
667 "table-caption", "table-cell", "table-column", "table-column-group",
623 "table-footer-group", "table-header-group", "table-row", "table-row-group",
668 "table-footer-group", "table-header-group", "table-row", "table-row-group",
624 "tamil",
669 "tamil",
625 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
670 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
626 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
671 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
627 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
672 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
628 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
673 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
629 "trad-chinese-formal", "trad-chinese-informal",
674 "trad-chinese-formal", "trad-chinese-informal",
630 "translate", "translate3d", "translateX", "translateY", "translateZ",
675 "translate", "translate3d", "translateX", "translateY", "translateZ",
631 "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
676 "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
632 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
677 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
633 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
678 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
634 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
679 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
635 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
680 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
636 "window", "windowframe", "windowtext", "words", "x-large", "x-small", "xor",
681 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
637 "xx-large", "xx-small"
682 "xx-large", "xx-small"
638 ], valueKeywords = keySet(valueKeywords_);
683 ], valueKeywords = keySet(valueKeywords_);
639
684
640 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(propertyKeywords_)
685 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
641 .concat(nonStandardPropertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
686 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
687 .concat(valueKeywords_);
642 CodeMirror.registerHelper("hintWords", "css", allWords);
688 CodeMirror.registerHelper("hintWords", "css", allWords);
643
689
644 function tokenCComment(stream, state) {
690 function tokenCComment(stream, state) {
645 var maybeEnd = false, ch;
691 var maybeEnd = false, ch;
646 while ((ch = stream.next()) != null) {
692 while ((ch = stream.next()) != null) {
647 if (maybeEnd && ch == "/") {
693 if (maybeEnd && ch == "/") {
648 state.tokenize = null;
694 state.tokenize = null;
649 break;
695 break;
650 }
696 }
651 maybeEnd = (ch == "*");
697 maybeEnd = (ch == "*");
652 }
698 }
653 return ["comment", "comment"];
699 return ["comment", "comment"];
654 }
700 }
655
701
656 CodeMirror.defineMIME("text/css", {
702 CodeMirror.defineMIME("text/css", {
657 documentTypes: documentTypes,
703 documentTypes: documentTypes,
658 mediaTypes: mediaTypes,
704 mediaTypes: mediaTypes,
659 mediaFeatures: mediaFeatures,
705 mediaFeatures: mediaFeatures,
706 mediaValueKeywords: mediaValueKeywords,
660 propertyKeywords: propertyKeywords,
707 propertyKeywords: propertyKeywords,
661 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
708 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
662 fontProperties: fontProperties,
709 fontProperties: fontProperties,
663 counterDescriptors: counterDescriptors,
710 counterDescriptors: counterDescriptors,
664 colorKeywords: colorKeywords,
711 colorKeywords: colorKeywords,
665 valueKeywords: valueKeywords,
712 valueKeywords: valueKeywords,
666 tokenHooks: {
713 tokenHooks: {
667 "/": function(stream, state) {
714 "/": function(stream, state) {
668 if (!stream.eat("*")) return false;
715 if (!stream.eat("*")) return false;
669 state.tokenize = tokenCComment;
716 state.tokenize = tokenCComment;
670 return tokenCComment(stream, state);
717 return tokenCComment(stream, state);
671 }
718 }
672 },
719 },
673 name: "css"
720 name: "css"
674 });
721 });
675
722
676 CodeMirror.defineMIME("text/x-scss", {
723 CodeMirror.defineMIME("text/x-scss", {
677 mediaTypes: mediaTypes,
724 mediaTypes: mediaTypes,
678 mediaFeatures: mediaFeatures,
725 mediaFeatures: mediaFeatures,
726 mediaValueKeywords: mediaValueKeywords,
679 propertyKeywords: propertyKeywords,
727 propertyKeywords: propertyKeywords,
680 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
728 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
681 colorKeywords: colorKeywords,
729 colorKeywords: colorKeywords,
682 valueKeywords: valueKeywords,
730 valueKeywords: valueKeywords,
683 fontProperties: fontProperties,
731 fontProperties: fontProperties,
684 allowNested: true,
732 allowNested: true,
685 tokenHooks: {
733 tokenHooks: {
686 "/": function(stream, state) {
734 "/": function(stream, state) {
687 if (stream.eat("/")) {
735 if (stream.eat("/")) {
688 stream.skipToEnd();
736 stream.skipToEnd();
689 return ["comment", "comment"];
737 return ["comment", "comment"];
690 } else if (stream.eat("*")) {
738 } else if (stream.eat("*")) {
691 state.tokenize = tokenCComment;
739 state.tokenize = tokenCComment;
692 return tokenCComment(stream, state);
740 return tokenCComment(stream, state);
693 } else {
741 } else {
694 return ["operator", "operator"];
742 return ["operator", "operator"];
695 }
743 }
696 },
744 },
697 ":": function(stream) {
745 ":": function(stream) {
698 if (stream.match(/\s*\{/))
746 if (stream.match(/\s*\{/))
699 return [null, "{"];
747 return [null, "{"];
700 return false;
748 return false;
701 },
749 },
702 "$": function(stream) {
750 "$": function(stream) {
703 stream.match(/^[\w-]+/);
751 stream.match(/^[\w-]+/);
704 if (stream.match(/^\s*:/, false))
752 if (stream.match(/^\s*:/, false))
705 return ["variable-2", "variable-definition"];
753 return ["variable-2", "variable-definition"];
706 return ["variable-2", "variable"];
754 return ["variable-2", "variable"];
707 },
755 },
708 "#": function(stream) {
756 "#": function(stream) {
709 if (!stream.eat("{")) return false;
757 if (!stream.eat("{")) return false;
710 return [null, "interpolation"];
758 return [null, "interpolation"];
711 }
759 }
712 },
760 },
713 name: "css",
761 name: "css",
714 helperType: "scss"
762 helperType: "scss"
715 });
763 });
716
764
717 CodeMirror.defineMIME("text/x-less", {
765 CodeMirror.defineMIME("text/x-less", {
718 mediaTypes: mediaTypes,
766 mediaTypes: mediaTypes,
719 mediaFeatures: mediaFeatures,
767 mediaFeatures: mediaFeatures,
768 mediaValueKeywords: mediaValueKeywords,
720 propertyKeywords: propertyKeywords,
769 propertyKeywords: propertyKeywords,
721 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
770 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
722 colorKeywords: colorKeywords,
771 colorKeywords: colorKeywords,
723 valueKeywords: valueKeywords,
772 valueKeywords: valueKeywords,
724 fontProperties: fontProperties,
773 fontProperties: fontProperties,
725 allowNested: true,
774 allowNested: true,
726 tokenHooks: {
775 tokenHooks: {
727 "/": function(stream, state) {
776 "/": function(stream, state) {
728 if (stream.eat("/")) {
777 if (stream.eat("/")) {
729 stream.skipToEnd();
778 stream.skipToEnd();
730 return ["comment", "comment"];
779 return ["comment", "comment"];
731 } else if (stream.eat("*")) {
780 } else if (stream.eat("*")) {
732 state.tokenize = tokenCComment;
781 state.tokenize = tokenCComment;
733 return tokenCComment(stream, state);
782 return tokenCComment(stream, state);
734 } else {
783 } else {
735 return ["operator", "operator"];
784 return ["operator", "operator"];
736 }
785 }
737 },
786 },
738 "@": function(stream) {
787 "@": function(stream) {
739 if (stream.eat("{")) return [null, "interpolation"];
788 if (stream.eat("{")) return [null, "interpolation"];
740 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
789 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
741 stream.eatWhile(/[\w\\\-]/);
790 stream.eatWhile(/[\w\\\-]/);
742 if (stream.match(/^\s*:/, false))
791 if (stream.match(/^\s*:/, false))
743 return ["variable-2", "variable-definition"];
792 return ["variable-2", "variable-definition"];
744 return ["variable-2", "variable"];
793 return ["variable-2", "variable"];
745 },
794 },
746 "&": function() {
795 "&": function() {
747 return ["atom", "atom"];
796 return ["atom", "atom"];
748 }
797 }
749 },
798 },
750 name: "css",
799 name: "css",
751 helperType: "less"
800 helperType: "less"
752 });
801 });
753
802
803 CodeMirror.defineMIME("text/x-gss", {
804 documentTypes: documentTypes,
805 mediaTypes: mediaTypes,
806 mediaFeatures: mediaFeatures,
807 propertyKeywords: propertyKeywords,
808 nonStandardPropertyKeywords: nonStandardPropertyKeywords,
809 fontProperties: fontProperties,
810 counterDescriptors: counterDescriptors,
811 colorKeywords: colorKeywords,
812 valueKeywords: valueKeywords,
813 supportsAtComponent: true,
814 tokenHooks: {
815 "/": function(stream, state) {
816 if (!stream.eat("*")) return false;
817 state.tokenize = tokenCComment;
818 return tokenCComment(stream, state);
819 }
820 },
821 name: "css",
822 helperType: "gss"
823 });
824
754 });
825 });
@@ -1,146 +1,146 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 // By the Neo4j Team and contributors.
4 // By the Neo4j Team and contributors.
5 // https://github.com/neo4j-contrib/CodeMirror
5 // https://github.com/neo4j-contrib/CodeMirror
6
6
7 (function(mod) {
7 (function(mod) {
8 if (typeof exports == "object" && typeof module == "object") // CommonJS
8 if (typeof exports == "object" && typeof module == "object") // CommonJS
9 mod(require("../../lib/codemirror"));
9 mod(require("../../lib/codemirror"));
10 else if (typeof define == "function" && define.amd) // AMD
10 else if (typeof define == "function" && define.amd) // AMD
11 define(["../../lib/codemirror"], mod);
11 define(["../../lib/codemirror"], mod);
12 else // Plain browser env
12 else // Plain browser env
13 mod(CodeMirror);
13 mod(CodeMirror);
14 })(function(CodeMirror) {
14 })(function(CodeMirror) {
15 "use strict";
15 "use strict";
16 var wordRegexp = function(words) {
16 var wordRegexp = function(words) {
17 return new RegExp("^(?:" + words.join("|") + ")$", "i");
17 return new RegExp("^(?:" + words.join("|") + ")$", "i");
18 };
18 };
19
19
20 CodeMirror.defineMode("cypher", function(config) {
20 CodeMirror.defineMode("cypher", function(config) {
21 var tokenBase = function(stream/*, state*/) {
21 var tokenBase = function(stream/*, state*/) {
22 var ch = stream.next();
22 var ch = stream.next();
23 if (ch === "\"" || ch === "'") {
23 if (ch === "\"" || ch === "'") {
24 stream.match(/.+?["']/);
24 stream.match(/.+?["']/);
25 return "string";
25 return "string";
26 }
26 }
27 if (/[{}\(\),\.;\[\]]/.test(ch)) {
27 if (/[{}\(\),\.;\[\]]/.test(ch)) {
28 curPunc = ch;
28 curPunc = ch;
29 return "node";
29 return "node";
30 } else if (ch === "/" && stream.eat("/")) {
30 } else if (ch === "/" && stream.eat("/")) {
31 stream.skipToEnd();
31 stream.skipToEnd();
32 return "comment";
32 return "comment";
33 } else if (operatorChars.test(ch)) {
33 } else if (operatorChars.test(ch)) {
34 stream.eatWhile(operatorChars);
34 stream.eatWhile(operatorChars);
35 return null;
35 return null;
36 } else {
36 } else {
37 stream.eatWhile(/[_\w\d]/);
37 stream.eatWhile(/[_\w\d]/);
38 if (stream.eat(":")) {
38 if (stream.eat(":")) {
39 stream.eatWhile(/[\w\d_\-]/);
39 stream.eatWhile(/[\w\d_\-]/);
40 return "atom";
40 return "atom";
41 }
41 }
42 var word = stream.current();
42 var word = stream.current();
43 if (funcs.test(word)) return "builtin";
43 if (funcs.test(word)) return "builtin";
44 if (preds.test(word)) return "def";
44 if (preds.test(word)) return "def";
45 if (keywords.test(word)) return "keyword";
45 if (keywords.test(word)) return "keyword";
46 return "variable";
46 return "variable";
47 }
47 }
48 };
48 };
49 var pushContext = function(state, type, col) {
49 var pushContext = function(state, type, col) {
50 return state.context = {
50 return state.context = {
51 prev: state.context,
51 prev: state.context,
52 indent: state.indent,
52 indent: state.indent,
53 col: col,
53 col: col,
54 type: type
54 type: type
55 };
55 };
56 };
56 };
57 var popContext = function(state) {
57 var popContext = function(state) {
58 state.indent = state.context.indent;
58 state.indent = state.context.indent;
59 return state.context = state.context.prev;
59 return state.context = state.context.prev;
60 };
60 };
61 var indentUnit = config.indentUnit;
61 var indentUnit = config.indentUnit;
62 var curPunc;
62 var curPunc;
63 var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "keys", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "right", "round", "rtrim", "shortestPath", "sign", "sin", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "trim", "type", "upper"]);
63 var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "keys", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "reverse", "right", "round", "rtrim", "shortestPath", "sign", "sin", "size", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "toString", "trim", "type", "upper"]);
64 var preds = wordRegexp(["all", "and", "any", "has", "in", "none", "not", "or", "single", "xor"]);
64 var preds = wordRegexp(["all", "and", "any", "contains", "exists", "has", "in", "none", "not", "or", "single", "xor"]);
65 var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "distinct", "drop", "else", "end", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with"]);
65 var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "detach", "distinct", "drop", "else", "end", "ends", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "join", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "starts", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with"]);
66 var operatorChars = /[*+\-<>=&|~%^]/;
66 var operatorChars = /[*+\-<>=&|~%^]/;
67
67
68 return {
68 return {
69 startState: function(/*base*/) {
69 startState: function(/*base*/) {
70 return {
70 return {
71 tokenize: tokenBase,
71 tokenize: tokenBase,
72 context: null,
72 context: null,
73 indent: 0,
73 indent: 0,
74 col: 0
74 col: 0
75 };
75 };
76 },
76 },
77 token: function(stream, state) {
77 token: function(stream, state) {
78 if (stream.sol()) {
78 if (stream.sol()) {
79 if (state.context && (state.context.align == null)) {
79 if (state.context && (state.context.align == null)) {
80 state.context.align = false;
80 state.context.align = false;
81 }
81 }
82 state.indent = stream.indentation();
82 state.indent = stream.indentation();
83 }
83 }
84 if (stream.eatSpace()) {
84 if (stream.eatSpace()) {
85 return null;
85 return null;
86 }
86 }
87 var style = state.tokenize(stream, state);
87 var style = state.tokenize(stream, state);
88 if (style !== "comment" && state.context && (state.context.align == null) && state.context.type !== "pattern") {
88 if (style !== "comment" && state.context && (state.context.align == null) && state.context.type !== "pattern") {
89 state.context.align = true;
89 state.context.align = true;
90 }
90 }
91 if (curPunc === "(") {
91 if (curPunc === "(") {
92 pushContext(state, ")", stream.column());
92 pushContext(state, ")", stream.column());
93 } else if (curPunc === "[") {
93 } else if (curPunc === "[") {
94 pushContext(state, "]", stream.column());
94 pushContext(state, "]", stream.column());
95 } else if (curPunc === "{") {
95 } else if (curPunc === "{") {
96 pushContext(state, "}", stream.column());
96 pushContext(state, "}", stream.column());
97 } else if (/[\]\}\)]/.test(curPunc)) {
97 } else if (/[\]\}\)]/.test(curPunc)) {
98 while (state.context && state.context.type === "pattern") {
98 while (state.context && state.context.type === "pattern") {
99 popContext(state);
99 popContext(state);
100 }
100 }
101 if (state.context && curPunc === state.context.type) {
101 if (state.context && curPunc === state.context.type) {
102 popContext(state);
102 popContext(state);
103 }
103 }
104 } else if (curPunc === "." && state.context && state.context.type === "pattern") {
104 } else if (curPunc === "." && state.context && state.context.type === "pattern") {
105 popContext(state);
105 popContext(state);
106 } else if (/atom|string|variable/.test(style) && state.context) {
106 } else if (/atom|string|variable/.test(style) && state.context) {
107 if (/[\}\]]/.test(state.context.type)) {
107 if (/[\}\]]/.test(state.context.type)) {
108 pushContext(state, "pattern", stream.column());
108 pushContext(state, "pattern", stream.column());
109 } else if (state.context.type === "pattern" && !state.context.align) {
109 } else if (state.context.type === "pattern" && !state.context.align) {
110 state.context.align = true;
110 state.context.align = true;
111 state.context.col = stream.column();
111 state.context.col = stream.column();
112 }
112 }
113 }
113 }
114 return style;
114 return style;
115 },
115 },
116 indent: function(state, textAfter) {
116 indent: function(state, textAfter) {
117 var firstChar = textAfter && textAfter.charAt(0);
117 var firstChar = textAfter && textAfter.charAt(0);
118 var context = state.context;
118 var context = state.context;
119 if (/[\]\}]/.test(firstChar)) {
119 if (/[\]\}]/.test(firstChar)) {
120 while (context && context.type === "pattern") {
120 while (context && context.type === "pattern") {
121 context = context.prev;
121 context = context.prev;
122 }
122 }
123 }
123 }
124 var closing = context && firstChar === context.type;
124 var closing = context && firstChar === context.type;
125 if (!context) return 0;
125 if (!context) return 0;
126 if (context.type === "keywords") return CodeMirror.commands.newlineAndIndent;
126 if (context.type === "keywords") return CodeMirror.commands.newlineAndIndent;
127 if (context.align) return context.col + (closing ? 0 : 1);
127 if (context.align) return context.col + (closing ? 0 : 1);
128 return context.indent + (closing ? 0 : indentUnit);
128 return context.indent + (closing ? 0 : indentUnit);
129 }
129 }
130 };
130 };
131 });
131 });
132
132
133 CodeMirror.modeExtensions["cypher"] = {
133 CodeMirror.modeExtensions["cypher"] = {
134 autoFormatLineBreaks: function(text) {
134 autoFormatLineBreaks: function(text) {
135 var i, lines, reProcessedPortion;
135 var i, lines, reProcessedPortion;
136 var lines = text.split("\n");
136 var lines = text.split("\n");
137 var reProcessedPortion = /\s+\b(return|where|order by|match|with|skip|limit|create|delete|set)\b\s/g;
137 var reProcessedPortion = /\s+\b(return|where|order by|match|with|skip|limit|create|delete|set)\b\s/g;
138 for (var i = 0; i < lines.length; i++)
138 for (var i = 0; i < lines.length; i++)
139 lines[i] = lines[i].replace(reProcessedPortion, " \n$1 ").trim();
139 lines[i] = lines[i].replace(reProcessedPortion, " \n$1 ").trim();
140 return lines.join("\n");
140 return lines.join("\n");
141 }
141 }
142 };
142 };
143
143
144 CodeMirror.defineMIME("application/x-cypher-query", "cypher");
144 CodeMirror.defineMIME("application/x-cypher-query", "cypher");
145
145
146 });
146 });
@@ -1,50 +1,130 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../clike/clike"));
6 mod(require("../../lib/codemirror"), require("../clike/clike"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../clike/clike"], mod);
8 define(["../../lib/codemirror", "../clike/clike"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 var keywords = ("this super static final const abstract class extends external factory " +
14 var keywords = ("this super static final const abstract class extends external factory " +
15 "implements get native operator set typedef with enum throw rethrow " +
15 "implements get native operator set typedef with enum throw rethrow " +
16 "assert break case continue default in return new deferred async await " +
16 "assert break case continue default in return new deferred async await " +
17 "try catch finally do else for if switch while import library export " +
17 "try catch finally do else for if switch while import library export " +
18 "part of show hide is").split(" ");
18 "part of show hide is as").split(" ");
19 var blockKeywords = "try catch finally do else for if switch while".split(" ");
19 var blockKeywords = "try catch finally do else for if switch while".split(" ");
20 var atoms = "true false null".split(" ");
20 var atoms = "true false null".split(" ");
21 var builtins = "void bool num int double dynamic var String".split(" ");
21 var builtins = "void bool num int double dynamic var String".split(" ");
22
22
23 function set(words) {
23 function set(words) {
24 var obj = {};
24 var obj = {};
25 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
25 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
26 return obj;
26 return obj;
27 }
27 }
28
28
29 function pushInterpolationStack(state) {
30 (state.interpolationStack || (state.interpolationStack = [])).push(state.tokenize);
31 }
32
33 function popInterpolationStack(state) {
34 return (state.interpolationStack || (state.interpolationStack = [])).pop();
35 }
36
37 function sizeInterpolationStack(state) {
38 return state.interpolationStack ? state.interpolationStack.length : 0;
39 }
40
29 CodeMirror.defineMIME("application/dart", {
41 CodeMirror.defineMIME("application/dart", {
30 name: "clike",
42 name: "clike",
31 keywords: set(keywords),
43 keywords: set(keywords),
32 multiLineStrings: true,
33 blockKeywords: set(blockKeywords),
44 blockKeywords: set(blockKeywords),
34 builtin: set(builtins),
45 builtin: set(builtins),
35 atoms: set(atoms),
46 atoms: set(atoms),
36 hooks: {
47 hooks: {
37 "@": function(stream) {
48 "@": function(stream) {
38 stream.eatWhile(/[\w\$_]/);
49 stream.eatWhile(/[\w\$_\.]/);
39 return "meta";
50 return "meta";
51 },
52
53 // custom string handling to deal with triple-quoted strings and string interpolation
54 "'": function(stream, state) {
55 return tokenString("'", stream, state, false);
56 },
57 "\"": function(stream, state) {
58 return tokenString("\"", stream, state, false);
59 },
60 "r": function(stream, state) {
61 var peek = stream.peek();
62 if (peek == "'" || peek == "\"") {
63 return tokenString(stream.next(), stream, state, true);
64 }
65 return false;
66 },
67
68 "}": function(_stream, state) {
69 // "}" is end of interpolation, if interpolation stack is non-empty
70 if (sizeInterpolationStack(state) > 0) {
71 state.tokenize = popInterpolationStack(state);
72 return null;
73 }
74 return false;
40 }
75 }
41 }
76 }
42 });
77 });
43
78
79 function tokenString(quote, stream, state, raw) {
80 var tripleQuoted = false;
81 if (stream.eat(quote)) {
82 if (stream.eat(quote)) tripleQuoted = true;
83 else return "string"; //empty string
84 }
85 function tokenStringHelper(stream, state) {
86 var escaped = false;
87 while (!stream.eol()) {
88 if (!raw && !escaped && stream.peek() == "$") {
89 pushInterpolationStack(state);
90 state.tokenize = tokenInterpolation;
91 return "string";
92 }
93 var next = stream.next();
94 if (next == quote && !escaped && (!tripleQuoted || stream.match(quote + quote))) {
95 state.tokenize = null;
96 break;
97 }
98 escaped = !raw && !escaped && next == "\\";
99 }
100 return "string";
101 }
102 state.tokenize = tokenStringHelper;
103 return tokenStringHelper(stream, state);
104 }
105
106 function tokenInterpolation(stream, state) {
107 stream.eat("$");
108 if (stream.eat("{")) {
109 // let clike handle the content of ${...},
110 // we take over again when "}" appears (see hooks).
111 state.tokenize = null;
112 } else {
113 state.tokenize = tokenInterpolationIdentifier;
114 }
115 return null;
116 }
117
118 function tokenInterpolationIdentifier(stream, state) {
119 stream.eatWhile(/[\w_]/);
120 state.tokenize = popInterpolationStack(state);
121 return "variable";
122 }
123
44 CodeMirror.registerHelper("hintWords", "application/dart", keywords.concat(atoms).concat(builtins));
124 CodeMirror.registerHelper("hintWords", "application/dart", keywords.concat(atoms).concat(builtins));
45
125
46 // This is needed to make loading through meta.js work.
126 // This is needed to make loading through meta.js work.
47 CodeMirror.defineMode("dart", function(conf) {
127 CodeMirror.defineMode("dart", function(conf) {
48 return CodeMirror.getMode(conf, "application/dart");
128 return CodeMirror.getMode(conf, "application/dart");
49 }, "clike");
129 }, "clike");
50 });
130 });
@@ -1,350 +1,356 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
7 require("../../addon/mode/overlay"));
7 require("../../addon/mode/overlay"));
8 else if (typeof define == "function" && define.amd) // AMD
8 else if (typeof define == "function" && define.amd) // AMD
9 define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
9 define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
10 "../../addon/mode/overlay"], mod);
10 "../../addon/mode/overlay"], mod);
11 else // Plain browser env
11 else // Plain browser env
12 mod(CodeMirror);
12 mod(CodeMirror);
13 })(function(CodeMirror) {
13 })(function(CodeMirror) {
14 "use strict";
14 "use strict";
15
15
16 CodeMirror.defineMode("django:inner", function() {
16 CodeMirror.defineMode("django:inner", function() {
17 var keywords = ["block", "endblock", "for", "endfor", "true", "false",
17 var keywords = ["block", "endblock", "for", "endfor", "true", "false", "filter", "endfilter",
18 "loop", "none", "self", "super", "if", "endif", "as",
18 "loop", "none", "self", "super", "if", "elif", "endif", "as", "else", "import",
19 "else", "import", "with", "endwith", "without", "context", "ifequal", "endifequal",
19 "with", "endwith", "without", "context", "ifequal", "endifequal", "ifnotequal",
20 "ifnotequal", "endifnotequal", "extends", "include", "load", "comment",
20 "endifnotequal", "extends", "include", "load", "comment", "endcomment",
21 "endcomment", "empty", "url", "static", "trans", "blocktrans", "now", "regroup",
21 "empty", "url", "static", "trans", "blocktrans", "endblocktrans", "now",
22 "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle", "csrf_token",
22 "regroup", "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle",
23 "autoescape", "endautoescape", "spaceless", "ssi", "templatetag",
23 "csrf_token", "autoescape", "endautoescape", "spaceless", "endspaceless",
24 "verbatim", "endverbatim", "widthratio"],
24 "ssi", "templatetag", "verbatim", "endverbatim", "widthratio"],
25 filters = ["add", "addslashes", "capfirst", "center", "cut", "date",
25 filters = ["add", "addslashes", "capfirst", "center", "cut", "date",
26 "default", "default_if_none", "dictsort",
26 "default", "default_if_none", "dictsort",
27 "dictsortreversed", "divisibleby", "escape", "escapejs",
27 "dictsortreversed", "divisibleby", "escape", "escapejs",
28 "filesizeformat", "first", "floatformat", "force_escape",
28 "filesizeformat", "first", "floatformat", "force_escape",
29 "get_digit", "iriencode", "join", "last", "length",
29 "get_digit", "iriencode", "join", "last", "length",
30 "length_is", "linebreaks", "linebreaksbr", "linenumbers",
30 "length_is", "linebreaks", "linebreaksbr", "linenumbers",
31 "ljust", "lower", "make_list", "phone2numeric", "pluralize",
31 "ljust", "lower", "make_list", "phone2numeric", "pluralize",
32 "pprint", "random", "removetags", "rjust", "safe",
32 "pprint", "random", "removetags", "rjust", "safe",
33 "safeseq", "slice", "slugify", "stringformat", "striptags",
33 "safeseq", "slice", "slugify", "stringformat", "striptags",
34 "time", "timesince", "timeuntil", "title", "truncatechars",
34 "time", "timesince", "timeuntil", "title", "truncatechars",
35 "truncatechars_html", "truncatewords", "truncatewords_html",
35 "truncatechars_html", "truncatewords", "truncatewords_html",
36 "unordered_list", "upper", "urlencode", "urlize",
36 "unordered_list", "upper", "urlencode", "urlize",
37 "urlizetrunc", "wordcount", "wordwrap", "yesno"],
37 "urlizetrunc", "wordcount", "wordwrap", "yesno"],
38 operators = ["==", "!=", "<", ">", "<=", ">=", "in", "not", "or", "and"];
38 operators = ["==", "!=", "<", ">", "<=", ">="],
39 wordOperators = ["in", "not", "or", "and"];
39
40
40 keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b");
41 keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b");
41 filters = new RegExp("^\\b(" + filters.join("|") + ")\\b");
42 filters = new RegExp("^\\b(" + filters.join("|") + ")\\b");
42 operators = new RegExp("^\\b(" + operators.join("|") + ")\\b");
43 operators = new RegExp("^\\b(" + operators.join("|") + ")\\b");
44 wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b");
43
45
44 // We have to return "null" instead of null, in order to avoid string
46 // We have to return "null" instead of null, in order to avoid string
45 // styling as the default, when using Django templates inside HTML
47 // styling as the default, when using Django templates inside HTML
46 // element attributes
48 // element attributes
47 function tokenBase (stream, state) {
49 function tokenBase (stream, state) {
48 // Attempt to identify a variable, template or comment tag respectively
50 // Attempt to identify a variable, template or comment tag respectively
49 if (stream.match("{{")) {
51 if (stream.match("{{")) {
50 state.tokenize = inVariable;
52 state.tokenize = inVariable;
51 return "tag";
53 return "tag";
52 } else if (stream.match("{%")) {
54 } else if (stream.match("{%")) {
53 state.tokenize = inTag;
55 state.tokenize = inTag;
54 return "tag";
56 return "tag";
55 } else if (stream.match("{#")) {
57 } else if (stream.match("{#")) {
56 state.tokenize = inComment;
58 state.tokenize = inComment;
57 return "comment";
59 return "comment";
58 }
60 }
59
61
60 // Ignore completely any stream series that do not match the
62 // Ignore completely any stream series that do not match the
61 // Django template opening tags.
63 // Django template opening tags.
62 while (stream.next() != null && !stream.match("{{", false) && !stream.match("{%", false)) {}
64 while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {}
63 return null;
65 return null;
64 }
66 }
65
67
66 // A string can be included in either single or double quotes (this is
68 // A string can be included in either single or double quotes (this is
67 // the delimeter). Mark everything as a string until the start delimeter
69 // the delimeter). Mark everything as a string until the start delimeter
68 // occurs again.
70 // occurs again.
69 function inString (delimeter, previousTokenizer) {
71 function inString (delimeter, previousTokenizer) {
70 return function (stream, state) {
72 return function (stream, state) {
71 if (!state.escapeNext && stream.eat(delimeter)) {
73 if (!state.escapeNext && stream.eat(delimeter)) {
72 state.tokenize = previousTokenizer;
74 state.tokenize = previousTokenizer;
73 } else {
75 } else {
74 if (state.escapeNext) {
76 if (state.escapeNext) {
75 state.escapeNext = false;
77 state.escapeNext = false;
76 }
78 }
77
79
78 var ch = stream.next();
80 var ch = stream.next();
79
81
80 // Take into account the backslash for escaping characters, such as
82 // Take into account the backslash for escaping characters, such as
81 // the string delimeter.
83 // the string delimeter.
82 if (ch == "\\") {
84 if (ch == "\\") {
83 state.escapeNext = true;
85 state.escapeNext = true;
84 }
86 }
85 }
87 }
86
88
87 return "string";
89 return "string";
88 };
90 };
89 }
91 }
90
92
91 // Apply Django template variable syntax highlighting
93 // Apply Django template variable syntax highlighting
92 function inVariable (stream, state) {
94 function inVariable (stream, state) {
93 // Attempt to match a dot that precedes a property
95 // Attempt to match a dot that precedes a property
94 if (state.waitDot) {
96 if (state.waitDot) {
95 state.waitDot = false;
97 state.waitDot = false;
96
98
97 if (stream.peek() != ".") {
99 if (stream.peek() != ".") {
98 return "null";
100 return "null";
99 }
101 }
100
102
101 // Dot folowed by a non-word character should be considered an error.
103 // Dot folowed by a non-word character should be considered an error.
102 if (stream.match(/\.\W+/)) {
104 if (stream.match(/\.\W+/)) {
103 return "error";
105 return "error";
104 } else if (stream.eat(".")) {
106 } else if (stream.eat(".")) {
105 state.waitProperty = true;
107 state.waitProperty = true;
106 return "null";
108 return "null";
107 } else {
109 } else {
108 throw Error ("Unexpected error while waiting for property.");
110 throw Error ("Unexpected error while waiting for property.");
109 }
111 }
110 }
112 }
111
113
112 // Attempt to match a pipe that precedes a filter
114 // Attempt to match a pipe that precedes a filter
113 if (state.waitPipe) {
115 if (state.waitPipe) {
114 state.waitPipe = false;
116 state.waitPipe = false;
115
117
116 if (stream.peek() != "|") {
118 if (stream.peek() != "|") {
117 return "null";
119 return "null";
118 }
120 }
119
121
120 // Pipe folowed by a non-word character should be considered an error.
122 // Pipe folowed by a non-word character should be considered an error.
121 if (stream.match(/\.\W+/)) {
123 if (stream.match(/\.\W+/)) {
122 return "error";
124 return "error";
123 } else if (stream.eat("|")) {
125 } else if (stream.eat("|")) {
124 state.waitFilter = true;
126 state.waitFilter = true;
125 return "null";
127 return "null";
126 } else {
128 } else {
127 throw Error ("Unexpected error while waiting for filter.");
129 throw Error ("Unexpected error while waiting for filter.");
128 }
130 }
129 }
131 }
130
132
131 // Highlight properties
133 // Highlight properties
132 if (state.waitProperty) {
134 if (state.waitProperty) {
133 state.waitProperty = false;
135 state.waitProperty = false;
134 if (stream.match(/\b(\w+)\b/)) {
136 if (stream.match(/\b(\w+)\b/)) {
135 state.waitDot = true; // A property can be followed by another property
137 state.waitDot = true; // A property can be followed by another property
136 state.waitPipe = true; // A property can be followed by a filter
138 state.waitPipe = true; // A property can be followed by a filter
137 return "property";
139 return "property";
138 }
140 }
139 }
141 }
140
142
141 // Highlight filters
143 // Highlight filters
142 if (state.waitFilter) {
144 if (state.waitFilter) {
143 state.waitFilter = false;
145 state.waitFilter = false;
144 if (stream.match(filters)) {
146 if (stream.match(filters)) {
145 return "variable-2";
147 return "variable-2";
146 }
148 }
147 }
149 }
148
150
149 // Ignore all white spaces
151 // Ignore all white spaces
150 if (stream.eatSpace()) {
152 if (stream.eatSpace()) {
151 state.waitProperty = false;
153 state.waitProperty = false;
152 return "null";
154 return "null";
153 }
155 }
154
156
155 // Identify numbers
157 // Identify numbers
156 if (stream.match(/\b\d+(\.\d+)?\b/)) {
158 if (stream.match(/\b\d+(\.\d+)?\b/)) {
157 return "number";
159 return "number";
158 }
160 }
159
161
160 // Identify strings
162 // Identify strings
161 if (stream.match("'")) {
163 if (stream.match("'")) {
162 state.tokenize = inString("'", state.tokenize);
164 state.tokenize = inString("'", state.tokenize);
163 return "string";
165 return "string";
164 } else if (stream.match('"')) {
166 } else if (stream.match('"')) {
165 state.tokenize = inString('"', state.tokenize);
167 state.tokenize = inString('"', state.tokenize);
166 return "string";
168 return "string";
167 }
169 }
168
170
169 // Attempt to find the variable
171 // Attempt to find the variable
170 if (stream.match(/\b(\w+)\b/) && !state.foundVariable) {
172 if (stream.match(/\b(\w+)\b/) && !state.foundVariable) {
171 state.waitDot = true;
173 state.waitDot = true;
172 state.waitPipe = true; // A property can be followed by a filter
174 state.waitPipe = true; // A property can be followed by a filter
173 return "variable";
175 return "variable";
174 }
176 }
175
177
176 // If found closing tag reset
178 // If found closing tag reset
177 if (stream.match("}}")) {
179 if (stream.match("}}")) {
178 state.waitProperty = null;
180 state.waitProperty = null;
179 state.waitFilter = null;
181 state.waitFilter = null;
180 state.waitDot = null;
182 state.waitDot = null;
181 state.waitPipe = null;
183 state.waitPipe = null;
182 state.tokenize = tokenBase;
184 state.tokenize = tokenBase;
183 return "tag";
185 return "tag";
184 }
186 }
185
187
186 // If nothing was found, advance to the next character
188 // If nothing was found, advance to the next character
187 stream.next();
189 stream.next();
188 return "null";
190 return "null";
189 }
191 }
190
192
191 function inTag (stream, state) {
193 function inTag (stream, state) {
192 // Attempt to match a dot that precedes a property
194 // Attempt to match a dot that precedes a property
193 if (state.waitDot) {
195 if (state.waitDot) {
194 state.waitDot = false;
196 state.waitDot = false;
195
197
196 if (stream.peek() != ".") {
198 if (stream.peek() != ".") {
197 return "null";
199 return "null";
198 }
200 }
199
201
200 // Dot folowed by a non-word character should be considered an error.
202 // Dot folowed by a non-word character should be considered an error.
201 if (stream.match(/\.\W+/)) {
203 if (stream.match(/\.\W+/)) {
202 return "error";
204 return "error";
203 } else if (stream.eat(".")) {
205 } else if (stream.eat(".")) {
204 state.waitProperty = true;
206 state.waitProperty = true;
205 return "null";
207 return "null";
206 } else {
208 } else {
207 throw Error ("Unexpected error while waiting for property.");
209 throw Error ("Unexpected error while waiting for property.");
208 }
210 }
209 }
211 }
210
212
211 // Attempt to match a pipe that precedes a filter
213 // Attempt to match a pipe that precedes a filter
212 if (state.waitPipe) {
214 if (state.waitPipe) {
213 state.waitPipe = false;
215 state.waitPipe = false;
214
216
215 if (stream.peek() != "|") {
217 if (stream.peek() != "|") {
216 return "null";
218 return "null";
217 }
219 }
218
220
219 // Pipe folowed by a non-word character should be considered an error.
221 // Pipe folowed by a non-word character should be considered an error.
220 if (stream.match(/\.\W+/)) {
222 if (stream.match(/\.\W+/)) {
221 return "error";
223 return "error";
222 } else if (stream.eat("|")) {
224 } else if (stream.eat("|")) {
223 state.waitFilter = true;
225 state.waitFilter = true;
224 return "null";
226 return "null";
225 } else {
227 } else {
226 throw Error ("Unexpected error while waiting for filter.");
228 throw Error ("Unexpected error while waiting for filter.");
227 }
229 }
228 }
230 }
229
231
230 // Highlight properties
232 // Highlight properties
231 if (state.waitProperty) {
233 if (state.waitProperty) {
232 state.waitProperty = false;
234 state.waitProperty = false;
233 if (stream.match(/\b(\w+)\b/)) {
235 if (stream.match(/\b(\w+)\b/)) {
234 state.waitDot = true; // A property can be followed by another property
236 state.waitDot = true; // A property can be followed by another property
235 state.waitPipe = true; // A property can be followed by a filter
237 state.waitPipe = true; // A property can be followed by a filter
236 return "property";
238 return "property";
237 }
239 }
238 }
240 }
239
241
240 // Highlight filters
242 // Highlight filters
241 if (state.waitFilter) {
243 if (state.waitFilter) {
242 state.waitFilter = false;
244 state.waitFilter = false;
243 if (stream.match(filters)) {
245 if (stream.match(filters)) {
244 return "variable-2";
246 return "variable-2";
245 }
247 }
246 }
248 }
247
249
248 // Ignore all white spaces
250 // Ignore all white spaces
249 if (stream.eatSpace()) {
251 if (stream.eatSpace()) {
250 state.waitProperty = false;
252 state.waitProperty = false;
251 return "null";
253 return "null";
252 }
254 }
253
255
254 // Identify numbers
256 // Identify numbers
255 if (stream.match(/\b\d+(\.\d+)?\b/)) {
257 if (stream.match(/\b\d+(\.\d+)?\b/)) {
256 return "number";
258 return "number";
257 }
259 }
258
260
259 // Identify strings
261 // Identify strings
260 if (stream.match("'")) {
262 if (stream.match("'")) {
261 state.tokenize = inString("'", state.tokenize);
263 state.tokenize = inString("'", state.tokenize);
262 return "string";
264 return "string";
263 } else if (stream.match('"')) {
265 } else if (stream.match('"')) {
264 state.tokenize = inString('"', state.tokenize);
266 state.tokenize = inString('"', state.tokenize);
265 return "string";
267 return "string";
266 }
268 }
267
269
268 // Attempt to match an operator
270 // Attempt to match an operator
269 if (stream.match(operators)) {
271 if (stream.match(operators)) {
270 return "operator";
272 return "operator";
271 }
273 }
272
274
275 // Attempt to match a word operator
276 if (stream.match(wordOperators)) {
277 return "keyword";
278 }
279
273 // Attempt to match a keyword
280 // Attempt to match a keyword
274 var keywordMatch = stream.match(keywords);
281 var keywordMatch = stream.match(keywords);
275 if (keywordMatch) {
282 if (keywordMatch) {
276 if (keywordMatch[0] == "comment") {
283 if (keywordMatch[0] == "comment") {
277 state.blockCommentTag = true;
284 state.blockCommentTag = true;
278 }
285 }
279 return "keyword";
286 return "keyword";
280 }
287 }
281
288
282 // Attempt to match a variable
289 // Attempt to match a variable
283 if (stream.match(/\b(\w+)\b/)) {
290 if (stream.match(/\b(\w+)\b/)) {
284 state.waitDot = true;
291 state.waitDot = true;
285 state.waitPipe = true; // A property can be followed by a filter
292 state.waitPipe = true; // A property can be followed by a filter
286 return "variable";
293 return "variable";
287 }
294 }
288
295
289 // If found closing tag reset
296 // If found closing tag reset
290 if (stream.match("%}")) {
297 if (stream.match("%}")) {
291 state.waitProperty = null;
298 state.waitProperty = null;
292 state.waitFilter = null;
299 state.waitFilter = null;
293 state.waitDot = null;
300 state.waitDot = null;
294 state.waitPipe = null;
301 state.waitPipe = null;
295 // If the tag that closes is a block comment tag, we want to mark the
302 // If the tag that closes is a block comment tag, we want to mark the
296 // following code as comment, until the tag closes.
303 // following code as comment, until the tag closes.
297 if (state.blockCommentTag) {
304 if (state.blockCommentTag) {
298 state.blockCommentTag = false; // Release the "lock"
305 state.blockCommentTag = false; // Release the "lock"
299 state.tokenize = inBlockComment;
306 state.tokenize = inBlockComment;
300 } else {
307 } else {
301 state.tokenize = tokenBase;
308 state.tokenize = tokenBase;
302 }
309 }
303 return "tag";
310 return "tag";
304 }
311 }
305
312
306 // If nothing was found, advance to the next character
313 // If nothing was found, advance to the next character
307 stream.next();
314 stream.next();
308 return "null";
315 return "null";
309 }
316 }
310
317
311 // Mark everything as comment inside the tag and the tag itself.
318 // Mark everything as comment inside the tag and the tag itself.
312 function inComment (stream, state) {
319 function inComment (stream, state) {
313 if (stream.match("#}")) {
320 if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase
314 state.tokenize = tokenBase;
321 else stream.skipToEnd()
315 }
316 return "comment";
322 return "comment";
317 }
323 }
318
324
319 // Mark everything as a comment until the `blockcomment` tag closes.
325 // Mark everything as a comment until the `blockcomment` tag closes.
320 function inBlockComment (stream, state) {
326 function inBlockComment (stream, state) {
321 if (stream.match(/\{%\s*endcomment\s*%\}/, false)) {
327 if (stream.match(/\{%\s*endcomment\s*%\}/, false)) {
322 state.tokenize = inTag;
328 state.tokenize = inTag;
323 stream.match("{%");
329 stream.match("{%");
324 return "tag";
330 return "tag";
325 } else {
331 } else {
326 stream.next();
332 stream.next();
327 return "comment";
333 return "comment";
328 }
334 }
329 }
335 }
330
336
331 return {
337 return {
332 startState: function () {
338 startState: function () {
333 return {tokenize: tokenBase};
339 return {tokenize: tokenBase};
334 },
340 },
335 token: function (stream, state) {
341 token: function (stream, state) {
336 return state.tokenize(stream, state);
342 return state.tokenize(stream, state);
337 },
343 },
338 blockCommentStart: "{% comment %}",
344 blockCommentStart: "{% comment %}",
339 blockCommentEnd: "{% endcomment %}"
345 blockCommentEnd: "{% endcomment %}"
340 };
346 };
341 });
347 });
342
348
343 CodeMirror.defineMode("django", function(config) {
349 CodeMirror.defineMode("django", function(config) {
344 var htmlBase = CodeMirror.getMode(config, "text/html");
350 var htmlBase = CodeMirror.getMode(config, "text/html");
345 var djangoInner = CodeMirror.getMode(config, "django:inner");
351 var djangoInner = CodeMirror.getMode(config, "django:inner");
346 return CodeMirror.overlayMode(htmlBase, djangoInner);
352 return CodeMirror.overlayMode(htmlBase, djangoInner);
347 });
353 });
348
354
349 CodeMirror.defineMIME("text/x-django", "django");
355 CodeMirror.defineMIME("text/x-django", "django");
350 });
356 });
@@ -1,76 +1,79 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
6 mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
8 define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 // Collect all Dockerfile directives
14 // Collect all Dockerfile directives
15 var instructions = ["from", "maintainer", "run", "cmd", "expose", "env",
15 var instructions = ["from", "maintainer", "run", "cmd", "expose", "env",
16 "add", "copy", "entrypoint", "volume", "user",
16 "add", "copy", "entrypoint", "volume", "user",
17 "workdir", "onbuild"],
17 "workdir", "onbuild"],
18 instructionRegex = "(" + instructions.join('|') + ")",
18 instructionRegex = "(" + instructions.join('|') + ")",
19 instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
19 instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
20 instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
20 instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
21
21
22 CodeMirror.defineSimpleMode("dockerfile", {
22 CodeMirror.defineSimpleMode("dockerfile", {
23 start: [
23 start: [
24 // Block comment: This is a line starting with a comment
24 // Block comment: This is a line starting with a comment
25 {
25 {
26 regex: /#.*$/,
26 regex: /#.*$/,
27 token: "comment"
27 token: "comment"
28 },
28 },
29 // Highlight an instruction without any arguments (for convenience)
29 // Highlight an instruction without any arguments (for convenience)
30 {
30 {
31 regex: instructionOnlyLine,
31 regex: instructionOnlyLine,
32 token: "variable-2"
32 token: "variable-2"
33 },
33 },
34 // Highlight an instruction followed by arguments
34 // Highlight an instruction followed by arguments
35 {
35 {
36 regex: instructionWithArguments,
36 regex: instructionWithArguments,
37 token: ["variable-2", null],
37 token: ["variable-2", null],
38 next: "arguments"
38 next: "arguments"
39 },
39 },
40 {
40 {
41 regex: /./,
41 regex: /./,
42 token: null
42 token: null
43 }
43 }
44 ],
44 ],
45 arguments: [
45 arguments: [
46 {
46 {
47 // Line comment without instruction arguments is an error
47 // Line comment without instruction arguments is an error
48 regex: /#.*$/,
48 regex: /#.*$/,
49 token: "error",
49 token: "error",
50 next: "start"
50 next: "start"
51 },
51 },
52 {
52 {
53 regex: /[^#]+\\$/,
53 regex: /[^#]+\\$/,
54 token: null
54 token: null
55 },
55 },
56 {
56 {
57 // Match everything except for the inline comment
57 // Match everything except for the inline comment
58 regex: /[^#]+/,
58 regex: /[^#]+/,
59 token: null,
59 token: null,
60 next: "start"
60 next: "start"
61 },
61 },
62 {
62 {
63 regex: /$/,
63 regex: /$/,
64 token: null,
64 token: null,
65 next: "start"
65 next: "start"
66 },
66 },
67 // Fail safe return to start
67 // Fail safe return to start
68 {
68 {
69 token: null,
69 token: null,
70 next: "start"
70 next: "start"
71 }
71 }
72 ]
72 ],
73 meta: {
74 lineComment: "#"
75 }
73 });
76 });
74
77
75 CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");
78 CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");
76 });
79 });
@@ -1,205 +1,205 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("elm", function() {
14 CodeMirror.defineMode("elm", function() {
15
15
16 function switchState(source, setState, f) {
16 function switchState(source, setState, f) {
17 setState(f);
17 setState(f);
18 return f(source, setState);
18 return f(source, setState);
19 }
19 }
20
20
21 // These should all be Unicode extended, as per the Haskell 2010 report
21 // These should all be Unicode extended, as per the Haskell 2010 report
22 var smallRE = /[a-z_]/;
22 var smallRE = /[a-z_]/;
23 var largeRE = /[A-Z]/;
23 var largeRE = /[A-Z]/;
24 var digitRE = /[0-9]/;
24 var digitRE = /[0-9]/;
25 var hexitRE = /[0-9A-Fa-f]/;
25 var hexitRE = /[0-9A-Fa-f]/;
26 var octitRE = /[0-7]/;
26 var octitRE = /[0-7]/;
27 var idRE = /[a-z_A-Z0-9\']/;
27 var idRE = /[a-z_A-Z0-9\']/;
28 var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/;
28 var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/;
29 var specialRE = /[(),;[\]`{}]/;
29 var specialRE = /[(),;[\]`{}]/;
30 var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
30 var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
31
31
32 function normal() {
32 function normal() {
33 return function (source, setState) {
33 return function (source, setState) {
34 if (source.eatWhile(whiteCharRE)) {
34 if (source.eatWhile(whiteCharRE)) {
35 return null;
35 return null;
36 }
36 }
37
37
38 var ch = source.next();
38 var ch = source.next();
39 if (specialRE.test(ch)) {
39 if (specialRE.test(ch)) {
40 if (ch == '{' && source.eat('-')) {
40 if (ch == '{' && source.eat('-')) {
41 var t = "comment";
41 var t = "comment";
42 if (source.eat('#')) t = "meta";
42 if (source.eat('#')) t = "meta";
43 return switchState(source, setState, ncomment(t, 1));
43 return switchState(source, setState, ncomment(t, 1));
44 }
44 }
45 return null;
45 return null;
46 }
46 }
47
47
48 if (ch == '\'') {
48 if (ch == '\'') {
49 if (source.eat('\\'))
49 if (source.eat('\\'))
50 source.next(); // should handle other escapes here
50 source.next(); // should handle other escapes here
51 else
51 else
52 source.next();
52 source.next();
53
53
54 if (source.eat('\''))
54 if (source.eat('\''))
55 return "string";
55 return "string";
56 return "error";
56 return "error";
57 }
57 }
58
58
59 if (ch == '"') {
59 if (ch == '"') {
60 return switchState(source, setState, stringLiteral);
60 return switchState(source, setState, stringLiteral);
61 }
61 }
62
62
63 if (largeRE.test(ch)) {
63 if (largeRE.test(ch)) {
64 source.eatWhile(idRE);
64 source.eatWhile(idRE);
65 if (source.eat('.'))
65 if (source.eat('.'))
66 return "qualifier";
66 return "qualifier";
67 return "variable-2";
67 return "variable-2";
68 }
68 }
69
69
70 if (smallRE.test(ch)) {
70 if (smallRE.test(ch)) {
71 var isDef = source.pos === 1;
71 var isDef = source.pos === 1;
72 source.eatWhile(idRE);
72 source.eatWhile(idRE);
73 return isDef ? "variable-3" : "variable";
73 return isDef ? "variable-3" : "variable";
74 }
74 }
75
75
76 if (digitRE.test(ch)) {
76 if (digitRE.test(ch)) {
77 if (ch == '0') {
77 if (ch == '0') {
78 if (source.eat(/[xX]/)) {
78 if (source.eat(/[xX]/)) {
79 source.eatWhile(hexitRE); // should require at least 1
79 source.eatWhile(hexitRE); // should require at least 1
80 return "integer";
80 return "integer";
81 }
81 }
82 if (source.eat(/[oO]/)) {
82 if (source.eat(/[oO]/)) {
83 source.eatWhile(octitRE); // should require at least 1
83 source.eatWhile(octitRE); // should require at least 1
84 return "number";
84 return "number";
85 }
85 }
86 }
86 }
87 source.eatWhile(digitRE);
87 source.eatWhile(digitRE);
88 var t = "number";
88 var t = "number";
89 if (source.eat('.')) {
89 if (source.eat('.')) {
90 t = "number";
90 t = "number";
91 source.eatWhile(digitRE); // should require at least 1
91 source.eatWhile(digitRE); // should require at least 1
92 }
92 }
93 if (source.eat(/[eE]/)) {
93 if (source.eat(/[eE]/)) {
94 t = "number";
94 t = "number";
95 source.eat(/[-+]/);
95 source.eat(/[-+]/);
96 source.eatWhile(digitRE); // should require at least 1
96 source.eatWhile(digitRE); // should require at least 1
97 }
97 }
98 return t;
98 return t;
99 }
99 }
100
100
101 if (symbolRE.test(ch)) {
101 if (symbolRE.test(ch)) {
102 if (ch == '-' && source.eat(/-/)) {
102 if (ch == '-' && source.eat(/-/)) {
103 source.eatWhile(/-/);
103 source.eatWhile(/-/);
104 if (!source.eat(symbolRE)) {
104 if (!source.eat(symbolRE)) {
105 source.skipToEnd();
105 source.skipToEnd();
106 return "comment";
106 return "comment";
107 }
107 }
108 }
108 }
109 source.eatWhile(symbolRE);
109 source.eatWhile(symbolRE);
110 return "builtin";
110 return "builtin";
111 }
111 }
112
112
113 return "error";
113 return "error";
114 }
114 }
115 }
115 }
116
116
117 function ncomment(type, nest) {
117 function ncomment(type, nest) {
118 if (nest == 0) {
118 if (nest == 0) {
119 return normal();
119 return normal();
120 }
120 }
121 return function(source, setState) {
121 return function(source, setState) {
122 var currNest = nest;
122 var currNest = nest;
123 while (!source.eol()) {
123 while (!source.eol()) {
124 var ch = source.next();
124 var ch = source.next();
125 if (ch == '{' && source.eat('-')) {
125 if (ch == '{' && source.eat('-')) {
126 ++currNest;
126 ++currNest;
127 } else if (ch == '-' && source.eat('}')) {
127 } else if (ch == '-' && source.eat('}')) {
128 --currNest;
128 --currNest;
129 if (currNest == 0) {
129 if (currNest == 0) {
130 setState(normal());
130 setState(normal());
131 return type;
131 return type;
132 }
132 }
133 }
133 }
134 }
134 }
135 setState(ncomment(type, currNest));
135 setState(ncomment(type, currNest));
136 return type;
136 return type;
137 }
137 }
138 }
138 }
139
139
140 function stringLiteral(source, setState) {
140 function stringLiteral(source, setState) {
141 while (!source.eol()) {
141 while (!source.eol()) {
142 var ch = source.next();
142 var ch = source.next();
143 if (ch == '"') {
143 if (ch == '"') {
144 setState(normal());
144 setState(normal());
145 return "string";
145 return "string";
146 }
146 }
147 if (ch == '\\') {
147 if (ch == '\\') {
148 if (source.eol() || source.eat(whiteCharRE)) {
148 if (source.eol() || source.eat(whiteCharRE)) {
149 setState(stringGap);
149 setState(stringGap);
150 return "string";
150 return "string";
151 }
151 }
152 if (!source.eat('&')) source.next(); // should handle other escapes here
152 if (!source.eat('&')) source.next(); // should handle other escapes here
153 }
153 }
154 }
154 }
155 setState(normal());
155 setState(normal());
156 return "error";
156 return "error";
157 }
157 }
158
158
159 function stringGap(source, setState) {
159 function stringGap(source, setState) {
160 if (source.eat('\\')) {
160 if (source.eat('\\')) {
161 return switchState(source, setState, stringLiteral);
161 return switchState(source, setState, stringLiteral);
162 }
162 }
163 source.next();
163 source.next();
164 setState(normal());
164 setState(normal());
165 return "error";
165 return "error";
166 }
166 }
167
167
168
168
169 var wellKnownWords = (function() {
169 var wellKnownWords = (function() {
170 var wkw = {};
170 var wkw = {};
171
171
172 var keywords = [
172 var keywords = [
173 "case", "of", "as",
173 "case", "of", "as",
174 "if", "then", "else",
174 "if", "then", "else",
175 "let", "in",
175 "let", "in",
176 "infix", "infixl", "infixr",
176 "infix", "infixl", "infixr",
177 "type", "alias",
177 "type", "alias",
178 "input", "output", "foreign", "loopback",
178 "input", "output", "foreign", "loopback",
179 "module", "where", "import", "exposing",
179 "module", "where", "import", "exposing",
180 "_", "..", "|", ":", "=", "\\", "\"", "->", "<-"
180 "_", "..", "|", ":", "=", "\\", "\"", "->", "<-"
181 ];
181 ];
182
182
183 for (var i = keywords.length; i--;)
183 for (var i = keywords.length; i--;)
184 wkw[keywords[i]] = "keyword";
184 wkw[keywords[i]] = "keyword";
185
185
186 return wkw;
186 return wkw;
187 })();
187 })();
188
188
189
189
190
190
191 return {
191 return {
192 startState: function () { return { f: normal() }; },
192 startState: function () { return { f: normal() }; },
193 copyState: function (s) { return { f: s.f }; },
193 copyState: function (s) { return { f: s.f }; },
194
194
195 token: function(stream, state) {
195 token: function(stream, state) {
196 var t = state.f(stream, function(s) { state.f = s; });
196 var t = state.f(stream, function(s) { state.f = s; });
197 var w = stream.current();
197 var w = stream.current();
198 return (wellKnownWords.hasOwnProperty(w)) ? wellKnownWords[w] : t;
198 return (wellKnownWords.hasOwnProperty(w)) ? wellKnownWords[w] : t;
199 }
199 }
200 };
200 };
201
201
202 });
202 });
203
203
204 CodeMirror.defineMIME("text/x-elm", "elm");
204 CodeMirror.defineMIME("text/x-elm", "elm");
205 })();
205 });
@@ -1,622 +1,618 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 /*jshint unused:true, eqnull:true, curly:true, bitwise:true */
4 /*jshint unused:true, eqnull:true, curly:true, bitwise:true */
5 /*jshint undef:true, latedef:true, trailing:true */
5 /*jshint undef:true, latedef:true, trailing:true */
6 /*global CodeMirror:true */
6 /*global CodeMirror:true */
7
7
8 // erlang mode.
8 // erlang mode.
9 // tokenizer -> token types -> CodeMirror styles
9 // tokenizer -> token types -> CodeMirror styles
10 // tokenizer maintains a parse stack
10 // tokenizer maintains a parse stack
11 // indenter uses the parse stack
11 // indenter uses the parse stack
12
12
13 // TODO indenter:
13 // TODO indenter:
14 // bit syntax
14 // bit syntax
15 // old guard/bif/conversion clashes (e.g. "float/1")
15 // old guard/bif/conversion clashes (e.g. "float/1")
16 // type/spec/opaque
16 // type/spec/opaque
17
17
18 (function(mod) {
18 (function(mod) {
19 if (typeof exports == "object" && typeof module == "object") // CommonJS
19 if (typeof exports == "object" && typeof module == "object") // CommonJS
20 mod(require("../../lib/codemirror"));
20 mod(require("../../lib/codemirror"));
21 else if (typeof define == "function" && define.amd) // AMD
21 else if (typeof define == "function" && define.amd) // AMD
22 define(["../../lib/codemirror"], mod);
22 define(["../../lib/codemirror"], mod);
23 else // Plain browser env
23 else // Plain browser env
24 mod(CodeMirror);
24 mod(CodeMirror);
25 })(function(CodeMirror) {
25 })(function(CodeMirror) {
26 "use strict";
26 "use strict";
27
27
28 CodeMirror.defineMIME("text/x-erlang", "erlang");
28 CodeMirror.defineMIME("text/x-erlang", "erlang");
29
29
30 CodeMirror.defineMode("erlang", function(cmCfg) {
30 CodeMirror.defineMode("erlang", function(cmCfg) {
31 "use strict";
31 "use strict";
32
32
33 /////////////////////////////////////////////////////////////////////////////
33 /////////////////////////////////////////////////////////////////////////////
34 // constants
34 // constants
35
35
36 var typeWords = [
36 var typeWords = [
37 "-type", "-spec", "-export_type", "-opaque"];
37 "-type", "-spec", "-export_type", "-opaque"];
38
38
39 var keywordWords = [
39 var keywordWords = [
40 "after","begin","catch","case","cond","end","fun","if",
40 "after","begin","catch","case","cond","end","fun","if",
41 "let","of","query","receive","try","when"];
41 "let","of","query","receive","try","when"];
42
42
43 var separatorRE = /[\->,;]/;
43 var separatorRE = /[\->,;]/;
44 var separatorWords = [
44 var separatorWords = [
45 "->",";",","];
45 "->",";",","];
46
46
47 var operatorAtomWords = [
47 var operatorAtomWords = [
48 "and","andalso","band","bnot","bor","bsl","bsr","bxor",
48 "and","andalso","band","bnot","bor","bsl","bsr","bxor",
49 "div","not","or","orelse","rem","xor"];
49 "div","not","or","orelse","rem","xor"];
50
50
51 var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/;
51 var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/;
52 var operatorSymbolWords = [
52 var operatorSymbolWords = [
53 "=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];
53 "=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];
54
54
55 var openParenRE = /[<\(\[\{]/;
55 var openParenRE = /[<\(\[\{]/;
56 var openParenWords = [
56 var openParenWords = [
57 "<<","(","[","{"];
57 "<<","(","[","{"];
58
58
59 var closeParenRE = /[>\)\]\}]/;
59 var closeParenRE = /[>\)\]\}]/;
60 var closeParenWords = [
60 var closeParenWords = [
61 "}","]",")",">>"];
61 "}","]",")",">>"];
62
62
63 var guardWords = [
63 var guardWords = [
64 "is_atom","is_binary","is_bitstring","is_boolean","is_float",
64 "is_atom","is_binary","is_bitstring","is_boolean","is_float",
65 "is_function","is_integer","is_list","is_number","is_pid",
65 "is_function","is_integer","is_list","is_number","is_pid",
66 "is_port","is_record","is_reference","is_tuple",
66 "is_port","is_record","is_reference","is_tuple",
67 "atom","binary","bitstring","boolean","function","integer","list",
67 "atom","binary","bitstring","boolean","function","integer","list",
68 "number","pid","port","record","reference","tuple"];
68 "number","pid","port","record","reference","tuple"];
69
69
70 var bifWords = [
70 var bifWords = [
71 "abs","adler32","adler32_combine","alive","apply","atom_to_binary",
71 "abs","adler32","adler32_combine","alive","apply","atom_to_binary",
72 "atom_to_list","binary_to_atom","binary_to_existing_atom",
72 "atom_to_list","binary_to_atom","binary_to_existing_atom",
73 "binary_to_list","binary_to_term","bit_size","bitstring_to_list",
73 "binary_to_list","binary_to_term","bit_size","bitstring_to_list",
74 "byte_size","check_process_code","contact_binary","crc32",
74 "byte_size","check_process_code","contact_binary","crc32",
75 "crc32_combine","date","decode_packet","delete_module",
75 "crc32_combine","date","decode_packet","delete_module",
76 "disconnect_node","element","erase","exit","float","float_to_list",
76 "disconnect_node","element","erase","exit","float","float_to_list",
77 "garbage_collect","get","get_keys","group_leader","halt","hd",
77 "garbage_collect","get","get_keys","group_leader","halt","hd",
78 "integer_to_list","internal_bif","iolist_size","iolist_to_binary",
78 "integer_to_list","internal_bif","iolist_size","iolist_to_binary",
79 "is_alive","is_atom","is_binary","is_bitstring","is_boolean",
79 "is_alive","is_atom","is_binary","is_bitstring","is_boolean",
80 "is_float","is_function","is_integer","is_list","is_number","is_pid",
80 "is_float","is_function","is_integer","is_list","is_number","is_pid",
81 "is_port","is_process_alive","is_record","is_reference","is_tuple",
81 "is_port","is_process_alive","is_record","is_reference","is_tuple",
82 "length","link","list_to_atom","list_to_binary","list_to_bitstring",
82 "length","link","list_to_atom","list_to_binary","list_to_bitstring",
83 "list_to_existing_atom","list_to_float","list_to_integer",
83 "list_to_existing_atom","list_to_float","list_to_integer",
84 "list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
84 "list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
85 "monitor_node","node","node_link","node_unlink","nodes","notalive",
85 "monitor_node","node","node_link","node_unlink","nodes","notalive",
86 "now","open_port","pid_to_list","port_close","port_command",
86 "now","open_port","pid_to_list","port_close","port_command",
87 "port_connect","port_control","pre_loaded","process_flag",
87 "port_connect","port_control","pre_loaded","process_flag",
88 "process_info","processes","purge_module","put","register",
88 "process_info","processes","purge_module","put","register",
89 "registered","round","self","setelement","size","spawn","spawn_link",
89 "registered","round","self","setelement","size","spawn","spawn_link",
90 "spawn_monitor","spawn_opt","split_binary","statistics",
90 "spawn_monitor","spawn_opt","split_binary","statistics",
91 "term_to_binary","time","throw","tl","trunc","tuple_size",
91 "term_to_binary","time","throw","tl","trunc","tuple_size",
92 "tuple_to_list","unlink","unregister","whereis"];
92 "tuple_to_list","unlink","unregister","whereis"];
93
93
94 // upper case: [A-Z] [Ø-Þ] [À-Ö]
94 // upper case: [A-Z] [Ø-Þ] [À-Ö]
95 // lower case: [a-z] [ß-ö] [ø-ÿ]
95 // lower case: [a-z] [ß-ö] [ø-ÿ]
96 var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/;
96 var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/;
97 var escapesRE =
97 var escapesRE =
98 /[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/;
98 /[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/;
99
99
100 /////////////////////////////////////////////////////////////////////////////
100 /////////////////////////////////////////////////////////////////////////////
101 // tokenizer
101 // tokenizer
102
102
103 function tokenizer(stream,state) {
103 function tokenizer(stream,state) {
104 // in multi-line string
104 // in multi-line string
105 if (state.in_string) {
105 if (state.in_string) {
106 state.in_string = (!doubleQuote(stream));
106 state.in_string = (!doubleQuote(stream));
107 return rval(state,stream,"string");
107 return rval(state,stream,"string");
108 }
108 }
109
109
110 // in multi-line atom
110 // in multi-line atom
111 if (state.in_atom) {
111 if (state.in_atom) {
112 state.in_atom = (!singleQuote(stream));
112 state.in_atom = (!singleQuote(stream));
113 return rval(state,stream,"atom");
113 return rval(state,stream,"atom");
114 }
114 }
115
115
116 // whitespace
116 // whitespace
117 if (stream.eatSpace()) {
117 if (stream.eatSpace()) {
118 return rval(state,stream,"whitespace");
118 return rval(state,stream,"whitespace");
119 }
119 }
120
120
121 // attributes and type specs
121 // attributes and type specs
122 if (!peekToken(state) &&
122 if (!peekToken(state) &&
123 stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) {
123 stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) {
124 if (is_member(stream.current(),typeWords)) {
124 if (is_member(stream.current(),typeWords)) {
125 return rval(state,stream,"type");
125 return rval(state,stream,"type");
126 }else{
126 }else{
127 return rval(state,stream,"attribute");
127 return rval(state,stream,"attribute");
128 }
128 }
129 }
129 }
130
130
131 var ch = stream.next();
131 var ch = stream.next();
132
132
133 // comment
133 // comment
134 if (ch == '%') {
134 if (ch == '%') {
135 stream.skipToEnd();
135 stream.skipToEnd();
136 return rval(state,stream,"comment");
136 return rval(state,stream,"comment");
137 }
137 }
138
138
139 // colon
139 // colon
140 if (ch == ":") {
140 if (ch == ":") {
141 return rval(state,stream,"colon");
141 return rval(state,stream,"colon");
142 }
142 }
143
143
144 // macro
144 // macro
145 if (ch == '?') {
145 if (ch == '?') {
146 stream.eatSpace();
146 stream.eatSpace();
147 stream.eatWhile(anumRE);
147 stream.eatWhile(anumRE);
148 return rval(state,stream,"macro");
148 return rval(state,stream,"macro");
149 }
149 }
150
150
151 // record
151 // record
152 if (ch == "#") {
152 if (ch == "#") {
153 stream.eatSpace();
153 stream.eatSpace();
154 stream.eatWhile(anumRE);
154 stream.eatWhile(anumRE);
155 return rval(state,stream,"record");
155 return rval(state,stream,"record");
156 }
156 }
157
157
158 // dollar escape
158 // dollar escape
159 if (ch == "$") {
159 if (ch == "$") {
160 if (stream.next() == "\\" && !stream.match(escapesRE)) {
160 if (stream.next() == "\\" && !stream.match(escapesRE)) {
161 return rval(state,stream,"error");
161 return rval(state,stream,"error");
162 }
162 }
163 return rval(state,stream,"number");
163 return rval(state,stream,"number");
164 }
164 }
165
165
166 // dot
166 // dot
167 if (ch == ".") {
167 if (ch == ".") {
168 return rval(state,stream,"dot");
168 return rval(state,stream,"dot");
169 }
169 }
170
170
171 // quoted atom
171 // quoted atom
172 if (ch == '\'') {
172 if (ch == '\'') {
173 if (!(state.in_atom = (!singleQuote(stream)))) {
173 if (!(state.in_atom = (!singleQuote(stream)))) {
174 if (stream.match(/\s*\/\s*[0-9]/,false)) {
174 if (stream.match(/\s*\/\s*[0-9]/,false)) {
175 stream.match(/\s*\/\s*[0-9]/,true);
175 stream.match(/\s*\/\s*[0-9]/,true);
176 return rval(state,stream,"fun"); // 'f'/0 style fun
176 return rval(state,stream,"fun"); // 'f'/0 style fun
177 }
177 }
178 if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) {
178 if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) {
179 return rval(state,stream,"function");
179 return rval(state,stream,"function");
180 }
180 }
181 }
181 }
182 return rval(state,stream,"atom");
182 return rval(state,stream,"atom");
183 }
183 }
184
184
185 // string
185 // string
186 if (ch == '"') {
186 if (ch == '"') {
187 state.in_string = (!doubleQuote(stream));
187 state.in_string = (!doubleQuote(stream));
188 return rval(state,stream,"string");
188 return rval(state,stream,"string");
189 }
189 }
190
190
191 // variable
191 // variable
192 if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) {
192 if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) {
193 stream.eatWhile(anumRE);
193 stream.eatWhile(anumRE);
194 return rval(state,stream,"variable");
194 return rval(state,stream,"variable");
195 }
195 }
196
196
197 // atom/keyword/BIF/function
197 // atom/keyword/BIF/function
198 if (/[a-z_ß-öø-ÿ]/.test(ch)) {
198 if (/[a-z_ß-öø-ÿ]/.test(ch)) {
199 stream.eatWhile(anumRE);
199 stream.eatWhile(anumRE);
200
200
201 if (stream.match(/\s*\/\s*[0-9]/,false)) {
201 if (stream.match(/\s*\/\s*[0-9]/,false)) {
202 stream.match(/\s*\/\s*[0-9]/,true);
202 stream.match(/\s*\/\s*[0-9]/,true);
203 return rval(state,stream,"fun"); // f/0 style fun
203 return rval(state,stream,"fun"); // f/0 style fun
204 }
204 }
205
205
206 var w = stream.current();
206 var w = stream.current();
207
207
208 if (is_member(w,keywordWords)) {
208 if (is_member(w,keywordWords)) {
209 return rval(state,stream,"keyword");
209 return rval(state,stream,"keyword");
210 }else if (is_member(w,operatorAtomWords)) {
210 }else if (is_member(w,operatorAtomWords)) {
211 return rval(state,stream,"operator");
211 return rval(state,stream,"operator");
212 }else if (stream.match(/\s*\(/,false)) {
212 }else if (stream.match(/\s*\(/,false)) {
213 // 'put' and 'erlang:put' are bifs, 'foo:put' is not
213 // 'put' and 'erlang:put' are bifs, 'foo:put' is not
214 if (is_member(w,bifWords) &&
214 if (is_member(w,bifWords) &&
215 ((peekToken(state).token != ":") ||
215 ((peekToken(state).token != ":") ||
216 (peekToken(state,2).token == "erlang"))) {
216 (peekToken(state,2).token == "erlang"))) {
217 return rval(state,stream,"builtin");
217 return rval(state,stream,"builtin");
218 }else if (is_member(w,guardWords)) {
218 }else if (is_member(w,guardWords)) {
219 return rval(state,stream,"guard");
219 return rval(state,stream,"guard");
220 }else{
220 }else{
221 return rval(state,stream,"function");
221 return rval(state,stream,"function");
222 }
222 }
223 }else if (is_member(w,operatorAtomWords)) {
224 return rval(state,stream,"operator");
225 }else if (lookahead(stream) == ":") {
223 }else if (lookahead(stream) == ":") {
226 if (w == "erlang") {
224 if (w == "erlang") {
227 return rval(state,stream,"builtin");
225 return rval(state,stream,"builtin");
228 } else {
226 } else {
229 return rval(state,stream,"function");
227 return rval(state,stream,"function");
230 }
228 }
231 }else if (is_member(w,["true","false"])) {
229 }else if (is_member(w,["true","false"])) {
232 return rval(state,stream,"boolean");
230 return rval(state,stream,"boolean");
233 }else if (is_member(w,["true","false"])) {
234 return rval(state,stream,"boolean");
235 }else{
231 }else{
236 return rval(state,stream,"atom");
232 return rval(state,stream,"atom");
237 }
233 }
238 }
234 }
239
235
240 // number
236 // number
241 var digitRE = /[0-9]/;
237 var digitRE = /[0-9]/;
242 var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int
238 var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int
243 if (digitRE.test(ch)) {
239 if (digitRE.test(ch)) {
244 stream.eatWhile(digitRE);
240 stream.eatWhile(digitRE);
245 if (stream.eat('#')) { // 36#aZ style integer
241 if (stream.eat('#')) { // 36#aZ style integer
246 if (!stream.eatWhile(radixRE)) {
242 if (!stream.eatWhile(radixRE)) {
247 stream.backUp(1); //"36#" - syntax error
243 stream.backUp(1); //"36#" - syntax error
248 }
244 }
249 } else if (stream.eat('.')) { // float
245 } else if (stream.eat('.')) { // float
250 if (!stream.eatWhile(digitRE)) {
246 if (!stream.eatWhile(digitRE)) {
251 stream.backUp(1); // "3." - probably end of function
247 stream.backUp(1); // "3." - probably end of function
252 } else {
248 } else {
253 if (stream.eat(/[eE]/)) { // float with exponent
249 if (stream.eat(/[eE]/)) { // float with exponent
254 if (stream.eat(/[-+]/)) {
250 if (stream.eat(/[-+]/)) {
255 if (!stream.eatWhile(digitRE)) {
251 if (!stream.eatWhile(digitRE)) {
256 stream.backUp(2); // "2e-" - syntax error
252 stream.backUp(2); // "2e-" - syntax error
257 }
253 }
258 } else {
254 } else {
259 if (!stream.eatWhile(digitRE)) {
255 if (!stream.eatWhile(digitRE)) {
260 stream.backUp(1); // "2e" - syntax error
256 stream.backUp(1); // "2e" - syntax error
261 }
257 }
262 }
258 }
263 }
259 }
264 }
260 }
265 }
261 }
266 return rval(state,stream,"number"); // normal integer
262 return rval(state,stream,"number"); // normal integer
267 }
263 }
268
264
269 // open parens
265 // open parens
270 if (nongreedy(stream,openParenRE,openParenWords)) {
266 if (nongreedy(stream,openParenRE,openParenWords)) {
271 return rval(state,stream,"open_paren");
267 return rval(state,stream,"open_paren");
272 }
268 }
273
269
274 // close parens
270 // close parens
275 if (nongreedy(stream,closeParenRE,closeParenWords)) {
271 if (nongreedy(stream,closeParenRE,closeParenWords)) {
276 return rval(state,stream,"close_paren");
272 return rval(state,stream,"close_paren");
277 }
273 }
278
274
279 // separators
275 // separators
280 if (greedy(stream,separatorRE,separatorWords)) {
276 if (greedy(stream,separatorRE,separatorWords)) {
281 return rval(state,stream,"separator");
277 return rval(state,stream,"separator");
282 }
278 }
283
279
284 // operators
280 // operators
285 if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {
281 if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {
286 return rval(state,stream,"operator");
282 return rval(state,stream,"operator");
287 }
283 }
288
284
289 return rval(state,stream,null);
285 return rval(state,stream,null);
290 }
286 }
291
287
292 /////////////////////////////////////////////////////////////////////////////
288 /////////////////////////////////////////////////////////////////////////////
293 // utilities
289 // utilities
294 function nongreedy(stream,re,words) {
290 function nongreedy(stream,re,words) {
295 if (stream.current().length == 1 && re.test(stream.current())) {
291 if (stream.current().length == 1 && re.test(stream.current())) {
296 stream.backUp(1);
292 stream.backUp(1);
297 while (re.test(stream.peek())) {
293 while (re.test(stream.peek())) {
298 stream.next();
294 stream.next();
299 if (is_member(stream.current(),words)) {
295 if (is_member(stream.current(),words)) {
300 return true;
296 return true;
301 }
297 }
302 }
298 }
303 stream.backUp(stream.current().length-1);
299 stream.backUp(stream.current().length-1);
304 }
300 }
305 return false;
301 return false;
306 }
302 }
307
303
308 function greedy(stream,re,words) {
304 function greedy(stream,re,words) {
309 if (stream.current().length == 1 && re.test(stream.current())) {
305 if (stream.current().length == 1 && re.test(stream.current())) {
310 while (re.test(stream.peek())) {
306 while (re.test(stream.peek())) {
311 stream.next();
307 stream.next();
312 }
308 }
313 while (0 < stream.current().length) {
309 while (0 < stream.current().length) {
314 if (is_member(stream.current(),words)) {
310 if (is_member(stream.current(),words)) {
315 return true;
311 return true;
316 }else{
312 }else{
317 stream.backUp(1);
313 stream.backUp(1);
318 }
314 }
319 }
315 }
320 stream.next();
316 stream.next();
321 }
317 }
322 return false;
318 return false;
323 }
319 }
324
320
325 function doubleQuote(stream) {
321 function doubleQuote(stream) {
326 return quote(stream, '"', '\\');
322 return quote(stream, '"', '\\');
327 }
323 }
328
324
329 function singleQuote(stream) {
325 function singleQuote(stream) {
330 return quote(stream,'\'','\\');
326 return quote(stream,'\'','\\');
331 }
327 }
332
328
333 function quote(stream,quoteChar,escapeChar) {
329 function quote(stream,quoteChar,escapeChar) {
334 while (!stream.eol()) {
330 while (!stream.eol()) {
335 var ch = stream.next();
331 var ch = stream.next();
336 if (ch == quoteChar) {
332 if (ch == quoteChar) {
337 return true;
333 return true;
338 }else if (ch == escapeChar) {
334 }else if (ch == escapeChar) {
339 stream.next();
335 stream.next();
340 }
336 }
341 }
337 }
342 return false;
338 return false;
343 }
339 }
344
340
345 function lookahead(stream) {
341 function lookahead(stream) {
346 var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false);
342 var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false);
347 return m ? m.pop() : "";
343 return m ? m.pop() : "";
348 }
344 }
349
345
350 function is_member(element,list) {
346 function is_member(element,list) {
351 return (-1 < list.indexOf(element));
347 return (-1 < list.indexOf(element));
352 }
348 }
353
349
354 function rval(state,stream,type) {
350 function rval(state,stream,type) {
355
351
356 // parse stack
352 // parse stack
357 pushToken(state,realToken(type,stream));
353 pushToken(state,realToken(type,stream));
358
354
359 // map erlang token type to CodeMirror style class
355 // map erlang token type to CodeMirror style class
360 // erlang -> CodeMirror tag
356 // erlang -> CodeMirror tag
361 switch (type) {
357 switch (type) {
362 case "atom": return "atom";
358 case "atom": return "atom";
363 case "attribute": return "attribute";
359 case "attribute": return "attribute";
364 case "boolean": return "atom";
360 case "boolean": return "atom";
365 case "builtin": return "builtin";
361 case "builtin": return "builtin";
366 case "close_paren": return null;
362 case "close_paren": return null;
367 case "colon": return null;
363 case "colon": return null;
368 case "comment": return "comment";
364 case "comment": return "comment";
369 case "dot": return null;
365 case "dot": return null;
370 case "error": return "error";
366 case "error": return "error";
371 case "fun": return "meta";
367 case "fun": return "meta";
372 case "function": return "tag";
368 case "function": return "tag";
373 case "guard": return "property";
369 case "guard": return "property";
374 case "keyword": return "keyword";
370 case "keyword": return "keyword";
375 case "macro": return "variable-2";
371 case "macro": return "variable-2";
376 case "number": return "number";
372 case "number": return "number";
377 case "open_paren": return null;
373 case "open_paren": return null;
378 case "operator": return "operator";
374 case "operator": return "operator";
379 case "record": return "bracket";
375 case "record": return "bracket";
380 case "separator": return null;
376 case "separator": return null;
381 case "string": return "string";
377 case "string": return "string";
382 case "type": return "def";
378 case "type": return "def";
383 case "variable": return "variable";
379 case "variable": return "variable";
384 default: return null;
380 default: return null;
385 }
381 }
386 }
382 }
387
383
388 function aToken(tok,col,ind,typ) {
384 function aToken(tok,col,ind,typ) {
389 return {token: tok,
385 return {token: tok,
390 column: col,
386 column: col,
391 indent: ind,
387 indent: ind,
392 type: typ};
388 type: typ};
393 }
389 }
394
390
395 function realToken(type,stream) {
391 function realToken(type,stream) {
396 return aToken(stream.current(),
392 return aToken(stream.current(),
397 stream.column(),
393 stream.column(),
398 stream.indentation(),
394 stream.indentation(),
399 type);
395 type);
400 }
396 }
401
397
402 function fakeToken(type) {
398 function fakeToken(type) {
403 return aToken(type,0,0,type);
399 return aToken(type,0,0,type);
404 }
400 }
405
401
406 function peekToken(state,depth) {
402 function peekToken(state,depth) {
407 var len = state.tokenStack.length;
403 var len = state.tokenStack.length;
408 var dep = (depth ? depth : 1);
404 var dep = (depth ? depth : 1);
409
405
410 if (len < dep) {
406 if (len < dep) {
411 return false;
407 return false;
412 }else{
408 }else{
413 return state.tokenStack[len-dep];
409 return state.tokenStack[len-dep];
414 }
410 }
415 }
411 }
416
412
417 function pushToken(state,token) {
413 function pushToken(state,token) {
418
414
419 if (!(token.type == "comment" || token.type == "whitespace")) {
415 if (!(token.type == "comment" || token.type == "whitespace")) {
420 state.tokenStack = maybe_drop_pre(state.tokenStack,token);
416 state.tokenStack = maybe_drop_pre(state.tokenStack,token);
421 state.tokenStack = maybe_drop_post(state.tokenStack);
417 state.tokenStack = maybe_drop_post(state.tokenStack);
422 }
418 }
423 }
419 }
424
420
425 function maybe_drop_pre(s,token) {
421 function maybe_drop_pre(s,token) {
426 var last = s.length-1;
422 var last = s.length-1;
427
423
428 if (0 < last && s[last].type === "record" && token.type === "dot") {
424 if (0 < last && s[last].type === "record" && token.type === "dot") {
429 s.pop();
425 s.pop();
430 }else if (0 < last && s[last].type === "group") {
426 }else if (0 < last && s[last].type === "group") {
431 s.pop();
427 s.pop();
432 s.push(token);
428 s.push(token);
433 }else{
429 }else{
434 s.push(token);
430 s.push(token);
435 }
431 }
436 return s;
432 return s;
437 }
433 }
438
434
439 function maybe_drop_post(s) {
435 function maybe_drop_post(s) {
440 var last = s.length-1;
436 var last = s.length-1;
441
437
442 if (s[last].type === "dot") {
438 if (s[last].type === "dot") {
443 return [];
439 return [];
444 }
440 }
445 if (s[last].type === "fun" && s[last-1].token === "fun") {
441 if (s[last].type === "fun" && s[last-1].token === "fun") {
446 return s.slice(0,last-1);
442 return s.slice(0,last-1);
447 }
443 }
448 switch (s[s.length-1].token) {
444 switch (s[s.length-1].token) {
449 case "}": return d(s,{g:["{"]});
445 case "}": return d(s,{g:["{"]});
450 case "]": return d(s,{i:["["]});
446 case "]": return d(s,{i:["["]});
451 case ")": return d(s,{i:["("]});
447 case ")": return d(s,{i:["("]});
452 case ">>": return d(s,{i:["<<"]});
448 case ">>": return d(s,{i:["<<"]});
453 case "end": return d(s,{i:["begin","case","fun","if","receive","try"]});
449 case "end": return d(s,{i:["begin","case","fun","if","receive","try"]});
454 case ",": return d(s,{e:["begin","try","when","->",
450 case ",": return d(s,{e:["begin","try","when","->",
455 ",","(","[","{","<<"]});
451 ",","(","[","{","<<"]});
456 case "->": return d(s,{r:["when"],
452 case "->": return d(s,{r:["when"],
457 m:["try","if","case","receive"]});
453 m:["try","if","case","receive"]});
458 case ";": return d(s,{E:["case","fun","if","receive","try","when"]});
454 case ";": return d(s,{E:["case","fun","if","receive","try","when"]});
459 case "catch":return d(s,{e:["try"]});
455 case "catch":return d(s,{e:["try"]});
460 case "of": return d(s,{e:["case"]});
456 case "of": return d(s,{e:["case"]});
461 case "after":return d(s,{e:["receive","try"]});
457 case "after":return d(s,{e:["receive","try"]});
462 default: return s;
458 default: return s;
463 }
459 }
464 }
460 }
465
461
466 function d(stack,tt) {
462 function d(stack,tt) {
467 // stack is a stack of Token objects.
463 // stack is a stack of Token objects.
468 // tt is an object; {type:tokens}
464 // tt is an object; {type:tokens}
469 // type is a char, tokens is a list of token strings.
465 // type is a char, tokens is a list of token strings.
470 // The function returns (possibly truncated) stack.
466 // The function returns (possibly truncated) stack.
471 // It will descend the stack, looking for a Token such that Token.token
467 // It will descend the stack, looking for a Token such that Token.token
472 // is a member of tokens. If it does not find that, it will normally (but
468 // is a member of tokens. If it does not find that, it will normally (but
473 // see "E" below) return stack. If it does find a match, it will remove
469 // see "E" below) return stack. If it does find a match, it will remove
474 // all the Tokens between the top and the matched Token.
470 // all the Tokens between the top and the matched Token.
475 // If type is "m", that is all it does.
471 // If type is "m", that is all it does.
476 // If type is "i", it will also remove the matched Token and the top Token.
472 // If type is "i", it will also remove the matched Token and the top Token.
477 // If type is "g", like "i", but add a fake "group" token at the top.
473 // If type is "g", like "i", but add a fake "group" token at the top.
478 // If type is "r", it will remove the matched Token, but not the top Token.
474 // If type is "r", it will remove the matched Token, but not the top Token.
479 // If type is "e", it will keep the matched Token but not the top Token.
475 // If type is "e", it will keep the matched Token but not the top Token.
480 // If type is "E", it behaves as for type "e", except if there is no match,
476 // If type is "E", it behaves as for type "e", except if there is no match,
481 // in which case it will return an empty stack.
477 // in which case it will return an empty stack.
482
478
483 for (var type in tt) {
479 for (var type in tt) {
484 var len = stack.length-1;
480 var len = stack.length-1;
485 var tokens = tt[type];
481 var tokens = tt[type];
486 for (var i = len-1; -1 < i ; i--) {
482 for (var i = len-1; -1 < i ; i--) {
487 if (is_member(stack[i].token,tokens)) {
483 if (is_member(stack[i].token,tokens)) {
488 var ss = stack.slice(0,i);
484 var ss = stack.slice(0,i);
489 switch (type) {
485 switch (type) {
490 case "m": return ss.concat(stack[i]).concat(stack[len]);
486 case "m": return ss.concat(stack[i]).concat(stack[len]);
491 case "r": return ss.concat(stack[len]);
487 case "r": return ss.concat(stack[len]);
492 case "i": return ss;
488 case "i": return ss;
493 case "g": return ss.concat(fakeToken("group"));
489 case "g": return ss.concat(fakeToken("group"));
494 case "E": return ss.concat(stack[i]);
490 case "E": return ss.concat(stack[i]);
495 case "e": return ss.concat(stack[i]);
491 case "e": return ss.concat(stack[i]);
496 }
492 }
497 }
493 }
498 }
494 }
499 }
495 }
500 return (type == "E" ? [] : stack);
496 return (type == "E" ? [] : stack);
501 }
497 }
502
498
503 /////////////////////////////////////////////////////////////////////////////
499 /////////////////////////////////////////////////////////////////////////////
504 // indenter
500 // indenter
505
501
506 function indenter(state,textAfter) {
502 function indenter(state,textAfter) {
507 var t;
503 var t;
508 var unit = cmCfg.indentUnit;
504 var unit = cmCfg.indentUnit;
509 var wordAfter = wordafter(textAfter);
505 var wordAfter = wordafter(textAfter);
510 var currT = peekToken(state,1);
506 var currT = peekToken(state,1);
511 var prevT = peekToken(state,2);
507 var prevT = peekToken(state,2);
512
508
513 if (state.in_string || state.in_atom) {
509 if (state.in_string || state.in_atom) {
514 return CodeMirror.Pass;
510 return CodeMirror.Pass;
515 }else if (!prevT) {
511 }else if (!prevT) {
516 return 0;
512 return 0;
517 }else if (currT.token == "when") {
513 }else if (currT.token == "when") {
518 return currT.column+unit;
514 return currT.column+unit;
519 }else if (wordAfter === "when" && prevT.type === "function") {
515 }else if (wordAfter === "when" && prevT.type === "function") {
520 return prevT.indent+unit;
516 return prevT.indent+unit;
521 }else if (wordAfter === "(" && currT.token === "fun") {
517 }else if (wordAfter === "(" && currT.token === "fun") {
522 return currT.column+3;
518 return currT.column+3;
523 }else if (wordAfter === "catch" && (t = getToken(state,["try"]))) {
519 }else if (wordAfter === "catch" && (t = getToken(state,["try"]))) {
524 return t.column;
520 return t.column;
525 }else if (is_member(wordAfter,["end","after","of"])) {
521 }else if (is_member(wordAfter,["end","after","of"])) {
526 t = getToken(state,["begin","case","fun","if","receive","try"]);
522 t = getToken(state,["begin","case","fun","if","receive","try"]);
527 return t ? t.column : CodeMirror.Pass;
523 return t ? t.column : CodeMirror.Pass;
528 }else if (is_member(wordAfter,closeParenWords)) {
524 }else if (is_member(wordAfter,closeParenWords)) {
529 t = getToken(state,openParenWords);
525 t = getToken(state,openParenWords);
530 return t ? t.column : CodeMirror.Pass;
526 return t ? t.column : CodeMirror.Pass;
531 }else if (is_member(currT.token,[",","|","||"]) ||
527 }else if (is_member(currT.token,[",","|","||"]) ||
532 is_member(wordAfter,[",","|","||"])) {
528 is_member(wordAfter,[",","|","||"])) {
533 t = postcommaToken(state);
529 t = postcommaToken(state);
534 return t ? t.column+t.token.length : unit;
530 return t ? t.column+t.token.length : unit;
535 }else if (currT.token == "->") {
531 }else if (currT.token == "->") {
536 if (is_member(prevT.token, ["receive","case","if","try"])) {
532 if (is_member(prevT.token, ["receive","case","if","try"])) {
537 return prevT.column+unit+unit;
533 return prevT.column+unit+unit;
538 }else{
534 }else{
539 return prevT.column+unit;
535 return prevT.column+unit;
540 }
536 }
541 }else if (is_member(currT.token,openParenWords)) {
537 }else if (is_member(currT.token,openParenWords)) {
542 return currT.column+currT.token.length;
538 return currT.column+currT.token.length;
543 }else{
539 }else{
544 t = defaultToken(state);
540 t = defaultToken(state);
545 return truthy(t) ? t.column+unit : 0;
541 return truthy(t) ? t.column+unit : 0;
546 }
542 }
547 }
543 }
548
544
549 function wordafter(str) {
545 function wordafter(str) {
550 var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/);
546 var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/);
551
547
552 return truthy(m) && (m.index === 0) ? m[0] : "";
548 return truthy(m) && (m.index === 0) ? m[0] : "";
553 }
549 }
554
550
555 function postcommaToken(state) {
551 function postcommaToken(state) {
556 var objs = state.tokenStack.slice(0,-1);
552 var objs = state.tokenStack.slice(0,-1);
557 var i = getTokenIndex(objs,"type",["open_paren"]);
553 var i = getTokenIndex(objs,"type",["open_paren"]);
558
554
559 return truthy(objs[i]) ? objs[i] : false;
555 return truthy(objs[i]) ? objs[i] : false;
560 }
556 }
561
557
562 function defaultToken(state) {
558 function defaultToken(state) {
563 var objs = state.tokenStack;
559 var objs = state.tokenStack;
564 var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]);
560 var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]);
565 var oper = getTokenIndex(objs,"type",["operator"]);
561 var oper = getTokenIndex(objs,"type",["operator"]);
566
562
567 if (truthy(stop) && truthy(oper) && stop < oper) {
563 if (truthy(stop) && truthy(oper) && stop < oper) {
568 return objs[stop+1];
564 return objs[stop+1];
569 } else if (truthy(stop)) {
565 } else if (truthy(stop)) {
570 return objs[stop];
566 return objs[stop];
571 } else {
567 } else {
572 return false;
568 return false;
573 }
569 }
574 }
570 }
575
571
576 function getToken(state,tokens) {
572 function getToken(state,tokens) {
577 var objs = state.tokenStack;
573 var objs = state.tokenStack;
578 var i = getTokenIndex(objs,"token",tokens);
574 var i = getTokenIndex(objs,"token",tokens);
579
575
580 return truthy(objs[i]) ? objs[i] : false;
576 return truthy(objs[i]) ? objs[i] : false;
581 }
577 }
582
578
583 function getTokenIndex(objs,propname,propvals) {
579 function getTokenIndex(objs,propname,propvals) {
584
580
585 for (var i = objs.length-1; -1 < i ; i--) {
581 for (var i = objs.length-1; -1 < i ; i--) {
586 if (is_member(objs[i][propname],propvals)) {
582 if (is_member(objs[i][propname],propvals)) {
587 return i;
583 return i;
588 }
584 }
589 }
585 }
590 return false;
586 return false;
591 }
587 }
592
588
593 function truthy(x) {
589 function truthy(x) {
594 return (x !== false) && (x != null);
590 return (x !== false) && (x != null);
595 }
591 }
596
592
597 /////////////////////////////////////////////////////////////////////////////
593 /////////////////////////////////////////////////////////////////////////////
598 // this object defines the mode
594 // this object defines the mode
599
595
600 return {
596 return {
601 startState:
597 startState:
602 function() {
598 function() {
603 return {tokenStack: [],
599 return {tokenStack: [],
604 in_string: false,
600 in_string: false,
605 in_atom: false};
601 in_atom: false};
606 },
602 },
607
603
608 token:
604 token:
609 function(stream, state) {
605 function(stream, state) {
610 return tokenizer(stream, state);
606 return tokenizer(stream, state);
611 },
607 },
612
608
613 indent:
609 indent:
614 function(state, textAfter) {
610 function(state, textAfter) {
615 return indenter(state,textAfter);
611 return indenter(state,textAfter);
616 },
612 },
617
613
618 lineComment: "%"
614 lineComment: "%"
619 };
615 };
620 });
616 });
621
617
622 });
618 });
@@ -1,123 +1,130 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
6 mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
8 define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
15
14 CodeMirror.defineMode("gfm", function(config, modeConfig) {
16 CodeMirror.defineMode("gfm", function(config, modeConfig) {
15 var codeDepth = 0;
17 var codeDepth = 0;
16 function blankLine(state) {
18 function blankLine(state) {
17 state.code = false;
19 state.code = false;
18 return null;
20 return null;
19 }
21 }
20 var gfmOverlay = {
22 var gfmOverlay = {
21 startState: function() {
23 startState: function() {
22 return {
24 return {
23 code: false,
25 code: false,
24 codeBlock: false,
26 codeBlock: false,
25 ateSpace: false
27 ateSpace: false
26 };
28 };
27 },
29 },
28 copyState: function(s) {
30 copyState: function(s) {
29 return {
31 return {
30 code: s.code,
32 code: s.code,
31 codeBlock: s.codeBlock,
33 codeBlock: s.codeBlock,
32 ateSpace: s.ateSpace
34 ateSpace: s.ateSpace
33 };
35 };
34 },
36 },
35 token: function(stream, state) {
37 token: function(stream, state) {
36 state.combineTokens = null;
38 state.combineTokens = null;
37
39
38 // Hack to prevent formatting override inside code blocks (block and inline)
40 // Hack to prevent formatting override inside code blocks (block and inline)
39 if (state.codeBlock) {
41 if (state.codeBlock) {
40 if (stream.match(/^```/)) {
42 if (stream.match(/^```+/)) {
41 state.codeBlock = false;
43 state.codeBlock = false;
42 return null;
44 return null;
43 }
45 }
44 stream.skipToEnd();
46 stream.skipToEnd();
45 return null;
47 return null;
46 }
48 }
47 if (stream.sol()) {
49 if (stream.sol()) {
48 state.code = false;
50 state.code = false;
49 }
51 }
50 if (stream.sol() && stream.match(/^```/)) {
52 if (stream.sol() && stream.match(/^```+/)) {
51 stream.skipToEnd();
53 stream.skipToEnd();
52 state.codeBlock = true;
54 state.codeBlock = true;
53 return null;
55 return null;
54 }
56 }
55 // If this block is changed, it may need to be updated in Markdown mode
57 // If this block is changed, it may need to be updated in Markdown mode
56 if (stream.peek() === '`') {
58 if (stream.peek() === '`') {
57 stream.next();
59 stream.next();
58 var before = stream.pos;
60 var before = stream.pos;
59 stream.eatWhile('`');
61 stream.eatWhile('`');
60 var difference = 1 + stream.pos - before;
62 var difference = 1 + stream.pos - before;
61 if (!state.code) {
63 if (!state.code) {
62 codeDepth = difference;
64 codeDepth = difference;
63 state.code = true;
65 state.code = true;
64 } else {
66 } else {
65 if (difference === codeDepth) { // Must be exact
67 if (difference === codeDepth) { // Must be exact
66 state.code = false;
68 state.code = false;
67 }
69 }
68 }
70 }
69 return null;
71 return null;
70 } else if (state.code) {
72 } else if (state.code) {
71 stream.next();
73 stream.next();
72 return null;
74 return null;
73 }
75 }
74 // Check if space. If so, links can be formatted later on
76 // Check if space. If so, links can be formatted later on
75 if (stream.eatSpace()) {
77 if (stream.eatSpace()) {
76 state.ateSpace = true;
78 state.ateSpace = true;
77 return null;
79 return null;
78 }
80 }
79 if (stream.sol() || state.ateSpace) {
81 if (stream.sol() || state.ateSpace) {
80 state.ateSpace = false;
82 state.ateSpace = false;
81 if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
83 if (modeConfig.gitHubSpice !== false) {
82 // User/Project@SHA
84 if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
83 // User@SHA
85 // User/Project@SHA
84 // SHA
86 // User@SHA
85 state.combineTokens = true;
87 // SHA
86 return "link";
88 state.combineTokens = true;
87 } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
89 return "link";
88 // User/Project#Num
90 } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
89 // User#Num
91 // User/Project#Num
90 // #Num
92 // User#Num
91 state.combineTokens = true;
93 // #Num
92 return "link";
94 state.combineTokens = true;
95 return "link";
96 }
93 }
97 }
94 }
98 }
95 if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i) &&
99 if (stream.match(urlRE) &&
96 stream.string.slice(stream.start - 2, stream.start) != "](") {
100 stream.string.slice(stream.start - 2, stream.start) != "](" &&
101 (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
97 // URLs
102 // URLs
98 // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
103 // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
99 // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
104 // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
105 // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
100 state.combineTokens = true;
106 state.combineTokens = true;
101 return "link";
107 return "link";
102 }
108 }
103 stream.next();
109 stream.next();
104 return null;
110 return null;
105 },
111 },
106 blankLine: blankLine
112 blankLine: blankLine
107 };
113 };
108
114
109 var markdownConfig = {
115 var markdownConfig = {
110 underscoresBreakWords: false,
116 underscoresBreakWords: false,
111 taskLists: true,
117 taskLists: true,
112 fencedCodeBlocks: true,
118 fencedCodeBlocks: '```',
113 strikethrough: true
119 strikethrough: true
114 };
120 };
115 for (var attr in modeConfig) {
121 for (var attr in modeConfig) {
116 markdownConfig[attr] = modeConfig[attr];
122 markdownConfig[attr] = modeConfig[attr];
117 }
123 }
118 markdownConfig.name = "markdown";
124 markdownConfig.name = "markdown";
119 CodeMirror.defineMIME("gfmBase", markdownConfig);
125 return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
120 return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay);
126
121 }, "markdown");
127 }, "markdown");
122
128
129 CodeMirror.defineMIME("text/x-gfm", "gfm");
123 });
130 });
@@ -1,185 +1,185 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("go", function(config) {
14 CodeMirror.defineMode("go", function(config) {
15 var indentUnit = config.indentUnit;
15 var indentUnit = config.indentUnit;
16
16
17 var keywords = {
17 var keywords = {
18 "break":true, "case":true, "chan":true, "const":true, "continue":true,
18 "break":true, "case":true, "chan":true, "const":true, "continue":true,
19 "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
19 "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
20 "func":true, "go":true, "goto":true, "if":true, "import":true,
20 "func":true, "go":true, "goto":true, "if":true, "import":true,
21 "interface":true, "map":true, "package":true, "range":true, "return":true,
21 "interface":true, "map":true, "package":true, "range":true, "return":true,
22 "select":true, "struct":true, "switch":true, "type":true, "var":true,
22 "select":true, "struct":true, "switch":true, "type":true, "var":true,
23 "bool":true, "byte":true, "complex64":true, "complex128":true,
23 "bool":true, "byte":true, "complex64":true, "complex128":true,
24 "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
24 "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
25 "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
25 "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
26 "uint64":true, "int":true, "uint":true, "uintptr":true
26 "uint64":true, "int":true, "uint":true, "uintptr":true
27 };
27 };
28
28
29 var atoms = {
29 var atoms = {
30 "true":true, "false":true, "iota":true, "nil":true, "append":true,
30 "true":true, "false":true, "iota":true, "nil":true, "append":true,
31 "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
31 "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
32 "len":true, "make":true, "new":true, "panic":true, "print":true,
32 "len":true, "make":true, "new":true, "panic":true, "print":true,
33 "println":true, "real":true, "recover":true
33 "println":true, "real":true, "recover":true
34 };
34 };
35
35
36 var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
36 var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
37
37
38 var curPunc;
38 var curPunc;
39
39
40 function tokenBase(stream, state) {
40 function tokenBase(stream, state) {
41 var ch = stream.next();
41 var ch = stream.next();
42 if (ch == '"' || ch == "'" || ch == "`") {
42 if (ch == '"' || ch == "'" || ch == "`") {
43 state.tokenize = tokenString(ch);
43 state.tokenize = tokenString(ch);
44 return state.tokenize(stream, state);
44 return state.tokenize(stream, state);
45 }
45 }
46 if (/[\d\.]/.test(ch)) {
46 if (/[\d\.]/.test(ch)) {
47 if (ch == ".") {
47 if (ch == ".") {
48 stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
48 stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
49 } else if (ch == "0") {
49 } else if (ch == "0") {
50 stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
50 stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
51 } else {
51 } else {
52 stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
52 stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
53 }
53 }
54 return "number";
54 return "number";
55 }
55 }
56 if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
56 if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
57 curPunc = ch;
57 curPunc = ch;
58 return null;
58 return null;
59 }
59 }
60 if (ch == "/") {
60 if (ch == "/") {
61 if (stream.eat("*")) {
61 if (stream.eat("*")) {
62 state.tokenize = tokenComment;
62 state.tokenize = tokenComment;
63 return tokenComment(stream, state);
63 return tokenComment(stream, state);
64 }
64 }
65 if (stream.eat("/")) {
65 if (stream.eat("/")) {
66 stream.skipToEnd();
66 stream.skipToEnd();
67 return "comment";
67 return "comment";
68 }
68 }
69 }
69 }
70 if (isOperatorChar.test(ch)) {
70 if (isOperatorChar.test(ch)) {
71 stream.eatWhile(isOperatorChar);
71 stream.eatWhile(isOperatorChar);
72 return "operator";
72 return "operator";
73 }
73 }
74 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
74 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
75 var cur = stream.current();
75 var cur = stream.current();
76 if (keywords.propertyIsEnumerable(cur)) {
76 if (keywords.propertyIsEnumerable(cur)) {
77 if (cur == "case" || cur == "default") curPunc = "case";
77 if (cur == "case" || cur == "default") curPunc = "case";
78 return "keyword";
78 return "keyword";
79 }
79 }
80 if (atoms.propertyIsEnumerable(cur)) return "atom";
80 if (atoms.propertyIsEnumerable(cur)) return "atom";
81 return "variable";
81 return "variable";
82 }
82 }
83
83
84 function tokenString(quote) {
84 function tokenString(quote) {
85 return function(stream, state) {
85 return function(stream, state) {
86 var escaped = false, next, end = false;
86 var escaped = false, next, end = false;
87 while ((next = stream.next()) != null) {
87 while ((next = stream.next()) != null) {
88 if (next == quote && !escaped) {end = true; break;}
88 if (next == quote && !escaped) {end = true; break;}
89 escaped = !escaped && next == "\\";
89 escaped = !escaped && quote != "`" && next == "\\";
90 }
90 }
91 if (end || !(escaped || quote == "`"))
91 if (end || !(escaped || quote == "`"))
92 state.tokenize = tokenBase;
92 state.tokenize = tokenBase;
93 return "string";
93 return "string";
94 };
94 };
95 }
95 }
96
96
97 function tokenComment(stream, state) {
97 function tokenComment(stream, state) {
98 var maybeEnd = false, ch;
98 var maybeEnd = false, ch;
99 while (ch = stream.next()) {
99 while (ch = stream.next()) {
100 if (ch == "/" && maybeEnd) {
100 if (ch == "/" && maybeEnd) {
101 state.tokenize = tokenBase;
101 state.tokenize = tokenBase;
102 break;
102 break;
103 }
103 }
104 maybeEnd = (ch == "*");
104 maybeEnd = (ch == "*");
105 }
105 }
106 return "comment";
106 return "comment";
107 }
107 }
108
108
109 function Context(indented, column, type, align, prev) {
109 function Context(indented, column, type, align, prev) {
110 this.indented = indented;
110 this.indented = indented;
111 this.column = column;
111 this.column = column;
112 this.type = type;
112 this.type = type;
113 this.align = align;
113 this.align = align;
114 this.prev = prev;
114 this.prev = prev;
115 }
115 }
116 function pushContext(state, col, type) {
116 function pushContext(state, col, type) {
117 return state.context = new Context(state.indented, col, type, null, state.context);
117 return state.context = new Context(state.indented, col, type, null, state.context);
118 }
118 }
119 function popContext(state) {
119 function popContext(state) {
120 if (!state.context.prev) return;
120 if (!state.context.prev) return;
121 var t = state.context.type;
121 var t = state.context.type;
122 if (t == ")" || t == "]" || t == "}")
122 if (t == ")" || t == "]" || t == "}")
123 state.indented = state.context.indented;
123 state.indented = state.context.indented;
124 return state.context = state.context.prev;
124 return state.context = state.context.prev;
125 }
125 }
126
126
127 // Interface
127 // Interface
128
128
129 return {
129 return {
130 startState: function(basecolumn) {
130 startState: function(basecolumn) {
131 return {
131 return {
132 tokenize: null,
132 tokenize: null,
133 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
133 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
134 indented: 0,
134 indented: 0,
135 startOfLine: true
135 startOfLine: true
136 };
136 };
137 },
137 },
138
138
139 token: function(stream, state) {
139 token: function(stream, state) {
140 var ctx = state.context;
140 var ctx = state.context;
141 if (stream.sol()) {
141 if (stream.sol()) {
142 if (ctx.align == null) ctx.align = false;
142 if (ctx.align == null) ctx.align = false;
143 state.indented = stream.indentation();
143 state.indented = stream.indentation();
144 state.startOfLine = true;
144 state.startOfLine = true;
145 if (ctx.type == "case") ctx.type = "}";
145 if (ctx.type == "case") ctx.type = "}";
146 }
146 }
147 if (stream.eatSpace()) return null;
147 if (stream.eatSpace()) return null;
148 curPunc = null;
148 curPunc = null;
149 var style = (state.tokenize || tokenBase)(stream, state);
149 var style = (state.tokenize || tokenBase)(stream, state);
150 if (style == "comment") return style;
150 if (style == "comment") return style;
151 if (ctx.align == null) ctx.align = true;
151 if (ctx.align == null) ctx.align = true;
152
152
153 if (curPunc == "{") pushContext(state, stream.column(), "}");
153 if (curPunc == "{") pushContext(state, stream.column(), "}");
154 else if (curPunc == "[") pushContext(state, stream.column(), "]");
154 else if (curPunc == "[") pushContext(state, stream.column(), "]");
155 else if (curPunc == "(") pushContext(state, stream.column(), ")");
155 else if (curPunc == "(") pushContext(state, stream.column(), ")");
156 else if (curPunc == "case") ctx.type = "case";
156 else if (curPunc == "case") ctx.type = "case";
157 else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
157 else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
158 else if (curPunc == ctx.type) popContext(state);
158 else if (curPunc == ctx.type) popContext(state);
159 state.startOfLine = false;
159 state.startOfLine = false;
160 return style;
160 return style;
161 },
161 },
162
162
163 indent: function(state, textAfter) {
163 indent: function(state, textAfter) {
164 if (state.tokenize != tokenBase && state.tokenize != null) return 0;
164 if (state.tokenize != tokenBase && state.tokenize != null) return 0;
165 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
165 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
166 if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
166 if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
167 state.context.type = "}";
167 state.context.type = "}";
168 return ctx.indented;
168 return ctx.indented;
169 }
169 }
170 var closing = firstChar == ctx.type;
170 var closing = firstChar == ctx.type;
171 if (ctx.align) return ctx.column + (closing ? 0 : 1);
171 if (ctx.align) return ctx.column + (closing ? 0 : 1);
172 else return ctx.indented + (closing ? 0 : indentUnit);
172 else return ctx.indented + (closing ? 0 : indentUnit);
173 },
173 },
174
174
175 electricChars: "{}):",
175 electricChars: "{}):",
176 fold: "brace",
176 fold: "brace",
177 blockCommentStart: "/*",
177 blockCommentStart: "/*",
178 blockCommentEnd: "*/",
178 blockCommentEnd: "*/",
179 lineComment: "//"
179 lineComment: "//"
180 };
180 };
181 });
181 });
182
182
183 CodeMirror.defineMIME("text/x-go", "go");
183 CodeMirror.defineMIME("text/x-go", "go");
184
184
185 });
185 });
@@ -1,159 +1,161 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);
8 define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 // full haml mode. This handled embeded ruby and html fragments too
14 // full haml mode. This handled embeded ruby and html fragments too
15 CodeMirror.defineMode("haml", function(config) {
15 CodeMirror.defineMode("haml", function(config) {
16 var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
16 var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
17 var rubyMode = CodeMirror.getMode(config, "ruby");
17 var rubyMode = CodeMirror.getMode(config, "ruby");
18
18
19 function rubyInQuote(endQuote) {
19 function rubyInQuote(endQuote) {
20 return function(stream, state) {
20 return function(stream, state) {
21 var ch = stream.peek();
21 var ch = stream.peek();
22 if (ch == endQuote && state.rubyState.tokenize.length == 1) {
22 if (ch == endQuote && state.rubyState.tokenize.length == 1) {
23 // step out of ruby context as it seems to complete processing all the braces
23 // step out of ruby context as it seems to complete processing all the braces
24 stream.next();
24 stream.next();
25 state.tokenize = html;
25 state.tokenize = html;
26 return "closeAttributeTag";
26 return "closeAttributeTag";
27 } else {
27 } else {
28 return ruby(stream, state);
28 return ruby(stream, state);
29 }
29 }
30 };
30 };
31 }
31 }
32
32
33 function ruby(stream, state) {
33 function ruby(stream, state) {
34 if (stream.match("-#")) {
34 if (stream.match("-#")) {
35 stream.skipToEnd();
35 stream.skipToEnd();
36 return "comment";
36 return "comment";
37 }
37 }
38 return rubyMode.token(stream, state.rubyState);
38 return rubyMode.token(stream, state.rubyState);
39 }
39 }
40
40
41 function html(stream, state) {
41 function html(stream, state) {
42 var ch = stream.peek();
42 var ch = stream.peek();
43
43
44 // handle haml declarations. All declarations that cant be handled here
44 // handle haml declarations. All declarations that cant be handled here
45 // will be passed to html mode
45 // will be passed to html mode
46 if (state.previousToken.style == "comment" ) {
46 if (state.previousToken.style == "comment" ) {
47 if (state.indented > state.previousToken.indented) {
47 if (state.indented > state.previousToken.indented) {
48 stream.skipToEnd();
48 stream.skipToEnd();
49 return "commentLine";
49 return "commentLine";
50 }
50 }
51 }
51 }
52
52
53 if (state.startOfLine) {
53 if (state.startOfLine) {
54 if (ch == "!" && stream.match("!!")) {
54 if (ch == "!" && stream.match("!!")) {
55 stream.skipToEnd();
55 stream.skipToEnd();
56 return "tag";
56 return "tag";
57 } else if (stream.match(/^%[\w:#\.]+=/)) {
57 } else if (stream.match(/^%[\w:#\.]+=/)) {
58 state.tokenize = ruby;
58 state.tokenize = ruby;
59 return "hamlTag";
59 return "hamlTag";
60 } else if (stream.match(/^%[\w:]+/)) {
60 } else if (stream.match(/^%[\w:]+/)) {
61 return "hamlTag";
61 return "hamlTag";
62 } else if (ch == "/" ) {
62 } else if (ch == "/" ) {
63 stream.skipToEnd();
63 stream.skipToEnd();
64 return "comment";
64 return "comment";
65 }
65 }
66 }
66 }
67
67
68 if (state.startOfLine || state.previousToken.style == "hamlTag") {
68 if (state.startOfLine || state.previousToken.style == "hamlTag") {
69 if ( ch == "#" || ch == ".") {
69 if ( ch == "#" || ch == ".") {
70 stream.match(/[\w-#\.]*/);
70 stream.match(/[\w-#\.]*/);
71 return "hamlAttribute";
71 return "hamlAttribute";
72 }
72 }
73 }
73 }
74
74
75 // donot handle --> as valid ruby, make it HTML close comment instead
75 // donot handle --> as valid ruby, make it HTML close comment instead
76 if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
76 if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
77 state.tokenize = ruby;
77 state.tokenize = ruby;
78 return state.tokenize(stream, state);
78 return state.tokenize(stream, state);
79 }
79 }
80
80
81 if (state.previousToken.style == "hamlTag" ||
81 if (state.previousToken.style == "hamlTag" ||
82 state.previousToken.style == "closeAttributeTag" ||
82 state.previousToken.style == "closeAttributeTag" ||
83 state.previousToken.style == "hamlAttribute") {
83 state.previousToken.style == "hamlAttribute") {
84 if (ch == "(") {
84 if (ch == "(") {
85 state.tokenize = rubyInQuote(")");
85 state.tokenize = rubyInQuote(")");
86 return state.tokenize(stream, state);
86 return state.tokenize(stream, state);
87 } else if (ch == "{") {
87 } else if (ch == "{") {
88 state.tokenize = rubyInQuote("}");
88 if (!stream.match(/^\{%.*/)) {
89 return state.tokenize(stream, state);
89 state.tokenize = rubyInQuote("}");
90 return state.tokenize(stream, state);
91 }
90 }
92 }
91 }
93 }
92
94
93 return htmlMode.token(stream, state.htmlState);
95 return htmlMode.token(stream, state.htmlState);
94 }
96 }
95
97
96 return {
98 return {
97 // default to html mode
99 // default to html mode
98 startState: function() {
100 startState: function() {
99 var htmlState = htmlMode.startState();
101 var htmlState = htmlMode.startState();
100 var rubyState = rubyMode.startState();
102 var rubyState = rubyMode.startState();
101 return {
103 return {
102 htmlState: htmlState,
104 htmlState: htmlState,
103 rubyState: rubyState,
105 rubyState: rubyState,
104 indented: 0,
106 indented: 0,
105 previousToken: { style: null, indented: 0},
107 previousToken: { style: null, indented: 0},
106 tokenize: html
108 tokenize: html
107 };
109 };
108 },
110 },
109
111
110 copyState: function(state) {
112 copyState: function(state) {
111 return {
113 return {
112 htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
114 htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
113 rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
115 rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
114 indented: state.indented,
116 indented: state.indented,
115 previousToken: state.previousToken,
117 previousToken: state.previousToken,
116 tokenize: state.tokenize
118 tokenize: state.tokenize
117 };
119 };
118 },
120 },
119
121
120 token: function(stream, state) {
122 token: function(stream, state) {
121 if (stream.sol()) {
123 if (stream.sol()) {
122 state.indented = stream.indentation();
124 state.indented = stream.indentation();
123 state.startOfLine = true;
125 state.startOfLine = true;
124 }
126 }
125 if (stream.eatSpace()) return null;
127 if (stream.eatSpace()) return null;
126 var style = state.tokenize(stream, state);
128 var style = state.tokenize(stream, state);
127 state.startOfLine = false;
129 state.startOfLine = false;
128 // dont record comment line as we only want to measure comment line with
130 // dont record comment line as we only want to measure comment line with
129 // the opening comment block
131 // the opening comment block
130 if (style && style != "commentLine") {
132 if (style && style != "commentLine") {
131 state.previousToken = { style: style, indented: state.indented };
133 state.previousToken = { style: style, indented: state.indented };
132 }
134 }
133 // if current state is ruby and the previous token is not `,` reset the
135 // if current state is ruby and the previous token is not `,` reset the
134 // tokenize to html
136 // tokenize to html
135 if (stream.eol() && state.tokenize == ruby) {
137 if (stream.eol() && state.tokenize == ruby) {
136 stream.backUp(1);
138 stream.backUp(1);
137 var ch = stream.peek();
139 var ch = stream.peek();
138 stream.next();
140 stream.next();
139 if (ch && ch != ",") {
141 if (ch && ch != ",") {
140 state.tokenize = html;
142 state.tokenize = html;
141 }
143 }
142 }
144 }
143 // reprocess some of the specific style tag when finish setting previousToken
145 // reprocess some of the specific style tag when finish setting previousToken
144 if (style == "hamlTag") {
146 if (style == "hamlTag") {
145 style = "tag";
147 style = "tag";
146 } else if (style == "commentLine") {
148 } else if (style == "commentLine") {
147 style = "comment";
149 style = "comment";
148 } else if (style == "hamlAttribute") {
150 } else if (style == "hamlAttribute") {
149 style = "attribute";
151 style = "attribute";
150 } else if (style == "closeAttributeTag") {
152 } else if (style == "closeAttributeTag") {
151 style = null;
153 style = null;
152 }
154 }
153 return style;
155 return style;
154 }
156 }
155 };
157 };
156 }, "htmlmixed", "ruby");
158 }, "htmlmixed", "ruby");
157
159
158 CodeMirror.defineMIME("text/x-haml", "haml");
160 CodeMirror.defineMIME("text/x-haml", "haml");
159 });
161 });
@@ -1,53 +1,62 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
6 mod(require("../../lib/codemirror"), require("../../addon/mode/simple"), require("../../addon/mode/multiplex"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
8 define(["../../lib/codemirror", "../../addon/mode/simple", "../../addon/mode/multiplex"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineSimpleMode("handlebars", {
14 CodeMirror.defineSimpleMode("handlebars-tags", {
15 start: [
15 start: [
16 { regex: /\{\{!--/, push: "dash_comment", token: "comment" },
16 { regex: /\{\{!--/, push: "dash_comment", token: "comment" },
17 { regex: /\{\{!/, push: "comment", token: "comment" },
17 { regex: /\{\{!/, push: "comment", token: "comment" },
18 { regex: /\{\{/, push: "handlebars", token: "tag" }
18 { regex: /\{\{/, push: "handlebars", token: "tag" }
19 ],
19 ],
20 handlebars: [
20 handlebars: [
21 { regex: /\}\}/, pop: true, token: "tag" },
21 { regex: /\}\}/, pop: true, token: "tag" },
22
22
23 // Double and single quotes
23 // Double and single quotes
24 { regex: /"(?:[^\\]|\\.)*?"/, token: "string" },
24 { regex: /"(?:[^\\"]|\\.)*"?/, token: "string" },
25 { regex: /'(?:[^\\]|\\.)*?'/, token: "string" },
25 { regex: /'(?:[^\\']|\\.)*'?/, token: "string" },
26
26
27 // Handlebars keywords
27 // Handlebars keywords
28 { regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" },
28 { regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" },
29 { regex: /(?:else|this)\b/, token: "keyword" },
29 { regex: /(?:else|this)\b/, token: "keyword" },
30
30
31 // Numeral
31 // Numeral
32 { regex: /\d+/i, token: "number" },
32 { regex: /\d+/i, token: "number" },
33
33
34 // Atoms like = and .
34 // Atoms like = and .
35 { regex: /=|~|@|true|false/, token: "atom" },
35 { regex: /=|~|@|true|false/, token: "atom" },
36
36
37 // Paths
37 // Paths
38 { regex: /(?:\.\.\/)*(?:[A-Za-z_][\w\.]*)+/, token: "variable-2" }
38 { regex: /(?:\.\.\/)*(?:[A-Za-z_][\w\.]*)+/, token: "variable-2" }
39 ],
39 ],
40 dash_comment: [
40 dash_comment: [
41 { regex: /--\}\}/, pop: true, token: "comment" },
41 { regex: /--\}\}/, pop: true, token: "comment" },
42
42
43 // Commented code
43 // Commented code
44 { regex: /./, token: "comment"}
44 { regex: /./, token: "comment"}
45 ],
45 ],
46 comment: [
46 comment: [
47 { regex: /\}\}/, pop: true, token: "comment" },
47 { regex: /\}\}/, pop: true, token: "comment" },
48 { regex: /./, token: "comment" }
48 { regex: /./, token: "comment" }
49 ]
49 ]
50 });
50 });
51
51
52 CodeMirror.defineMode("handlebars", function(config, parserConfig) {
53 var handlebars = CodeMirror.getMode(config, "handlebars-tags");
54 if (!parserConfig || !parserConfig.base) return handlebars;
55 return CodeMirror.multiplexingMode(
56 CodeMirror.getMode(config, parserConfig.base),
57 {open: "{{", close: "}}", mode: handlebars, parseDelimiters: true}
58 );
59 });
60
52 CodeMirror.defineMIME("text/x-handlebars-template", "handlebars");
61 CodeMirror.defineMIME("text/x-handlebars-template", "handlebars");
53 });
62 });
@@ -1,518 +1,515 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("haxe", function(config, parserConfig) {
14 CodeMirror.defineMode("haxe", function(config, parserConfig) {
15 var indentUnit = config.indentUnit;
15 var indentUnit = config.indentUnit;
16
16
17 // Tokenizer
17 // Tokenizer
18
18
19 var keywords = function(){
19 function kw(type) {return {type: type, style: "keyword"};}
20 function kw(type) {return {type: type, style: "keyword"};}
20 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
21 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
21 var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"};
22 var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"};
23 var type = kw("typedef");
22 var type = kw("typedef");
24 return {
23 var keywords = {
25 "if": A, "while": A, "else": B, "do": B, "try": B,
24 "if": A, "while": A, "else": B, "do": B, "try": B,
26 "return": C, "break": C, "continue": C, "new": C, "throw": C,
25 "return": C, "break": C, "continue": C, "new": C, "throw": C,
27 "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"),
26 "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"),
28 "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"),
27 "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"),
29 "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"),
28 "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"),
30 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
29 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
31 "in": operator, "never": kw("property_access"), "trace":kw("trace"),
30 "in": operator, "never": kw("property_access"), "trace":kw("trace"),
32 "class": type, "abstract":type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
31 "class": type, "abstract":type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
33 "true": atom, "false": atom, "null": atom
32 "true": atom, "false": atom, "null": atom
34 };
33 };
35 }();
36
34
37 var isOperatorChar = /[+\-*&%=<>!?|]/;
35 var isOperatorChar = /[+\-*&%=<>!?|]/;
38
36
39 function chain(stream, state, f) {
37 function chain(stream, state, f) {
40 state.tokenize = f;
38 state.tokenize = f;
41 return f(stream, state);
39 return f(stream, state);
42 }
40 }
43
41
44 function nextUntilUnescaped(stream, end) {
42 function toUnescaped(stream, end) {
45 var escaped = false, next;
43 var escaped = false, next;
46 while ((next = stream.next()) != null) {
44 while ((next = stream.next()) != null) {
47 if (next == end && !escaped)
45 if (next == end && !escaped)
48 return false;
46 return true;
49 escaped = !escaped && next == "\\";
47 escaped = !escaped && next == "\\";
50 }
48 }
51 return escaped;
52 }
49 }
53
50
54 // Used as scratch variables to communicate multiple values without
51 // Used as scratch variables to communicate multiple values without
55 // consing up tons of objects.
52 // consing up tons of objects.
56 var type, content;
53 var type, content;
57 function ret(tp, style, cont) {
54 function ret(tp, style, cont) {
58 type = tp; content = cont;
55 type = tp; content = cont;
59 return style;
56 return style;
60 }
57 }
61
58
62 function haxeTokenBase(stream, state) {
59 function haxeTokenBase(stream, state) {
63 var ch = stream.next();
60 var ch = stream.next();
64 if (ch == '"' || ch == "'")
61 if (ch == '"' || ch == "'") {
65 return chain(stream, state, haxeTokenString(ch));
62 return chain(stream, state, haxeTokenString(ch));
66 else if (/[\[\]{}\(\),;\:\.]/.test(ch))
63 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
67 return ret(ch);
64 return ret(ch);
68 else if (ch == "0" && stream.eat(/x/i)) {
65 } else if (ch == "0" && stream.eat(/x/i)) {
69 stream.eatWhile(/[\da-f]/i);
66 stream.eatWhile(/[\da-f]/i);
70 return ret("number", "number");
67 return ret("number", "number");
71 }
68 } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
72 else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
69 stream.match(/^\d*(?:\.\d*(?!\.))?(?:[eE][+\-]?\d+)?/);
73 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
74 return ret("number", "number");
70 return ret("number", "number");
75 }
71 } else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) {
76 else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) {
72 toUnescaped(stream, "/");
77 nextUntilUnescaped(stream, "/");
78 stream.eatWhile(/[gimsu]/);
73 stream.eatWhile(/[gimsu]/);
79 return ret("regexp", "string-2");
74 return ret("regexp", "string-2");
80 }
75 } else if (ch == "/") {
81 else if (ch == "/") {
82 if (stream.eat("*")) {
76 if (stream.eat("*")) {
83 return chain(stream, state, haxeTokenComment);
77 return chain(stream, state, haxeTokenComment);
84 }
78 } else if (stream.eat("/")) {
85 else if (stream.eat("/")) {
86 stream.skipToEnd();
79 stream.skipToEnd();
87 return ret("comment", "comment");
80 return ret("comment", "comment");
88 }
81 } else {
89 else {
90 stream.eatWhile(isOperatorChar);
82 stream.eatWhile(isOperatorChar);
91 return ret("operator", null, stream.current());
83 return ret("operator", null, stream.current());
92 }
84 }
93 }
85 } else if (ch == "#") {
94 else if (ch == "#") {
95 stream.skipToEnd();
86 stream.skipToEnd();
96 return ret("conditional", "meta");
87 return ret("conditional", "meta");
97 }
88 } else if (ch == "@") {
98 else if (ch == "@") {
99 stream.eat(/:/);
89 stream.eat(/:/);
100 stream.eatWhile(/[\w_]/);
90 stream.eatWhile(/[\w_]/);
101 return ret ("metadata", "meta");
91 return ret ("metadata", "meta");
102 }
92 } else if (isOperatorChar.test(ch)) {
103 else if (isOperatorChar.test(ch)) {
104 stream.eatWhile(isOperatorChar);
93 stream.eatWhile(isOperatorChar);
105 return ret("operator", null, stream.current());
94 return ret("operator", null, stream.current());
106 }
95 } else {
107 else {
96 var word;
108 var word;
97 if(/[A-Z]/.test(ch)) {
109 if(/[A-Z]/.test(ch))
98 stream.eatWhile(/[\w_<>]/);
110 {
99 word = stream.current();
111 stream.eatWhile(/[\w_<>]/);
100 return ret("type", "variable-3", word);
112 word = stream.current();
101 } else {
113 return ret("type", "variable-3", word);
114 }
115 else
116 {
117 stream.eatWhile(/[\w_]/);
102 stream.eatWhile(/[\w_]/);
118 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
103 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
119 return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
104 return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
120 ret("variable", "variable", word);
105 ret("variable", "variable", word);
121 }
106 }
122 }
107 }
123 }
108 }
124
109
125 function haxeTokenString(quote) {
110 function haxeTokenString(quote) {
126 return function(stream, state) {
111 return function(stream, state) {
127 if (!nextUntilUnescaped(stream, quote))
112 if (toUnescaped(stream, quote))
128 state.tokenize = haxeTokenBase;
113 state.tokenize = haxeTokenBase;
129 return ret("string", "string");
114 return ret("string", "string");
130 };
115 };
131 }
116 }
132
117
133 function haxeTokenComment(stream, state) {
118 function haxeTokenComment(stream, state) {
134 var maybeEnd = false, ch;
119 var maybeEnd = false, ch;
135 while (ch = stream.next()) {
120 while (ch = stream.next()) {
136 if (ch == "/" && maybeEnd) {
121 if (ch == "/" && maybeEnd) {
137 state.tokenize = haxeTokenBase;
122 state.tokenize = haxeTokenBase;
138 break;
123 break;
139 }
124 }
140 maybeEnd = (ch == "*");
125 maybeEnd = (ch == "*");
141 }
126 }
142 return ret("comment", "comment");
127 return ret("comment", "comment");
143 }
128 }
144
129
145 // Parser
130 // Parser
146
131
147 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
132 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
148
133
149 function HaxeLexical(indented, column, type, align, prev, info) {
134 function HaxeLexical(indented, column, type, align, prev, info) {
150 this.indented = indented;
135 this.indented = indented;
151 this.column = column;
136 this.column = column;
152 this.type = type;
137 this.type = type;
153 this.prev = prev;
138 this.prev = prev;
154 this.info = info;
139 this.info = info;
155 if (align != null) this.align = align;
140 if (align != null) this.align = align;
156 }
141 }
157
142
158 function inScope(state, varname) {
143 function inScope(state, varname) {
159 for (var v = state.localVars; v; v = v.next)
144 for (var v = state.localVars; v; v = v.next)
160 if (v.name == varname) return true;
145 if (v.name == varname) return true;
161 }
146 }
162
147
163 function parseHaxe(state, style, type, content, stream) {
148 function parseHaxe(state, style, type, content, stream) {
164 var cc = state.cc;
149 var cc = state.cc;
165 // Communicate our context to the combinators.
150 // Communicate our context to the combinators.
166 // (Less wasteful than consing up a hundred closures on every call.)
151 // (Less wasteful than consing up a hundred closures on every call.)
167 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
152 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
168
153
169 if (!state.lexical.hasOwnProperty("align"))
154 if (!state.lexical.hasOwnProperty("align"))
170 state.lexical.align = true;
155 state.lexical.align = true;
171
156
172 while(true) {
157 while(true) {
173 var combinator = cc.length ? cc.pop() : statement;
158 var combinator = cc.length ? cc.pop() : statement;
174 if (combinator(type, content)) {
159 if (combinator(type, content)) {
175 while(cc.length && cc[cc.length - 1].lex)
160 while(cc.length && cc[cc.length - 1].lex)
176 cc.pop()();
161 cc.pop()();
177 if (cx.marked) return cx.marked;
162 if (cx.marked) return cx.marked;
178 if (type == "variable" && inScope(state, content)) return "variable-2";
163 if (type == "variable" && inScope(state, content)) return "variable-2";
179 if (type == "variable" && imported(state, content)) return "variable-3";
164 if (type == "variable" && imported(state, content)) return "variable-3";
180 return style;
165 return style;
181 }
166 }
182 }
167 }
183 }
168 }
184
169
185 function imported(state, typename)
170 function imported(state, typename) {
186 {
171 if (/[a-z]/.test(typename.charAt(0)))
187 if (/[a-z]/.test(typename.charAt(0)))
172 return false;
188 return false;
173 var len = state.importedtypes.length;
189 var len = state.importedtypes.length;
174 for (var i = 0; i<len; i++)
190 for (var i = 0; i<len; i++)
175 if(state.importedtypes[i]==typename) return true;
191 if(state.importedtypes[i]==typename) return true;
192 }
176 }
193
177
194
195 function registerimport(importname) {
178 function registerimport(importname) {
196 var state = cx.state;
179 var state = cx.state;
197 for (var t = state.importedtypes; t; t = t.next)
180 for (var t = state.importedtypes; t; t = t.next)
198 if(t.name == importname) return;
181 if(t.name == importname) return;
199 state.importedtypes = { name: importname, next: state.importedtypes };
182 state.importedtypes = { name: importname, next: state.importedtypes };
200 }
183 }
201 // Combinator utils
184 // Combinator utils
202
185
203 var cx = {state: null, column: null, marked: null, cc: null};
186 var cx = {state: null, column: null, marked: null, cc: null};
204 function pass() {
187 function pass() {
205 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
188 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
206 }
189 }
207 function cont() {
190 function cont() {
208 pass.apply(null, arguments);
191 pass.apply(null, arguments);
209 return true;
192 return true;
210 }
193 }
194 function inList(name, list) {
195 for (var v = list; v; v = v.next)
196 if (v.name == name) return true;
197 return false;
198 }
211 function register(varname) {
199 function register(varname) {
212 var state = cx.state;
200 var state = cx.state;
213 if (state.context) {
201 if (state.context) {
214 cx.marked = "def";
202 cx.marked = "def";
215 for (var v = state.localVars; v; v = v.next)
203 if (inList(varname, state.localVars)) return;
216 if (v.name == varname) return;
217 state.localVars = {name: varname, next: state.localVars};
204 state.localVars = {name: varname, next: state.localVars};
205 } else if (state.globalVars) {
206 if (inList(varname, state.globalVars)) return;
207 state.globalVars = {name: varname, next: state.globalVars};
218 }
208 }
219 }
209 }
220
210
221 // Combinators
211 // Combinators
222
212
223 var defaultVars = {name: "this", next: null};
213 var defaultVars = {name: "this", next: null};
224 function pushcontext() {
214 function pushcontext() {
225 if (!cx.state.context) cx.state.localVars = defaultVars;
215 if (!cx.state.context) cx.state.localVars = defaultVars;
226 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
216 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
227 }
217 }
228 function popcontext() {
218 function popcontext() {
229 cx.state.localVars = cx.state.context.vars;
219 cx.state.localVars = cx.state.context.vars;
230 cx.state.context = cx.state.context.prev;
220 cx.state.context = cx.state.context.prev;
231 }
221 }
222 popcontext.lex = true;
232 function pushlex(type, info) {
223 function pushlex(type, info) {
233 var result = function() {
224 var result = function() {
234 var state = cx.state;
225 var state = cx.state;
235 state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
226 state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
236 };
227 };
237 result.lex = true;
228 result.lex = true;
238 return result;
229 return result;
239 }
230 }
240 function poplex() {
231 function poplex() {
241 var state = cx.state;
232 var state = cx.state;
242 if (state.lexical.prev) {
233 if (state.lexical.prev) {
243 if (state.lexical.type == ")")
234 if (state.lexical.type == ")")
244 state.indented = state.lexical.indented;
235 state.indented = state.lexical.indented;
245 state.lexical = state.lexical.prev;
236 state.lexical = state.lexical.prev;
246 }
237 }
247 }
238 }
248 poplex.lex = true;
239 poplex.lex = true;
249
240
250 function expect(wanted) {
241 function expect(wanted) {
251 function f(type) {
242 function f(type) {
252 if (type == wanted) return cont();
243 if (type == wanted) return cont();
253 else if (wanted == ";") return pass();
244 else if (wanted == ";") return pass();
254 else return cont(f);
245 else return cont(f);
255 };
246 }
256 return f;
247 return f;
257 }
248 }
258
249
259 function statement(type) {
250 function statement(type) {
260 if (type == "@") return cont(metadef);
251 if (type == "@") return cont(metadef);
261 if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
252 if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
262 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
253 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
263 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
254 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
264 if (type == "{") return cont(pushlex("}"), pushcontext, block, poplex, popcontext);
255 if (type == "{") return cont(pushlex("}"), pushcontext, block, poplex, popcontext);
265 if (type == ";") return cont();
256 if (type == ";") return cont();
266 if (type == "attribute") return cont(maybeattribute);
257 if (type == "attribute") return cont(maybeattribute);
267 if (type == "function") return cont(functiondef);
258 if (type == "function") return cont(functiondef);
268 if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
259 if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
269 poplex, statement, poplex);
260 poplex, statement, poplex);
270 if (type == "variable") return cont(pushlex("stat"), maybelabel);
261 if (type == "variable") return cont(pushlex("stat"), maybelabel);
271 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
262 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
272 block, poplex, poplex);
263 block, poplex, poplex);
273 if (type == "case") return cont(expression, expect(":"));
264 if (type == "case") return cont(expression, expect(":"));
274 if (type == "default") return cont(expect(":"));
265 if (type == "default") return cont(expect(":"));
275 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
266 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
276 statement, poplex, popcontext);
267 statement, poplex, popcontext);
277 if (type == "import") return cont(importdef, expect(";"));
268 if (type == "import") return cont(importdef, expect(";"));
278 if (type == "typedef") return cont(typedef);
269 if (type == "typedef") return cont(typedef);
279 return pass(pushlex("stat"), expression, expect(";"), poplex);
270 return pass(pushlex("stat"), expression, expect(";"), poplex);
280 }
271 }
281 function expression(type) {
272 function expression(type) {
282 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
273 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
274 if (type == "type" ) return cont(maybeoperator);
283 if (type == "function") return cont(functiondef);
275 if (type == "function") return cont(functiondef);
284 if (type == "keyword c") return cont(maybeexpression);
276 if (type == "keyword c") return cont(maybeexpression);
285 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
277 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
286 if (type == "operator") return cont(expression);
278 if (type == "operator") return cont(expression);
287 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
279 if (type == "[") return cont(pushlex("]"), commasep(maybeexpression, "]"), poplex, maybeoperator);
288 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
280 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
289 return cont();
281 return cont();
290 }
282 }
291 function maybeexpression(type) {
283 function maybeexpression(type) {
292 if (type.match(/[;\}\)\],]/)) return pass();
284 if (type.match(/[;\}\)\],]/)) return pass();
293 return pass(expression);
285 return pass(expression);
294 }
286 }
295
287
296 function maybeoperator(type, value) {
288 function maybeoperator(type, value) {
297 if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
289 if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
298 if (type == "operator" || type == ":") return cont(expression);
290 if (type == "operator" || type == ":") return cont(expression);
299 if (type == ";") return;
291 if (type == ";") return;
300 if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
292 if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
301 if (type == ".") return cont(property, maybeoperator);
293 if (type == ".") return cont(property, maybeoperator);
302 if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
294 if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
303 }
295 }
304
296
305 function maybeattribute(type) {
297 function maybeattribute(type) {
306 if (type == "attribute") return cont(maybeattribute);
298 if (type == "attribute") return cont(maybeattribute);
307 if (type == "function") return cont(functiondef);
299 if (type == "function") return cont(functiondef);
308 if (type == "var") return cont(vardef1);
300 if (type == "var") return cont(vardef1);
309 }
301 }
310
302
311 function metadef(type) {
303 function metadef(type) {
312 if(type == ":") return cont(metadef);
304 if(type == ":") return cont(metadef);
313 if(type == "variable") return cont(metadef);
305 if(type == "variable") return cont(metadef);
314 if(type == "(") return cont(pushlex(")"), commasep(metaargs, ")"), poplex, statement);
306 if(type == "(") return cont(pushlex(")"), commasep(metaargs, ")"), poplex, statement);
315 }
307 }
316 function metaargs(type) {
308 function metaargs(type) {
317 if(type == "variable") return cont();
309 if(type == "variable") return cont();
318 }
310 }
319
311
320 function importdef (type, value) {
312 function importdef (type, value) {
321 if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
313 if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
322 else if(type == "variable" || type == "property" || type == "." || value == "*") return cont(importdef);
314 else if(type == "variable" || type == "property" || type == "." || value == "*") return cont(importdef);
323 }
315 }
324
316
325 function typedef (type, value)
317 function typedef (type, value)
326 {
318 {
327 if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
319 if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
328 else if (type == "type" && /[A-Z]/.test(value.charAt(0))) { return cont(); }
320 else if (type == "type" && /[A-Z]/.test(value.charAt(0))) { return cont(); }
329 }
321 }
330
322
331 function maybelabel(type) {
323 function maybelabel(type) {
332 if (type == ":") return cont(poplex, statement);
324 if (type == ":") return cont(poplex, statement);
333 return pass(maybeoperator, expect(";"), poplex);
325 return pass(maybeoperator, expect(";"), poplex);
334 }
326 }
335 function property(type) {
327 function property(type) {
336 if (type == "variable") {cx.marked = "property"; return cont();}
328 if (type == "variable") {cx.marked = "property"; return cont();}
337 }
329 }
338 function objprop(type) {
330 function objprop(type) {
339 if (type == "variable") cx.marked = "property";
331 if (type == "variable") cx.marked = "property";
340 if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
332 if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
341 }
333 }
342 function commasep(what, end) {
334 function commasep(what, end) {
343 function proceed(type) {
335 function proceed(type) {
344 if (type == ",") return cont(what, proceed);
336 if (type == ",") return cont(what, proceed);
345 if (type == end) return cont();
337 if (type == end) return cont();
346 return cont(expect(end));
338 return cont(expect(end));
347 }
339 }
348 return function(type) {
340 return function(type) {
349 if (type == end) return cont();
341 if (type == end) return cont();
350 else return pass(what, proceed);
342 else return pass(what, proceed);
351 };
343 };
352 }
344 }
353 function block(type) {
345 function block(type) {
354 if (type == "}") return cont();
346 if (type == "}") return cont();
355 return pass(statement, block);
347 return pass(statement, block);
356 }
348 }
357 function vardef1(type, value) {
349 function vardef1(type, value) {
358 if (type == "variable"){register(value); return cont(typeuse, vardef2);}
350 if (type == "variable"){register(value); return cont(typeuse, vardef2);}
359 return cont();
351 return cont();
360 }
352 }
361 function vardef2(type, value) {
353 function vardef2(type, value) {
362 if (value == "=") return cont(expression, vardef2);
354 if (value == "=") return cont(expression, vardef2);
363 if (type == ",") return cont(vardef1);
355 if (type == ",") return cont(vardef1);
364 }
356 }
365 function forspec1(type, value) {
357 function forspec1(type, value) {
366 if (type == "variable") {
358 if (type == "variable") {
367 register(value);
359 register(value);
368 }
360 return cont(forin, expression)
369 return cont(pushlex(")"), pushcontext, forin, expression, poplex, statement, popcontext);
361 } else {
362 return pass()
363 }
370 }
364 }
371 function forin(_type, value) {
365 function forin(_type, value) {
372 if (value == "in") return cont();
366 if (value == "in") return cont();
373 }
367 }
374 function functiondef(type, value) {
368 function functiondef(type, value) {
375 if (type == "variable") {register(value); return cont(functiondef);}
369 //function names starting with upper-case letters are recognised as types, so cludging them together here.
370 if (type == "variable" || type == "type") {register(value); return cont(functiondef);}
376 if (value == "new") return cont(functiondef);
371 if (value == "new") return cont(functiondef);
377 if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, typeuse, statement, popcontext);
372 if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, typeuse, statement, popcontext);
378 }
373 }
379 function typeuse(type) {
374 function typeuse(type) {
380 if(type == ":") return cont(typestring);
375 if(type == ":") return cont(typestring);
381 }
376 }
382 function typestring(type) {
377 function typestring(type) {
383 if(type == "type") return cont();
378 if(type == "type") return cont();
384 if(type == "variable") return cont();
379 if(type == "variable") return cont();
385 if(type == "{") return cont(pushlex("}"), commasep(typeprop, "}"), poplex);
380 if(type == "{") return cont(pushlex("}"), commasep(typeprop, "}"), poplex);
386 }
381 }
387 function typeprop(type) {
382 function typeprop(type) {
388 if(type == "variable") return cont(typeuse);
383 if(type == "variable") return cont(typeuse);
389 }
384 }
390 function funarg(type, value) {
385 function funarg(type, value) {
391 if (type == "variable") {register(value); return cont(typeuse);}
386 if (type == "variable") {register(value); return cont(typeuse);}
392 }
387 }
393
388
394 // Interface
389 // Interface
395
396 return {
390 return {
397 startState: function(basecolumn) {
391 startState: function(basecolumn) {
398 var defaulttypes = ["Int", "Float", "String", "Void", "Std", "Bool", "Dynamic", "Array"];
392 var defaulttypes = ["Int", "Float", "String", "Void", "Std", "Bool", "Dynamic", "Array"];
399 return {
393 var state = {
400 tokenize: haxeTokenBase,
394 tokenize: haxeTokenBase,
401 reAllowed: true,
395 reAllowed: true,
402 kwAllowed: true,
396 kwAllowed: true,
403 cc: [],
397 cc: [],
404 lexical: new HaxeLexical((basecolumn || 0) - indentUnit, 0, "block", false),
398 lexical: new HaxeLexical((basecolumn || 0) - indentUnit, 0, "block", false),
405 localVars: parserConfig.localVars,
399 localVars: parserConfig.localVars,
406 importedtypes: defaulttypes,
400 importedtypes: defaulttypes,
407 context: parserConfig.localVars && {vars: parserConfig.localVars},
401 context: parserConfig.localVars && {vars: parserConfig.localVars},
408 indented: 0
402 indented: 0
409 };
403 };
404 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
405 state.globalVars = parserConfig.globalVars;
406 return state;
410 },
407 },
411
408
412 token: function(stream, state) {
409 token: function(stream, state) {
413 if (stream.sol()) {
410 if (stream.sol()) {
414 if (!state.lexical.hasOwnProperty("align"))
411 if (!state.lexical.hasOwnProperty("align"))
415 state.lexical.align = false;
412 state.lexical.align = false;
416 state.indented = stream.indentation();
413 state.indented = stream.indentation();
417 }
414 }
418 if (stream.eatSpace()) return null;
415 if (stream.eatSpace()) return null;
419 var style = state.tokenize(stream, state);
416 var style = state.tokenize(stream, state);
420 if (type == "comment") return style;
417 if (type == "comment") return style;
421 state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
418 state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
422 state.kwAllowed = type != '.';
419 state.kwAllowed = type != '.';
423 return parseHaxe(state, style, type, content, stream);
420 return parseHaxe(state, style, type, content, stream);
424 },
421 },
425
422
426 indent: function(state, textAfter) {
423 indent: function(state, textAfter) {
427 if (state.tokenize != haxeTokenBase) return 0;
424 if (state.tokenize != haxeTokenBase) return 0;
428 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
425 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
429 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
426 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
430 var type = lexical.type, closing = firstChar == type;
427 var type = lexical.type, closing = firstChar == type;
431 if (type == "vardef") return lexical.indented + 4;
428 if (type == "vardef") return lexical.indented + 4;
432 else if (type == "form" && firstChar == "{") return lexical.indented;
429 else if (type == "form" && firstChar == "{") return lexical.indented;
433 else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
430 else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
434 else if (lexical.info == "switch" && !closing)
431 else if (lexical.info == "switch" && !closing)
435 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
432 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
436 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
433 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
437 else return lexical.indented + (closing ? 0 : indentUnit);
434 else return lexical.indented + (closing ? 0 : indentUnit);
438 },
435 },
439
436
440 electricChars: "{}",
437 electricChars: "{}",
441 blockCommentStart: "/*",
438 blockCommentStart: "/*",
442 blockCommentEnd: "*/",
439 blockCommentEnd: "*/",
443 lineComment: "//"
440 lineComment: "//"
444 };
441 };
445 });
442 });
446
443
447 CodeMirror.defineMIME("text/x-haxe", "haxe");
444 CodeMirror.defineMIME("text/x-haxe", "haxe");
448
445
449 CodeMirror.defineMode("hxml", function () {
446 CodeMirror.defineMode("hxml", function () {
450
447
451 return {
448 return {
452 startState: function () {
449 startState: function () {
453 return {
450 return {
454 define: false,
451 define: false,
455 inString: false
452 inString: false
456 };
453 };
457 },
454 },
458 token: function (stream, state) {
455 token: function (stream, state) {
459 var ch = stream.peek();
456 var ch = stream.peek();
460 var sol = stream.sol();
457 var sol = stream.sol();
461
458
462 ///* comments */
459 ///* comments */
463 if (ch == "#") {
460 if (ch == "#") {
464 stream.skipToEnd();
461 stream.skipToEnd();
465 return "comment";
462 return "comment";
466 }
463 }
467 if (sol && ch == "-") {
464 if (sol && ch == "-") {
468 var style = "variable-2";
465 var style = "variable-2";
469
466
470 stream.eat(/-/);
467 stream.eat(/-/);
471
468
472 if (stream.peek() == "-") {
469 if (stream.peek() == "-") {
473 stream.eat(/-/);
470 stream.eat(/-/);
474 style = "keyword a";
471 style = "keyword a";
475 }
472 }
476
473
477 if (stream.peek() == "D") {
474 if (stream.peek() == "D") {
478 stream.eat(/[D]/);
475 stream.eat(/[D]/);
479 style = "keyword c";
476 style = "keyword c";
480 state.define = true;
477 state.define = true;
481 }
478 }
482
479
483 stream.eatWhile(/[A-Z]/i);
480 stream.eatWhile(/[A-Z]/i);
484 return style;
481 return style;
485 }
482 }
486
483
487 var ch = stream.peek();
484 var ch = stream.peek();
488
485
489 if (state.inString == false && ch == "'") {
486 if (state.inString == false && ch == "'") {
490 state.inString = true;
487 state.inString = true;
491 ch = stream.next();
488 ch = stream.next();
492 }
489 }
493
490
494 if (state.inString == true) {
491 if (state.inString == true) {
495 if (stream.skipTo("'")) {
492 if (stream.skipTo("'")) {
496
493
497 } else {
494 } else {
498 stream.skipToEnd();
495 stream.skipToEnd();
499 }
496 }
500
497
501 if (stream.peek() == "'") {
498 if (stream.peek() == "'") {
502 stream.next();
499 stream.next();
503 state.inString = false;
500 state.inString = false;
504 }
501 }
505
502
506 return "string";
503 return "string";
507 }
504 }
508
505
509 stream.next();
506 stream.next();
510 return null;
507 return null;
511 },
508 },
512 lineComment: "#"
509 lineComment: "#"
513 };
510 };
514 });
511 });
515
512
516 CodeMirror.defineMIME("text/x-hxml", "hxml");
513 CodeMirror.defineMIME("text/x-hxml", "hxml");
517
514
518 });
515 });
@@ -1,121 +1,150 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
8 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
14 CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
15 var htmlMode = CodeMirror.getMode(config, {name: "xml",
16 htmlMode: true,
17 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
18 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag});
19 var cssMode = CodeMirror.getMode(config, "css");
20
21 var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes;
22 scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i,
23 mode: CodeMirror.getMode(config, "javascript")});
24 if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) {
25 var conf = scriptTypesConf[i];
26 scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)});
27 }
28 scriptTypes.push({matches: /./,
29 mode: CodeMirror.getMode(config, "text/plain")});
30
13
31 function html(stream, state) {
14 var defaultTags = {
32 var tagName = state.htmlState.tagName;
15 script: [
33 if (tagName) tagName = tagName.toLowerCase();
16 ["lang", /(javascript|babel)/i, "javascript"],
34 var style = htmlMode.token(stream, state.htmlState);
17 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
35 if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
18 ["type", /./, "text/plain"],
36 // Script block: mode to change to depends on type attribute
19 [null, null, "javascript"]
37 var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i);
20 ],
38 scriptType = scriptType ? scriptType[1] : "";
21 style: [
39 if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1);
22 ["lang", /^css$/i, "css"],
40 for (var i = 0; i < scriptTypes.length; ++i) {
23 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
41 var tp = scriptTypes[i];
24 ["type", /./, "text/plain"],
42 if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) {
25 [null, null, "css"]
43 if (tp.mode) {
26 ]
44 state.token = script;
27 };
45 state.localMode = tp.mode;
28
46 state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, ""));
47 }
48 break;
49 }
50 }
51 } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") {
52 state.token = css;
53 state.localMode = cssMode;
54 state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
55 }
56 return style;
57 }
58 function maybeBackup(stream, pat, style) {
29 function maybeBackup(stream, pat, style) {
59 var cur = stream.current();
30 var cur = stream.current(), close = cur.search(pat);
60 var close = cur.search(pat);
31 if (close > -1) {
61 if (close > -1) stream.backUp(cur.length - close);
32 stream.backUp(cur.length - close);
62 else if (cur.match(/<\/?$/)) {
33 } else if (cur.match(/<\/?$/)) {
63 stream.backUp(cur.length);
34 stream.backUp(cur.length);
64 if (!stream.match(pat, false)) stream.match(cur);
35 if (!stream.match(pat, false)) stream.match(cur);
65 }
36 }
66 return style;
37 return style;
67 }
38 }
68 function script(stream, state) {
39
69 if (stream.match(/^<\/\s*script\s*>/i, false)) {
40 var attrRegexpCache = {};
70 state.token = html;
41 function getAttrRegexp(attr) {
71 state.localState = state.localMode = null;
42 var regexp = attrRegexpCache[attr];
72 return null;
43 if (regexp) return regexp;
73 }
44 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
74 return maybeBackup(stream, /<\/\s*script\s*>/,
45 }
75 state.localMode.token(stream, state.localState));
46
47 function getAttrValue(stream, attr) {
48 var pos = stream.pos, match;
49 while (pos >= 0 && stream.string.charAt(pos) !== "<") pos--;
50 if (pos < 0) return pos;
51 if (match = stream.string.slice(pos, stream.pos).match(getAttrRegexp(attr)))
52 return match[2];
53 return "";
76 }
54 }
77 function css(stream, state) {
55
78 if (stream.match(/^<\/\s*style\s*>/i, false)) {
56 function getTagRegexp(tagName, anchored) {
79 state.token = html;
57 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
80 state.localState = state.localMode = null;
58 }
81 return null;
59
60 function addTags(from, to) {
61 for (var tag in from) {
62 var dest = to[tag] || (to[tag] = []);
63 var source = from[tag];
64 for (var i = source.length - 1; i >= 0; i--)
65 dest.unshift(source[i])
82 }
66 }
83 return maybeBackup(stream, /<\/\s*style\s*>/,
67 }
84 cssMode.token(stream, state.localState));
68
69 function findMatchingMode(tagInfo, stream) {
70 for (var i = 0; i < tagInfo.length; i++) {
71 var spec = tagInfo[i];
72 if (!spec[0] || spec[1].test(getAttrValue(stream, spec[0]))) return spec[2];
73 }
85 }
74 }
86
75
87 return {
76 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
88 startState: function() {
77 var htmlMode = CodeMirror.getMode(config, {
89 var state = htmlMode.startState();
78 name: "xml",
90 return {token: html, localMode: null, localState: null, htmlState: state};
79 htmlMode: true,
91 },
80 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
81 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
82 });
92
83
93 copyState: function(state) {
84 var tags = {};
94 if (state.localState)
85 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
95 var local = CodeMirror.copyState(state.localMode, state.localState);
86 addTags(defaultTags, tags);
96 return {token: state.token, localMode: state.localMode, localState: local,
87 if (configTags) addTags(configTags, tags);
97 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
88 if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
98 },
89 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
99
90
100 token: function(stream, state) {
91 function html(stream, state) {
101 return state.token(stream, state);
92 var tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase();
102 },
93 var tagInfo = tagName && tags.hasOwnProperty(tagName) && tags[tagName];
94
95 var style = htmlMode.token(stream, state.htmlState), modeSpec;
103
96
104 indent: function(state, textAfter) {
97 if (tagInfo && /\btag\b/.test(style) && stream.current() === ">" &&
105 if (!state.localMode || /^\s*<\//.test(textAfter))
98 (modeSpec = findMatchingMode(tagInfo, stream))) {
106 return htmlMode.indent(state.htmlState, textAfter);
99 var mode = CodeMirror.getMode(config, modeSpec);
107 else if (state.localMode.indent)
100 var endTagA = getTagRegexp(tagName, true), endTag = getTagRegexp(tagName, false);
108 return state.localMode.indent(state.localState, textAfter);
101 state.token = function (stream, state) {
109 else
102 if (stream.match(endTagA, false)) {
110 return CodeMirror.Pass;
103 state.token = html;
111 },
104 state.localState = state.localMode = null;
105 return null;
106 }
107 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
108 };
109 state.localMode = mode;
110 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
111 }
112 return style;
113 };
114
115 return {
116 startState: function () {
117 var state = htmlMode.startState();
118 return {token: html, localMode: null, localState: null, htmlState: state};
119 },
112
120
113 innerMode: function(state) {
121 copyState: function (state) {
114 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
122 var local;
115 }
123 if (state.localState) {
116 };
124 local = CodeMirror.copyState(state.localMode, state.localState);
117 }, "xml", "javascript", "css");
125 }
126 return {token: state.token, localMode: state.localMode, localState: local,
127 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
128 },
129
130 token: function (stream, state) {
131 return state.token(stream, state);
132 },
118
133
119 CodeMirror.defineMIME("text/html", "htmlmixed");
134 indent: function (state, textAfter) {
135 if (!state.localMode || /^\s*<\//.test(textAfter))
136 return htmlMode.indent(state.htmlState, textAfter);
137 else if (state.localMode.indent)
138 return state.localMode.indent(state.localState, textAfter);
139 else
140 return CodeMirror.Pass;
141 },
120
142
143 innerMode: function (state) {
144 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
145 }
146 };
147 }, "xml", "javascript", "css");
148
149 CodeMirror.defineMIME("text/html", "htmlmixed");
121 });
150 });
@@ -1,590 +1,590 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed"));
6 mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod);
8 define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode('jade', function (config) {
14 CodeMirror.defineMode('jade', function (config) {
15 // token types
15 // token types
16 var KEYWORD = 'keyword';
16 var KEYWORD = 'keyword';
17 var DOCTYPE = 'meta';
17 var DOCTYPE = 'meta';
18 var ID = 'builtin';
18 var ID = 'builtin';
19 var CLASS = 'qualifier';
19 var CLASS = 'qualifier';
20
20
21 var ATTRS_NEST = {
21 var ATTRS_NEST = {
22 '{': '}',
22 '{': '}',
23 '(': ')',
23 '(': ')',
24 '[': ']'
24 '[': ']'
25 };
25 };
26
26
27 var jsMode = CodeMirror.getMode(config, 'javascript');
27 var jsMode = CodeMirror.getMode(config, 'javascript');
28
28
29 function State() {
29 function State() {
30 this.javaScriptLine = false;
30 this.javaScriptLine = false;
31 this.javaScriptLineExcludesColon = false;
31 this.javaScriptLineExcludesColon = false;
32
32
33 this.javaScriptArguments = false;
33 this.javaScriptArguments = false;
34 this.javaScriptArgumentsDepth = 0;
34 this.javaScriptArgumentsDepth = 0;
35
35
36 this.isInterpolating = false;
36 this.isInterpolating = false;
37 this.interpolationNesting = 0;
37 this.interpolationNesting = 0;
38
38
39 this.jsState = jsMode.startState();
39 this.jsState = jsMode.startState();
40
40
41 this.restOfLine = '';
41 this.restOfLine = '';
42
42
43 this.isIncludeFiltered = false;
43 this.isIncludeFiltered = false;
44 this.isEach = false;
44 this.isEach = false;
45
45
46 this.lastTag = '';
46 this.lastTag = '';
47 this.scriptType = '';
47 this.scriptType = '';
48
48
49 // Attributes Mode
49 // Attributes Mode
50 this.isAttrs = false;
50 this.isAttrs = false;
51 this.attrsNest = [];
51 this.attrsNest = [];
52 this.inAttributeName = true;
52 this.inAttributeName = true;
53 this.attributeIsType = false;
53 this.attributeIsType = false;
54 this.attrValue = '';
54 this.attrValue = '';
55
55
56 // Indented Mode
56 // Indented Mode
57 this.indentOf = Infinity;
57 this.indentOf = Infinity;
58 this.indentToken = '';
58 this.indentToken = '';
59
59
60 this.innerMode = null;
60 this.innerMode = null;
61 this.innerState = null;
61 this.innerState = null;
62
62
63 this.innerModeForLine = false;
63 this.innerModeForLine = false;
64 }
64 }
65 /**
65 /**
66 * Safely copy a state
66 * Safely copy a state
67 *
67 *
68 * @return {State}
68 * @return {State}
69 */
69 */
70 State.prototype.copy = function () {
70 State.prototype.copy = function () {
71 var res = new State();
71 var res = new State();
72 res.javaScriptLine = this.javaScriptLine;
72 res.javaScriptLine = this.javaScriptLine;
73 res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;
73 res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;
74 res.javaScriptArguments = this.javaScriptArguments;
74 res.javaScriptArguments = this.javaScriptArguments;
75 res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
75 res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
76 res.isInterpolating = this.isInterpolating;
76 res.isInterpolating = this.isInterpolating;
77 res.interpolationNesting = this.intpolationNesting;
77 res.interpolationNesting = this.interpolationNesting;
78
78
79 res.jsState = CodeMirror.copyState(jsMode, this.jsState);
79 res.jsState = CodeMirror.copyState(jsMode, this.jsState);
80
80
81 res.innerMode = this.innerMode;
81 res.innerMode = this.innerMode;
82 if (this.innerMode && this.innerState) {
82 if (this.innerMode && this.innerState) {
83 res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);
83 res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);
84 }
84 }
85
85
86 res.restOfLine = this.restOfLine;
86 res.restOfLine = this.restOfLine;
87
87
88 res.isIncludeFiltered = this.isIncludeFiltered;
88 res.isIncludeFiltered = this.isIncludeFiltered;
89 res.isEach = this.isEach;
89 res.isEach = this.isEach;
90 res.lastTag = this.lastTag;
90 res.lastTag = this.lastTag;
91 res.scriptType = this.scriptType;
91 res.scriptType = this.scriptType;
92 res.isAttrs = this.isAttrs;
92 res.isAttrs = this.isAttrs;
93 res.attrsNest = this.attrsNest.slice();
93 res.attrsNest = this.attrsNest.slice();
94 res.inAttributeName = this.inAttributeName;
94 res.inAttributeName = this.inAttributeName;
95 res.attributeIsType = this.attributeIsType;
95 res.attributeIsType = this.attributeIsType;
96 res.attrValue = this.attrValue;
96 res.attrValue = this.attrValue;
97 res.indentOf = this.indentOf;
97 res.indentOf = this.indentOf;
98 res.indentToken = this.indentToken;
98 res.indentToken = this.indentToken;
99
99
100 res.innerModeForLine = this.innerModeForLine;
100 res.innerModeForLine = this.innerModeForLine;
101
101
102 return res;
102 return res;
103 };
103 };
104
104
105 function javaScript(stream, state) {
105 function javaScript(stream, state) {
106 if (stream.sol()) {
106 if (stream.sol()) {
107 // if javaScriptLine was set at end of line, ignore it
107 // if javaScriptLine was set at end of line, ignore it
108 state.javaScriptLine = false;
108 state.javaScriptLine = false;
109 state.javaScriptLineExcludesColon = false;
109 state.javaScriptLineExcludesColon = false;
110 }
110 }
111 if (state.javaScriptLine) {
111 if (state.javaScriptLine) {
112 if (state.javaScriptLineExcludesColon && stream.peek() === ':') {
112 if (state.javaScriptLineExcludesColon && stream.peek() === ':') {
113 state.javaScriptLine = false;
113 state.javaScriptLine = false;
114 state.javaScriptLineExcludesColon = false;
114 state.javaScriptLineExcludesColon = false;
115 return;
115 return;
116 }
116 }
117 var tok = jsMode.token(stream, state.jsState);
117 var tok = jsMode.token(stream, state.jsState);
118 if (stream.eol()) state.javaScriptLine = false;
118 if (stream.eol()) state.javaScriptLine = false;
119 return tok || true;
119 return tok || true;
120 }
120 }
121 }
121 }
122 function javaScriptArguments(stream, state) {
122 function javaScriptArguments(stream, state) {
123 if (state.javaScriptArguments) {
123 if (state.javaScriptArguments) {
124 if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {
124 if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {
125 state.javaScriptArguments = false;
125 state.javaScriptArguments = false;
126 return;
126 return;
127 }
127 }
128 if (stream.peek() === '(') {
128 if (stream.peek() === '(') {
129 state.javaScriptArgumentsDepth++;
129 state.javaScriptArgumentsDepth++;
130 } else if (stream.peek() === ')') {
130 } else if (stream.peek() === ')') {
131 state.javaScriptArgumentsDepth--;
131 state.javaScriptArgumentsDepth--;
132 }
132 }
133 if (state.javaScriptArgumentsDepth === 0) {
133 if (state.javaScriptArgumentsDepth === 0) {
134 state.javaScriptArguments = false;
134 state.javaScriptArguments = false;
135 return;
135 return;
136 }
136 }
137
137
138 var tok = jsMode.token(stream, state.jsState);
138 var tok = jsMode.token(stream, state.jsState);
139 return tok || true;
139 return tok || true;
140 }
140 }
141 }
141 }
142
142
143 function yieldStatement(stream) {
143 function yieldStatement(stream) {
144 if (stream.match(/^yield\b/)) {
144 if (stream.match(/^yield\b/)) {
145 return 'keyword';
145 return 'keyword';
146 }
146 }
147 }
147 }
148
148
149 function doctype(stream) {
149 function doctype(stream) {
150 if (stream.match(/^(?:doctype) *([^\n]+)?/)) {
150 if (stream.match(/^(?:doctype) *([^\n]+)?/)) {
151 return DOCTYPE;
151 return DOCTYPE;
152 }
152 }
153 }
153 }
154
154
155 function interpolation(stream, state) {
155 function interpolation(stream, state) {
156 if (stream.match('#{')) {
156 if (stream.match('#{')) {
157 state.isInterpolating = true;
157 state.isInterpolating = true;
158 state.interpolationNesting = 0;
158 state.interpolationNesting = 0;
159 return 'punctuation';
159 return 'punctuation';
160 }
160 }
161 }
161 }
162
162
163 function interpolationContinued(stream, state) {
163 function interpolationContinued(stream, state) {
164 if (state.isInterpolating) {
164 if (state.isInterpolating) {
165 if (stream.peek() === '}') {
165 if (stream.peek() === '}') {
166 state.interpolationNesting--;
166 state.interpolationNesting--;
167 if (state.interpolationNesting < 0) {
167 if (state.interpolationNesting < 0) {
168 stream.next();
168 stream.next();
169 state.isInterpolating = false;
169 state.isInterpolating = false;
170 return 'puncutation';
170 return 'punctuation';
171 }
171 }
172 } else if (stream.peek() === '{') {
172 } else if (stream.peek() === '{') {
173 state.interpolationNesting++;
173 state.interpolationNesting++;
174 }
174 }
175 return jsMode.token(stream, state.jsState) || true;
175 return jsMode.token(stream, state.jsState) || true;
176 }
176 }
177 }
177 }
178
178
179 function caseStatement(stream, state) {
179 function caseStatement(stream, state) {
180 if (stream.match(/^case\b/)) {
180 if (stream.match(/^case\b/)) {
181 state.javaScriptLine = true;
181 state.javaScriptLine = true;
182 return KEYWORD;
182 return KEYWORD;
183 }
183 }
184 }
184 }
185
185
186 function when(stream, state) {
186 function when(stream, state) {
187 if (stream.match(/^when\b/)) {
187 if (stream.match(/^when\b/)) {
188 state.javaScriptLine = true;
188 state.javaScriptLine = true;
189 state.javaScriptLineExcludesColon = true;
189 state.javaScriptLineExcludesColon = true;
190 return KEYWORD;
190 return KEYWORD;
191 }
191 }
192 }
192 }
193
193
194 function defaultStatement(stream) {
194 function defaultStatement(stream) {
195 if (stream.match(/^default\b/)) {
195 if (stream.match(/^default\b/)) {
196 return KEYWORD;
196 return KEYWORD;
197 }
197 }
198 }
198 }
199
199
200 function extendsStatement(stream, state) {
200 function extendsStatement(stream, state) {
201 if (stream.match(/^extends?\b/)) {
201 if (stream.match(/^extends?\b/)) {
202 state.restOfLine = 'string';
202 state.restOfLine = 'string';
203 return KEYWORD;
203 return KEYWORD;
204 }
204 }
205 }
205 }
206
206
207 function append(stream, state) {
207 function append(stream, state) {
208 if (stream.match(/^append\b/)) {
208 if (stream.match(/^append\b/)) {
209 state.restOfLine = 'variable';
209 state.restOfLine = 'variable';
210 return KEYWORD;
210 return KEYWORD;
211 }
211 }
212 }
212 }
213 function prepend(stream, state) {
213 function prepend(stream, state) {
214 if (stream.match(/^prepend\b/)) {
214 if (stream.match(/^prepend\b/)) {
215 state.restOfLine = 'variable';
215 state.restOfLine = 'variable';
216 return KEYWORD;
216 return KEYWORD;
217 }
217 }
218 }
218 }
219 function block(stream, state) {
219 function block(stream, state) {
220 if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) {
220 if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) {
221 state.restOfLine = 'variable';
221 state.restOfLine = 'variable';
222 return KEYWORD;
222 return KEYWORD;
223 }
223 }
224 }
224 }
225
225
226 function include(stream, state) {
226 function include(stream, state) {
227 if (stream.match(/^include\b/)) {
227 if (stream.match(/^include\b/)) {
228 state.restOfLine = 'string';
228 state.restOfLine = 'string';
229 return KEYWORD;
229 return KEYWORD;
230 }
230 }
231 }
231 }
232
232
233 function includeFiltered(stream, state) {
233 function includeFiltered(stream, state) {
234 if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) {
234 if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) {
235 state.isIncludeFiltered = true;
235 state.isIncludeFiltered = true;
236 return KEYWORD;
236 return KEYWORD;
237 }
237 }
238 }
238 }
239
239
240 function includeFilteredContinued(stream, state) {
240 function includeFilteredContinued(stream, state) {
241 if (state.isIncludeFiltered) {
241 if (state.isIncludeFiltered) {
242 var tok = filter(stream, state);
242 var tok = filter(stream, state);
243 state.isIncludeFiltered = false;
243 state.isIncludeFiltered = false;
244 state.restOfLine = 'string';
244 state.restOfLine = 'string';
245 return tok;
245 return tok;
246 }
246 }
247 }
247 }
248
248
249 function mixin(stream, state) {
249 function mixin(stream, state) {
250 if (stream.match(/^mixin\b/)) {
250 if (stream.match(/^mixin\b/)) {
251 state.javaScriptLine = true;
251 state.javaScriptLine = true;
252 return KEYWORD;
252 return KEYWORD;
253 }
253 }
254 }
254 }
255
255
256 function call(stream, state) {
256 function call(stream, state) {
257 if (stream.match(/^\+([-\w]+)/)) {
257 if (stream.match(/^\+([-\w]+)/)) {
258 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
258 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
259 state.javaScriptArguments = true;
259 state.javaScriptArguments = true;
260 state.javaScriptArgumentsDepth = 0;
260 state.javaScriptArgumentsDepth = 0;
261 }
261 }
262 return 'variable';
262 return 'variable';
263 }
263 }
264 if (stream.match(/^\+#{/, false)) {
264 if (stream.match(/^\+#{/, false)) {
265 stream.next();
265 stream.next();
266 state.mixinCallAfter = true;
266 state.mixinCallAfter = true;
267 return interpolation(stream, state);
267 return interpolation(stream, state);
268 }
268 }
269 }
269 }
270 function callArguments(stream, state) {
270 function callArguments(stream, state) {
271 if (state.mixinCallAfter) {
271 if (state.mixinCallAfter) {
272 state.mixinCallAfter = false;
272 state.mixinCallAfter = false;
273 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
273 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
274 state.javaScriptArguments = true;
274 state.javaScriptArguments = true;
275 state.javaScriptArgumentsDepth = 0;
275 state.javaScriptArgumentsDepth = 0;
276 }
276 }
277 return true;
277 return true;
278 }
278 }
279 }
279 }
280
280
281 function conditional(stream, state) {
281 function conditional(stream, state) {
282 if (stream.match(/^(if|unless|else if|else)\b/)) {
282 if (stream.match(/^(if|unless|else if|else)\b/)) {
283 state.javaScriptLine = true;
283 state.javaScriptLine = true;
284 return KEYWORD;
284 return KEYWORD;
285 }
285 }
286 }
286 }
287
287
288 function each(stream, state) {
288 function each(stream, state) {
289 if (stream.match(/^(- *)?(each|for)\b/)) {
289 if (stream.match(/^(- *)?(each|for)\b/)) {
290 state.isEach = true;
290 state.isEach = true;
291 return KEYWORD;
291 return KEYWORD;
292 }
292 }
293 }
293 }
294 function eachContinued(stream, state) {
294 function eachContinued(stream, state) {
295 if (state.isEach) {
295 if (state.isEach) {
296 if (stream.match(/^ in\b/)) {
296 if (stream.match(/^ in\b/)) {
297 state.javaScriptLine = true;
297 state.javaScriptLine = true;
298 state.isEach = false;
298 state.isEach = false;
299 return KEYWORD;
299 return KEYWORD;
300 } else if (stream.sol() || stream.eol()) {
300 } else if (stream.sol() || stream.eol()) {
301 state.isEach = false;
301 state.isEach = false;
302 } else if (stream.next()) {
302 } else if (stream.next()) {
303 while (!stream.match(/^ in\b/, false) && stream.next());
303 while (!stream.match(/^ in\b/, false) && stream.next());
304 return 'variable';
304 return 'variable';
305 }
305 }
306 }
306 }
307 }
307 }
308
308
309 function whileStatement(stream, state) {
309 function whileStatement(stream, state) {
310 if (stream.match(/^while\b/)) {
310 if (stream.match(/^while\b/)) {
311 state.javaScriptLine = true;
311 state.javaScriptLine = true;
312 return KEYWORD;
312 return KEYWORD;
313 }
313 }
314 }
314 }
315
315
316 function tag(stream, state) {
316 function tag(stream, state) {
317 var captures;
317 var captures;
318 if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) {
318 if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) {
319 state.lastTag = captures[1].toLowerCase();
319 state.lastTag = captures[1].toLowerCase();
320 if (state.lastTag === 'script') {
320 if (state.lastTag === 'script') {
321 state.scriptType = 'application/javascript';
321 state.scriptType = 'application/javascript';
322 }
322 }
323 return 'tag';
323 return 'tag';
324 }
324 }
325 }
325 }
326
326
327 function filter(stream, state) {
327 function filter(stream, state) {
328 if (stream.match(/^:([\w\-]+)/)) {
328 if (stream.match(/^:([\w\-]+)/)) {
329 var innerMode;
329 var innerMode;
330 if (config && config.innerModes) {
330 if (config && config.innerModes) {
331 innerMode = config.innerModes(stream.current().substring(1));
331 innerMode = config.innerModes(stream.current().substring(1));
332 }
332 }
333 if (!innerMode) {
333 if (!innerMode) {
334 innerMode = stream.current().substring(1);
334 innerMode = stream.current().substring(1);
335 }
335 }
336 if (typeof innerMode === 'string') {
336 if (typeof innerMode === 'string') {
337 innerMode = CodeMirror.getMode(config, innerMode);
337 innerMode = CodeMirror.getMode(config, innerMode);
338 }
338 }
339 setInnerMode(stream, state, innerMode);
339 setInnerMode(stream, state, innerMode);
340 return 'atom';
340 return 'atom';
341 }
341 }
342 }
342 }
343
343
344 function code(stream, state) {
344 function code(stream, state) {
345 if (stream.match(/^(!?=|-)/)) {
345 if (stream.match(/^(!?=|-)/)) {
346 state.javaScriptLine = true;
346 state.javaScriptLine = true;
347 return 'punctuation';
347 return 'punctuation';
348 }
348 }
349 }
349 }
350
350
351 function id(stream) {
351 function id(stream) {
352 if (stream.match(/^#([\w-]+)/)) {
352 if (stream.match(/^#([\w-]+)/)) {
353 return ID;
353 return ID;
354 }
354 }
355 }
355 }
356
356
357 function className(stream) {
357 function className(stream) {
358 if (stream.match(/^\.([\w-]+)/)) {
358 if (stream.match(/^\.([\w-]+)/)) {
359 return CLASS;
359 return CLASS;
360 }
360 }
361 }
361 }
362
362
363 function attrs(stream, state) {
363 function attrs(stream, state) {
364 if (stream.peek() == '(') {
364 if (stream.peek() == '(') {
365 stream.next();
365 stream.next();
366 state.isAttrs = true;
366 state.isAttrs = true;
367 state.attrsNest = [];
367 state.attrsNest = [];
368 state.inAttributeName = true;
368 state.inAttributeName = true;
369 state.attrValue = '';
369 state.attrValue = '';
370 state.attributeIsType = false;
370 state.attributeIsType = false;
371 return 'punctuation';
371 return 'punctuation';
372 }
372 }
373 }
373 }
374
374
375 function attrsContinued(stream, state) {
375 function attrsContinued(stream, state) {
376 if (state.isAttrs) {
376 if (state.isAttrs) {
377 if (ATTRS_NEST[stream.peek()]) {
377 if (ATTRS_NEST[stream.peek()]) {
378 state.attrsNest.push(ATTRS_NEST[stream.peek()]);
378 state.attrsNest.push(ATTRS_NEST[stream.peek()]);
379 }
379 }
380 if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {
380 if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {
381 state.attrsNest.pop();
381 state.attrsNest.pop();
382 } else if (stream.eat(')')) {
382 } else if (stream.eat(')')) {
383 state.isAttrs = false;
383 state.isAttrs = false;
384 return 'punctuation';
384 return 'punctuation';
385 }
385 }
386 if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) {
386 if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) {
387 if (stream.peek() === '=' || stream.peek() === '!') {
387 if (stream.peek() === '=' || stream.peek() === '!') {
388 state.inAttributeName = false;
388 state.inAttributeName = false;
389 state.jsState = jsMode.startState();
389 state.jsState = jsMode.startState();
390 if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {
390 if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {
391 state.attributeIsType = true;
391 state.attributeIsType = true;
392 } else {
392 } else {
393 state.attributeIsType = false;
393 state.attributeIsType = false;
394 }
394 }
395 }
395 }
396 return 'attribute';
396 return 'attribute';
397 }
397 }
398
398
399 var tok = jsMode.token(stream, state.jsState);
399 var tok = jsMode.token(stream, state.jsState);
400 if (state.attributeIsType && tok === 'string') {
400 if (state.attributeIsType && tok === 'string') {
401 state.scriptType = stream.current().toString();
401 state.scriptType = stream.current().toString();
402 }
402 }
403 if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {
403 if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {
404 try {
404 try {
405 Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, ''));
405 Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, ''));
406 state.inAttributeName = true;
406 state.inAttributeName = true;
407 state.attrValue = '';
407 state.attrValue = '';
408 stream.backUp(stream.current().length);
408 stream.backUp(stream.current().length);
409 return attrsContinued(stream, state);
409 return attrsContinued(stream, state);
410 } catch (ex) {
410 } catch (ex) {
411 //not the end of an attribute
411 //not the end of an attribute
412 }
412 }
413 }
413 }
414 state.attrValue += stream.current();
414 state.attrValue += stream.current();
415 return tok || true;
415 return tok || true;
416 }
416 }
417 }
417 }
418
418
419 function attributesBlock(stream, state) {
419 function attributesBlock(stream, state) {
420 if (stream.match(/^&attributes\b/)) {
420 if (stream.match(/^&attributes\b/)) {
421 state.javaScriptArguments = true;
421 state.javaScriptArguments = true;
422 state.javaScriptArgumentsDepth = 0;
422 state.javaScriptArgumentsDepth = 0;
423 return 'keyword';
423 return 'keyword';
424 }
424 }
425 }
425 }
426
426
427 function indent(stream) {
427 function indent(stream) {
428 if (stream.sol() && stream.eatSpace()) {
428 if (stream.sol() && stream.eatSpace()) {
429 return 'indent';
429 return 'indent';
430 }
430 }
431 }
431 }
432
432
433 function comment(stream, state) {
433 function comment(stream, state) {
434 if (stream.match(/^ *\/\/(-)?([^\n]*)/)) {
434 if (stream.match(/^ *\/\/(-)?([^\n]*)/)) {
435 state.indentOf = stream.indentation();
435 state.indentOf = stream.indentation();
436 state.indentToken = 'comment';
436 state.indentToken = 'comment';
437 return 'comment';
437 return 'comment';
438 }
438 }
439 }
439 }
440
440
441 function colon(stream) {
441 function colon(stream) {
442 if (stream.match(/^: */)) {
442 if (stream.match(/^: */)) {
443 return 'colon';
443 return 'colon';
444 }
444 }
445 }
445 }
446
446
447 function text(stream, state) {
447 function text(stream, state) {
448 if (stream.match(/^(?:\| ?| )([^\n]+)/)) {
448 if (stream.match(/^(?:\| ?| )([^\n]+)/)) {
449 return 'string';
449 return 'string';
450 }
450 }
451 if (stream.match(/^(<[^\n]*)/, false)) {
451 if (stream.match(/^(<[^\n]*)/, false)) {
452 // html string
452 // html string
453 setInnerMode(stream, state, 'htmlmixed');
453 setInnerMode(stream, state, 'htmlmixed');
454 state.innerModeForLine = true;
454 state.innerModeForLine = true;
455 return innerMode(stream, state, true);
455 return innerMode(stream, state, true);
456 }
456 }
457 }
457 }
458
458
459 function dot(stream, state) {
459 function dot(stream, state) {
460 if (stream.eat('.')) {
460 if (stream.eat('.')) {
461 var innerMode = null;
461 var innerMode = null;
462 if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {
462 if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {
463 innerMode = state.scriptType.toLowerCase().replace(/"|'/g, '');
463 innerMode = state.scriptType.toLowerCase().replace(/"|'/g, '');
464 } else if (state.lastTag === 'style') {
464 } else if (state.lastTag === 'style') {
465 innerMode = 'css';
465 innerMode = 'css';
466 }
466 }
467 setInnerMode(stream, state, innerMode);
467 setInnerMode(stream, state, innerMode);
468 return 'dot';
468 return 'dot';
469 }
469 }
470 }
470 }
471
471
472 function fail(stream) {
472 function fail(stream) {
473 stream.next();
473 stream.next();
474 return null;
474 return null;
475 }
475 }
476
476
477
477
478 function setInnerMode(stream, state, mode) {
478 function setInnerMode(stream, state, mode) {
479 mode = CodeMirror.mimeModes[mode] || mode;
479 mode = CodeMirror.mimeModes[mode] || mode;
480 mode = config.innerModes ? config.innerModes(mode) || mode : mode;
480 mode = config.innerModes ? config.innerModes(mode) || mode : mode;
481 mode = CodeMirror.mimeModes[mode] || mode;
481 mode = CodeMirror.mimeModes[mode] || mode;
482 mode = CodeMirror.getMode(config, mode);
482 mode = CodeMirror.getMode(config, mode);
483 state.indentOf = stream.indentation();
483 state.indentOf = stream.indentation();
484
484
485 if (mode && mode.name !== 'null') {
485 if (mode && mode.name !== 'null') {
486 state.innerMode = mode;
486 state.innerMode = mode;
487 } else {
487 } else {
488 state.indentToken = 'string';
488 state.indentToken = 'string';
489 }
489 }
490 }
490 }
491 function innerMode(stream, state, force) {
491 function innerMode(stream, state, force) {
492 if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {
492 if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {
493 if (state.innerMode) {
493 if (state.innerMode) {
494 if (!state.innerState) {
494 if (!state.innerState) {
495 state.innerState = state.innerMode.startState ? state.innerMode.startState(stream.indentation()) : {};
495 state.innerState = state.innerMode.startState ? state.innerMode.startState(stream.indentation()) : {};
496 }
496 }
497 return stream.hideFirstChars(state.indentOf + 2, function () {
497 return stream.hideFirstChars(state.indentOf + 2, function () {
498 return state.innerMode.token(stream, state.innerState) || true;
498 return state.innerMode.token(stream, state.innerState) || true;
499 });
499 });
500 } else {
500 } else {
501 stream.skipToEnd();
501 stream.skipToEnd();
502 return state.indentToken;
502 return state.indentToken;
503 }
503 }
504 } else if (stream.sol()) {
504 } else if (stream.sol()) {
505 state.indentOf = Infinity;
505 state.indentOf = Infinity;
506 state.indentToken = null;
506 state.indentToken = null;
507 state.innerMode = null;
507 state.innerMode = null;
508 state.innerState = null;
508 state.innerState = null;
509 }
509 }
510 }
510 }
511 function restOfLine(stream, state) {
511 function restOfLine(stream, state) {
512 if (stream.sol()) {
512 if (stream.sol()) {
513 // if restOfLine was set at end of line, ignore it
513 // if restOfLine was set at end of line, ignore it
514 state.restOfLine = '';
514 state.restOfLine = '';
515 }
515 }
516 if (state.restOfLine) {
516 if (state.restOfLine) {
517 stream.skipToEnd();
517 stream.skipToEnd();
518 var tok = state.restOfLine;
518 var tok = state.restOfLine;
519 state.restOfLine = '';
519 state.restOfLine = '';
520 return tok;
520 return tok;
521 }
521 }
522 }
522 }
523
523
524
524
525 function startState() {
525 function startState() {
526 return new State();
526 return new State();
527 }
527 }
528 function copyState(state) {
528 function copyState(state) {
529 return state.copy();
529 return state.copy();
530 }
530 }
531 /**
531 /**
532 * Get the next token in the stream
532 * Get the next token in the stream
533 *
533 *
534 * @param {Stream} stream
534 * @param {Stream} stream
535 * @param {State} state
535 * @param {State} state
536 */
536 */
537 function nextToken(stream, state) {
537 function nextToken(stream, state) {
538 var tok = innerMode(stream, state)
538 var tok = innerMode(stream, state)
539 || restOfLine(stream, state)
539 || restOfLine(stream, state)
540 || interpolationContinued(stream, state)
540 || interpolationContinued(stream, state)
541 || includeFilteredContinued(stream, state)
541 || includeFilteredContinued(stream, state)
542 || eachContinued(stream, state)
542 || eachContinued(stream, state)
543 || attrsContinued(stream, state)
543 || attrsContinued(stream, state)
544 || javaScript(stream, state)
544 || javaScript(stream, state)
545 || javaScriptArguments(stream, state)
545 || javaScriptArguments(stream, state)
546 || callArguments(stream, state)
546 || callArguments(stream, state)
547
547
548 || yieldStatement(stream, state)
548 || yieldStatement(stream, state)
549 || doctype(stream, state)
549 || doctype(stream, state)
550 || interpolation(stream, state)
550 || interpolation(stream, state)
551 || caseStatement(stream, state)
551 || caseStatement(stream, state)
552 || when(stream, state)
552 || when(stream, state)
553 || defaultStatement(stream, state)
553 || defaultStatement(stream, state)
554 || extendsStatement(stream, state)
554 || extendsStatement(stream, state)
555 || append(stream, state)
555 || append(stream, state)
556 || prepend(stream, state)
556 || prepend(stream, state)
557 || block(stream, state)
557 || block(stream, state)
558 || include(stream, state)
558 || include(stream, state)
559 || includeFiltered(stream, state)
559 || includeFiltered(stream, state)
560 || mixin(stream, state)
560 || mixin(stream, state)
561 || call(stream, state)
561 || call(stream, state)
562 || conditional(stream, state)
562 || conditional(stream, state)
563 || each(stream, state)
563 || each(stream, state)
564 || whileStatement(stream, state)
564 || whileStatement(stream, state)
565 || tag(stream, state)
565 || tag(stream, state)
566 || filter(stream, state)
566 || filter(stream, state)
567 || code(stream, state)
567 || code(stream, state)
568 || id(stream, state)
568 || id(stream, state)
569 || className(stream, state)
569 || className(stream, state)
570 || attrs(stream, state)
570 || attrs(stream, state)
571 || attributesBlock(stream, state)
571 || attributesBlock(stream, state)
572 || indent(stream, state)
572 || indent(stream, state)
573 || text(stream, state)
573 || text(stream, state)
574 || comment(stream, state)
574 || comment(stream, state)
575 || colon(stream, state)
575 || colon(stream, state)
576 || dot(stream, state)
576 || dot(stream, state)
577 || fail(stream, state);
577 || fail(stream, state);
578
578
579 return tok === true ? null : tok;
579 return tok === true ? null : tok;
580 }
580 }
581 return {
581 return {
582 startState: startState,
582 startState: startState,
583 copyState: copyState,
583 copyState: copyState,
584 token: nextToken
584 token: nextToken
585 };
585 };
586 });
586 }, 'javascript', 'css', 'htmlmixed');
587
587
588 CodeMirror.defineMIME('text/x-jade', 'jade');
588 CodeMirror.defineMIME('text/x-jade', 'jade');
589
589
590 });
590 });
@@ -1,704 +1,742 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 // TODO actually recognize syntax of TypeScript constructs
4 // TODO actually recognize syntax of TypeScript constructs
5
5
6 (function(mod) {
6 (function(mod) {
7 if (typeof exports == "object" && typeof module == "object") // CommonJS
7 if (typeof exports == "object" && typeof module == "object") // CommonJS
8 mod(require("../../lib/codemirror"));
8 mod(require("../../lib/codemirror"));
9 else if (typeof define == "function" && define.amd) // AMD
9 else if (typeof define == "function" && define.amd) // AMD
10 define(["../../lib/codemirror"], mod);
10 define(["../../lib/codemirror"], mod);
11 else // Plain browser env
11 else // Plain browser env
12 mod(CodeMirror);
12 mod(CodeMirror);
13 })(function(CodeMirror) {
13 })(function(CodeMirror) {
14 "use strict";
14 "use strict";
15
15
16 function expressionAllowed(stream, state, backUp) {
17 return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
18 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
19 }
20
16 CodeMirror.defineMode("javascript", function(config, parserConfig) {
21 CodeMirror.defineMode("javascript", function(config, parserConfig) {
17 var indentUnit = config.indentUnit;
22 var indentUnit = config.indentUnit;
18 var statementIndent = parserConfig.statementIndent;
23 var statementIndent = parserConfig.statementIndent;
19 var jsonldMode = parserConfig.jsonld;
24 var jsonldMode = parserConfig.jsonld;
20 var jsonMode = parserConfig.json || jsonldMode;
25 var jsonMode = parserConfig.json || jsonldMode;
21 var isTS = parserConfig.typescript;
26 var isTS = parserConfig.typescript;
22 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
27 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
23
28
24 // Tokenizer
29 // Tokenizer
25
30
26 var keywords = function(){
31 var keywords = function(){
27 function kw(type) {return {type: type, style: "keyword"};}
32 function kw(type) {return {type: type, style: "keyword"};}
28 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
33 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
29 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
34 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
30
35
31 var jsKeywords = {
36 var jsKeywords = {
32 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
37 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
33 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C,
38 "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
34 "var": kw("var"), "const": kw("var"), "let": kw("var"),
39 "var": kw("var"), "const": kw("var"), "let": kw("var"),
35 "function": kw("function"), "catch": kw("catch"),
40 "function": kw("function"), "catch": kw("catch"),
36 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
41 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
37 "in": operator, "typeof": operator, "instanceof": operator,
42 "in": operator, "typeof": operator, "instanceof": operator,
38 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
43 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
39 "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"),
44 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
40 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
45 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
41 };
46 };
42
47
43 // Extend the 'normal' keywords with the TypeScript language extensions
48 // Extend the 'normal' keywords with the TypeScript language extensions
44 if (isTS) {
49 if (isTS) {
45 var type = {type: "variable", style: "variable-3"};
50 var type = {type: "variable", style: "variable-3"};
46 var tsKeywords = {
51 var tsKeywords = {
47 // object-like things
52 // object-like things
48 "interface": kw("interface"),
53 "interface": kw("class"),
49 "extends": kw("extends"),
54 "implements": C,
50 "constructor": kw("constructor"),
55 "namespace": C,
56 "module": kw("module"),
57 "enum": kw("module"),
51
58
52 // scope modifiers
59 // scope modifiers
53 "public": kw("public"),
60 "public": kw("modifier"),
54 "private": kw("private"),
61 "private": kw("modifier"),
55 "protected": kw("protected"),
62 "protected": kw("modifier"),
56 "static": kw("static"),
63 "abstract": kw("modifier"),
64
65 // operators
66 "as": operator,
57
67
58 // types
68 // types
59 "string": type, "number": type, "bool": type, "any": type
69 "string": type, "number": type, "boolean": type, "any": type
60 };
70 };
61
71
62 for (var attr in tsKeywords) {
72 for (var attr in tsKeywords) {
63 jsKeywords[attr] = tsKeywords[attr];
73 jsKeywords[attr] = tsKeywords[attr];
64 }
74 }
65 }
75 }
66
76
67 return jsKeywords;
77 return jsKeywords;
68 }();
78 }();
69
79
70 var isOperatorChar = /[+\-*&%=<>!?|~^]/;
80 var isOperatorChar = /[+\-*&%=<>!?|~^]/;
71 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
81 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
72
82
73 function readRegexp(stream) {
83 function readRegexp(stream) {
74 var escaped = false, next, inSet = false;
84 var escaped = false, next, inSet = false;
75 while ((next = stream.next()) != null) {
85 while ((next = stream.next()) != null) {
76 if (!escaped) {
86 if (!escaped) {
77 if (next == "/" && !inSet) return;
87 if (next == "/" && !inSet) return;
78 if (next == "[") inSet = true;
88 if (next == "[") inSet = true;
79 else if (inSet && next == "]") inSet = false;
89 else if (inSet && next == "]") inSet = false;
80 }
90 }
81 escaped = !escaped && next == "\\";
91 escaped = !escaped && next == "\\";
82 }
92 }
83 }
93 }
84
94
85 // Used as scratch variables to communicate multiple values without
95 // Used as scratch variables to communicate multiple values without
86 // consing up tons of objects.
96 // consing up tons of objects.
87 var type, content;
97 var type, content;
88 function ret(tp, style, cont) {
98 function ret(tp, style, cont) {
89 type = tp; content = cont;
99 type = tp; content = cont;
90 return style;
100 return style;
91 }
101 }
92 function tokenBase(stream, state) {
102 function tokenBase(stream, state) {
93 var ch = stream.next();
103 var ch = stream.next();
94 if (ch == '"' || ch == "'") {
104 if (ch == '"' || ch == "'") {
95 state.tokenize = tokenString(ch);
105 state.tokenize = tokenString(ch);
96 return state.tokenize(stream, state);
106 return state.tokenize(stream, state);
97 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
107 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
98 return ret("number", "number");
108 return ret("number", "number");
99 } else if (ch == "." && stream.match("..")) {
109 } else if (ch == "." && stream.match("..")) {
100 return ret("spread", "meta");
110 return ret("spread", "meta");
101 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
111 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
102 return ret(ch);
112 return ret(ch);
103 } else if (ch == "=" && stream.eat(">")) {
113 } else if (ch == "=" && stream.eat(">")) {
104 return ret("=>", "operator");
114 return ret("=>", "operator");
105 } else if (ch == "0" && stream.eat(/x/i)) {
115 } else if (ch == "0" && stream.eat(/x/i)) {
106 stream.eatWhile(/[\da-f]/i);
116 stream.eatWhile(/[\da-f]/i);
107 return ret("number", "number");
117 return ret("number", "number");
118 } else if (ch == "0" && stream.eat(/o/i)) {
119 stream.eatWhile(/[0-7]/i);
120 return ret("number", "number");
121 } else if (ch == "0" && stream.eat(/b/i)) {
122 stream.eatWhile(/[01]/i);
123 return ret("number", "number");
108 } else if (/\d/.test(ch)) {
124 } else if (/\d/.test(ch)) {
109 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
125 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
110 return ret("number", "number");
126 return ret("number", "number");
111 } else if (ch == "/") {
127 } else if (ch == "/") {
112 if (stream.eat("*")) {
128 if (stream.eat("*")) {
113 state.tokenize = tokenComment;
129 state.tokenize = tokenComment;
114 return tokenComment(stream, state);
130 return tokenComment(stream, state);
115 } else if (stream.eat("/")) {
131 } else if (stream.eat("/")) {
116 stream.skipToEnd();
132 stream.skipToEnd();
117 return ret("comment", "comment");
133 return ret("comment", "comment");
118 } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
134 } else if (expressionAllowed(stream, state, 1)) {
119 state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
120 readRegexp(stream);
135 readRegexp(stream);
121 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
136 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
122 return ret("regexp", "string-2");
137 return ret("regexp", "string-2");
123 } else {
138 } else {
124 stream.eatWhile(isOperatorChar);
139 stream.eatWhile(isOperatorChar);
125 return ret("operator", "operator", stream.current());
140 return ret("operator", "operator", stream.current());
126 }
141 }
127 } else if (ch == "`") {
142 } else if (ch == "`") {
128 state.tokenize = tokenQuasi;
143 state.tokenize = tokenQuasi;
129 return tokenQuasi(stream, state);
144 return tokenQuasi(stream, state);
130 } else if (ch == "#") {
145 } else if (ch == "#") {
131 stream.skipToEnd();
146 stream.skipToEnd();
132 return ret("error", "error");
147 return ret("error", "error");
133 } else if (isOperatorChar.test(ch)) {
148 } else if (isOperatorChar.test(ch)) {
134 stream.eatWhile(isOperatorChar);
149 stream.eatWhile(isOperatorChar);
135 return ret("operator", "operator", stream.current());
150 return ret("operator", "operator", stream.current());
136 } else if (wordRE.test(ch)) {
151 } else if (wordRE.test(ch)) {
137 stream.eatWhile(wordRE);
152 stream.eatWhile(wordRE);
138 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
153 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
139 return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
154 return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
140 ret("variable", "variable", word);
155 ret("variable", "variable", word);
141 }
156 }
142 }
157 }
143
158
144 function tokenString(quote) {
159 function tokenString(quote) {
145 return function(stream, state) {
160 return function(stream, state) {
146 var escaped = false, next;
161 var escaped = false, next;
147 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
162 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
148 state.tokenize = tokenBase;
163 state.tokenize = tokenBase;
149 return ret("jsonld-keyword", "meta");
164 return ret("jsonld-keyword", "meta");
150 }
165 }
151 while ((next = stream.next()) != null) {
166 while ((next = stream.next()) != null) {
152 if (next == quote && !escaped) break;
167 if (next == quote && !escaped) break;
153 escaped = !escaped && next == "\\";
168 escaped = !escaped && next == "\\";
154 }
169 }
155 if (!escaped) state.tokenize = tokenBase;
170 if (!escaped) state.tokenize = tokenBase;
156 return ret("string", "string");
171 return ret("string", "string");
157 };
172 };
158 }
173 }
159
174
160 function tokenComment(stream, state) {
175 function tokenComment(stream, state) {
161 var maybeEnd = false, ch;
176 var maybeEnd = false, ch;
162 while (ch = stream.next()) {
177 while (ch = stream.next()) {
163 if (ch == "/" && maybeEnd) {
178 if (ch == "/" && maybeEnd) {
164 state.tokenize = tokenBase;
179 state.tokenize = tokenBase;
165 break;
180 break;
166 }
181 }
167 maybeEnd = (ch == "*");
182 maybeEnd = (ch == "*");
168 }
183 }
169 return ret("comment", "comment");
184 return ret("comment", "comment");
170 }
185 }
171
186
172 function tokenQuasi(stream, state) {
187 function tokenQuasi(stream, state) {
173 var escaped = false, next;
188 var escaped = false, next;
174 while ((next = stream.next()) != null) {
189 while ((next = stream.next()) != null) {
175 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
190 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
176 state.tokenize = tokenBase;
191 state.tokenize = tokenBase;
177 break;
192 break;
178 }
193 }
179 escaped = !escaped && next == "\\";
194 escaped = !escaped && next == "\\";
180 }
195 }
181 return ret("quasi", "string-2", stream.current());
196 return ret("quasi", "string-2", stream.current());
182 }
197 }
183
198
184 var brackets = "([{}])";
199 var brackets = "([{}])";
185 // This is a crude lookahead trick to try and notice that we're
200 // This is a crude lookahead trick to try and notice that we're
186 // parsing the argument patterns for a fat-arrow function before we
201 // parsing the argument patterns for a fat-arrow function before we
187 // actually hit the arrow token. It only works if the arrow is on
202 // actually hit the arrow token. It only works if the arrow is on
188 // the same line as the arguments and there's no strange noise
203 // the same line as the arguments and there's no strange noise
189 // (comments) in between. Fallback is to only notice when we hit the
204 // (comments) in between. Fallback is to only notice when we hit the
190 // arrow, and not declare the arguments as locals for the arrow
205 // arrow, and not declare the arguments as locals for the arrow
191 // body.
206 // body.
192 function findFatArrow(stream, state) {
207 function findFatArrow(stream, state) {
193 if (state.fatArrowAt) state.fatArrowAt = null;
208 if (state.fatArrowAt) state.fatArrowAt = null;
194 var arrow = stream.string.indexOf("=>", stream.start);
209 var arrow = stream.string.indexOf("=>", stream.start);
195 if (arrow < 0) return;
210 if (arrow < 0) return;
196
211
197 var depth = 0, sawSomething = false;
212 var depth = 0, sawSomething = false;
198 for (var pos = arrow - 1; pos >= 0; --pos) {
213 for (var pos = arrow - 1; pos >= 0; --pos) {
199 var ch = stream.string.charAt(pos);
214 var ch = stream.string.charAt(pos);
200 var bracket = brackets.indexOf(ch);
215 var bracket = brackets.indexOf(ch);
201 if (bracket >= 0 && bracket < 3) {
216 if (bracket >= 0 && bracket < 3) {
202 if (!depth) { ++pos; break; }
217 if (!depth) { ++pos; break; }
203 if (--depth == 0) break;
218 if (--depth == 0) break;
204 } else if (bracket >= 3 && bracket < 6) {
219 } else if (bracket >= 3 && bracket < 6) {
205 ++depth;
220 ++depth;
206 } else if (wordRE.test(ch)) {
221 } else if (wordRE.test(ch)) {
207 sawSomething = true;
222 sawSomething = true;
208 } else if (/["'\/]/.test(ch)) {
223 } else if (/["'\/]/.test(ch)) {
209 return;
224 return;
210 } else if (sawSomething && !depth) {
225 } else if (sawSomething && !depth) {
211 ++pos;
226 ++pos;
212 break;
227 break;
213 }
228 }
214 }
229 }
215 if (sawSomething && !depth) state.fatArrowAt = pos;
230 if (sawSomething && !depth) state.fatArrowAt = pos;
216 }
231 }
217
232
218 // Parser
233 // Parser
219
234
220 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
235 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
221
236
222 function JSLexical(indented, column, type, align, prev, info) {
237 function JSLexical(indented, column, type, align, prev, info) {
223 this.indented = indented;
238 this.indented = indented;
224 this.column = column;
239 this.column = column;
225 this.type = type;
240 this.type = type;
226 this.prev = prev;
241 this.prev = prev;
227 this.info = info;
242 this.info = info;
228 if (align != null) this.align = align;
243 if (align != null) this.align = align;
229 }
244 }
230
245
231 function inScope(state, varname) {
246 function inScope(state, varname) {
232 for (var v = state.localVars; v; v = v.next)
247 for (var v = state.localVars; v; v = v.next)
233 if (v.name == varname) return true;
248 if (v.name == varname) return true;
234 for (var cx = state.context; cx; cx = cx.prev) {
249 for (var cx = state.context; cx; cx = cx.prev) {
235 for (var v = cx.vars; v; v = v.next)
250 for (var v = cx.vars; v; v = v.next)
236 if (v.name == varname) return true;
251 if (v.name == varname) return true;
237 }
252 }
238 }
253 }
239
254
240 function parseJS(state, style, type, content, stream) {
255 function parseJS(state, style, type, content, stream) {
241 var cc = state.cc;
256 var cc = state.cc;
242 // Communicate our context to the combinators.
257 // Communicate our context to the combinators.
243 // (Less wasteful than consing up a hundred closures on every call.)
258 // (Less wasteful than consing up a hundred closures on every call.)
244 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
259 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
245
260
246 if (!state.lexical.hasOwnProperty("align"))
261 if (!state.lexical.hasOwnProperty("align"))
247 state.lexical.align = true;
262 state.lexical.align = true;
248
263
249 while(true) {
264 while(true) {
250 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
265 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
251 if (combinator(type, content)) {
266 if (combinator(type, content)) {
252 while(cc.length && cc[cc.length - 1].lex)
267 while(cc.length && cc[cc.length - 1].lex)
253 cc.pop()();
268 cc.pop()();
254 if (cx.marked) return cx.marked;
269 if (cx.marked) return cx.marked;
255 if (type == "variable" && inScope(state, content)) return "variable-2";
270 if (type == "variable" && inScope(state, content)) return "variable-2";
256 return style;
271 return style;
257 }
272 }
258 }
273 }
259 }
274 }
260
275
261 // Combinator utils
276 // Combinator utils
262
277
263 var cx = {state: null, column: null, marked: null, cc: null};
278 var cx = {state: null, column: null, marked: null, cc: null};
264 function pass() {
279 function pass() {
265 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
280 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
266 }
281 }
267 function cont() {
282 function cont() {
268 pass.apply(null, arguments);
283 pass.apply(null, arguments);
269 return true;
284 return true;
270 }
285 }
271 function register(varname) {
286 function register(varname) {
272 function inList(list) {
287 function inList(list) {
273 for (var v = list; v; v = v.next)
288 for (var v = list; v; v = v.next)
274 if (v.name == varname) return true;
289 if (v.name == varname) return true;
275 return false;
290 return false;
276 }
291 }
277 var state = cx.state;
292 var state = cx.state;
293 cx.marked = "def";
278 if (state.context) {
294 if (state.context) {
279 cx.marked = "def";
280 if (inList(state.localVars)) return;
295 if (inList(state.localVars)) return;
281 state.localVars = {name: varname, next: state.localVars};
296 state.localVars = {name: varname, next: state.localVars};
282 } else {
297 } else {
283 if (inList(state.globalVars)) return;
298 if (inList(state.globalVars)) return;
284 if (parserConfig.globalVars)
299 if (parserConfig.globalVars)
285 state.globalVars = {name: varname, next: state.globalVars};
300 state.globalVars = {name: varname, next: state.globalVars};
286 }
301 }
287 }
302 }
288
303
289 // Combinators
304 // Combinators
290
305
291 var defaultVars = {name: "this", next: {name: "arguments"}};
306 var defaultVars = {name: "this", next: {name: "arguments"}};
292 function pushcontext() {
307 function pushcontext() {
293 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
308 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
294 cx.state.localVars = defaultVars;
309 cx.state.localVars = defaultVars;
295 }
310 }
296 function popcontext() {
311 function popcontext() {
297 cx.state.localVars = cx.state.context.vars;
312 cx.state.localVars = cx.state.context.vars;
298 cx.state.context = cx.state.context.prev;
313 cx.state.context = cx.state.context.prev;
299 }
314 }
300 function pushlex(type, info) {
315 function pushlex(type, info) {
301 var result = function() {
316 var result = function() {
302 var state = cx.state, indent = state.indented;
317 var state = cx.state, indent = state.indented;
303 if (state.lexical.type == "stat") indent = state.lexical.indented;
318 if (state.lexical.type == "stat") indent = state.lexical.indented;
304 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
319 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
305 indent = outer.indented;
320 indent = outer.indented;
306 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
321 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
307 };
322 };
308 result.lex = true;
323 result.lex = true;
309 return result;
324 return result;
310 }
325 }
311 function poplex() {
326 function poplex() {
312 var state = cx.state;
327 var state = cx.state;
313 if (state.lexical.prev) {
328 if (state.lexical.prev) {
314 if (state.lexical.type == ")")
329 if (state.lexical.type == ")")
315 state.indented = state.lexical.indented;
330 state.indented = state.lexical.indented;
316 state.lexical = state.lexical.prev;
331 state.lexical = state.lexical.prev;
317 }
332 }
318 }
333 }
319 poplex.lex = true;
334 poplex.lex = true;
320
335
321 function expect(wanted) {
336 function expect(wanted) {
322 function exp(type) {
337 function exp(type) {
323 if (type == wanted) return cont();
338 if (type == wanted) return cont();
324 else if (wanted == ";") return pass();
339 else if (wanted == ";") return pass();
325 else return cont(exp);
340 else return cont(exp);
326 };
341 };
327 return exp;
342 return exp;
328 }
343 }
329
344
330 function statement(type, value) {
345 function statement(type, value) {
331 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
346 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
332 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
347 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
333 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
348 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
334 if (type == "{") return cont(pushlex("}"), block, poplex);
349 if (type == "{") return cont(pushlex("}"), block, poplex);
335 if (type == ";") return cont();
350 if (type == ";") return cont();
336 if (type == "if") {
351 if (type == "if") {
337 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
352 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
338 cx.state.cc.pop()();
353 cx.state.cc.pop()();
339 return cont(pushlex("form"), expression, statement, poplex, maybeelse);
354 return cont(pushlex("form"), expression, statement, poplex, maybeelse);
340 }
355 }
341 if (type == "function") return cont(functiondef);
356 if (type == "function") return cont(functiondef);
342 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
357 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
343 if (type == "variable") return cont(pushlex("stat"), maybelabel);
358 if (type == "variable") return cont(pushlex("stat"), maybelabel);
344 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
359 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
345 block, poplex, poplex);
360 block, poplex, poplex);
346 if (type == "case") return cont(expression, expect(":"));
361 if (type == "case") return cont(expression, expect(":"));
347 if (type == "default") return cont(expect(":"));
362 if (type == "default") return cont(expect(":"));
348 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
363 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
349 statement, poplex, popcontext);
364 statement, poplex, popcontext);
350 if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
351 if (type == "class") return cont(pushlex("form"), className, poplex);
365 if (type == "class") return cont(pushlex("form"), className, poplex);
352 if (type == "export") return cont(pushlex("form"), afterExport, poplex);
366 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
353 if (type == "import") return cont(pushlex("form"), afterImport, poplex);
367 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
368 if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
354 return pass(pushlex("stat"), expression, expect(";"), poplex);
369 return pass(pushlex("stat"), expression, expect(";"), poplex);
355 }
370 }
356 function expression(type) {
371 function expression(type) {
357 return expressionInner(type, false);
372 return expressionInner(type, false);
358 }
373 }
359 function expressionNoComma(type) {
374 function expressionNoComma(type) {
360 return expressionInner(type, true);
375 return expressionInner(type, true);
361 }
376 }
362 function expressionInner(type, noComma) {
377 function expressionInner(type, noComma) {
363 if (cx.state.fatArrowAt == cx.stream.start) {
378 if (cx.state.fatArrowAt == cx.stream.start) {
364 var body = noComma ? arrowBodyNoComma : arrowBody;
379 var body = noComma ? arrowBodyNoComma : arrowBody;
365 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
380 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
366 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
381 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
367 }
382 }
368
383
369 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
384 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
370 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
385 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
371 if (type == "function") return cont(functiondef, maybeop);
386 if (type == "function") return cont(functiondef, maybeop);
372 if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
387 if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
373 if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
388 if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
374 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
389 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
375 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
390 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
376 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
391 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
377 if (type == "quasi") { return pass(quasi, maybeop); }
392 if (type == "quasi") return pass(quasi, maybeop);
393 if (type == "new") return cont(maybeTarget(noComma));
378 return cont();
394 return cont();
379 }
395 }
380 function maybeexpression(type) {
396 function maybeexpression(type) {
381 if (type.match(/[;\}\)\],]/)) return pass();
397 if (type.match(/[;\}\)\],]/)) return pass();
382 return pass(expression);
398 return pass(expression);
383 }
399 }
384 function maybeexpressionNoComma(type) {
400 function maybeexpressionNoComma(type) {
385 if (type.match(/[;\}\)\],]/)) return pass();
401 if (type.match(/[;\}\)\],]/)) return pass();
386 return pass(expressionNoComma);
402 return pass(expressionNoComma);
387 }
403 }
388
404
389 function maybeoperatorComma(type, value) {
405 function maybeoperatorComma(type, value) {
390 if (type == ",") return cont(expression);
406 if (type == ",") return cont(expression);
391 return maybeoperatorNoComma(type, value, false);
407 return maybeoperatorNoComma(type, value, false);
392 }
408 }
393 function maybeoperatorNoComma(type, value, noComma) {
409 function maybeoperatorNoComma(type, value, noComma) {
394 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
410 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
395 var expr = noComma == false ? expression : expressionNoComma;
411 var expr = noComma == false ? expression : expressionNoComma;
396 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
412 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
397 if (type == "operator") {
413 if (type == "operator") {
398 if (/\+\+|--/.test(value)) return cont(me);
414 if (/\+\+|--/.test(value)) return cont(me);
399 if (value == "?") return cont(expression, expect(":"), expr);
415 if (value == "?") return cont(expression, expect(":"), expr);
400 return cont(expr);
416 return cont(expr);
401 }
417 }
402 if (type == "quasi") { return pass(quasi, me); }
418 if (type == "quasi") { return pass(quasi, me); }
403 if (type == ";") return;
419 if (type == ";") return;
404 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
420 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
405 if (type == ".") return cont(property, me);
421 if (type == ".") return cont(property, me);
406 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
422 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
407 }
423 }
408 function quasi(type, value) {
424 function quasi(type, value) {
409 if (type != "quasi") return pass();
425 if (type != "quasi") return pass();
410 if (value.slice(value.length - 2) != "${") return cont(quasi);
426 if (value.slice(value.length - 2) != "${") return cont(quasi);
411 return cont(expression, continueQuasi);
427 return cont(expression, continueQuasi);
412 }
428 }
413 function continueQuasi(type) {
429 function continueQuasi(type) {
414 if (type == "}") {
430 if (type == "}") {
415 cx.marked = "string-2";
431 cx.marked = "string-2";
416 cx.state.tokenize = tokenQuasi;
432 cx.state.tokenize = tokenQuasi;
417 return cont(quasi);
433 return cont(quasi);
418 }
434 }
419 }
435 }
420 function arrowBody(type) {
436 function arrowBody(type) {
421 findFatArrow(cx.stream, cx.state);
437 findFatArrow(cx.stream, cx.state);
422 return pass(type == "{" ? statement : expression);
438 return pass(type == "{" ? statement : expression);
423 }
439 }
424 function arrowBodyNoComma(type) {
440 function arrowBodyNoComma(type) {
425 findFatArrow(cx.stream, cx.state);
441 findFatArrow(cx.stream, cx.state);
426 return pass(type == "{" ? statement : expressionNoComma);
442 return pass(type == "{" ? statement : expressionNoComma);
427 }
443 }
444 function maybeTarget(noComma) {
445 return function(type) {
446 if (type == ".") return cont(noComma ? targetNoComma : target);
447 else return pass(noComma ? expressionNoComma : expression);
448 };
449 }
450 function target(_, value) {
451 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
452 }
453 function targetNoComma(_, value) {
454 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
455 }
428 function maybelabel(type) {
456 function maybelabel(type) {
429 if (type == ":") return cont(poplex, statement);
457 if (type == ":") return cont(poplex, statement);
430 return pass(maybeoperatorComma, expect(";"), poplex);
458 return pass(maybeoperatorComma, expect(";"), poplex);
431 }
459 }
432 function property(type) {
460 function property(type) {
433 if (type == "variable") {cx.marked = "property"; return cont();}
461 if (type == "variable") {cx.marked = "property"; return cont();}
434 }
462 }
435 function objprop(type, value) {
463 function objprop(type, value) {
436 if (type == "variable" || cx.style == "keyword") {
464 if (type == "variable" || cx.style == "keyword") {
437 cx.marked = "property";
465 cx.marked = "property";
438 if (value == "get" || value == "set") return cont(getterSetter);
466 if (value == "get" || value == "set") return cont(getterSetter);
439 return cont(afterprop);
467 return cont(afterprop);
440 } else if (type == "number" || type == "string") {
468 } else if (type == "number" || type == "string") {
441 cx.marked = jsonldMode ? "property" : (cx.style + " property");
469 cx.marked = jsonldMode ? "property" : (cx.style + " property");
442 return cont(afterprop);
470 return cont(afterprop);
443 } else if (type == "jsonld-keyword") {
471 } else if (type == "jsonld-keyword") {
444 return cont(afterprop);
472 return cont(afterprop);
473 } else if (type == "modifier") {
474 return cont(objprop)
445 } else if (type == "[") {
475 } else if (type == "[") {
446 return cont(expression, expect("]"), afterprop);
476 return cont(expression, expect("]"), afterprop);
477 } else if (type == "spread") {
478 return cont(expression);
447 }
479 }
448 }
480 }
449 function getterSetter(type) {
481 function getterSetter(type) {
450 if (type != "variable") return pass(afterprop);
482 if (type != "variable") return pass(afterprop);
451 cx.marked = "property";
483 cx.marked = "property";
452 return cont(functiondef);
484 return cont(functiondef);
453 }
485 }
454 function afterprop(type) {
486 function afterprop(type) {
455 if (type == ":") return cont(expressionNoComma);
487 if (type == ":") return cont(expressionNoComma);
456 if (type == "(") return pass(functiondef);
488 if (type == "(") return pass(functiondef);
457 }
489 }
458 function commasep(what, end) {
490 function commasep(what, end) {
459 function proceed(type) {
491 function proceed(type) {
460 if (type == ",") {
492 if (type == ",") {
461 var lex = cx.state.lexical;
493 var lex = cx.state.lexical;
462 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
494 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
463 return cont(what, proceed);
495 return cont(what, proceed);
464 }
496 }
465 if (type == end) return cont();
497 if (type == end) return cont();
466 return cont(expect(end));
498 return cont(expect(end));
467 }
499 }
468 return function(type) {
500 return function(type) {
469 if (type == end) return cont();
501 if (type == end) return cont();
470 return pass(what, proceed);
502 return pass(what, proceed);
471 };
503 };
472 }
504 }
473 function contCommasep(what, end, info) {
505 function contCommasep(what, end, info) {
474 for (var i = 3; i < arguments.length; i++)
506 for (var i = 3; i < arguments.length; i++)
475 cx.cc.push(arguments[i]);
507 cx.cc.push(arguments[i]);
476 return cont(pushlex(end, info), commasep(what, end), poplex);
508 return cont(pushlex(end, info), commasep(what, end), poplex);
477 }
509 }
478 function block(type) {
510 function block(type) {
479 if (type == "}") return cont();
511 if (type == "}") return cont();
480 return pass(statement, block);
512 return pass(statement, block);
481 }
513 }
482 function maybetype(type) {
514 function maybetype(type) {
483 if (isTS && type == ":") return cont(typedef);
515 if (isTS && type == ":") return cont(typedef);
484 }
516 }
485 function maybedefault(_, value) {
517 function maybedefault(_, value) {
486 if (value == "=") return cont(expressionNoComma);
518 if (value == "=") return cont(expressionNoComma);
487 }
519 }
488 function typedef(type) {
520 function typedef(type) {
489 if (type == "variable") {cx.marked = "variable-3"; return cont();}
521 if (type == "variable") {cx.marked = "variable-3"; return cont();}
490 }
522 }
491 function vardef() {
523 function vardef() {
492 return pass(pattern, maybetype, maybeAssign, vardefCont);
524 return pass(pattern, maybetype, maybeAssign, vardefCont);
493 }
525 }
494 function pattern(type, value) {
526 function pattern(type, value) {
527 if (type == "modifier") return cont(pattern)
495 if (type == "variable") { register(value); return cont(); }
528 if (type == "variable") { register(value); return cont(); }
529 if (type == "spread") return cont(pattern);
496 if (type == "[") return contCommasep(pattern, "]");
530 if (type == "[") return contCommasep(pattern, "]");
497 if (type == "{") return contCommasep(proppattern, "}");
531 if (type == "{") return contCommasep(proppattern, "}");
498 }
532 }
499 function proppattern(type, value) {
533 function proppattern(type, value) {
500 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
534 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
501 register(value);
535 register(value);
502 return cont(maybeAssign);
536 return cont(maybeAssign);
503 }
537 }
504 if (type == "variable") cx.marked = "property";
538 if (type == "variable") cx.marked = "property";
539 if (type == "spread") return cont(pattern);
540 if (type == "}") return pass();
505 return cont(expect(":"), pattern, maybeAssign);
541 return cont(expect(":"), pattern, maybeAssign);
506 }
542 }
507 function maybeAssign(_type, value) {
543 function maybeAssign(_type, value) {
508 if (value == "=") return cont(expressionNoComma);
544 if (value == "=") return cont(expressionNoComma);
509 }
545 }
510 function vardefCont(type) {
546 function vardefCont(type) {
511 if (type == ",") return cont(vardef);
547 if (type == ",") return cont(vardef);
512 }
548 }
513 function maybeelse(type, value) {
549 function maybeelse(type, value) {
514 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
550 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
515 }
551 }
516 function forspec(type) {
552 function forspec(type) {
517 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
553 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
518 }
554 }
519 function forspec1(type) {
555 function forspec1(type) {
520 if (type == "var") return cont(vardef, expect(";"), forspec2);
556 if (type == "var") return cont(vardef, expect(";"), forspec2);
521 if (type == ";") return cont(forspec2);
557 if (type == ";") return cont(forspec2);
522 if (type == "variable") return cont(formaybeinof);
558 if (type == "variable") return cont(formaybeinof);
523 return pass(expression, expect(";"), forspec2);
559 return pass(expression, expect(";"), forspec2);
524 }
560 }
525 function formaybeinof(_type, value) {
561 function formaybeinof(_type, value) {
526 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
562 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
527 return cont(maybeoperatorComma, forspec2);
563 return cont(maybeoperatorComma, forspec2);
528 }
564 }
529 function forspec2(type, value) {
565 function forspec2(type, value) {
530 if (type == ";") return cont(forspec3);
566 if (type == ";") return cont(forspec3);
531 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
567 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
532 return pass(expression, expect(";"), forspec3);
568 return pass(expression, expect(";"), forspec3);
533 }
569 }
534 function forspec3(type) {
570 function forspec3(type) {
535 if (type != ")") cont(expression);
571 if (type != ")") cont(expression);
536 }
572 }
537 function functiondef(type, value) {
573 function functiondef(type, value) {
538 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
574 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
539 if (type == "variable") {register(value); return cont(functiondef);}
575 if (type == "variable") {register(value); return cont(functiondef);}
540 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
576 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
541 }
577 }
542 function funarg(type) {
578 function funarg(type) {
543 if (type == "spread") return cont(funarg);
579 if (type == "spread") return cont(funarg);
544 return pass(pattern, maybetype, maybedefault);
580 return pass(pattern, maybetype, maybedefault);
545 }
581 }
546 function className(type, value) {
582 function className(type, value) {
547 if (type == "variable") {register(value); return cont(classNameAfter);}
583 if (type == "variable") {register(value); return cont(classNameAfter);}
548 }
584 }
549 function classNameAfter(type, value) {
585 function classNameAfter(type, value) {
550 if (value == "extends") return cont(expression, classNameAfter);
586 if (value == "extends") return cont(expression, classNameAfter);
551 if (type == "{") return cont(pushlex("}"), classBody, poplex);
587 if (type == "{") return cont(pushlex("}"), classBody, poplex);
552 }
588 }
553 function classBody(type, value) {
589 function classBody(type, value) {
554 if (type == "variable" || cx.style == "keyword") {
590 if (type == "variable" || cx.style == "keyword") {
555 if (value == "static") {
591 if (value == "static") {
556 cx.marked = "keyword";
592 cx.marked = "keyword";
557 return cont(classBody);
593 return cont(classBody);
558 }
594 }
559 cx.marked = "property";
595 cx.marked = "property";
560 if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
596 if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
561 return cont(functiondef, classBody);
597 return cont(functiondef, classBody);
562 }
598 }
563 if (value == "*") {
599 if (value == "*") {
564 cx.marked = "keyword";
600 cx.marked = "keyword";
565 return cont(classBody);
601 return cont(classBody);
566 }
602 }
567 if (type == ";") return cont(classBody);
603 if (type == ";") return cont(classBody);
568 if (type == "}") return cont();
604 if (type == "}") return cont();
569 }
605 }
570 function classGetterSetter(type) {
606 function classGetterSetter(type) {
571 if (type != "variable") return pass();
607 if (type != "variable") return pass();
572 cx.marked = "property";
608 cx.marked = "property";
573 return cont();
609 return cont();
574 }
610 }
575 function afterModule(type, value) {
576 if (type == "string") return cont(statement);
577 if (type == "variable") { register(value); return cont(maybeFrom); }
578 }
579 function afterExport(_type, value) {
611 function afterExport(_type, value) {
580 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
612 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
581 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
613 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
582 return pass(statement);
614 return pass(statement);
583 }
615 }
584 function afterImport(type) {
616 function afterImport(type) {
585 if (type == "string") return cont();
617 if (type == "string") return cont();
586 return pass(importSpec, maybeFrom);
618 return pass(importSpec, maybeFrom);
587 }
619 }
588 function importSpec(type, value) {
620 function importSpec(type, value) {
589 if (type == "{") return contCommasep(importSpec, "}");
621 if (type == "{") return contCommasep(importSpec, "}");
590 if (type == "variable") register(value);
622 if (type == "variable") register(value);
591 if (value == "*") cx.marked = "keyword";
623 if (value == "*") cx.marked = "keyword";
592 return cont(maybeAs);
624 return cont(maybeAs);
593 }
625 }
594 function maybeAs(_type, value) {
626 function maybeAs(_type, value) {
595 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
627 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
596 }
628 }
597 function maybeFrom(_type, value) {
629 function maybeFrom(_type, value) {
598 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
630 if (value == "from") { cx.marked = "keyword"; return cont(expression); }
599 }
631 }
600 function arrayLiteral(type) {
632 function arrayLiteral(type) {
601 if (type == "]") return cont();
633 if (type == "]") return cont();
602 return pass(expressionNoComma, maybeArrayComprehension);
634 return pass(expressionNoComma, maybeArrayComprehension);
603 }
635 }
604 function maybeArrayComprehension(type) {
636 function maybeArrayComprehension(type) {
605 if (type == "for") return pass(comprehension, expect("]"));
637 if (type == "for") return pass(comprehension, expect("]"));
606 if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
638 if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
607 return pass(commasep(expressionNoComma, "]"));
639 return pass(commasep(expressionNoComma, "]"));
608 }
640 }
609 function comprehension(type) {
641 function comprehension(type) {
610 if (type == "for") return cont(forspec, comprehension);
642 if (type == "for") return cont(forspec, comprehension);
611 if (type == "if") return cont(expression, comprehension);
643 if (type == "if") return cont(expression, comprehension);
612 }
644 }
613
645
614 function isContinuedStatement(state, textAfter) {
646 function isContinuedStatement(state, textAfter) {
615 return state.lastType == "operator" || state.lastType == "," ||
647 return state.lastType == "operator" || state.lastType == "," ||
616 isOperatorChar.test(textAfter.charAt(0)) ||
648 isOperatorChar.test(textAfter.charAt(0)) ||
617 /[,.]/.test(textAfter.charAt(0));
649 /[,.]/.test(textAfter.charAt(0));
618 }
650 }
619
651
620 // Interface
652 // Interface
621
653
622 return {
654 return {
623 startState: function(basecolumn) {
655 startState: function(basecolumn) {
624 var state = {
656 var state = {
625 tokenize: tokenBase,
657 tokenize: tokenBase,
626 lastType: "sof",
658 lastType: "sof",
627 cc: [],
659 cc: [],
628 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
660 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
629 localVars: parserConfig.localVars,
661 localVars: parserConfig.localVars,
630 context: parserConfig.localVars && {vars: parserConfig.localVars},
662 context: parserConfig.localVars && {vars: parserConfig.localVars},
631 indented: 0
663 indented: basecolumn || 0
632 };
664 };
633 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
665 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
634 state.globalVars = parserConfig.globalVars;
666 state.globalVars = parserConfig.globalVars;
635 return state;
667 return state;
636 },
668 },
637
669
638 token: function(stream, state) {
670 token: function(stream, state) {
639 if (stream.sol()) {
671 if (stream.sol()) {
640 if (!state.lexical.hasOwnProperty("align"))
672 if (!state.lexical.hasOwnProperty("align"))
641 state.lexical.align = false;
673 state.lexical.align = false;
642 state.indented = stream.indentation();
674 state.indented = stream.indentation();
643 findFatArrow(stream, state);
675 findFatArrow(stream, state);
644 }
676 }
645 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
677 if (state.tokenize != tokenComment && stream.eatSpace()) return null;
646 var style = state.tokenize(stream, state);
678 var style = state.tokenize(stream, state);
647 if (type == "comment") return style;
679 if (type == "comment") return style;
648 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
680 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
649 return parseJS(state, style, type, content, stream);
681 return parseJS(state, style, type, content, stream);
650 },
682 },
651
683
652 indent: function(state, textAfter) {
684 indent: function(state, textAfter) {
653 if (state.tokenize == tokenComment) return CodeMirror.Pass;
685 if (state.tokenize == tokenComment) return CodeMirror.Pass;
654 if (state.tokenize != tokenBase) return 0;
686 if (state.tokenize != tokenBase) return 0;
655 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
687 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
656 // Kludge to prevent 'maybelse' from blocking lexical scope pops
688 // Kludge to prevent 'maybelse' from blocking lexical scope pops
657 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
689 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
658 var c = state.cc[i];
690 var c = state.cc[i];
659 if (c == poplex) lexical = lexical.prev;
691 if (c == poplex) lexical = lexical.prev;
660 else if (c != maybeelse) break;
692 else if (c != maybeelse) break;
661 }
693 }
662 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
694 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
663 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
695 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
664 lexical = lexical.prev;
696 lexical = lexical.prev;
665 var type = lexical.type, closing = firstChar == type;
697 var type = lexical.type, closing = firstChar == type;
666
698
667 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
699 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
668 else if (type == "form" && firstChar == "{") return lexical.indented;
700 else if (type == "form" && firstChar == "{") return lexical.indented;
669 else if (type == "form") return lexical.indented + indentUnit;
701 else if (type == "form") return lexical.indented + indentUnit;
670 else if (type == "stat")
702 else if (type == "stat")
671 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
703 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
672 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
704 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
673 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
705 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
674 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
706 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
675 else return lexical.indented + (closing ? 0 : indentUnit);
707 else return lexical.indented + (closing ? 0 : indentUnit);
676 },
708 },
677
709
678 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
710 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
679 blockCommentStart: jsonMode ? null : "/*",
711 blockCommentStart: jsonMode ? null : "/*",
680 blockCommentEnd: jsonMode ? null : "*/",
712 blockCommentEnd: jsonMode ? null : "*/",
681 lineComment: jsonMode ? null : "//",
713 lineComment: jsonMode ? null : "//",
682 fold: "brace",
714 fold: "brace",
683 closeBrackets: "()[]{}''\"\"``",
715 closeBrackets: "()[]{}''\"\"``",
684
716
685 helperType: jsonMode ? "json" : "javascript",
717 helperType: jsonMode ? "json" : "javascript",
686 jsonldMode: jsonldMode,
718 jsonldMode: jsonldMode,
687 jsonMode: jsonMode
719 jsonMode: jsonMode,
720
721 expressionAllowed: expressionAllowed,
722 skipExpression: function(state) {
723 var top = state.cc[state.cc.length - 1]
724 if (top == expression || top == expressionNoComma) state.cc.pop()
725 }
688 };
726 };
689 });
727 });
690
728
691 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
729 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
692
730
693 CodeMirror.defineMIME("text/javascript", "javascript");
731 CodeMirror.defineMIME("text/javascript", "javascript");
694 CodeMirror.defineMIME("text/ecmascript", "javascript");
732 CodeMirror.defineMIME("text/ecmascript", "javascript");
695 CodeMirror.defineMIME("application/javascript", "javascript");
733 CodeMirror.defineMIME("application/javascript", "javascript");
696 CodeMirror.defineMIME("application/x-javascript", "javascript");
734 CodeMirror.defineMIME("application/x-javascript", "javascript");
697 CodeMirror.defineMIME("application/ecmascript", "javascript");
735 CodeMirror.defineMIME("application/ecmascript", "javascript");
698 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
736 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
699 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
737 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
700 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
738 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
701 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
739 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
702 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
740 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
703
741
704 });
742 });
@@ -1,299 +1,364 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("julia", function(_conf, parserConf) {
14 CodeMirror.defineMode("julia", function(_conf, parserConf) {
15 var ERRORCLASS = 'error';
15 var ERRORCLASS = 'error';
16
16
17 function wordRegexp(words) {
17 function wordRegexp(words) {
18 return new RegExp("^((" + words.join(")|(") + "))\\b");
18 return new RegExp("^((" + words.join(")|(") + "))\\b");
19 }
19 }
20
20
21 var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/;
21 var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/;
22 var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
22 var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
23 var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
23 var identifiers = parserConf.identifiers || /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
24 var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
24 var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
25 var blockClosers = ["end", "else", "elseif", "catch", "finally"];
25 var blockClosers = ["end", "else", "elseif", "catch", "finally"];
26 var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall'];
26 var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype'];
27 var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];
27 var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf'];
28
28
29 //var stringPrefixes = new RegExp("^[br]?('|\")")
29 //var stringPrefixes = new RegExp("^[br]?('|\")")
30 var stringPrefixes = /^(`|'|"{3}|([br]?"))/;
30 var stringPrefixes = /^(`|'|"{3}|([brv]?"))/;
31 var keywords = wordRegexp(keywordList);
31 var keywords = wordRegexp(keywordList);
32 var builtins = wordRegexp(builtinList);
32 var builtins = wordRegexp(builtinList);
33 var openers = wordRegexp(blockOpeners);
33 var openers = wordRegexp(blockOpeners);
34 var closers = wordRegexp(blockClosers);
34 var closers = wordRegexp(blockClosers);
35 var macro = /^@[_A-Za-z][_A-Za-z0-9]*/;
35 var macro = /^@[_A-Za-z][_A-Za-z0-9]*/;
36 var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/;
36 var symbol = /^:[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
37 var typeAnnotation = /^::[^.,;"{()=$\s]+({[^}]*}+)*/;
37
38
38 function in_array(state) {
39 function inArray(state) {
39 var ch = cur_scope(state);
40 var ch = currentScope(state);
40 if(ch=="[" || ch=="{") {
41 if (ch == '[') {
41 return true;
42 return true;
42 }
43 }
43 else {
44 return false;
44 return false;
45 }
46 }
45 }
47
46
48 function cur_scope(state) {
47 function currentScope(state) {
49 if(state.scopes.length==0) {
48 if (state.scopes.length == 0) {
50 return null;
49 return null;
51 }
50 }
52 return state.scopes[state.scopes.length - 1];
51 return state.scopes[state.scopes.length - 1];
53 }
52 }
54
53
55 // tokenizers
54 // tokenizers
56 function tokenBase(stream, state) {
55 function tokenBase(stream, state) {
56 //Handle multiline comments
57 if (stream.match(/^#=\s*/)) {
58 state.scopes.push('#=');
59 }
60 if (currentScope(state) == '#=' && stream.match(/^=#/)) {
61 state.scopes.pop();
62 return 'comment';
63 }
64 if (state.scopes.indexOf('#=') >= 0) {
65 if (!stream.match(/.*?(?=(#=|=#))/)) {
66 stream.skipToEnd();
67 }
68 return 'comment';
69 }
70
57 // Handle scope changes
71 // Handle scope changes
58 var leaving_expr = state.leaving_expr;
72 var leavingExpr = state.leavingExpr;
59 if(stream.sol()) {
73 if (stream.sol()) {
60 leaving_expr = false;
74 leavingExpr = false;
61 }
75 }
62 state.leaving_expr = false;
76 state.leavingExpr = false;
63 if(leaving_expr) {
77 if (leavingExpr) {
64 if(stream.match(/^'+/)) {
78 if (stream.match(/^'+/)) {
65 return 'operator';
79 return 'operator';
66 }
80 }
67
68 }
81 }
69
82
70 if(stream.match(/^\.{2,3}/)) {
83 if (stream.match(/^\.{2,3}/)) {
71 return 'operator';
84 return 'operator';
72 }
85 }
73
86
74 if (stream.eatSpace()) {
87 if (stream.eatSpace()) {
75 return null;
88 return null;
76 }
89 }
77
90
78 var ch = stream.peek();
91 var ch = stream.peek();
79 // Handle Comments
92
93 // Handle single line comments
80 if (ch === '#') {
94 if (ch === '#') {
81 stream.skipToEnd();
95 stream.skipToEnd();
82 return 'comment';
96 return 'comment';
83 }
84 if(ch==='[') {
85 state.scopes.push("[");
86 }
97 }
87
98
88 if(ch==='{') {
99 if (ch === '[') {
89 state.scopes.push("{");
100 state.scopes.push('[');
90 }
101 }
91
102
92 var scope=cur_scope(state);
103 var scope = currentScope(state);
93
104
94 if(scope==='[' && ch===']') {
105 if (scope == '[' && ch === ']') {
95 state.scopes.pop();
106 state.scopes.pop();
96 state.leaving_expr=true;
107 state.leavingExpr = true;
97 }
108 }
98
109
99 if(scope==='{' && ch==='}') {
110 if (scope == '(' && ch === ')') {
100 state.scopes.pop();
111 state.scopes.pop();
101 state.leaving_expr=true;
112 state.leavingExpr = true;
102 }
103
104 if(ch===')') {
105 state.leaving_expr = true;
106 }
113 }
107
114
108 var match;
115 var match;
109 if(!in_array(state) && (match=stream.match(openers, false))) {
116 if (!inArray(state) && (match=stream.match(openers, false))) {
110 state.scopes.push(match);
117 state.scopes.push(match);
111 }
118 }
112
119
113 if(!in_array(state) && stream.match(closers, false)) {
120 if (!inArray(state) && stream.match(closers, false)) {
114 state.scopes.pop();
121 state.scopes.pop();
115 }
122 }
116
123
117 if(in_array(state)) {
124 if (inArray(state)) {
118 if(stream.match(/^end/)) {
125 if (state.lastToken == 'end' && stream.match(/^:/)) {
126 return 'operator';
127 }
128 if (stream.match(/^end/)) {
119 return 'number';
129 return 'number';
120 }
130 }
121
122 }
131 }
123
132
124 if(stream.match(/^=>/)) {
133 if (stream.match(/^=>/)) {
125 return 'operator';
134 return 'operator';
126 }
135 }
127
136
128
129 // Handle Number Literals
137 // Handle Number Literals
130 if (stream.match(/^[0-9\.]/, false)) {
138 if (stream.match(/^[0-9\.]/, false)) {
131 var imMatcher = RegExp(/^im\b/);
139 var imMatcher = RegExp(/^im\b/);
132 var floatLiteral = false;
140 var floatLiteral = false;
133 // Floats
141 // Floats
134 if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
142 if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
135 if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
143 if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
136 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
144 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
145 if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { floatLiteral = true; }
137 if (floatLiteral) {
146 if (floatLiteral) {
138 // Float literals may be "imaginary"
147 // Float literals may be "imaginary"
139 stream.match(imMatcher);
148 stream.match(imMatcher);
140 state.leaving_expr = true;
149 state.leavingExpr = true;
141 return 'number';
150 return 'number';
142 }
151 }
143 // Integers
152 // Integers
144 var intLiteral = false;
153 var intLiteral = false;
145 // Hex
154 // Hex
146 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
155 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
147 // Binary
156 // Binary
148 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
157 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
149 // Octal
158 // Octal
150 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
159 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
151 // Decimal
160 // Decimal
152 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
161 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
153 intLiteral = true;
162 intLiteral = true;
154 }
163 }
155 // Zero by itself with no other piece of number.
164 // Zero by itself with no other piece of number.
156 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
165 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
157 if (intLiteral) {
166 if (intLiteral) {
158 // Integer literals may be "long"
167 // Integer literals may be "long"
159 stream.match(imMatcher);
168 stream.match(imMatcher);
160 state.leaving_expr = true;
169 state.leavingExpr = true;
161 return 'number';
170 return 'number';
162 }
171 }
163 }
172 }
164
173
165 if(stream.match(/^(::)|(<:)/)) {
174 if (stream.match(/^<:/)) {
166 return 'operator';
175 return 'operator';
167 }
176 }
168
177
178 if (stream.match(typeAnnotation)) {
179 return 'builtin';
180 }
181
169 // Handle symbols
182 // Handle symbols
170 if(!leaving_expr && stream.match(symbol)) {
183 if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) {
171 return 'string';
184 return 'builtin';
185 }
186
187 // Handle parametric types
188 if (stream.match(/^{[^}]*}(?=\()/)) {
189 return 'builtin';
172 }
190 }
173
191
174 // Handle operators and Delimiters
192 // Handle operators and Delimiters
175 if (stream.match(operators)) {
193 if (stream.match(operators)) {
176 return 'operator';
194 return 'operator';
177 }
195 }
178
196
179
180 // Handle Strings
197 // Handle Strings
181 if (stream.match(stringPrefixes)) {
198 if (stream.match(stringPrefixes)) {
182 state.tokenize = tokenStringFactory(stream.current());
199 state.tokenize = tokenStringFactory(stream.current());
183 return state.tokenize(stream, state);
200 return state.tokenize(stream, state);
184 }
201 }
185
202
186 if (stream.match(macro)) {
203 if (stream.match(macro)) {
187 return 'meta';
204 return 'meta';
188 }
205 }
189
206
190
191 if (stream.match(delimiters)) {
207 if (stream.match(delimiters)) {
192 return null;
208 return null;
193 }
209 }
194
210
195 if (stream.match(keywords)) {
211 if (stream.match(keywords)) {
196 return 'keyword';
212 return 'keyword';
197 }
213 }
198
214
199 if (stream.match(builtins)) {
215 if (stream.match(builtins)) {
200 return 'builtin';
216 return 'builtin';
201 }
217 }
202
218
219 var isDefinition = state.isDefinition ||
220 state.lastToken == 'function' ||
221 state.lastToken == 'macro' ||
222 state.lastToken == 'type' ||
223 state.lastToken == 'immutable';
203
224
204 if (stream.match(identifiers)) {
225 if (stream.match(identifiers)) {
205 state.leaving_expr=true;
226 if (isDefinition) {
227 if (stream.peek() === '.') {
228 state.isDefinition = true;
229 return 'variable';
230 }
231 state.isDefinition = false;
232 return 'def';
233 }
234 if (stream.match(/^({[^}]*})*\(/, false)) {
235 return callOrDef(stream, state);
236 }
237 state.leavingExpr = true;
206 return 'variable';
238 return 'variable';
207 }
239 }
240
208 // Handle non-detected items
241 // Handle non-detected items
209 stream.next();
242 stream.next();
210 return ERRORCLASS;
243 return ERRORCLASS;
211 }
244 }
212
245
246 function callOrDef(stream, state) {
247 var match = stream.match(/^(\(\s*)/);
248 if (match) {
249 if (state.firstParenPos < 0)
250 state.firstParenPos = state.scopes.length;
251 state.scopes.push('(');
252 state.charsAdvanced += match[1].length;
253 }
254 if (currentScope(state) == '(' && stream.match(/^\)/)) {
255 state.scopes.pop();
256 state.charsAdvanced += 1;
257 if (state.scopes.length <= state.firstParenPos) {
258 var isDefinition = stream.match(/^\s*?=(?!=)/, false);
259 stream.backUp(state.charsAdvanced);
260 state.firstParenPos = -1;
261 state.charsAdvanced = 0;
262 if (isDefinition)
263 return 'def';
264 return 'builtin';
265 }
266 }
267 // Unfortunately javascript does not support multiline strings, so we have
268 // to undo anything done upto here if a function call or definition splits
269 // over two or more lines.
270 if (stream.match(/^$/g, false)) {
271 stream.backUp(state.charsAdvanced);
272 while (state.scopes.length > state.firstParenPos + 1)
273 state.scopes.pop();
274 state.firstParenPos = -1;
275 state.charsAdvanced = 0;
276 return 'builtin';
277 }
278 state.charsAdvanced += stream.match(/^([^()]*)/)[1].length;
279 return callOrDef(stream, state);
280 }
281
213 function tokenStringFactory(delimiter) {
282 function tokenStringFactory(delimiter) {
214 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
283 while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
215 delimiter = delimiter.substr(1);
284 delimiter = delimiter.substr(1);
216 }
285 }
217 var singleline = delimiter.length == 1;
286 var singleline = delimiter == "'";
218 var OUTCLASS = 'string';
287 var OUTCLASS = 'string';
219
288
220 function tokenString(stream, state) {
289 function tokenString(stream, state) {
221 while (!stream.eol()) {
290 while (!stream.eol()) {
222 stream.eatWhile(/[^'"\\]/);
291 stream.eatWhile(/[^'"\\]/);
223 if (stream.eat('\\')) {
292 if (stream.eat('\\')) {
224 stream.next();
293 stream.next();
225 if (singleline && stream.eol()) {
294 if (singleline && stream.eol()) {
226 return OUTCLASS;
295 return OUTCLASS;
227 }
296 }
228 } else if (stream.match(delimiter)) {
297 } else if (stream.match(delimiter)) {
229 state.tokenize = tokenBase;
298 state.tokenize = tokenBase;
230 return OUTCLASS;
299 return OUTCLASS;
231 } else {
300 } else {
232 stream.eat(/['"]/);
301 stream.eat(/['"]/);
233 }
302 }
234 }
303 }
235 if (singleline) {
304 if (singleline) {
236 if (parserConf.singleLineStringErrors) {
305 if (parserConf.singleLineStringErrors) {
237 return ERRORCLASS;
306 return ERRORCLASS;
238 } else {
307 } else {
239 state.tokenize = tokenBase;
308 state.tokenize = tokenBase;
240 }
309 }
241 }
310 }
242 return OUTCLASS;
311 return OUTCLASS;
243 }
312 }
244 tokenString.isString = true;
313 tokenString.isString = true;
245 return tokenString;
314 return tokenString;
246 }
315 }
247
316
248 function tokenLexer(stream, state) {
249 var style = state.tokenize(stream, state);
250 var current = stream.current();
251
252 // Handle '.' connected identifiers
253 if (current === '.') {
254 style = stream.match(identifiers, false) ? null : ERRORCLASS;
255 if (style === null && state.lastStyle === 'meta') {
256 // Apply 'meta' style to '.' connected identifiers when
257 // appropriate.
258 style = 'meta';
259 }
260 return style;
261 }
262
263 return style;
264 }
265
266 var external = {
317 var external = {
267 startState: function() {
318 startState: function() {
268 return {
319 return {
269 tokenize: tokenBase,
320 tokenize: tokenBase,
270 scopes: [],
321 scopes: [],
271 leaving_expr: false
322 lastToken: null,
323 leavingExpr: false,
324 isDefinition: false,
325 charsAdvanced: 0,
326 firstParenPos: -1
272 };
327 };
273 },
328 },
274
329
275 token: function(stream, state) {
330 token: function(stream, state) {
276 var style = tokenLexer(stream, state);
331 var style = state.tokenize(stream, state);
277 state.lastStyle = style;
332 var current = stream.current();
333
334 if (current && style) {
335 state.lastToken = current;
336 }
337
338 // Handle '.' connected identifiers
339 if (current === '.') {
340 style = stream.match(identifiers, false) || stream.match(macro, false) ||
341 stream.match(/\(/, false) ? 'operator' : ERRORCLASS;
342 }
278 return style;
343 return style;
279 },
344 },
280
345
281 indent: function(state, textAfter) {
346 indent: function(state, textAfter) {
282 var delta = 0;
347 var delta = 0;
283 if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") {
348 if (textAfter == "end" || textAfter == "]" || textAfter == "}" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") {
284 delta = -1;
349 delta = -1;
285 }
350 }
286 return (state.scopes.length + delta) * 4;
351 return (state.scopes.length + delta) * _conf.indentUnit;
287 },
352 },
288
353
289 lineComment: "#",
354 lineComment: "#",
290 fold: "indent",
355 fold: "indent",
291 electricChars: "edlsifyh]}"
356 electricChars: "edlsifyh]}"
292 };
357 };
293 return external;
358 return external;
294 });
359 });
295
360
296
361
297 CodeMirror.defineMIME("text/x-julia", "julia");
362 CodeMirror.defineMIME("text/x-julia", "julia");
298
363
299 });
364 });
@@ -1,781 +1,803 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
8 define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
14 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
15
15
16 var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
16 var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
17 var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
17 var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
18
18
19 function getMode(name) {
19 function getMode(name) {
20 if (CodeMirror.findModeByName) {
20 if (CodeMirror.findModeByName) {
21 var found = CodeMirror.findModeByName(name);
21 var found = CodeMirror.findModeByName(name);
22 if (found) name = found.mime || found.mimes[0];
22 if (found) name = found.mime || found.mimes[0];
23 }
23 }
24 var mode = CodeMirror.getMode(cmCfg, name);
24 var mode = CodeMirror.getMode(cmCfg, name);
25 return mode.name == "null" ? null : mode;
25 return mode.name == "null" ? null : mode;
26 }
26 }
27
27
28 // Should characters that affect highlighting be highlighted separate?
28 // Should characters that affect highlighting be highlighted separate?
29 // Does not include characters that will be output (such as `1.` and `-` for lists)
29 // Does not include characters that will be output (such as `1.` and `-` for lists)
30 if (modeCfg.highlightFormatting === undefined)
30 if (modeCfg.highlightFormatting === undefined)
31 modeCfg.highlightFormatting = false;
31 modeCfg.highlightFormatting = false;
32
32
33 // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
33 // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
34 // Excess `>` will emit `error` token.
34 // Excess `>` will emit `error` token.
35 if (modeCfg.maxBlockquoteDepth === undefined)
35 if (modeCfg.maxBlockquoteDepth === undefined)
36 modeCfg.maxBlockquoteDepth = 0;
36 modeCfg.maxBlockquoteDepth = 0;
37
37
38 // Should underscores in words open/close em/strong?
38 // Should underscores in words open/close em/strong?
39 if (modeCfg.underscoresBreakWords === undefined)
39 if (modeCfg.underscoresBreakWords === undefined)
40 modeCfg.underscoresBreakWords = true;
40 modeCfg.underscoresBreakWords = true;
41
41
42 // Turn on fenced code blocks? ("```" to start/end)
42 // Use `fencedCodeBlocks` to configure fenced code blocks. false to
43 if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
43 // disable, string to specify a precise regexp that the fence should
44 // match, and true to allow three or more backticks or tildes (as
45 // per CommonMark).
44
46
45 // Turn on task lists? ("- [ ] " and "- [x] ")
47 // Turn on task lists? ("- [ ] " and "- [x] ")
46 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
48 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
47
49
48 // Turn on strikethrough syntax
50 // Turn on strikethrough syntax
49 if (modeCfg.strikethrough === undefined)
51 if (modeCfg.strikethrough === undefined)
50 modeCfg.strikethrough = false;
52 modeCfg.strikethrough = false;
51
53
54 // Allow token types to be overridden by user-provided token types.
55 if (modeCfg.tokenTypeOverrides === undefined)
56 modeCfg.tokenTypeOverrides = {};
57
52 var codeDepth = 0;
58 var codeDepth = 0;
53
59
54 var header = 'header'
60 var tokenTypes = {
55 , code = 'comment'
61 header: "header",
56 , quote = 'quote'
62 code: "comment",
57 , list1 = 'variable-2'
63 quote: "quote",
58 , list2 = 'variable-3'
64 list1: "variable-2",
59 , list3 = 'keyword'
65 list2: "variable-3",
60 , hr = 'hr'
66 list3: "keyword",
61 , image = 'tag'
67 hr: "hr",
62 , formatting = 'formatting'
68 image: "tag",
63 , linkinline = 'link'
69 formatting: "formatting",
64 , linkemail = 'link'
70 linkInline: "link",
65 , linktext = 'link'
71 linkEmail: "link",
66 , linkhref = 'string'
72 linkText: "link",
67 , em = 'em'
73 linkHref: "string",
68 , strong = 'strong'
74 em: "em",
69 , strikethrough = 'strikethrough';
75 strong: "strong",
76 strikethrough: "strikethrough"
77 };
78
79 for (var tokenType in tokenTypes) {
80 if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
81 tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
82 }
83 }
70
84
71 var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
85 var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
72 , ulRE = /^[*\-+]\s+/
86 , ulRE = /^[*\-+]\s+/
73 , olRE = /^[0-9]+([.)])\s+/
87 , olRE = /^[0-9]+([.)])\s+/
74 , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
88 , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
75 , atxHeaderRE = /^(#+)(?: |$)/
89 , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
76 , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
90 , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
77 , textRE = /^[^#!\[\]*_\\<>` "'(~]+/;
91 , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
92 , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
93 ")[ \\t]*([\\w+#]*)");
78
94
79 function switchInline(stream, state, f) {
95 function switchInline(stream, state, f) {
80 state.f = state.inline = f;
96 state.f = state.inline = f;
81 return f(stream, state);
97 return f(stream, state);
82 }
98 }
83
99
84 function switchBlock(stream, state, f) {
100 function switchBlock(stream, state, f) {
85 state.f = state.block = f;
101 state.f = state.block = f;
86 return f(stream, state);
102 return f(stream, state);
87 }
103 }
88
104
105 function lineIsEmpty(line) {
106 return !line || !/\S/.test(line.string)
107 }
89
108
90 // Blocks
109 // Blocks
91
110
92 function blankLine(state) {
111 function blankLine(state) {
93 // Reset linkTitle state
112 // Reset linkTitle state
94 state.linkTitle = false;
113 state.linkTitle = false;
95 // Reset EM state
114 // Reset EM state
96 state.em = false;
115 state.em = false;
97 // Reset STRONG state
116 // Reset STRONG state
98 state.strong = false;
117 state.strong = false;
99 // Reset strikethrough state
118 // Reset strikethrough state
100 state.strikethrough = false;
119 state.strikethrough = false;
101 // Reset state.quote
120 // Reset state.quote
102 state.quote = 0;
121 state.quote = 0;
103 // Reset state.indentedCode
122 // Reset state.indentedCode
104 state.indentedCode = false;
123 state.indentedCode = false;
105 if (!htmlFound && state.f == htmlBlock) {
124 if (!htmlFound && state.f == htmlBlock) {
106 state.f = inlineNormal;
125 state.f = inlineNormal;
107 state.block = blockNormal;
126 state.block = blockNormal;
108 }
127 }
109 // Reset state.trailingSpace
128 // Reset state.trailingSpace
110 state.trailingSpace = 0;
129 state.trailingSpace = 0;
111 state.trailingSpaceNewLine = false;
130 state.trailingSpaceNewLine = false;
112 // Mark this line as blank
131 // Mark this line as blank
113 state.thisLineHasContent = false;
132 state.prevLine = state.thisLine
133 state.thisLine = null
114 return null;
134 return null;
115 }
135 }
116
136
117 function blockNormal(stream, state) {
137 function blockNormal(stream, state) {
118
138
119 var sol = stream.sol();
139 var sol = stream.sol();
120
140
121 var prevLineIsList = state.list !== false,
141 var prevLineIsList = state.list !== false,
122 prevLineIsIndentedCode = state.indentedCode;
142 prevLineIsIndentedCode = state.indentedCode;
123
143
124 state.indentedCode = false;
144 state.indentedCode = false;
125
145
126 if (prevLineIsList) {
146 if (prevLineIsList) {
127 if (state.indentationDiff >= 0) { // Continued list
147 if (state.indentationDiff >= 0) { // Continued list
128 if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
148 if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
129 state.indentation -= state.indentationDiff;
149 state.indentation -= state.indentationDiff;
130 }
150 }
131 state.list = null;
151 state.list = null;
132 } else if (state.indentation > 0) {
152 } else if (state.indentation > 0) {
133 state.list = null;
153 state.list = null;
134 state.listDepth = Math.floor(state.indentation / 4);
154 state.listDepth = Math.floor(state.indentation / 4);
135 } else { // No longer a list
155 } else { // No longer a list
136 state.list = false;
156 state.list = false;
137 state.listDepth = 0;
157 state.listDepth = 0;
138 }
158 }
139 }
159 }
140
160
141 var match = null;
161 var match = null;
142 if (state.indentationDiff >= 4) {
162 if (state.indentationDiff >= 4) {
143 stream.skipToEnd();
163 stream.skipToEnd();
144 if (prevLineIsIndentedCode || !state.prevLineHasContent) {
164 if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
145 state.indentation -= 4;
165 state.indentation -= 4;
146 state.indentedCode = true;
166 state.indentedCode = true;
147 return code;
167 return tokenTypes.code;
148 } else {
168 } else {
149 return null;
169 return null;
150 }
170 }
151 } else if (stream.eatSpace()) {
171 } else if (stream.eatSpace()) {
152 return null;
172 return null;
153 } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
173 } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
154 state.header = match[1].length;
174 state.header = match[1].length;
155 if (modeCfg.highlightFormatting) state.formatting = "header";
175 if (modeCfg.highlightFormatting) state.formatting = "header";
156 state.f = state.inline;
176 state.f = state.inline;
157 return getType(state);
177 return getType(state);
158 } else if (state.prevLineHasContent && !state.quote && !prevLineIsList && !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
178 } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
179 !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
159 state.header = match[0].charAt(0) == '=' ? 1 : 2;
180 state.header = match[0].charAt(0) == '=' ? 1 : 2;
160 if (modeCfg.highlightFormatting) state.formatting = "header";
181 if (modeCfg.highlightFormatting) state.formatting = "header";
161 state.f = state.inline;
182 state.f = state.inline;
162 return getType(state);
183 return getType(state);
163 } else if (stream.eat('>')) {
184 } else if (stream.eat('>')) {
164 state.quote = sol ? 1 : state.quote + 1;
185 state.quote = sol ? 1 : state.quote + 1;
165 if (modeCfg.highlightFormatting) state.formatting = "quote";
186 if (modeCfg.highlightFormatting) state.formatting = "quote";
166 stream.eatSpace();
187 stream.eatSpace();
167 return getType(state);
188 return getType(state);
168 } else if (stream.peek() === '[') {
189 } else if (stream.peek() === '[') {
169 return switchInline(stream, state, footnoteLink);
190 return switchInline(stream, state, footnoteLink);
170 } else if (stream.match(hrRE, true)) {
191 } else if (stream.match(hrRE, true)) {
171 state.hr = true;
192 state.hr = true;
172 return hr;
193 return tokenTypes.hr;
173 } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
194 } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
174 var listType = null;
195 var listType = null;
175 if (stream.match(ulRE, true)) {
196 if (stream.match(ulRE, true)) {
176 listType = 'ul';
197 listType = 'ul';
177 } else {
198 } else {
178 stream.match(olRE, true);
199 stream.match(olRE, true);
179 listType = 'ol';
200 listType = 'ol';
180 }
201 }
181 state.indentation += 4;
202 state.indentation = stream.column() + stream.current().length;
182 state.list = true;
203 state.list = true;
183 state.listDepth++;
204 state.listDepth++;
184 if (modeCfg.taskLists && stream.match(taskListRE, false)) {
205 if (modeCfg.taskLists && stream.match(taskListRE, false)) {
185 state.taskList = true;
206 state.taskList = true;
186 }
207 }
187 state.f = state.inline;
208 state.f = state.inline;
188 if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
209 if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
189 return getType(state);
210 return getType(state);
190 } else if (modeCfg.fencedCodeBlocks && stream.match(/^```[ \t]*([\w+#]*)/, true)) {
211 } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
212 state.fencedChars = match[1]
191 // try switching mode
213 // try switching mode
192 state.localMode = getMode(RegExp.$1);
214 state.localMode = getMode(match[2]);
193 if (state.localMode) state.localState = state.localMode.startState();
215 if (state.localMode) state.localState = state.localMode.startState();
194 state.f = state.block = local;
216 state.f = state.block = local;
195 if (modeCfg.highlightFormatting) state.formatting = "code-block";
217 if (modeCfg.highlightFormatting) state.formatting = "code-block";
196 state.code = true;
218 state.code = true;
197 return getType(state);
219 return getType(state);
198 }
220 }
199
221
200 return switchInline(stream, state, state.inline);
222 return switchInline(stream, state, state.inline);
201 }
223 }
202
224
203 function htmlBlock(stream, state) {
225 function htmlBlock(stream, state) {
204 var style = htmlMode.token(stream, state.htmlState);
226 var style = htmlMode.token(stream, state.htmlState);
205 if ((htmlFound && state.htmlState.tagStart === null && !state.htmlState.context) ||
227 if ((htmlFound && state.htmlState.tagStart === null &&
228 (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
206 (state.md_inside && stream.current().indexOf(">") > -1)) {
229 (state.md_inside && stream.current().indexOf(">") > -1)) {
207 state.f = inlineNormal;
230 state.f = inlineNormal;
208 state.block = blockNormal;
231 state.block = blockNormal;
209 state.htmlState = null;
232 state.htmlState = null;
210 }
233 }
211 return style;
234 return style;
212 }
235 }
213
236
214 function local(stream, state) {
237 function local(stream, state) {
215 if (stream.sol() && stream.match("```", false)) {
238 if (state.fencedChars && stream.match(state.fencedChars, false)) {
216 state.localMode = state.localState = null;
239 state.localMode = state.localState = null;
217 state.f = state.block = leavingLocal;
240 state.f = state.block = leavingLocal;
218 return null;
241 return null;
219 } else if (state.localMode) {
242 } else if (state.localMode) {
220 return state.localMode.token(stream, state.localState);
243 return state.localMode.token(stream, state.localState);
221 } else {
244 } else {
222 stream.skipToEnd();
245 stream.skipToEnd();
223 return code;
246 return tokenTypes.code;
224 }
247 }
225 }
248 }
226
249
227 function leavingLocal(stream, state) {
250 function leavingLocal(stream, state) {
228 stream.match("```");
251 stream.match(state.fencedChars);
229 state.block = blockNormal;
252 state.block = blockNormal;
230 state.f = inlineNormal;
253 state.f = inlineNormal;
254 state.fencedChars = null;
231 if (modeCfg.highlightFormatting) state.formatting = "code-block";
255 if (modeCfg.highlightFormatting) state.formatting = "code-block";
232 state.code = true;
256 state.code = true;
233 var returnType = getType(state);
257 var returnType = getType(state);
234 state.code = false;
258 state.code = false;
235 return returnType;
259 return returnType;
236 }
260 }
237
261
238 // Inline
262 // Inline
239 function getType(state) {
263 function getType(state) {
240 var styles = [];
264 var styles = [];
241
265
242 if (state.formatting) {
266 if (state.formatting) {
243 styles.push(formatting);
267 styles.push(tokenTypes.formatting);
244
268
245 if (typeof state.formatting === "string") state.formatting = [state.formatting];
269 if (typeof state.formatting === "string") state.formatting = [state.formatting];
246
270
247 for (var i = 0; i < state.formatting.length; i++) {
271 for (var i = 0; i < state.formatting.length; i++) {
248 styles.push(formatting + "-" + state.formatting[i]);
272 styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
249
273
250 if (state.formatting[i] === "header") {
274 if (state.formatting[i] === "header") {
251 styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
275 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
252 }
276 }
253
277
254 // Add `formatting-quote` and `formatting-quote-#` for blockquotes
278 // Add `formatting-quote` and `formatting-quote-#` for blockquotes
255 // Add `error` instead if the maximum blockquote nesting depth is passed
279 // Add `error` instead if the maximum blockquote nesting depth is passed
256 if (state.formatting[i] === "quote") {
280 if (state.formatting[i] === "quote") {
257 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
281 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
258 styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
282 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
259 } else {
283 } else {
260 styles.push("error");
284 styles.push("error");
261 }
285 }
262 }
286 }
263 }
287 }
264 }
288 }
265
289
266 if (state.taskOpen) {
290 if (state.taskOpen) {
267 styles.push("meta");
291 styles.push("meta");
268 return styles.length ? styles.join(' ') : null;
292 return styles.length ? styles.join(' ') : null;
269 }
293 }
270 if (state.taskClosed) {
294 if (state.taskClosed) {
271 styles.push("property");
295 styles.push("property");
272 return styles.length ? styles.join(' ') : null;
296 return styles.length ? styles.join(' ') : null;
273 }
297 }
274
298
275 if (state.linkHref) {
299 if (state.linkHref) {
276 styles.push(linkhref, "url");
300 styles.push(tokenTypes.linkHref, "url");
277 } else { // Only apply inline styles to non-url text
301 } else { // Only apply inline styles to non-url text
278 if (state.strong) { styles.push(strong); }
302 if (state.strong) { styles.push(tokenTypes.strong); }
279 if (state.em) { styles.push(em); }
303 if (state.em) { styles.push(tokenTypes.em); }
280 if (state.strikethrough) { styles.push(strikethrough); }
304 if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
281
305 if (state.linkText) { styles.push(tokenTypes.linkText); }
282 if (state.linkText) { styles.push(linktext); }
306 if (state.code) { styles.push(tokenTypes.code); }
283
284 if (state.code) { styles.push(code); }
285 }
307 }
286
308
287 if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
309 if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
288
310
289 if (state.quote) {
311 if (state.quote) {
290 styles.push(quote);
312 styles.push(tokenTypes.quote);
291
313
292 // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
314 // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
293 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
315 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
294 styles.push(quote + "-" + state.quote);
316 styles.push(tokenTypes.quote + "-" + state.quote);
295 } else {
317 } else {
296 styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
318 styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
297 }
319 }
298 }
320 }
299
321
300 if (state.list !== false) {
322 if (state.list !== false) {
301 var listMod = (state.listDepth - 1) % 3;
323 var listMod = (state.listDepth - 1) % 3;
302 if (!listMod) {
324 if (!listMod) {
303 styles.push(list1);
325 styles.push(tokenTypes.list1);
304 } else if (listMod === 1) {
326 } else if (listMod === 1) {
305 styles.push(list2);
327 styles.push(tokenTypes.list2);
306 } else {
328 } else {
307 styles.push(list3);
329 styles.push(tokenTypes.list3);
308 }
330 }
309 }
331 }
310
332
311 if (state.trailingSpaceNewLine) {
333 if (state.trailingSpaceNewLine) {
312 styles.push("trailing-space-new-line");
334 styles.push("trailing-space-new-line");
313 } else if (state.trailingSpace) {
335 } else if (state.trailingSpace) {
314 styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
336 styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
315 }
337 }
316
338
317 return styles.length ? styles.join(' ') : null;
339 return styles.length ? styles.join(' ') : null;
318 }
340 }
319
341
320 function handleText(stream, state) {
342 function handleText(stream, state) {
321 if (stream.match(textRE, true)) {
343 if (stream.match(textRE, true)) {
322 return getType(state);
344 return getType(state);
323 }
345 }
324 return undefined;
346 return undefined;
325 }
347 }
326
348
327 function inlineNormal(stream, state) {
349 function inlineNormal(stream, state) {
328 var style = state.text(stream, state);
350 var style = state.text(stream, state);
329 if (typeof style !== 'undefined')
351 if (typeof style !== 'undefined')
330 return style;
352 return style;
331
353
332 if (state.list) { // List marker (*, +, -, 1., etc)
354 if (state.list) { // List marker (*, +, -, 1., etc)
333 state.list = null;
355 state.list = null;
334 return getType(state);
356 return getType(state);
335 }
357 }
336
358
337 if (state.taskList) {
359 if (state.taskList) {
338 var taskOpen = stream.match(taskListRE, true)[1] !== "x";
360 var taskOpen = stream.match(taskListRE, true)[1] !== "x";
339 if (taskOpen) state.taskOpen = true;
361 if (taskOpen) state.taskOpen = true;
340 else state.taskClosed = true;
362 else state.taskClosed = true;
341 if (modeCfg.highlightFormatting) state.formatting = "task";
363 if (modeCfg.highlightFormatting) state.formatting = "task";
342 state.taskList = false;
364 state.taskList = false;
343 return getType(state);
365 return getType(state);
344 }
366 }
345
367
346 state.taskOpen = false;
368 state.taskOpen = false;
347 state.taskClosed = false;
369 state.taskClosed = false;
348
370
349 if (state.header && stream.match(/^#+$/, true)) {
371 if (state.header && stream.match(/^#+$/, true)) {
350 if (modeCfg.highlightFormatting) state.formatting = "header";
372 if (modeCfg.highlightFormatting) state.formatting = "header";
351 return getType(state);
373 return getType(state);
352 }
374 }
353
375
354 // Get sol() value now, before character is consumed
376 // Get sol() value now, before character is consumed
355 var sol = stream.sol();
377 var sol = stream.sol();
356
378
357 var ch = stream.next();
379 var ch = stream.next();
358
380
359 if (ch === '\\') {
381 if (ch === '\\') {
360 stream.next();
382 stream.next();
361 if (modeCfg.highlightFormatting) {
383 if (modeCfg.highlightFormatting) {
362 var type = getType(state);
384 var type = getType(state);
363 return type ? type + " formatting-escape" : "formatting-escape";
385 var formattingEscape = tokenTypes.formatting + "-escape";
386 return type ? type + " " + formattingEscape : formattingEscape;
364 }
387 }
365 }
388 }
366
389
367 // Matches link titles present on next line
390 // Matches link titles present on next line
368 if (state.linkTitle) {
391 if (state.linkTitle) {
369 state.linkTitle = false;
392 state.linkTitle = false;
370 var matchCh = ch;
393 var matchCh = ch;
371 if (ch === '(') {
394 if (ch === '(') {
372 matchCh = ')';
395 matchCh = ')';
373 }
396 }
374 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
397 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
375 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
398 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
376 if (stream.match(new RegExp(regex), true)) {
399 if (stream.match(new RegExp(regex), true)) {
377 return linkhref;
400 return tokenTypes.linkHref;
378 }
401 }
379 }
402 }
380
403
381 // If this block is changed, it may need to be updated in GFM mode
404 // If this block is changed, it may need to be updated in GFM mode
382 if (ch === '`') {
405 if (ch === '`') {
383 var previousFormatting = state.formatting;
406 var previousFormatting = state.formatting;
384 if (modeCfg.highlightFormatting) state.formatting = "code";
407 if (modeCfg.highlightFormatting) state.formatting = "code";
385 var t = getType(state);
408 var t = getType(state);
386 var before = stream.pos;
409 var before = stream.pos;
387 stream.eatWhile('`');
410 stream.eatWhile('`');
388 var difference = 1 + stream.pos - before;
411 var difference = 1 + stream.pos - before;
389 if (!state.code) {
412 if (!state.code) {
390 codeDepth = difference;
413 codeDepth = difference;
391 state.code = true;
414 state.code = true;
392 return getType(state);
415 return getType(state);
393 } else {
416 } else {
394 if (difference === codeDepth) { // Must be exact
417 if (difference === codeDepth) { // Must be exact
395 state.code = false;
418 state.code = false;
396 return t;
419 return t;
397 }
420 }
398 state.formatting = previousFormatting;
421 state.formatting = previousFormatting;
399 return getType(state);
422 return getType(state);
400 }
423 }
401 } else if (state.code) {
424 } else if (state.code) {
402 return getType(state);
425 return getType(state);
403 }
426 }
404
427
405 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
428 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
406 stream.match(/\[[^\]]*\]/);
429 stream.match(/\[[^\]]*\]/);
407 state.inline = state.f = linkHref;
430 state.inline = state.f = linkHref;
408 return image;
431 return tokenTypes.image;
409 }
432 }
410
433
411 if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
434 if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
412 state.linkText = true;
435 state.linkText = true;
413 if (modeCfg.highlightFormatting) state.formatting = "link";
436 if (modeCfg.highlightFormatting) state.formatting = "link";
414 return getType(state);
437 return getType(state);
415 }
438 }
416
439
417 if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
440 if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
418 if (modeCfg.highlightFormatting) state.formatting = "link";
441 if (modeCfg.highlightFormatting) state.formatting = "link";
419 var type = getType(state);
442 var type = getType(state);
420 state.linkText = false;
443 state.linkText = false;
421 state.inline = state.f = linkHref;
444 state.inline = state.f = linkHref;
422 return type;
445 return type;
423 }
446 }
424
447
425 if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
448 if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
426 state.f = state.inline = linkInline;
449 state.f = state.inline = linkInline;
427 if (modeCfg.highlightFormatting) state.formatting = "link";
450 if (modeCfg.highlightFormatting) state.formatting = "link";
428 var type = getType(state);
451 var type = getType(state);
429 if (type){
452 if (type){
430 type += " ";
453 type += " ";
431 } else {
454 } else {
432 type = "";
455 type = "";
433 }
456 }
434 return type + linkinline;
457 return type + tokenTypes.linkInline;
435 }
458 }
436
459
437 if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
460 if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
438 state.f = state.inline = linkInline;
461 state.f = state.inline = linkInline;
439 if (modeCfg.highlightFormatting) state.formatting = "link";
462 if (modeCfg.highlightFormatting) state.formatting = "link";
440 var type = getType(state);
463 var type = getType(state);
441 if (type){
464 if (type){
442 type += " ";
465 type += " ";
443 } else {
466 } else {
444 type = "";
467 type = "";
445 }
468 }
446 return type + linkemail;
469 return type + tokenTypes.linkEmail;
447 }
470 }
448
471
449 if (ch === '<' && stream.match(/^\w/, false)) {
472 if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
450 if (stream.string.indexOf(">") != -1) {
473 var end = stream.string.indexOf(">", stream.pos);
451 var atts = stream.string.substring(1,stream.string.indexOf(">"));
474 if (end != -1) {
452 if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
475 var atts = stream.string.substring(stream.start, end);
453 state.md_inside = true;
476 if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
454 }
455 }
477 }
456 stream.backUp(1);
478 stream.backUp(1);
457 state.htmlState = CodeMirror.startState(htmlMode);
479 state.htmlState = CodeMirror.startState(htmlMode);
458 return switchBlock(stream, state, htmlBlock);
480 return switchBlock(stream, state, htmlBlock);
459 }
481 }
460
482
461 if (ch === '<' && stream.match(/^\/\w*?>/)) {
483 if (ch === '<' && stream.match(/^\/\w*?>/)) {
462 state.md_inside = false;
484 state.md_inside = false;
463 return "tag";
485 return "tag";
464 }
486 }
465
487
466 var ignoreUnderscore = false;
488 var ignoreUnderscore = false;
467 if (!modeCfg.underscoresBreakWords) {
489 if (!modeCfg.underscoresBreakWords) {
468 if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
490 if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
469 var prevPos = stream.pos - 2;
491 var prevPos = stream.pos - 2;
470 if (prevPos >= 0) {
492 if (prevPos >= 0) {
471 var prevCh = stream.string.charAt(prevPos);
493 var prevCh = stream.string.charAt(prevPos);
472 if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
494 if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
473 ignoreUnderscore = true;
495 ignoreUnderscore = true;
474 }
496 }
475 }
497 }
476 }
498 }
477 }
499 }
478 if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
500 if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
479 if (sol && stream.peek() === ' ') {
501 if (sol && stream.peek() === ' ') {
480 // Do nothing, surrounded by newline and space
502 // Do nothing, surrounded by newline and space
481 } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
503 } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
482 if (modeCfg.highlightFormatting) state.formatting = "strong";
504 if (modeCfg.highlightFormatting) state.formatting = "strong";
483 var t = getType(state);
505 var t = getType(state);
484 state.strong = false;
506 state.strong = false;
485 return t;
507 return t;
486 } else if (!state.strong && stream.eat(ch)) { // Add STRONG
508 } else if (!state.strong && stream.eat(ch)) { // Add STRONG
487 state.strong = ch;
509 state.strong = ch;
488 if (modeCfg.highlightFormatting) state.formatting = "strong";
510 if (modeCfg.highlightFormatting) state.formatting = "strong";
489 return getType(state);
511 return getType(state);
490 } else if (state.em === ch) { // Remove EM
512 } else if (state.em === ch) { // Remove EM
491 if (modeCfg.highlightFormatting) state.formatting = "em";
513 if (modeCfg.highlightFormatting) state.formatting = "em";
492 var t = getType(state);
514 var t = getType(state);
493 state.em = false;
515 state.em = false;
494 return t;
516 return t;
495 } else if (!state.em) { // Add EM
517 } else if (!state.em) { // Add EM
496 state.em = ch;
518 state.em = ch;
497 if (modeCfg.highlightFormatting) state.formatting = "em";
519 if (modeCfg.highlightFormatting) state.formatting = "em";
498 return getType(state);
520 return getType(state);
499 }
521 }
500 } else if (ch === ' ') {
522 } else if (ch === ' ') {
501 if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
523 if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
502 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
524 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
503 return getType(state);
525 return getType(state);
504 } else { // Not surrounded by spaces, back up pointer
526 } else { // Not surrounded by spaces, back up pointer
505 stream.backUp(1);
527 stream.backUp(1);
506 }
528 }
507 }
529 }
508 }
530 }
509
531
510 if (modeCfg.strikethrough) {
532 if (modeCfg.strikethrough) {
511 if (ch === '~' && stream.eatWhile(ch)) {
533 if (ch === '~' && stream.eatWhile(ch)) {
512 if (state.strikethrough) {// Remove strikethrough
534 if (state.strikethrough) {// Remove strikethrough
513 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
535 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
514 var t = getType(state);
536 var t = getType(state);
515 state.strikethrough = false;
537 state.strikethrough = false;
516 return t;
538 return t;
517 } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
539 } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
518 state.strikethrough = true;
540 state.strikethrough = true;
519 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
541 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
520 return getType(state);
542 return getType(state);
521 }
543 }
522 } else if (ch === ' ') {
544 } else if (ch === ' ') {
523 if (stream.match(/^~~/, true)) { // Probably surrounded by space
545 if (stream.match(/^~~/, true)) { // Probably surrounded by space
524 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
546 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
525 return getType(state);
547 return getType(state);
526 } else { // Not surrounded by spaces, back up pointer
548 } else { // Not surrounded by spaces, back up pointer
527 stream.backUp(2);
549 stream.backUp(2);
528 }
550 }
529 }
551 }
530 }
552 }
531 }
553 }
532
554
533 if (ch === ' ') {
555 if (ch === ' ') {
534 if (stream.match(/ +$/, false)) {
556 if (stream.match(/ +$/, false)) {
535 state.trailingSpace++;
557 state.trailingSpace++;
536 } else if (state.trailingSpace) {
558 } else if (state.trailingSpace) {
537 state.trailingSpaceNewLine = true;
559 state.trailingSpaceNewLine = true;
538 }
560 }
539 }
561 }
540
562
541 return getType(state);
563 return getType(state);
542 }
564 }
543
565
544 function linkInline(stream, state) {
566 function linkInline(stream, state) {
545 var ch = stream.next();
567 var ch = stream.next();
546
568
547 if (ch === ">") {
569 if (ch === ">") {
548 state.f = state.inline = inlineNormal;
570 state.f = state.inline = inlineNormal;
549 if (modeCfg.highlightFormatting) state.formatting = "link";
571 if (modeCfg.highlightFormatting) state.formatting = "link";
550 var type = getType(state);
572 var type = getType(state);
551 if (type){
573 if (type){
552 type += " ";
574 type += " ";
553 } else {
575 } else {
554 type = "";
576 type = "";
555 }
577 }
556 return type + linkinline;
578 return type + tokenTypes.linkInline;
557 }
579 }
558
580
559 stream.match(/^[^>]+/, true);
581 stream.match(/^[^>]+/, true);
560
582
561 return linkinline;
583 return tokenTypes.linkInline;
562 }
584 }
563
585
564 function linkHref(stream, state) {
586 function linkHref(stream, state) {
565 // Check if space, and return NULL if so (to avoid marking the space)
587 // Check if space, and return NULL if so (to avoid marking the space)
566 if(stream.eatSpace()){
588 if(stream.eatSpace()){
567 return null;
589 return null;
568 }
590 }
569 var ch = stream.next();
591 var ch = stream.next();
570 if (ch === '(' || ch === '[') {
592 if (ch === '(' || ch === '[') {
571 state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
593 state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
572 if (modeCfg.highlightFormatting) state.formatting = "link-string";
594 if (modeCfg.highlightFormatting) state.formatting = "link-string";
573 state.linkHref = true;
595 state.linkHref = true;
574 return getType(state);
596 return getType(state);
575 }
597 }
576 return 'error';
598 return 'error';
577 }
599 }
578
600
579 function getLinkHrefInside(endChar) {
601 function getLinkHrefInside(endChar) {
580 return function(stream, state) {
602 return function(stream, state) {
581 var ch = stream.next();
603 var ch = stream.next();
582
604
583 if (ch === endChar) {
605 if (ch === endChar) {
584 state.f = state.inline = inlineNormal;
606 state.f = state.inline = inlineNormal;
585 if (modeCfg.highlightFormatting) state.formatting = "link-string";
607 if (modeCfg.highlightFormatting) state.formatting = "link-string";
586 var returnState = getType(state);
608 var returnState = getType(state);
587 state.linkHref = false;
609 state.linkHref = false;
588 return returnState;
610 return returnState;
589 }
611 }
590
612
591 if (stream.match(inlineRE(endChar), true)) {
613 if (stream.match(inlineRE(endChar), true)) {
592 stream.backUp(1);
614 stream.backUp(1);
593 }
615 }
594
616
595 state.linkHref = true;
617 state.linkHref = true;
596 return getType(state);
618 return getType(state);
597 };
619 };
598 }
620 }
599
621
600 function footnoteLink(stream, state) {
622 function footnoteLink(stream, state) {
601 if (stream.match(/^[^\]]*\]:/, false)) {
623 if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
602 state.f = footnoteLinkInside;
624 state.f = footnoteLinkInside;
603 stream.next(); // Consume [
625 stream.next(); // Consume [
604 if (modeCfg.highlightFormatting) state.formatting = "link";
626 if (modeCfg.highlightFormatting) state.formatting = "link";
605 state.linkText = true;
627 state.linkText = true;
606 return getType(state);
628 return getType(state);
607 }
629 }
608 return switchInline(stream, state, inlineNormal);
630 return switchInline(stream, state, inlineNormal);
609 }
631 }
610
632
611 function footnoteLinkInside(stream, state) {
633 function footnoteLinkInside(stream, state) {
612 if (stream.match(/^\]:/, true)) {
634 if (stream.match(/^\]:/, true)) {
613 state.f = state.inline = footnoteUrl;
635 state.f = state.inline = footnoteUrl;
614 if (modeCfg.highlightFormatting) state.formatting = "link";
636 if (modeCfg.highlightFormatting) state.formatting = "link";
615 var returnType = getType(state);
637 var returnType = getType(state);
616 state.linkText = false;
638 state.linkText = false;
617 return returnType;
639 return returnType;
618 }
640 }
619
641
620 stream.match(/^[^\]]+/, true);
642 stream.match(/^([^\]\\]|\\.)+/, true);
621
643
622 return linktext;
644 return tokenTypes.linkText;
623 }
645 }
624
646
625 function footnoteUrl(stream, state) {
647 function footnoteUrl(stream, state) {
626 // Check if space, and return NULL if so (to avoid marking the space)
648 // Check if space, and return NULL if so (to avoid marking the space)
627 if(stream.eatSpace()){
649 if(stream.eatSpace()){
628 return null;
650 return null;
629 }
651 }
630 // Match URL
652 // Match URL
631 stream.match(/^[^\s]+/, true);
653 stream.match(/^[^\s]+/, true);
632 // Check for link title
654 // Check for link title
633 if (stream.peek() === undefined) { // End of line, set flag to check next line
655 if (stream.peek() === undefined) { // End of line, set flag to check next line
634 state.linkTitle = true;
656 state.linkTitle = true;
635 } else { // More content on line, check if link title
657 } else { // More content on line, check if link title
636 stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
658 stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
637 }
659 }
638 state.f = state.inline = inlineNormal;
660 state.f = state.inline = inlineNormal;
639 return linkhref + " url";
661 return tokenTypes.linkHref + " url";
640 }
662 }
641
663
642 var savedInlineRE = [];
664 var savedInlineRE = [];
643 function inlineRE(endChar) {
665 function inlineRE(endChar) {
644 if (!savedInlineRE[endChar]) {
666 if (!savedInlineRE[endChar]) {
645 // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
667 // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
646 endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
668 endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
647 // Match any non-endChar, escaped character, as well as the closing
669 // Match any non-endChar, escaped character, as well as the closing
648 // endChar.
670 // endChar.
649 savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
671 savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
650 }
672 }
651 return savedInlineRE[endChar];
673 return savedInlineRE[endChar];
652 }
674 }
653
675
654 var mode = {
676 var mode = {
655 startState: function() {
677 startState: function() {
656 return {
678 return {
657 f: blockNormal,
679 f: blockNormal,
658
680
659 prevLineHasContent: false,
681 prevLine: null,
660 thisLineHasContent: false,
682 thisLine: null,
661
683
662 block: blockNormal,
684 block: blockNormal,
663 htmlState: null,
685 htmlState: null,
664 indentation: 0,
686 indentation: 0,
665
687
666 inline: inlineNormal,
688 inline: inlineNormal,
667 text: handleText,
689 text: handleText,
668
690
669 formatting: false,
691 formatting: false,
670 linkText: false,
692 linkText: false,
671 linkHref: false,
693 linkHref: false,
672 linkTitle: false,
694 linkTitle: false,
673 em: false,
695 em: false,
674 strong: false,
696 strong: false,
675 header: 0,
697 header: 0,
676 hr: false,
698 hr: false,
677 taskList: false,
699 taskList: false,
678 list: false,
700 list: false,
679 listDepth: 0,
701 listDepth: 0,
680 quote: 0,
702 quote: 0,
681 trailingSpace: 0,
703 trailingSpace: 0,
682 trailingSpaceNewLine: false,
704 trailingSpaceNewLine: false,
683 strikethrough: false
705 strikethrough: false,
706 fencedChars: null
684 };
707 };
685 },
708 },
686
709
687 copyState: function(s) {
710 copyState: function(s) {
688 return {
711 return {
689 f: s.f,
712 f: s.f,
690
713
691 prevLineHasContent: s.prevLineHasContent,
714 prevLine: s.prevLine,
692 thisLineHasContent: s.thisLineHasContent,
715 thisLine: s.thisLine,
693
716
694 block: s.block,
717 block: s.block,
695 htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
718 htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
696 indentation: s.indentation,
719 indentation: s.indentation,
697
720
698 localMode: s.localMode,
721 localMode: s.localMode,
699 localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
722 localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
700
723
701 inline: s.inline,
724 inline: s.inline,
702 text: s.text,
725 text: s.text,
703 formatting: false,
726 formatting: false,
704 linkTitle: s.linkTitle,
727 linkTitle: s.linkTitle,
728 code: s.code,
705 em: s.em,
729 em: s.em,
706 strong: s.strong,
730 strong: s.strong,
707 strikethrough: s.strikethrough,
731 strikethrough: s.strikethrough,
708 header: s.header,
732 header: s.header,
709 hr: s.hr,
733 hr: s.hr,
710 taskList: s.taskList,
734 taskList: s.taskList,
711 list: s.list,
735 list: s.list,
712 listDepth: s.listDepth,
736 listDepth: s.listDepth,
713 quote: s.quote,
737 quote: s.quote,
714 indentedCode: s.indentedCode,
738 indentedCode: s.indentedCode,
715 trailingSpace: s.trailingSpace,
739 trailingSpace: s.trailingSpace,
716 trailingSpaceNewLine: s.trailingSpaceNewLine,
740 trailingSpaceNewLine: s.trailingSpaceNewLine,
717 md_inside: s.md_inside
741 md_inside: s.md_inside,
742 fencedChars: s.fencedChars
718 };
743 };
719 },
744 },
720
745
721 token: function(stream, state) {
746 token: function(stream, state) {
722
747
723 // Reset state.formatting
748 // Reset state.formatting
724 state.formatting = false;
749 state.formatting = false;
725
750
726 if (stream.sol()) {
751 if (stream != state.thisLine) {
727 var forceBlankLine = !!state.header || state.hr;
752 var forceBlankLine = state.header || state.hr;
728
753
729 // Reset state.header and state.hr
754 // Reset state.header and state.hr
730 state.header = 0;
755 state.header = 0;
731 state.hr = false;
756 state.hr = false;
732
757
733 if (stream.match(/^\s*$/, true) || forceBlankLine) {
758 if (stream.match(/^\s*$/, true) || forceBlankLine) {
734 state.prevLineHasContent = false;
735 blankLine(state);
759 blankLine(state);
736 return forceBlankLine ? this.token(stream, state) : null;
760 if (!forceBlankLine) return null
737 } else {
761 state.prevLine = null
738 state.prevLineHasContent = state.thisLineHasContent;
739 state.thisLineHasContent = true;
740 }
762 }
741
763
764 state.prevLine = state.thisLine
765 state.thisLine = stream
766
742 // Reset state.taskList
767 // Reset state.taskList
743 state.taskList = false;
768 state.taskList = false;
744
769
745 // Reset state.code
746 state.code = false;
747
748 // Reset state.trailingSpace
770 // Reset state.trailingSpace
749 state.trailingSpace = 0;
771 state.trailingSpace = 0;
750 state.trailingSpaceNewLine = false;
772 state.trailingSpaceNewLine = false;
751
773
752 state.f = state.block;
774 state.f = state.block;
753 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
775 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
754 var difference = Math.floor((indentation - state.indentation) / 4) * 4;
776 var difference = Math.floor((indentation - state.indentation) / 4) * 4;
755 if (difference > 4) difference = 4;
777 if (difference > 4) difference = 4;
756 var adjustedIndentation = state.indentation + difference;
778 var adjustedIndentation = state.indentation + difference;
757 state.indentationDiff = adjustedIndentation - state.indentation;
779 state.indentationDiff = adjustedIndentation - state.indentation;
758 state.indentation = adjustedIndentation;
780 state.indentation = adjustedIndentation;
759 if (indentation > 0) return null;
781 if (indentation > 0) return null;
760 }
782 }
761 return state.f(stream, state);
783 return state.f(stream, state);
762 },
784 },
763
785
764 innerMode: function(state) {
786 innerMode: function(state) {
765 if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
787 if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
766 if (state.localState) return {state: state.localState, mode: state.localMode};
788 if (state.localState) return {state: state.localState, mode: state.localMode};
767 return {state: state, mode: mode};
789 return {state: state, mode: mode};
768 },
790 },
769
791
770 blankLine: blankLine,
792 blankLine: blankLine,
771
793
772 getType: getType,
794 getType: getType,
773
795
774 fold: "markdown"
796 fold: "markdown"
775 };
797 };
776 return mode;
798 return mode;
777 }, "xml");
799 }, "xml");
778
800
779 CodeMirror.defineMIME("text/x-markdown", "markdown");
801 CodeMirror.defineMIME("text/x-markdown", "markdown");
780
802
781 });
803 });
@@ -1,188 +1,201 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../lib/codemirror"));
6 mod(require("../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../lib/codemirror"], mod);
8 define(["../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.modeInfo = [
14 CodeMirror.modeInfo = [
15 {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
15 {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
16 {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
16 {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
17 {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn, asn1"]},
17 {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
18 {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
18 {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
19 {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
19 {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
20 {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
20 {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
21 {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
21 {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
22 {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
22 {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
23 {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
23 {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
24 {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
25 {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
26 {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
24 {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
27 {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
25 {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
28 {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
26 {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
29 {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
27 {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
30 {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
28 {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
31 {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
32 {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]},
29 {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
33 {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
30 {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
34 {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
31 {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
35 {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
32 {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
36 {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
33 {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
37 {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
34 {name: "Django", mime: "text/x-django", mode: "django"},
38 {name: "Django", mime: "text/x-django", mode: "django"},
35 {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
39 {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
36 {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
40 {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
37 {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
41 {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
38 {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
42 {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
39 {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
43 {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
40 {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
44 {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
41 {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
45 {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
42 {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
46 {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
43 {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
47 {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
44 {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
48 {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
45 {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
49 {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
46 {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
50 {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
47 {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
51 {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
48 {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
52 {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
49 {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
53 {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
50 {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
54 {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
51 {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
55 {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
52 {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
56 {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
53 {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
57 {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
54 {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
58 {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
55 {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
59 {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
60 {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
56 {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
61 {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
57 {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
62 {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
58 {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
63 {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
59 {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
64 {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
60 {name: "HTTP", mime: "message/http", mode: "http"},
65 {name: "HTTP", mime: "message/http", mode: "http"},
61 {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
66 {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
62 {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
67 {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
63 {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
68 {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
64 {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
69 {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
65 {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
70 {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
66 mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
71 mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
67 {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
72 {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
68 {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
73 {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
74 {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
69 {name: "Jinja2", mime: "null", mode: "jinja2"},
75 {name: "Jinja2", mime: "null", mode: "jinja2"},
70 {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
76 {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
71 {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin", ext: ["kt"]},
77 {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
72 {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
78 {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
73 {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
79 {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
74 {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
80 {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
75 {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
81 {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
76 {name: "mIRC", mime: "text/mirc", mode: "mirc"},
82 {name: "mIRC", mime: "text/mirc", mode: "mirc"},
77 {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
83 {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
78 {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
84 {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
79 {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
85 {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
80 {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
86 {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
81 {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
87 {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
82 {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
88 {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
83 {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
89 {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
90 {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
84 {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
91 {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
85 {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
92 {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
86 {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
93 {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
87 {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
94 {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
95 {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
88 {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
96 {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
89 {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
97 {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
90 {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
98 {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
91 {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
99 {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
92 {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
100 {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
93 {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
101 {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
94 {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
102 {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
95 {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
103 {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
96 {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
104 {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
97 {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
105 {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
98 {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
106 {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
99 {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
107 {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
100 {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
108 {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
101 {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
109 {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
102 {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
110 {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
103 {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
111 {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
104 {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
112 {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
105 {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
113 {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
106 {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
114 {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
107 {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
115 {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
108 {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
116 {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
109 {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]},
117 {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
110 {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
118 {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
111 {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
119 {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
112 {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
120 {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
113 {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
121 {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
114 {name: "Solr", mime: "text/x-solr", mode: "solr"},
122 {name: "Solr", mime: "text/x-solr", mode: "solr"},
115 {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
123 {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
116 {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
124 {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
117 {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
125 {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
118 {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
126 {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
127 {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
119 {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
128 {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
120 {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
129 {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
121 {name: "sTeX", mime: "text/x-stex", mode: "stex"},
130 {name: "sTeX", mime: "text/x-stex", mode: "stex"},
122 {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
131 {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
123 {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
132 {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
124 {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
133 {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
125 {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
134 {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
126 {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
135 {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
127 {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
136 {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
128 {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
137 {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
129 {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
138 {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
130 {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
139 {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
131 {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
140 {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
132 {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
141 {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
133 {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
142 {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
134 {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
143 {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
135 {name: "Twig", mime: "text/x-twig", mode: "twig"},
144 {name: "Twig", mime: "text/x-twig", mode: "twig"},
136 {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
145 {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
137 {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
146 {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
138 {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
147 {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
139 {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
148 {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
149 {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
140 {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
150 {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
141 {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
151 {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
142 {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
152 {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
143 {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}
153 {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
154 {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
155 {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
156 {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}
144 ];
157 ];
145 // Ensure all modes have a mime property for backwards compatibility
158 // Ensure all modes have a mime property for backwards compatibility
146 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
159 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
147 var info = CodeMirror.modeInfo[i];
160 var info = CodeMirror.modeInfo[i];
148 if (info.mimes) info.mime = info.mimes[0];
161 if (info.mimes) info.mime = info.mimes[0];
149 }
162 }
150
163
151 CodeMirror.findModeByMIME = function(mime) {
164 CodeMirror.findModeByMIME = function(mime) {
152 mime = mime.toLowerCase();
165 mime = mime.toLowerCase();
153 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
166 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
154 var info = CodeMirror.modeInfo[i];
167 var info = CodeMirror.modeInfo[i];
155 if (info.mime == mime) return info;
168 if (info.mime == mime) return info;
156 if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
169 if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
157 if (info.mimes[j] == mime) return info;
170 if (info.mimes[j] == mime) return info;
158 }
171 }
159 };
172 };
160
173
161 CodeMirror.findModeByExtension = function(ext) {
174 CodeMirror.findModeByExtension = function(ext) {
162 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
175 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
163 var info = CodeMirror.modeInfo[i];
176 var info = CodeMirror.modeInfo[i];
164 if (info.ext) for (var j = 0; j < info.ext.length; j++)
177 if (info.ext) for (var j = 0; j < info.ext.length; j++)
165 if (info.ext[j] == ext) return info;
178 if (info.ext[j] == ext) return info;
166 }
179 }
167 };
180 };
168
181
169 CodeMirror.findModeByFileName = function(filename) {
182 CodeMirror.findModeByFileName = function(filename) {
170 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
183 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
171 var info = CodeMirror.modeInfo[i];
184 var info = CodeMirror.modeInfo[i];
172 if (info.file && info.file.test(filename)) return info;
185 if (info.file && info.file.test(filename)) return info;
173 }
186 }
174 var dot = filename.lastIndexOf(".");
187 var dot = filename.lastIndexOf(".");
175 var ext = dot > -1 && filename.substring(dot + 1, filename.length);
188 var ext = dot > -1 && filename.substring(dot + 1, filename.length);
176 if (ext) return CodeMirror.findModeByExtension(ext);
189 if (ext) return CodeMirror.findModeByExtension(ext);
177 };
190 };
178
191
179 CodeMirror.findModeByName = function(name) {
192 CodeMirror.findModeByName = function(name) {
180 name = name.toLowerCase();
193 name = name.toLowerCase();
181 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
194 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
182 var info = CodeMirror.modeInfo[i];
195 var info = CodeMirror.modeInfo[i];
183 if (info.name.toLowerCase() == name) return info;
196 if (info.name.toLowerCase() == name) return info;
184 if (info.alias) for (var j = 0; j < info.alias.length; j++)
197 if (info.alias) for (var j = 0; j < info.alias.length; j++)
185 if (info.alias[j].toLowerCase() == name) return info;
198 if (info.alias[j].toLowerCase() == name) return info;
186 }
199 }
187 };
200 };
188 });
201 });
@@ -1,336 +1,336 b''
1 var _EMPTY_EXT = {'exts': [], 'mode': 'plain'};
1 var _EMPTY_EXT = {'exts': [], 'mode': 'plain'};
2
2
3 MIME_TO_EXT = {
3 MIME_TO_EXT = {
4 "application/json": {"exts": ["*.json","*.map"], "mode": "javascript"},
4 "application/json": {"exts": ["*.json","*.map"], "mode": "javascript"},
5 "application/postscript": {"exts": ["*.ps","*.eps"], "mode": ""},
5 "application/postscript": {"exts": ["*.ps","*.eps"], "mode": ""},
6 "application/sieve": {"exts": ["*.siv","*.sieve"], "mode": "sieve"},
6 "application/sieve": {"exts": ["*.siv","*.sieve"], "mode": "sieve"},
7 "application/typescript": {"exts": ["*.ts"], "mode": "javascript"},
7 "application/typescript": {"exts": ["*.ts"], "mode": "javascript"},
8 "application/x-actionscript": {"exts": ["*.as"], "mode": ""},
8 "application/x-actionscript": {"exts": ["*.as"], "mode": ""},
9 "application/x-actionscript3": {"exts": ["*.as"], "mode": ""},
9 "application/x-actionscript3": {"exts": ["*.as"], "mode": ""},
10 "application/x-aspx": {"exts": ["*.aspx"], "mode": "htmlembedded"},
10 "application/x-aspx": {"exts": ["*.aspx"], "mode": "htmlembedded"},
11 "application/x-awk": {"exts": ["*.awk"], "mode": ""},
11 "application/x-awk": {"exts": ["*.awk"], "mode": ""},
12 "application/x-befunge": {"exts": ["*.befunge"], "mode": ""},
12 "application/x-befunge": {"exts": ["*.befunge"], "mode": ""},
13 "application/x-brainfuck": {"exts": ["*.bf","*.b"], "mode": ""},
13 "application/x-brainfuck": {"exts": ["*.bf","*.b"], "mode": ""},
14 "application/x-cheetah": {"exts": ["*.tmpl","*.spt"], "mode": ""},
14 "application/x-cheetah": {"exts": ["*.tmpl","*.spt"], "mode": ""},
15 "application/x-coldfusion": {"exts": ["*.cfm","*.cfml","*.cfc"], "mode": ""},
15 "application/x-coldfusion": {"exts": ["*.cfm","*.cfml","*.cfc"], "mode": ""},
16 "application/x-csh": {"exts": ["*.tcsh","*.csh"], "mode": ""},
16 "application/x-csh": {"exts": ["*.tcsh","*.csh"], "mode": ""},
17 "application/x-dos-batch": {"exts": ["*.bat","*.cmd"], "mode": ""},
17 "application/x-dos-batch": {"exts": ["*.bat","*.cmd"], "mode": ""},
18 "application/x-ecl": {"exts": ["*.ecl"], "mode": ""},
18 "application/x-ecl": {"exts": ["*.ecl"], "mode": ""},
19 "application/x-ejs": {"exts": ["*.ejs"], "mode": "htmlembedded"},
19 "application/x-ejs": {"exts": ["*.ejs"], "mode": "htmlembedded"},
20 "application/x-evoque": {"exts": ["*.evoque"], "mode": ""},
20 "application/x-evoque": {"exts": ["*.evoque"], "mode": ""},
21 "application/x-fantom": {"exts": ["*.fan"], "mode": ""},
21 "application/x-fantom": {"exts": ["*.fan"], "mode": ""},
22 "application/x-genshi": {"exts": ["*.kid"], "mode": ""},
22 "application/x-genshi": {"exts": ["*.kid"], "mode": ""},
23 "application/x-gettext": {"exts": ["*.pot","*.po"], "mode": ""},
23 "application/x-gettext": {"exts": ["*.pot","*.po"], "mode": ""},
24 "application/x-json": {"exts": ["*.json"], "mode": ""},
24 "application/x-json": {"exts": ["*.json"], "mode": ""},
25 "application/x-jsp": {"exts": ["*.jsp"], "mode": "htmlembedded"},
25 "application/x-jsp": {"exts": ["*.jsp"], "mode": "htmlembedded"},
26 "application/x-mako": {"exts": ["*.mako"], "mode": ""},
26 "application/x-mako": {"exts": ["*.mako"], "mode": ""},
27 "application/x-mason": {"exts": ["*.m","*.mhtml","*.mc","*.mi","autohandler","dhandler"], "mode": ""},
27 "application/x-mason": {"exts": ["*.m","*.mhtml","*.mc","*.mi","autohandler","dhandler"], "mode": ""},
28 "application/x-myghty": {"exts": ["*.myt","autodelegate"], "mode": ""},
28 "application/x-myghty": {"exts": ["*.myt","autodelegate"], "mode": ""},
29 "application/x-php": {"exts": ["*.phtml"], "mode": ""},
29 "application/x-php": {"exts": ["*.phtml"], "mode": ""},
30 "application/x-pypylog": {"exts": ["*.pypylog"], "mode": ""},
30 "application/x-pypylog": {"exts": ["*.pypylog"], "mode": ""},
31 "application/x-qml": {"exts": ["*.qml"], "mode": ""},
31 "application/x-qml": {"exts": ["*.qml"], "mode": ""},
32 "application/x-sh-session": {"exts": ["*.shell-session"], "mode": ""},
32 "application/x-sh-session": {"exts": ["*.shell-session"], "mode": ""},
33 "application/x-shell-session": {"exts": ["*.sh-session"], "mode": ""},
33 "application/x-shell-session": {"exts": ["*.sh-session"], "mode": ""},
34 "application/x-smarty": {"exts": ["*.tpl"], "mode": ""},
34 "application/x-smarty": {"exts": ["*.tpl"], "mode": ""},
35 "application/x-sparql-query": {"exts": [], "mode": "sparql"},
35 "application/x-sparql-query": {"exts": [], "mode": "sparql"},
36 "application/x-ssp": {"exts": ["*.ssp"], "mode": ""},
36 "application/x-ssp": {"exts": ["*.ssp"], "mode": ""},
37 "application/x-troff": {"exts": ["*.[1234567]","*.man"], "mode": ""},
37 "application/x-troff": {"exts": ["*.[1234567]","*.man"], "mode": ""},
38 "application/x-urbiscript": {"exts": ["*.u"], "mode": ""},
38 "application/x-urbiscript": {"exts": ["*.u"], "mode": ""},
39 "application/xml": {"exts": ["*.xml","*.xsl","*.rss","*.xslt","*.xsd","*.wsdl"], "mode": "xml"},
39 "application/xml": {"exts": ["*.xml","*.xsl","*.rss","*.xslt","*.xsd","*.wsdl"], "mode": "xml"},
40 "application/xml+evoque": {"exts": ["*.xml"], "mode": ""},
40 "application/xml+evoque": {"exts": ["*.xml"], "mode": ""},
41 "application/xml-dtd": {"exts": ["*.dtd"], "mode": "dtd"},
41 "application/xml-dtd": {"exts": ["*.dtd"], "mode": "dtd"},
42 "application/xquery": {"exts": ["*.xqy","*.xquery","*.xq","*.xql","*.xqm","*.xy"], "mode": "xquery"},
42 "application/xquery": {"exts": ["*.xqy","*.xquery","*.xq","*.xql","*.xqm","*.xy"], "mode": "xquery"},
43 "application/xsl+xml": {"exts": ["*.xsl","*.xslt","*.xpl"], "mode": ""},
43 "application/xsl+xml": {"exts": ["*.xsl","*.xslt","*.xpl"], "mode": ""},
44 "jinja2": {"exts": [".jinja2"], "mode": "jinja2"},
44 "jinja2": {"exts": [".jinja2"], "mode": "jinja2"},
45 "message/http": {"exts": [], "mode": "http"},
45 "message/http": {"exts": [], "mode": "http"},
46 "text/S-plus": {"exts": ["*.S","*.R",".Rhistory",".Rprofile"], "mode": ""},
46 "text/S-plus": {"exts": ["*.S","*.R",".Rhistory",".Rprofile"], "mode": ""},
47 "text/apl": {"exts": ["*.dyalog","*.pgp","*.apl"], "mode": "apl"},
47 "text/apl": {"exts": ["*.dyalog","*.pgp","*.apl"], "mode": "apl"},
48 "text/coffeescript": {"exts": ["*.coffee"], "mode": ""},
48 "text/coffeescript": {"exts": ["*.coffee"], "mode": ""},
49 "text/css": {"exts": ["*.css"], "mode": "css"},
49 "text/css": {"exts": ["*.css"], "mode": "css"},
50 "text/haxe": {"exts": ["*.hx"], "mode": ""},
50 "text/haxe": {"exts": ["*.hx"], "mode": ""},
51 "text/html": {"exts": ["*.html","*.htm","*.xhtml","*.xslt"], "mode": "htmlmixed"},
51 "text/html": {"exts": ["*.html","*.htm","*.xhtml","*.xslt"], "mode": "htmlmixed"},
52 "text/html+evoque": {"exts": ["*.html"], "mode": ""},
52 "text/html+evoque": {"exts": ["*.html"], "mode": ""},
53 "text/html+ruby": {"exts": ["*.rhtml"], "mode": ""},
53 "text/html+ruby": {"exts": ["*.rhtml"], "mode": ""},
54 "text/idl": {"exts": ["*.pro"], "mode": ""},
54 "text/idl": {"exts": ["*.pro"], "mode": ""},
55 "text/javascript": {"exts": ["*.js"], "mode": "javascript"},
55 "text/javascript": {"exts": ["*.js"], "mode": "javascript"},
56 "text/livescript": {"exts": ["*.ls"], "mode": ""},
56 "text/livescript": {"exts": ["*.ls"], "mode": ""},
57 "text/matlab": {"exts": ["*.m"], "mode": ""},
57 "text/matlab": {"exts": ["*.m"], "mode": ""},
58 "text/mirc": {"exts": [], "mode": "mirc"},
58 "text/mirc": {"exts": [], "mode": "mirc"},
59 "text/n-triples": {"exts": ["*.nt"], "mode": "ntriples"},
59 "text/n-triples": {"exts": ["*.nt"], "mode": "ntriples"},
60 "text/octave": {"exts": ["*.m"], "mode": ""},
60 "text/octave": {"exts": ["*.m"], "mode": ""},
61 "text/plain": {"exts": ["*.txt","*.text","*.conf","*.def","*.list","*.log"], "mode": "null"},
61 "text/plain": {"exts": ["*.txt","*.text","*.conf","*.def","*.list","*.log"], "mode": "null"},
62 "text/scilab": {"exts": ["*.sci","*.sce","*.tst"], "mode": ""},
62 "text/scilab": {"exts": ["*.sci","*.sce","*.tst"], "mode": ""},
63 "text/smali": {"exts": ["*.smali"], "mode": ""},
63 "text/smali": {"exts": ["*.smali"], "mode": ""},
64 "text/tiki": {"exts": [], "mode": "tiki"},
64 "text/tiki": {"exts": [], "mode": "tiki"},
65 "text/vbscript": {"exts": ["*.vb","*.vbs"], "mode": "vbscript"},
65 "text/vbscript": {"exts": ["*.vb","*.vbs"], "mode": "vbscript"},
66 "text/velocity": {"exts": ["*.vtl"], "mode": "velocity"},
66 "text/velocity": {"exts": ["*.vtl"], "mode": "velocity"},
67 "text/x-abap": {"exts": ["*.abap"], "mode": ""},
67 "text/x-abap": {"exts": ["*.abap"], "mode": ""},
68 "text/x-ada": {"exts": ["*.adb","*.ads","*.ada"], "mode": ""},
68 "text/x-ada": {"exts": ["*.adb","*.ads","*.ada"], "mode": ""},
69 "text/x-apacheconf": {"exts": [".htaccess","apache.conf","apache2.conf"], "mode": ""},
69 "text/x-apacheconf": {"exts": [".htaccess","apache.conf","apache2.conf"], "mode": ""},
70 "text/x-aspectj": {"exts": ["*.aj"], "mode": ""},
70 "text/x-aspectj": {"exts": ["*.aj"], "mode": ""},
71 "text/x-asterisk": {"exts": [], "mode": "asterisk"},
71 "text/x-asterisk": {"exts": [], "mode": "asterisk"},
72 "text/x-asymptote": {"exts": ["*.asy"], "mode": ""},
72 "text/x-asymptote": {"exts": ["*.asy"], "mode": ""},
73 "text/x-autohotkey": {"exts": ["*.ahk","*.ahkl"], "mode": ""},
73 "text/x-autohotkey": {"exts": ["*.ahk","*.ahkl"], "mode": ""},
74 "text/x-autoit": {"exts": ["*.au3"], "mode": ""},
74 "text/x-autoit": {"exts": ["*.au3"], "mode": ""},
75 "text/x-bmx": {"exts": ["*.bmx"], "mode": ""},
75 "text/x-bmx": {"exts": ["*.bmx"], "mode": ""},
76 "text/x-boo": {"exts": ["*.boo"], "mode": ""},
76 "text/x-boo": {"exts": ["*.boo"], "mode": ""},
77 "text/x-c": {"exts": ["*.c"], "mode": "clike"},
77 "text/x-c": {"exts": ["*.c"], "mode": "clike"},
78 "text/x-c++hdr": {"exts": ["*.cpp","*.hpp","*.c++","*.h++","*.cc","*.hh","*.cxx","*.hxx","*.C","*.H","*.cp","*.CPP"], "mode": "clike"},
78 "text/x-c++hdr": {"exts": ["*.cpp","*.hpp","*.c++","*.h++","*.cc","*.hh","*.cxx","*.hxx","*.C","*.H","*.cp","*.CPP"], "mode": "clike"},
79 "text/x-c++src": {"exts": ["*.cpp","*.c++","*.cc","*.cxx","*.hpp","*.h++","*.hh","*.hxx"], "mode": "clike"},
79 "text/x-c++src": {"exts": ["*.cpp","*.c++","*.cc","*.cxx","*.hpp","*.h++","*.hh","*.hxx"], "mode": "clike"},
80 "text/x-c-objdump": {"exts": ["*.c-objdump"], "mode": ""},
80 "text/x-c-objdump": {"exts": ["*.c-objdump"], "mode": ""},
81 "text/x-ceylon": {"exts": ["*.ceylon"], "mode": ""},
81 "text/x-ceylon": {"exts": ["*.ceylon"], "mode": ""},
82 "text/x-chdr": {"exts": ["*.c","*.h","*.idc"], "mode": "clike"},
82 "text/x-chdr": {"exts": ["*.c","*.h","*.idc"], "mode": "clike"},
83 "text/x-clojure": {"exts": ["*.clj"], "mode": "clojure"},
83 "text/x-clojure": {"exts": ["*.clj"], "mode": "clojure"},
84 "text/x-cmake": {"exts": ["*.cmake","CMakeLists.txt","*.cmake.in"], "mode": "cmake"},
84 "text/x-cmake": {"exts": ["*.cmake","CMakeLists.txt","*.cmake.in"], "mode": "cmake"},
85 "text/x-cobol": {"exts": ["*.cob","*.COB","*.cpy","*.CPY"], "mode": "cobol"},
85 "text/x-cobol": {"exts": ["*.cob","*.COB","*.cpy","*.CPY"], "mode": "cobol"},
86 "text/x-coffeescript": {"exts": ["*.coffee"], "mode": "coffeescript"},
86 "text/x-coffeescript": {"exts": ["*.coffee"], "mode": "coffeescript"},
87 "text/x-common-lisp": {"exts": ["*.cl","*.lisp","*.el"], "mode": "commonlisp"},
87 "text/x-common-lisp": {"exts": ["*.cl","*.lisp","*.el"], "mode": "commonlisp"},
88 "text/x-coq": {"exts": ["*.v"], "mode": ""},
88 "text/x-coq": {"exts": ["*.v"], "mode": ""},
89 "text/x-cpp-objdump": {"exts": ["*.cpp-objdump","*.c++-objdump","*.cxx-objdump"], "mode": ""},
89 "text/x-cpp-objdump": {"exts": ["*.cpp-objdump","*.c++-objdump","*.cxx-objdump"], "mode": ""},
90 "text/x-crocsrc": {"exts": ["*.croc"], "mode": ""},
90 "text/x-crocsrc": {"exts": ["*.croc"], "mode": ""},
91 "text/x-csharp": {"exts": ["*.cs"], "mode": "clike"},
91 "text/x-csharp": {"exts": ["*.cs"], "mode": "clike"},
92 "text/x-csrc": {"exts": ["*.c","*.h"], "mode": "clike"},
92 "text/x-csrc": {"exts": ["*.c","*.h"], "mode": "clike"},
93 "text/x-cuda": {"exts": ["*.cu","*.cuh"], "mode": ""},
93 "text/x-cuda": {"exts": ["*.cu","*.cuh"], "mode": ""},
94 "text/x-cython": {"exts": ["*.pyx","*.pxd","*.pxi"], "mode": "python"},
94 "text/x-cython": {"exts": ["*.pyx","*.pxd","*.pxi"], "mode": "python"},
95 "text/x-d": {"exts": ["*.d"], "mode": "d"},
95 "text/x-d": {"exts": ["*.d"], "mode": "d"},
96 "text/x-d-objdump": {"exts": ["*.d-objdump"], "mode": ""},
96 "text/x-d-objdump": {"exts": ["*.d-objdump"], "mode": ""},
97 "text/x-dart": {"exts": ["*.dart"], "mode": ""},
97 "text/x-dart": {"exts": ["*.dart"], "mode": ""},
98 "text/x-dg": {"exts": ["*.dg"], "mode": ""},
98 "text/x-dg": {"exts": ["*.dg"], "mode": ""},
99 "text/x-diff": {"exts": ["*.diff","*.patch"], "mode": "diff"},
99 "text/x-diff": {"exts": ["*.diff","*.patch"], "mode": "diff"},
100 "text/x-dsrc": {"exts": ["*.d","*.di"], "mode": ""},
100 "text/x-dsrc": {"exts": ["*.d","*.di"], "mode": ""},
101 "text/x-duel": {"exts": ["*.duel","*.jbst"], "mode": ""},
101 "text/x-duel": {"exts": ["*.duel","*.jbst"], "mode": ""},
102 "text/x-dylan": {"exts": ["*.dylan","*.dyl","*.intr"], "mode": "dylan"},
102 "text/x-dylan": {"exts": ["*.dylan","*.dyl","*.intr"], "mode": "dylan"},
103 "text/x-dylan-console": {"exts": ["*.dylan-console"], "mode": ""},
103 "text/x-dylan-console": {"exts": ["*.dylan-console"], "mode": ""},
104 "text/x-dylan-lid": {"exts": ["*.lid","*.hdp"], "mode": ""},
104 "text/x-dylan-lid": {"exts": ["*.lid","*.hdp"], "mode": ""},
105 "text/x-echdr": {"exts": ["*.ec","*.eh"], "mode": ""},
105 "text/x-echdr": {"exts": ["*.ec","*.eh"], "mode": ""},
106 "text/x-ecl": {"exts": ["*.ecl"], "mode": "ecl"},
106 "text/x-ecl": {"exts": ["*.ecl"], "mode": "ecl"},
107 "text/x-elixir": {"exts": ["*.ex","*.exs"], "mode": ""},
107 "text/x-elixir": {"exts": ["*.ex","*.exs"], "mode": ""},
108 "text/x-erl-shellsession": {"exts": ["*.erl-sh"], "mode": ""},
108 "text/x-erl-shellsession": {"exts": ["*.erl-sh"], "mode": ""},
109 "text/x-erlang": {"exts": ["*.erl","*.hrl","*.es","*.escript"], "mode": "erlang"},
109 "text/x-erlang": {"exts": ["*.erl","*.hrl","*.es","*.escript"], "mode": "erlang"},
110 "text/x-factor": {"exts": ["*.factor"], "mode": "factor"},
110 "text/x-factor": {"exts": ["*.factor"], "mode": "factor"},
111 "text/x-fancysrc": {"exts": ["*.fy","*.fancypack"], "mode": ""},
111 "text/x-fancysrc": {"exts": ["*.fy","*.fancypack"], "mode": ""},
112 "text/x-felix": {"exts": ["*.flx","*.flxh"], "mode": ""},
112 "text/x-felix": {"exts": ["*.flx","*.flxh"], "mode": ""},
113 "text/x-fortran": {"exts": ["*.f","*.f90","*.F","*.F90","*.for","*.f77"], "mode": "fortran"},
113 "text/x-fortran": {"exts": ["*.f","*.f90","*.F","*.F90","*.for","*.f77"], "mode": "fortran"},
114 "text/x-fsharp": {"exts": ["*.fs","*.fsi"], "mode": "mllike"},
114 "text/x-fsharp": {"exts": ["*.fs","*.fsi"], "mode": "mllike"},
115 "text/x-gas": {"exts": ["*.s","*.S"], "mode": "gas"},
115 "text/x-gas": {"exts": ["*.s","*.S"], "mode": "gas"},
116 "text/x-gfm": {"exts": ["*.md","*.MD"], "mode": "markdown"},
116 "text/x-gfm": {"exts": ["*.md","*.MD"], "mode": "gfm"},
117 "text/x-gherkin": {"exts": ["*.feature"], "mode": ""},
117 "text/x-gherkin": {"exts": ["*.feature"], "mode": ""},
118 "text/x-glslsrc": {"exts": ["*.vert","*.frag","*.geo"], "mode": ""},
118 "text/x-glslsrc": {"exts": ["*.vert","*.frag","*.geo"], "mode": ""},
119 "text/x-gnuplot": {"exts": ["*.plot","*.plt"], "mode": ""},
119 "text/x-gnuplot": {"exts": ["*.plot","*.plt"], "mode": ""},
120 "text/x-go": {"exts": ["*.go"], "mode": "go"},
120 "text/x-go": {"exts": ["*.go"], "mode": "go"},
121 "text/x-gooddata-cl": {"exts": ["*.gdc"], "mode": ""},
121 "text/x-gooddata-cl": {"exts": ["*.gdc"], "mode": ""},
122 "text/x-gooddata-maql": {"exts": ["*.maql"], "mode": ""},
122 "text/x-gooddata-maql": {"exts": ["*.maql"], "mode": ""},
123 "text/x-gosrc": {"exts": ["*.go"], "mode": ""},
123 "text/x-gosrc": {"exts": ["*.go"], "mode": ""},
124 "text/x-gosu": {"exts": ["*.gs","*.gsx","*.gsp","*.vark"], "mode": ""},
124 "text/x-gosu": {"exts": ["*.gs","*.gsx","*.gsp","*.vark"], "mode": ""},
125 "text/x-gosu-template": {"exts": ["*.gst"], "mode": ""},
125 "text/x-gosu-template": {"exts": ["*.gst"], "mode": ""},
126 "text/x-groovy": {"exts": ["*.groovy"], "mode": "groovy"},
126 "text/x-groovy": {"exts": ["*.groovy"], "mode": "groovy"},
127 "text/x-haml": {"exts": ["*.haml"], "mode": "haml"},
127 "text/x-haml": {"exts": ["*.haml"], "mode": "haml"},
128 "text/x-haskell": {"exts": ["*.hs"], "mode": "haskell"},
128 "text/x-haskell": {"exts": ["*.hs"], "mode": "haskell"},
129 "text/x-haxe": {"exts": ["*.hx"], "mode": "haxe"},
129 "text/x-haxe": {"exts": ["*.hx"], "mode": "haxe"},
130 "text/x-hybris": {"exts": ["*.hy","*.hyb"], "mode": ""},
130 "text/x-hybris": {"exts": ["*.hy","*.hyb"], "mode": ""},
131 "text/x-ini": {"exts": ["*.ini","*.cfg"], "mode": ""},
131 "text/x-ini": {"exts": ["*.ini","*.cfg"], "mode": ""},
132 "text/x-iokesrc": {"exts": ["*.ik"], "mode": ""},
132 "text/x-iokesrc": {"exts": ["*.ik"], "mode": ""},
133 "text/x-iosrc": {"exts": ["*.io"], "mode": ""},
133 "text/x-iosrc": {"exts": ["*.io"], "mode": ""},
134 "text/x-irclog": {"exts": ["*.weechatlog"], "mode": ""},
134 "text/x-irclog": {"exts": ["*.weechatlog"], "mode": ""},
135 "text/x-jade": {"exts": ["*.jade"], "mode": "jade"},
135 "text/x-jade": {"exts": ["*.jade"], "mode": "jade"},
136 "text/x-java": {"exts": ["*.java"], "mode": "clike"},
136 "text/x-java": {"exts": ["*.java"], "mode": "clike"},
137 "text/x-julia": {"exts": ["*.jl"], "mode": "julia"},
137 "text/x-julia": {"exts": ["*.jl"], "mode": "julia"},
138 "text/x-kconfig": {"exts": ["Kconfig","*Config.in*","external.in*","standard-modules.in"], "mode": ""},
138 "text/x-kconfig": {"exts": ["Kconfig","*Config.in*","external.in*","standard-modules.in"], "mode": ""},
139 "text/x-koka": {"exts": ["*.kk","*.kki"], "mode": ""},
139 "text/x-koka": {"exts": ["*.kk","*.kki"], "mode": ""},
140 "text/x-kotlin": {"exts": ["*.kt"], "mode": "kotlin"},
140 "text/x-kotlin": {"exts": ["*.kt"], "mode": "clike"},
141 "text/x-lasso": {"exts": ["*.lasso","*.lasso[89]"], "mode": ""},
141 "text/x-lasso": {"exts": ["*.lasso","*.lasso[89]"], "mode": ""},
142 "text/x-latex": {"exts": ["*.ltx","*.text"], "mode": "stex"},
142 "text/x-latex": {"exts": ["*.ltx","*.text"], "mode": "stex"},
143 "text/x-less": {"exts": ["*.less"], "mode": "css"},
143 "text/x-less": {"exts": ["*.less"], "mode": "css"},
144 "text/x-literate-haskell": {"exts": ["*.lhs"], "mode": ""},
144 "text/x-literate-haskell": {"exts": ["*.lhs"], "mode": "haskell-literate"},
145 "text/x-livescript": {"exts": ["*.ls"], "mode": "livescript"},
145 "text/x-livescript": {"exts": ["*.ls"], "mode": "livescript"},
146 "text/x-llvm": {"exts": ["*.ll"], "mode": ""},
146 "text/x-llvm": {"exts": ["*.ll"], "mode": ""},
147 "text/x-logos": {"exts": ["*.x","*.xi","*.xm","*.xmi"], "mode": ""},
147 "text/x-logos": {"exts": ["*.x","*.xi","*.xm","*.xmi"], "mode": ""},
148 "text/x-logtalk": {"exts": ["*.lgt"], "mode": ""},
148 "text/x-logtalk": {"exts": ["*.lgt"], "mode": ""},
149 "text/x-lua": {"exts": ["*.lua","*.wlua"], "mode": "lua"},
149 "text/x-lua": {"exts": ["*.lua","*.wlua"], "mode": "lua"},
150 "text/x-makefile": {"exts": ["*.mak","Makefile","makefile","Makefile.*","GNUmakefile"], "mode": ""},
150 "text/x-makefile": {"exts": ["*.mak","Makefile","makefile","Makefile.*","GNUmakefile"], "mode": ""},
151 "text/x-mariadb": {"exts": ["*.sql"], "mode": "sql"},
151 "text/x-mariadb": {"exts": ["*.sql"], "mode": "sql"},
152 "text/x-markdown": {"exts": ["*.md","*.markdown","*.mdown","*.mkd"], "mode": "gfm"},
152 "text/x-markdown": {"exts": ["*.md","*.markdown","*.mdown","*.mkd"], "mode": "gfm"},
153 "text/x-minidsrc": {"exts": ["*.md"], "mode": "gfm"},
153 "text/x-minidsrc": {"exts": ["*.md"], "mode": "gfm"},
154 "text/x-modelica": {"exts": ["*.mo"], "mode": "modelica"},
154 "text/x-modelica": {"exts": ["*.mo"], "mode": "modelica"},
155 "text/x-modula2": {"exts": ["*.def","*.mod"], "mode": ""},
155 "text/x-modula2": {"exts": ["*.def","*.mod"], "mode": ""},
156 "text/x-monkey": {"exts": ["*.monkey"], "mode": ""},
156 "text/x-monkey": {"exts": ["*.monkey"], "mode": ""},
157 "text/x-moocode": {"exts": ["*.moo"], "mode": ""},
157 "text/x-moocode": {"exts": ["*.moo"], "mode": ""},
158 "text/x-moonscript": {"exts": ["*.moon"], "mode": ""},
158 "text/x-moonscript": {"exts": ["*.moon"], "mode": ""},
159 "text/x-nasm": {"exts": ["*.asm","*.ASM"], "mode": ""},
159 "text/x-nasm": {"exts": ["*.asm","*.ASM"], "mode": ""},
160 "text/x-nemerle": {"exts": ["*.n"], "mode": ""},
160 "text/x-nemerle": {"exts": ["*.n"], "mode": ""},
161 "text/x-newlisp": {"exts": ["*.lsp","*.nl"], "mode": ""},
161 "text/x-newlisp": {"exts": ["*.lsp","*.nl"], "mode": ""},
162 "text/x-newspeak": {"exts": ["*.ns2"], "mode": ""},
162 "text/x-newspeak": {"exts": ["*.ns2"], "mode": ""},
163 "text/x-nginx-conf": {"exts": ["*.conf"], "mode": "nginx"},
163 "text/x-nginx-conf": {"exts": ["*.conf"], "mode": "nginx"},
164 "text/x-nimrod": {"exts": ["*.nim","*.nimrod"], "mode": ""},
164 "text/x-nimrod": {"exts": ["*.nim","*.nimrod"], "mode": ""},
165 "text/x-nsis": {"exts": ["*.nsi","*.nsh"], "mode": ""},
165 "text/x-nsis": {"exts": ["*.nsi","*.nsh"], "mode": "nsis"},
166 "text/x-objdump": {"exts": ["*.objdump"], "mode": ""},
166 "text/x-objdump": {"exts": ["*.objdump"], "mode": ""},
167 "text/x-objective-c": {"exts": ["*.m","*.h"], "mode": ""},
167 "text/x-objective-c": {"exts": ["*.m","*.h"], "mode": ""},
168 "text/x-objective-c++": {"exts": ["*.mm","*.hh"], "mode": ""},
168 "text/x-objective-c++": {"exts": ["*.mm","*.hh"], "mode": ""},
169 "text/x-objective-j": {"exts": ["*.j"], "mode": ""},
169 "text/x-objective-j": {"exts": ["*.j"], "mode": ""},
170 "text/x-ocaml": {"exts": ["*.ml","*.mli","*.mll","*.mly"], "mode": "mllike"},
170 "text/x-ocaml": {"exts": ["*.ml","*.mli","*.mll","*.mly"], "mode": "mllike"},
171 "text/x-ooc": {"exts": ["*.ooc"], "mode": ""},
171 "text/x-ooc": {"exts": ["*.ooc"], "mode": ""},
172 "text/x-opa": {"exts": ["*.opa"], "mode": ""},
172 "text/x-opa": {"exts": ["*.opa"], "mode": ""},
173 "text/x-openedge": {"exts": ["*.p","*.cls"], "mode": ""},
173 "text/x-openedge": {"exts": ["*.p","*.cls"], "mode": ""},
174 "text/x-pascal": {"exts": ["*.pas","*.p"], "mode": "pascal"},
174 "text/x-pascal": {"exts": ["*.pas","*.p"], "mode": "pascal"},
175 "text/x-perl": {"exts": ["*.pl","*.pm"], "mode": "perl"},
175 "text/x-perl": {"exts": ["*.pl","*.pm"], "mode": "perl"},
176 "text/x-php": {"exts": ["*.php","*.php[345]","*.inc"], "mode": "php"},
176 "text/x-php": {"exts": ["*.php","*.php[345]","*.inc"], "mode": "php"},
177 "text/x-pig": {"exts": ["*.pig"], "mode": "pig"},
177 "text/x-pig": {"exts": ["*.pig"], "mode": "pig"},
178 "text/x-povray": {"exts": ["*.pov","*.inc"], "mode": ""},
178 "text/x-povray": {"exts": ["*.pov","*.inc"], "mode": ""},
179 "text/x-powershell": {"exts": ["*.ps1"], "mode": ""},
179 "text/x-powershell": {"exts": ["*.ps1"], "mode": ""},
180 "text/x-prolog": {"exts": ["*.prolog","*.pro","*.pl"], "mode": ""},
180 "text/x-prolog": {"exts": ["*.prolog","*.pro","*.pl"], "mode": ""},
181 "text/x-properties": {"exts": ["*.properties","*.ini","*.in"], "mode": "properties"},
181 "text/x-properties": {"exts": ["*.properties","*.ini","*.in"], "mode": "properties"},
182 "text/x-python": {"exts": ["*.py","*.pyw","*.sc","SConstruct","SConscript","*.tac","*.sage"], "mode": "python"},
182 "text/x-python": {"exts": ["*.py","*.pyw","*.sc","SConstruct","SConscript","*.tac","*.sage"], "mode": "python"},
183 "text/x-python-traceback": {"exts": ["*.pytb"], "mode": ""},
183 "text/x-python-traceback": {"exts": ["*.pytb"], "mode": ""},
184 "text/x-python3-traceback": {"exts": ["*.py3tb"], "mode": ""},
184 "text/x-python3-traceback": {"exts": ["*.py3tb"], "mode": ""},
185 "text/x-r-doc": {"exts": ["*.Rd"], "mode": ""},
185 "text/x-r-doc": {"exts": ["*.Rd"], "mode": ""},
186 "text/x-racket": {"exts": ["*.rkt","*.rktl"], "mode": ""},
186 "text/x-racket": {"exts": ["*.rkt","*.rktl"], "mode": ""},
187 "text/x-rebol": {"exts": ["*.r","*.r3"], "mode": ""},
187 "text/x-rebol": {"exts": ["*.r","*.r3"], "mode": ""},
188 "text/x-robotframework": {"exts": ["*.txt","*.robot"], "mode": ""},
188 "text/x-robotframework": {"exts": ["*.txt","*.robot"], "mode": ""},
189 "text/x-rpm-spec": {"exts": ["*.spec"], "mode": "rpm"},
189 "text/x-rpm-spec": {"exts": ["*.spec"], "mode": "rpm"},
190 "text/x-rsrc": {"exts": ["*.r"], "mode": "r"},
190 "text/x-rsrc": {"exts": ["*.r"], "mode": "r"},
191 "text/x-rst": {"exts": ["*.rst","*.rest"], "mode": "rst"},
191 "text/x-rst": {"exts": ["*.rst","*.rest"], "mode": "rst"},
192 "text/x-ruby": {"exts": ["*.rb","*.rbw","Rakefile","*.rake","*.gemspec","*.rbx","*.duby"], "mode": "ruby"},
192 "text/x-ruby": {"exts": ["*.rb","*.rbw","Rakefile","*.rake","*.gemspec","*.rbx","*.duby"], "mode": "ruby"},
193 "text/x-rustsrc": {"exts": ["*.rs","*.rc"], "mode": "rust"},
193 "text/x-rustsrc": {"exts": ["*.rs","*.rc"], "mode": "rust"},
194 "text/x-sass": {"exts": ["*.sass"], "mode": "sass"},
194 "text/x-sass": {"exts": ["*.sass"], "mode": "sass"},
195 "text/x-scala": {"exts": ["*.scala"], "mode": "clike"},
195 "text/x-scala": {"exts": ["*.scala"], "mode": "clike"},
196 "text/x-scaml": {"exts": ["*.scaml"], "mode": ""},
196 "text/x-scaml": {"exts": ["*.scaml"], "mode": ""},
197 "text/x-scheme": {"exts": ["*.scm","*.ss"], "mode": "scheme"},
197 "text/x-scheme": {"exts": ["*.scm","*.ss"], "mode": "scheme"},
198 "text/x-scss": {"exts": ["*.scss"], "mode": "css"},
198 "text/x-scss": {"exts": ["*.scss"], "mode": "css"},
199 "text/x-sh": {"exts": ["*.sh","*.ksh","*.bash","*.ebuild","*.eclass",".bashrc","bashrc",".bash_*","bash_*"], "mode": "shell"},
199 "text/x-sh": {"exts": ["*.sh","*.ksh","*.bash","*.ebuild","*.eclass",".bashrc","bashrc",".bash_*","bash_*"], "mode": "shell"},
200 "text/x-smalltalk": {"exts": ["*.st"], "mode": ""},
200 "text/x-smalltalk": {"exts": ["*.st"], "mode": ""},
201 "text/x-smarty": {"exts": ["*.tpl"], "mode": "smarty"},
201 "text/x-smarty": {"exts": ["*.tpl"], "mode": "smarty"},
202 "text/x-snobol": {"exts": ["*.snobol"], "mode": ""},
202 "text/x-snobol": {"exts": ["*.snobol"], "mode": ""},
203 "text/x-sourcepawn": {"exts": ["*.sp"], "mode": ""},
203 "text/x-sourcepawn": {"exts": ["*.sp"], "mode": ""},
204 "text/x-sql": {"exts": ["*.sql"], "mode": "sql"},
204 "text/x-sql": {"exts": ["*.sql"], "mode": "sql"},
205 "text/x-sqlite3-console": {"exts": ["*.sqlite3-console"], "mode": ""},
205 "text/x-sqlite3-console": {"exts": ["*.sqlite3-console"], "mode": ""},
206 "text/x-squidconf": {"exts": ["squid.conf"], "mode": ""},
206 "text/x-squidconf": {"exts": ["squid.conf"], "mode": ""},
207 "text/x-standardml": {"exts": ["*.sml","*.sig","*.fun"], "mode": ""},
207 "text/x-standardml": {"exts": ["*.sml","*.sig","*.fun"], "mode": ""},
208 "text/x-stex": {"exts": [], "mode": "stex"},
208 "text/x-stex": {"exts": [], "mode": "stex"},
209 "text/x-stsrc": {"exts": ["*.rs","*.rc","*.st"], "mode": "smalltalk"},
209 "text/x-stsrc": {"exts": ["*.rs","*.rc","*.st"], "mode": "smalltalk"},
210 "text/x-systemverilog": {"exts": ["*.sv","*.svh","*.v"], "mode": "verilog"},
210 "text/x-systemverilog": {"exts": ["*.sv","*.svh","*.v"], "mode": "verilog"},
211 "text/x-tcl": {"exts": ["*.tcl"], "mode": "tcl"},
211 "text/x-tcl": {"exts": ["*.tcl"], "mode": "tcl"},
212 "text/x-tea": {"exts": ["*.tea"], "mode": ""},
212 "text/x-tea": {"exts": ["*.tea"], "mode": ""},
213 "text/x-tex": {"exts": ["*.tex","*.aux","*.toc"], "mode": ""},
213 "text/x-tex": {"exts": ["*.tex","*.aux","*.toc"], "mode": ""},
214 "text/x-tiddlywiki": {"exts": [], "mode": "tiddlywiki"},
214 "text/x-tiddlywiki": {"exts": [], "mode": "tiddlywiki"},
215 "text/x-typescript": {"exts": ["*.ts"], "mode": ""},
215 "text/x-typescript": {"exts": ["*.ts"], "mode": ""},
216 "text/x-vala": {"exts": ["*.vala","*.vapi"], "mode": ""},
216 "text/x-vala": {"exts": ["*.vala","*.vapi"], "mode": ""},
217 "text/x-vb": {"exts": ["*.vb"], "mode": "vb"},
217 "text/x-vb": {"exts": ["*.vb"], "mode": "vb"},
218 "text/x-vbnet": {"exts": ["*.vb","*.bas"], "mode": ""},
218 "text/x-vbnet": {"exts": ["*.vb","*.bas"], "mode": ""},
219 "text/x-verilog": {"exts": ["*.v"], "mode": "verilog"},
219 "text/x-verilog": {"exts": ["*.v"], "mode": "verilog"},
220 "text/x-vhdl": {"exts": ["*.vhdl","*.vhd"], "mode": ""},
220 "text/x-vhdl": {"exts": ["*.vhdl","*.vhd"], "mode": "vhdl"},
221 "text/x-vim": {"exts": ["*.vim",".vimrc",".exrc",".gvimrc","_vimrc","_exrc","_gvimrc","vimrc","gvimrc"], "mode": ""},
221 "text/x-vim": {"exts": ["*.vim",".vimrc",".exrc",".gvimrc","_vimrc","_exrc","_gvimrc","vimrc","gvimrc"], "mode": ""},
222 "text/x-windows-registry": {"exts": ["*.reg"], "mode": ""},
222 "text/x-windows-registry": {"exts": ["*.reg"], "mode": ""},
223 "text/x-xtend": {"exts": ["*.xtend"], "mode": ""},
223 "text/x-xtend": {"exts": ["*.xtend"], "mode": ""},
224 "text/x-yaml": {"exts": ["*.yaml","*.yml"], "mode": "yaml"},
224 "text/x-yaml": {"exts": ["*.yaml","*.yml"], "mode": "yaml"},
225 "text/x-z80": {"exts": ["*.z80"], "mode": "z80"},
225 "text/x-z80": {"exts": ["*.z80"], "mode": "z80"},
226 "text/xml": {"exts": ["*.xml","*.xsl","*.rss","*.xslt","*.xsd","*.wsdl"], "mode": ""},
226 "text/xml": {"exts": ["*.xml","*.xsl","*.rss","*.xslt","*.xsd","*.wsdl"], "mode": ""},
227 "text/xquery": {"exts": ["*.xqy","*.xquery","*.xq","*.xql","*.xqm"], "mode": ""}
227 "text/xquery": {"exts": ["*.xqy","*.xquery","*.xq","*.xql","*.xqm"], "mode": ""}
228 };
228 };
229
229
230 /* Special case for overriding mode by file extensions
230 /* Special case for overriding mode by file extensions
231 * key is extensions, value is codemirror mode
231 * key is extensions, value is codemirror mode
232 * */
232 * */
233 _SPECIAL_CASES = {
233 _SPECIAL_CASES = {
234 "md": "markdown",
234 "md": "markdown",
235 "markdown": "markdown"
235 "markdown": "markdown"
236 };
236 };
237
237
238 /**
238 /**
239 * Get's proposed extension based on given mimetype
239 * Get's proposed extension based on given mimetype
240 *
240 *
241 * @param mimetype
241 * @param mimetype
242 * @returns extensions (default .txt)
242 * @returns extensions (default .txt)
243 */
243 */
244 var getExtFromMimeType = function(mimetype){
244 var getExtFromMimeType = function(mimetype){
245
245
246 var proposed_exts = MIME_TO_EXT[mimetype] || _EMPTY_EXT;
246 var proposed_exts = MIME_TO_EXT[mimetype] || _EMPTY_EXT;
247 if(proposed_exts.exts.length < 1){
247 if(proposed_exts.exts.length < 1){
248 //fallback to text/plain
248 //fallback to text/plain
249 proposed_exts = {'exts': ['*.txt'], 'mode': '' }
249 proposed_exts = {'exts': ['*.txt'], 'mode': '' }
250 }
250 }
251 // get the first
251 // get the first
252 var ext = proposed_exts.exts[0];
252 var ext = proposed_exts.exts[0];
253 if(ext[0] == '*'){
253 if(ext[0] == '*'){
254 ext = ext.substr(1)
254 ext = ext.substr(1)
255 }
255 }
256
256
257 return ext
257 return ext
258 };
258 };
259
259
260 var getMimeTypeFromExt = function(ext, multiple){
260 var getMimeTypeFromExt = function(ext, multiple){
261 mimetypes = [];
261 mimetypes = [];
262 for (k in MIME_TO_EXT){
262 for (k in MIME_TO_EXT){
263 var mode = MIME_TO_EXT[k];
263 var mode = MIME_TO_EXT[k];
264 if ($.inArray("*."+ext, mode.exts) != -1){
264 if ($.inArray("*."+ext, mode.exts) != -1){
265 mimetypes.push(k)
265 mimetypes.push(k)
266 }
266 }
267 }
267 }
268 if(multiple){
268 if(multiple){
269 return mimetypes
269 return mimetypes
270 }
270 }
271 if(mimetypes.length > 0){
271 if(mimetypes.length > 0){
272 return mimetypes[0]
272 return mimetypes[0]
273 }
273 }
274
274
275 };
275 };
276
276
277 var getFilenameAndExt = function(filename){
277 var getFilenameAndExt = function(filename){
278 var parts = filename.split('.');
278 var parts = filename.split('.');
279 var ext = null;
279 var ext = null;
280 var filename = null;
280 var filename = null;
281
281
282 if (parts.length > 1){
282 if (parts.length > 1){
283 var ext = parts.pop();
283 var ext = parts.pop();
284 var filename = parts.join("");
284 var filename = parts.join("");
285 }
285 }
286 return {"filename": filename, "ext": ext}
286 return {"filename": filename, "ext": ext}
287 }
287 }
288
288
289 /**
289 /**
290 * Detect mode from extension, this is mostly used to override the
290 * Detect mode from extension, this is mostly used to override the
291 * detection by mimetype
291 * detection by mimetype
292 *
292 *
293 * @param filename
293 * @param filename
294 */
294 */
295 var detectCodeMirrorModeFromExt = function(filename, fallback){
295 var detectCodeMirrorModeFromExt = function(filename, fallback){
296 var ext = filename.split('.');
296 var ext = filename.split('.');
297 if (ext){
297 if (ext){
298 var ext = ext[ext.length-1];
298 var ext = ext[ext.length-1];
299 }
299 }
300 // try to do a lookup by extension
300 // try to do a lookup by extension
301 var _special_mode = _SPECIAL_CASES[ext];
301 var _special_mode = _SPECIAL_CASES[ext];
302 if (_special_mode){
302 if (_special_mode){
303 return _special_mode
303 return _special_mode
304 }
304 }
305 if(fallback !== undefined && fallback === true){
305 if(fallback !== undefined && fallback === true){
306 var mimetype = getMimeTypeFromExt(ext);
306 var mimetype = getMimeTypeFromExt(ext);
307 if(mimetype){
307 if(mimetype){
308 return MIME_TO_EXT[mimetype].mode;
308 return MIME_TO_EXT[mimetype].mode;
309 }
309 }
310
310
311 }
311 }
312 }
312 }
313
313
314
314
315 /**
315 /**
316 * Try to detect a codemirror mode based on a filename and mimetype
316 * Try to detect a codemirror mode based on a filename and mimetype
317 *
317 *
318 * @param filename
318 * @param filename
319 * @param mimetype
319 * @param mimetype
320 * @returns mode or undefined
320 * @returns mode or undefined
321 */
321 */
322 var detectCodeMirrorMode = function(filename, mimetype, fallback){
322 var detectCodeMirrorMode = function(filename, mimetype, fallback){
323 // just use _SPECIAL_CASES for detection here, as we usually got mimetype
323 // just use _SPECIAL_CASES for detection here, as we usually got mimetype
324 // and it's faster to lookup by mimetype.
324 // and it's faster to lookup by mimetype.
325 var do_fallback = fallback || false;
325 var do_fallback = fallback || false;
326 var _mode_from_ext = detectCodeMirrorModeFromExt(filename, do_fallback);
326 var _mode_from_ext = detectCodeMirrorModeFromExt(filename, do_fallback);
327 if(_mode_from_ext){
327 if(_mode_from_ext){
328 return _mode_from_ext
328 return _mode_from_ext
329 }
329 }
330
330
331 // first try to match by exact mimetype
331 // first try to match by exact mimetype
332 var mode = MIME_TO_EXT[mimetype];
332 var mode = MIME_TO_EXT[mimetype];
333 if(mode && mode.mode){
333 if(mode && mode.mode){
334 return mode.mode;
334 return mode.mode;
335 }
335 }
336 }
336 }
@@ -1,178 +1,178 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("nginx", function(config) {
14 CodeMirror.defineMode("nginx", function(config) {
15
15
16 function words(str) {
16 function words(str) {
17 var obj = {}, words = str.split(" ");
17 var obj = {}, words = str.split(" ");
18 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
18 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
19 return obj;
19 return obj;
20 }
20 }
21
21
22 var keywords = words(
22 var keywords = words(
23 /* ngxDirectiveControl */ "break return rewrite set" +
23 /* ngxDirectiveControl */ "break return rewrite set" +
24 /* ngxDirective */ " accept_mutex accept_mutex_delay access_log add_after_body add_before_body add_header addition_types aio alias allow ancient_browser ancient_browser_value auth_basic auth_basic_user_file auth_http auth_http_header auth_http_timeout autoindex autoindex_exact_size autoindex_localtime charset charset_types client_body_buffer_size client_body_in_file_only client_body_in_single_buffer client_body_temp_path client_body_timeout client_header_buffer_size client_header_timeout client_max_body_size connection_pool_size create_full_put_path daemon dav_access dav_methods debug_connection debug_points default_type degradation degrade deny devpoll_changes devpoll_events directio directio_alignment empty_gif env epoll_events error_log eventport_events expires fastcgi_bind fastcgi_buffer_size fastcgi_buffers fastcgi_busy_buffers_size fastcgi_cache fastcgi_cache_key fastcgi_cache_methods fastcgi_cache_min_uses fastcgi_cache_path fastcgi_cache_use_stale fastcgi_cache_valid fastcgi_catch_stderr fastcgi_connect_timeout fastcgi_hide_header fastcgi_ignore_client_abort fastcgi_ignore_headers fastcgi_index fastcgi_intercept_errors fastcgi_max_temp_file_size fastcgi_next_upstream fastcgi_param fastcgi_pass_header fastcgi_pass_request_body fastcgi_pass_request_headers fastcgi_read_timeout fastcgi_send_lowat fastcgi_send_timeout fastcgi_split_path_info fastcgi_store fastcgi_store_access fastcgi_temp_file_write_size fastcgi_temp_path fastcgi_upstream_fail_timeout fastcgi_upstream_max_fails flv geoip_city geoip_country google_perftools_profiles gzip gzip_buffers gzip_comp_level gzip_disable gzip_hash gzip_http_version gzip_min_length gzip_no_buffer gzip_proxied gzip_static gzip_types gzip_vary gzip_window if_modified_since ignore_invalid_headers image_filter image_filter_buffer image_filter_jpeg_quality image_filter_transparency imap_auth imap_capabilities imap_client_buffer index ip_hash keepalive_requests keepalive_timeout kqueue_changes kqueue_events large_client_header_buffers limit_conn limit_conn_log_level limit_rate limit_rate_after limit_req limit_req_log_level limit_req_zone limit_zone lingering_time lingering_timeout lock_file log_format log_not_found log_subrequest map_hash_bucket_size map_hash_max_size master_process memcached_bind memcached_buffer_size memcached_connect_timeout memcached_next_upstream memcached_read_timeout memcached_send_timeout memcached_upstream_fail_timeout memcached_upstream_max_fails merge_slashes min_delete_depth modern_browser modern_browser_value msie_padding msie_refresh multi_accept open_file_cache open_file_cache_errors open_file_cache_events open_file_cache_min_uses open_file_cache_valid open_log_file_cache output_buffers override_charset perl perl_modules perl_require perl_set pid pop3_auth pop3_capabilities port_in_redirect postpone_gzipping postpone_output protocol proxy proxy_bind proxy_buffer proxy_buffer_size proxy_buffering proxy_buffers proxy_busy_buffers_size proxy_cache proxy_cache_key proxy_cache_methods proxy_cache_min_uses proxy_cache_path proxy_cache_use_stale proxy_cache_valid proxy_connect_timeout proxy_headers_hash_bucket_size proxy_headers_hash_max_size proxy_hide_header proxy_ignore_client_abort proxy_ignore_headers proxy_intercept_errors proxy_max_temp_file_size proxy_method proxy_next_upstream proxy_pass_error_message proxy_pass_header proxy_pass_request_body proxy_pass_request_headers proxy_read_timeout proxy_redirect proxy_send_lowat proxy_send_timeout proxy_set_body proxy_set_header proxy_ssl_session_reuse proxy_store proxy_store_access proxy_temp_file_write_size proxy_temp_path proxy_timeout proxy_upstream_fail_timeout proxy_upstream_max_fails random_index read_ahead real_ip_header recursive_error_pages request_pool_size reset_timedout_connection resolver resolver_timeout rewrite_log rtsig_overflow_events rtsig_overflow_test rtsig_overflow_threshold rtsig_signo satisfy secure_link_secret send_lowat send_timeout sendfile sendfile_max_chunk server_name_in_redirect server_names_hash_bucket_size server_names_hash_max_size server_tokens set_real_ip_from smtp_auth smtp_capabilities smtp_client_buffer smtp_greeting_delay so_keepalive source_charset ssi ssi_ignore_recycled_buffers ssi_min_file_chunk ssi_silent_errors ssi_types ssi_value_length ssl ssl_certificate ssl_certificate_key ssl_ciphers ssl_client_certificate ssl_crl ssl_dhparam ssl_engine ssl_prefer_server_ciphers ssl_protocols ssl_session_cache ssl_session_timeout ssl_verify_client ssl_verify_depth starttls stub_status sub_filter sub_filter_once sub_filter_types tcp_nodelay tcp_nopush thread_stack_size timeout timer_resolution types_hash_bucket_size types_hash_max_size underscores_in_headers uninitialized_variable_warn use user userid userid_domain userid_expires userid_mark userid_name userid_p3p userid_path userid_service valid_referers variables_hash_bucket_size variables_hash_max_size worker_connections worker_cpu_affinity worker_priority worker_processes worker_rlimit_core worker_rlimit_nofile worker_rlimit_sigpending worker_threads working_directory xclient xml_entities xslt_stylesheet xslt_typesdrew@li229-23"
24 /* ngxDirective */ " accept_mutex accept_mutex_delay access_log add_after_body add_before_body add_header addition_types aio alias allow ancient_browser ancient_browser_value auth_basic auth_basic_user_file auth_http auth_http_header auth_http_timeout autoindex autoindex_exact_size autoindex_localtime charset charset_types client_body_buffer_size client_body_in_file_only client_body_in_single_buffer client_body_temp_path client_body_timeout client_header_buffer_size client_header_timeout client_max_body_size connection_pool_size create_full_put_path daemon dav_access dav_methods debug_connection debug_points default_type degradation degrade deny devpoll_changes devpoll_events directio directio_alignment empty_gif env epoll_events error_log eventport_events expires fastcgi_bind fastcgi_buffer_size fastcgi_buffers fastcgi_busy_buffers_size fastcgi_cache fastcgi_cache_key fastcgi_cache_methods fastcgi_cache_min_uses fastcgi_cache_path fastcgi_cache_use_stale fastcgi_cache_valid fastcgi_catch_stderr fastcgi_connect_timeout fastcgi_hide_header fastcgi_ignore_client_abort fastcgi_ignore_headers fastcgi_index fastcgi_intercept_errors fastcgi_max_temp_file_size fastcgi_next_upstream fastcgi_param fastcgi_pass_header fastcgi_pass_request_body fastcgi_pass_request_headers fastcgi_read_timeout fastcgi_send_lowat fastcgi_send_timeout fastcgi_split_path_info fastcgi_store fastcgi_store_access fastcgi_temp_file_write_size fastcgi_temp_path fastcgi_upstream_fail_timeout fastcgi_upstream_max_fails flv geoip_city geoip_country google_perftools_profiles gzip gzip_buffers gzip_comp_level gzip_disable gzip_hash gzip_http_version gzip_min_length gzip_no_buffer gzip_proxied gzip_static gzip_types gzip_vary gzip_window if_modified_since ignore_invalid_headers image_filter image_filter_buffer image_filter_jpeg_quality image_filter_transparency imap_auth imap_capabilities imap_client_buffer index ip_hash keepalive_requests keepalive_timeout kqueue_changes kqueue_events large_client_header_buffers limit_conn limit_conn_log_level limit_rate limit_rate_after limit_req limit_req_log_level limit_req_zone limit_zone lingering_time lingering_timeout lock_file log_format log_not_found log_subrequest map_hash_bucket_size map_hash_max_size master_process memcached_bind memcached_buffer_size memcached_connect_timeout memcached_next_upstream memcached_read_timeout memcached_send_timeout memcached_upstream_fail_timeout memcached_upstream_max_fails merge_slashes min_delete_depth modern_browser modern_browser_value msie_padding msie_refresh multi_accept open_file_cache open_file_cache_errors open_file_cache_events open_file_cache_min_uses open_file_cache_valid open_log_file_cache output_buffers override_charset perl perl_modules perl_require perl_set pid pop3_auth pop3_capabilities port_in_redirect postpone_gzipping postpone_output protocol proxy proxy_bind proxy_buffer proxy_buffer_size proxy_buffering proxy_buffers proxy_busy_buffers_size proxy_cache proxy_cache_key proxy_cache_methods proxy_cache_min_uses proxy_cache_path proxy_cache_use_stale proxy_cache_valid proxy_connect_timeout proxy_headers_hash_bucket_size proxy_headers_hash_max_size proxy_hide_header proxy_ignore_client_abort proxy_ignore_headers proxy_intercept_errors proxy_max_temp_file_size proxy_method proxy_next_upstream proxy_pass_error_message proxy_pass_header proxy_pass_request_body proxy_pass_request_headers proxy_read_timeout proxy_redirect proxy_send_lowat proxy_send_timeout proxy_set_body proxy_set_header proxy_ssl_session_reuse proxy_store proxy_store_access proxy_temp_file_write_size proxy_temp_path proxy_timeout proxy_upstream_fail_timeout proxy_upstream_max_fails random_index read_ahead real_ip_header recursive_error_pages request_pool_size reset_timedout_connection resolver resolver_timeout rewrite_log rtsig_overflow_events rtsig_overflow_test rtsig_overflow_threshold rtsig_signo satisfy secure_link_secret send_lowat send_timeout sendfile sendfile_max_chunk server_name_in_redirect server_names_hash_bucket_size server_names_hash_max_size server_tokens set_real_ip_from smtp_auth smtp_capabilities smtp_client_buffer smtp_greeting_delay so_keepalive source_charset ssi ssi_ignore_recycled_buffers ssi_min_file_chunk ssi_silent_errors ssi_types ssi_value_length ssl ssl_certificate ssl_certificate_key ssl_ciphers ssl_client_certificate ssl_crl ssl_dhparam ssl_engine ssl_prefer_server_ciphers ssl_protocols ssl_session_cache ssl_session_timeout ssl_verify_client ssl_verify_depth starttls stub_status sub_filter sub_filter_once sub_filter_types tcp_nodelay tcp_nopush thread_stack_size timeout timer_resolution types_hash_bucket_size types_hash_max_size underscores_in_headers uninitialized_variable_warn use user userid userid_domain userid_expires userid_mark userid_name userid_p3p userid_path userid_service valid_referers variables_hash_bucket_size variables_hash_max_size worker_connections worker_cpu_affinity worker_priority worker_processes worker_rlimit_core worker_rlimit_nofile worker_rlimit_sigpending worker_threads working_directory xclient xml_entities xslt_stylesheet xslt_typesdrew@li229-23"
25 );
25 );
26
26
27 var keywords_block = words(
27 var keywords_block = words(
28 /* ngxDirectiveBlock */ "http mail events server types location upstream charset_map limit_except if geo map"
28 /* ngxDirectiveBlock */ "http mail events server types location upstream charset_map limit_except if geo map"
29 );
29 );
30
30
31 var keywords_important = words(
31 var keywords_important = words(
32 /* ngxDirectiveImportant */ "include root server server_name listen internal proxy_pass memcached_pass fastcgi_pass try_files"
32 /* ngxDirectiveImportant */ "include root server server_name listen internal proxy_pass memcached_pass fastcgi_pass try_files"
33 );
33 );
34
34
35 var indentUnit = config.indentUnit, type;
35 var indentUnit = config.indentUnit, type;
36 function ret(style, tp) {type = tp; return style;}
36 function ret(style, tp) {type = tp; return style;}
37
37
38 function tokenBase(stream, state) {
38 function tokenBase(stream, state) {
39
39
40
40
41 stream.eatWhile(/[\w\$_]/);
41 stream.eatWhile(/[\w\$_]/);
42
42
43 var cur = stream.current();
43 var cur = stream.current();
44
44
45
45
46 if (keywords.propertyIsEnumerable(cur)) {
46 if (keywords.propertyIsEnumerable(cur)) {
47 return "keyword";
47 return "keyword";
48 }
48 }
49 else if (keywords_block.propertyIsEnumerable(cur)) {
49 else if (keywords_block.propertyIsEnumerable(cur)) {
50 return "variable-2";
50 return "variable-2";
51 }
51 }
52 else if (keywords_important.propertyIsEnumerable(cur)) {
52 else if (keywords_important.propertyIsEnumerable(cur)) {
53 return "string-2";
53 return "string-2";
54 }
54 }
55 /**/
55 /**/
56
56
57 var ch = stream.next();
57 var ch = stream.next();
58 if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
58 if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
59 else if (ch == "/" && stream.eat("*")) {
59 else if (ch == "/" && stream.eat("*")) {
60 state.tokenize = tokenCComment;
60 state.tokenize = tokenCComment;
61 return tokenCComment(stream, state);
61 return tokenCComment(stream, state);
62 }
62 }
63 else if (ch == "<" && stream.eat("!")) {
63 else if (ch == "<" && stream.eat("!")) {
64 state.tokenize = tokenSGMLComment;
64 state.tokenize = tokenSGMLComment;
65 return tokenSGMLComment(stream, state);
65 return tokenSGMLComment(stream, state);
66 }
66 }
67 else if (ch == "=") ret(null, "compare");
67 else if (ch == "=") ret(null, "compare");
68 else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
68 else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
69 else if (ch == "\"" || ch == "'") {
69 else if (ch == "\"" || ch == "'") {
70 state.tokenize = tokenString(ch);
70 state.tokenize = tokenString(ch);
71 return state.tokenize(stream, state);
71 return state.tokenize(stream, state);
72 }
72 }
73 else if (ch == "#") {
73 else if (ch == "#") {
74 stream.skipToEnd();
74 stream.skipToEnd();
75 return ret("comment", "comment");
75 return ret("comment", "comment");
76 }
76 }
77 else if (ch == "!") {
77 else if (ch == "!") {
78 stream.match(/^\s*\w*/);
78 stream.match(/^\s*\w*/);
79 return ret("keyword", "important");
79 return ret("keyword", "important");
80 }
80 }
81 else if (/\d/.test(ch)) {
81 else if (/\d/.test(ch)) {
82 stream.eatWhile(/[\w.%]/);
82 stream.eatWhile(/[\w.%]/);
83 return ret("number", "unit");
83 return ret("number", "unit");
84 }
84 }
85 else if (/[,.+>*\/]/.test(ch)) {
85 else if (/[,.+>*\/]/.test(ch)) {
86 return ret(null, "select-op");
86 return ret(null, "select-op");
87 }
87 }
88 else if (/[;{}:\[\]]/.test(ch)) {
88 else if (/[;{}:\[\]]/.test(ch)) {
89 return ret(null, ch);
89 return ret(null, ch);
90 }
90 }
91 else {
91 else {
92 stream.eatWhile(/[\w\\\-]/);
92 stream.eatWhile(/[\w\\\-]/);
93 return ret("variable", "variable");
93 return ret("variable", "variable");
94 }
94 }
95 }
95 }
96
96
97 function tokenCComment(stream, state) {
97 function tokenCComment(stream, state) {
98 var maybeEnd = false, ch;
98 var maybeEnd = false, ch;
99 while ((ch = stream.next()) != null) {
99 while ((ch = stream.next()) != null) {
100 if (maybeEnd && ch == "/") {
100 if (maybeEnd && ch == "/") {
101 state.tokenize = tokenBase;
101 state.tokenize = tokenBase;
102 break;
102 break;
103 }
103 }
104 maybeEnd = (ch == "*");
104 maybeEnd = (ch == "*");
105 }
105 }
106 return ret("comment", "comment");
106 return ret("comment", "comment");
107 }
107 }
108
108
109 function tokenSGMLComment(stream, state) {
109 function tokenSGMLComment(stream, state) {
110 var dashes = 0, ch;
110 var dashes = 0, ch;
111 while ((ch = stream.next()) != null) {
111 while ((ch = stream.next()) != null) {
112 if (dashes >= 2 && ch == ">") {
112 if (dashes >= 2 && ch == ">") {
113 state.tokenize = tokenBase;
113 state.tokenize = tokenBase;
114 break;
114 break;
115 }
115 }
116 dashes = (ch == "-") ? dashes + 1 : 0;
116 dashes = (ch == "-") ? dashes + 1 : 0;
117 }
117 }
118 return ret("comment", "comment");
118 return ret("comment", "comment");
119 }
119 }
120
120
121 function tokenString(quote) {
121 function tokenString(quote) {
122 return function(stream, state) {
122 return function(stream, state) {
123 var escaped = false, ch;
123 var escaped = false, ch;
124 while ((ch = stream.next()) != null) {
124 while ((ch = stream.next()) != null) {
125 if (ch == quote && !escaped)
125 if (ch == quote && !escaped)
126 break;
126 break;
127 escaped = !escaped && ch == "\\";
127 escaped = !escaped && ch == "\\";
128 }
128 }
129 if (!escaped) state.tokenize = tokenBase;
129 if (!escaped) state.tokenize = tokenBase;
130 return ret("string", "string");
130 return ret("string", "string");
131 };
131 };
132 }
132 }
133
133
134 return {
134 return {
135 startState: function(base) {
135 startState: function(base) {
136 return {tokenize: tokenBase,
136 return {tokenize: tokenBase,
137 baseIndent: base || 0,
137 baseIndent: base || 0,
138 stack: []};
138 stack: []};
139 },
139 },
140
140
141 token: function(stream, state) {
141 token: function(stream, state) {
142 if (stream.eatSpace()) return null;
142 if (stream.eatSpace()) return null;
143 type = null;
143 type = null;
144 var style = state.tokenize(stream, state);
144 var style = state.tokenize(stream, state);
145
145
146 var context = state.stack[state.stack.length-1];
146 var context = state.stack[state.stack.length-1];
147 if (type == "hash" && context == "rule") style = "atom";
147 if (type == "hash" && context == "rule") style = "atom";
148 else if (style == "variable") {
148 else if (style == "variable") {
149 if (context == "rule") style = "number";
149 if (context == "rule") style = "number";
150 else if (!context || context == "@media{") style = "tag";
150 else if (!context || context == "@media{") style = "tag";
151 }
151 }
152
152
153 if (context == "rule" && /^[\{\};]$/.test(type))
153 if (context == "rule" && /^[\{\};]$/.test(type))
154 state.stack.pop();
154 state.stack.pop();
155 if (type == "{") {
155 if (type == "{") {
156 if (context == "@media") state.stack[state.stack.length-1] = "@media{";
156 if (context == "@media") state.stack[state.stack.length-1] = "@media{";
157 else state.stack.push("{");
157 else state.stack.push("{");
158 }
158 }
159 else if (type == "}") state.stack.pop();
159 else if (type == "}") state.stack.pop();
160 else if (type == "@media") state.stack.push("@media");
160 else if (type == "@media") state.stack.push("@media");
161 else if (context == "{" && type != "comment") state.stack.push("rule");
161 else if (context == "{" && type != "comment") state.stack.push("rule");
162 return style;
162 return style;
163 },
163 },
164
164
165 indent: function(state, textAfter) {
165 indent: function(state, textAfter) {
166 var n = state.stack.length;
166 var n = state.stack.length;
167 if (/^\}/.test(textAfter))
167 if (/^\}/.test(textAfter))
168 n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
168 n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
169 return state.baseIndent + n * indentUnit;
169 return state.baseIndent + n * indentUnit;
170 },
170 },
171
171
172 electricChars: "}"
172 electricChars: "}"
173 };
173 };
174 });
174 });
175
175
176 CodeMirror.defineMIME("text/nginx", "text/x-nginx-conf");
176 CodeMirror.defineMIME("text/x-nginx-conf", "nginx");
177
177
178 });
178 });
@@ -1,230 +1,234 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike"));
6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod);
8 define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 function keywords(str) {
14 function keywords(str) {
15 var obj = {}, words = str.split(" ");
15 var obj = {}, words = str.split(" ");
16 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
16 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
17 return obj;
17 return obj;
18 }
18 }
19
19
20 // Helper for phpString
20 // Helper for phpString
21 function matchSequence(list, end, escapes) {
21 function matchSequence(list, end, escapes) {
22 if (list.length == 0) return phpString(end);
22 if (list.length == 0) return phpString(end);
23 return function (stream, state) {
23 return function (stream, state) {
24 var patterns = list[0];
24 var patterns = list[0];
25 for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
25 for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
26 state.tokenize = matchSequence(list.slice(1), end);
26 state.tokenize = matchSequence(list.slice(1), end);
27 return patterns[i][1];
27 return patterns[i][1];
28 }
28 }
29 state.tokenize = phpString(end, escapes);
29 state.tokenize = phpString(end, escapes);
30 return "string";
30 return "string";
31 };
31 };
32 }
32 }
33 function phpString(closing, escapes) {
33 function phpString(closing, escapes) {
34 return function(stream, state) { return phpString_(stream, state, closing, escapes); };
34 return function(stream, state) { return phpString_(stream, state, closing, escapes); };
35 }
35 }
36 function phpString_(stream, state, closing, escapes) {
36 function phpString_(stream, state, closing, escapes) {
37 // "Complex" syntax
37 // "Complex" syntax
38 if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
38 if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
39 state.tokenize = null;
39 state.tokenize = null;
40 return "string";
40 return "string";
41 }
41 }
42
42
43 // Simple syntax
43 // Simple syntax
44 if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
44 if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
45 // After the variable name there may appear array or object operator.
45 // After the variable name there may appear array or object operator.
46 if (stream.match("[", false)) {
46 if (stream.match("[", false)) {
47 // Match array operator
47 // Match array operator
48 state.tokenize = matchSequence([
48 state.tokenize = matchSequence([
49 [["[", null]],
49 [["[", null]],
50 [[/\d[\w\.]*/, "number"],
50 [[/\d[\w\.]*/, "number"],
51 [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
51 [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
52 [/[\w\$]+/, "variable"]],
52 [/[\w\$]+/, "variable"]],
53 [["]", null]]
53 [["]", null]]
54 ], closing, escapes);
54 ], closing, escapes);
55 }
55 }
56 if (stream.match(/\-\>\w/, false)) {
56 if (stream.match(/\-\>\w/, false)) {
57 // Match object operator
57 // Match object operator
58 state.tokenize = matchSequence([
58 state.tokenize = matchSequence([
59 [["->", null]],
59 [["->", null]],
60 [[/[\w]+/, "variable"]]
60 [[/[\w]+/, "variable"]]
61 ], closing, escapes);
61 ], closing, escapes);
62 }
62 }
63 return "variable-2";
63 return "variable-2";
64 }
64 }
65
65
66 var escaped = false;
66 var escaped = false;
67 // Normal string
67 // Normal string
68 while (!stream.eol() &&
68 while (!stream.eol() &&
69 (escaped || escapes === false ||
69 (escaped || escapes === false ||
70 (!stream.match("{$", false) &&
70 (!stream.match("{$", false) &&
71 !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
71 !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
72 if (!escaped && stream.match(closing)) {
72 if (!escaped && stream.match(closing)) {
73 state.tokenize = null;
73 state.tokenize = null;
74 state.tokStack.pop(); state.tokStack.pop();
74 state.tokStack.pop(); state.tokStack.pop();
75 break;
75 break;
76 }
76 }
77 escaped = stream.next() == "\\" && !escaped;
77 escaped = stream.next() == "\\" && !escaped;
78 }
78 }
79 return "string";
79 return "string";
80 }
80 }
81
81
82 var phpKeywords = "abstract and array as break case catch class clone const continue declare default " +
82 var phpKeywords = "abstract and array as break case catch class clone const continue declare default " +
83 "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
83 "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
84 "for foreach function global goto if implements interface instanceof namespace " +
84 "for foreach function global goto if implements interface instanceof namespace " +
85 "new or private protected public static switch throw trait try use var while xor " +
85 "new or private protected public static switch throw trait try use var while xor " +
86 "die echo empty exit eval include include_once isset list require require_once return " +
86 "die echo empty exit eval include include_once isset list require require_once return " +
87 "print unset __halt_compiler self static parent yield insteadof finally";
87 "print unset __halt_compiler self static parent yield insteadof finally";
88 var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";
88 var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";
89 var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";
89 var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";
90 CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
90 CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
91 CodeMirror.registerHelper("wordChars", "php", /[\w$]/);
91 CodeMirror.registerHelper("wordChars", "php", /[\w$]/);
92
92
93 var phpConfig = {
93 var phpConfig = {
94 name: "clike",
94 name: "clike",
95 helperType: "php",
95 helperType: "php",
96 keywords: keywords(phpKeywords),
96 keywords: keywords(phpKeywords),
97 blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
97 blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
98 defKeywords: keywords("class function interface namespace trait"),
98 defKeywords: keywords("class function interface namespace trait"),
99 atoms: keywords(phpAtoms),
99 atoms: keywords(phpAtoms),
100 builtin: keywords(phpBuiltin),
100 builtin: keywords(phpBuiltin),
101 multiLineStrings: true,
101 multiLineStrings: true,
102 hooks: {
102 hooks: {
103 "$": function(stream) {
103 "$": function(stream) {
104 stream.eatWhile(/[\w\$_]/);
104 stream.eatWhile(/[\w\$_]/);
105 return "variable-2";
105 return "variable-2";
106 },
106 },
107 "<": function(stream, state) {
107 "<": function(stream, state) {
108 if (stream.match(/<</)) {
108 var before;
109 var nowDoc = stream.eat("'");
109 if (before = stream.match(/<<\s*/)) {
110 var quoted = stream.eat(/['"]/);
110 stream.eatWhile(/[\w\.]/);
111 stream.eatWhile(/[\w\.]/);
111 var delim = stream.current().slice(3 + (nowDoc ? 1 : 0));
112 var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));
112 if (nowDoc) stream.eat("'");
113 if (quoted) stream.eat(quoted);
113 if (delim) {
114 if (delim) {
114 (state.tokStack || (state.tokStack = [])).push(delim, 0);
115 (state.tokStack || (state.tokStack = [])).push(delim, 0);
115 state.tokenize = phpString(delim, nowDoc ? false : true);
116 state.tokenize = phpString(delim, quoted != "'");
116 return "string";
117 return "string";
117 }
118 }
118 }
119 }
119 return false;
120 return false;
120 },
121 },
121 "#": function(stream) {
122 "#": function(stream) {
122 while (!stream.eol() && !stream.match("?>", false)) stream.next();
123 while (!stream.eol() && !stream.match("?>", false)) stream.next();
123 return "comment";
124 return "comment";
124 },
125 },
125 "/": function(stream) {
126 "/": function(stream) {
126 if (stream.eat("/")) {
127 if (stream.eat("/")) {
127 while (!stream.eol() && !stream.match("?>", false)) stream.next();
128 while (!stream.eol() && !stream.match("?>", false)) stream.next();
128 return "comment";
129 return "comment";
129 }
130 }
130 return false;
131 return false;
131 },
132 },
132 '"': function(_stream, state) {
133 '"': function(_stream, state) {
133 (state.tokStack || (state.tokStack = [])).push('"', 0);
134 (state.tokStack || (state.tokStack = [])).push('"', 0);
134 state.tokenize = phpString('"');
135 state.tokenize = phpString('"');
135 return "string";
136 return "string";
136 },
137 },
137 "{": function(_stream, state) {
138 "{": function(_stream, state) {
138 if (state.tokStack && state.tokStack.length)
139 if (state.tokStack && state.tokStack.length)
139 state.tokStack[state.tokStack.length - 1]++;
140 state.tokStack[state.tokStack.length - 1]++;
140 return false;
141 return false;
141 },
142 },
142 "}": function(_stream, state) {
143 "}": function(_stream, state) {
143 if (state.tokStack && state.tokStack.length > 0 &&
144 if (state.tokStack && state.tokStack.length > 0 &&
144 !--state.tokStack[state.tokStack.length - 1]) {
145 !--state.tokStack[state.tokStack.length - 1]) {
145 state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
146 state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
146 }
147 }
147 return false;
148 return false;
148 }
149 }
149 }
150 }
150 };
151 };
151
152
152 CodeMirror.defineMode("php", function(config, parserConfig) {
153 CodeMirror.defineMode("php", function(config, parserConfig) {
153 var htmlMode = CodeMirror.getMode(config, "text/html");
154 var htmlMode = CodeMirror.getMode(config, "text/html");
154 var phpMode = CodeMirror.getMode(config, phpConfig);
155 var phpMode = CodeMirror.getMode(config, phpConfig);
155
156
156 function dispatch(stream, state) {
157 function dispatch(stream, state) {
157 var isPHP = state.curMode == phpMode;
158 var isPHP = state.curMode == phpMode;
158 if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null;
159 if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null;
159 if (!isPHP) {
160 if (!isPHP) {
160 if (stream.match(/^<\?\w*/)) {
161 if (stream.match(/^<\?\w*/)) {
161 state.curMode = phpMode;
162 state.curMode = phpMode;
163 if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, ""))
162 state.curState = state.php;
164 state.curState = state.php;
163 return "meta";
165 return "meta";
164 }
166 }
165 if (state.pending == '"' || state.pending == "'") {
167 if (state.pending == '"' || state.pending == "'") {
166 while (!stream.eol() && stream.next() != state.pending) {}
168 while (!stream.eol() && stream.next() != state.pending) {}
167 var style = "string";
169 var style = "string";
168 } else if (state.pending && stream.pos < state.pending.end) {
170 } else if (state.pending && stream.pos < state.pending.end) {
169 stream.pos = state.pending.end;
171 stream.pos = state.pending.end;
170 var style = state.pending.style;
172 var style = state.pending.style;
171 } else {
173 } else {
172 var style = htmlMode.token(stream, state.curState);
174 var style = htmlMode.token(stream, state.curState);
173 }
175 }
174 if (state.pending) state.pending = null;
176 if (state.pending) state.pending = null;
175 var cur = stream.current(), openPHP = cur.search(/<\?/), m;
177 var cur = stream.current(), openPHP = cur.search(/<\?/), m;
176 if (openPHP != -1) {
178 if (openPHP != -1) {
177 if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0];
179 if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0];
178 else state.pending = {end: stream.pos, style: style};
180 else state.pending = {end: stream.pos, style: style};
179 stream.backUp(cur.length - openPHP);
181 stream.backUp(cur.length - openPHP);
180 }
182 }
181 return style;
183 return style;
182 } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
184 } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
183 state.curMode = htmlMode;
185 state.curMode = htmlMode;
184 state.curState = state.html;
186 state.curState = state.html;
187 if (!state.php.context.prev) state.php = null;
185 return "meta";
188 return "meta";
186 } else {
189 } else {
187 return phpMode.token(stream, state.curState);
190 return phpMode.token(stream, state.curState);
188 }
191 }
189 }
192 }
190
193
191 return {
194 return {
192 startState: function() {
195 startState: function() {
193 var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode);
196 var html = CodeMirror.startState(htmlMode)
197 var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null
194 return {html: html,
198 return {html: html,
195 php: php,
199 php: php,
196 curMode: parserConfig.startOpen ? phpMode : htmlMode,
200 curMode: parserConfig.startOpen ? phpMode : htmlMode,
197 curState: parserConfig.startOpen ? php : html,
201 curState: parserConfig.startOpen ? php : html,
198 pending: null};
202 pending: null};
199 },
203 },
200
204
201 copyState: function(state) {
205 copyState: function(state) {
202 var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
206 var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
203 php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
207 php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;
204 if (state.curMode == htmlMode) cur = htmlNew;
208 if (state.curMode == htmlMode) cur = htmlNew;
205 else cur = phpNew;
209 else cur = phpNew;
206 return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
210 return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
207 pending: state.pending};
211 pending: state.pending};
208 },
212 },
209
213
210 token: dispatch,
214 token: dispatch,
211
215
212 indent: function(state, textAfter) {
216 indent: function(state, textAfter) {
213 if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
217 if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
214 (state.curMode == phpMode && /^\?>/.test(textAfter)))
218 (state.curMode == phpMode && /^\?>/.test(textAfter)))
215 return htmlMode.indent(state.html, textAfter);
219 return htmlMode.indent(state.html, textAfter);
216 return state.curMode.indent(state.curState, textAfter);
220 return state.curMode.indent(state.curState, textAfter);
217 },
221 },
218
222
219 blockCommentStart: "/*",
223 blockCommentStart: "/*",
220 blockCommentEnd: "*/",
224 blockCommentEnd: "*/",
221 lineComment: "//",
225 lineComment: "//",
222
226
223 innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
227 innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
224 };
228 };
225 }, "htmlmixed", "clike");
229 }, "htmlmixed", "clike");
226
230
227 CodeMirror.defineMIME("application/x-httpd-php", "php");
231 CodeMirror.defineMIME("application/x-httpd-php", "php");
228 CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
232 CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
229 CodeMirror.defineMIME("text/x-php", phpConfig);
233 CodeMirror.defineMIME("text/x-php", phpConfig);
230 });
234 });
@@ -1,358 +1,347 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 function wordRegexp(words) {
14 function wordRegexp(words) {
15 return new RegExp("^((" + words.join(")|(") + "))\\b");
15 return new RegExp("^((" + words.join(")|(") + "))\\b");
16 }
16 }
17
17
18 var wordOperators = wordRegexp(["and", "or", "not", "is"]);
18 var wordOperators = wordRegexp(["and", "or", "not", "is"]);
19 var commonKeywords = ["as", "assert", "break", "class", "continue",
19 var commonKeywords = ["as", "assert", "break", "class", "continue",
20 "def", "del", "elif", "else", "except", "finally",
20 "def", "del", "elif", "else", "except", "finally",
21 "for", "from", "global", "if", "import",
21 "for", "from", "global", "if", "import",
22 "lambda", "pass", "raise", "return",
22 "lambda", "pass", "raise", "return",
23 "try", "while", "with", "yield", "in"];
23 "try", "while", "with", "yield", "in"];
24 var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
24 var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
25 "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
25 "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
26 "enumerate", "eval", "filter", "float", "format", "frozenset",
26 "enumerate", "eval", "filter", "float", "format", "frozenset",
27 "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
27 "getattr", "globals", "hasattr", "hash", "help", "hex", "id",
28 "input", "int", "isinstance", "issubclass", "iter", "len",
28 "input", "int", "isinstance", "issubclass", "iter", "len",
29 "list", "locals", "map", "max", "memoryview", "min", "next",
29 "list", "locals", "map", "max", "memoryview", "min", "next",
30 "object", "oct", "open", "ord", "pow", "property", "range",
30 "object", "oct", "open", "ord", "pow", "property", "range",
31 "repr", "reversed", "round", "set", "setattr", "slice",
31 "repr", "reversed", "round", "set", "setattr", "slice",
32 "sorted", "staticmethod", "str", "sum", "super", "tuple",
32 "sorted", "staticmethod", "str", "sum", "super", "tuple",
33 "type", "vars", "zip", "__import__", "NotImplemented",
33 "type", "vars", "zip", "__import__", "NotImplemented",
34 "Ellipsis", "__debug__"];
34 "Ellipsis", "__debug__"];
35 var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
35 var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
36 "file", "intern", "long", "raw_input", "reduce", "reload",
36 "file", "intern", "long", "raw_input", "reduce", "reload",
37 "unichr", "unicode", "xrange", "False", "True", "None"],
37 "unichr", "unicode", "xrange", "False", "True", "None"],
38 keywords: ["exec", "print"]};
38 keywords: ["exec", "print"]};
39 var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
39 var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
40 keywords: ["nonlocal", "False", "True", "None", "async", "await"]};
40 keywords: ["nonlocal", "False", "True", "None", "async", "await"]};
41
41
42 CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
42 CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
43
43
44 function top(state) {
44 function top(state) {
45 return state.scopes[state.scopes.length - 1];
45 return state.scopes[state.scopes.length - 1];
46 }
46 }
47
47
48 CodeMirror.defineMode("python", function(conf, parserConf) {
48 CodeMirror.defineMode("python", function(conf, parserConf) {
49 var ERRORCLASS = "error";
49 var ERRORCLASS = "error";
50
50
51 var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]");
51 var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/;
52 var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
52 var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/;
53 var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
53 var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/;
54 var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
54 var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/;
55
55
56 if (parserConf.version && parseInt(parserConf.version, 10) == 3){
56 if (parserConf.version && parseInt(parserConf.version, 10) == 3){
57 // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
57 // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
58 var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!@]");
58 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/;
59 var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
59 var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
60 } else {
60 } else {
61 var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
61 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/;
62 var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
62 var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
63 }
63 }
64
64
65 var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
65 var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
66
66
67 var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
67 var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
68 if(parserConf.extra_keywords != undefined){
68 if(parserConf.extra_keywords != undefined){
69 myKeywords = myKeywords.concat(parserConf.extra_keywords);
69 myKeywords = myKeywords.concat(parserConf.extra_keywords);
70 }
70 }
71 if(parserConf.extra_builtins != undefined){
71 if(parserConf.extra_builtins != undefined){
72 myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
72 myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
73 }
73 }
74 if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
74 if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
75 myKeywords = myKeywords.concat(py3.keywords);
75 myKeywords = myKeywords.concat(py3.keywords);
76 myBuiltins = myBuiltins.concat(py3.builtins);
76 myBuiltins = myBuiltins.concat(py3.builtins);
77 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
77 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
78 } else {
78 } else {
79 myKeywords = myKeywords.concat(py2.keywords);
79 myKeywords = myKeywords.concat(py2.keywords);
80 myBuiltins = myBuiltins.concat(py2.builtins);
80 myBuiltins = myBuiltins.concat(py2.builtins);
81 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
81 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
82 }
82 }
83 var keywords = wordRegexp(myKeywords);
83 var keywords = wordRegexp(myKeywords);
84 var builtins = wordRegexp(myBuiltins);
84 var builtins = wordRegexp(myBuiltins);
85
85
86 // tokenizers
86 // tokenizers
87 function tokenBase(stream, state) {
87 function tokenBase(stream, state) {
88 // Handle scope changes
88 // Handle scope changes
89 if (stream.sol() && top(state).type == "py") {
89 if (stream.sol() && top(state).type == "py") {
90 var scopeOffset = top(state).offset;
90 var scopeOffset = top(state).offset;
91 if (stream.eatSpace()) {
91 if (stream.eatSpace()) {
92 var lineOffset = stream.indentation();
92 var lineOffset = stream.indentation();
93 if (lineOffset > scopeOffset)
93 if (lineOffset > scopeOffset)
94 pushScope(stream, state, "py");
94 pushScope(stream, state, "py");
95 else if (lineOffset < scopeOffset && dedent(stream, state))
95 else if (lineOffset < scopeOffset && dedent(stream, state))
96 state.errorToken = true;
96 state.errorToken = true;
97 return null;
97 return null;
98 } else {
98 } else {
99 var style = tokenBaseInner(stream, state);
99 var style = tokenBaseInner(stream, state);
100 if (scopeOffset > 0 && dedent(stream, state))
100 if (scopeOffset > 0 && dedent(stream, state))
101 style += " " + ERRORCLASS;
101 style += " " + ERRORCLASS;
102 return style;
102 return style;
103 }
103 }
104 }
104 }
105 return tokenBaseInner(stream, state);
105 return tokenBaseInner(stream, state);
106 }
106 }
107
107
108 function tokenBaseInner(stream, state) {
108 function tokenBaseInner(stream, state) {
109 if (stream.eatSpace()) return null;
109 if (stream.eatSpace()) return null;
110
110
111 var ch = stream.peek();
111 var ch = stream.peek();
112
112
113 // Handle Comments
113 // Handle Comments
114 if (ch == "#") {
114 if (ch == "#") {
115 stream.skipToEnd();
115 stream.skipToEnd();
116 return "comment";
116 return "comment";
117 }
117 }
118
118
119 // Handle Number Literals
119 // Handle Number Literals
120 if (stream.match(/^[0-9\.]/, false)) {
120 if (stream.match(/^[0-9\.]/, false)) {
121 var floatLiteral = false;
121 var floatLiteral = false;
122 // Floats
122 // Floats
123 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
123 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
124 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
124 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
125 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
125 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
126 if (floatLiteral) {
126 if (floatLiteral) {
127 // Float literals may be "imaginary"
127 // Float literals may be "imaginary"
128 stream.eat(/J/i);
128 stream.eat(/J/i);
129 return "number";
129 return "number";
130 }
130 }
131 // Integers
131 // Integers
132 var intLiteral = false;
132 var intLiteral = false;
133 // Hex
133 // Hex
134 if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
134 if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
135 // Binary
135 // Binary
136 if (stream.match(/^0b[01]+/i)) intLiteral = true;
136 if (stream.match(/^0b[01]+/i)) intLiteral = true;
137 // Octal
137 // Octal
138 if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
138 if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
139 // Decimal
139 // Decimal
140 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
140 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
141 // Decimal literals may be "imaginary"
141 // Decimal literals may be "imaginary"
142 stream.eat(/J/i);
142 stream.eat(/J/i);
143 // TODO - Can you have imaginary longs?
143 // TODO - Can you have imaginary longs?
144 intLiteral = true;
144 intLiteral = true;
145 }
145 }
146 // Zero by itself with no other piece of number.
146 // Zero by itself with no other piece of number.
147 if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
147 if (stream.match(/^0(?![\dx])/i)) intLiteral = true;
148 if (intLiteral) {
148 if (intLiteral) {
149 // Integer literals may be "long"
149 // Integer literals may be "long"
150 stream.eat(/L/i);
150 stream.eat(/L/i);
151 return "number";
151 return "number";
152 }
152 }
153 }
153 }
154
154
155 // Handle Strings
155 // Handle Strings
156 if (stream.match(stringPrefixes)) {
156 if (stream.match(stringPrefixes)) {
157 state.tokenize = tokenStringFactory(stream.current());
157 state.tokenize = tokenStringFactory(stream.current());
158 return state.tokenize(stream, state);
158 return state.tokenize(stream, state);
159 }
159 }
160
160
161 // Handle operators and Delimiters
161 // Handle operators and Delimiters
162 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
162 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
163 return null;
163 return "punctuation";
164
164
165 if (stream.match(doubleOperators) || stream.match(singleOperators))
165 if (stream.match(doubleOperators) || stream.match(singleOperators))
166 return "operator";
166 return "operator";
167
167
168 if (stream.match(singleDelimiters))
168 if (stream.match(singleDelimiters))
169 return null;
169 return "punctuation";
170
171 if (state.lastToken == "." && stream.match(identifiers))
172 return "property";
170
173
171 if (stream.match(keywords) || stream.match(wordOperators))
174 if (stream.match(keywords) || stream.match(wordOperators))
172 return "keyword";
175 return "keyword";
173
176
174 if (stream.match(builtins))
177 if (stream.match(builtins))
175 return "builtin";
178 return "builtin";
176
179
177 if (stream.match(/^(self|cls)\b/))
180 if (stream.match(/^(self|cls)\b/))
178 return "variable-2";
181 return "variable-2";
179
182
180 if (stream.match(identifiers)) {
183 if (stream.match(identifiers)) {
181 if (state.lastToken == "def" || state.lastToken == "class")
184 if (state.lastToken == "def" || state.lastToken == "class")
182 return "def";
185 return "def";
183 return "variable";
186 return "variable";
184 }
187 }
185
188
186 // Handle non-detected items
189 // Handle non-detected items
187 stream.next();
190 stream.next();
188 return ERRORCLASS;
191 return ERRORCLASS;
189 }
192 }
190
193
191 function tokenStringFactory(delimiter) {
194 function tokenStringFactory(delimiter) {
192 while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
195 while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
193 delimiter = delimiter.substr(1);
196 delimiter = delimiter.substr(1);
194
197
195 var singleline = delimiter.length == 1;
198 var singleline = delimiter.length == 1;
196 var OUTCLASS = "string";
199 var OUTCLASS = "string";
197
200
198 function tokenString(stream, state) {
201 function tokenString(stream, state) {
199 while (!stream.eol()) {
202 while (!stream.eol()) {
200 stream.eatWhile(/[^'"\\]/);
203 stream.eatWhile(/[^'"\\]/);
201 if (stream.eat("\\")) {
204 if (stream.eat("\\")) {
202 stream.next();
205 stream.next();
203 if (singleline && stream.eol())
206 if (singleline && stream.eol())
204 return OUTCLASS;
207 return OUTCLASS;
205 } else if (stream.match(delimiter)) {
208 } else if (stream.match(delimiter)) {
206 state.tokenize = tokenBase;
209 state.tokenize = tokenBase;
207 return OUTCLASS;
210 return OUTCLASS;
208 } else {
211 } else {
209 stream.eat(/['"]/);
212 stream.eat(/['"]/);
210 }
213 }
211 }
214 }
212 if (singleline) {
215 if (singleline) {
213 if (parserConf.singleLineStringErrors)
216 if (parserConf.singleLineStringErrors)
214 return ERRORCLASS;
217 return ERRORCLASS;
215 else
218 else
216 state.tokenize = tokenBase;
219 state.tokenize = tokenBase;
217 }
220 }
218 return OUTCLASS;
221 return OUTCLASS;
219 }
222 }
220 tokenString.isString = true;
223 tokenString.isString = true;
221 return tokenString;
224 return tokenString;
222 }
225 }
223
226
224 function pushScope(stream, state, type) {
227 function pushScope(stream, state, type) {
225 var offset = 0, align = null;
228 var offset = 0, align = null;
226 if (type == "py") {
229 if (type == "py") {
227 while (top(state).type != "py")
230 while (top(state).type != "py")
228 state.scopes.pop();
231 state.scopes.pop();
229 }
232 }
230 offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
233 offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
231 if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
234 if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
232 align = stream.column() + 1;
235 align = stream.column() + 1;
233 state.scopes.push({offset: offset, type: type, align: align});
236 state.scopes.push({offset: offset, type: type, align: align});
234 }
237 }
235
238
236 function dedent(stream, state) {
239 function dedent(stream, state) {
237 var indented = stream.indentation();
240 var indented = stream.indentation();
238 while (top(state).offset > indented) {
241 while (top(state).offset > indented) {
239 if (top(state).type != "py") return true;
242 if (top(state).type != "py") return true;
240 state.scopes.pop();
243 state.scopes.pop();
241 }
244 }
242 return top(state).offset != indented;
245 return top(state).offset != indented;
243 }
246 }
244
247
245 function tokenLexer(stream, state) {
248 function tokenLexer(stream, state) {
246 var style = state.tokenize(stream, state);
249 var style = state.tokenize(stream, state);
247 var current = stream.current();
250 var current = stream.current();
248
251
249 // Handle '.' connected identifiers
250 if (current == ".") {
251 style = stream.match(identifiers, false) ? null : ERRORCLASS;
252 if (style == null && state.lastStyle == "meta") {
253 // Apply 'meta' style to '.' connected identifiers when
254 // appropriate.
255 style = "meta";
256 }
257 return style;
258 }
259
260 // Handle decorators
252 // Handle decorators
261 if (current == "@"){
253 if (current == "@"){
262 if(parserConf.version && parseInt(parserConf.version, 10) == 3){
254 if(parserConf.version && parseInt(parserConf.version, 10) == 3){
263 return stream.match(identifiers, false) ? "meta" : "operator";
255 return stream.match(identifiers, false) ? "meta" : "operator";
264 } else {
256 } else {
265 return stream.match(identifiers, false) ? "meta" : ERRORCLASS;
257 return stream.match(identifiers, false) ? "meta" : ERRORCLASS;
266 }
258 }
267 }
259 }
268
260
269 if ((style == "variable" || style == "builtin")
261 if ((style == "variable" || style == "builtin")
270 && state.lastStyle == "meta")
262 && state.lastToken == "meta")
271 style = "meta";
263 style = "meta";
272
264
273 // Handle scope changes.
265 // Handle scope changes.
274 if (current == "pass" || current == "return")
266 if (current == "pass" || current == "return")
275 state.dedent += 1;
267 state.dedent += 1;
276
268
277 if (current == "lambda") state.lambda = true;
269 if (current == "lambda") state.lambda = true;
278 if (current == ":" && !state.lambda && top(state).type == "py")
270 if (current == ":" && !state.lambda && top(state).type == "py")
279 pushScope(stream, state, "py");
271 pushScope(stream, state, "py");
280
272
281 var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
273 var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
282 if (delimiter_index != -1)
274 if (delimiter_index != -1)
283 pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
275 pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
284
276
285 delimiter_index = "])}".indexOf(current);
277 delimiter_index = "])}".indexOf(current);
286 if (delimiter_index != -1) {
278 if (delimiter_index != -1) {
287 if (top(state).type == current) state.scopes.pop();
279 if (top(state).type == current) state.scopes.pop();
288 else return ERRORCLASS;
280 else return ERRORCLASS;
289 }
281 }
290 if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
282 if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
291 if (state.scopes.length > 1) state.scopes.pop();
283 if (state.scopes.length > 1) state.scopes.pop();
292 state.dedent -= 1;
284 state.dedent -= 1;
293 }
285 }
294
286
295 return style;
287 return style;
296 }
288 }
297
289
298 var external = {
290 var external = {
299 startState: function(basecolumn) {
291 startState: function(basecolumn) {
300 return {
292 return {
301 tokenize: tokenBase,
293 tokenize: tokenBase,
302 scopes: [{offset: basecolumn || 0, type: "py", align: null}],
294 scopes: [{offset: basecolumn || 0, type: "py", align: null}],
303 lastStyle: null,
304 lastToken: null,
295 lastToken: null,
305 lambda: false,
296 lambda: false,
306 dedent: 0
297 dedent: 0
307 };
298 };
308 },
299 },
309
300
310 token: function(stream, state) {
301 token: function(stream, state) {
311 var addErr = state.errorToken;
302 var addErr = state.errorToken;
312 if (addErr) state.errorToken = false;
303 if (addErr) state.errorToken = false;
313 var style = tokenLexer(stream, state);
304 var style = tokenLexer(stream, state);
314
305
315 state.lastStyle = style;
306 if (style && style != "comment")
316
307 state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style;
317 var current = stream.current();
308 if (style == "punctuation") style = null;
318 if (current && style)
319 state.lastToken = current;
320
309
321 if (stream.eol() && state.lambda)
310 if (stream.eol() && state.lambda)
322 state.lambda = false;
311 state.lambda = false;
323 return addErr ? style + " " + ERRORCLASS : style;
312 return addErr ? style + " " + ERRORCLASS : style;
324 },
313 },
325
314
326 indent: function(state, textAfter) {
315 indent: function(state, textAfter) {
327 if (state.tokenize != tokenBase)
316 if (state.tokenize != tokenBase)
328 return state.tokenize.isString ? CodeMirror.Pass : 0;
317 return state.tokenize.isString ? CodeMirror.Pass : 0;
329
318
330 var scope = top(state);
319 var scope = top(state);
331 var closing = textAfter && textAfter.charAt(0) == scope.type;
320 var closing = textAfter && textAfter.charAt(0) == scope.type;
332 if (scope.align != null)
321 if (scope.align != null)
333 return scope.align - (closing ? 1 : 0);
322 return scope.align - (closing ? 1 : 0);
334 else if (closing && state.scopes.length > 1)
323 else if (closing && state.scopes.length > 1)
335 return state.scopes[state.scopes.length - 2].offset;
324 return state.scopes[state.scopes.length - 2].offset;
336 else
325 else
337 return scope.offset;
326 return scope.offset;
338 },
327 },
339
328
340 closeBrackets: {triples: "'\""},
329 closeBrackets: {triples: "'\""},
341 lineComment: "#",
330 lineComment: "#",
342 fold: "indent"
331 fold: "indent"
343 };
332 };
344 return external;
333 return external;
345 });
334 });
346
335
347 CodeMirror.defineMIME("text/x-python", "python");
336 CodeMirror.defineMIME("text/x-python", "python");
348
337
349 var words = function(str) { return str.split(" "); };
338 var words = function(str) { return str.split(" "); };
350
339
351 CodeMirror.defineMIME("text/x-cython", {
340 CodeMirror.defineMIME("text/x-cython", {
352 name: "python",
341 name: "python",
353 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
342 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
354 "extern gil include nogil property public"+
343 "extern gil include nogil property public"+
355 "readonly struct union DEF IF ELIF ELSE")
344 "readonly struct union DEF IF ELIF ELSE")
356 });
345 });
357
346
358 });
347 });
@@ -1,101 +1,109 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("rpm-changes", function() {
14 CodeMirror.defineMode("rpm-changes", function() {
15 var headerSeperator = /^-+$/;
15 var headerSeperator = /^-+$/;
16 var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /;
16 var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /;
17 var simpleEmail = /^[\w+.-]+@[\w.-]+/;
17 var simpleEmail = /^[\w+.-]+@[\w.-]+/;
18
18
19 return {
19 return {
20 token: function(stream) {
20 token: function(stream) {
21 if (stream.sol()) {
21 if (stream.sol()) {
22 if (stream.match(headerSeperator)) { return 'tag'; }
22 if (stream.match(headerSeperator)) { return 'tag'; }
23 if (stream.match(headerLine)) { return 'tag'; }
23 if (stream.match(headerLine)) { return 'tag'; }
24 }
24 }
25 if (stream.match(simpleEmail)) { return 'string'; }
25 if (stream.match(simpleEmail)) { return 'string'; }
26 stream.next();
26 stream.next();
27 return null;
27 return null;
28 }
28 }
29 };
29 };
30 });
30 });
31
31
32 CodeMirror.defineMIME("text/x-rpm-changes", "rpm-changes");
32 CodeMirror.defineMIME("text/x-rpm-changes", "rpm-changes");
33
33
34 // Quick and dirty spec file highlighting
34 // Quick and dirty spec file highlighting
35
35
36 CodeMirror.defineMode("rpm-spec", function() {
36 CodeMirror.defineMode("rpm-spec", function() {
37 var arch = /^(i386|i586|i686|x86_64|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/;
37 var arch = /^(i386|i586|i686|x86_64|ppc64le|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/;
38
38
39 var preamble = /^(Name|Version|Release|License|Summary|Url|Group|Source|BuildArch|BuildRequires|BuildRoot|AutoReqProv|Provides|Requires(\(\w+\))?|Obsoletes|Conflicts|Recommends|Source\d*|Patch\d*|ExclusiveArch|NoSource|Supplements):/;
39 var preamble = /^[a-zA-Z0-9()]+:/;
40 var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/;
40 var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pretrans|posttrans|pre|post|triggerin|triggerun|verifyscript|check|triggerpostun|triggerprein|trigger)/;
41 var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros
41 var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros
42 var control_flow_simple = /^%(else|endif)/; // rpm control flow macros
42 var control_flow_simple = /^%(else|endif)/; // rpm control flow macros
43 var operators = /^(\!|\?|\<\=|\<|\>\=|\>|\=\=|\&\&|\|\|)/; // operators in control flow macros
43 var operators = /^(\!|\?|\<\=|\<|\>\=|\>|\=\=|\&\&|\|\|)/; // operators in control flow macros
44
44
45 return {
45 return {
46 startState: function () {
46 startState: function () {
47 return {
47 return {
48 controlFlow: false,
48 controlFlow: false,
49 macroParameters: false,
49 macroParameters: false,
50 section: false
50 section: false
51 };
51 };
52 },
52 },
53 token: function (stream, state) {
53 token: function (stream, state) {
54 var ch = stream.peek();
54 var ch = stream.peek();
55 if (ch == "#") { stream.skipToEnd(); return "comment"; }
55 if (ch == "#") { stream.skipToEnd(); return "comment"; }
56
56
57 if (stream.sol()) {
57 if (stream.sol()) {
58 if (stream.match(preamble)) { return "preamble"; }
58 if (stream.match(preamble)) { return "header"; }
59 if (stream.match(section)) { return "section"; }
59 if (stream.match(section)) { return "atom"; }
60 }
60 }
61
61
62 if (stream.match(/^\$\w+/)) { return "def"; } // Variables like '$RPM_BUILD_ROOT'
62 if (stream.match(/^\$\w+/)) { return "def"; } // Variables like '$RPM_BUILD_ROOT'
63 if (stream.match(/^\$\{\w+\}/)) { return "def"; } // Variables like '${RPM_BUILD_ROOT}'
63 if (stream.match(/^\$\{\w+\}/)) { return "def"; } // Variables like '${RPM_BUILD_ROOT}'
64
64
65 if (stream.match(control_flow_simple)) { return "keyword"; }
65 if (stream.match(control_flow_simple)) { return "keyword"; }
66 if (stream.match(control_flow_complex)) {
66 if (stream.match(control_flow_complex)) {
67 state.controlFlow = true;
67 state.controlFlow = true;
68 return "keyword";
68 return "keyword";
69 }
69 }
70 if (state.controlFlow) {
70 if (state.controlFlow) {
71 if (stream.match(operators)) { return "operator"; }
71 if (stream.match(operators)) { return "operator"; }
72 if (stream.match(/^(\d+)/)) { return "number"; }
72 if (stream.match(/^(\d+)/)) { return "number"; }
73 if (stream.eol()) { state.controlFlow = false; }
73 if (stream.eol()) { state.controlFlow = false; }
74 }
74 }
75
75
76 if (stream.match(arch)) { return "number"; }
76 if (stream.match(arch)) {
77 if (stream.eol()) { state.controlFlow = false; }
78 return "number";
79 }
77
80
78 // Macros like '%make_install' or '%attr(0775,root,root)'
81 // Macros like '%make_install' or '%attr(0775,root,root)'
79 if (stream.match(/^%[\w]+/)) {
82 if (stream.match(/^%[\w]+/)) {
80 if (stream.match(/^\(/)) { state.macroParameters = true; }
83 if (stream.match(/^\(/)) { state.macroParameters = true; }
81 return "macro";
84 return "keyword";
82 }
85 }
83 if (state.macroParameters) {
86 if (state.macroParameters) {
84 if (stream.match(/^\d+/)) { return "number";}
87 if (stream.match(/^\d+/)) { return "number";}
85 if (stream.match(/^\)/)) {
88 if (stream.match(/^\)/)) {
86 state.macroParameters = false;
89 state.macroParameters = false;
87 return "macro";
90 return "keyword";
88 }
91 }
89 }
92 }
90 if (stream.match(/^%\{\??[\w \-]+\}/)) { return "macro"; } // Macros like '%{defined fedora}'
93
94 // Macros like '%{defined fedora}'
95 if (stream.match(/^%\{\??[\w \-\:\!]+\}/)) {
96 if (stream.eol()) { state.controlFlow = false; }
97 return "def";
98 }
91
99
92 //TODO: Include bash script sub-parser (CodeMirror supports that)
100 //TODO: Include bash script sub-parser (CodeMirror supports that)
93 stream.next();
101 stream.next();
94 return null;
102 return null;
95 }
103 }
96 };
104 };
97 });
105 });
98
106
99 CodeMirror.defineMIME("text/x-rpm-spec", "rpm-spec");
107 CodeMirror.defineMIME("text/x-rpm-spec", "rpm-spec");
100
108
101 });
109 });
@@ -1,285 +1,285 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("ruby", function(config) {
14 CodeMirror.defineMode("ruby", function(config) {
15 function wordObj(words) {
15 function wordObj(words) {
16 var o = {};
16 var o = {};
17 for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
17 for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
18 return o;
18 return o;
19 }
19 }
20 var keywords = wordObj([
20 var keywords = wordObj([
21 "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
21 "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
22 "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or",
22 "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or",
23 "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
23 "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
24 "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
24 "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
25 "caller", "lambda", "proc", "public", "protected", "private", "require", "load",
25 "caller", "lambda", "proc", "public", "protected", "private", "require", "load",
26 "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
26 "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
27 ]);
27 ]);
28 var indentWords = wordObj(["def", "class", "case", "for", "while", "module", "then",
28 var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then",
29 "catch", "loop", "proc", "begin"]);
29 "catch", "loop", "proc", "begin"]);
30 var dedentWords = wordObj(["end", "until"]);
30 var dedentWords = wordObj(["end", "until"]);
31 var matching = {"[": "]", "{": "}", "(": ")"};
31 var matching = {"[": "]", "{": "}", "(": ")"};
32 var curPunc;
32 var curPunc;
33
33
34 function chain(newtok, stream, state) {
34 function chain(newtok, stream, state) {
35 state.tokenize.push(newtok);
35 state.tokenize.push(newtok);
36 return newtok(stream, state);
36 return newtok(stream, state);
37 }
37 }
38
38
39 function tokenBase(stream, state) {
39 function tokenBase(stream, state) {
40 curPunc = null;
41 if (stream.sol() && stream.match("=begin") && stream.eol()) {
40 if (stream.sol() && stream.match("=begin") && stream.eol()) {
42 state.tokenize.push(readBlockComment);
41 state.tokenize.push(readBlockComment);
43 return "comment";
42 return "comment";
44 }
43 }
45 if (stream.eatSpace()) return null;
44 if (stream.eatSpace()) return null;
46 var ch = stream.next(), m;
45 var ch = stream.next(), m;
47 if (ch == "`" || ch == "'" || ch == '"') {
46 if (ch == "`" || ch == "'" || ch == '"') {
48 return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
47 return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
49 } else if (ch == "/") {
48 } else if (ch == "/") {
50 var currentIndex = stream.current().length;
49 var currentIndex = stream.current().length;
51 if (stream.skipTo("/")) {
50 if (stream.skipTo("/")) {
52 var search_till = stream.current().length;
51 var search_till = stream.current().length;
53 stream.backUp(stream.current().length - currentIndex);
52 stream.backUp(stream.current().length - currentIndex);
54 var balance = 0; // balance brackets
53 var balance = 0; // balance brackets
55 while (stream.current().length < search_till) {
54 while (stream.current().length < search_till) {
56 var chchr = stream.next();
55 var chchr = stream.next();
57 if (chchr == "(") balance += 1;
56 if (chchr == "(") balance += 1;
58 else if (chchr == ")") balance -= 1;
57 else if (chchr == ")") balance -= 1;
59 if (balance < 0) break;
58 if (balance < 0) break;
60 }
59 }
61 stream.backUp(stream.current().length - currentIndex);
60 stream.backUp(stream.current().length - currentIndex);
62 if (balance == 0)
61 if (balance == 0)
63 return chain(readQuoted(ch, "string-2", true), stream, state);
62 return chain(readQuoted(ch, "string-2", true), stream, state);
64 }
63 }
65 return "operator";
64 return "operator";
66 } else if (ch == "%") {
65 } else if (ch == "%") {
67 var style = "string", embed = true;
66 var style = "string", embed = true;
68 if (stream.eat("s")) style = "atom";
67 if (stream.eat("s")) style = "atom";
69 else if (stream.eat(/[WQ]/)) style = "string";
68 else if (stream.eat(/[WQ]/)) style = "string";
70 else if (stream.eat(/[r]/)) style = "string-2";
69 else if (stream.eat(/[r]/)) style = "string-2";
71 else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
70 else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
72 var delim = stream.eat(/[^\w\s=]/);
71 var delim = stream.eat(/[^\w\s=]/);
73 if (!delim) return "operator";
72 if (!delim) return "operator";
74 if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
73 if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
75 return chain(readQuoted(delim, style, embed, true), stream, state);
74 return chain(readQuoted(delim, style, embed, true), stream, state);
76 } else if (ch == "#") {
75 } else if (ch == "#") {
77 stream.skipToEnd();
76 stream.skipToEnd();
78 return "comment";
77 return "comment";
79 } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
78 } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
80 return chain(readHereDoc(m[1]), stream, state);
79 return chain(readHereDoc(m[1]), stream, state);
81 } else if (ch == "0") {
80 } else if (ch == "0") {
82 if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
81 if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
83 else if (stream.eat("b")) stream.eatWhile(/[01]/);
82 else if (stream.eat("b")) stream.eatWhile(/[01]/);
84 else stream.eatWhile(/[0-7]/);
83 else stream.eatWhile(/[0-7]/);
85 return "number";
84 return "number";
86 } else if (/\d/.test(ch)) {
85 } else if (/\d/.test(ch)) {
87 stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/);
86 stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/);
88 return "number";
87 return "number";
89 } else if (ch == "?") {
88 } else if (ch == "?") {
90 while (stream.match(/^\\[CM]-/)) {}
89 while (stream.match(/^\\[CM]-/)) {}
91 if (stream.eat("\\")) stream.eatWhile(/\w/);
90 if (stream.eat("\\")) stream.eatWhile(/\w/);
92 else stream.next();
91 else stream.next();
93 return "string";
92 return "string";
94 } else if (ch == ":") {
93 } else if (ch == ":") {
95 if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
94 if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
96 if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
95 if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
97
96
98 // :> :>> :< :<< are valid symbols
97 // :> :>> :< :<< are valid symbols
99 if (stream.eat(/[\<\>]/)) {
98 if (stream.eat(/[\<\>]/)) {
100 stream.eat(/[\<\>]/);
99 stream.eat(/[\<\>]/);
101 return "atom";
100 return "atom";
102 }
101 }
103
102
104 // :+ :- :/ :* :| :& :! are valid symbols
103 // :+ :- :/ :* :| :& :! are valid symbols
105 if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
104 if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
106 return "atom";
105 return "atom";
107 }
106 }
108
107
109 // Symbols can't start by a digit
108 // Symbols can't start by a digit
110 if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
109 if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
111 stream.eatWhile(/[\w$\xa1-\uffff]/);
110 stream.eatWhile(/[\w$\xa1-\uffff]/);
112 // Only one ? ! = is allowed and only as the last character
111 // Only one ? ! = is allowed and only as the last character
113 stream.eat(/[\?\!\=]/);
112 stream.eat(/[\?\!\=]/);
114 return "atom";
113 return "atom";
115 }
114 }
116 return "operator";
115 return "operator";
117 } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
116 } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
118 stream.eat("@");
117 stream.eat("@");
119 stream.eatWhile(/[\w\xa1-\uffff]/);
118 stream.eatWhile(/[\w\xa1-\uffff]/);
120 return "variable-2";
119 return "variable-2";
121 } else if (ch == "$") {
120 } else if (ch == "$") {
122 if (stream.eat(/[a-zA-Z_]/)) {
121 if (stream.eat(/[a-zA-Z_]/)) {
123 stream.eatWhile(/[\w]/);
122 stream.eatWhile(/[\w]/);
124 } else if (stream.eat(/\d/)) {
123 } else if (stream.eat(/\d/)) {
125 stream.eat(/\d/);
124 stream.eat(/\d/);
126 } else {
125 } else {
127 stream.next(); // Must be a special global like $: or $!
126 stream.next(); // Must be a special global like $: or $!
128 }
127 }
129 return "variable-3";
128 return "variable-3";
130 } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
129 } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
131 stream.eatWhile(/[\w\xa1-\uffff]/);
130 stream.eatWhile(/[\w\xa1-\uffff]/);
132 stream.eat(/[\?\!]/);
131 stream.eat(/[\?\!]/);
133 if (stream.eat(":")) return "atom";
132 if (stream.eat(":")) return "atom";
134 return "ident";
133 return "ident";
135 } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
134 } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
136 curPunc = "|";
135 curPunc = "|";
137 return null;
136 return null;
138 } else if (/[\(\)\[\]{}\\;]/.test(ch)) {
137 } else if (/[\(\)\[\]{}\\;]/.test(ch)) {
139 curPunc = ch;
138 curPunc = ch;
140 return null;
139 return null;
141 } else if (ch == "-" && stream.eat(">")) {
140 } else if (ch == "-" && stream.eat(">")) {
142 return "arrow";
141 return "arrow";
143 } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
142 } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
144 var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/);
143 var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/);
145 if (ch == "." && !more) curPunc = ".";
144 if (ch == "." && !more) curPunc = ".";
146 return "operator";
145 return "operator";
147 } else {
146 } else {
148 return null;
147 return null;
149 }
148 }
150 }
149 }
151
150
152 function tokenBaseUntilBrace(depth) {
151 function tokenBaseUntilBrace(depth) {
153 if (!depth) depth = 1;
152 if (!depth) depth = 1;
154 return function(stream, state) {
153 return function(stream, state) {
155 if (stream.peek() == "}") {
154 if (stream.peek() == "}") {
156 if (depth == 1) {
155 if (depth == 1) {
157 state.tokenize.pop();
156 state.tokenize.pop();
158 return state.tokenize[state.tokenize.length-1](stream, state);
157 return state.tokenize[state.tokenize.length-1](stream, state);
159 } else {
158 } else {
160 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);
159 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);
161 }
160 }
162 } else if (stream.peek() == "{") {
161 } else if (stream.peek() == "{") {
163 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);
162 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);
164 }
163 }
165 return tokenBase(stream, state);
164 return tokenBase(stream, state);
166 };
165 };
167 }
166 }
168 function tokenBaseOnce() {
167 function tokenBaseOnce() {
169 var alreadyCalled = false;
168 var alreadyCalled = false;
170 return function(stream, state) {
169 return function(stream, state) {
171 if (alreadyCalled) {
170 if (alreadyCalled) {
172 state.tokenize.pop();
171 state.tokenize.pop();
173 return state.tokenize[state.tokenize.length-1](stream, state);
172 return state.tokenize[state.tokenize.length-1](stream, state);
174 }
173 }
175 alreadyCalled = true;
174 alreadyCalled = true;
176 return tokenBase(stream, state);
175 return tokenBase(stream, state);
177 };
176 };
178 }
177 }
179 function readQuoted(quote, style, embed, unescaped) {
178 function readQuoted(quote, style, embed, unescaped) {
180 return function(stream, state) {
179 return function(stream, state) {
181 var escaped = false, ch;
180 var escaped = false, ch;
182
181
183 if (state.context.type === 'read-quoted-paused') {
182 if (state.context.type === 'read-quoted-paused') {
184 state.context = state.context.prev;
183 state.context = state.context.prev;
185 stream.eat("}");
184 stream.eat("}");
186 }
185 }
187
186
188 while ((ch = stream.next()) != null) {
187 while ((ch = stream.next()) != null) {
189 if (ch == quote && (unescaped || !escaped)) {
188 if (ch == quote && (unescaped || !escaped)) {
190 state.tokenize.pop();
189 state.tokenize.pop();
191 break;
190 break;
192 }
191 }
193 if (embed && ch == "#" && !escaped) {
192 if (embed && ch == "#" && !escaped) {
194 if (stream.eat("{")) {
193 if (stream.eat("{")) {
195 if (quote == "}") {
194 if (quote == "}") {
196 state.context = {prev: state.context, type: 'read-quoted-paused'};
195 state.context = {prev: state.context, type: 'read-quoted-paused'};
197 }
196 }
198 state.tokenize.push(tokenBaseUntilBrace());
197 state.tokenize.push(tokenBaseUntilBrace());
199 break;
198 break;
200 } else if (/[@\$]/.test(stream.peek())) {
199 } else if (/[@\$]/.test(stream.peek())) {
201 state.tokenize.push(tokenBaseOnce());
200 state.tokenize.push(tokenBaseOnce());
202 break;
201 break;
203 }
202 }
204 }
203 }
205 escaped = !escaped && ch == "\\";
204 escaped = !escaped && ch == "\\";
206 }
205 }
207 return style;
206 return style;
208 };
207 };
209 }
208 }
210 function readHereDoc(phrase) {
209 function readHereDoc(phrase) {
211 return function(stream, state) {
210 return function(stream, state) {
212 if (stream.match(phrase)) state.tokenize.pop();
211 if (stream.match(phrase)) state.tokenize.pop();
213 else stream.skipToEnd();
212 else stream.skipToEnd();
214 return "string";
213 return "string";
215 };
214 };
216 }
215 }
217 function readBlockComment(stream, state) {
216 function readBlockComment(stream, state) {
218 if (stream.sol() && stream.match("=end") && stream.eol())
217 if (stream.sol() && stream.match("=end") && stream.eol())
219 state.tokenize.pop();
218 state.tokenize.pop();
220 stream.skipToEnd();
219 stream.skipToEnd();
221 return "comment";
220 return "comment";
222 }
221 }
223
222
224 return {
223 return {
225 startState: function() {
224 startState: function() {
226 return {tokenize: [tokenBase],
225 return {tokenize: [tokenBase],
227 indented: 0,
226 indented: 0,
228 context: {type: "top", indented: -config.indentUnit},
227 context: {type: "top", indented: -config.indentUnit},
229 continuedLine: false,
228 continuedLine: false,
230 lastTok: null,
229 lastTok: null,
231 varList: false};
230 varList: false};
232 },
231 },
233
232
234 token: function(stream, state) {
233 token: function(stream, state) {
234 curPunc = null;
235 if (stream.sol()) state.indented = stream.indentation();
235 if (stream.sol()) state.indented = stream.indentation();
236 var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype;
236 var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype;
237 var thisTok = curPunc;
237 var thisTok = curPunc;
238 if (style == "ident") {
238 if (style == "ident") {
239 var word = stream.current();
239 var word = stream.current();
240 style = state.lastTok == "." ? "property"
240 style = state.lastTok == "." ? "property"
241 : keywords.propertyIsEnumerable(stream.current()) ? "keyword"
241 : keywords.propertyIsEnumerable(stream.current()) ? "keyword"
242 : /^[A-Z]/.test(word) ? "tag"
242 : /^[A-Z]/.test(word) ? "tag"
243 : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def"
243 : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def"
244 : "variable";
244 : "variable";
245 if (style == "keyword") {
245 if (style == "keyword") {
246 thisTok = word;
246 thisTok = word;
247 if (indentWords.propertyIsEnumerable(word)) kwtype = "indent";
247 if (indentWords.propertyIsEnumerable(word)) kwtype = "indent";
248 else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent";
248 else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent";
249 else if ((word == "if" || word == "unless") && stream.column() == stream.indentation())
249 else if ((word == "if" || word == "unless") && stream.column() == stream.indentation())
250 kwtype = "indent";
250 kwtype = "indent";
251 else if (word == "do" && state.context.indented < state.indented)
251 else if (word == "do" && state.context.indented < state.indented)
252 kwtype = "indent";
252 kwtype = "indent";
253 }
253 }
254 }
254 }
255 if (curPunc || (style && style != "comment")) state.lastTok = thisTok;
255 if (curPunc || (style && style != "comment")) state.lastTok = thisTok;
256 if (curPunc == "|") state.varList = !state.varList;
256 if (curPunc == "|") state.varList = !state.varList;
257
257
258 if (kwtype == "indent" || /[\(\[\{]/.test(curPunc))
258 if (kwtype == "indent" || /[\(\[\{]/.test(curPunc))
259 state.context = {prev: state.context, type: curPunc || style, indented: state.indented};
259 state.context = {prev: state.context, type: curPunc || style, indented: state.indented};
260 else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev)
260 else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev)
261 state.context = state.context.prev;
261 state.context = state.context.prev;
262
262
263 if (stream.eol())
263 if (stream.eol())
264 state.continuedLine = (curPunc == "\\" || style == "operator");
264 state.continuedLine = (curPunc == "\\" || style == "operator");
265 return style;
265 return style;
266 },
266 },
267
267
268 indent: function(state, textAfter) {
268 indent: function(state, textAfter) {
269 if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0;
269 if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0;
270 var firstChar = textAfter && textAfter.charAt(0);
270 var firstChar = textAfter && textAfter.charAt(0);
271 var ct = state.context;
271 var ct = state.context;
272 var closing = ct.type == matching[firstChar] ||
272 var closing = ct.type == matching[firstChar] ||
273 ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
273 ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
274 return ct.indented + (closing ? 0 : config.indentUnit) +
274 return ct.indented + (closing ? 0 : config.indentUnit) +
275 (state.continuedLine ? config.indentUnit : 0);
275 (state.continuedLine ? config.indentUnit : 0);
276 },
276 },
277
277
278 electricChars: "}de", // enD and rescuE
278 electricInput: /^\s*(?:end|rescue|\})$/,
279 lineComment: "#"
279 lineComment: "#"
280 };
280 };
281 });
281 });
282
282
283 CodeMirror.defineMIME("text/x-ruby", "ruby");
283 CodeMirror.defineMIME("text/x-ruby", "ruby");
284
284
285 });
285 });
@@ -1,451 +1,71 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("rust", function() {
14 CodeMirror.defineSimpleMode("rust",{
15 var indentUnit = 4, altIndentUnit = 2;
15 start: [
16 var valKeywords = {
16 // string and byte string
17 "if": "if-style", "while": "if-style", "loop": "else-style", "else": "else-style",
17 {regex: /b?"/, token: "string", next: "string"},
18 "do": "else-style", "ret": "else-style", "fail": "else-style",
18 // raw string and raw byte string
19 "break": "atom", "cont": "atom", "const": "let", "resource": "fn",
19 {regex: /b?r"/, token: "string", next: "string_raw"},
20 "let": "let", "fn": "fn", "for": "for", "alt": "alt", "iface": "iface",
20 {regex: /b?r#+"/, token: "string", next: "string_raw_hash"},
21 "impl": "impl", "type": "type", "enum": "enum", "mod": "mod",
21 // character
22 "as": "op", "true": "atom", "false": "atom", "assert": "op", "check": "op",
22 {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"},
23 "claim": "op", "native": "ignore", "unsafe": "ignore", "import": "else-style",
23 // byte
24 "export": "else-style", "copy": "op", "log": "op", "log_err": "op",
24 {regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2"},
25 "use": "op", "bind": "op", "self": "atom", "struct": "enum"
26 };
27 var typeKeywords = function() {
28 var keywords = {"fn": "fn", "block": "fn", "obj": "obj"};
29 var atoms = "bool uint int i8 i16 i32 i64 u8 u16 u32 u64 float f32 f64 str char".split(" ");
30 for (var i = 0, e = atoms.length; i < e; ++i) keywords[atoms[i]] = "atom";
31 return keywords;
32 }();
33 var operatorChar = /[+\-*&%=<>!?|\.@]/;
34
35 // Tokenizer
36
37 // Used as scratch variable to communicate multiple values without
38 // consing up tons of objects.
39 var tcat, content;
40 function r(tc, style) {
41 tcat = tc;
42 return style;
43 }
44
45 function tokenBase(stream, state) {
46 var ch = stream.next();
47 if (ch == '"') {
48 state.tokenize = tokenString;
49 return state.tokenize(stream, state);
50 }
51 if (ch == "'") {
52 tcat = "atom";
53 if (stream.eat("\\")) {
54 if (stream.skipTo("'")) { stream.next(); return "string"; }
55 else { return "error"; }
56 } else {
57 stream.next();
58 return stream.eat("'") ? "string" : "error";
59 }
60 }
61 if (ch == "/") {
62 if (stream.eat("/")) { stream.skipToEnd(); return "comment"; }
63 if (stream.eat("*")) {
64 state.tokenize = tokenComment(1);
65 return state.tokenize(stream, state);
66 }
67 }
68 if (ch == "#") {
69 if (stream.eat("[")) { tcat = "open-attr"; return null; }
70 stream.eatWhile(/\w/);
71 return r("macro", "meta");
72 }
73 if (ch == ":" && stream.match(":<")) {
74 return r("op", null);
75 }
76 if (ch.match(/\d/) || (ch == "." && stream.eat(/\d/))) {
77 var flp = false;
78 if (!stream.match(/^x[\da-f]+/i) && !stream.match(/^b[01]+/)) {
79 stream.eatWhile(/\d/);
80 if (stream.eat(".")) { flp = true; stream.eatWhile(/\d/); }
81 if (stream.match(/^e[+\-]?\d+/i)) { flp = true; }
82 }
83 if (flp) stream.match(/^f(?:32|64)/);
84 else stream.match(/^[ui](?:8|16|32|64)/);
85 return r("atom", "number");
86 }
87 if (ch.match(/[()\[\]{}:;,]/)) return r(ch, null);
88 if (ch == "-" && stream.eat(">")) return r("->", null);
89 if (ch.match(operatorChar)) {
90 stream.eatWhile(operatorChar);
91 return r("op", null);
92 }
93 stream.eatWhile(/\w/);
94 content = stream.current();
95 if (stream.match(/^::\w/)) {
96 stream.backUp(1);
97 return r("prefix", "variable-2");
98 }
99 if (state.keywords.propertyIsEnumerable(content))
100 return r(state.keywords[content], content.match(/true|false/) ? "atom" : "keyword");
101 return r("name", "variable");
102 }
103
104 function tokenString(stream, state) {
105 var ch, escaped = false;
106 while (ch = stream.next()) {
107 if (ch == '"' && !escaped) {
108 state.tokenize = tokenBase;
109 return r("atom", "string");
110 }
111 escaped = !escaped && ch == "\\";
112 }
113 // Hack to not confuse the parser when a string is split in
114 // pieces.
115 return r("op", "string");
116 }
117
118 function tokenComment(depth) {
119 return function(stream, state) {
120 var lastCh = null, ch;
121 while (ch = stream.next()) {
122 if (ch == "/" && lastCh == "*") {
123 if (depth == 1) {
124 state.tokenize = tokenBase;
125 break;
126 } else {
127 state.tokenize = tokenComment(depth - 1);
128 return state.tokenize(stream, state);
129 }
130 }
131 if (ch == "*" && lastCh == "/") {
132 state.tokenize = tokenComment(depth + 1);
133 return state.tokenize(stream, state);
134 }
135 lastCh = ch;
136 }
137 return "comment";
138 };
139 }
140
141 // Parser
142
143 var cx = {state: null, stream: null, marked: null, cc: null};
144 function pass() {
145 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
146 }
147 function cont() {
148 pass.apply(null, arguments);
149 return true;
150 }
151
152 function pushlex(type, info) {
153 var result = function() {
154 var state = cx.state;
155 state.lexical = {indented: state.indented, column: cx.stream.column(),
156 type: type, prev: state.lexical, info: info};
157 };
158 result.lex = true;
159 return result;
160 }
161 function poplex() {
162 var state = cx.state;
163 if (state.lexical.prev) {
164 if (state.lexical.type == ")")
165 state.indented = state.lexical.indented;
166 state.lexical = state.lexical.prev;
167 }
168 }
169 function typecx() { cx.state.keywords = typeKeywords; }
170 function valcx() { cx.state.keywords = valKeywords; }
171 poplex.lex = typecx.lex = valcx.lex = true;
172
173 function commasep(comb, end) {
174 function more(type) {
175 if (type == ",") return cont(comb, more);
176 if (type == end) return cont();
177 return cont(more);
178 }
179 return function(type) {
180 if (type == end) return cont();
181 return pass(comb, more);
182 };
183 }
184
25
185 function stat_of(comb, tag) {
26 {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,
186 return cont(pushlex("stat", tag), comb, poplex, block);
27 token: "number"},
187 }
28 {regex: /(let(?:\s+mut)?|fn|enum|mod|struct|type)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: ["keyword", null, "def"]},
188 function block(type) {
29 {regex: /(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/, token: "keyword"},
189 if (type == "}") return cont();
30 {regex: /\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/, token: "atom"},
190 if (type == "let") return stat_of(letdef1, "let");
31 {regex: /\b(?:true|false|Some|None|Ok|Err)\b/, token: "builtin"},
191 if (type == "fn") return stat_of(fndef);
32 {regex: /\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,
192 if (type == "type") return cont(pushlex("stat"), tydef, endstatement, poplex, block);
33 token: ["keyword", null ,"def"]},
193 if (type == "enum") return stat_of(enumdef);
34 {regex: /#!?\[.*\]/, token: "meta"},
194 if (type == "mod") return stat_of(mod);
35 {regex: /\/\/.*/, token: "comment"},
195 if (type == "iface") return stat_of(iface);
36 {regex: /\/\*/, token: "comment", next: "comment"},
196 if (type == "impl") return stat_of(impl);
37 {regex: /[-+\/*=<>!]+/, token: "operator"},
197 if (type == "open-attr") return cont(pushlex("]"), commasep(expression, "]"), poplex);
38 {regex: /[a-zA-Z_]\w*!/,token: "variable-3"},
198 if (type == "ignore" || type.match(/[\]\);,]/)) return cont(block);
39 {regex: /[a-zA-Z_]\w*/, token: "variable"},
199 return pass(pushlex("stat"), expression, poplex, endstatement, block);
40 {regex: /[\{\[\(]/, indent: true},
200 }
41 {regex: /[\}\]\)]/, dedent: true}
201 function endstatement(type) {
42 ],
202 if (type == ";") return cont();
43 string: [
203 return pass();
44 {regex: /"/, token: "string", next: "start"},
204 }
45 {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"}
205 function expression(type) {
46 ],
206 if (type == "atom" || type == "name") return cont(maybeop);
47 string_raw: [
207 if (type == "{") return cont(pushlex("}"), exprbrace, poplex);
48 {regex: /"/, token: "string", next: "start"},
208 if (type.match(/[\[\(]/)) return matchBrackets(type, expression);
49 {regex: /[^"]*/, token: "string"}
209 if (type.match(/[\]\)\};,]/)) return pass();
50 ],
210 if (type == "if-style") return cont(expression, expression);
51 string_raw_hash: [
211 if (type == "else-style" || type == "op") return cont(expression);
52 {regex: /"#+/, token: "string", next: "start"},
212 if (type == "for") return cont(pattern, maybetype, inop, expression, expression);
53 {regex: /(?:[^"]|"(?!#))*/, token: "string"}
213 if (type == "alt") return cont(expression, altbody);
54 ],
214 if (type == "fn") return cont(fndef);
55 comment: [
215 if (type == "macro") return cont(macro);
56 {regex: /.*?\*\//, token: "comment", next: "start"},
216 return cont();
57 {regex: /.*/, token: "comment"}
217 }
58 ],
218 function maybeop(type) {
59 meta: {
219 if (content == ".") return cont(maybeprop);
60 dontIndentStates: ["comment"],
220 if (content == "::<"){return cont(typarams, maybeop);}
61 electricInput: /^\s*\}$/,
221 if (type == "op" || content == ":") return cont(expression);
222 if (type == "(" || type == "[") return matchBrackets(type, expression);
223 return pass();
224 }
225 function maybeprop() {
226 if (content.match(/^\w+$/)) {cx.marked = "variable"; return cont(maybeop);}
227 return pass(expression);
228 }
229 function exprbrace(type) {
230 if (type == "op") {
231 if (content == "|") return cont(blockvars, poplex, pushlex("}", "block"), block);
232 if (content == "||") return cont(poplex, pushlex("}", "block"), block);
233 }
234 if (content == "mutable" || (content.match(/^\w+$/) && cx.stream.peek() == ":"
235 && !cx.stream.match("::", false)))
236 return pass(record_of(expression));
237 return pass(block);
238 }
239 function record_of(comb) {
240 function ro(type) {
241 if (content == "mutable" || content == "with") {cx.marked = "keyword"; return cont(ro);}
242 if (content.match(/^\w*$/)) {cx.marked = "variable"; return cont(ro);}
243 if (type == ":") return cont(comb, ro);
244 if (type == "}") return cont();
245 return cont(ro);
246 }
247 return ro;
248 }
249 function blockvars(type) {
250 if (type == "name") {cx.marked = "def"; return cont(blockvars);}
251 if (type == "op" && content == "|") return cont();
252 return cont(blockvars);
253 }
254
255 function letdef1(type) {
256 if (type.match(/[\]\)\};]/)) return cont();
257 if (content == "=") return cont(expression, letdef2);
258 if (type == ",") return cont(letdef1);
259 return pass(pattern, maybetype, letdef1);
260 }
261 function letdef2(type) {
262 if (type.match(/[\]\)\};,]/)) return pass(letdef1);
263 else return pass(expression, letdef2);
264 }
265 function maybetype(type) {
266 if (type == ":") return cont(typecx, rtype, valcx);
267 return pass();
268 }
269 function inop(type) {
270 if (type == "name" && content == "in") {cx.marked = "keyword"; return cont();}
271 return pass();
272 }
273 function fndef(type) {
274 if (content == "@" || content == "~") {cx.marked = "keyword"; return cont(fndef);}
275 if (type == "name") {cx.marked = "def"; return cont(fndef);}
276 if (content == "<") return cont(typarams, fndef);
277 if (type == "{") return pass(expression);
278 if (type == "(") return cont(pushlex(")"), commasep(argdef, ")"), poplex, fndef);
279 if (type == "->") return cont(typecx, rtype, valcx, fndef);
280 if (type == ";") return cont();
281 return cont(fndef);
282 }
283 function tydef(type) {
284 if (type == "name") {cx.marked = "def"; return cont(tydef);}
285 if (content == "<") return cont(typarams, tydef);
286 if (content == "=") return cont(typecx, rtype, valcx);
287 return cont(tydef);
288 }
289 function enumdef(type) {
290 if (type == "name") {cx.marked = "def"; return cont(enumdef);}
291 if (content == "<") return cont(typarams, enumdef);
292 if (content == "=") return cont(typecx, rtype, valcx, endstatement);
293 if (type == "{") return cont(pushlex("}"), typecx, enumblock, valcx, poplex);
294 return cont(enumdef);
295 }
296 function enumblock(type) {
297 if (type == "}") return cont();
298 if (type == "(") return cont(pushlex(")"), commasep(rtype, ")"), poplex, enumblock);
299 if (content.match(/^\w+$/)) cx.marked = "def";
300 return cont(enumblock);
301 }
302 function mod(type) {
303 if (type == "name") {cx.marked = "def"; return cont(mod);}
304 if (type == "{") return cont(pushlex("}"), block, poplex);
305 return pass();
306 }
307 function iface(type) {
308 if (type == "name") {cx.marked = "def"; return cont(iface);}
309 if (content == "<") return cont(typarams, iface);
310 if (type == "{") return cont(pushlex("}"), block, poplex);
311 return pass();
312 }
313 function impl(type) {
314 if (content == "<") return cont(typarams, impl);
315 if (content == "of" || content == "for") {cx.marked = "keyword"; return cont(rtype, impl);}
316 if (type == "name") {cx.marked = "def"; return cont(impl);}
317 if (type == "{") return cont(pushlex("}"), block, poplex);
318 return pass();
319 }
320 function typarams() {
321 if (content == ">") return cont();
322 if (content == ",") return cont(typarams);
323 if (content == ":") return cont(rtype, typarams);
324 return pass(rtype, typarams);
325 }
326 function argdef(type) {
327 if (type == "name") {cx.marked = "def"; return cont(argdef);}
328 if (type == ":") return cont(typecx, rtype, valcx);
329 return pass();
330 }
331 function rtype(type) {
332 if (type == "name") {cx.marked = "variable-3"; return cont(rtypemaybeparam); }
333 if (content == "mutable") {cx.marked = "keyword"; return cont(rtype);}
334 if (type == "atom") return cont(rtypemaybeparam);
335 if (type == "op" || type == "obj") return cont(rtype);
336 if (type == "fn") return cont(fntype);
337 if (type == "{") return cont(pushlex("{"), record_of(rtype), poplex);
338 return matchBrackets(type, rtype);
339 }
340 function rtypemaybeparam() {
341 if (content == "<") return cont(typarams);
342 return pass();
343 }
344 function fntype(type) {
345 if (type == "(") return cont(pushlex("("), commasep(rtype, ")"), poplex, fntype);
346 if (type == "->") return cont(rtype);
347 return pass();
348 }
349 function pattern(type) {
350 if (type == "name") {cx.marked = "def"; return cont(patternmaybeop);}
351 if (type == "atom") return cont(patternmaybeop);
352 if (type == "op") return cont(pattern);
353 if (type.match(/[\]\)\};,]/)) return pass();
354 return matchBrackets(type, pattern);
355 }
356 function patternmaybeop(type) {
357 if (type == "op" && content == ".") return cont();
358 if (content == "to") {cx.marked = "keyword"; return cont(pattern);}
359 else return pass();
360 }
361 function altbody(type) {
362 if (type == "{") return cont(pushlex("}", "alt"), altblock1, poplex);
363 return pass();
364 }
365 function altblock1(type) {
366 if (type == "}") return cont();
367 if (type == "|") return cont(altblock1);
368 if (content == "when") {cx.marked = "keyword"; return cont(expression, altblock2);}
369 if (type.match(/[\]\);,]/)) return cont(altblock1);
370 return pass(pattern, altblock2);
371 }
372 function altblock2(type) {
373 if (type == "{") return cont(pushlex("}", "alt"), block, poplex, altblock1);
374 else return pass(altblock1);
375 }
376
377 function macro(type) {
378 if (type.match(/[\[\(\{]/)) return matchBrackets(type, expression);
379 return pass();
380 }
381 function matchBrackets(type, comb) {
382 if (type == "[") return cont(pushlex("]"), commasep(comb, "]"), poplex);
383 if (type == "(") return cont(pushlex(")"), commasep(comb, ")"), poplex);
384 if (type == "{") return cont(pushlex("}"), commasep(comb, "}"), poplex);
385 return cont();
386 }
387
388 function parse(state, stream, style) {
389 var cc = state.cc;
390 // Communicate our context to the combinators.
391 // (Less wasteful than consing up a hundred closures on every call.)
392 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
393
394 while (true) {
395 var combinator = cc.length ? cc.pop() : block;
396 if (combinator(tcat)) {
397 while(cc.length && cc[cc.length - 1].lex)
398 cc.pop()();
399 return cx.marked || style;
400 }
401 }
402 }
403
404 return {
405 startState: function() {
406 return {
407 tokenize: tokenBase,
408 cc: [],
409 lexical: {indented: -indentUnit, column: 0, type: "top", align: false},
410 keywords: valKeywords,
411 indented: 0
412 };
413 },
414
415 token: function(stream, state) {
416 if (stream.sol()) {
417 if (!state.lexical.hasOwnProperty("align"))
418 state.lexical.align = false;
419 state.indented = stream.indentation();
420 }
421 if (stream.eatSpace()) return null;
422 tcat = content = null;
423 var style = state.tokenize(stream, state);
424 if (style == "comment") return style;
425 if (!state.lexical.hasOwnProperty("align"))
426 state.lexical.align = true;
427 if (tcat == "prefix") return style;
428 if (!content) content = stream.current();
429 return parse(state, stream, style);
430 },
431
432 indent: function(state, textAfter) {
433 if (state.tokenize != tokenBase) return 0;
434 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
435 type = lexical.type, closing = firstChar == type;
436 if (type == "stat") return lexical.indented + indentUnit;
437 if (lexical.align) return lexical.column + (closing ? 0 : 1);
438 return lexical.indented + (closing ? 0 : (lexical.info == "alt" ? altIndentUnit : indentUnit));
439 },
440
441 electricChars: "{}",
442 blockCommentStart: "/*",
62 blockCommentStart: "/*",
443 blockCommentEnd: "*/",
63 blockCommentEnd: "*/",
444 lineComment: "//",
64 lineComment: "//",
445 fold: "brace"
65 fold: "brace"
446 };
66 }
447 });
67 });
448
68
69
449 CodeMirror.defineMIME("text/x-rustsrc", "rust");
70 CodeMirror.defineMIME("text/x-rustsrc", "rust");
450
451 });
71 });
@@ -1,174 +1,176 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("sparql", function(config) {
14 CodeMirror.defineMode("sparql", function(config) {
15 var indentUnit = config.indentUnit;
15 var indentUnit = config.indentUnit;
16 var curPunc;
16 var curPunc;
17
17
18 function wordRegexp(words) {
18 function wordRegexp(words) {
19 return new RegExp("^(?:" + words.join("|") + ")$", "i");
19 return new RegExp("^(?:" + words.join("|") + ")$", "i");
20 }
20 }
21 var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
21 var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
22 "iri", "uri", "bnode", "count", "sum", "min", "max", "avg", "sample",
22 "iri", "uri", "bnode", "count", "sum", "min", "max", "avg", "sample",
23 "group_concat", "rand", "abs", "ceil", "floor", "round", "concat", "substr", "strlen",
23 "group_concat", "rand", "abs", "ceil", "floor", "round", "concat", "substr", "strlen",
24 "replace", "ucase", "lcase", "encode_for_uri", "contains", "strstarts", "strends",
24 "replace", "ucase", "lcase", "encode_for_uri", "contains", "strstarts", "strends",
25 "strbefore", "strafter", "year", "month", "day", "hours", "minutes", "seconds",
25 "strbefore", "strafter", "year", "month", "day", "hours", "minutes", "seconds",
26 "timezone", "tz", "now", "uuid", "struuid", "md5", "sha1", "sha256", "sha384",
26 "timezone", "tz", "now", "uuid", "struuid", "md5", "sha1", "sha256", "sha384",
27 "sha512", "coalesce", "if", "strlang", "strdt", "isnumeric", "regex", "exists",
27 "sha512", "coalesce", "if", "strlang", "strdt", "isnumeric", "regex", "exists",
28 "isblank", "isliteral", "a"]);
28 "isblank", "isliteral", "a"]);
29 var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
29 var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
30 "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
30 "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
31 "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group",
31 "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group",
32 "minus", "in", "not", "service", "silent", "using", "insert", "delete", "union",
32 "minus", "in", "not", "service", "silent", "using", "insert", "delete", "union",
33 "true", "false", "with",
33 "true", "false", "with",
34 "data", "copy", "to", "move", "add", "create", "drop", "clear", "load"]);
34 "data", "copy", "to", "move", "add", "create", "drop", "clear", "load"]);
35 var operatorChars = /[*+\-<>=&|\^\/!\?]/;
35 var operatorChars = /[*+\-<>=&|\^\/!\?]/;
36
36
37 function tokenBase(stream, state) {
37 function tokenBase(stream, state) {
38 var ch = stream.next();
38 var ch = stream.next();
39 curPunc = null;
39 curPunc = null;
40 if (ch == "$" || ch == "?") {
40 if (ch == "$" || ch == "?") {
41 if(ch == "?" && stream.match(/\s/, false)){
41 if(ch == "?" && stream.match(/\s/, false)){
42 return "operator";
42 return "operator";
43 }
43 }
44 stream.match(/^[\w\d]*/);
44 stream.match(/^[\w\d]*/);
45 return "variable-2";
45 return "variable-2";
46 }
46 }
47 else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
47 else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
48 stream.match(/^[^\s\u00a0>]*>?/);
48 stream.match(/^[^\s\u00a0>]*>?/);
49 return "atom";
49 return "atom";
50 }
50 }
51 else if (ch == "\"" || ch == "'") {
51 else if (ch == "\"" || ch == "'") {
52 state.tokenize = tokenLiteral(ch);
52 state.tokenize = tokenLiteral(ch);
53 return state.tokenize(stream, state);
53 return state.tokenize(stream, state);
54 }
54 }
55 else if (/[{}\(\),\.;\[\]]/.test(ch)) {
55 else if (/[{}\(\),\.;\[\]]/.test(ch)) {
56 curPunc = ch;
56 curPunc = ch;
57 return "bracket";
57 return "bracket";
58 }
58 }
59 else if (ch == "#") {
59 else if (ch == "#") {
60 stream.skipToEnd();
60 stream.skipToEnd();
61 return "comment";
61 return "comment";
62 }
62 }
63 else if (operatorChars.test(ch)) {
63 else if (operatorChars.test(ch)) {
64 stream.eatWhile(operatorChars);
64 stream.eatWhile(operatorChars);
65 return "operator";
65 return "operator";
66 }
66 }
67 else if (ch == ":") {
67 else if (ch == ":") {
68 stream.eatWhile(/[\w\d\._\-]/);
68 stream.eatWhile(/[\w\d\._\-]/);
69 return "atom";
69 return "atom";
70 }
70 }
71 else if (ch == "@") {
71 else if (ch == "@") {
72 stream.eatWhile(/[a-z\d\-]/i);
72 stream.eatWhile(/[a-z\d\-]/i);
73 return "meta";
73 return "meta";
74 }
74 }
75 else {
75 else {
76 stream.eatWhile(/[_\w\d]/);
76 stream.eatWhile(/[_\w\d]/);
77 if (stream.eat(":")) {
77 if (stream.eat(":")) {
78 stream.eatWhile(/[\w\d_\-]/);
78 stream.eatWhile(/[\w\d_\-]/);
79 return "atom";
79 return "atom";
80 }
80 }
81 var word = stream.current();
81 var word = stream.current();
82 if (ops.test(word))
82 if (ops.test(word))
83 return "builtin";
83 return "builtin";
84 else if (keywords.test(word))
84 else if (keywords.test(word))
85 return "keyword";
85 return "keyword";
86 else
86 else
87 return "variable";
87 return "variable";
88 }
88 }
89 }
89 }
90
90
91 function tokenLiteral(quote) {
91 function tokenLiteral(quote) {
92 return function(stream, state) {
92 return function(stream, state) {
93 var escaped = false, ch;
93 var escaped = false, ch;
94 while ((ch = stream.next()) != null) {
94 while ((ch = stream.next()) != null) {
95 if (ch == quote && !escaped) {
95 if (ch == quote && !escaped) {
96 state.tokenize = tokenBase;
96 state.tokenize = tokenBase;
97 break;
97 break;
98 }
98 }
99 escaped = !escaped && ch == "\\";
99 escaped = !escaped && ch == "\\";
100 }
100 }
101 return "string";
101 return "string";
102 };
102 };
103 }
103 }
104
104
105 function pushContext(state, type, col) {
105 function pushContext(state, type, col) {
106 state.context = {prev: state.context, indent: state.indent, col: col, type: type};
106 state.context = {prev: state.context, indent: state.indent, col: col, type: type};
107 }
107 }
108 function popContext(state) {
108 function popContext(state) {
109 state.indent = state.context.indent;
109 state.indent = state.context.indent;
110 state.context = state.context.prev;
110 state.context = state.context.prev;
111 }
111 }
112
112
113 return {
113 return {
114 startState: function() {
114 startState: function() {
115 return {tokenize: tokenBase,
115 return {tokenize: tokenBase,
116 context: null,
116 context: null,
117 indent: 0,
117 indent: 0,
118 col: 0};
118 col: 0};
119 },
119 },
120
120
121 token: function(stream, state) {
121 token: function(stream, state) {
122 if (stream.sol()) {
122 if (stream.sol()) {
123 if (state.context && state.context.align == null) state.context.align = false;
123 if (state.context && state.context.align == null) state.context.align = false;
124 state.indent = stream.indentation();
124 state.indent = stream.indentation();
125 }
125 }
126 if (stream.eatSpace()) return null;
126 if (stream.eatSpace()) return null;
127 var style = state.tokenize(stream, state);
127 var style = state.tokenize(stream, state);
128
128
129 if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") {
129 if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") {
130 state.context.align = true;
130 state.context.align = true;
131 }
131 }
132
132
133 if (curPunc == "(") pushContext(state, ")", stream.column());
133 if (curPunc == "(") pushContext(state, ")", stream.column());
134 else if (curPunc == "[") pushContext(state, "]", stream.column());
134 else if (curPunc == "[") pushContext(state, "]", stream.column());
135 else if (curPunc == "{") pushContext(state, "}", stream.column());
135 else if (curPunc == "{") pushContext(state, "}", stream.column());
136 else if (/[\]\}\)]/.test(curPunc)) {
136 else if (/[\]\}\)]/.test(curPunc)) {
137 while (state.context && state.context.type == "pattern") popContext(state);
137 while (state.context && state.context.type == "pattern") popContext(state);
138 if (state.context && curPunc == state.context.type) popContext(state);
138 if (state.context && curPunc == state.context.type) popContext(state);
139 }
139 }
140 else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
140 else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
141 else if (/atom|string|variable/.test(style) && state.context) {
141 else if (/atom|string|variable/.test(style) && state.context) {
142 if (/[\}\]]/.test(state.context.type))
142 if (/[\}\]]/.test(state.context.type))
143 pushContext(state, "pattern", stream.column());
143 pushContext(state, "pattern", stream.column());
144 else if (state.context.type == "pattern" && !state.context.align) {
144 else if (state.context.type == "pattern" && !state.context.align) {
145 state.context.align = true;
145 state.context.align = true;
146 state.context.col = stream.column();
146 state.context.col = stream.column();
147 }
147 }
148 }
148 }
149
149
150 return style;
150 return style;
151 },
151 },
152
152
153 indent: function(state, textAfter) {
153 indent: function(state, textAfter) {
154 var firstChar = textAfter && textAfter.charAt(0);
154 var firstChar = textAfter && textAfter.charAt(0);
155 var context = state.context;
155 var context = state.context;
156 if (/[\]\}]/.test(firstChar))
156 if (/[\]\}]/.test(firstChar))
157 while (context && context.type == "pattern") context = context.prev;
157 while (context && context.type == "pattern") context = context.prev;
158
158
159 var closing = context && firstChar == context.type;
159 var closing = context && firstChar == context.type;
160 if (!context)
160 if (!context)
161 return 0;
161 return 0;
162 else if (context.type == "pattern")
162 else if (context.type == "pattern")
163 return context.col;
163 return context.col;
164 else if (context.align)
164 else if (context.align)
165 return context.col + (closing ? 0 : 1);
165 return context.col + (closing ? 0 : 1);
166 else
166 else
167 return context.indent + (closing ? 0 : indentUnit);
167 return context.indent + (closing ? 0 : indentUnit);
168 }
168 },
169
170 lineComment: "#"
169 };
171 };
170 });
172 });
171
173
172 CodeMirror.defineMIME("application/sparql-query", "sparql");
174 CodeMirror.defineMIME("application/sparql-query", "sparql");
173
175
174 });
176 });
@@ -1,391 +1,391 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("sql", function(config, parserConfig) {
14 CodeMirror.defineMode("sql", function(config, parserConfig) {
15 "use strict";
15 "use strict";
16
16
17 var client = parserConfig.client || {},
17 var client = parserConfig.client || {},
18 atoms = parserConfig.atoms || {"false": true, "true": true, "null": true},
18 atoms = parserConfig.atoms || {"false": true, "true": true, "null": true},
19 builtin = parserConfig.builtin || {},
19 builtin = parserConfig.builtin || {},
20 keywords = parserConfig.keywords || {},
20 keywords = parserConfig.keywords || {},
21 operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
21 operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
22 support = parserConfig.support || {},
22 support = parserConfig.support || {},
23 hooks = parserConfig.hooks || {},
23 hooks = parserConfig.hooks || {},
24 dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};
24 dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};
25
25
26 function tokenBase(stream, state) {
26 function tokenBase(stream, state) {
27 var ch = stream.next();
27 var ch = stream.next();
28
28
29 // call hooks from the mime type
29 // call hooks from the mime type
30 if (hooks[ch]) {
30 if (hooks[ch]) {
31 var result = hooks[ch](stream, state);
31 var result = hooks[ch](stream, state);
32 if (result !== false) return result;
32 if (result !== false) return result;
33 }
33 }
34
34
35 if (support.hexNumber == true &&
35 if (support.hexNumber == true &&
36 ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
36 ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
37 || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
37 || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
38 // hex
38 // hex
39 // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
39 // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
40 return "number";
40 return "number";
41 } else if (support.binaryNumber == true &&
41 } else if (support.binaryNumber == true &&
42 (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
42 (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
43 || (ch == "0" && stream.match(/^b[01]+/)))) {
43 || (ch == "0" && stream.match(/^b[01]+/)))) {
44 // bitstring
44 // bitstring
45 // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
45 // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
46 return "number";
46 return "number";
47 } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
47 } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
48 // numbers
48 // numbers
49 // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
49 // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
50 stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
50 stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
51 support.decimallessFloat == true && stream.eat('.');
51 support.decimallessFloat == true && stream.eat('.');
52 return "number";
52 return "number";
53 } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
53 } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
54 // placeholders
54 // placeholders
55 return "variable-3";
55 return "variable-3";
56 } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
56 } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
57 // strings
57 // strings
58 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
58 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
59 state.tokenize = tokenLiteral(ch);
59 state.tokenize = tokenLiteral(ch);
60 return state.tokenize(stream, state);
60 return state.tokenize(stream, state);
61 } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
61 } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
62 || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
62 || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
63 && (stream.peek() == "'" || stream.peek() == '"'))) {
63 && (stream.peek() == "'" || stream.peek() == '"'))) {
64 // charset casting: _utf8'str', N'str', n'str'
64 // charset casting: _utf8'str', N'str', n'str'
65 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
65 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
66 return "keyword";
66 return "keyword";
67 } else if (/^[\(\),\;\[\]]/.test(ch)) {
67 } else if (/^[\(\),\;\[\]]/.test(ch)) {
68 // no highlightning
68 // no highlightning
69 return null;
69 return null;
70 } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
70 } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
71 // 1-line comment
71 // 1-line comment
72 stream.skipToEnd();
72 stream.skipToEnd();
73 return "comment";
73 return "comment";
74 } else if ((support.commentHash && ch == "#")
74 } else if ((support.commentHash && ch == "#")
75 || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
75 || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
76 // 1-line comments
76 // 1-line comments
77 // ref: https://kb.askmonty.org/en/comment-syntax/
77 // ref: https://kb.askmonty.org/en/comment-syntax/
78 stream.skipToEnd();
78 stream.skipToEnd();
79 return "comment";
79 return "comment";
80 } else if (ch == "/" && stream.eat("*")) {
80 } else if (ch == "/" && stream.eat("*")) {
81 // multi-line comments
81 // multi-line comments
82 // ref: https://kb.askmonty.org/en/comment-syntax/
82 // ref: https://kb.askmonty.org/en/comment-syntax/
83 state.tokenize = tokenComment;
83 state.tokenize = tokenComment;
84 return state.tokenize(stream, state);
84 return state.tokenize(stream, state);
85 } else if (ch == ".") {
85 } else if (ch == ".") {
86 // .1 for 0.1
86 // .1 for 0.1
87 if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
87 if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
88 return "number";
88 return "number";
89 }
89 }
90 // .table_name (ODBC)
90 // .table_name (ODBC)
91 // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
91 // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
92 if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
92 if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
93 return "variable-2";
93 return "variable-2";
94 }
94 }
95 } else if (operatorChars.test(ch)) {
95 } else if (operatorChars.test(ch)) {
96 // operators
96 // operators
97 stream.eatWhile(operatorChars);
97 stream.eatWhile(operatorChars);
98 return null;
98 return null;
99 } else if (ch == '{' &&
99 } else if (ch == '{' &&
100 (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
100 (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
101 // dates (weird ODBC syntax)
101 // dates (weird ODBC syntax)
102 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
102 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
103 return "number";
103 return "number";
104 } else {
104 } else {
105 stream.eatWhile(/^[_\w\d]/);
105 stream.eatWhile(/^[_\w\d]/);
106 var word = stream.current().toLowerCase();
106 var word = stream.current().toLowerCase();
107 // dates (standard SQL syntax)
107 // dates (standard SQL syntax)
108 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
108 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
109 if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
109 if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
110 return "number";
110 return "number";
111 if (atoms.hasOwnProperty(word)) return "atom";
111 if (atoms.hasOwnProperty(word)) return "atom";
112 if (builtin.hasOwnProperty(word)) return "builtin";
112 if (builtin.hasOwnProperty(word)) return "builtin";
113 if (keywords.hasOwnProperty(word)) return "keyword";
113 if (keywords.hasOwnProperty(word)) return "keyword";
114 if (client.hasOwnProperty(word)) return "string-2";
114 if (client.hasOwnProperty(word)) return "string-2";
115 return null;
115 return null;
116 }
116 }
117 }
117 }
118
118
119 // 'string', with char specified in quote escaped by '\'
119 // 'string', with char specified in quote escaped by '\'
120 function tokenLiteral(quote) {
120 function tokenLiteral(quote) {
121 return function(stream, state) {
121 return function(stream, state) {
122 var escaped = false, ch;
122 var escaped = false, ch;
123 while ((ch = stream.next()) != null) {
123 while ((ch = stream.next()) != null) {
124 if (ch == quote && !escaped) {
124 if (ch == quote && !escaped) {
125 state.tokenize = tokenBase;
125 state.tokenize = tokenBase;
126 break;
126 break;
127 }
127 }
128 escaped = !escaped && ch == "\\";
128 escaped = !escaped && ch == "\\";
129 }
129 }
130 return "string";
130 return "string";
131 };
131 };
132 }
132 }
133 function tokenComment(stream, state) {
133 function tokenComment(stream, state) {
134 while (true) {
134 while (true) {
135 if (stream.skipTo("*")) {
135 if (stream.skipTo("*")) {
136 stream.next();
136 stream.next();
137 if (stream.eat("/")) {
137 if (stream.eat("/")) {
138 state.tokenize = tokenBase;
138 state.tokenize = tokenBase;
139 break;
139 break;
140 }
140 }
141 } else {
141 } else {
142 stream.skipToEnd();
142 stream.skipToEnd();
143 break;
143 break;
144 }
144 }
145 }
145 }
146 return "comment";
146 return "comment";
147 }
147 }
148
148
149 function pushContext(stream, state, type) {
149 function pushContext(stream, state, type) {
150 state.context = {
150 state.context = {
151 prev: state.context,
151 prev: state.context,
152 indent: stream.indentation(),
152 indent: stream.indentation(),
153 col: stream.column(),
153 col: stream.column(),
154 type: type
154 type: type
155 };
155 };
156 }
156 }
157
157
158 function popContext(state) {
158 function popContext(state) {
159 state.indent = state.context.indent;
159 state.indent = state.context.indent;
160 state.context = state.context.prev;
160 state.context = state.context.prev;
161 }
161 }
162
162
163 return {
163 return {
164 startState: function() {
164 startState: function() {
165 return {tokenize: tokenBase, context: null};
165 return {tokenize: tokenBase, context: null};
166 },
166 },
167
167
168 token: function(stream, state) {
168 token: function(stream, state) {
169 if (stream.sol()) {
169 if (stream.sol()) {
170 if (state.context && state.context.align == null)
170 if (state.context && state.context.align == null)
171 state.context.align = false;
171 state.context.align = false;
172 }
172 }
173 if (stream.eatSpace()) return null;
173 if (stream.eatSpace()) return null;
174
174
175 var style = state.tokenize(stream, state);
175 var style = state.tokenize(stream, state);
176 if (style == "comment") return style;
176 if (style == "comment") return style;
177
177
178 if (state.context && state.context.align == null)
178 if (state.context && state.context.align == null)
179 state.context.align = true;
179 state.context.align = true;
180
180
181 var tok = stream.current();
181 var tok = stream.current();
182 if (tok == "(")
182 if (tok == "(")
183 pushContext(stream, state, ")");
183 pushContext(stream, state, ")");
184 else if (tok == "[")
184 else if (tok == "[")
185 pushContext(stream, state, "]");
185 pushContext(stream, state, "]");
186 else if (state.context && state.context.type == tok)
186 else if (state.context && state.context.type == tok)
187 popContext(state);
187 popContext(state);
188 return style;
188 return style;
189 },
189 },
190
190
191 indent: function(state, textAfter) {
191 indent: function(state, textAfter) {
192 var cx = state.context;
192 var cx = state.context;
193 if (!cx) return CodeMirror.Pass;
193 if (!cx) return CodeMirror.Pass;
194 var closing = textAfter.charAt(0) == cx.type;
194 var closing = textAfter.charAt(0) == cx.type;
195 if (cx.align) return cx.col + (closing ? 0 : 1);
195 if (cx.align) return cx.col + (closing ? 0 : 1);
196 else return cx.indent + (closing ? 0 : config.indentUnit);
196 else return cx.indent + (closing ? 0 : config.indentUnit);
197 },
197 },
198
198
199 blockCommentStart: "/*",
199 blockCommentStart: "/*",
200 blockCommentEnd: "*/",
200 blockCommentEnd: "*/",
201 lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
201 lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
202 };
202 };
203 });
203 });
204
204
205 (function() {
205 (function() {
206 "use strict";
206 "use strict";
207
207
208 // `identifier`
208 // `identifier`
209 function hookIdentifier(stream) {
209 function hookIdentifier(stream) {
210 // MySQL/MariaDB identifiers
210 // MySQL/MariaDB identifiers
211 // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
211 // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
212 var ch;
212 var ch;
213 while ((ch = stream.next()) != null) {
213 while ((ch = stream.next()) != null) {
214 if (ch == "`" && !stream.eat("`")) return "variable-2";
214 if (ch == "`" && !stream.eat("`")) return "variable-2";
215 }
215 }
216 stream.backUp(stream.current().length - 1);
216 stream.backUp(stream.current().length - 1);
217 return stream.eatWhile(/\w/) ? "variable-2" : null;
217 return stream.eatWhile(/\w/) ? "variable-2" : null;
218 }
218 }
219
219
220 // variable token
220 // variable token
221 function hookVar(stream) {
221 function hookVar(stream) {
222 // variables
222 // variables
223 // @@prefix.varName @varName
223 // @@prefix.varName @varName
224 // varName can be quoted with ` or ' or "
224 // varName can be quoted with ` or ' or "
225 // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
225 // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
226 if (stream.eat("@")) {
226 if (stream.eat("@")) {
227 stream.match(/^session\./);
227 stream.match(/^session\./);
228 stream.match(/^local\./);
228 stream.match(/^local\./);
229 stream.match(/^global\./);
229 stream.match(/^global\./);
230 }
230 }
231
231
232 if (stream.eat("'")) {
232 if (stream.eat("'")) {
233 stream.match(/^.*'/);
233 stream.match(/^.*'/);
234 return "variable-2";
234 return "variable-2";
235 } else if (stream.eat('"')) {
235 } else if (stream.eat('"')) {
236 stream.match(/^.*"/);
236 stream.match(/^.*"/);
237 return "variable-2";
237 return "variable-2";
238 } else if (stream.eat("`")) {
238 } else if (stream.eat("`")) {
239 stream.match(/^.*`/);
239 stream.match(/^.*`/);
240 return "variable-2";
240 return "variable-2";
241 } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
241 } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
242 return "variable-2";
242 return "variable-2";
243 }
243 }
244 return null;
244 return null;
245 };
245 };
246
246
247 // short client keyword token
247 // short client keyword token
248 function hookClient(stream) {
248 function hookClient(stream) {
249 // \N means NULL
249 // \N means NULL
250 // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
250 // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
251 if (stream.eat("N")) {
251 if (stream.eat("N")) {
252 return "atom";
252 return "atom";
253 }
253 }
254 // \g, etc
254 // \g, etc
255 // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
255 // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
256 return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
256 return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
257 }
257 }
258
258
259 // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
259 // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
260 var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where ";
260 var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit";
261
261
262 // turn a space-separated list into an array
262 // turn a space-separated list into an array
263 function set(str) {
263 function set(str) {
264 var obj = {}, words = str.split(" ");
264 var obj = {}, words = str.split(" ");
265 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
265 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
266 return obj;
266 return obj;
267 }
267 }
268
268
269 // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
269 // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
270 CodeMirror.defineMIME("text/x-sql", {
270 CodeMirror.defineMIME("text/x-sql", {
271 name: "sql",
271 name: "sql",
272 keywords: set(sqlKeywords + "begin"),
272 keywords: set(sqlKeywords + "begin"),
273 builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
273 builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
274 atoms: set("false true null unknown"),
274 atoms: set("false true null unknown"),
275 operatorChars: /^[*+\-%<>!=]/,
275 operatorChars: /^[*+\-%<>!=]/,
276 dateSQL: set("date time timestamp"),
276 dateSQL: set("date time timestamp"),
277 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
277 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
278 });
278 });
279
279
280 CodeMirror.defineMIME("text/x-mssql", {
280 CodeMirror.defineMIME("text/x-mssql", {
281 name: "sql",
281 name: "sql",
282 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
282 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
283 keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare"),
283 keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare"),
284 builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),
284 builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),
285 atoms: set("false true null unknown"),
285 atoms: set("false true null unknown"),
286 operatorChars: /^[*+\-%<>!=]/,
286 operatorChars: /^[*+\-%<>!=]/,
287 dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
287 dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
288 hooks: {
288 hooks: {
289 "@": hookVar
289 "@": hookVar
290 }
290 }
291 });
291 });
292
292
293 CodeMirror.defineMIME("text/x-mysql", {
293 CodeMirror.defineMIME("text/x-mysql", {
294 name: "sql",
294 name: "sql",
295 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
295 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
296 keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
296 keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
297 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
297 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
298 atoms: set("false true null unknown"),
298 atoms: set("false true null unknown"),
299 operatorChars: /^[*+\-%<>!=&|^]/,
299 operatorChars: /^[*+\-%<>!=&|^]/,
300 dateSQL: set("date time timestamp"),
300 dateSQL: set("date time timestamp"),
301 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
301 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
302 hooks: {
302 hooks: {
303 "@": hookVar,
303 "@": hookVar,
304 "`": hookIdentifier,
304 "`": hookIdentifier,
305 "\\": hookClient
305 "\\": hookClient
306 }
306 }
307 });
307 });
308
308
309 CodeMirror.defineMIME("text/x-mariadb", {
309 CodeMirror.defineMIME("text/x-mariadb", {
310 name: "sql",
310 name: "sql",
311 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
311 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
312 keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
312 keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
313 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
313 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
314 atoms: set("false true null unknown"),
314 atoms: set("false true null unknown"),
315 operatorChars: /^[*+\-%<>!=&|^]/,
315 operatorChars: /^[*+\-%<>!=&|^]/,
316 dateSQL: set("date time timestamp"),
316 dateSQL: set("date time timestamp"),
317 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
317 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
318 hooks: {
318 hooks: {
319 "@": hookVar,
319 "@": hookVar,
320 "`": hookIdentifier,
320 "`": hookIdentifier,
321 "\\": hookClient
321 "\\": hookClient
322 }
322 }
323 });
323 });
324
324
325 // the query language used by Apache Cassandra is called CQL, but this mime type
325 // the query language used by Apache Cassandra is called CQL, but this mime type
326 // is called Cassandra to avoid confusion with Contextual Query Language
326 // is called Cassandra to avoid confusion with Contextual Query Language
327 CodeMirror.defineMIME("text/x-cassandra", {
327 CodeMirror.defineMIME("text/x-cassandra", {
328 name: "sql",
328 name: "sql",
329 client: { },
329 client: { },
330 keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),
330 keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"),
331 builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),
331 builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"),
332 atoms: set("false true infinity NaN"),
332 atoms: set("false true infinity NaN"),
333 operatorChars: /^[<>=]/,
333 operatorChars: /^[<>=]/,
334 dateSQL: { },
334 dateSQL: { },
335 support: set("commentSlashSlash decimallessFloat"),
335 support: set("commentSlashSlash decimallessFloat"),
336 hooks: { }
336 hooks: { }
337 });
337 });
338
338
339 // this is based on Peter Raganitsch's 'plsql' mode
339 // this is based on Peter Raganitsch's 'plsql' mode
340 CodeMirror.defineMIME("text/x-plsql", {
340 CodeMirror.defineMIME("text/x-plsql", {
341 name: "sql",
341 name: "sql",
342 client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
342 client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
343 keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
343 keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
344 builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
344 builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
345 operatorChars: /^[*+\-%<>!=~]/,
345 operatorChars: /^[*+\-%<>!=~]/,
346 dateSQL: set("date time timestamp"),
346 dateSQL: set("date time timestamp"),
347 support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
347 support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
348 });
348 });
349
349
350 // Created to support specific hive keywords
350 // Created to support specific hive keywords
351 CodeMirror.defineMIME("text/x-hive", {
351 CodeMirror.defineMIME("text/x-hive", {
352 name: "sql",
352 name: "sql",
353 keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),
353 keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),
354 builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),
354 builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),
355 atoms: set("false true null unknown"),
355 atoms: set("false true null unknown"),
356 operatorChars: /^[*+\-%<>!=]/,
356 operatorChars: /^[*+\-%<>!=]/,
357 dateSQL: set("date timestamp"),
357 dateSQL: set("date timestamp"),
358 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
358 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
359 });
359 });
360 }());
360 }());
361
361
362 });
362 });
363
363
364 /*
364 /*
365 How Properties of Mime Types are used by SQL Mode
365 How Properties of Mime Types are used by SQL Mode
366 =================================================
366 =================================================
367
367
368 keywords:
368 keywords:
369 A list of keywords you want to be highlighted.
369 A list of keywords you want to be highlighted.
370 builtin:
370 builtin:
371 A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
371 A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
372 operatorChars:
372 operatorChars:
373 All characters that must be handled as operators.
373 All characters that must be handled as operators.
374 client:
374 client:
375 Commands parsed and executed by the client (not the server).
375 Commands parsed and executed by the client (not the server).
376 support:
376 support:
377 A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
377 A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
378 * ODBCdotTable: .tableName
378 * ODBCdotTable: .tableName
379 * zerolessFloat: .1
379 * zerolessFloat: .1
380 * doubleQuote
380 * doubleQuote
381 * nCharCast: N'string'
381 * nCharCast: N'string'
382 * charsetCast: _utf8'string'
382 * charsetCast: _utf8'string'
383 * commentHash: use # char for comments
383 * commentHash: use # char for comments
384 * commentSlashSlash: use // for comments
384 * commentSlashSlash: use // for comments
385 * commentSpaceRequired: require a space after -- for comments
385 * commentSpaceRequired: require a space after -- for comments
386 atoms:
386 atoms:
387 Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
387 Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
388 UNKNOWN, INFINITY, UNDERFLOW, NaN...
388 UNKNOWN, INFINITY, UNDERFLOW, NaN...
389 dateSQL:
389 dateSQL:
390 Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
390 Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
391 */
391 */
@@ -1,768 +1,769 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 // Stylus mode created by Dmitry Kiselyov http://git.io/AaRB
4 // Stylus mode created by Dmitry Kiselyov http://git.io/AaRB
5
5
6 (function(mod) {
6 (function(mod) {
7 if (typeof exports == "object" && typeof module == "object") // CommonJS
7 if (typeof exports == "object" && typeof module == "object") // CommonJS
8 mod(require("../../lib/codemirror"));
8 mod(require("../../lib/codemirror"));
9 else if (typeof define == "function" && define.amd) // AMD
9 else if (typeof define == "function" && define.amd) // AMD
10 define(["../../lib/codemirror"], mod);
10 define(["../../lib/codemirror"], mod);
11 else // Plain browser env
11 else // Plain browser env
12 mod(CodeMirror);
12 mod(CodeMirror);
13 })(function(CodeMirror) {
13 })(function(CodeMirror) {
14 "use strict";
14 "use strict";
15
15
16 CodeMirror.defineMode("stylus", function(config) {
16 CodeMirror.defineMode("stylus", function(config) {
17 var indentUnit = config.indentUnit,
17 var indentUnit = config.indentUnit,
18 tagKeywords = keySet(tagKeywords_),
18 tagKeywords = keySet(tagKeywords_),
19 tagVariablesRegexp = /^(a|b|i|s|col|em)$/i,
19 tagVariablesRegexp = /^(a|b|i|s|col|em)$/i,
20 propertyKeywords = keySet(propertyKeywords_),
20 propertyKeywords = keySet(propertyKeywords_),
21 nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_),
21 nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_),
22 valueKeywords = keySet(valueKeywords_),
22 valueKeywords = keySet(valueKeywords_),
23 colorKeywords = keySet(colorKeywords_),
23 colorKeywords = keySet(colorKeywords_),
24 documentTypes = keySet(documentTypes_),
24 documentTypes = keySet(documentTypes_),
25 documentTypesRegexp = wordRegexp(documentTypes_),
25 documentTypesRegexp = wordRegexp(documentTypes_),
26 mediaFeatures = keySet(mediaFeatures_),
26 mediaFeatures = keySet(mediaFeatures_),
27 mediaTypes = keySet(mediaTypes_),
27 mediaTypes = keySet(mediaTypes_),
28 fontProperties = keySet(fontProperties_),
28 fontProperties = keySet(fontProperties_),
29 operatorsRegexp = /^\s*([.]{2,3}|&&|\|\||\*\*|[?!=:]?=|[-+*\/%<>]=?|\?:|\~)/,
29 operatorsRegexp = /^\s*([.]{2,3}|&&|\|\||\*\*|[?!=:]?=|[-+*\/%<>]=?|\?:|\~)/,
30 wordOperatorKeywordsRegexp = wordRegexp(wordOperatorKeywords_),
30 wordOperatorKeywordsRegexp = wordRegexp(wordOperatorKeywords_),
31 blockKeywords = keySet(blockKeywords_),
31 blockKeywords = keySet(blockKeywords_),
32 vendorPrefixesRegexp = new RegExp(/^\-(moz|ms|o|webkit)-/i),
32 vendorPrefixesRegexp = new RegExp(/^\-(moz|ms|o|webkit)-/i),
33 commonAtoms = keySet(commonAtoms_),
33 commonAtoms = keySet(commonAtoms_),
34 firstWordMatch = "",
34 firstWordMatch = "",
35 states = {},
35 states = {},
36 ch,
36 ch,
37 style,
37 style,
38 type,
38 type,
39 override;
39 override;
40
40
41 /**
41 /**
42 * Tokenizers
42 * Tokenizers
43 */
43 */
44 function tokenBase(stream, state) {
44 function tokenBase(stream, state) {
45 firstWordMatch = stream.string.match(/(^[\w-]+\s*=\s*$)|(^\s*[\w-]+\s*=\s*[\w-])|(^\s*(\.|#|@|\$|\&|\[|\d|\+|::?|\{|\>|~|\/)?\s*[\w-]*([a-z0-9-]|\*|\/\*)(\(|,)?)/);
45 firstWordMatch = stream.string.match(/(^[\w-]+\s*=\s*$)|(^\s*[\w-]+\s*=\s*[\w-])|(^\s*(\.|#|@|\$|\&|\[|\d|\+|::?|\{|\>|~|\/)?\s*[\w-]*([a-z0-9-]|\*|\/\*)(\(|,)?)/);
46 state.context.line.firstWord = firstWordMatch ? firstWordMatch[0].replace(/^\s*/, "") : "";
46 state.context.line.firstWord = firstWordMatch ? firstWordMatch[0].replace(/^\s*/, "") : "";
47 state.context.line.indent = stream.indentation();
47 state.context.line.indent = stream.indentation();
48 ch = stream.peek();
48 ch = stream.peek();
49
49
50 // Line comment
50 // Line comment
51 if (stream.match("//")) {
51 if (stream.match("//")) {
52 stream.skipToEnd();
52 stream.skipToEnd();
53 return ["comment", "comment"];
53 return ["comment", "comment"];
54 }
54 }
55 // Block comment
55 // Block comment
56 if (stream.match("/*")) {
56 if (stream.match("/*")) {
57 state.tokenize = tokenCComment;
57 state.tokenize = tokenCComment;
58 return tokenCComment(stream, state);
58 return tokenCComment(stream, state);
59 }
59 }
60 // String
60 // String
61 if (ch == "\"" || ch == "'") {
61 if (ch == "\"" || ch == "'") {
62 stream.next();
62 stream.next();
63 state.tokenize = tokenString(ch);
63 state.tokenize = tokenString(ch);
64 return state.tokenize(stream, state);
64 return state.tokenize(stream, state);
65 }
65 }
66 // Def
66 // Def
67 if (ch == "@") {
67 if (ch == "@") {
68 stream.next();
68 stream.next();
69 stream.eatWhile(/[\w\\-]/);
69 stream.eatWhile(/[\w\\-]/);
70 return ["def", stream.current()];
70 return ["def", stream.current()];
71 }
71 }
72 // ID selector or Hex color
72 // ID selector or Hex color
73 if (ch == "#") {
73 if (ch == "#") {
74 stream.next();
74 stream.next();
75 // Hex color
75 // Hex color
76 if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) {
76 if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) {
77 return ["atom", "atom"];
77 return ["atom", "atom"];
78 }
78 }
79 // ID selector
79 // ID selector
80 if (stream.match(/^[a-z][\w-]*/i)) {
80 if (stream.match(/^[a-z][\w-]*/i)) {
81 return ["builtin", "hash"];
81 return ["builtin", "hash"];
82 }
82 }
83 }
83 }
84 // Vendor prefixes
84 // Vendor prefixes
85 if (stream.match(vendorPrefixesRegexp)) {
85 if (stream.match(vendorPrefixesRegexp)) {
86 return ["meta", "vendor-prefixes"];
86 return ["meta", "vendor-prefixes"];
87 }
87 }
88 // Numbers
88 // Numbers
89 if (stream.match(/^-?[0-9]?\.?[0-9]/)) {
89 if (stream.match(/^-?[0-9]?\.?[0-9]/)) {
90 stream.eatWhile(/[a-z%]/i);
90 stream.eatWhile(/[a-z%]/i);
91 return ["number", "unit"];
91 return ["number", "unit"];
92 }
92 }
93 // !important|optional
93 // !important|optional
94 if (ch == "!") {
94 if (ch == "!") {
95 stream.next();
95 stream.next();
96 return [stream.match(/^(important|optional)/i) ? "keyword": "operator", "important"];
96 return [stream.match(/^(important|optional)/i) ? "keyword": "operator", "important"];
97 }
97 }
98 // Class
98 // Class
99 if (ch == "." && stream.match(/^\.[a-z][\w-]*/i)) {
99 if (ch == "." && stream.match(/^\.[a-z][\w-]*/i)) {
100 return ["qualifier", "qualifier"];
100 return ["qualifier", "qualifier"];
101 }
101 }
102 // url url-prefix domain regexp
102 // url url-prefix domain regexp
103 if (stream.match(documentTypesRegexp)) {
103 if (stream.match(documentTypesRegexp)) {
104 if (stream.peek() == "(") state.tokenize = tokenParenthesized;
104 if (stream.peek() == "(") state.tokenize = tokenParenthesized;
105 return ["property", "word"];
105 return ["property", "word"];
106 }
106 }
107 // Mixins / Functions
107 // Mixins / Functions
108 if (stream.match(/^[a-z][\w-]*\(/i)) {
108 if (stream.match(/^[a-z][\w-]*\(/i)) {
109 stream.backUp(1);
109 stream.backUp(1);
110 return ["keyword", "mixin"];
110 return ["keyword", "mixin"];
111 }
111 }
112 // Block mixins
112 // Block mixins
113 if (stream.match(/^(\+|-)[a-z][\w-]*\(/i)) {
113 if (stream.match(/^(\+|-)[a-z][\w-]*\(/i)) {
114 stream.backUp(1);
114 stream.backUp(1);
115 return ["keyword", "block-mixin"];
115 return ["keyword", "block-mixin"];
116 }
116 }
117 // Parent Reference BEM naming
117 // Parent Reference BEM naming
118 if (stream.string.match(/^\s*&/) && stream.match(/^[-_]+[a-z][\w-]*/)) {
118 if (stream.string.match(/^\s*&/) && stream.match(/^[-_]+[a-z][\w-]*/)) {
119 return ["qualifier", "qualifier"];
119 return ["qualifier", "qualifier"];
120 }
120 }
121 // / Root Reference & Parent Reference
121 // / Root Reference & Parent Reference
122 if (stream.match(/^(\/|&)(-|_|:|\.|#|[a-z])/)) {
122 if (stream.match(/^(\/|&)(-|_|:|\.|#|[a-z])/)) {
123 stream.backUp(1);
123 stream.backUp(1);
124 return ["variable-3", "reference"];
124 return ["variable-3", "reference"];
125 }
125 }
126 if (stream.match(/^&{1}\s*$/)) {
126 if (stream.match(/^&{1}\s*$/)) {
127 return ["variable-3", "reference"];
127 return ["variable-3", "reference"];
128 }
128 }
129 // Variable
130 if (ch == "$" && stream.match(/^\$[\w-]+/i)) {
131 return ["variable-2", "variable-name"];
132 }
133 // Word operator
129 // Word operator
134 if (stream.match(wordOperatorKeywordsRegexp)) {
130 if (stream.match(wordOperatorKeywordsRegexp)) {
135 return ["operator", "operator"];
131 return ["operator", "operator"];
136 }
132 }
137 // Word
133 // Word
138 if (stream.match(/^[-_]*[a-z0-9]+[\w-]*/i)) {
134 if (stream.match(/^\$?[-_]*[a-z0-9]+[\w-]*/i)) {
135 // Variable
139 if (stream.match(/^(\.|\[)[\w-\'\"\]]+/i, false)) {
136 if (stream.match(/^(\.|\[)[\w-\'\"\]]+/i, false)) {
140 if (!wordIsTag(stream.current())) {
137 if (!wordIsTag(stream.current())) {
141 stream.match(/[\w-]+/);
138 stream.match(/\./);
142 return ["variable-2", "variable-name"];
139 return ["variable-2", "variable-name"];
143 }
140 }
144 }
141 }
145 return ["variable-2", "word"];
142 return ["variable-2", "word"];
146 }
143 }
147 // Operators
144 // Operators
148 if (stream.match(operatorsRegexp)) {
145 if (stream.match(operatorsRegexp)) {
149 return ["operator", stream.current()];
146 return ["operator", stream.current()];
150 }
147 }
151 // Delimiters
148 // Delimiters
152 if (/[:;,{}\[\]\(\)]/.test(ch)) {
149 if (/[:;,{}\[\]\(\)]/.test(ch)) {
153 stream.next();
150 stream.next();
154 return [null, ch];
151 return [null, ch];
155 }
152 }
156 // Non-detected items
153 // Non-detected items
157 stream.next();
154 stream.next();
158 return [null, null];
155 return [null, null];
159 }
156 }
160
157
161 /**
158 /**
162 * Token comment
159 * Token comment
163 */
160 */
164 function tokenCComment(stream, state) {
161 function tokenCComment(stream, state) {
165 var maybeEnd = false, ch;
162 var maybeEnd = false, ch;
166 while ((ch = stream.next()) != null) {
163 while ((ch = stream.next()) != null) {
167 if (maybeEnd && ch == "/") {
164 if (maybeEnd && ch == "/") {
168 state.tokenize = null;
165 state.tokenize = null;
169 break;
166 break;
170 }
167 }
171 maybeEnd = (ch == "*");
168 maybeEnd = (ch == "*");
172 }
169 }
173 return ["comment", "comment"];
170 return ["comment", "comment"];
174 }
171 }
175
172
176 /**
173 /**
177 * Token string
174 * Token string
178 */
175 */
179 function tokenString(quote) {
176 function tokenString(quote) {
180 return function(stream, state) {
177 return function(stream, state) {
181 var escaped = false, ch;
178 var escaped = false, ch;
182 while ((ch = stream.next()) != null) {
179 while ((ch = stream.next()) != null) {
183 if (ch == quote && !escaped) {
180 if (ch == quote && !escaped) {
184 if (quote == ")") stream.backUp(1);
181 if (quote == ")") stream.backUp(1);
185 break;
182 break;
186 }
183 }
187 escaped = !escaped && ch == "\\";
184 escaped = !escaped && ch == "\\";
188 }
185 }
189 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
186 if (ch == quote || !escaped && quote != ")") state.tokenize = null;
190 return ["string", "string"];
187 return ["string", "string"];
191 };
188 };
192 }
189 }
193
190
194 /**
191 /**
195 * Token parenthesized
192 * Token parenthesized
196 */
193 */
197 function tokenParenthesized(stream, state) {
194 function tokenParenthesized(stream, state) {
198 stream.next(); // Must be "("
195 stream.next(); // Must be "("
199 if (!stream.match(/\s*[\"\')]/, false))
196 if (!stream.match(/\s*[\"\')]/, false))
200 state.tokenize = tokenString(")");
197 state.tokenize = tokenString(")");
201 else
198 else
202 state.tokenize = null;
199 state.tokenize = null;
203 return [null, "("];
200 return [null, "("];
204 }
201 }
205
202
206 /**
203 /**
207 * Context management
204 * Context management
208 */
205 */
209 function Context(type, indent, prev, line) {
206 function Context(type, indent, prev, line) {
210 this.type = type;
207 this.type = type;
211 this.indent = indent;
208 this.indent = indent;
212 this.prev = prev;
209 this.prev = prev;
213 this.line = line || {firstWord: "", indent: 0};
210 this.line = line || {firstWord: "", indent: 0};
214 }
211 }
215
212
216 function pushContext(state, stream, type, indent) {
213 function pushContext(state, stream, type, indent) {
217 indent = indent >= 0 ? indent : indentUnit;
214 indent = indent >= 0 ? indent : indentUnit;
218 state.context = new Context(type, stream.indentation() + indent, state.context);
215 state.context = new Context(type, stream.indentation() + indent, state.context);
219 return type;
216 return type;
220 }
217 }
221
218
222 function popContext(state, currentIndent) {
219 function popContext(state, currentIndent) {
223 var contextIndent = state.context.indent - indentUnit;
220 var contextIndent = state.context.indent - indentUnit;
224 currentIndent = currentIndent || false;
221 currentIndent = currentIndent || false;
225 state.context = state.context.prev;
222 state.context = state.context.prev;
226 if (currentIndent) state.context.indent = contextIndent;
223 if (currentIndent) state.context.indent = contextIndent;
227 return state.context.type;
224 return state.context.type;
228 }
225 }
229
226
230 function pass(type, stream, state) {
227 function pass(type, stream, state) {
231 return states[state.context.type](type, stream, state);
228 return states[state.context.type](type, stream, state);
232 }
229 }
233
230
234 function popAndPass(type, stream, state, n) {
231 function popAndPass(type, stream, state, n) {
235 for (var i = n || 1; i > 0; i--)
232 for (var i = n || 1; i > 0; i--)
236 state.context = state.context.prev;
233 state.context = state.context.prev;
237 return pass(type, stream, state);
234 return pass(type, stream, state);
238 }
235 }
239
236
240
237
241 /**
238 /**
242 * Parser
239 * Parser
243 */
240 */
244 function wordIsTag(word) {
241 function wordIsTag(word) {
245 return word.toLowerCase() in tagKeywords;
242 return word.toLowerCase() in tagKeywords;
246 }
243 }
247
244
248 function wordIsProperty(word) {
245 function wordIsProperty(word) {
249 word = word.toLowerCase();
246 word = word.toLowerCase();
250 return word in propertyKeywords || word in fontProperties;
247 return word in propertyKeywords || word in fontProperties;
251 }
248 }
252
249
253 function wordIsBlock(word) {
250 function wordIsBlock(word) {
254 return word.toLowerCase() in blockKeywords;
251 return word.toLowerCase() in blockKeywords;
255 }
252 }
256
253
257 function wordIsVendorPrefix(word) {
254 function wordIsVendorPrefix(word) {
258 return word.toLowerCase().match(vendorPrefixesRegexp);
255 return word.toLowerCase().match(vendorPrefixesRegexp);
259 }
256 }
260
257
261 function wordAsValue(word) {
258 function wordAsValue(word) {
262 var wordLC = word.toLowerCase();
259 var wordLC = word.toLowerCase();
263 var override = "variable-2";
260 var override = "variable-2";
264 if (wordIsTag(word)) override = "tag";
261 if (wordIsTag(word)) override = "tag";
265 else if (wordIsBlock(word)) override = "block-keyword";
262 else if (wordIsBlock(word)) override = "block-keyword";
266 else if (wordIsProperty(word)) override = "property";
263 else if (wordIsProperty(word)) override = "property";
267 else if (wordLC in valueKeywords || wordLC in commonAtoms) override = "atom";
264 else if (wordLC in valueKeywords || wordLC in commonAtoms) override = "atom";
268 else if (wordLC == "return" || wordLC in colorKeywords) override = "keyword";
265 else if (wordLC == "return" || wordLC in colorKeywords) override = "keyword";
269
266
270 // Font family
267 // Font family
271 else if (word.match(/^[A-Z]/)) override = "string";
268 else if (word.match(/^[A-Z]/)) override = "string";
272 return override;
269 return override;
273 }
270 }
274
271
275 function typeIsBlock(type, stream) {
272 function typeIsBlock(type, stream) {
276 return ((endOfLine(stream) && (type == "{" || type == "]" || type == "hash" || type == "qualifier")) || type == "block-mixin");
273 return ((endOfLine(stream) && (type == "{" || type == "]" || type == "hash" || type == "qualifier")) || type == "block-mixin");
277 }
274 }
278
275
279 function typeIsInterpolation(type, stream) {
276 function typeIsInterpolation(type, stream) {
280 return type == "{" && stream.match(/^\s*\$?[\w-]+/i, false);
277 return type == "{" && stream.match(/^\s*\$?[\w-]+/i, false);
281 }
278 }
282
279
283 function typeIsPseudo(type, stream) {
280 function typeIsPseudo(type, stream) {
284 return type == ":" && stream.match(/^[a-z-]+/, false);
281 return type == ":" && stream.match(/^[a-z-]+/, false);
285 }
282 }
286
283
287 function startOfLine(stream) {
284 function startOfLine(stream) {
288 return stream.sol() || stream.string.match(new RegExp("^\\s*" + escapeRegExp(stream.current())));
285 return stream.sol() || stream.string.match(new RegExp("^\\s*" + escapeRegExp(stream.current())));
289 }
286 }
290
287
291 function endOfLine(stream) {
288 function endOfLine(stream) {
292 return stream.eol() || stream.match(/^\s*$/, false);
289 return stream.eol() || stream.match(/^\s*$/, false);
293 }
290 }
294
291
295 function firstWordOfLine(line) {
292 function firstWordOfLine(line) {
296 var re = /^\s*[-_]*[a-z0-9]+[\w-]*/i;
293 var re = /^\s*[-_]*[a-z0-9]+[\w-]*/i;
297 var result = typeof line == "string" ? line.match(re) : line.string.match(re);
294 var result = typeof line == "string" ? line.match(re) : line.string.match(re);
298 return result ? result[0].replace(/^\s*/, "") : "";
295 return result ? result[0].replace(/^\s*/, "") : "";
299 }
296 }
300
297
301
298
302 /**
299 /**
303 * Block
300 * Block
304 */
301 */
305 states.block = function(type, stream, state) {
302 states.block = function(type, stream, state) {
306 if ((type == "comment" && startOfLine(stream)) ||
303 if ((type == "comment" && startOfLine(stream)) ||
307 (type == "," && endOfLine(stream)) ||
304 (type == "," && endOfLine(stream)) ||
308 type == "mixin") {
305 type == "mixin") {
309 return pushContext(state, stream, "block", 0);
306 return pushContext(state, stream, "block", 0);
310 }
307 }
311 if (typeIsInterpolation(type, stream)) {
308 if (typeIsInterpolation(type, stream)) {
312 return pushContext(state, stream, "interpolation");
309 return pushContext(state, stream, "interpolation");
313 }
310 }
314 if (endOfLine(stream) && type == "]") {
311 if (endOfLine(stream) && type == "]") {
315 if (!/^\s*(\.|#|:|\[|\*|&)/.test(stream.string) && !wordIsTag(firstWordOfLine(stream))) {
312 if (!/^\s*(\.|#|:|\[|\*|&)/.test(stream.string) && !wordIsTag(firstWordOfLine(stream))) {
316 return pushContext(state, stream, "block", 0);
313 return pushContext(state, stream, "block", 0);
317 }
314 }
318 }
315 }
319 if (typeIsBlock(type, stream, state)) {
316 if (typeIsBlock(type, stream, state)) {
320 return pushContext(state, stream, "block");
317 return pushContext(state, stream, "block");
321 }
318 }
322 if (type == "}" && endOfLine(stream)) {
319 if (type == "}" && endOfLine(stream)) {
323 return pushContext(state, stream, "block", 0);
320 return pushContext(state, stream, "block", 0);
324 }
321 }
325 if (type == "variable-name") {
322 if (type == "variable-name") {
326 if ((stream.indentation() == 0 && startOfLine(stream)) || wordIsBlock(firstWordOfLine(stream))) {
323 if (stream.string.match(/^\s?\$[\w-\.\[\]\'\"]+$/) || wordIsBlock(firstWordOfLine(stream))) {
327 return pushContext(state, stream, "variableName");
324 return pushContext(state, stream, "variableName");
328 }
325 }
329 else {
326 else {
330 return pushContext(state, stream, "variableName", 0);
327 return pushContext(state, stream, "variableName", 0);
331 }
328 }
332 }
329 }
333 if (type == "=") {
330 if (type == "=") {
334 if (!endOfLine(stream) && !wordIsBlock(firstWordOfLine(stream))) {
331 if (!endOfLine(stream) && !wordIsBlock(firstWordOfLine(stream))) {
335 return pushContext(state, stream, "block", 0);
332 return pushContext(state, stream, "block", 0);
336 }
333 }
337 return pushContext(state, stream, "block");
334 return pushContext(state, stream, "block");
338 }
335 }
339 if (type == "*") {
336 if (type == "*") {
340 if (endOfLine(stream) || stream.match(/\s*(,|\.|#|\[|:|{)/,false)) {
337 if (endOfLine(stream) || stream.match(/\s*(,|\.|#|\[|:|{)/,false)) {
341 override = "tag";
338 override = "tag";
342 return pushContext(state, stream, "block");
339 return pushContext(state, stream, "block");
343 }
340 }
344 }
341 }
345 if (typeIsPseudo(type, stream)) {
342 if (typeIsPseudo(type, stream)) {
346 return pushContext(state, stream, "pseudo");
343 return pushContext(state, stream, "pseudo");
347 }
344 }
348 if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {
345 if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {
349 return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock");
346 return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock");
350 }
347 }
351 if (/@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
348 if (/@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
352 return pushContext(state, stream, "keyframes");
349 return pushContext(state, stream, "keyframes");
353 }
350 }
354 if (/@extends?/.test(type)) {
351 if (/@extends?/.test(type)) {
355 return pushContext(state, stream, "extend", 0);
352 return pushContext(state, stream, "extend", 0);
356 }
353 }
357 if (type && type.charAt(0) == "@") {
354 if (type && type.charAt(0) == "@") {
358
355
359 // Property Lookup
356 // Property Lookup
360 if (stream.indentation() > 0 && wordIsProperty(stream.current().slice(1))) {
357 if (stream.indentation() > 0 && wordIsProperty(stream.current().slice(1))) {
361 override = "variable-2";
358 override = "variable-2";
362 return "block";
359 return "block";
363 }
360 }
364 if (/(@import|@require|@charset)/.test(type)) {
361 if (/(@import|@require|@charset)/.test(type)) {
365 return pushContext(state, stream, "block", 0);
362 return pushContext(state, stream, "block", 0);
366 }
363 }
367 return pushContext(state, stream, "block");
364 return pushContext(state, stream, "block");
368 }
365 }
369 if (type == "reference" && endOfLine(stream)) {
366 if (type == "reference" && endOfLine(stream)) {
370 return pushContext(state, stream, "block");
367 return pushContext(state, stream, "block");
371 }
368 }
372 if (type == "(") {
369 if (type == "(") {
373 return pushContext(state, stream, "parens");
370 return pushContext(state, stream, "parens");
374 }
371 }
375
372
376 if (type == "vendor-prefixes") {
373 if (type == "vendor-prefixes") {
377 return pushContext(state, stream, "vendorPrefixes");
374 return pushContext(state, stream, "vendorPrefixes");
378 }
375 }
379 if (type == "word") {
376 if (type == "word") {
380 var word = stream.current();
377 var word = stream.current();
381 override = wordAsValue(word);
378 override = wordAsValue(word);
382
379
383 if (override == "property") {
380 if (override == "property") {
384 if (startOfLine(stream)) {
381 if (startOfLine(stream)) {
385 return pushContext(state, stream, "block", 0);
382 return pushContext(state, stream, "block", 0);
386 } else {
383 } else {
387 override = "atom";
384 override = "atom";
388 return "block";
385 return "block";
389 }
386 }
390 }
387 }
391
388
392 if (override == "tag") {
389 if (override == "tag") {
393
390
394 // tag is a css value
391 // tag is a css value
395 if (/embed|menu|pre|progress|sub|table/.test(word)) {
392 if (/embed|menu|pre|progress|sub|table/.test(word)) {
396 if (wordIsProperty(firstWordOfLine(stream))) {
393 if (wordIsProperty(firstWordOfLine(stream))) {
397 override = "atom";
394 override = "atom";
398 return "block";
395 return "block";
399 }
396 }
400 }
397 }
401
398
402 // tag is an attribute
399 // tag is an attribute
403 if (stream.string.match(new RegExp("\\[\\s*" + word + "|" + word +"\\s*\\]"))) {
400 if (stream.string.match(new RegExp("\\[\\s*" + word + "|" + word +"\\s*\\]"))) {
404 override = "atom";
401 override = "atom";
405 return "block";
402 return "block";
406 }
403 }
407
404
408 // tag is a variable
405 // tag is a variable
409 if (tagVariablesRegexp.test(word)) {
406 if (tagVariablesRegexp.test(word)) {
410 if ((startOfLine(stream) && stream.string.match(/=/)) ||
407 if ((startOfLine(stream) && stream.string.match(/=/)) ||
411 (!startOfLine(stream) &&
408 (!startOfLine(stream) &&
412 !stream.string.match(/^(\s*\.|#|\&|\[|\/|>|\*)/) &&
409 !stream.string.match(/^(\s*\.|#|\&|\[|\/|>|\*)/) &&
413 !wordIsTag(firstWordOfLine(stream)))) {
410 !wordIsTag(firstWordOfLine(stream)))) {
414 override = "variable-2";
411 override = "variable-2";
415 if (wordIsBlock(firstWordOfLine(stream))) return "block";
412 if (wordIsBlock(firstWordOfLine(stream))) return "block";
416 return pushContext(state, stream, "block", 0);
413 return pushContext(state, stream, "block", 0);
417 }
414 }
418 }
415 }
419
416
420 if (endOfLine(stream)) return pushContext(state, stream, "block");
417 if (endOfLine(stream)) return pushContext(state, stream, "block");
421 }
418 }
422 if (override == "block-keyword") {
419 if (override == "block-keyword") {
423 override = "keyword";
420 override = "keyword";
424
421
425 // Postfix conditionals
422 // Postfix conditionals
426 if (stream.current(/(if|unless)/) && !startOfLine(stream)) {
423 if (stream.current(/(if|unless)/) && !startOfLine(stream)) {
427 return "block";
424 return "block";
428 }
425 }
429 return pushContext(state, stream, "block");
426 return pushContext(state, stream, "block");
430 }
427 }
431 if (word == "return") return pushContext(state, stream, "block", 0);
428 if (word == "return") return pushContext(state, stream, "block", 0);
429
430 // Placeholder selector
431 if (override == "variable-2" && stream.string.match(/^\s?\$[\w-\.\[\]\'\"]+$/)) {
432 return pushContext(state, stream, "block");
433 }
432 }
434 }
433 return state.context.type;
435 return state.context.type;
434 };
436 };
435
437
436
438
437 /**
439 /**
438 * Parens
440 * Parens
439 */
441 */
440 states.parens = function(type, stream, state) {
442 states.parens = function(type, stream, state) {
441 if (type == "(") return pushContext(state, stream, "parens");
443 if (type == "(") return pushContext(state, stream, "parens");
442 if (type == ")") {
444 if (type == ")") {
443 if (state.context.prev.type == "parens") {
445 if (state.context.prev.type == "parens") {
444 return popContext(state);
446 return popContext(state);
445 }
447 }
446 if ((stream.string.match(/^[a-z][\w-]*\(/i) && endOfLine(stream)) ||
448 if ((stream.string.match(/^[a-z][\w-]*\(/i) && endOfLine(stream)) ||
447 wordIsBlock(firstWordOfLine(stream)) ||
449 wordIsBlock(firstWordOfLine(stream)) ||
448 /(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(firstWordOfLine(stream)) ||
450 /(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(firstWordOfLine(stream)) ||
449 (!stream.string.match(/^-?[a-z][\w-\.\[\]\'\"]*\s*=/) &&
451 (!stream.string.match(/^-?[a-z][\w-\.\[\]\'\"]*\s*=/) &&
450 wordIsTag(firstWordOfLine(stream)))) {
452 wordIsTag(firstWordOfLine(stream)))) {
451 return pushContext(state, stream, "block");
453 return pushContext(state, stream, "block");
452 }
454 }
453 if (stream.string.match(/^[\$-]?[a-z][\w-\.\[\]\'\"]*\s*=/) ||
455 if (stream.string.match(/^[\$-]?[a-z][\w-\.\[\]\'\"]*\s*=/) ||
454 stream.string.match(/^\s*(\(|\)|[0-9])/) ||
456 stream.string.match(/^\s*(\(|\)|[0-9])/) ||
455 stream.string.match(/^\s+[a-z][\w-]*\(/i) ||
457 stream.string.match(/^\s+[a-z][\w-]*\(/i) ||
456 stream.string.match(/^\s+[\$-]?[a-z]/i)) {
458 stream.string.match(/^\s+[\$-]?[a-z]/i)) {
457 return pushContext(state, stream, "block", 0);
459 return pushContext(state, stream, "block", 0);
458 }
460 }
459 if (endOfLine(stream)) return pushContext(state, stream, "block");
461 if (endOfLine(stream)) return pushContext(state, stream, "block");
460 else return pushContext(state, stream, "block", 0);
462 else return pushContext(state, stream, "block", 0);
461 }
463 }
462 if (type && type.charAt(0) == "@" && wordIsProperty(stream.current().slice(1))) {
464 if (type && type.charAt(0) == "@" && wordIsProperty(stream.current().slice(1))) {
463 override = "variable-2";
465 override = "variable-2";
464 }
466 }
465 if (type == "word") {
467 if (type == "word") {
466 var word = stream.current();
468 var word = stream.current();
467 override = wordAsValue(word);
469 override = wordAsValue(word);
468 if (override == "tag" && tagVariablesRegexp.test(word)) {
470 if (override == "tag" && tagVariablesRegexp.test(word)) {
469 override = "variable-2";
471 override = "variable-2";
470 }
472 }
471 if (override == "property" || word == "to") override = "atom";
473 if (override == "property" || word == "to") override = "atom";
472 }
474 }
473 if (type == "variable-name") {
475 if (type == "variable-name") {
474 return pushContext(state, stream, "variableName");
476 return pushContext(state, stream, "variableName");
475 }
477 }
476 if (typeIsPseudo(type, stream)) {
478 if (typeIsPseudo(type, stream)) {
477 return pushContext(state, stream, "pseudo");
479 return pushContext(state, stream, "pseudo");
478 }
480 }
479 return state.context.type;
481 return state.context.type;
480 };
482 };
481
483
482
484
483 /**
485 /**
484 * Vendor prefixes
486 * Vendor prefixes
485 */
487 */
486 states.vendorPrefixes = function(type, stream, state) {
488 states.vendorPrefixes = function(type, stream, state) {
487 if (type == "word") {
489 if (type == "word") {
488 override = "property";
490 override = "property";
489 return pushContext(state, stream, "block", 0);
491 return pushContext(state, stream, "block", 0);
490 }
492 }
491 return popContext(state);
493 return popContext(state);
492 };
494 };
493
495
494
496
495 /**
497 /**
496 * Pseudo
498 * Pseudo
497 */
499 */
498 states.pseudo = function(type, stream, state) {
500 states.pseudo = function(type, stream, state) {
499 if (!wordIsProperty(firstWordOfLine(stream.string))) {
501 if (!wordIsProperty(firstWordOfLine(stream.string))) {
500 stream.match(/^[a-z-]+/);
502 stream.match(/^[a-z-]+/);
501 override = "variable-3";
503 override = "variable-3";
502 if (endOfLine(stream)) return pushContext(state, stream, "block");
504 if (endOfLine(stream)) return pushContext(state, stream, "block");
503 return popContext(state);
505 return popContext(state);
504 }
506 }
505 return popAndPass(type, stream, state);
507 return popAndPass(type, stream, state);
506 };
508 };
507
509
508
510
509 /**
511 /**
510 * atBlock
512 * atBlock
511 */
513 */
512 states.atBlock = function(type, stream, state) {
514 states.atBlock = function(type, stream, state) {
513 if (type == "(") return pushContext(state, stream, "atBlock_parens");
515 if (type == "(") return pushContext(state, stream, "atBlock_parens");
514 if (typeIsBlock(type, stream, state)) {
516 if (typeIsBlock(type, stream, state)) {
515 return pushContext(state, stream, "block");
517 return pushContext(state, stream, "block");
516 }
518 }
517 if (typeIsInterpolation(type, stream)) {
519 if (typeIsInterpolation(type, stream)) {
518 return pushContext(state, stream, "interpolation");
520 return pushContext(state, stream, "interpolation");
519 }
521 }
520 if (type == "word") {
522 if (type == "word") {
521 var word = stream.current().toLowerCase();
523 var word = stream.current().toLowerCase();
522 if (/^(only|not|and|or)$/.test(word))
524 if (/^(only|not|and|or)$/.test(word))
523 override = "keyword";
525 override = "keyword";
524 else if (documentTypes.hasOwnProperty(word))
526 else if (documentTypes.hasOwnProperty(word))
525 override = "tag";
527 override = "tag";
526 else if (mediaTypes.hasOwnProperty(word))
528 else if (mediaTypes.hasOwnProperty(word))
527 override = "attribute";
529 override = "attribute";
528 else if (mediaFeatures.hasOwnProperty(word))
530 else if (mediaFeatures.hasOwnProperty(word))
529 override = "property";
531 override = "property";
530 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
532 else if (nonStandardPropertyKeywords.hasOwnProperty(word))
531 override = "string-2";
533 override = "string-2";
532 else override = wordAsValue(stream.current());
534 else override = wordAsValue(stream.current());
533 if (override == "tag" && endOfLine(stream)) {
535 if (override == "tag" && endOfLine(stream)) {
534 return pushContext(state, stream, "block");
536 return pushContext(state, stream, "block");
535 }
537 }
536 }
538 }
537 if (type == "operator" && /^(not|and|or)$/.test(stream.current())) {
539 if (type == "operator" && /^(not|and|or)$/.test(stream.current())) {
538 override = "keyword";
540 override = "keyword";
539 }
541 }
540 return state.context.type;
542 return state.context.type;
541 };
543 };
542
544
543 states.atBlock_parens = function(type, stream, state) {
545 states.atBlock_parens = function(type, stream, state) {
544 if (type == "{" || type == "}") return state.context.type;
546 if (type == "{" || type == "}") return state.context.type;
545 if (type == ")") {
547 if (type == ")") {
546 if (endOfLine(stream)) return pushContext(state, stream, "block");
548 if (endOfLine(stream)) return pushContext(state, stream, "block");
547 else return pushContext(state, stream, "atBlock");
549 else return pushContext(state, stream, "atBlock");
548 }
550 }
549 if (type == "word") {
551 if (type == "word") {
550 var word = stream.current().toLowerCase();
552 var word = stream.current().toLowerCase();
551 override = wordAsValue(word);
553 override = wordAsValue(word);
552 if (/^(max|min)/.test(word)) override = "property";
554 if (/^(max|min)/.test(word)) override = "property";
553 if (override == "tag") {
555 if (override == "tag") {
554 tagVariablesRegexp.test(word) ? override = "variable-2" : override = "atom";
556 tagVariablesRegexp.test(word) ? override = "variable-2" : override = "atom";
555 }
557 }
556 return state.context.type;
558 return state.context.type;
557 }
559 }
558 return states.atBlock(type, stream, state);
560 return states.atBlock(type, stream, state);
559 };
561 };
560
562
561
563
562 /**
564 /**
563 * Keyframes
565 * Keyframes
564 */
566 */
565 states.keyframes = function(type, stream, state) {
567 states.keyframes = function(type, stream, state) {
566 if (stream.indentation() == "0" && ((type == "}" && startOfLine(stream)) || type == "]" || type == "hash"
568 if (stream.indentation() == "0" && ((type == "}" && startOfLine(stream)) || type == "]" || type == "hash"
567 || type == "qualifier" || wordIsTag(stream.current()))) {
569 || type == "qualifier" || wordIsTag(stream.current()))) {
568 return popAndPass(type, stream, state);
570 return popAndPass(type, stream, state);
569 }
571 }
570 if (type == "{") return pushContext(state, stream, "keyframes");
572 if (type == "{") return pushContext(state, stream, "keyframes");
571 if (type == "}") {
573 if (type == "}") {
572 if (startOfLine(stream)) return popContext(state, true);
574 if (startOfLine(stream)) return popContext(state, true);
573 else return pushContext(state, stream, "keyframes");
575 else return pushContext(state, stream, "keyframes");
574 }
576 }
575 if (type == "unit" && /^[0-9]+\%$/.test(stream.current())) {
577 if (type == "unit" && /^[0-9]+\%$/.test(stream.current())) {
576 return pushContext(state, stream, "keyframes");
578 return pushContext(state, stream, "keyframes");
577 }
579 }
578 if (type == "word") {
580 if (type == "word") {
579 override = wordAsValue(stream.current());
581 override = wordAsValue(stream.current());
580 if (override == "block-keyword") {
582 if (override == "block-keyword") {
581 override = "keyword";
583 override = "keyword";
582 return pushContext(state, stream, "keyframes");
584 return pushContext(state, stream, "keyframes");
583 }
585 }
584 }
586 }
585 if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {
587 if (/@(font-face|media|supports|(-moz-)?document)/.test(type)) {
586 return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock");
588 return pushContext(state, stream, endOfLine(stream) ? "block" : "atBlock");
587 }
589 }
588 if (type == "mixin") {
590 if (type == "mixin") {
589 return pushContext(state, stream, "block", 0);
591 return pushContext(state, stream, "block", 0);
590 }
592 }
591 return state.context.type;
593 return state.context.type;
592 };
594 };
593
595
594
596
595 /**
597 /**
596 * Interpolation
598 * Interpolation
597 */
599 */
598 states.interpolation = function(type, stream, state) {
600 states.interpolation = function(type, stream, state) {
599 if (type == "{") popContext(state) && pushContext(state, stream, "block");
601 if (type == "{") popContext(state) && pushContext(state, stream, "block");
600 if (type == "}") {
602 if (type == "}") {
601 if (stream.string.match(/^\s*(\.|#|:|\[|\*|&|>|~|\+|\/)/i) ||
603 if (stream.string.match(/^\s*(\.|#|:|\[|\*|&|>|~|\+|\/)/i) ||
602 (stream.string.match(/^\s*[a-z]/i) && wordIsTag(firstWordOfLine(stream)))) {
604 (stream.string.match(/^\s*[a-z]/i) && wordIsTag(firstWordOfLine(stream)))) {
603 return pushContext(state, stream, "block");
605 return pushContext(state, stream, "block");
604 }
606 }
605 if (!stream.string.match(/^(\{|\s*\&)/) ||
607 if (!stream.string.match(/^(\{|\s*\&)/) ||
606 stream.match(/\s*[\w-]/,false)) {
608 stream.match(/\s*[\w-]/,false)) {
607 return pushContext(state, stream, "block", 0);
609 return pushContext(state, stream, "block", 0);
608 }
610 }
609 return pushContext(state, stream, "block");
611 return pushContext(state, stream, "block");
610 }
612 }
611 if (type == "variable-name") {
613 if (type == "variable-name") {
612 return pushContext(state, stream, "variableName", 0);
614 return pushContext(state, stream, "variableName", 0);
613 }
615 }
614 if (type == "word") {
616 if (type == "word") {
615 override = wordAsValue(stream.current());
617 override = wordAsValue(stream.current());
616 if (override == "tag") override = "atom";
618 if (override == "tag") override = "atom";
617 }
619 }
618 return state.context.type;
620 return state.context.type;
619 };
621 };
620
622
621
623
622 /**
624 /**
623 * Extend/s
625 * Extend/s
624 */
626 */
625 states.extend = function(type, stream, state) {
627 states.extend = function(type, stream, state) {
626 if (type == "[" || type == "=") return "extend";
628 if (type == "[" || type == "=") return "extend";
627 if (type == "]") return popContext(state);
629 if (type == "]") return popContext(state);
628 if (type == "word") {
630 if (type == "word") {
629 override = wordAsValue(stream.current());
631 override = wordAsValue(stream.current());
630 return "extend";
632 return "extend";
631 }
633 }
632 return popContext(state);
634 return popContext(state);
633 };
635 };
634
636
635
637
636 /**
638 /**
637 * Variable name
639 * Variable name
638 */
640 */
639 states.variableName = function(type, stream, state) {
641 states.variableName = function(type, stream, state) {
640 if (type == "string" || type == "[" || type == "]" || stream.current().match(/^(\.|\$)/)) {
642 if (type == "string" || type == "[" || type == "]" || stream.current().match(/^(\.|\$)/)) {
641 if (stream.current().match(/^\.[\w-]+/i)) override = "variable-2";
643 if (stream.current().match(/^\.[\w-]+/i)) override = "variable-2";
642 if (endOfLine(stream)) return popContext(state);
643 return "variableName";
644 return "variableName";
644 }
645 }
645 return popAndPass(type, stream, state);
646 return popAndPass(type, stream, state);
646 };
647 };
647
648
648
649
649 return {
650 return {
650 startState: function(base) {
651 startState: function(base) {
651 return {
652 return {
652 tokenize: null,
653 tokenize: null,
653 state: "block",
654 state: "block",
654 context: new Context("block", base || 0, null)
655 context: new Context("block", base || 0, null)
655 };
656 };
656 },
657 },
657 token: function(stream, state) {
658 token: function(stream, state) {
658 if (!state.tokenize && stream.eatSpace()) return null;
659 if (!state.tokenize && stream.eatSpace()) return null;
659 style = (state.tokenize || tokenBase)(stream, state);
660 style = (state.tokenize || tokenBase)(stream, state);
660 if (style && typeof style == "object") {
661 if (style && typeof style == "object") {
661 type = style[1];
662 type = style[1];
662 style = style[0];
663 style = style[0];
663 }
664 }
664 override = style;
665 override = style;
665 state.state = states[state.state](type, stream, state);
666 state.state = states[state.state](type, stream, state);
666 return override;
667 return override;
667 },
668 },
668 indent: function(state, textAfter, line) {
669 indent: function(state, textAfter, line) {
669
670
670 var cx = state.context,
671 var cx = state.context,
671 ch = textAfter && textAfter.charAt(0),
672 ch = textAfter && textAfter.charAt(0),
672 indent = cx.indent,
673 indent = cx.indent,
673 lineFirstWord = firstWordOfLine(textAfter),
674 lineFirstWord = firstWordOfLine(textAfter),
674 lineIndent = line.length - line.replace(/^\s*/, "").length,
675 lineIndent = line.length - line.replace(/^\s*/, "").length,
675 prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : "",
676 prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : "",
676 prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent;
677 prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent;
677
678
678 if (cx.prev &&
679 if (cx.prev &&
679 (ch == "}" && (cx.type == "block" || cx.type == "atBlock" || cx.type == "keyframes") ||
680 (ch == "}" && (cx.type == "block" || cx.type == "atBlock" || cx.type == "keyframes") ||
680 ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
681 ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
681 ch == "{" && (cx.type == "at"))) {
682 ch == "{" && (cx.type == "at"))) {
682 indent = cx.indent - indentUnit;
683 indent = cx.indent - indentUnit;
683 cx = cx.prev;
684 cx = cx.prev;
684 } else if (!(/(\})/.test(ch))) {
685 } else if (!(/(\})/.test(ch))) {
685 if (/@|\$|\d/.test(ch) ||
686 if (/@|\$|\d/.test(ch) ||
686 /^\{/.test(textAfter) ||
687 /^\{/.test(textAfter) ||
687 /^\s*\/(\/|\*)/.test(textAfter) ||
688 /^\s*\/(\/|\*)/.test(textAfter) ||
688 /^\s*\/\*/.test(prevLineFirstWord) ||
689 /^\s*\/\*/.test(prevLineFirstWord) ||
689 /^\s*[\w-\.\[\]\'\"]+\s*(\?|:|\+)?=/i.test(textAfter) ||
690 /^\s*[\w-\.\[\]\'\"]+\s*(\?|:|\+)?=/i.test(textAfter) ||
690 /^(\+|-)?[a-z][\w-]*\(/i.test(textAfter) ||
691 /^(\+|-)?[a-z][\w-]*\(/i.test(textAfter) ||
691 /^return/.test(textAfter) ||
692 /^return/.test(textAfter) ||
692 wordIsBlock(lineFirstWord)) {
693 wordIsBlock(lineFirstWord)) {
693 indent = lineIndent;
694 indent = lineIndent;
694 } else if (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(ch) || wordIsTag(lineFirstWord)) {
695 } else if (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(ch) || wordIsTag(lineFirstWord)) {
695 if (/\,\s*$/.test(prevLineFirstWord)) {
696 if (/\,\s*$/.test(prevLineFirstWord)) {
696 indent = prevLineIndent;
697 indent = prevLineIndent;
697 } else if (/^\s+/.test(line) && (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(prevLineFirstWord) || wordIsTag(prevLineFirstWord))) {
698 } else if (/^\s+/.test(line) && (/(\.|#|:|\[|\*|&|>|~|\+|\/)/.test(prevLineFirstWord) || wordIsTag(prevLineFirstWord))) {
698 indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;
699 indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;
699 } else {
700 } else {
700 indent = lineIndent;
701 indent = lineIndent;
701 }
702 }
702 } else if (!/,\s*$/.test(line) && (wordIsVendorPrefix(lineFirstWord) || wordIsProperty(lineFirstWord))) {
703 } else if (!/,\s*$/.test(line) && (wordIsVendorPrefix(lineFirstWord) || wordIsProperty(lineFirstWord))) {
703 if (wordIsBlock(prevLineFirstWord)) {
704 if (wordIsBlock(prevLineFirstWord)) {
704 indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;
705 indent = lineIndent <= prevLineIndent ? prevLineIndent : prevLineIndent + indentUnit;
705 } else if (/^\{/.test(prevLineFirstWord)) {
706 } else if (/^\{/.test(prevLineFirstWord)) {
706 indent = lineIndent <= prevLineIndent ? lineIndent : prevLineIndent + indentUnit;
707 indent = lineIndent <= prevLineIndent ? lineIndent : prevLineIndent + indentUnit;
707 } else if (wordIsVendorPrefix(prevLineFirstWord) || wordIsProperty(prevLineFirstWord)) {
708 } else if (wordIsVendorPrefix(prevLineFirstWord) || wordIsProperty(prevLineFirstWord)) {
708 indent = lineIndent >= prevLineIndent ? prevLineIndent : lineIndent;
709 indent = lineIndent >= prevLineIndent ? prevLineIndent : lineIndent;
709 } else if (/^(\.|#|:|\[|\*|&|@|\+|\-|>|~|\/)/.test(prevLineFirstWord) ||
710 } else if (/^(\.|#|:|\[|\*|&|@|\+|\-|>|~|\/)/.test(prevLineFirstWord) ||
710 /=\s*$/.test(prevLineFirstWord) ||
711 /=\s*$/.test(prevLineFirstWord) ||
711 wordIsTag(prevLineFirstWord) ||
712 wordIsTag(prevLineFirstWord) ||
712 /^\$[\w-\.\[\]\'\"]/.test(prevLineFirstWord)) {
713 /^\$[\w-\.\[\]\'\"]/.test(prevLineFirstWord)) {
713 indent = prevLineIndent + indentUnit;
714 indent = prevLineIndent + indentUnit;
714 } else {
715 } else {
715 indent = lineIndent;
716 indent = lineIndent;
716 }
717 }
717 }
718 }
718 }
719 }
719 return indent;
720 return indent;
720 },
721 },
721 electricChars: "}",
722 electricChars: "}",
722 lineComment: "//",
723 lineComment: "//",
723 fold: "indent"
724 fold: "indent"
724 };
725 };
725 });
726 });
726
727
727 // developer.mozilla.org/en-US/docs/Web/HTML/Element
728 // developer.mozilla.org/en-US/docs/Web/HTML/Element
728 var tagKeywords_ = ["a","abbr","address","area","article","aside","audio", "b", "base","bdi", "bdo","bgsound","blockquote","body","br","button","canvas","caption","cite", "code","col","colgroup","data","datalist","dd","del","details","dfn","div", "dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1", "h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe", "img","input","ins","kbd","keygen","label","legend","li","link","main","map", "mark","marquee","menu","menuitem","meta","meter","nav","nobr","noframes", "noscript","object","ol","optgroup","option","output","p","param","pre", "progress","q","rp","rt","ruby","s","samp","script","section","select", "small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","track", "u","ul","var","video"];
729 var tagKeywords_ = ["a","abbr","address","area","article","aside","audio", "b", "base","bdi", "bdo","bgsound","blockquote","body","br","button","canvas","caption","cite", "code","col","colgroup","data","datalist","dd","del","details","dfn","div", "dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1", "h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe", "img","input","ins","kbd","keygen","label","legend","li","link","main","map", "mark","marquee","menu","menuitem","meta","meter","nav","nobr","noframes", "noscript","object","ol","optgroup","option","output","p","param","pre", "progress","q","rp","rt","ruby","s","samp","script","section","select", "small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","track", "u","ul","var","video"];
729
730
730 // github.com/codemirror/CodeMirror/blob/master/mode/css/css.js
731 // github.com/codemirror/CodeMirror/blob/master/mode/css/css.js
731 var documentTypes_ = ["domain", "regexp", "url", "url-prefix"];
732 var documentTypes_ = ["domain", "regexp", "url", "url-prefix"];
732 var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"];
733 var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"];
733 var mediaFeatures_ = ["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid"];
734 var mediaFeatures_ = ["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid"];
734 var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marker-offset","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"];
735 var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marker-offset","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"];
735 var nonStandardPropertyKeywords_ = ["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"];
736 var nonStandardPropertyKeywords_ = ["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"];
736 var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"];
737 var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"];
737 var colorKeywords_ = ["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"];
738 var colorKeywords_ = ["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"];
738 var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","footnotes","forwards","from","geometricPrecision","georgian","graytext","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","solid","somali","source-atop","source-in","source-out","source-over","space","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale"];
739 var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","footnotes","forwards","from","geometricPrecision","georgian","graytext","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","solid","somali","source-atop","source-in","source-out","source-over","space","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around"];
739
740
740 var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"],
741 var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"],
741 blockKeywords_ = ["for","if","else","unless", "from", "to"],
742 blockKeywords_ = ["for","if","else","unless", "from", "to"],
742 commonAtoms_ = ["null","true","false","href","title","type","not-allowed","readonly","disabled"],
743 commonAtoms_ = ["null","true","false","href","title","type","not-allowed","readonly","disabled"],
743 commonDef_ = ["@font-face", "@keyframes", "@media", "@viewport", "@page", "@host", "@supports", "@block", "@css"];
744 commonDef_ = ["@font-face", "@keyframes", "@media", "@viewport", "@page", "@host", "@supports", "@block", "@css"];
744
745
745 var hintWords = tagKeywords_.concat(documentTypes_,mediaTypes_,mediaFeatures_,
746 var hintWords = tagKeywords_.concat(documentTypes_,mediaTypes_,mediaFeatures_,
746 propertyKeywords_,nonStandardPropertyKeywords_,
747 propertyKeywords_,nonStandardPropertyKeywords_,
747 colorKeywords_,valueKeywords_,fontProperties_,
748 colorKeywords_,valueKeywords_,fontProperties_,
748 wordOperatorKeywords_,blockKeywords_,
749 wordOperatorKeywords_,blockKeywords_,
749 commonAtoms_,commonDef_);
750 commonAtoms_,commonDef_);
750
751
751 function wordRegexp(words) {
752 function wordRegexp(words) {
752 words = words.sort(function(a,b){return b > a;});
753 words = words.sort(function(a,b){return b > a;});
753 return new RegExp("^((" + words.join(")|(") + "))\\b");
754 return new RegExp("^((" + words.join(")|(") + "))\\b");
754 }
755 }
755
756
756 function keySet(array) {
757 function keySet(array) {
757 var keys = {};
758 var keys = {};
758 for (var i = 0; i < array.length; ++i) keys[array[i]] = true;
759 for (var i = 0; i < array.length; ++i) keys[array[i]] = true;
759 return keys;
760 return keys;
760 }
761 }
761
762
762 function escapeRegExp(text) {
763 function escapeRegExp(text) {
763 return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
764 return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
764 }
765 }
765
766
766 CodeMirror.registerHelper("hintWords", "stylus", hintWords);
767 CodeMirror.registerHelper("hintWords", "stylus", hintWords);
767 CodeMirror.defineMIME("text/x-styl", "stylus");
768 CodeMirror.defineMIME("text/x-styl", "stylus");
768 });
769 });
@@ -1,203 +1,202 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 // Swift mode created by Michael Kaminsky https://github.com/mkaminsky11
4 // Swift mode created by Michael Kaminsky https://github.com/mkaminsky11
5
5
6 (function(mod) {
6 (function(mod) {
7 if (typeof exports == "object" && typeof module == "object")
7 if (typeof exports == "object" && typeof module == "object")
8 mod(require("../../lib/codemirror"))
8 mod(require("../../lib/codemirror"))
9 else if (typeof define == "function" && define.amd)
9 else if (typeof define == "function" && define.amd)
10 define(["../../lib/codemirror"], mod)
10 define(["../../lib/codemirror"], mod)
11 else
11 else
12 mod(CodeMirror)
12 mod(CodeMirror)
13 })(function(CodeMirror) {
13 })(function(CodeMirror) {
14 "use strict"
14 "use strict"
15
15
16 function trim(str) { return /^\s*(.*?)\s*$/.exec(str)[1] }
16 function wordSet(words) {
17
17 var set = {}
18 var separators = [" ","\\\+","\\\-","\\\(","\\\)","\\\*","/",":","\\\?","\\\<","\\\>"," ","\\\."]
18 for (var i = 0; i < words.length; i++) set[words[i]] = true
19 var tokens = new RegExp(separators.join("|"),"g")
19 return set
20
21 function getWord(string, pos) {
22 var index = -1, count = 1
23 var words = string.split(tokens)
24 for (var i = 0; i < words.length; i++) {
25 for(var j = 1; j <= words[i].length; j++) {
26 if (count==pos) index = i
27 count++
28 }
29 count++
30 }
31 var ret = ["", ""]
32 if (pos == 0) {
33 ret[1] = words[0]
34 ret[0] = null
35 } else {
36 ret[1] = words[index]
37 ret[0] = words[index-1]
38 }
39 return ret
40 }
20 }
41
21
42 CodeMirror.defineMode("swift", function() {
22 var keywords = wordSet(["var","let","class","deinit","enum","extension","func","import","init","protocol",
43 var keywords=["var","let","class","deinit","enum","extension","func","import","init","let","protocol","static","struct","subscript","typealias","var","as","dynamicType","is","new","super","self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case","continue","default","do","else","fallthrough","if","in","for","return","switch","where","while","associativity","didSet","get","infix","inout","left","mutating","none","nonmutating","operator","override","postfix","precedence","prefix","right","set","unowned","unowned(safe)","unowned(unsafe)","weak","willSet"]
23 "static","struct","subscript","typealias","as","dynamicType","is","new","super",
44 var commonConstants=["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null","this","super"]
24 "self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case",
45 var types=["String","bool","int","string","double","Double","Int","Float","float","public","private","extension"]
25 "continue","default","do","else","fallthrough","if","in","for","return","switch",
46 var numbers=["0","1","2","3","4","5","6","7","8","9"]
26 "where","while","associativity","didSet","get","infix","inout","left","mutating",
47 var operators=["+","-","/","*","%","=","|","&","<",">"]
27 "none","nonmutating","operator","override","postfix","precedence","prefix","right",
48 var punc=[";",",",".","(",")","{","}","[","]"]
28 "set","unowned","weak","willSet"])
49 var delimiters=/^(?:[()\[\]{},:`=;]|\.\.?\.?)/
29 var definingKeywords = wordSet(["var","let","class","enum","extension","func","import","protocol","struct",
50 var identifiers=/^[_A-Za-z$][_A-Za-z$0-9]*/
30 "typealias","dynamicType","for"])
51 var properties=/^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/
31 var atoms = wordSet(["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null",
52 var regexPrefixes=/^(\/{3}|\/)/
32 "this","super"])
33 var types = wordSet(["String","bool","int","string","double","Double","Int","Float","float","public",
34 "private","extension"])
35 var operators = "+-/*%=|&<>#"
36 var punc = ";,.(){}[]"
37 var number = /^-?(?:(?:[\d_]+\.[_\d]*|\.[_\d]+|0o[0-7_\.]+|0b[01_\.]+)(?:e-?[\d_]+)?|0x[\d_a-f\.]+(?:p-?[\d_]+)?)/i
38 var identifier = /^[_A-Za-z$][_A-Za-z$0-9]*/
39 var property = /^[@\.][_A-Za-z$][_A-Za-z$0-9]*/
40 var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\//
41
42 function tokenBase(stream, state, prev) {
43 if (stream.sol()) state.indented = stream.indentation()
44 if (stream.eatSpace()) return null
45
46 var ch = stream.peek()
47 if (ch == "/") {
48 if (stream.match("//")) {
49 stream.skipToEnd()
50 return "comment"
51 }
52 if (stream.match("/*")) {
53 state.tokenize.push(tokenComment)
54 return tokenComment(stream, state)
55 }
56 if (stream.match(regexp)) return "string-2"
57 }
58 if (operators.indexOf(ch) > -1) {
59 stream.next()
60 return "operator"
61 }
62 if (punc.indexOf(ch) > -1) {
63 stream.next()
64 stream.match("..")
65 return "punctuation"
66 }
67 if (ch == '"' || ch == "'") {
68 stream.next()
69 var tokenize = tokenString(ch)
70 state.tokenize.push(tokenize)
71 return tokenize(stream, state)
72 }
73
74 if (stream.match(number)) return "number"
75 if (stream.match(property)) return "property"
76
77 if (stream.match(identifier)) {
78 var ident = stream.current()
79 if (keywords.hasOwnProperty(ident)) {
80 if (definingKeywords.hasOwnProperty(ident))
81 state.prev = "define"
82 return "keyword"
83 }
84 if (types.hasOwnProperty(ident)) return "variable-2"
85 if (atoms.hasOwnProperty(ident)) return "atom"
86 if (prev == "define") return "def"
87 return "variable"
88 }
53
89
90 stream.next()
91 return null
92 }
93
94 function tokenUntilClosingParen() {
95 var depth = 0
96 return function(stream, state, prev) {
97 var inner = tokenBase(stream, state, prev)
98 if (inner == "punctuation") {
99 if (stream.current() == "(") ++depth
100 else if (stream.current() == ")") {
101 if (depth == 0) {
102 stream.backUp(1)
103 state.tokenize.pop()
104 return state.tokenize[state.tokenize.length - 1](stream, state)
105 }
106 else --depth
107 }
108 }
109 return inner
110 }
111 }
112
113 function tokenString(quote) {
114 return function(stream, state) {
115 var ch, escaped = false
116 while (ch = stream.next()) {
117 if (escaped) {
118 if (ch == "(") {
119 state.tokenize.push(tokenUntilClosingParen())
120 return "string"
121 }
122 escaped = false
123 } else if (ch == quote) {
124 break
125 } else {
126 escaped = ch == "\\"
127 }
128 }
129 state.tokenize.pop()
130 return "string"
131 }
132 }
133
134 function tokenComment(stream, state) {
135 stream.match(/^(?:[^*]|\*(?!\/))*/)
136 if (stream.match("*/")) state.tokenize.pop()
137 return "comment"
138 }
139
140 function Context(prev, align, indented) {
141 this.prev = prev
142 this.align = align
143 this.indented = indented
144 }
145
146 function pushContext(state, stream) {
147 var align = stream.match(/^\s*($|\/[\/\*])/, false) ? null : stream.column() + 1
148 state.context = new Context(state.context, align, state.indented)
149 }
150
151 function popContext(state) {
152 if (state.context) {
153 state.indented = state.context.indented
154 state.context = state.context.prev
155 }
156 }
157
158 CodeMirror.defineMode("swift", function(config) {
54 return {
159 return {
55 startState: function() {
160 startState: function() {
56 return {
161 return {
57 prev: false,
162 prev: null,
58 string: false,
163 context: null,
59 escape: false,
164 indented: 0,
60 inner: false,
165 tokenize: []
61 comment: false,
62 num_left: 0,
63 num_right: 0,
64 doubleString: false,
65 singleString: false
66 }
166 }
67 },
167 },
68 token: function(stream, state) {
69 if (stream.eatSpace()) return null
70
168
71 var ch = stream.next()
169 token: function(stream, state) {
72 if (state.string) {
170 var prev = state.prev
73 if (state.escape) {
171 state.prev = null
74 state.escape = false
172 var tokenize = state.tokenize[state.tokenize.length - 1] || tokenBase
75 return "string"
173 var style = tokenize(stream, state, prev)
76 } else {
174 if (!style || style == "comment") state.prev = prev
77 if ((ch == "\"" && (state.doubleString && !state.singleString) ||
175 else if (!state.prev) state.prev = style
78 (ch == "'" && (!state.doubleString && state.singleString))) &&
79 !state.escape) {
80 state.string = false
81 state.doubleString = false
82 state.singleString = false
83 return "string"
84 } else if (ch == "\\" && stream.peek() == "(") {
85 state.inner = true
86 state.string = false
87 return "keyword"
88 } else if (ch == "\\" && stream.peek() != "(") {
89 state.escape = true
90 state.string = true
91 return "string"
92 } else {
93 return "string"
94 }
95 }
96 } else if (state.comment) {
97 if (ch == "*" && stream.peek() == "/") {
98 state.prev = "*"
99 return "comment"
100 } else if (ch == "/" && state.prev == "*") {
101 state.prev = false
102 state.comment = false
103 return "comment"
104 }
105 return "comment"
106 } else {
107 if (ch == "/") {
108 if (stream.peek() == "/") {
109 stream.skipToEnd()
110 return "comment"
111 }
112 if (stream.peek() == "*") {
113 state.comment = true
114 return "comment"
115 }
116 }
117 if (ch == "(" && state.inner) {
118 state.num_left++
119 return null
120 }
121 if (ch == ")" && state.inner) {
122 state.num_right++
123 if (state.num_left == state.num_right) {
124 state.inner=false
125 state.string=true
126 }
127 return null
128 }
129
176
130 var ret = getWord(stream.string, stream.pos)
177 if (style == "punctuation") {
131 var the_word = ret[1]
178 var bracket = /[\(\[\{]|([\]\)\}])/.exec(stream.current())
132 var prev_word = ret[0]
179 if (bracket) (bracket[1] ? popContext : pushContext)(state, stream)
180 }
133
181
134 if (operators.indexOf(ch + "") > -1) return "operator"
182 return style
135 if (punc.indexOf(ch) > -1) return "punctuation"
183 },
136
137 if (typeof the_word != "undefined") {
138 the_word = trim(the_word)
139 if (typeof prev_word != "undefined") prev_word = trim(prev_word)
140 if (the_word.charAt(0) == "#") return null
141
142 if (types.indexOf(the_word) > -1) return "def"
143 if (commonConstants.indexOf(the_word) > -1) return "atom"
144 if (numbers.indexOf(the_word) > -1) return "number"
145
146 if ((numbers.indexOf(the_word.charAt(0) + "") > -1 ||
147 operators.indexOf(the_word.charAt(0) + "") > -1) &&
148 numbers.indexOf(ch) > -1) {
149 return "number"
150 }
151
184
152 if (keywords.indexOf(the_word) > -1 ||
185 indent: function(state, textAfter) {
153 keywords.indexOf(the_word.split(tokens)[0]) > -1)
186 var cx = state.context
154 return "keyword"
187 if (!cx) return 0
155 if (keywords.indexOf(prev_word) > -1) return "def"
188 var closing = /^[\]\}\)]/.test(textAfter)
156 }
189 if (cx.align != null) return cx.align - (closing ? 1 : 0)
157 if (ch == '"' && !state.doubleString) {
190 return cx.indented + (closing ? 0 : config.indentUnit)
158 state.string = true
191 },
159 state.doubleString = true
192
160 return "string"
193 electricInput: /^\s*[\)\}\]]$/,
161 }
194
162 if (ch == "'" && !state.singleString) {
195 lineComment: "//",
163 state.string = true
196 blockCommentStart: "/*",
164 state.singleString = true
197 blockCommentEnd: "*/"
165 return "string"
166 }
167 if (ch == "(" && state.inner)
168 state.num_left++
169 if (ch == ")" && state.inner) {
170 state.num_right++
171 if (state.num_left == state.num_right) {
172 state.inner = false
173 state.string = true
174 }
175 return null
176 }
177 if (stream.match(/^-?[0-9\.]/, false)) {
178 if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i) ||
179 stream.match(/^-?\d+\.\d*/) ||
180 stream.match(/^-?\.\d+/)) {
181 if (stream.peek() == ".") stream.backUp(1)
182 return "number"
183 }
184 if (stream.match(/^-?0x[0-9a-f]+/i) ||
185 stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/) ||
186 stream.match(/^-?0(?![\dx])/i))
187 return "number"
188 }
189 if (stream.match(regexPrefixes)) {
190 if (stream.current()!="/" || stream.match(/^.*\//,false)) return "string"
191 else stream.backUp(1)
192 }
193 if (stream.match(delimiters)) return "punctuation"
194 if (stream.match(identifiers)) return "variable"
195 if (stream.match(properties)) return "property"
196 return "variable"
197 }
198 }
199 }
198 }
200 })
199 })
201
200
202 CodeMirror.defineMIME("text/x-swift","swift")
201 CodeMirror.defineMIME("text/x-swift","swift")
203 })
202 })
@@ -1,384 +1,394 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 CodeMirror.defineMode("xml", function(config, parserConfig) {
14 var htmlConfig = {
15 var indentUnit = config.indentUnit;
15 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
16 var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
16 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
17 var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
17 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
18 if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
18 'track': true, 'wbr': true, 'menuitem': true},
19 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
20 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
21 'th': true, 'tr': true},
22 contextGrabbers: {
23 'dd': {'dd': true, 'dt': true},
24 'dt': {'dd': true, 'dt': true},
25 'li': {'li': true},
26 'option': {'option': true, 'optgroup': true},
27 'optgroup': {'optgroup': true},
28 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
29 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
30 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
31 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
32 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
33 'rp': {'rp': true, 'rt': true},
34 'rt': {'rp': true, 'rt': true},
35 'tbody': {'tbody': true, 'tfoot': true},
36 'td': {'td': true, 'th': true},
37 'tfoot': {'tbody': true},
38 'th': {'td': true, 'th': true},
39 'thead': {'tbody': true, 'tfoot': true},
40 'tr': {'tr': true}
41 },
42 doNotIndent: {"pre": true},
43 allowUnquoted: true,
44 allowMissing: true,
45 caseFold: true
46 }
19
47
20 var Kludges = parserConfig.htmlMode ? {
48 var xmlConfig = {
21 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
49 autoSelfClosers: {},
22 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
50 implicitlyClosed: {},
23 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
51 contextGrabbers: {},
24 'track': true, 'wbr': true, 'menuitem': true},
52 doNotIndent: {},
25 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
53 allowUnquoted: false,
26 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
54 allowMissing: false,
27 'th': true, 'tr': true},
55 caseFold: false
28 contextGrabbers: {
56 }
29 'dd': {'dd': true, 'dt': true},
57
30 'dt': {'dd': true, 'dt': true},
58 CodeMirror.defineMode("xml", function(editorConf, config_) {
31 'li': {'li': true},
59 var indentUnit = editorConf.indentUnit
32 'option': {'option': true, 'optgroup': true},
60 var config = {}
33 'optgroup': {'optgroup': true},
61 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
34 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
62 for (var prop in defaults) config[prop] = defaults[prop]
35 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
63 for (var prop in config_) config[prop] = config_[prop]
36 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
37 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
38 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
39 'rp': {'rp': true, 'rt': true},
40 'rt': {'rp': true, 'rt': true},
41 'tbody': {'tbody': true, 'tfoot': true},
42 'td': {'td': true, 'th': true},
43 'tfoot': {'tbody': true},
44 'th': {'td': true, 'th': true},
45 'thead': {'tbody': true, 'tfoot': true},
46 'tr': {'tr': true}
47 },
48 doNotIndent: {"pre": true},
49 allowUnquoted: true,
50 allowMissing: true,
51 caseFold: true
52 } : {
53 autoSelfClosers: {},
54 implicitlyClosed: {},
55 contextGrabbers: {},
56 doNotIndent: {},
57 allowUnquoted: false,
58 allowMissing: false,
59 caseFold: false
60 };
61 var alignCDATA = parserConfig.alignCDATA;
62
64
63 // Return variables for tokenizers
65 // Return variables for tokenizers
64 var type, setStyle;
66 var type, setStyle;
65
67
66 function inText(stream, state) {
68 function inText(stream, state) {
67 function chain(parser) {
69 function chain(parser) {
68 state.tokenize = parser;
70 state.tokenize = parser;
69 return parser(stream, state);
71 return parser(stream, state);
70 }
72 }
71
73
72 var ch = stream.next();
74 var ch = stream.next();
73 if (ch == "<") {
75 if (ch == "<") {
74 if (stream.eat("!")) {
76 if (stream.eat("!")) {
75 if (stream.eat("[")) {
77 if (stream.eat("[")) {
76 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
78 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
77 else return null;
79 else return null;
78 } else if (stream.match("--")) {
80 } else if (stream.match("--")) {
79 return chain(inBlock("comment", "-->"));
81 return chain(inBlock("comment", "-->"));
80 } else if (stream.match("DOCTYPE", true, true)) {
82 } else if (stream.match("DOCTYPE", true, true)) {
81 stream.eatWhile(/[\w\._\-]/);
83 stream.eatWhile(/[\w\._\-]/);
82 return chain(doctype(1));
84 return chain(doctype(1));
83 } else {
85 } else {
84 return null;
86 return null;
85 }
87 }
86 } else if (stream.eat("?")) {
88 } else if (stream.eat("?")) {
87 stream.eatWhile(/[\w\._\-]/);
89 stream.eatWhile(/[\w\._\-]/);
88 state.tokenize = inBlock("meta", "?>");
90 state.tokenize = inBlock("meta", "?>");
89 return "meta";
91 return "meta";
90 } else {
92 } else {
91 type = stream.eat("/") ? "closeTag" : "openTag";
93 type = stream.eat("/") ? "closeTag" : "openTag";
92 state.tokenize = inTag;
94 state.tokenize = inTag;
93 return "tag bracket";
95 return "tag bracket";
94 }
96 }
95 } else if (ch == "&") {
97 } else if (ch == "&") {
96 var ok;
98 var ok;
97 if (stream.eat("#")) {
99 if (stream.eat("#")) {
98 if (stream.eat("x")) {
100 if (stream.eat("x")) {
99 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
101 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
100 } else {
102 } else {
101 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
103 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
102 }
104 }
103 } else {
105 } else {
104 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
106 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
105 }
107 }
106 return ok ? "atom" : "error";
108 return ok ? "atom" : "error";
107 } else {
109 } else {
108 stream.eatWhile(/[^&<]/);
110 stream.eatWhile(/[^&<]/);
109 return null;
111 return null;
110 }
112 }
111 }
113 }
114 inText.isInText = true;
112
115
113 function inTag(stream, state) {
116 function inTag(stream, state) {
114 var ch = stream.next();
117 var ch = stream.next();
115 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
118 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
116 state.tokenize = inText;
119 state.tokenize = inText;
117 type = ch == ">" ? "endTag" : "selfcloseTag";
120 type = ch == ">" ? "endTag" : "selfcloseTag";
118 return "tag bracket";
121 return "tag bracket";
119 } else if (ch == "=") {
122 } else if (ch == "=") {
120 type = "equals";
123 type = "equals";
121 return null;
124 return null;
122 } else if (ch == "<") {
125 } else if (ch == "<") {
123 state.tokenize = inText;
126 state.tokenize = inText;
124 state.state = baseState;
127 state.state = baseState;
125 state.tagName = state.tagStart = null;
128 state.tagName = state.tagStart = null;
126 var next = state.tokenize(stream, state);
129 var next = state.tokenize(stream, state);
127 return next ? next + " tag error" : "tag error";
130 return next ? next + " tag error" : "tag error";
128 } else if (/[\'\"]/.test(ch)) {
131 } else if (/[\'\"]/.test(ch)) {
129 state.tokenize = inAttribute(ch);
132 state.tokenize = inAttribute(ch);
130 state.stringStartCol = stream.column();
133 state.stringStartCol = stream.column();
131 return state.tokenize(stream, state);
134 return state.tokenize(stream, state);
132 } else {
135 } else {
133 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
136 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
134 return "word";
137 return "word";
135 }
138 }
136 }
139 }
137
140
138 function inAttribute(quote) {
141 function inAttribute(quote) {
139 var closure = function(stream, state) {
142 var closure = function(stream, state) {
140 while (!stream.eol()) {
143 while (!stream.eol()) {
141 if (stream.next() == quote) {
144 if (stream.next() == quote) {
142 state.tokenize = inTag;
145 state.tokenize = inTag;
143 break;
146 break;
144 }
147 }
145 }
148 }
146 return "string";
149 return "string";
147 };
150 };
148 closure.isInAttribute = true;
151 closure.isInAttribute = true;
149 return closure;
152 return closure;
150 }
153 }
151
154
152 function inBlock(style, terminator) {
155 function inBlock(style, terminator) {
153 return function(stream, state) {
156 return function(stream, state) {
154 while (!stream.eol()) {
157 while (!stream.eol()) {
155 if (stream.match(terminator)) {
158 if (stream.match(terminator)) {
156 state.tokenize = inText;
159 state.tokenize = inText;
157 break;
160 break;
158 }
161 }
159 stream.next();
162 stream.next();
160 }
163 }
161 return style;
164 return style;
162 };
165 };
163 }
166 }
164 function doctype(depth) {
167 function doctype(depth) {
165 return function(stream, state) {
168 return function(stream, state) {
166 var ch;
169 var ch;
167 while ((ch = stream.next()) != null) {
170 while ((ch = stream.next()) != null) {
168 if (ch == "<") {
171 if (ch == "<") {
169 state.tokenize = doctype(depth + 1);
172 state.tokenize = doctype(depth + 1);
170 return state.tokenize(stream, state);
173 return state.tokenize(stream, state);
171 } else if (ch == ">") {
174 } else if (ch == ">") {
172 if (depth == 1) {
175 if (depth == 1) {
173 state.tokenize = inText;
176 state.tokenize = inText;
174 break;
177 break;
175 } else {
178 } else {
176 state.tokenize = doctype(depth - 1);
179 state.tokenize = doctype(depth - 1);
177 return state.tokenize(stream, state);
180 return state.tokenize(stream, state);
178 }
181 }
179 }
182 }
180 }
183 }
181 return "meta";
184 return "meta";
182 };
185 };
183 }
186 }
184
187
185 function Context(state, tagName, startOfLine) {
188 function Context(state, tagName, startOfLine) {
186 this.prev = state.context;
189 this.prev = state.context;
187 this.tagName = tagName;
190 this.tagName = tagName;
188 this.indent = state.indented;
191 this.indent = state.indented;
189 this.startOfLine = startOfLine;
192 this.startOfLine = startOfLine;
190 if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
193 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
191 this.noIndent = true;
194 this.noIndent = true;
192 }
195 }
193 function popContext(state) {
196 function popContext(state) {
194 if (state.context) state.context = state.context.prev;
197 if (state.context) state.context = state.context.prev;
195 }
198 }
196 function maybePopContext(state, nextTagName) {
199 function maybePopContext(state, nextTagName) {
197 var parentTagName;
200 var parentTagName;
198 while (true) {
201 while (true) {
199 if (!state.context) {
202 if (!state.context) {
200 return;
203 return;
201 }
204 }
202 parentTagName = state.context.tagName;
205 parentTagName = state.context.tagName;
203 if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
206 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
204 !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
207 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
205 return;
208 return;
206 }
209 }
207 popContext(state);
210 popContext(state);
208 }
211 }
209 }
212 }
210
213
211 function baseState(type, stream, state) {
214 function baseState(type, stream, state) {
212 if (type == "openTag") {
215 if (type == "openTag") {
213 state.tagStart = stream.column();
216 state.tagStart = stream.column();
214 return tagNameState;
217 return tagNameState;
215 } else if (type == "closeTag") {
218 } else if (type == "closeTag") {
216 return closeTagNameState;
219 return closeTagNameState;
217 } else {
220 } else {
218 return baseState;
221 return baseState;
219 }
222 }
220 }
223 }
221 function tagNameState(type, stream, state) {
224 function tagNameState(type, stream, state) {
222 if (type == "word") {
225 if (type == "word") {
223 state.tagName = stream.current();
226 state.tagName = stream.current();
224 setStyle = "tag";
227 setStyle = "tag";
225 return attrState;
228 return attrState;
226 } else {
229 } else {
227 setStyle = "error";
230 setStyle = "error";
228 return tagNameState;
231 return tagNameState;
229 }
232 }
230 }
233 }
231 function closeTagNameState(type, stream, state) {
234 function closeTagNameState(type, stream, state) {
232 if (type == "word") {
235 if (type == "word") {
233 var tagName = stream.current();
236 var tagName = stream.current();
234 if (state.context && state.context.tagName != tagName &&
237 if (state.context && state.context.tagName != tagName &&
235 Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
238 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
236 popContext(state);
239 popContext(state);
237 if (state.context && state.context.tagName == tagName) {
240 if (state.context && state.context.tagName == tagName) {
238 setStyle = "tag";
241 setStyle = "tag";
239 return closeState;
242 return closeState;
240 } else {
243 } else {
241 setStyle = "tag error";
244 setStyle = "tag error";
242 return closeStateErr;
245 return closeStateErr;
243 }
246 }
244 } else {
247 } else {
245 setStyle = "error";
248 setStyle = "error";
246 return closeStateErr;
249 return closeStateErr;
247 }
250 }
248 }
251 }
249
252
250 function closeState(type, _stream, state) {
253 function closeState(type, _stream, state) {
251 if (type != "endTag") {
254 if (type != "endTag") {
252 setStyle = "error";
255 setStyle = "error";
253 return closeState;
256 return closeState;
254 }
257 }
255 popContext(state);
258 popContext(state);
256 return baseState;
259 return baseState;
257 }
260 }
258 function closeStateErr(type, stream, state) {
261 function closeStateErr(type, stream, state) {
259 setStyle = "error";
262 setStyle = "error";
260 return closeState(type, stream, state);
263 return closeState(type, stream, state);
261 }
264 }
262
265
263 function attrState(type, _stream, state) {
266 function attrState(type, _stream, state) {
264 if (type == "word") {
267 if (type == "word") {
265 setStyle = "attribute";
268 setStyle = "attribute";
266 return attrEqState;
269 return attrEqState;
267 } else if (type == "endTag" || type == "selfcloseTag") {
270 } else if (type == "endTag" || type == "selfcloseTag") {
268 var tagName = state.tagName, tagStart = state.tagStart;
271 var tagName = state.tagName, tagStart = state.tagStart;
269 state.tagName = state.tagStart = null;
272 state.tagName = state.tagStart = null;
270 if (type == "selfcloseTag" ||
273 if (type == "selfcloseTag" ||
271 Kludges.autoSelfClosers.hasOwnProperty(tagName)) {
274 config.autoSelfClosers.hasOwnProperty(tagName)) {
272 maybePopContext(state, tagName);
275 maybePopContext(state, tagName);
273 } else {
276 } else {
274 maybePopContext(state, tagName);
277 maybePopContext(state, tagName);
275 state.context = new Context(state, tagName, tagStart == state.indented);
278 state.context = new Context(state, tagName, tagStart == state.indented);
276 }
279 }
277 return baseState;
280 return baseState;
278 }
281 }
279 setStyle = "error";
282 setStyle = "error";
280 return attrState;
283 return attrState;
281 }
284 }
282 function attrEqState(type, stream, state) {
285 function attrEqState(type, stream, state) {
283 if (type == "equals") return attrValueState;
286 if (type == "equals") return attrValueState;
284 if (!Kludges.allowMissing) setStyle = "error";
287 if (!config.allowMissing) setStyle = "error";
285 return attrState(type, stream, state);
288 return attrState(type, stream, state);
286 }
289 }
287 function attrValueState(type, stream, state) {
290 function attrValueState(type, stream, state) {
288 if (type == "string") return attrContinuedState;
291 if (type == "string") return attrContinuedState;
289 if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
292 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
290 setStyle = "error";
293 setStyle = "error";
291 return attrState(type, stream, state);
294 return attrState(type, stream, state);
292 }
295 }
293 function attrContinuedState(type, stream, state) {
296 function attrContinuedState(type, stream, state) {
294 if (type == "string") return attrContinuedState;
297 if (type == "string") return attrContinuedState;
295 return attrState(type, stream, state);
298 return attrState(type, stream, state);
296 }
299 }
297
300
298 return {
301 return {
299 startState: function() {
302 startState: function(baseIndent) {
300 return {tokenize: inText,
303 var state = {tokenize: inText,
301 state: baseState,
304 state: baseState,
302 indented: 0,
305 indented: baseIndent || 0,
303 tagName: null, tagStart: null,
306 tagName: null, tagStart: null,
304 context: null};
307 context: null}
308 if (baseIndent != null) state.baseIndent = baseIndent
309 return state
305 },
310 },
306
311
307 token: function(stream, state) {
312 token: function(stream, state) {
308 if (!state.tagName && stream.sol())
313 if (!state.tagName && stream.sol())
309 state.indented = stream.indentation();
314 state.indented = stream.indentation();
310
315
311 if (stream.eatSpace()) return null;
316 if (stream.eatSpace()) return null;
312 type = null;
317 type = null;
313 var style = state.tokenize(stream, state);
318 var style = state.tokenize(stream, state);
314 if ((style || type) && style != "comment") {
319 if ((style || type) && style != "comment") {
315 setStyle = null;
320 setStyle = null;
316 state.state = state.state(type || style, stream, state);
321 state.state = state.state(type || style, stream, state);
317 if (setStyle)
322 if (setStyle)
318 style = setStyle == "error" ? style + " error" : setStyle;
323 style = setStyle == "error" ? style + " error" : setStyle;
319 }
324 }
320 return style;
325 return style;
321 },
326 },
322
327
323 indent: function(state, textAfter, fullLine) {
328 indent: function(state, textAfter, fullLine) {
324 var context = state.context;
329 var context = state.context;
325 // Indent multi-line strings (e.g. css).
330 // Indent multi-line strings (e.g. css).
326 if (state.tokenize.isInAttribute) {
331 if (state.tokenize.isInAttribute) {
327 if (state.tagStart == state.indented)
332 if (state.tagStart == state.indented)
328 return state.stringStartCol + 1;
333 return state.stringStartCol + 1;
329 else
334 else
330 return state.indented + indentUnit;
335 return state.indented + indentUnit;
331 }
336 }
332 if (context && context.noIndent) return CodeMirror.Pass;
337 if (context && context.noIndent) return CodeMirror.Pass;
333 if (state.tokenize != inTag && state.tokenize != inText)
338 if (state.tokenize != inTag && state.tokenize != inText)
334 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
339 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
335 // Indent the starts of attribute names.
340 // Indent the starts of attribute names.
336 if (state.tagName) {
341 if (state.tagName) {
337 if (multilineTagIndentPastTag)
342 if (config.multilineTagIndentPastTag !== false)
338 return state.tagStart + state.tagName.length + 2;
343 return state.tagStart + state.tagName.length + 2;
339 else
344 else
340 return state.tagStart + indentUnit * multilineTagIndentFactor;
345 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
341 }
346 }
342 if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
347 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
343 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
348 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
344 if (tagAfter && tagAfter[1]) { // Closing tag spotted
349 if (tagAfter && tagAfter[1]) { // Closing tag spotted
345 while (context) {
350 while (context) {
346 if (context.tagName == tagAfter[2]) {
351 if (context.tagName == tagAfter[2]) {
347 context = context.prev;
352 context = context.prev;
348 break;
353 break;
349 } else if (Kludges.implicitlyClosed.hasOwnProperty(context.tagName)) {
354 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
350 context = context.prev;
355 context = context.prev;
351 } else {
356 } else {
352 break;
357 break;
353 }
358 }
354 }
359 }
355 } else if (tagAfter) { // Opening tag spotted
360 } else if (tagAfter) { // Opening tag spotted
356 while (context) {
361 while (context) {
357 var grabbers = Kludges.contextGrabbers[context.tagName];
362 var grabbers = config.contextGrabbers[context.tagName];
358 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
363 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
359 context = context.prev;
364 context = context.prev;
360 else
365 else
361 break;
366 break;
362 }
367 }
363 }
368 }
364 while (context && !context.startOfLine)
369 while (context && context.prev && !context.startOfLine)
365 context = context.prev;
370 context = context.prev;
366 if (context) return context.indent + indentUnit;
371 if (context) return context.indent + indentUnit;
367 else return 0;
372 else return state.baseIndent || 0;
368 },
373 },
369
374
370 electricInput: /<\/[\s\w:]+>$/,
375 electricInput: /<\/[\s\w:]+>$/,
371 blockCommentStart: "<!--",
376 blockCommentStart: "<!--",
372 blockCommentEnd: "-->",
377 blockCommentEnd: "-->",
373
378
374 configuration: parserConfig.htmlMode ? "html" : "xml",
379 configuration: config.htmlMode ? "html" : "xml",
375 helperType: parserConfig.htmlMode ? "html" : "xml"
380 helperType: config.htmlMode ? "html" : "xml",
381
382 skipAttribute: function(state) {
383 if (state.state == attrValueState)
384 state.state = attrState
385 }
376 };
386 };
377 });
387 });
378
388
379 CodeMirror.defineMIME("text/xml", "xml");
389 CodeMirror.defineMIME("text/xml", "xml");
380 CodeMirror.defineMIME("application/xml", "xml");
390 CodeMirror.defineMIME("application/xml", "xml");
381 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
391 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
382 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
392 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
383
393
384 });
394 });
This diff has been collapsed as it changes many lines, (573 lines changed) Show them Hide them
@@ -1,8735 +1,8890 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 // This is CodeMirror (http://codemirror.net), a code editor
4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM.
5 // implemented in JavaScript on top of the browser's DOM.
6 //
6 //
7 // You can find some technical background for some of the code below
7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
9
10 (function(mod) {
10 (function(mod) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS
11 if (typeof exports == "object" && typeof module == "object") // CommonJS
12 module.exports = mod();
12 module.exports = mod();
13 else if (typeof define == "function" && define.amd) // AMD
13 else if (typeof define == "function" && define.amd) // AMD
14 return define([], mod);
14 return define([], mod);
15 else // Plain browser env
15 else // Plain browser env
16 this.CodeMirror = mod();
16 (this || window).CodeMirror = mod();
17 })(function() {
17 })(function() {
18 "use strict";
18 "use strict";
19
19
20 // BROWSER SNIFFING
20 // BROWSER SNIFFING
21
21
22 // Kludges for bugs and behavior differences that can't be feature
22 // Kludges for bugs and behavior differences that can't be feature
23 // detected are enabled based on userAgent etc sniffing.
23 // detected are enabled based on userAgent etc sniffing.
24
24 var userAgent = navigator.userAgent;
25 var gecko = /gecko\/\d/i.test(navigator.userAgent);
25 var platform = navigator.platform;
26 var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
26
27 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
27 var gecko = /gecko\/\d/i.test(userAgent);
28 var ie_upto10 = /MSIE \d/.test(userAgent);
29 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
28 var ie = ie_upto10 || ie_11up;
30 var ie = ie_upto10 || ie_11up;
29 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
31 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
30 var webkit = /WebKit\//.test(navigator.userAgent);
32 var webkit = /WebKit\//.test(userAgent);
31 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
33 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
32 var chrome = /Chrome\//.test(navigator.userAgent);
34 var chrome = /Chrome\//.test(userAgent);
33 var presto = /Opera\//.test(navigator.userAgent);
35 var presto = /Opera\//.test(userAgent);
34 var safari = /Apple Computer/.test(navigator.vendor);
36 var safari = /Apple Computer/.test(navigator.vendor);
35 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(userAgent);
36 var phantom = /PhantomJS/.test(navigator.userAgent);
38 var phantom = /PhantomJS/.test(userAgent);
37
39
38 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
40 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
39 // This is woefully incomplete. Suggestions for alternative methods welcome.
41 // This is woefully incomplete. Suggestions for alternative methods welcome.
40 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(userAgent);
41 var mac = ios || /Mac/.test(navigator.platform);
43 var mac = ios || /Mac/.test(platform);
42 var windows = /win/i.test(navigator.platform);
44 var windows = /win/i.test(platform);
43
45
44 var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
46 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
45 if (presto_version) presto_version = Number(presto_version[1]);
47 if (presto_version) presto_version = Number(presto_version[1]);
46 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
48 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
47 // 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
48 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
50 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
49 var captureRightClick = gecko || (ie && ie_version >= 9);
51 var captureRightClick = gecko || (ie && ie_version >= 9);
50
52
51 // Optimize some code when these features are not used.
53 // Optimize some code when these features are not used.
52 var sawReadOnlySpans = false, sawCollapsedSpans = false;
54 var sawReadOnlySpans = false, sawCollapsedSpans = false;
53
55
54 // EDITOR CONSTRUCTOR
56 // EDITOR CONSTRUCTOR
55
57
56 // A CodeMirror instance represents an editor. This is the object
58 // A CodeMirror instance represents an editor. This is the object
57 // that user code is usually dealing with.
59 // that user code is usually dealing with.
58
60
59 function CodeMirror(place, options) {
61 function CodeMirror(place, options) {
60 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
62 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
61
63
62 this.options = options = options ? copyObj(options) : {};
64 this.options = options = options ? copyObj(options) : {};
63 // Determine effective options based on given values and defaults.
65 // Determine effective options based on given values and defaults.
64 copyObj(defaults, options, false);
66 copyObj(defaults, options, false);
65 setGuttersForLineNumbers(options);
67 setGuttersForLineNumbers(options);
66
68
67 var doc = options.value;
69 var doc = options.value;
68 if (typeof doc == "string") doc = new Doc(doc, options.mode);
70 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
69 this.doc = doc;
71 this.doc = doc;
70
72
71 var input = new CodeMirror.inputStyles[options.inputStyle](this);
73 var input = new CodeMirror.inputStyles[options.inputStyle](this);
72 var display = this.display = new Display(place, doc, input);
74 var display = this.display = new Display(place, doc, input);
73 display.wrapper.CodeMirror = this;
75 display.wrapper.CodeMirror = this;
74 updateGutters(this);
76 updateGutters(this);
75 themeChanged(this);
77 themeChanged(this);
76 if (options.lineWrapping)
78 if (options.lineWrapping)
77 this.display.wrapper.className += " CodeMirror-wrap";
79 this.display.wrapper.className += " CodeMirror-wrap";
78 if (options.autofocus && !mobile) display.input.focus();
80 if (options.autofocus && !mobile) display.input.focus();
79 initScrollbars(this);
81 initScrollbars(this);
80
82
81 this.state = {
83 this.state = {
82 keyMaps: [], // stores maps added by addKeyMap
84 keyMaps: [], // stores maps added by addKeyMap
83 overlays: [], // highlighting overlays, as added by addOverlay
85 overlays: [], // highlighting overlays, as added by addOverlay
84 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
86 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
85 overwrite: false,
87 overwrite: false,
86 delayingBlurEvent: false,
88 delayingBlurEvent: false,
87 focused: false,
89 focused: false,
88 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
90 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
89 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
91 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
92 selectingText: false,
90 draggingText: false,
93 draggingText: false,
91 highlight: new Delayed(), // stores highlight worker timeout
94 highlight: new Delayed(), // stores highlight worker timeout
92 keySeq: null, // Unfinished key sequence
95 keySeq: null, // Unfinished key sequence
93 specialChars: null
96 specialChars: null
94 };
97 };
95
98
96 var cm = this;
99 var cm = this;
97
100
98 // Override magic textarea content restore that IE sometimes does
101 // Override magic textarea content restore that IE sometimes does
99 // on our hidden textarea on reload
102 // on our hidden textarea on reload
100 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
103 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
101
104
102 registerEventHandlers(this);
105 registerEventHandlers(this);
103 ensureGlobalHandlers();
106 ensureGlobalHandlers();
104
107
105 startOperation(this);
108 startOperation(this);
106 this.curOp.forceUpdate = true;
109 this.curOp.forceUpdate = true;
107 attachDoc(this, doc);
110 attachDoc(this, doc);
108
111
109 if ((options.autofocus && !mobile) || cm.hasFocus())
112 if ((options.autofocus && !mobile) || cm.hasFocus())
110 setTimeout(bind(onFocus, this), 20);
113 setTimeout(bind(onFocus, this), 20);
111 else
114 else
112 onBlur(this);
115 onBlur(this);
113
116
114 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
117 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
115 optionHandlers[opt](this, options[opt], Init);
118 optionHandlers[opt](this, options[opt], Init);
116 maybeUpdateLineNumberWidth(this);
119 maybeUpdateLineNumberWidth(this);
117 if (options.finishInit) options.finishInit(this);
120 if (options.finishInit) options.finishInit(this);
118 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
121 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
119 endOperation(this);
122 endOperation(this);
120 // Suppress optimizelegibility in Webkit, since it breaks text
123 // Suppress optimizelegibility in Webkit, since it breaks text
121 // measuring on line wrapping boundaries.
124 // measuring on line wrapping boundaries.
122 if (webkit && options.lineWrapping &&
125 if (webkit && options.lineWrapping &&
123 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
126 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
124 display.lineDiv.style.textRendering = "auto";
127 display.lineDiv.style.textRendering = "auto";
125 }
128 }
126
129
127 // DISPLAY CONSTRUCTOR
130 // DISPLAY CONSTRUCTOR
128
131
129 // The display handles the DOM integration, both for input reading
132 // The display handles the DOM integration, both for input reading
130 // and content drawing. It holds references to DOM nodes and
133 // and content drawing. It holds references to DOM nodes and
131 // display-related state.
134 // display-related state.
132
135
133 function Display(place, doc, input) {
136 function Display(place, doc, input) {
134 var d = this;
137 var d = this;
135 this.input = input;
138 this.input = input;
136
139
137 // Covers bottom-right square when both scrollbars are present.
140 // Covers bottom-right square when both scrollbars are present.
138 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
141 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
139 d.scrollbarFiller.setAttribute("cm-not-content", "true");
142 d.scrollbarFiller.setAttribute("cm-not-content", "true");
140 // Covers bottom of gutter when coverGutterNextToScrollbar is on
143 // Covers bottom of gutter when coverGutterNextToScrollbar is on
141 // and h scrollbar is present.
144 // and h scrollbar is present.
142 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
145 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
143 d.gutterFiller.setAttribute("cm-not-content", "true");
146 d.gutterFiller.setAttribute("cm-not-content", "true");
144 // Will contain the actual code, positioned to cover the viewport.
147 // Will contain the actual code, positioned to cover the viewport.
145 d.lineDiv = elt("div", null, "CodeMirror-code");
148 d.lineDiv = elt("div", null, "CodeMirror-code");
146 // Elements are added to these to represent selection and cursors.
149 // Elements are added to these to represent selection and cursors.
147 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
150 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
148 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
151 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
149 // A visibility: hidden element used to find the size of things.
152 // A visibility: hidden element used to find the size of things.
150 d.measure = elt("div", null, "CodeMirror-measure");
153 d.measure = elt("div", null, "CodeMirror-measure");
151 // When lines outside of the viewport are measured, they are drawn in this.
154 // When lines outside of the viewport are measured, they are drawn in this.
152 d.lineMeasure = elt("div", null, "CodeMirror-measure");
155 d.lineMeasure = elt("div", null, "CodeMirror-measure");
153 // 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
154 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
157 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
155 null, "position: relative; outline: none");
158 null, "position: relative; outline: none");
156 // Moved around its parent to cover visible view.
159 // Moved around its parent to cover visible view.
157 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");
158 // Set to the height of the document, allowing scrolling.
161 // Set to the height of the document, allowing scrolling.
159 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
162 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
160 d.sizerWidth = null;
163 d.sizerWidth = null;
161 // Behavior of elts with overflow: auto and padding is
164 // Behavior of elts with overflow: auto and padding is
162 // inconsistent across browsers. This is used to ensure the
165 // inconsistent across browsers. This is used to ensure the
163 // scrollable area is big enough.
166 // scrollable area is big enough.
164 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
167 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
165 // Will contain the gutters, if any.
168 // Will contain the gutters, if any.
166 d.gutters = elt("div", null, "CodeMirror-gutters");
169 d.gutters = elt("div", null, "CodeMirror-gutters");
167 d.lineGutter = null;
170 d.lineGutter = null;
168 // Actual scrollable element.
171 // Actual scrollable element.
169 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
172 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
170 d.scroller.setAttribute("tabIndex", "-1");
173 d.scroller.setAttribute("tabIndex", "-1");
171 // The element in which the editor lives.
174 // The element in which the editor lives.
172 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
175 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
173
176
174 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
177 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
175 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
178 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
176 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
179 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
177
180
178 if (place) {
181 if (place) {
179 if (place.appendChild) place.appendChild(d.wrapper);
182 if (place.appendChild) place.appendChild(d.wrapper);
180 else place(d.wrapper);
183 else place(d.wrapper);
181 }
184 }
182
185
183 // Current rendered range (may be bigger than the view window).
186 // Current rendered range (may be bigger than the view window).
184 d.viewFrom = d.viewTo = doc.first;
187 d.viewFrom = d.viewTo = doc.first;
185 d.reportedViewFrom = d.reportedViewTo = doc.first;
188 d.reportedViewFrom = d.reportedViewTo = doc.first;
186 // Information about the rendered lines.
189 // Information about the rendered lines.
187 d.view = [];
190 d.view = [];
188 d.renderedView = null;
191 d.renderedView = null;
189 // Holds info about a single rendered line when it was rendered
192 // Holds info about a single rendered line when it was rendered
190 // for measurement, while not in view.
193 // for measurement, while not in view.
191 d.externalMeasured = null;
194 d.externalMeasured = null;
192 // Empty space (in pixels) above the view
195 // Empty space (in pixels) above the view
193 d.viewOffset = 0;
196 d.viewOffset = 0;
194 d.lastWrapHeight = d.lastWrapWidth = 0;
197 d.lastWrapHeight = d.lastWrapWidth = 0;
195 d.updateLineNumbers = null;
198 d.updateLineNumbers = null;
196
199
197 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
200 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
198 d.scrollbarsClipped = false;
201 d.scrollbarsClipped = false;
199
202
200 // Used to only resize the line number gutter when necessary (when
203 // Used to only resize the line number gutter when necessary (when
201 // the amount of lines crosses a boundary that makes its width change)
204 // the amount of lines crosses a boundary that makes its width change)
202 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
205 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
203 // Set to true when a non-horizontal-scrolling line widget is
206 // Set to true when a non-horizontal-scrolling line widget is
204 // added. As an optimization, line widget aligning is skipped when
207 // added. As an optimization, line widget aligning is skipped when
205 // this is false.
208 // this is false.
206 d.alignWidgets = false;
209 d.alignWidgets = false;
207
210
208 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
211 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
209
212
210 // Tracks the maximum line length so that the horizontal scrollbar
213 // Tracks the maximum line length so that the horizontal scrollbar
211 // can be kept static when scrolling.
214 // can be kept static when scrolling.
212 d.maxLine = null;
215 d.maxLine = null;
213 d.maxLineLength = 0;
216 d.maxLineLength = 0;
214 d.maxLineChanged = false;
217 d.maxLineChanged = false;
215
218
216 // Used for measuring wheel scrolling granularity
219 // Used for measuring wheel scrolling granularity
217 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
220 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
218
221
219 // True when shift is held down.
222 // True when shift is held down.
220 d.shift = false;
223 d.shift = false;
221
224
222 // Used to track whether anything happened since the context menu
225 // Used to track whether anything happened since the context menu
223 // was opened.
226 // was opened.
224 d.selForContextMenu = null;
227 d.selForContextMenu = null;
225
228
226 d.activeTouch = null;
229 d.activeTouch = null;
227
230
228 input.init(d);
231 input.init(d);
229 }
232 }
230
233
231 // STATE UPDATES
234 // STATE UPDATES
232
235
233 // Used to get the editor into a consistent state again when options change.
236 // Used to get the editor into a consistent state again when options change.
234
237
235 function loadMode(cm) {
238 function loadMode(cm) {
236 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
239 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
237 resetModeState(cm);
240 resetModeState(cm);
238 }
241 }
239
242
240 function resetModeState(cm) {
243 function resetModeState(cm) {
241 cm.doc.iter(function(line) {
244 cm.doc.iter(function(line) {
242 if (line.stateAfter) line.stateAfter = null;
245 if (line.stateAfter) line.stateAfter = null;
243 if (line.styles) line.styles = null;
246 if (line.styles) line.styles = null;
244 });
247 });
245 cm.doc.frontier = cm.doc.first;
248 cm.doc.frontier = cm.doc.first;
246 startWorker(cm, 100);
249 startWorker(cm, 100);
247 cm.state.modeGen++;
250 cm.state.modeGen++;
248 if (cm.curOp) regChange(cm);
251 if (cm.curOp) regChange(cm);
249 }
252 }
250
253
251 function wrappingChanged(cm) {
254 function wrappingChanged(cm) {
252 if (cm.options.lineWrapping) {
255 if (cm.options.lineWrapping) {
253 addClass(cm.display.wrapper, "CodeMirror-wrap");
256 addClass(cm.display.wrapper, "CodeMirror-wrap");
254 cm.display.sizer.style.minWidth = "";
257 cm.display.sizer.style.minWidth = "";
255 cm.display.sizerWidth = null;
258 cm.display.sizerWidth = null;
256 } else {
259 } else {
257 rmClass(cm.display.wrapper, "CodeMirror-wrap");
260 rmClass(cm.display.wrapper, "CodeMirror-wrap");
258 findMaxLine(cm);
261 findMaxLine(cm);
259 }
262 }
260 estimateLineHeights(cm);
263 estimateLineHeights(cm);
261 regChange(cm);
264 regChange(cm);
262 clearCaches(cm);
265 clearCaches(cm);
263 setTimeout(function(){updateScrollbars(cm);}, 100);
266 setTimeout(function(){updateScrollbars(cm);}, 100);
264 }
267 }
265
268
266 // Returns a function that estimates the height of a line, to use as
269 // Returns a function that estimates the height of a line, to use as
267 // first approximation until the line becomes visible (and is thus
270 // first approximation until the line becomes visible (and is thus
268 // properly measurable).
271 // properly measurable).
269 function estimateHeight(cm) {
272 function estimateHeight(cm) {
270 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
273 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
271 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
274 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
272 return function(line) {
275 return function(line) {
273 if (lineIsHidden(cm.doc, line)) return 0;
276 if (lineIsHidden(cm.doc, line)) return 0;
274
277
275 var widgetsHeight = 0;
278 var widgetsHeight = 0;
276 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
279 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
277 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
280 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
278 }
281 }
279
282
280 if (wrapping)
283 if (wrapping)
281 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
284 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
282 else
285 else
283 return widgetsHeight + th;
286 return widgetsHeight + th;
284 };
287 };
285 }
288 }
286
289
287 function estimateLineHeights(cm) {
290 function estimateLineHeights(cm) {
288 var doc = cm.doc, est = estimateHeight(cm);
291 var doc = cm.doc, est = estimateHeight(cm);
289 doc.iter(function(line) {
292 doc.iter(function(line) {
290 var estHeight = est(line);
293 var estHeight = est(line);
291 if (estHeight != line.height) updateLineHeight(line, estHeight);
294 if (estHeight != line.height) updateLineHeight(line, estHeight);
292 });
295 });
293 }
296 }
294
297
295 function themeChanged(cm) {
298 function themeChanged(cm) {
296 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
299 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
297 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
300 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
298 clearCaches(cm);
301 clearCaches(cm);
299 }
302 }
300
303
301 function guttersChanged(cm) {
304 function guttersChanged(cm) {
302 updateGutters(cm);
305 updateGutters(cm);
303 regChange(cm);
306 regChange(cm);
304 setTimeout(function(){alignHorizontally(cm);}, 20);
307 setTimeout(function(){alignHorizontally(cm);}, 20);
305 }
308 }
306
309
307 // Rebuild the gutter elements, ensure the margin to the left of the
310 // Rebuild the gutter elements, ensure the margin to the left of the
308 // code matches their width.
311 // code matches their width.
309 function updateGutters(cm) {
312 function updateGutters(cm) {
310 var gutters = cm.display.gutters, specs = cm.options.gutters;
313 var gutters = cm.display.gutters, specs = cm.options.gutters;
311 removeChildren(gutters);
314 removeChildren(gutters);
312 for (var i = 0; i < specs.length; ++i) {
315 for (var i = 0; i < specs.length; ++i) {
313 var gutterClass = specs[i];
316 var gutterClass = specs[i];
314 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
317 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
315 if (gutterClass == "CodeMirror-linenumbers") {
318 if (gutterClass == "CodeMirror-linenumbers") {
316 cm.display.lineGutter = gElt;
319 cm.display.lineGutter = gElt;
317 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
320 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
318 }
321 }
319 }
322 }
320 gutters.style.display = i ? "" : "none";
323 gutters.style.display = i ? "" : "none";
321 updateGutterSpace(cm);
324 updateGutterSpace(cm);
322 }
325 }
323
326
324 function updateGutterSpace(cm) {
327 function updateGutterSpace(cm) {
325 var width = cm.display.gutters.offsetWidth;
328 var width = cm.display.gutters.offsetWidth;
326 cm.display.sizer.style.marginLeft = width + "px";
329 cm.display.sizer.style.marginLeft = width + "px";
327 }
330 }
328
331
329 // Compute the character length of a line, taking into account
332 // Compute the character length of a line, taking into account
330 // collapsed ranges (see markText) that might hide parts, and join
333 // collapsed ranges (see markText) that might hide parts, and join
331 // other lines onto it.
334 // other lines onto it.
332 function lineLength(line) {
335 function lineLength(line) {
333 if (line.height == 0) return 0;
336 if (line.height == 0) return 0;
334 var len = line.text.length, merged, cur = line;
337 var len = line.text.length, merged, cur = line;
335 while (merged = collapsedSpanAtStart(cur)) {
338 while (merged = collapsedSpanAtStart(cur)) {
336 var found = merged.find(0, true);
339 var found = merged.find(0, true);
337 cur = found.from.line;
340 cur = found.from.line;
338 len += found.from.ch - found.to.ch;
341 len += found.from.ch - found.to.ch;
339 }
342 }
340 cur = line;
343 cur = line;
341 while (merged = collapsedSpanAtEnd(cur)) {
344 while (merged = collapsedSpanAtEnd(cur)) {
342 var found = merged.find(0, true);
345 var found = merged.find(0, true);
343 len -= cur.text.length - found.from.ch;
346 len -= cur.text.length - found.from.ch;
344 cur = found.to.line;
347 cur = found.to.line;
345 len += cur.text.length - found.to.ch;
348 len += cur.text.length - found.to.ch;
346 }
349 }
347 return len;
350 return len;
348 }
351 }
349
352
350 // Find the longest line in the document.
353 // Find the longest line in the document.
351 function findMaxLine(cm) {
354 function findMaxLine(cm) {
352 var d = cm.display, doc = cm.doc;
355 var d = cm.display, doc = cm.doc;
353 d.maxLine = getLine(doc, doc.first);
356 d.maxLine = getLine(doc, doc.first);
354 d.maxLineLength = lineLength(d.maxLine);
357 d.maxLineLength = lineLength(d.maxLine);
355 d.maxLineChanged = true;
358 d.maxLineChanged = true;
356 doc.iter(function(line) {
359 doc.iter(function(line) {
357 var len = lineLength(line);
360 var len = lineLength(line);
358 if (len > d.maxLineLength) {
361 if (len > d.maxLineLength) {
359 d.maxLineLength = len;
362 d.maxLineLength = len;
360 d.maxLine = line;
363 d.maxLine = line;
361 }
364 }
362 });
365 });
363 }
366 }
364
367
365 // Make sure the gutters options contains the element
368 // Make sure the gutters options contains the element
366 // "CodeMirror-linenumbers" when the lineNumbers option is true.
369 // "CodeMirror-linenumbers" when the lineNumbers option is true.
367 function setGuttersForLineNumbers(options) {
370 function setGuttersForLineNumbers(options) {
368 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
371 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
369 if (found == -1 && options.lineNumbers) {
372 if (found == -1 && options.lineNumbers) {
370 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
373 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
371 } else if (found > -1 && !options.lineNumbers) {
374 } else if (found > -1 && !options.lineNumbers) {
372 options.gutters = options.gutters.slice(0);
375 options.gutters = options.gutters.slice(0);
373 options.gutters.splice(found, 1);
376 options.gutters.splice(found, 1);
374 }
377 }
375 }
378 }
376
379
377 // SCROLLBARS
380 // SCROLLBARS
378
381
379 // Prepare DOM reads needed to update the scrollbars. Done in one
382 // Prepare DOM reads needed to update the scrollbars. Done in one
380 // shot to minimize update/measure roundtrips.
383 // shot to minimize update/measure roundtrips.
381 function measureForScrollbars(cm) {
384 function measureForScrollbars(cm) {
382 var d = cm.display, gutterW = d.gutters.offsetWidth;
385 var d = cm.display, gutterW = d.gutters.offsetWidth;
383 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
386 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
384 return {
387 return {
385 clientHeight: d.scroller.clientHeight,
388 clientHeight: d.scroller.clientHeight,
386 viewHeight: d.wrapper.clientHeight,
389 viewHeight: d.wrapper.clientHeight,
387 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
390 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
388 viewWidth: d.wrapper.clientWidth,
391 viewWidth: d.wrapper.clientWidth,
389 barLeft: cm.options.fixedGutter ? gutterW : 0,
392 barLeft: cm.options.fixedGutter ? gutterW : 0,
390 docHeight: docH,
393 docHeight: docH,
391 scrollHeight: docH + scrollGap(cm) + d.barHeight,
394 scrollHeight: docH + scrollGap(cm) + d.barHeight,
392 nativeBarWidth: d.nativeBarWidth,
395 nativeBarWidth: d.nativeBarWidth,
393 gutterWidth: gutterW
396 gutterWidth: gutterW
394 };
397 };
395 }
398 }
396
399
397 function NativeScrollbars(place, scroll, cm) {
400 function NativeScrollbars(place, scroll, cm) {
398 this.cm = cm;
401 this.cm = cm;
399 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
402 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
400 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
403 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
401 place(vert); place(horiz);
404 place(vert); place(horiz);
402
405
403 on(vert, "scroll", function() {
406 on(vert, "scroll", function() {
404 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
407 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
405 });
408 });
406 on(horiz, "scroll", function() {
409 on(horiz, "scroll", function() {
407 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
410 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
408 });
411 });
409
412
410 this.checkedOverlay = false;
413 this.checkedZeroWidth = false;
411 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
414 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
412 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
415 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
413 }
416 }
414
417
415 NativeScrollbars.prototype = copyObj({
418 NativeScrollbars.prototype = copyObj({
416 update: function(measure) {
419 update: function(measure) {
417 var needsH = measure.scrollWidth > measure.clientWidth + 1;
420 var needsH = measure.scrollWidth > measure.clientWidth + 1;
418 var needsV = measure.scrollHeight > measure.clientHeight + 1;
421 var needsV = measure.scrollHeight > measure.clientHeight + 1;
419 var sWidth = measure.nativeBarWidth;
422 var sWidth = measure.nativeBarWidth;
420
423
421 if (needsV) {
424 if (needsV) {
422 this.vert.style.display = "block";
425 this.vert.style.display = "block";
423 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
426 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
424 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
427 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
425 // A bug in IE8 can cause this value to be negative, so guard it.
428 // A bug in IE8 can cause this value to be negative, so guard it.
426 this.vert.firstChild.style.height =
429 this.vert.firstChild.style.height =
427 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
430 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
428 } else {
431 } else {
429 this.vert.style.display = "";
432 this.vert.style.display = "";
430 this.vert.firstChild.style.height = "0";
433 this.vert.firstChild.style.height = "0";
431 }
434 }
432
435
433 if (needsH) {
436 if (needsH) {
434 this.horiz.style.display = "block";
437 this.horiz.style.display = "block";
435 this.horiz.style.right = needsV ? sWidth + "px" : "0";
438 this.horiz.style.right = needsV ? sWidth + "px" : "0";
436 this.horiz.style.left = measure.barLeft + "px";
439 this.horiz.style.left = measure.barLeft + "px";
437 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
440 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
438 this.horiz.firstChild.style.width =
441 this.horiz.firstChild.style.width =
439 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
442 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
440 } else {
443 } else {
441 this.horiz.style.display = "";
444 this.horiz.style.display = "";
442 this.horiz.firstChild.style.width = "0";
445 this.horiz.firstChild.style.width = "0";
443 }
446 }
444
447
445 if (!this.checkedOverlay && measure.clientHeight > 0) {
448 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
446 if (sWidth == 0) this.overlayHack();
449 if (sWidth == 0) this.zeroWidthHack();
447 this.checkedOverlay = true;
450 this.checkedZeroWidth = true;
448 }
451 }
449
452
450 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
453 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
451 },
454 },
452 setScrollLeft: function(pos) {
455 setScrollLeft: function(pos) {
453 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
456 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
457 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz);
454 },
458 },
455 setScrollTop: function(pos) {
459 setScrollTop: function(pos) {
456 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
460 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
457 },
461 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert);
458 overlayHack: function() {
462 },
463 zeroWidthHack: function() {
459 var w = mac && !mac_geMountainLion ? "12px" : "18px";
464 var w = mac && !mac_geMountainLion ? "12px" : "18px";
460 this.horiz.style.minHeight = this.vert.style.minWidth = w;
465 this.horiz.style.height = this.vert.style.width = w;
461 var self = this;
466 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
462 var barMouseDown = function(e) {
467 this.disableHoriz = new Delayed;
463 if (e_target(e) != self.vert && e_target(e) != self.horiz)
468 this.disableVert = new Delayed;
464 operation(self.cm, onMouseDown)(e);
469 },
465 };
470 enableZeroWidthBar: function(bar, delay) {
466 on(this.vert, "mousedown", barMouseDown);
471 bar.style.pointerEvents = "auto";
467 on(this.horiz, "mousedown", barMouseDown);
472 function maybeDisable() {
473 // To find out whether the scrollbar is still visible, we
474 // check whether the element under the pixel in the bottom
475 // left corner of the scrollbar box is the scrollbar box
476 // itself (when the bar is still visible) or its filler child
477 // (when the bar is hidden). If it is still visible, we keep
478 // it enabled, if it's hidden, we disable pointer events.
479 var box = bar.getBoundingClientRect();
480 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1);
481 if (elt != bar) bar.style.pointerEvents = "none";
482 else delay.set(1000, maybeDisable);
483 }
484 delay.set(1000, maybeDisable);
468 },
485 },
469 clear: function() {
486 clear: function() {
470 var parent = this.horiz.parentNode;
487 var parent = this.horiz.parentNode;
471 parent.removeChild(this.horiz);
488 parent.removeChild(this.horiz);
472 parent.removeChild(this.vert);
489 parent.removeChild(this.vert);
473 }
490 }
474 }, NativeScrollbars.prototype);
491 }, NativeScrollbars.prototype);
475
492
476 function NullScrollbars() {}
493 function NullScrollbars() {}
477
494
478 NullScrollbars.prototype = copyObj({
495 NullScrollbars.prototype = copyObj({
479 update: function() { return {bottom: 0, right: 0}; },
496 update: function() { return {bottom: 0, right: 0}; },
480 setScrollLeft: function() {},
497 setScrollLeft: function() {},
481 setScrollTop: function() {},
498 setScrollTop: function() {},
482 clear: function() {}
499 clear: function() {}
483 }, NullScrollbars.prototype);
500 }, NullScrollbars.prototype);
484
501
485 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
502 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
486
503
487 function initScrollbars(cm) {
504 function initScrollbars(cm) {
488 if (cm.display.scrollbars) {
505 if (cm.display.scrollbars) {
489 cm.display.scrollbars.clear();
506 cm.display.scrollbars.clear();
490 if (cm.display.scrollbars.addClass)
507 if (cm.display.scrollbars.addClass)
491 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
508 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
492 }
509 }
493
510
494 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
511 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
495 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
512 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
496 // Prevent clicks in the scrollbars from killing focus
513 // Prevent clicks in the scrollbars from killing focus
497 on(node, "mousedown", function() {
514 on(node, "mousedown", function() {
498 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
515 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
499 });
516 });
500 node.setAttribute("cm-not-content", "true");
517 node.setAttribute("cm-not-content", "true");
501 }, function(pos, axis) {
518 }, function(pos, axis) {
502 if (axis == "horizontal") setScrollLeft(cm, pos);
519 if (axis == "horizontal") setScrollLeft(cm, pos);
503 else setScrollTop(cm, pos);
520 else setScrollTop(cm, pos);
504 }, cm);
521 }, cm);
505 if (cm.display.scrollbars.addClass)
522 if (cm.display.scrollbars.addClass)
506 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
523 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
507 }
524 }
508
525
509 function updateScrollbars(cm, measure) {
526 function updateScrollbars(cm, measure) {
510 if (!measure) measure = measureForScrollbars(cm);
527 if (!measure) measure = measureForScrollbars(cm);
511 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
528 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
512 updateScrollbarsInner(cm, measure);
529 updateScrollbarsInner(cm, measure);
513 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
530 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
514 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
531 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
515 updateHeightsInViewport(cm);
532 updateHeightsInViewport(cm);
516 updateScrollbarsInner(cm, measureForScrollbars(cm));
533 updateScrollbarsInner(cm, measureForScrollbars(cm));
517 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
534 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
518 }
535 }
519 }
536 }
520
537
521 // Re-synchronize the fake scrollbars with the actual size of the
538 // Re-synchronize the fake scrollbars with the actual size of the
522 // content.
539 // content.
523 function updateScrollbarsInner(cm, measure) {
540 function updateScrollbarsInner(cm, measure) {
524 var d = cm.display;
541 var d = cm.display;
525 var sizes = d.scrollbars.update(measure);
542 var sizes = d.scrollbars.update(measure);
526
543
527 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
544 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
528 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
545 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
529
546
530 if (sizes.right && sizes.bottom) {
547 if (sizes.right && sizes.bottom) {
531 d.scrollbarFiller.style.display = "block";
548 d.scrollbarFiller.style.display = "block";
532 d.scrollbarFiller.style.height = sizes.bottom + "px";
549 d.scrollbarFiller.style.height = sizes.bottom + "px";
533 d.scrollbarFiller.style.width = sizes.right + "px";
550 d.scrollbarFiller.style.width = sizes.right + "px";
534 } else d.scrollbarFiller.style.display = "";
551 } else d.scrollbarFiller.style.display = "";
535 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
552 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
536 d.gutterFiller.style.display = "block";
553 d.gutterFiller.style.display = "block";
537 d.gutterFiller.style.height = sizes.bottom + "px";
554 d.gutterFiller.style.height = sizes.bottom + "px";
538 d.gutterFiller.style.width = measure.gutterWidth + "px";
555 d.gutterFiller.style.width = measure.gutterWidth + "px";
539 } else d.gutterFiller.style.display = "";
556 } else d.gutterFiller.style.display = "";
540 }
557 }
541
558
542 // Compute the lines that are visible in a given viewport (defaults
559 // Compute the lines that are visible in a given viewport (defaults
543 // the the current scroll position). viewport may contain top,
560 // the the current scroll position). viewport may contain top,
544 // height, and ensure (see op.scrollToPos) properties.
561 // height, and ensure (see op.scrollToPos) properties.
545 function visibleLines(display, doc, viewport) {
562 function visibleLines(display, doc, viewport) {
546 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
563 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
547 top = Math.floor(top - paddingTop(display));
564 top = Math.floor(top - paddingTop(display));
548 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
565 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
549
566
550 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
567 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
551 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
568 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
552 // forces those lines into the viewport (if possible).
569 // forces those lines into the viewport (if possible).
553 if (viewport && viewport.ensure) {
570 if (viewport && viewport.ensure) {
554 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
571 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
555 if (ensureFrom < from) {
572 if (ensureFrom < from) {
556 from = ensureFrom;
573 from = ensureFrom;
557 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
574 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
558 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
575 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
559 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
576 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
560 to = ensureTo;
577 to = ensureTo;
561 }
578 }
562 }
579 }
563 return {from: from, to: Math.max(to, from + 1)};
580 return {from: from, to: Math.max(to, from + 1)};
564 }
581 }
565
582
566 // LINE NUMBERS
583 // LINE NUMBERS
567
584
568 // Re-align line numbers and gutter marks to compensate for
585 // Re-align line numbers and gutter marks to compensate for
569 // horizontal scrolling.
586 // horizontal scrolling.
570 function alignHorizontally(cm) {
587 function alignHorizontally(cm) {
571 var display = cm.display, view = display.view;
588 var display = cm.display, view = display.view;
572 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
589 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
573 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
590 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
574 var gutterW = display.gutters.offsetWidth, left = comp + "px";
591 var gutterW = display.gutters.offsetWidth, left = comp + "px";
575 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
592 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
576 if (cm.options.fixedGutter && view[i].gutter)
593 if (cm.options.fixedGutter && view[i].gutter)
577 view[i].gutter.style.left = left;
594 view[i].gutter.style.left = left;
578 var align = view[i].alignable;
595 var align = view[i].alignable;
579 if (align) for (var j = 0; j < align.length; j++)
596 if (align) for (var j = 0; j < align.length; j++)
580 align[j].style.left = left;
597 align[j].style.left = left;
581 }
598 }
582 if (cm.options.fixedGutter)
599 if (cm.options.fixedGutter)
583 display.gutters.style.left = (comp + gutterW) + "px";
600 display.gutters.style.left = (comp + gutterW) + "px";
584 }
601 }
585
602
586 // Used to ensure that the line number gutter is still the right
603 // Used to ensure that the line number gutter is still the right
587 // size for the current document size. Returns true when an update
604 // size for the current document size. Returns true when an update
588 // is needed.
605 // is needed.
589 function maybeUpdateLineNumberWidth(cm) {
606 function maybeUpdateLineNumberWidth(cm) {
590 if (!cm.options.lineNumbers) return false;
607 if (!cm.options.lineNumbers) return false;
591 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
608 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
592 if (last.length != display.lineNumChars) {
609 if (last.length != display.lineNumChars) {
593 var test = display.measure.appendChild(elt("div", [elt("div", last)],
610 var test = display.measure.appendChild(elt("div", [elt("div", last)],
594 "CodeMirror-linenumber CodeMirror-gutter-elt"));
611 "CodeMirror-linenumber CodeMirror-gutter-elt"));
595 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
612 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
596 display.lineGutter.style.width = "";
613 display.lineGutter.style.width = "";
597 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
614 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
598 display.lineNumWidth = display.lineNumInnerWidth + padding;
615 display.lineNumWidth = display.lineNumInnerWidth + padding;
599 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
616 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
600 display.lineGutter.style.width = display.lineNumWidth + "px";
617 display.lineGutter.style.width = display.lineNumWidth + "px";
601 updateGutterSpace(cm);
618 updateGutterSpace(cm);
602 return true;
619 return true;
603 }
620 }
604 return false;
621 return false;
605 }
622 }
606
623
607 function lineNumberFor(options, i) {
624 function lineNumberFor(options, i) {
608 return String(options.lineNumberFormatter(i + options.firstLineNumber));
625 return String(options.lineNumberFormatter(i + options.firstLineNumber));
609 }
626 }
610
627
611 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
628 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
612 // but using getBoundingClientRect to get a sub-pixel-accurate
629 // but using getBoundingClientRect to get a sub-pixel-accurate
613 // result.
630 // result.
614 function compensateForHScroll(display) {
631 function compensateForHScroll(display) {
615 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
632 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
616 }
633 }
617
634
618 // DISPLAY DRAWING
635 // DISPLAY DRAWING
619
636
620 function DisplayUpdate(cm, viewport, force) {
637 function DisplayUpdate(cm, viewport, force) {
621 var display = cm.display;
638 var display = cm.display;
622
639
623 this.viewport = viewport;
640 this.viewport = viewport;
624 // Store some values that we'll need later (but don't want to force a relayout for)
641 // Store some values that we'll need later (but don't want to force a relayout for)
625 this.visible = visibleLines(display, cm.doc, viewport);
642 this.visible = visibleLines(display, cm.doc, viewport);
626 this.editorIsHidden = !display.wrapper.offsetWidth;
643 this.editorIsHidden = !display.wrapper.offsetWidth;
627 this.wrapperHeight = display.wrapper.clientHeight;
644 this.wrapperHeight = display.wrapper.clientHeight;
628 this.wrapperWidth = display.wrapper.clientWidth;
645 this.wrapperWidth = display.wrapper.clientWidth;
629 this.oldDisplayWidth = displayWidth(cm);
646 this.oldDisplayWidth = displayWidth(cm);
630 this.force = force;
647 this.force = force;
631 this.dims = getDimensions(cm);
648 this.dims = getDimensions(cm);
632 this.events = [];
649 this.events = [];
633 }
650 }
634
651
635 DisplayUpdate.prototype.signal = function(emitter, type) {
652 DisplayUpdate.prototype.signal = function(emitter, type) {
636 if (hasHandler(emitter, type))
653 if (hasHandler(emitter, type))
637 this.events.push(arguments);
654 this.events.push(arguments);
638 };
655 };
639 DisplayUpdate.prototype.finish = function() {
656 DisplayUpdate.prototype.finish = function() {
640 for (var i = 0; i < this.events.length; i++)
657 for (var i = 0; i < this.events.length; i++)
641 signal.apply(null, this.events[i]);
658 signal.apply(null, this.events[i]);
642 };
659 };
643
660
644 function maybeClipScrollbars(cm) {
661 function maybeClipScrollbars(cm) {
645 var display = cm.display;
662 var display = cm.display;
646 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
663 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
647 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
664 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
648 display.heightForcer.style.height = scrollGap(cm) + "px";
665 display.heightForcer.style.height = scrollGap(cm) + "px";
649 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
666 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
650 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
667 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
651 display.scrollbarsClipped = true;
668 display.scrollbarsClipped = true;
652 }
669 }
653 }
670 }
654
671
655 // Does the actual updating of the line display. Bails out
672 // Does the actual updating of the line display. Bails out
656 // (returning false) when there is nothing to be done and forced is
673 // (returning false) when there is nothing to be done and forced is
657 // false.
674 // false.
658 function updateDisplayIfNeeded(cm, update) {
675 function updateDisplayIfNeeded(cm, update) {
659 var display = cm.display, doc = cm.doc;
676 var display = cm.display, doc = cm.doc;
660
677
661 if (update.editorIsHidden) {
678 if (update.editorIsHidden) {
662 resetView(cm);
679 resetView(cm);
663 return false;
680 return false;
664 }
681 }
665
682
666 // Bail out if the visible area is already rendered and nothing changed.
683 // Bail out if the visible area is already rendered and nothing changed.
667 if (!update.force &&
684 if (!update.force &&
668 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
685 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
669 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
686 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
670 display.renderedView == display.view && countDirtyView(cm) == 0)
687 display.renderedView == display.view && countDirtyView(cm) == 0)
671 return false;
688 return false;
672
689
673 if (maybeUpdateLineNumberWidth(cm)) {
690 if (maybeUpdateLineNumberWidth(cm)) {
674 resetView(cm);
691 resetView(cm);
675 update.dims = getDimensions(cm);
692 update.dims = getDimensions(cm);
676 }
693 }
677
694
678 // Compute a suitable new viewport (from & to)
695 // Compute a suitable new viewport (from & to)
679 var end = doc.first + doc.size;
696 var end = doc.first + doc.size;
680 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
697 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
681 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
698 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
682 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
699 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
683 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
700 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
684 if (sawCollapsedSpans) {
701 if (sawCollapsedSpans) {
685 from = visualLineNo(cm.doc, from);
702 from = visualLineNo(cm.doc, from);
686 to = visualLineEndNo(cm.doc, to);
703 to = visualLineEndNo(cm.doc, to);
687 }
704 }
688
705
689 var different = from != display.viewFrom || to != display.viewTo ||
706 var different = from != display.viewFrom || to != display.viewTo ||
690 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
707 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
691 adjustView(cm, from, to);
708 adjustView(cm, from, to);
692
709
693 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
710 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
694 // Position the mover div to align with the current scroll position
711 // Position the mover div to align with the current scroll position
695 cm.display.mover.style.top = display.viewOffset + "px";
712 cm.display.mover.style.top = display.viewOffset + "px";
696
713
697 var toUpdate = countDirtyView(cm);
714 var toUpdate = countDirtyView(cm);
698 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
715 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
699 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
716 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
700 return false;
717 return false;
701
718
702 // For big changes, we hide the enclosing element during the
719 // For big changes, we hide the enclosing element during the
703 // update, since that speeds up the operations on most browsers.
720 // update, since that speeds up the operations on most browsers.
704 var focused = activeElt();
721 var focused = activeElt();
705 if (toUpdate > 4) display.lineDiv.style.display = "none";
722 if (toUpdate > 4) display.lineDiv.style.display = "none";
706 patchDisplay(cm, display.updateLineNumbers, update.dims);
723 patchDisplay(cm, display.updateLineNumbers, update.dims);
707 if (toUpdate > 4) display.lineDiv.style.display = "";
724 if (toUpdate > 4) display.lineDiv.style.display = "";
708 display.renderedView = display.view;
725 display.renderedView = display.view;
709 // There might have been a widget with a focused element that got
726 // There might have been a widget with a focused element that got
710 // hidden or updated, if so re-focus it.
727 // hidden or updated, if so re-focus it.
711 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
728 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
712
729
713 // Prevent selection and cursors from interfering with the scroll
730 // Prevent selection and cursors from interfering with the scroll
714 // width and height.
731 // width and height.
715 removeChildren(display.cursorDiv);
732 removeChildren(display.cursorDiv);
716 removeChildren(display.selectionDiv);
733 removeChildren(display.selectionDiv);
717 display.gutters.style.height = 0;
734 display.gutters.style.height = display.sizer.style.minHeight = 0;
718
735
719 if (different) {
736 if (different) {
720 display.lastWrapHeight = update.wrapperHeight;
737 display.lastWrapHeight = update.wrapperHeight;
721 display.lastWrapWidth = update.wrapperWidth;
738 display.lastWrapWidth = update.wrapperWidth;
722 startWorker(cm, 400);
739 startWorker(cm, 400);
723 }
740 }
724
741
725 display.updateLineNumbers = null;
742 display.updateLineNumbers = null;
726
743
727 return true;
744 return true;
728 }
745 }
729
746
730 function postUpdateDisplay(cm, update) {
747 function postUpdateDisplay(cm, update) {
731 var viewport = update.viewport;
748 var viewport = update.viewport;
732 for (var first = true;; first = false) {
749 for (var first = true;; first = false) {
733 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
750 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
734 // Clip forced viewport to actual scrollable area.
751 // Clip forced viewport to actual scrollable area.
735 if (viewport && viewport.top != null)
752 if (viewport && viewport.top != null)
736 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
753 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
737 // Updated line heights might result in the drawn area not
754 // Updated line heights might result in the drawn area not
738 // actually covering the viewport. Keep looping until it does.
755 // actually covering the viewport. Keep looping until it does.
739 update.visible = visibleLines(cm.display, cm.doc, viewport);
756 update.visible = visibleLines(cm.display, cm.doc, viewport);
740 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
757 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
741 break;
758 break;
742 }
759 }
743 if (!updateDisplayIfNeeded(cm, update)) break;
760 if (!updateDisplayIfNeeded(cm, update)) break;
744 updateHeightsInViewport(cm);
761 updateHeightsInViewport(cm);
745 var barMeasure = measureForScrollbars(cm);
762 var barMeasure = measureForScrollbars(cm);
746 updateSelection(cm);
763 updateSelection(cm);
747 setDocumentHeight(cm, barMeasure);
764 setDocumentHeight(cm, barMeasure);
748 updateScrollbars(cm, barMeasure);
765 updateScrollbars(cm, barMeasure);
749 }
766 }
750
767
751 update.signal(cm, "update", cm);
768 update.signal(cm, "update", cm);
752 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
769 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
753 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
770 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
754 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
771 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
755 }
772 }
756 }
773 }
757
774
758 function updateDisplaySimple(cm, viewport) {
775 function updateDisplaySimple(cm, viewport) {
759 var update = new DisplayUpdate(cm, viewport);
776 var update = new DisplayUpdate(cm, viewport);
760 if (updateDisplayIfNeeded(cm, update)) {
777 if (updateDisplayIfNeeded(cm, update)) {
761 updateHeightsInViewport(cm);
778 updateHeightsInViewport(cm);
762 postUpdateDisplay(cm, update);
779 postUpdateDisplay(cm, update);
763 var barMeasure = measureForScrollbars(cm);
780 var barMeasure = measureForScrollbars(cm);
764 updateSelection(cm);
781 updateSelection(cm);
765 setDocumentHeight(cm, barMeasure);
782 setDocumentHeight(cm, barMeasure);
766 updateScrollbars(cm, barMeasure);
783 updateScrollbars(cm, barMeasure);
767 update.finish();
784 update.finish();
768 }
785 }
769 }
786 }
770
787
771 function setDocumentHeight(cm, measure) {
788 function setDocumentHeight(cm, measure) {
772 cm.display.sizer.style.minHeight = measure.docHeight + "px";
789 cm.display.sizer.style.minHeight = measure.docHeight + "px";
773 var total = measure.docHeight + cm.display.barHeight;
790 var total = measure.docHeight + cm.display.barHeight;
774 cm.display.heightForcer.style.top = total + "px";
791 cm.display.heightForcer.style.top = total + "px";
775 cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
792 cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
776 }
793 }
777
794
778 // Read the actual heights of the rendered lines, and update their
795 // Read the actual heights of the rendered lines, and update their
779 // stored heights to match.
796 // stored heights to match.
780 function updateHeightsInViewport(cm) {
797 function updateHeightsInViewport(cm) {
781 var display = cm.display;
798 var display = cm.display;
782 var prevBottom = display.lineDiv.offsetTop;
799 var prevBottom = display.lineDiv.offsetTop;
783 for (var i = 0; i < display.view.length; i++) {
800 for (var i = 0; i < display.view.length; i++) {
784 var cur = display.view[i], height;
801 var cur = display.view[i], height;
785 if (cur.hidden) continue;
802 if (cur.hidden) continue;
786 if (ie && ie_version < 8) {
803 if (ie && ie_version < 8) {
787 var bot = cur.node.offsetTop + cur.node.offsetHeight;
804 var bot = cur.node.offsetTop + cur.node.offsetHeight;
788 height = bot - prevBottom;
805 height = bot - prevBottom;
789 prevBottom = bot;
806 prevBottom = bot;
790 } else {
807 } else {
791 var box = cur.node.getBoundingClientRect();
808 var box = cur.node.getBoundingClientRect();
792 height = box.bottom - box.top;
809 height = box.bottom - box.top;
793 }
810 }
794 var diff = cur.line.height - height;
811 var diff = cur.line.height - height;
795 if (height < 2) height = textHeight(display);
812 if (height < 2) height = textHeight(display);
796 if (diff > .001 || diff < -.001) {
813 if (diff > .001 || diff < -.001) {
797 updateLineHeight(cur.line, height);
814 updateLineHeight(cur.line, height);
798 updateWidgetHeight(cur.line);
815 updateWidgetHeight(cur.line);
799 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
816 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
800 updateWidgetHeight(cur.rest[j]);
817 updateWidgetHeight(cur.rest[j]);
801 }
818 }
802 }
819 }
803 }
820 }
804
821
805 // Read and store the height of line widgets associated with the
822 // Read and store the height of line widgets associated with the
806 // given line.
823 // given line.
807 function updateWidgetHeight(line) {
824 function updateWidgetHeight(line) {
808 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
825 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
809 line.widgets[i].height = line.widgets[i].node.offsetHeight;
826 line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
810 }
827 }
811
828
812 // Do a bulk-read of the DOM positions and sizes needed to draw the
829 // Do a bulk-read of the DOM positions and sizes needed to draw the
813 // view, so that we don't interleave reading and writing to the DOM.
830 // view, so that we don't interleave reading and writing to the DOM.
814 function getDimensions(cm) {
831 function getDimensions(cm) {
815 var d = cm.display, left = {}, width = {};
832 var d = cm.display, left = {}, width = {};
816 var gutterLeft = d.gutters.clientLeft;
833 var gutterLeft = d.gutters.clientLeft;
817 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
834 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
818 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
835 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
819 width[cm.options.gutters[i]] = n.clientWidth;
836 width[cm.options.gutters[i]] = n.clientWidth;
820 }
837 }
821 return {fixedPos: compensateForHScroll(d),
838 return {fixedPos: compensateForHScroll(d),
822 gutterTotalWidth: d.gutters.offsetWidth,
839 gutterTotalWidth: d.gutters.offsetWidth,
823 gutterLeft: left,
840 gutterLeft: left,
824 gutterWidth: width,
841 gutterWidth: width,
825 wrapperWidth: d.wrapper.clientWidth};
842 wrapperWidth: d.wrapper.clientWidth};
826 }
843 }
827
844
828 // Sync the actual display DOM structure with display.view, removing
845 // Sync the actual display DOM structure with display.view, removing
829 // nodes for lines that are no longer in view, and creating the ones
846 // nodes for lines that are no longer in view, and creating the ones
830 // that are not there yet, and updating the ones that are out of
847 // that are not there yet, and updating the ones that are out of
831 // date.
848 // date.
832 function patchDisplay(cm, updateNumbersFrom, dims) {
849 function patchDisplay(cm, updateNumbersFrom, dims) {
833 var display = cm.display, lineNumbers = cm.options.lineNumbers;
850 var display = cm.display, lineNumbers = cm.options.lineNumbers;
834 var container = display.lineDiv, cur = container.firstChild;
851 var container = display.lineDiv, cur = container.firstChild;
835
852
836 function rm(node) {
853 function rm(node) {
837 var next = node.nextSibling;
854 var next = node.nextSibling;
838 // Works around a throw-scroll bug in OS X Webkit
855 // Works around a throw-scroll bug in OS X Webkit
839 if (webkit && mac && cm.display.currentWheelTarget == node)
856 if (webkit && mac && cm.display.currentWheelTarget == node)
840 node.style.display = "none";
857 node.style.display = "none";
841 else
858 else
842 node.parentNode.removeChild(node);
859 node.parentNode.removeChild(node);
843 return next;
860 return next;
844 }
861 }
845
862
846 var view = display.view, lineN = display.viewFrom;
863 var view = display.view, lineN = display.viewFrom;
847 // Loop over the elements in the view, syncing cur (the DOM nodes
864 // Loop over the elements in the view, syncing cur (the DOM nodes
848 // in display.lineDiv) with the view as we go.
865 // in display.lineDiv) with the view as we go.
849 for (var i = 0; i < view.length; i++) {
866 for (var i = 0; i < view.length; i++) {
850 var lineView = view[i];
867 var lineView = view[i];
851 if (lineView.hidden) {
868 if (lineView.hidden) {
852 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
869 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
853 var node = buildLineElement(cm, lineView, lineN, dims);
870 var node = buildLineElement(cm, lineView, lineN, dims);
854 container.insertBefore(node, cur);
871 container.insertBefore(node, cur);
855 } else { // Already drawn
872 } else { // Already drawn
856 while (cur != lineView.node) cur = rm(cur);
873 while (cur != lineView.node) cur = rm(cur);
857 var updateNumber = lineNumbers && updateNumbersFrom != null &&
874 var updateNumber = lineNumbers && updateNumbersFrom != null &&
858 updateNumbersFrom <= lineN && lineView.lineNumber;
875 updateNumbersFrom <= lineN && lineView.lineNumber;
859 if (lineView.changes) {
876 if (lineView.changes) {
860 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
877 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
861 updateLineForChanges(cm, lineView, lineN, dims);
878 updateLineForChanges(cm, lineView, lineN, dims);
862 }
879 }
863 if (updateNumber) {
880 if (updateNumber) {
864 removeChildren(lineView.lineNumber);
881 removeChildren(lineView.lineNumber);
865 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
882 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
866 }
883 }
867 cur = lineView.node.nextSibling;
884 cur = lineView.node.nextSibling;
868 }
885 }
869 lineN += lineView.size;
886 lineN += lineView.size;
870 }
887 }
871 while (cur) cur = rm(cur);
888 while (cur) cur = rm(cur);
872 }
889 }
873
890
874 // When an aspect of a line changes, a string is added to
891 // When an aspect of a line changes, a string is added to
875 // lineView.changes. This updates the relevant part of the line's
892 // lineView.changes. This updates the relevant part of the line's
876 // DOM structure.
893 // DOM structure.
877 function updateLineForChanges(cm, lineView, lineN, dims) {
894 function updateLineForChanges(cm, lineView, lineN, dims) {
878 for (var j = 0; j < lineView.changes.length; j++) {
895 for (var j = 0; j < lineView.changes.length; j++) {
879 var type = lineView.changes[j];
896 var type = lineView.changes[j];
880 if (type == "text") updateLineText(cm, lineView);
897 if (type == "text") updateLineText(cm, lineView);
881 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
898 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
882 else if (type == "class") updateLineClasses(lineView);
899 else if (type == "class") updateLineClasses(lineView);
883 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
900 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
884 }
901 }
885 lineView.changes = null;
902 lineView.changes = null;
886 }
903 }
887
904
888 // Lines with gutter elements, widgets or a background class need to
905 // Lines with gutter elements, widgets or a background class need to
889 // be wrapped, and have the extra elements added to the wrapper div
906 // be wrapped, and have the extra elements added to the wrapper div
890 function ensureLineWrapped(lineView) {
907 function ensureLineWrapped(lineView) {
891 if (lineView.node == lineView.text) {
908 if (lineView.node == lineView.text) {
892 lineView.node = elt("div", null, null, "position: relative");
909 lineView.node = elt("div", null, null, "position: relative");
893 if (lineView.text.parentNode)
910 if (lineView.text.parentNode)
894 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
911 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
895 lineView.node.appendChild(lineView.text);
912 lineView.node.appendChild(lineView.text);
896 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
913 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
897 }
914 }
898 return lineView.node;
915 return lineView.node;
899 }
916 }
900
917
901 function updateLineBackground(lineView) {
918 function updateLineBackground(lineView) {
902 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
919 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
903 if (cls) cls += " CodeMirror-linebackground";
920 if (cls) cls += " CodeMirror-linebackground";
904 if (lineView.background) {
921 if (lineView.background) {
905 if (cls) lineView.background.className = cls;
922 if (cls) lineView.background.className = cls;
906 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
923 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
907 } else if (cls) {
924 } else if (cls) {
908 var wrap = ensureLineWrapped(lineView);
925 var wrap = ensureLineWrapped(lineView);
909 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
926 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
910 }
927 }
911 }
928 }
912
929
913 // Wrapper around buildLineContent which will reuse the structure
930 // Wrapper around buildLineContent which will reuse the structure
914 // in display.externalMeasured when possible.
931 // in display.externalMeasured when possible.
915 function getLineContent(cm, lineView) {
932 function getLineContent(cm, lineView) {
916 var ext = cm.display.externalMeasured;
933 var ext = cm.display.externalMeasured;
917 if (ext && ext.line == lineView.line) {
934 if (ext && ext.line == lineView.line) {
918 cm.display.externalMeasured = null;
935 cm.display.externalMeasured = null;
919 lineView.measure = ext.measure;
936 lineView.measure = ext.measure;
920 return ext.built;
937 return ext.built;
921 }
938 }
922 return buildLineContent(cm, lineView);
939 return buildLineContent(cm, lineView);
923 }
940 }
924
941
925 // Redraw the line's text. Interacts with the background and text
942 // Redraw the line's text. Interacts with the background and text
926 // classes because the mode may output tokens that influence these
943 // classes because the mode may output tokens that influence these
927 // classes.
944 // classes.
928 function updateLineText(cm, lineView) {
945 function updateLineText(cm, lineView) {
929 var cls = lineView.text.className;
946 var cls = lineView.text.className;
930 var built = getLineContent(cm, lineView);
947 var built = getLineContent(cm, lineView);
931 if (lineView.text == lineView.node) lineView.node = built.pre;
948 if (lineView.text == lineView.node) lineView.node = built.pre;
932 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
949 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
933 lineView.text = built.pre;
950 lineView.text = built.pre;
934 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
951 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
935 lineView.bgClass = built.bgClass;
952 lineView.bgClass = built.bgClass;
936 lineView.textClass = built.textClass;
953 lineView.textClass = built.textClass;
937 updateLineClasses(lineView);
954 updateLineClasses(lineView);
938 } else if (cls) {
955 } else if (cls) {
939 lineView.text.className = cls;
956 lineView.text.className = cls;
940 }
957 }
941 }
958 }
942
959
943 function updateLineClasses(lineView) {
960 function updateLineClasses(lineView) {
944 updateLineBackground(lineView);
961 updateLineBackground(lineView);
945 if (lineView.line.wrapClass)
962 if (lineView.line.wrapClass)
946 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
963 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
947 else if (lineView.node != lineView.text)
964 else if (lineView.node != lineView.text)
948 lineView.node.className = "";
965 lineView.node.className = "";
949 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
966 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
950 lineView.text.className = textClass || "";
967 lineView.text.className = textClass || "";
951 }
968 }
952
969
953 function updateLineGutter(cm, lineView, lineN, dims) {
970 function updateLineGutter(cm, lineView, lineN, dims) {
954 if (lineView.gutter) {
971 if (lineView.gutter) {
955 lineView.node.removeChild(lineView.gutter);
972 lineView.node.removeChild(lineView.gutter);
956 lineView.gutter = null;
973 lineView.gutter = null;
957 }
974 }
975 if (lineView.gutterBackground) {
976 lineView.node.removeChild(lineView.gutterBackground);
977 lineView.gutterBackground = null;
978 }
979 if (lineView.line.gutterClass) {
980 var wrap = ensureLineWrapped(lineView);
981 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
982 "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
983 "px; width: " + dims.gutterTotalWidth + "px");
984 wrap.insertBefore(lineView.gutterBackground, lineView.text);
985 }
958 var markers = lineView.line.gutterMarkers;
986 var markers = lineView.line.gutterMarkers;
959 if (cm.options.lineNumbers || markers) {
987 if (cm.options.lineNumbers || markers) {
960 var wrap = ensureLineWrapped(lineView);
988 var wrap = ensureLineWrapped(lineView);
961 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
989 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
962 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
990 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px");
963 "px; width: " + dims.gutterTotalWidth + "px");
964 cm.display.input.setUneditable(gutterWrap);
991 cm.display.input.setUneditable(gutterWrap);
965 wrap.insertBefore(gutterWrap, lineView.text);
992 wrap.insertBefore(gutterWrap, lineView.text);
966 if (lineView.line.gutterClass)
993 if (lineView.line.gutterClass)
967 gutterWrap.className += " " + lineView.line.gutterClass;
994 gutterWrap.className += " " + lineView.line.gutterClass;
968 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
995 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
969 lineView.lineNumber = gutterWrap.appendChild(
996 lineView.lineNumber = gutterWrap.appendChild(
970 elt("div", lineNumberFor(cm.options, lineN),
997 elt("div", lineNumberFor(cm.options, lineN),
971 "CodeMirror-linenumber CodeMirror-gutter-elt",
998 "CodeMirror-linenumber CodeMirror-gutter-elt",
972 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
999 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
973 + cm.display.lineNumInnerWidth + "px"));
1000 + cm.display.lineNumInnerWidth + "px"));
974 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
1001 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
975 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
1002 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
976 if (found)
1003 if (found)
977 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
1004 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
978 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
1005 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
979 }
1006 }
980 }
1007 }
981 }
1008 }
982
1009
983 function updateLineWidgets(cm, lineView, dims) {
1010 function updateLineWidgets(cm, lineView, dims) {
984 if (lineView.alignable) lineView.alignable = null;
1011 if (lineView.alignable) lineView.alignable = null;
985 for (var node = lineView.node.firstChild, next; node; node = next) {
1012 for (var node = lineView.node.firstChild, next; node; node = next) {
986 var next = node.nextSibling;
1013 var next = node.nextSibling;
987 if (node.className == "CodeMirror-linewidget")
1014 if (node.className == "CodeMirror-linewidget")
988 lineView.node.removeChild(node);
1015 lineView.node.removeChild(node);
989 }
1016 }
990 insertLineWidgets(cm, lineView, dims);
1017 insertLineWidgets(cm, lineView, dims);
991 }
1018 }
992
1019
993 // Build a line's DOM representation from scratch
1020 // Build a line's DOM representation from scratch
994 function buildLineElement(cm, lineView, lineN, dims) {
1021 function buildLineElement(cm, lineView, lineN, dims) {
995 var built = getLineContent(cm, lineView);
1022 var built = getLineContent(cm, lineView);
996 lineView.text = lineView.node = built.pre;
1023 lineView.text = lineView.node = built.pre;
997 if (built.bgClass) lineView.bgClass = built.bgClass;
1024 if (built.bgClass) lineView.bgClass = built.bgClass;
998 if (built.textClass) lineView.textClass = built.textClass;
1025 if (built.textClass) lineView.textClass = built.textClass;
999
1026
1000 updateLineClasses(lineView);
1027 updateLineClasses(lineView);
1001 updateLineGutter(cm, lineView, lineN, dims);
1028 updateLineGutter(cm, lineView, lineN, dims);
1002 insertLineWidgets(cm, lineView, dims);
1029 insertLineWidgets(cm, lineView, dims);
1003 return lineView.node;
1030 return lineView.node;
1004 }
1031 }
1005
1032
1006 // A lineView may contain multiple logical lines (when merged by
1033 // A lineView may contain multiple logical lines (when merged by
1007 // collapsed spans). The widgets for all of them need to be drawn.
1034 // collapsed spans). The widgets for all of them need to be drawn.
1008 function insertLineWidgets(cm, lineView, dims) {
1035 function insertLineWidgets(cm, lineView, dims) {
1009 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1036 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1010 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1037 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1011 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1038 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1012 }
1039 }
1013
1040
1014 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1041 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1015 if (!line.widgets) return;
1042 if (!line.widgets) return;
1016 var wrap = ensureLineWrapped(lineView);
1043 var wrap = ensureLineWrapped(lineView);
1017 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1044 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1018 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1045 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1019 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1046 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1020 positionLineWidget(widget, node, lineView, dims);
1047 positionLineWidget(widget, node, lineView, dims);
1021 cm.display.input.setUneditable(node);
1048 cm.display.input.setUneditable(node);
1022 if (allowAbove && widget.above)
1049 if (allowAbove && widget.above)
1023 wrap.insertBefore(node, lineView.gutter || lineView.text);
1050 wrap.insertBefore(node, lineView.gutter || lineView.text);
1024 else
1051 else
1025 wrap.appendChild(node);
1052 wrap.appendChild(node);
1026 signalLater(widget, "redraw");
1053 signalLater(widget, "redraw");
1027 }
1054 }
1028 }
1055 }
1029
1056
1030 function positionLineWidget(widget, node, lineView, dims) {
1057 function positionLineWidget(widget, node, lineView, dims) {
1031 if (widget.noHScroll) {
1058 if (widget.noHScroll) {
1032 (lineView.alignable || (lineView.alignable = [])).push(node);
1059 (lineView.alignable || (lineView.alignable = [])).push(node);
1033 var width = dims.wrapperWidth;
1060 var width = dims.wrapperWidth;
1034 node.style.left = dims.fixedPos + "px";
1061 node.style.left = dims.fixedPos + "px";
1035 if (!widget.coverGutter) {
1062 if (!widget.coverGutter) {
1036 width -= dims.gutterTotalWidth;
1063 width -= dims.gutterTotalWidth;
1037 node.style.paddingLeft = dims.gutterTotalWidth + "px";
1064 node.style.paddingLeft = dims.gutterTotalWidth + "px";
1038 }
1065 }
1039 node.style.width = width + "px";
1066 node.style.width = width + "px";
1040 }
1067 }
1041 if (widget.coverGutter) {
1068 if (widget.coverGutter) {
1042 node.style.zIndex = 5;
1069 node.style.zIndex = 5;
1043 node.style.position = "relative";
1070 node.style.position = "relative";
1044 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1071 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1045 }
1072 }
1046 }
1073 }
1047
1074
1048 // POSITION OBJECT
1075 // POSITION OBJECT
1049
1076
1050 // A Pos instance represents a position within the text.
1077 // A Pos instance represents a position within the text.
1051 var Pos = CodeMirror.Pos = function(line, ch) {
1078 var Pos = CodeMirror.Pos = function(line, ch) {
1052 if (!(this instanceof Pos)) return new Pos(line, ch);
1079 if (!(this instanceof Pos)) return new Pos(line, ch);
1053 this.line = line; this.ch = ch;
1080 this.line = line; this.ch = ch;
1054 };
1081 };
1055
1082
1056 // Compare two positions, return 0 if they are the same, a negative
1083 // Compare two positions, return 0 if they are the same, a negative
1057 // number when a is less, and a positive number otherwise.
1084 // number when a is less, and a positive number otherwise.
1058 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1085 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1059
1086
1060 function copyPos(x) {return Pos(x.line, x.ch);}
1087 function copyPos(x) {return Pos(x.line, x.ch);}
1061 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1088 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1062 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1089 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1063
1090
1064 // INPUT HANDLING
1091 // INPUT HANDLING
1065
1092
1066 function ensureFocus(cm) {
1093 function ensureFocus(cm) {
1067 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1094 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1068 }
1095 }
1069
1096
1070 function isReadOnly(cm) {
1071 return cm.options.readOnly || cm.doc.cantEdit;
1072 }
1073
1074 // This will be set to an array of strings when copying, so that,
1097 // This will be set to an array of strings when copying, so that,
1075 // when pasting, we know what kind of selections the copied text
1098 // when pasting, we know what kind of selections the copied text
1076 // was made out of.
1099 // was made out of.
1077 var lastCopied = null;
1100 var lastCopied = null;
1078
1101
1079 function applyTextInput(cm, inserted, deleted, sel, origin) {
1102 function applyTextInput(cm, inserted, deleted, sel, origin) {
1080 var doc = cm.doc;
1103 var doc = cm.doc;
1081 cm.display.shift = false;
1104 cm.display.shift = false;
1082 if (!sel) sel = doc.sel;
1105 if (!sel) sel = doc.sel;
1083
1106
1084 var paste = cm.state.pasteIncoming || origin == "paste";
1107 var paste = cm.state.pasteIncoming || origin == "paste";
1085 var textLines = splitLines(inserted), multiPaste = null;
1108 var textLines = doc.splitLines(inserted), multiPaste = null;
1086 // When pasing N lines into N selections, insert one line per selection
1109 // When pasing N lines into N selections, insert one line per selection
1087 if (paste && sel.ranges.length > 1) {
1110 if (paste && sel.ranges.length > 1) {
1088 if (lastCopied && lastCopied.join("\n") == inserted)
1111 if (lastCopied && lastCopied.join("\n") == inserted) {
1089 multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
1112 if (sel.ranges.length % lastCopied.length == 0) {
1090 else if (textLines.length == sel.ranges.length)
1113 multiPaste = [];
1114 for (var i = 0; i < lastCopied.length; i++)
1115 multiPaste.push(doc.splitLines(lastCopied[i]));
1116 }
1117 } else if (textLines.length == sel.ranges.length) {
1091 multiPaste = map(textLines, function(l) { return [l]; });
1118 multiPaste = map(textLines, function(l) { return [l]; });
1119 }
1092 }
1120 }
1093
1121
1094 // Normal behavior is to insert the new text into every selection
1122 // Normal behavior is to insert the new text into every selection
1095 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1123 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1096 var range = sel.ranges[i];
1124 var range = sel.ranges[i];
1097 var from = range.from(), to = range.to();
1125 var from = range.from(), to = range.to();
1098 if (range.empty()) {
1126 if (range.empty()) {
1099 if (deleted && deleted > 0) // Handle deletion
1127 if (deleted && deleted > 0) // Handle deletion
1100 from = Pos(from.line, from.ch - deleted);
1128 from = Pos(from.line, from.ch - deleted);
1101 else if (cm.state.overwrite && !paste) // Handle overwrite
1129 else if (cm.state.overwrite && !paste) // Handle overwrite
1102 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1130 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1103 }
1131 }
1104 var updateInput = cm.curOp.updateInput;
1132 var updateInput = cm.curOp.updateInput;
1105 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1133 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1106 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1134 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1107 makeChange(cm.doc, changeEvent);
1135 makeChange(cm.doc, changeEvent);
1108 signalLater(cm, "inputRead", cm, changeEvent);
1136 signalLater(cm, "inputRead", cm, changeEvent);
1109 }
1137 }
1110 if (inserted && !paste)
1138 if (inserted && !paste)
1111 triggerElectric(cm, inserted);
1139 triggerElectric(cm, inserted);
1112
1140
1113 ensureCursorVisible(cm);
1141 ensureCursorVisible(cm);
1114 cm.curOp.updateInput = updateInput;
1142 cm.curOp.updateInput = updateInput;
1115 cm.curOp.typing = true;
1143 cm.curOp.typing = true;
1116 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1144 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1117 }
1145 }
1118
1146
1119 function handlePaste(e, cm) {
1147 function handlePaste(e, cm) {
1120 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1148 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1121 if (pasted) {
1149 if (pasted) {
1122 e.preventDefault();
1150 e.preventDefault();
1123 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
1151 if (!cm.isReadOnly() && !cm.options.disableInput)
1152 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
1124 return true;
1153 return true;
1125 }
1154 }
1126 }
1155 }
1127
1156
1128 function triggerElectric(cm, inserted) {
1157 function triggerElectric(cm, inserted) {
1129 // When an 'electric' character is inserted, immediately trigger a reindent
1158 // When an 'electric' character is inserted, immediately trigger a reindent
1130 if (!cm.options.electricChars || !cm.options.smartIndent) return;
1159 if (!cm.options.electricChars || !cm.options.smartIndent) return;
1131 var sel = cm.doc.sel;
1160 var sel = cm.doc.sel;
1132
1161
1133 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1162 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1134 var range = sel.ranges[i];
1163 var range = sel.ranges[i];
1135 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
1164 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
1136 var mode = cm.getModeAt(range.head);
1165 var mode = cm.getModeAt(range.head);
1137 var indented = false;
1166 var indented = false;
1138 if (mode.electricChars) {
1167 if (mode.electricChars) {
1139 for (var j = 0; j < mode.electricChars.length; j++)
1168 for (var j = 0; j < mode.electricChars.length; j++)
1140 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1169 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1141 indented = indentLine(cm, range.head.line, "smart");
1170 indented = indentLine(cm, range.head.line, "smart");
1142 break;
1171 break;
1143 }
1172 }
1144 } else if (mode.electricInput) {
1173 } else if (mode.electricInput) {
1145 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
1174 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
1146 indented = indentLine(cm, range.head.line, "smart");
1175 indented = indentLine(cm, range.head.line, "smart");
1147 }
1176 }
1148 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1177 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1149 }
1178 }
1150 }
1179 }
1151
1180
1152 function copyableRanges(cm) {
1181 function copyableRanges(cm) {
1153 var text = [], ranges = [];
1182 var text = [], ranges = [];
1154 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1183 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1155 var line = cm.doc.sel.ranges[i].head.line;
1184 var line = cm.doc.sel.ranges[i].head.line;
1156 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1185 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1157 ranges.push(lineRange);
1186 ranges.push(lineRange);
1158 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1187 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1159 }
1188 }
1160 return {text: text, ranges: ranges};
1189 return {text: text, ranges: ranges};
1161 }
1190 }
1162
1191
1163 function disableBrowserMagic(field) {
1192 function disableBrowserMagic(field) {
1164 field.setAttribute("autocorrect", "off");
1193 field.setAttribute("autocorrect", "off");
1165 field.setAttribute("autocapitalize", "off");
1194 field.setAttribute("autocapitalize", "off");
1166 field.setAttribute("spellcheck", "false");
1195 field.setAttribute("spellcheck", "false");
1167 }
1196 }
1168
1197
1169 // TEXTAREA INPUT STYLE
1198 // TEXTAREA INPUT STYLE
1170
1199
1171 function TextareaInput(cm) {
1200 function TextareaInput(cm) {
1172 this.cm = cm;
1201 this.cm = cm;
1173 // See input.poll and input.reset
1202 // See input.poll and input.reset
1174 this.prevInput = "";
1203 this.prevInput = "";
1175
1204
1176 // Flag that indicates whether we expect input to appear real soon
1205 // Flag that indicates whether we expect input to appear real soon
1177 // now (after some event like 'keypress' or 'input') and are
1206 // now (after some event like 'keypress' or 'input') and are
1178 // polling intensively.
1207 // polling intensively.
1179 this.pollingFast = false;
1208 this.pollingFast = false;
1180 // Self-resetting timeout for the poller
1209 // Self-resetting timeout for the poller
1181 this.polling = new Delayed();
1210 this.polling = new Delayed();
1182 // Tracks when input.reset has punted to just putting a short
1211 // Tracks when input.reset has punted to just putting a short
1183 // string into the textarea instead of the full selection.
1212 // string into the textarea instead of the full selection.
1184 this.inaccurateSelection = false;
1213 this.inaccurateSelection = false;
1185 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1214 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1186 this.hasSelection = false;
1215 this.hasSelection = false;
1187 this.composing = null;
1216 this.composing = null;
1188 };
1217 };
1189
1218
1190 function hiddenTextarea() {
1219 function hiddenTextarea() {
1191 var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1220 var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1192 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1221 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1193 // The textarea is kept positioned near the cursor to prevent the
1222 // The textarea is kept positioned near the cursor to prevent the
1194 // fact that it'll be scrolled into view on input from scrolling
1223 // fact that it'll be scrolled into view on input from scrolling
1195 // our fake cursor out of view. On webkit, when wrap=off, paste is
1224 // our fake cursor out of view. On webkit, when wrap=off, paste is
1196 // very slow. So make the area wide instead.
1225 // very slow. So make the area wide instead.
1197 if (webkit) te.style.width = "1000px";
1226 if (webkit) te.style.width = "1000px";
1198 else te.setAttribute("wrap", "off");
1227 else te.setAttribute("wrap", "off");
1199 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1228 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1200 if (ios) te.style.border = "1px solid black";
1229 if (ios) te.style.border = "1px solid black";
1201 disableBrowserMagic(te);
1230 disableBrowserMagic(te);
1202 return div;
1231 return div;
1203 }
1232 }
1204
1233
1205 TextareaInput.prototype = copyObj({
1234 TextareaInput.prototype = copyObj({
1206 init: function(display) {
1235 init: function(display) {
1207 var input = this, cm = this.cm;
1236 var input = this, cm = this.cm;
1208
1237
1209 // Wraps and hides input textarea
1238 // Wraps and hides input textarea
1210 var div = this.wrapper = hiddenTextarea();
1239 var div = this.wrapper = hiddenTextarea();
1211 // The semihidden textarea that is focused when the editor is
1240 // The semihidden textarea that is focused when the editor is
1212 // focused, and receives input.
1241 // focused, and receives input.
1213 var te = this.textarea = div.firstChild;
1242 var te = this.textarea = div.firstChild;
1214 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1243 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1215
1244
1216 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1245 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1217 if (ios) te.style.width = "0px";
1246 if (ios) te.style.width = "0px";
1218
1247
1219 on(te, "input", function() {
1248 on(te, "input", function() {
1220 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1249 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1221 input.poll();
1250 input.poll();
1222 });
1251 });
1223
1252
1224 on(te, "paste", function(e) {
1253 on(te, "paste", function(e) {
1225 if (handlePaste(e, cm)) return true;
1254 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
1226
1255
1227 cm.state.pasteIncoming = true;
1256 cm.state.pasteIncoming = true;
1228 input.fastPoll();
1257 input.fastPoll();
1229 });
1258 });
1230
1259
1231 function prepareCopyCut(e) {
1260 function prepareCopyCut(e) {
1261 if (signalDOMEvent(cm, e)) return
1232 if (cm.somethingSelected()) {
1262 if (cm.somethingSelected()) {
1233 lastCopied = cm.getSelections();
1263 lastCopied = cm.getSelections();
1234 if (input.inaccurateSelection) {
1264 if (input.inaccurateSelection) {
1235 input.prevInput = "";
1265 input.prevInput = "";
1236 input.inaccurateSelection = false;
1266 input.inaccurateSelection = false;
1237 te.value = lastCopied.join("\n");
1267 te.value = lastCopied.join("\n");
1238 selectInput(te);
1268 selectInput(te);
1239 }
1269 }
1240 } else if (!cm.options.lineWiseCopyCut) {
1270 } else if (!cm.options.lineWiseCopyCut) {
1241 return;
1271 return;
1242 } else {
1272 } else {
1243 var ranges = copyableRanges(cm);
1273 var ranges = copyableRanges(cm);
1244 lastCopied = ranges.text;
1274 lastCopied = ranges.text;
1245 if (e.type == "cut") {
1275 if (e.type == "cut") {
1246 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1276 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1247 } else {
1277 } else {
1248 input.prevInput = "";
1278 input.prevInput = "";
1249 te.value = ranges.text.join("\n");
1279 te.value = ranges.text.join("\n");
1250 selectInput(te);
1280 selectInput(te);
1251 }
1281 }
1252 }
1282 }
1253 if (e.type == "cut") cm.state.cutIncoming = true;
1283 if (e.type == "cut") cm.state.cutIncoming = true;
1254 }
1284 }
1255 on(te, "cut", prepareCopyCut);
1285 on(te, "cut", prepareCopyCut);
1256 on(te, "copy", prepareCopyCut);
1286 on(te, "copy", prepareCopyCut);
1257
1287
1258 on(display.scroller, "paste", function(e) {
1288 on(display.scroller, "paste", function(e) {
1259 if (eventInWidget(display, e)) return;
1289 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
1260 cm.state.pasteIncoming = true;
1290 cm.state.pasteIncoming = true;
1261 input.focus();
1291 input.focus();
1262 });
1292 });
1263
1293
1264 // Prevent normal selection in the editor (we handle our own)
1294 // Prevent normal selection in the editor (we handle our own)
1265 on(display.lineSpace, "selectstart", function(e) {
1295 on(display.lineSpace, "selectstart", function(e) {
1266 if (!eventInWidget(display, e)) e_preventDefault(e);
1296 if (!eventInWidget(display, e)) e_preventDefault(e);
1267 });
1297 });
1268
1298
1269 on(te, "compositionstart", function() {
1299 on(te, "compositionstart", function() {
1270 var start = cm.getCursor("from");
1300 var start = cm.getCursor("from");
1301 if (input.composing) input.composing.range.clear()
1271 input.composing = {
1302 input.composing = {
1272 start: start,
1303 start: start,
1273 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1304 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1274 };
1305 };
1275 });
1306 });
1276 on(te, "compositionend", function() {
1307 on(te, "compositionend", function() {
1277 if (input.composing) {
1308 if (input.composing) {
1278 input.poll();
1309 input.poll();
1279 input.composing.range.clear();
1310 input.composing.range.clear();
1280 input.composing = null;
1311 input.composing = null;
1281 }
1312 }
1282 });
1313 });
1283 },
1314 },
1284
1315
1285 prepareSelection: function() {
1316 prepareSelection: function() {
1286 // Redraw the selection and/or cursor
1317 // Redraw the selection and/or cursor
1287 var cm = this.cm, display = cm.display, doc = cm.doc;
1318 var cm = this.cm, display = cm.display, doc = cm.doc;
1288 var result = prepareSelection(cm);
1319 var result = prepareSelection(cm);
1289
1320
1290 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1321 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1291 if (cm.options.moveInputWithCursor) {
1322 if (cm.options.moveInputWithCursor) {
1292 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1323 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1293 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1324 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1294 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1325 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1295 headPos.top + lineOff.top - wrapOff.top));
1326 headPos.top + lineOff.top - wrapOff.top));
1296 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1327 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1297 headPos.left + lineOff.left - wrapOff.left));
1328 headPos.left + lineOff.left - wrapOff.left));
1298 }
1329 }
1299
1330
1300 return result;
1331 return result;
1301 },
1332 },
1302
1333
1303 showSelection: function(drawn) {
1334 showSelection: function(drawn) {
1304 var cm = this.cm, display = cm.display;
1335 var cm = this.cm, display = cm.display;
1305 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1336 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1306 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1337 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1307 if (drawn.teTop != null) {
1338 if (drawn.teTop != null) {
1308 this.wrapper.style.top = drawn.teTop + "px";
1339 this.wrapper.style.top = drawn.teTop + "px";
1309 this.wrapper.style.left = drawn.teLeft + "px";
1340 this.wrapper.style.left = drawn.teLeft + "px";
1310 }
1341 }
1311 },
1342 },
1312
1343
1313 // Reset the input to correspond to the selection (or to be empty,
1344 // Reset the input to correspond to the selection (or to be empty,
1314 // when not typing and nothing is selected)
1345 // when not typing and nothing is selected)
1315 reset: function(typing) {
1346 reset: function(typing) {
1316 if (this.contextMenuPending) return;
1347 if (this.contextMenuPending) return;
1317 var minimal, selected, cm = this.cm, doc = cm.doc;
1348 var minimal, selected, cm = this.cm, doc = cm.doc;
1318 if (cm.somethingSelected()) {
1349 if (cm.somethingSelected()) {
1319 this.prevInput = "";
1350 this.prevInput = "";
1320 var range = doc.sel.primary();
1351 var range = doc.sel.primary();
1321 minimal = hasCopyEvent &&
1352 minimal = hasCopyEvent &&
1322 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1353 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1323 var content = minimal ? "-" : selected || cm.getSelection();
1354 var content = minimal ? "-" : selected || cm.getSelection();
1324 this.textarea.value = content;
1355 this.textarea.value = content;
1325 if (cm.state.focused) selectInput(this.textarea);
1356 if (cm.state.focused) selectInput(this.textarea);
1326 if (ie && ie_version >= 9) this.hasSelection = content;
1357 if (ie && ie_version >= 9) this.hasSelection = content;
1327 } else if (!typing) {
1358 } else if (!typing) {
1328 this.prevInput = this.textarea.value = "";
1359 this.prevInput = this.textarea.value = "";
1329 if (ie && ie_version >= 9) this.hasSelection = null;
1360 if (ie && ie_version >= 9) this.hasSelection = null;
1330 }
1361 }
1331 this.inaccurateSelection = minimal;
1362 this.inaccurateSelection = minimal;
1332 },
1363 },
1333
1364
1334 getField: function() { return this.textarea; },
1365 getField: function() { return this.textarea; },
1335
1366
1336 supportsTouch: function() { return false; },
1367 supportsTouch: function() { return false; },
1337
1368
1338 focus: function() {
1369 focus: function() {
1339 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1370 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1340 try { this.textarea.focus(); }
1371 try { this.textarea.focus(); }
1341 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
1372 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
1342 }
1373 }
1343 },
1374 },
1344
1375
1345 blur: function() { this.textarea.blur(); },
1376 blur: function() { this.textarea.blur(); },
1346
1377
1347 resetPosition: function() {
1378 resetPosition: function() {
1348 this.wrapper.style.top = this.wrapper.style.left = 0;
1379 this.wrapper.style.top = this.wrapper.style.left = 0;
1349 },
1380 },
1350
1381
1351 receivedFocus: function() { this.slowPoll(); },
1382 receivedFocus: function() { this.slowPoll(); },
1352
1383
1353 // Poll for input changes, using the normal rate of polling. This
1384 // Poll for input changes, using the normal rate of polling. This
1354 // runs as long as the editor is focused.
1385 // runs as long as the editor is focused.
1355 slowPoll: function() {
1386 slowPoll: function() {
1356 var input = this;
1387 var input = this;
1357 if (input.pollingFast) return;
1388 if (input.pollingFast) return;
1358 input.polling.set(this.cm.options.pollInterval, function() {
1389 input.polling.set(this.cm.options.pollInterval, function() {
1359 input.poll();
1390 input.poll();
1360 if (input.cm.state.focused) input.slowPoll();
1391 if (input.cm.state.focused) input.slowPoll();
1361 });
1392 });
1362 },
1393 },
1363
1394
1364 // When an event has just come in that is likely to add or change
1395 // When an event has just come in that is likely to add or change
1365 // something in the input textarea, we poll faster, to ensure that
1396 // something in the input textarea, we poll faster, to ensure that
1366 // the change appears on the screen quickly.
1397 // the change appears on the screen quickly.
1367 fastPoll: function() {
1398 fastPoll: function() {
1368 var missed = false, input = this;
1399 var missed = false, input = this;
1369 input.pollingFast = true;
1400 input.pollingFast = true;
1370 function p() {
1401 function p() {
1371 var changed = input.poll();
1402 var changed = input.poll();
1372 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1403 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1373 else {input.pollingFast = false; input.slowPoll();}
1404 else {input.pollingFast = false; input.slowPoll();}
1374 }
1405 }
1375 input.polling.set(20, p);
1406 input.polling.set(20, p);
1376 },
1407 },
1377
1408
1378 // Read input from the textarea, and update the document to match.
1409 // Read input from the textarea, and update the document to match.
1379 // When something is selected, it is present in the textarea, and
1410 // When something is selected, it is present in the textarea, and
1380 // selected (unless it is huge, in which case a placeholder is
1411 // selected (unless it is huge, in which case a placeholder is
1381 // used). When nothing is selected, the cursor sits after previously
1412 // used). When nothing is selected, the cursor sits after previously
1382 // seen text (can be empty), which is stored in prevInput (we must
1413 // seen text (can be empty), which is stored in prevInput (we must
1383 // not reset the textarea when typing, because that breaks IME).
1414 // not reset the textarea when typing, because that breaks IME).
1384 poll: function() {
1415 poll: function() {
1385 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1416 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1386 // Since this is called a *lot*, try to bail out as cheaply as
1417 // Since this is called a *lot*, try to bail out as cheaply as
1387 // possible when it is clear that nothing happened. hasSelection
1418 // possible when it is clear that nothing happened. hasSelection
1388 // will be the case when there is a lot of text in the textarea,
1419 // will be the case when there is a lot of text in the textarea,
1389 // in which case reading its value would be expensive.
1420 // in which case reading its value would be expensive.
1390 if (this.contextMenuPending || !cm.state.focused ||
1421 if (this.contextMenuPending || !cm.state.focused ||
1391 (hasSelection(input) && !prevInput) ||
1422 (hasSelection(input) && !prevInput && !this.composing) ||
1392 isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
1423 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
1393 return false;
1424 return false;
1394
1425
1395 var text = input.value;
1426 var text = input.value;
1396 // If nothing changed, bail.
1427 // If nothing changed, bail.
1397 if (text == prevInput && !cm.somethingSelected()) return false;
1428 if (text == prevInput && !cm.somethingSelected()) return false;
1398 // Work around nonsensical selection resetting in IE9/10, and
1429 // Work around nonsensical selection resetting in IE9/10, and
1399 // inexplicable appearance of private area unicode characters on
1430 // inexplicable appearance of private area unicode characters on
1400 // some key combos in Mac (#2689).
1431 // some key combos in Mac (#2689).
1401 if (ie && ie_version >= 9 && this.hasSelection === text ||
1432 if (ie && ie_version >= 9 && this.hasSelection === text ||
1402 mac && /[\uf700-\uf7ff]/.test(text)) {
1433 mac && /[\uf700-\uf7ff]/.test(text)) {
1403 cm.display.input.reset();
1434 cm.display.input.reset();
1404 return false;
1435 return false;
1405 }
1436 }
1406
1437
1407 if (cm.doc.sel == cm.display.selForContextMenu) {
1438 if (cm.doc.sel == cm.display.selForContextMenu) {
1408 var first = text.charCodeAt(0);
1439 var first = text.charCodeAt(0);
1409 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1440 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1410 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1441 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1411 }
1442 }
1412 // Find the part of the input that is actually new
1443 // Find the part of the input that is actually new
1413 var same = 0, l = Math.min(prevInput.length, text.length);
1444 var same = 0, l = Math.min(prevInput.length, text.length);
1414 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1445 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1415
1446
1416 var self = this;
1447 var self = this;
1417 runInOp(cm, function() {
1448 runInOp(cm, function() {
1418 applyTextInput(cm, text.slice(same), prevInput.length - same,
1449 applyTextInput(cm, text.slice(same), prevInput.length - same,
1419 null, self.composing ? "*compose" : null);
1450 null, self.composing ? "*compose" : null);
1420
1451
1421 // Don't leave long text in the textarea, since it makes further polling slow
1452 // Don't leave long text in the textarea, since it makes further polling slow
1422 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1453 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1423 else self.prevInput = text;
1454 else self.prevInput = text;
1424
1455
1425 if (self.composing) {
1456 if (self.composing) {
1426 self.composing.range.clear();
1457 self.composing.range.clear();
1427 self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1458 self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1428 {className: "CodeMirror-composing"});
1459 {className: "CodeMirror-composing"});
1429 }
1460 }
1430 });
1461 });
1431 return true;
1462 return true;
1432 },
1463 },
1433
1464
1434 ensurePolled: function() {
1465 ensurePolled: function() {
1435 if (this.pollingFast && this.poll()) this.pollingFast = false;
1466 if (this.pollingFast && this.poll()) this.pollingFast = false;
1436 },
1467 },
1437
1468
1438 onKeyPress: function() {
1469 onKeyPress: function() {
1439 if (ie && ie_version >= 9) this.hasSelection = null;
1470 if (ie && ie_version >= 9) this.hasSelection = null;
1440 this.fastPoll();
1471 this.fastPoll();
1441 },
1472 },
1442
1473
1443 onContextMenu: function(e) {
1474 onContextMenu: function(e) {
1444 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1475 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1445 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1476 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1446 if (!pos || presto) return; // Opera is difficult.
1477 if (!pos || presto) return; // Opera is difficult.
1447
1478
1448 // Reset the current text selection only if the click is done outside of the selection
1479 // Reset the current text selection only if the click is done outside of the selection
1449 // and 'resetSelectionOnContextMenu' option is true.
1480 // and 'resetSelectionOnContextMenu' option is true.
1450 var reset = cm.options.resetSelectionOnContextMenu;
1481 var reset = cm.options.resetSelectionOnContextMenu;
1451 if (reset && cm.doc.sel.contains(pos) == -1)
1482 if (reset && cm.doc.sel.contains(pos) == -1)
1452 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1483 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1453
1484
1454 var oldCSS = te.style.cssText;
1485 var oldCSS = te.style.cssText;
1455 input.wrapper.style.position = "absolute";
1486 input.wrapper.style.position = "absolute";
1456 te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1487 te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1457 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1488 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1458 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1489 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1459 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1490 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1460 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1491 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1461 display.input.focus();
1492 display.input.focus();
1462 if (webkit) window.scrollTo(null, oldScrollY);
1493 if (webkit) window.scrollTo(null, oldScrollY);
1463 display.input.reset();
1494 display.input.reset();
1464 // Adds "Select all" to context menu in FF
1495 // Adds "Select all" to context menu in FF
1465 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1496 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1466 input.contextMenuPending = true;
1497 input.contextMenuPending = true;
1467 display.selForContextMenu = cm.doc.sel;
1498 display.selForContextMenu = cm.doc.sel;
1468 clearTimeout(display.detectingSelectAll);
1499 clearTimeout(display.detectingSelectAll);
1469
1500
1470 // Select-all will be greyed out if there's nothing to select, so
1501 // Select-all will be greyed out if there's nothing to select, so
1471 // this adds a zero-width space so that we can later check whether
1502 // this adds a zero-width space so that we can later check whether
1472 // it got selected.
1503 // it got selected.
1473 function prepareSelectAllHack() {
1504 function prepareSelectAllHack() {
1474 if (te.selectionStart != null) {
1505 if (te.selectionStart != null) {
1475 var selected = cm.somethingSelected();
1506 var selected = cm.somethingSelected();
1476 var extval = "\u200b" + (selected ? te.value : "");
1507 var extval = "\u200b" + (selected ? te.value : "");
1477 te.value = "\u21da"; // Used to catch context-menu undo
1508 te.value = "\u21da"; // Used to catch context-menu undo
1478 te.value = extval;
1509 te.value = extval;
1479 input.prevInput = selected ? "" : "\u200b";
1510 input.prevInput = selected ? "" : "\u200b";
1480 te.selectionStart = 1; te.selectionEnd = extval.length;
1511 te.selectionStart = 1; te.selectionEnd = extval.length;
1481 // Re-set this, in case some other handler touched the
1512 // Re-set this, in case some other handler touched the
1482 // selection in the meantime.
1513 // selection in the meantime.
1483 display.selForContextMenu = cm.doc.sel;
1514 display.selForContextMenu = cm.doc.sel;
1484 }
1515 }
1485 }
1516 }
1486 function rehide() {
1517 function rehide() {
1487 input.contextMenuPending = false;
1518 input.contextMenuPending = false;
1488 input.wrapper.style.position = "relative";
1519 input.wrapper.style.position = "relative";
1489 te.style.cssText = oldCSS;
1520 te.style.cssText = oldCSS;
1490 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1521 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1491
1522
1492 // Try to detect the user choosing select-all
1523 // Try to detect the user choosing select-all
1493 if (te.selectionStart != null) {
1524 if (te.selectionStart != null) {
1494 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1525 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1495 var i = 0, poll = function() {
1526 var i = 0, poll = function() {
1496 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1527 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1497 te.selectionEnd > 0 && input.prevInput == "\u200b")
1528 te.selectionEnd > 0 && input.prevInput == "\u200b")
1498 operation(cm, commands.selectAll)(cm);
1529 operation(cm, commands.selectAll)(cm);
1499 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1530 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1500 else display.input.reset();
1531 else display.input.reset();
1501 };
1532 };
1502 display.detectingSelectAll = setTimeout(poll, 200);
1533 display.detectingSelectAll = setTimeout(poll, 200);
1503 }
1534 }
1504 }
1535 }
1505
1536
1506 if (ie && ie_version >= 9) prepareSelectAllHack();
1537 if (ie && ie_version >= 9) prepareSelectAllHack();
1507 if (captureRightClick) {
1538 if (captureRightClick) {
1508 e_stop(e);
1539 e_stop(e);
1509 var mouseup = function() {
1540 var mouseup = function() {
1510 off(window, "mouseup", mouseup);
1541 off(window, "mouseup", mouseup);
1511 setTimeout(rehide, 20);
1542 setTimeout(rehide, 20);
1512 };
1543 };
1513 on(window, "mouseup", mouseup);
1544 on(window, "mouseup", mouseup);
1514 } else {
1545 } else {
1515 setTimeout(rehide, 50);
1546 setTimeout(rehide, 50);
1516 }
1547 }
1517 },
1548 },
1518
1549
1550 readOnlyChanged: function(val) {
1551 if (!val) this.reset();
1552 },
1553
1519 setUneditable: nothing,
1554 setUneditable: nothing,
1520
1555
1521 needsContentAttribute: false
1556 needsContentAttribute: false
1522 }, TextareaInput.prototype);
1557 }, TextareaInput.prototype);
1523
1558
1524 // CONTENTEDITABLE INPUT STYLE
1559 // CONTENTEDITABLE INPUT STYLE
1525
1560
1526 function ContentEditableInput(cm) {
1561 function ContentEditableInput(cm) {
1527 this.cm = cm;
1562 this.cm = cm;
1528 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1563 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1529 this.polling = new Delayed();
1564 this.polling = new Delayed();
1530 this.gracePeriod = false;
1565 this.gracePeriod = false;
1531 }
1566 }
1532
1567
1533 ContentEditableInput.prototype = copyObj({
1568 ContentEditableInput.prototype = copyObj({
1534 init: function(display) {
1569 init: function(display) {
1535 var input = this, cm = input.cm;
1570 var input = this, cm = input.cm;
1536 var div = input.div = display.lineDiv;
1571 var div = input.div = display.lineDiv;
1537 div.contentEditable = "true";
1538 disableBrowserMagic(div);
1572 disableBrowserMagic(div);
1539
1573
1540 on(div, "paste", function(e) { handlePaste(e, cm); })
1574 on(div, "paste", function(e) {
1575 if (!signalDOMEvent(cm, e)) handlePaste(e, cm);
1576 })
1541
1577
1542 on(div, "compositionstart", function(e) {
1578 on(div, "compositionstart", function(e) {
1543 var data = e.data;
1579 var data = e.data;
1544 input.composing = {sel: cm.doc.sel, data: data, startData: data};
1580 input.composing = {sel: cm.doc.sel, data: data, startData: data};
1545 if (!data) return;
1581 if (!data) return;
1546 var prim = cm.doc.sel.primary();
1582 var prim = cm.doc.sel.primary();
1547 var line = cm.getLine(prim.head.line);
1583 var line = cm.getLine(prim.head.line);
1548 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1584 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1549 if (found > -1 && found <= prim.head.ch)
1585 if (found > -1 && found <= prim.head.ch)
1550 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1586 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1551 Pos(prim.head.line, found + data.length));
1587 Pos(prim.head.line, found + data.length));
1552 });
1588 });
1553 on(div, "compositionupdate", function(e) {
1589 on(div, "compositionupdate", function(e) {
1554 input.composing.data = e.data;
1590 input.composing.data = e.data;
1555 });
1591 });
1556 on(div, "compositionend", function(e) {
1592 on(div, "compositionend", function(e) {
1557 var ours = input.composing;
1593 var ours = input.composing;
1558 if (!ours) return;
1594 if (!ours) return;
1559 if (e.data != ours.startData && !/\u200b/.test(e.data))
1595 if (e.data != ours.startData && !/\u200b/.test(e.data))
1560 ours.data = e.data;
1596 ours.data = e.data;
1561 // Need a small delay to prevent other code (input event,
1597 // Need a small delay to prevent other code (input event,
1562 // selection polling) from doing damage when fired right after
1598 // selection polling) from doing damage when fired right after
1563 // compositionend.
1599 // compositionend.
1564 setTimeout(function() {
1600 setTimeout(function() {
1565 if (!ours.handled)
1601 if (!ours.handled)
1566 input.applyComposition(ours);
1602 input.applyComposition(ours);
1567 if (input.composing == ours)
1603 if (input.composing == ours)
1568 input.composing = null;
1604 input.composing = null;
1569 }, 50);
1605 }, 50);
1570 });
1606 });
1571
1607
1572 on(div, "touchstart", function() {
1608 on(div, "touchstart", function() {
1573 input.forceCompositionEnd();
1609 input.forceCompositionEnd();
1574 });
1610 });
1575
1611
1576 on(div, "input", function() {
1612 on(div, "input", function() {
1577 if (input.composing) return;
1613 if (input.composing) return;
1578 if (!input.pollContent())
1614 if (cm.isReadOnly() || !input.pollContent())
1579 runInOp(input.cm, function() {regChange(cm);});
1615 runInOp(input.cm, function() {regChange(cm);});
1580 });
1616 });
1581
1617
1582 function onCopyCut(e) {
1618 function onCopyCut(e) {
1619 if (signalDOMEvent(cm, e)) return
1583 if (cm.somethingSelected()) {
1620 if (cm.somethingSelected()) {
1584 lastCopied = cm.getSelections();
1621 lastCopied = cm.getSelections();
1585 if (e.type == "cut") cm.replaceSelection("", null, "cut");
1622 if (e.type == "cut") cm.replaceSelection("", null, "cut");
1586 } else if (!cm.options.lineWiseCopyCut) {
1623 } else if (!cm.options.lineWiseCopyCut) {
1587 return;
1624 return;
1588 } else {
1625 } else {
1589 var ranges = copyableRanges(cm);
1626 var ranges = copyableRanges(cm);
1590 lastCopied = ranges.text;
1627 lastCopied = ranges.text;
1591 if (e.type == "cut") {
1628 if (e.type == "cut") {
1592 cm.operation(function() {
1629 cm.operation(function() {
1593 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1630 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1594 cm.replaceSelection("", null, "cut");
1631 cm.replaceSelection("", null, "cut");
1595 });
1632 });
1596 }
1633 }
1597 }
1634 }
1598 // iOS exposes the clipboard API, but seems to discard content inserted into it
1635 // iOS exposes the clipboard API, but seems to discard content inserted into it
1599 if (e.clipboardData && !ios) {
1636 if (e.clipboardData && !ios) {
1600 e.preventDefault();
1637 e.preventDefault();
1601 e.clipboardData.clearData();
1638 e.clipboardData.clearData();
1602 e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1639 e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1603 } else {
1640 } else {
1604 // Old-fashioned briefly-focus-a-textarea hack
1641 // Old-fashioned briefly-focus-a-textarea hack
1605 var kludge = hiddenTextarea(), te = kludge.firstChild;
1642 var kludge = hiddenTextarea(), te = kludge.firstChild;
1606 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1643 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1607 te.value = lastCopied.join("\n");
1644 te.value = lastCopied.join("\n");
1608 var hadFocus = document.activeElement;
1645 var hadFocus = document.activeElement;
1609 selectInput(te);
1646 selectInput(te);
1610 setTimeout(function() {
1647 setTimeout(function() {
1611 cm.display.lineSpace.removeChild(kludge);
1648 cm.display.lineSpace.removeChild(kludge);
1612 hadFocus.focus();
1649 hadFocus.focus();
1613 }, 50);
1650 }, 50);
1614 }
1651 }
1615 }
1652 }
1616 on(div, "copy", onCopyCut);
1653 on(div, "copy", onCopyCut);
1617 on(div, "cut", onCopyCut);
1654 on(div, "cut", onCopyCut);
1618 },
1655 },
1619
1656
1620 prepareSelection: function() {
1657 prepareSelection: function() {
1621 var result = prepareSelection(this.cm, false);
1658 var result = prepareSelection(this.cm, false);
1622 result.focus = this.cm.state.focused;
1659 result.focus = this.cm.state.focused;
1623 return result;
1660 return result;
1624 },
1661 },
1625
1662
1626 showSelection: function(info) {
1663 showSelection: function(info) {
1627 if (!info || !this.cm.display.view.length) return;
1664 if (!info || !this.cm.display.view.length) return;
1628 if (info.focus) this.showPrimarySelection();
1665 if (info.focus) this.showPrimarySelection();
1629 this.showMultipleSelections(info);
1666 this.showMultipleSelections(info);
1630 },
1667 },
1631
1668
1632 showPrimarySelection: function() {
1669 showPrimarySelection: function() {
1633 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1670 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1634 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1671 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1635 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1672 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1636 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1673 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1637 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1674 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1638 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1675 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1639 return;
1676 return;
1640
1677
1641 var start = posToDOM(this.cm, prim.from());
1678 var start = posToDOM(this.cm, prim.from());
1642 var end = posToDOM(this.cm, prim.to());
1679 var end = posToDOM(this.cm, prim.to());
1643 if (!start && !end) return;
1680 if (!start && !end) return;
1644
1681
1645 var view = this.cm.display.view;
1682 var view = this.cm.display.view;
1646 var old = sel.rangeCount && sel.getRangeAt(0);
1683 var old = sel.rangeCount && sel.getRangeAt(0);
1647 if (!start) {
1684 if (!start) {
1648 start = {node: view[0].measure.map[2], offset: 0};
1685 start = {node: view[0].measure.map[2], offset: 0};
1649 } else if (!end) { // FIXME dangerously hacky
1686 } else if (!end) { // FIXME dangerously hacky
1650 var measure = view[view.length - 1].measure;
1687 var measure = view[view.length - 1].measure;
1651 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1688 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1652 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1689 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1653 }
1690 }
1654
1691
1655 try { var rng = range(start.node, start.offset, end.offset, end.node); }
1692 try { var rng = range(start.node, start.offset, end.offset, end.node); }
1656 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
1693 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
1657 if (rng) {
1694 if (rng) {
1658 sel.removeAllRanges();
1695 if (!gecko && this.cm.state.focused) {
1659 sel.addRange(rng);
1696 sel.collapse(start.node, start.offset);
1697 if (!rng.collapsed) sel.addRange(rng);
1698 } else {
1699 sel.removeAllRanges();
1700 sel.addRange(rng);
1701 }
1660 if (old && sel.anchorNode == null) sel.addRange(old);
1702 if (old && sel.anchorNode == null) sel.addRange(old);
1661 else if (gecko) this.startGracePeriod();
1703 else if (gecko) this.startGracePeriod();
1662 }
1704 }
1663 this.rememberSelection();
1705 this.rememberSelection();
1664 },
1706 },
1665
1707
1666 startGracePeriod: function() {
1708 startGracePeriod: function() {
1667 var input = this;
1709 var input = this;
1668 clearTimeout(this.gracePeriod);
1710 clearTimeout(this.gracePeriod);
1669 this.gracePeriod = setTimeout(function() {
1711 this.gracePeriod = setTimeout(function() {
1670 input.gracePeriod = false;
1712 input.gracePeriod = false;
1671 if (input.selectionChanged())
1713 if (input.selectionChanged())
1672 input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1714 input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1673 }, 20);
1715 }, 20);
1674 },
1716 },
1675
1717
1676 showMultipleSelections: function(info) {
1718 showMultipleSelections: function(info) {
1677 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1719 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1678 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1720 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1679 },
1721 },
1680
1722
1681 rememberSelection: function() {
1723 rememberSelection: function() {
1682 var sel = window.getSelection();
1724 var sel = window.getSelection();
1683 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1725 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1684 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1726 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1685 },
1727 },
1686
1728
1687 selectionInEditor: function() {
1729 selectionInEditor: function() {
1688 var sel = window.getSelection();
1730 var sel = window.getSelection();
1689 if (!sel.rangeCount) return false;
1731 if (!sel.rangeCount) return false;
1690 var node = sel.getRangeAt(0).commonAncestorContainer;
1732 var node = sel.getRangeAt(0).commonAncestorContainer;
1691 return contains(this.div, node);
1733 return contains(this.div, node);
1692 },
1734 },
1693
1735
1694 focus: function() {
1736 focus: function() {
1695 if (this.cm.options.readOnly != "nocursor") this.div.focus();
1737 if (this.cm.options.readOnly != "nocursor") this.div.focus();
1696 },
1738 },
1697 blur: function() { this.div.blur(); },
1739 blur: function() { this.div.blur(); },
1698 getField: function() { return this.div; },
1740 getField: function() { return this.div; },
1699
1741
1700 supportsTouch: function() { return true; },
1742 supportsTouch: function() { return true; },
1701
1743
1702 receivedFocus: function() {
1744 receivedFocus: function() {
1703 var input = this;
1745 var input = this;
1704 if (this.selectionInEditor())
1746 if (this.selectionInEditor())
1705 this.pollSelection();
1747 this.pollSelection();
1706 else
1748 else
1707 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1749 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1708
1750
1709 function poll() {
1751 function poll() {
1710 if (input.cm.state.focused) {
1752 if (input.cm.state.focused) {
1711 input.pollSelection();
1753 input.pollSelection();
1712 input.polling.set(input.cm.options.pollInterval, poll);
1754 input.polling.set(input.cm.options.pollInterval, poll);
1713 }
1755 }
1714 }
1756 }
1715 this.polling.set(this.cm.options.pollInterval, poll);
1757 this.polling.set(this.cm.options.pollInterval, poll);
1716 },
1758 },
1717
1759
1718 selectionChanged: function() {
1760 selectionChanged: function() {
1719 var sel = window.getSelection();
1761 var sel = window.getSelection();
1720 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1762 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1721 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1763 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1722 },
1764 },
1723
1765
1724 pollSelection: function() {
1766 pollSelection: function() {
1725 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1767 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1726 var sel = window.getSelection(), cm = this.cm;
1768 var sel = window.getSelection(), cm = this.cm;
1727 this.rememberSelection();
1769 this.rememberSelection();
1728 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1770 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1729 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1771 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1730 if (anchor && head) runInOp(cm, function() {
1772 if (anchor && head) runInOp(cm, function() {
1731 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1773 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1732 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1774 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1733 });
1775 });
1734 }
1776 }
1735 },
1777 },
1736
1778
1737 pollContent: function() {
1779 pollContent: function() {
1738 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1780 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1739 var from = sel.from(), to = sel.to();
1781 var from = sel.from(), to = sel.to();
1740 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1782 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1741
1783
1742 var fromIndex;
1784 var fromIndex;
1743 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
1785 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
1744 var fromLine = lineNo(display.view[0].line);
1786 var fromLine = lineNo(display.view[0].line);
1745 var fromNode = display.view[0].node;
1787 var fromNode = display.view[0].node;
1746 } else {
1788 } else {
1747 var fromLine = lineNo(display.view[fromIndex].line);
1789 var fromLine = lineNo(display.view[fromIndex].line);
1748 var fromNode = display.view[fromIndex - 1].node.nextSibling;
1790 var fromNode = display.view[fromIndex - 1].node.nextSibling;
1749 }
1791 }
1750 var toIndex = findViewIndex(cm, to.line);
1792 var toIndex = findViewIndex(cm, to.line);
1751 if (toIndex == display.view.length - 1) {
1793 if (toIndex == display.view.length - 1) {
1752 var toLine = display.viewTo - 1;
1794 var toLine = display.viewTo - 1;
1753 var toNode = display.lineDiv.lastChild;
1795 var toNode = display.lineDiv.lastChild;
1754 } else {
1796 } else {
1755 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1797 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1756 var toNode = display.view[toIndex + 1].node.previousSibling;
1798 var toNode = display.view[toIndex + 1].node.previousSibling;
1757 }
1799 }
1758
1800
1759 var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1801 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1760 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1802 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1761 while (newText.length > 1 && oldText.length > 1) {
1803 while (newText.length > 1 && oldText.length > 1) {
1762 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1804 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1763 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1805 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1764 else break;
1806 else break;
1765 }
1807 }
1766
1808
1767 var cutFront = 0, cutEnd = 0;
1809 var cutFront = 0, cutEnd = 0;
1768 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1810 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1769 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1811 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1770 ++cutFront;
1812 ++cutFront;
1771 var newBot = lst(newText), oldBot = lst(oldText);
1813 var newBot = lst(newText), oldBot = lst(oldText);
1772 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1814 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1773 oldBot.length - (oldText.length == 1 ? cutFront : 0));
1815 oldBot.length - (oldText.length == 1 ? cutFront : 0));
1774 while (cutEnd < maxCutEnd &&
1816 while (cutEnd < maxCutEnd &&
1775 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1817 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1776 ++cutEnd;
1818 ++cutEnd;
1777
1819
1778 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1820 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1779 newText[0] = newText[0].slice(cutFront);
1821 newText[0] = newText[0].slice(cutFront);
1780
1822
1781 var chFrom = Pos(fromLine, cutFront);
1823 var chFrom = Pos(fromLine, cutFront);
1782 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1824 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1783 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1825 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1784 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1826 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1785 return true;
1827 return true;
1786 }
1828 }
1787 },
1829 },
1788
1830
1789 ensurePolled: function() {
1831 ensurePolled: function() {
1790 this.forceCompositionEnd();
1832 this.forceCompositionEnd();
1791 },
1833 },
1792 reset: function() {
1834 reset: function() {
1793 this.forceCompositionEnd();
1835 this.forceCompositionEnd();
1794 },
1836 },
1795 forceCompositionEnd: function() {
1837 forceCompositionEnd: function() {
1796 if (!this.composing || this.composing.handled) return;
1838 if (!this.composing || this.composing.handled) return;
1797 this.applyComposition(this.composing);
1839 this.applyComposition(this.composing);
1798 this.composing.handled = true;
1840 this.composing.handled = true;
1799 this.div.blur();
1841 this.div.blur();
1800 this.div.focus();
1842 this.div.focus();
1801 },
1843 },
1802 applyComposition: function(composing) {
1844 applyComposition: function(composing) {
1803 if (composing.data && composing.data != composing.startData)
1845 if (this.cm.isReadOnly())
1846 operation(this.cm, regChange)(this.cm)
1847 else if (composing.data && composing.data != composing.startData)
1804 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1848 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1805 },
1849 },
1806
1850
1807 setUneditable: function(node) {
1851 setUneditable: function(node) {
1808 node.setAttribute("contenteditable", "false");
1852 node.contentEditable = "false"
1809 },
1853 },
1810
1854
1811 onKeyPress: function(e) {
1855 onKeyPress: function(e) {
1812 e.preventDefault();
1856 e.preventDefault();
1813 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
1857 if (!this.cm.isReadOnly())
1858 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
1859 },
1860
1861 readOnlyChanged: function(val) {
1862 this.div.contentEditable = String(val != "nocursor")
1814 },
1863 },
1815
1864
1816 onContextMenu: nothing,
1865 onContextMenu: nothing,
1817 resetPosition: nothing,
1866 resetPosition: nothing,
1818
1867
1819 needsContentAttribute: true
1868 needsContentAttribute: true
1820 }, ContentEditableInput.prototype);
1869 }, ContentEditableInput.prototype);
1821
1870
1822 function posToDOM(cm, pos) {
1871 function posToDOM(cm, pos) {
1823 var view = findViewForLine(cm, pos.line);
1872 var view = findViewForLine(cm, pos.line);
1824 if (!view || view.hidden) return null;
1873 if (!view || view.hidden) return null;
1825 var line = getLine(cm.doc, pos.line);
1874 var line = getLine(cm.doc, pos.line);
1826 var info = mapFromLineView(view, line, pos.line);
1875 var info = mapFromLineView(view, line, pos.line);
1827
1876
1828 var order = getOrder(line), side = "left";
1877 var order = getOrder(line), side = "left";
1829 if (order) {
1878 if (order) {
1830 var partPos = getBidiPartAt(order, pos.ch);
1879 var partPos = getBidiPartAt(order, pos.ch);
1831 side = partPos % 2 ? "right" : "left";
1880 side = partPos % 2 ? "right" : "left";
1832 }
1881 }
1833 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1882 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1834 result.offset = result.collapse == "right" ? result.end : result.start;
1883 result.offset = result.collapse == "right" ? result.end : result.start;
1835 return result;
1884 return result;
1836 }
1885 }
1837
1886
1838 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1887 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1839
1888
1840 function domToPos(cm, node, offset) {
1889 function domToPos(cm, node, offset) {
1841 var lineNode;
1890 var lineNode;
1842 if (node == cm.display.lineDiv) {
1891 if (node == cm.display.lineDiv) {
1843 lineNode = cm.display.lineDiv.childNodes[offset];
1892 lineNode = cm.display.lineDiv.childNodes[offset];
1844 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1893 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1845 node = null; offset = 0;
1894 node = null; offset = 0;
1846 } else {
1895 } else {
1847 for (lineNode = node;; lineNode = lineNode.parentNode) {
1896 for (lineNode = node;; lineNode = lineNode.parentNode) {
1848 if (!lineNode || lineNode == cm.display.lineDiv) return null;
1897 if (!lineNode || lineNode == cm.display.lineDiv) return null;
1849 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1898 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1850 }
1899 }
1851 }
1900 }
1852 for (var i = 0; i < cm.display.view.length; i++) {
1901 for (var i = 0; i < cm.display.view.length; i++) {
1853 var lineView = cm.display.view[i];
1902 var lineView = cm.display.view[i];
1854 if (lineView.node == lineNode)
1903 if (lineView.node == lineNode)
1855 return locateNodeInLineView(lineView, node, offset);
1904 return locateNodeInLineView(lineView, node, offset);
1856 }
1905 }
1857 }
1906 }
1858
1907
1859 function locateNodeInLineView(lineView, node, offset) {
1908 function locateNodeInLineView(lineView, node, offset) {
1860 var wrapper = lineView.text.firstChild, bad = false;
1909 var wrapper = lineView.text.firstChild, bad = false;
1861 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1910 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1862 if (node == wrapper) {
1911 if (node == wrapper) {
1863 bad = true;
1912 bad = true;
1864 node = wrapper.childNodes[offset];
1913 node = wrapper.childNodes[offset];
1865 offset = 0;
1914 offset = 0;
1866 if (!node) {
1915 if (!node) {
1867 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1916 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1868 return badPos(Pos(lineNo(line), line.text.length), bad);
1917 return badPos(Pos(lineNo(line), line.text.length), bad);
1869 }
1918 }
1870 }
1919 }
1871
1920
1872 var textNode = node.nodeType == 3 ? node : null, topNode = node;
1921 var textNode = node.nodeType == 3 ? node : null, topNode = node;
1873 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1922 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1874 textNode = node.firstChild;
1923 textNode = node.firstChild;
1875 if (offset) offset = textNode.nodeValue.length;
1924 if (offset) offset = textNode.nodeValue.length;
1876 }
1925 }
1877 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1926 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1878 var measure = lineView.measure, maps = measure.maps;
1927 var measure = lineView.measure, maps = measure.maps;
1879
1928
1880 function find(textNode, topNode, offset) {
1929 function find(textNode, topNode, offset) {
1881 for (var i = -1; i < (maps ? maps.length : 0); i++) {
1930 for (var i = -1; i < (maps ? maps.length : 0); i++) {
1882 var map = i < 0 ? measure.map : maps[i];
1931 var map = i < 0 ? measure.map : maps[i];
1883 for (var j = 0; j < map.length; j += 3) {
1932 for (var j = 0; j < map.length; j += 3) {
1884 var curNode = map[j + 2];
1933 var curNode = map[j + 2];
1885 if (curNode == textNode || curNode == topNode) {
1934 if (curNode == textNode || curNode == topNode) {
1886 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1935 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1887 var ch = map[j] + offset;
1936 var ch = map[j] + offset;
1888 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1937 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1889 return Pos(line, ch);
1938 return Pos(line, ch);
1890 }
1939 }
1891 }
1940 }
1892 }
1941 }
1893 }
1942 }
1894 var found = find(textNode, topNode, offset);
1943 var found = find(textNode, topNode, offset);
1895 if (found) return badPos(found, bad);
1944 if (found) return badPos(found, bad);
1896
1945
1897 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1946 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1898 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1947 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1899 found = find(after, after.firstChild, 0);
1948 found = find(after, after.firstChild, 0);
1900 if (found)
1949 if (found)
1901 return badPos(Pos(found.line, found.ch - dist), bad);
1950 return badPos(Pos(found.line, found.ch - dist), bad);
1902 else
1951 else
1903 dist += after.textContent.length;
1952 dist += after.textContent.length;
1904 }
1953 }
1905 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
1954 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
1906 found = find(before, before.firstChild, -1);
1955 found = find(before, before.firstChild, -1);
1907 if (found)
1956 if (found)
1908 return badPos(Pos(found.line, found.ch + dist), bad);
1957 return badPos(Pos(found.line, found.ch + dist), bad);
1909 else
1958 else
1910 dist += after.textContent.length;
1959 dist += after.textContent.length;
1911 }
1960 }
1912 }
1961 }
1913
1962
1914 function domTextBetween(cm, from, to, fromLine, toLine) {
1963 function domTextBetween(cm, from, to, fromLine, toLine) {
1915 var text = "", closing = false;
1964 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
1916 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1965 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1917 function walk(node) {
1966 function walk(node) {
1918 if (node.nodeType == 1) {
1967 if (node.nodeType == 1) {
1919 var cmText = node.getAttribute("cm-text");
1968 var cmText = node.getAttribute("cm-text");
1920 if (cmText != null) {
1969 if (cmText != null) {
1921 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1970 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1922 text += cmText;
1971 text += cmText;
1923 return;
1972 return;
1924 }
1973 }
1925 var markerID = node.getAttribute("cm-marker"), range;
1974 var markerID = node.getAttribute("cm-marker"), range;
1926 if (markerID) {
1975 if (markerID) {
1927 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1976 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1928 if (found.length && (range = found[0].find()))
1977 if (found.length && (range = found[0].find()))
1929 text += getBetween(cm.doc, range.from, range.to).join("\n");
1978 text += getBetween(cm.doc, range.from, range.to).join(lineSep);
1930 return;
1979 return;
1931 }
1980 }
1932 if (node.getAttribute("contenteditable") == "false") return;
1981 if (node.getAttribute("contenteditable") == "false") return;
1933 for (var i = 0; i < node.childNodes.length; i++)
1982 for (var i = 0; i < node.childNodes.length; i++)
1934 walk(node.childNodes[i]);
1983 walk(node.childNodes[i]);
1935 if (/^(pre|div|p)$/i.test(node.nodeName))
1984 if (/^(pre|div|p)$/i.test(node.nodeName))
1936 closing = true;
1985 closing = true;
1937 } else if (node.nodeType == 3) {
1986 } else if (node.nodeType == 3) {
1938 var val = node.nodeValue;
1987 var val = node.nodeValue;
1939 if (!val) return;
1988 if (!val) return;
1940 if (closing) {
1989 if (closing) {
1941 text += "\n";
1990 text += lineSep;
1942 closing = false;
1991 closing = false;
1943 }
1992 }
1944 text += val;
1993 text += val;
1945 }
1994 }
1946 }
1995 }
1947 for (;;) {
1996 for (;;) {
1948 walk(from);
1997 walk(from);
1949 if (from == to) break;
1998 if (from == to) break;
1950 from = from.nextSibling;
1999 from = from.nextSibling;
1951 }
2000 }
1952 return text;
2001 return text;
1953 }
2002 }
1954
2003
1955 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
2004 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
1956
2005
1957 // SELECTION / CURSOR
2006 // SELECTION / CURSOR
1958
2007
1959 // Selection objects are immutable. A new one is created every time
2008 // Selection objects are immutable. A new one is created every time
1960 // the selection changes. A selection is one or more non-overlapping
2009 // the selection changes. A selection is one or more non-overlapping
1961 // (and non-touching) ranges, sorted, and an integer that indicates
2010 // (and non-touching) ranges, sorted, and an integer that indicates
1962 // which one is the primary selection (the one that's scrolled into
2011 // which one is the primary selection (the one that's scrolled into
1963 // view, that getCursor returns, etc).
2012 // view, that getCursor returns, etc).
1964 function Selection(ranges, primIndex) {
2013 function Selection(ranges, primIndex) {
1965 this.ranges = ranges;
2014 this.ranges = ranges;
1966 this.primIndex = primIndex;
2015 this.primIndex = primIndex;
1967 }
2016 }
1968
2017
1969 Selection.prototype = {
2018 Selection.prototype = {
1970 primary: function() { return this.ranges[this.primIndex]; },
2019 primary: function() { return this.ranges[this.primIndex]; },
1971 equals: function(other) {
2020 equals: function(other) {
1972 if (other == this) return true;
2021 if (other == this) return true;
1973 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
2022 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
1974 for (var i = 0; i < this.ranges.length; i++) {
2023 for (var i = 0; i < this.ranges.length; i++) {
1975 var here = this.ranges[i], there = other.ranges[i];
2024 var here = this.ranges[i], there = other.ranges[i];
1976 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
2025 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
1977 }
2026 }
1978 return true;
2027 return true;
1979 },
2028 },
1980 deepCopy: function() {
2029 deepCopy: function() {
1981 for (var out = [], i = 0; i < this.ranges.length; i++)
2030 for (var out = [], i = 0; i < this.ranges.length; i++)
1982 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
2031 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
1983 return new Selection(out, this.primIndex);
2032 return new Selection(out, this.primIndex);
1984 },
2033 },
1985 somethingSelected: function() {
2034 somethingSelected: function() {
1986 for (var i = 0; i < this.ranges.length; i++)
2035 for (var i = 0; i < this.ranges.length; i++)
1987 if (!this.ranges[i].empty()) return true;
2036 if (!this.ranges[i].empty()) return true;
1988 return false;
2037 return false;
1989 },
2038 },
1990 contains: function(pos, end) {
2039 contains: function(pos, end) {
1991 if (!end) end = pos;
2040 if (!end) end = pos;
1992 for (var i = 0; i < this.ranges.length; i++) {
2041 for (var i = 0; i < this.ranges.length; i++) {
1993 var range = this.ranges[i];
2042 var range = this.ranges[i];
1994 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2043 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
1995 return i;
2044 return i;
1996 }
2045 }
1997 return -1;
2046 return -1;
1998 }
2047 }
1999 };
2048 };
2000
2049
2001 function Range(anchor, head) {
2050 function Range(anchor, head) {
2002 this.anchor = anchor; this.head = head;
2051 this.anchor = anchor; this.head = head;
2003 }
2052 }
2004
2053
2005 Range.prototype = {
2054 Range.prototype = {
2006 from: function() { return minPos(this.anchor, this.head); },
2055 from: function() { return minPos(this.anchor, this.head); },
2007 to: function() { return maxPos(this.anchor, this.head); },
2056 to: function() { return maxPos(this.anchor, this.head); },
2008 empty: function() {
2057 empty: function() {
2009 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2058 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2010 }
2059 }
2011 };
2060 };
2012
2061
2013 // Take an unsorted, potentially overlapping set of ranges, and
2062 // Take an unsorted, potentially overlapping set of ranges, and
2014 // build a selection out of it. 'Consumes' ranges array (modifying
2063 // build a selection out of it. 'Consumes' ranges array (modifying
2015 // it).
2064 // it).
2016 function normalizeSelection(ranges, primIndex) {
2065 function normalizeSelection(ranges, primIndex) {
2017 var prim = ranges[primIndex];
2066 var prim = ranges[primIndex];
2018 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2067 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2019 primIndex = indexOf(ranges, prim);
2068 primIndex = indexOf(ranges, prim);
2020 for (var i = 1; i < ranges.length; i++) {
2069 for (var i = 1; i < ranges.length; i++) {
2021 var cur = ranges[i], prev = ranges[i - 1];
2070 var cur = ranges[i], prev = ranges[i - 1];
2022 if (cmp(prev.to(), cur.from()) >= 0) {
2071 if (cmp(prev.to(), cur.from()) >= 0) {
2023 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2072 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2024 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2073 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2025 if (i <= primIndex) --primIndex;
2074 if (i <= primIndex) --primIndex;
2026 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2075 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2027 }
2076 }
2028 }
2077 }
2029 return new Selection(ranges, primIndex);
2078 return new Selection(ranges, primIndex);
2030 }
2079 }
2031
2080
2032 function simpleSelection(anchor, head) {
2081 function simpleSelection(anchor, head) {
2033 return new Selection([new Range(anchor, head || anchor)], 0);
2082 return new Selection([new Range(anchor, head || anchor)], 0);
2034 }
2083 }
2035
2084
2036 // Most of the external API clips given positions to make sure they
2085 // Most of the external API clips given positions to make sure they
2037 // actually exist within the document.
2086 // actually exist within the document.
2038 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2087 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2039 function clipPos(doc, pos) {
2088 function clipPos(doc, pos) {
2040 if (pos.line < doc.first) return Pos(doc.first, 0);
2089 if (pos.line < doc.first) return Pos(doc.first, 0);
2041 var last = doc.first + doc.size - 1;
2090 var last = doc.first + doc.size - 1;
2042 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2091 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2043 return clipToLen(pos, getLine(doc, pos.line).text.length);
2092 return clipToLen(pos, getLine(doc, pos.line).text.length);
2044 }
2093 }
2045 function clipToLen(pos, linelen) {
2094 function clipToLen(pos, linelen) {
2046 var ch = pos.ch;
2095 var ch = pos.ch;
2047 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2096 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2048 else if (ch < 0) return Pos(pos.line, 0);
2097 else if (ch < 0) return Pos(pos.line, 0);
2049 else return pos;
2098 else return pos;
2050 }
2099 }
2051 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2100 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2052 function clipPosArray(doc, array) {
2101 function clipPosArray(doc, array) {
2053 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2102 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2054 return out;
2103 return out;
2055 }
2104 }
2056
2105
2057 // SELECTION UPDATES
2106 // SELECTION UPDATES
2058
2107
2059 // The 'scroll' parameter given to many of these indicated whether
2108 // The 'scroll' parameter given to many of these indicated whether
2060 // the new cursor position should be scrolled into view after
2109 // the new cursor position should be scrolled into view after
2061 // modifying the selection.
2110 // modifying the selection.
2062
2111
2063 // If shift is held or the extend flag is set, extends a range to
2112 // If shift is held or the extend flag is set, extends a range to
2064 // include a given position (and optionally a second position).
2113 // include a given position (and optionally a second position).
2065 // Otherwise, simply returns the range between the given positions.
2114 // Otherwise, simply returns the range between the given positions.
2066 // Used for cursor motion and such.
2115 // Used for cursor motion and such.
2067 function extendRange(doc, range, head, other) {
2116 function extendRange(doc, range, head, other) {
2068 if (doc.cm && doc.cm.display.shift || doc.extend) {
2117 if (doc.cm && doc.cm.display.shift || doc.extend) {
2069 var anchor = range.anchor;
2118 var anchor = range.anchor;
2070 if (other) {
2119 if (other) {
2071 var posBefore = cmp(head, anchor) < 0;
2120 var posBefore = cmp(head, anchor) < 0;
2072 if (posBefore != (cmp(other, anchor) < 0)) {
2121 if (posBefore != (cmp(other, anchor) < 0)) {
2073 anchor = head;
2122 anchor = head;
2074 head = other;
2123 head = other;
2075 } else if (posBefore != (cmp(head, other) < 0)) {
2124 } else if (posBefore != (cmp(head, other) < 0)) {
2076 head = other;
2125 head = other;
2077 }
2126 }
2078 }
2127 }
2079 return new Range(anchor, head);
2128 return new Range(anchor, head);
2080 } else {
2129 } else {
2081 return new Range(other || head, head);
2130 return new Range(other || head, head);
2082 }
2131 }
2083 }
2132 }
2084
2133
2085 // Extend the primary selection range, discard the rest.
2134 // Extend the primary selection range, discard the rest.
2086 function extendSelection(doc, head, other, options) {
2135 function extendSelection(doc, head, other, options) {
2087 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2136 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2088 }
2137 }
2089
2138
2090 // Extend all selections (pos is an array of selections with length
2139 // Extend all selections (pos is an array of selections with length
2091 // equal the number of selections)
2140 // equal the number of selections)
2092 function extendSelections(doc, heads, options) {
2141 function extendSelections(doc, heads, options) {
2093 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2142 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2094 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2143 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2095 var newSel = normalizeSelection(out, doc.sel.primIndex);
2144 var newSel = normalizeSelection(out, doc.sel.primIndex);
2096 setSelection(doc, newSel, options);
2145 setSelection(doc, newSel, options);
2097 }
2146 }
2098
2147
2099 // Updates a single range in the selection.
2148 // Updates a single range in the selection.
2100 function replaceOneSelection(doc, i, range, options) {
2149 function replaceOneSelection(doc, i, range, options) {
2101 var ranges = doc.sel.ranges.slice(0);
2150 var ranges = doc.sel.ranges.slice(0);
2102 ranges[i] = range;
2151 ranges[i] = range;
2103 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2152 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2104 }
2153 }
2105
2154
2106 // Reset the selection to a single range.
2155 // Reset the selection to a single range.
2107 function setSimpleSelection(doc, anchor, head, options) {
2156 function setSimpleSelection(doc, anchor, head, options) {
2108 setSelection(doc, simpleSelection(anchor, head), options);
2157 setSelection(doc, simpleSelection(anchor, head), options);
2109 }
2158 }
2110
2159
2111 // Give beforeSelectionChange handlers a change to influence a
2160 // Give beforeSelectionChange handlers a change to influence a
2112 // selection update.
2161 // selection update.
2113 function filterSelectionChange(doc, sel) {
2162 function filterSelectionChange(doc, sel, options) {
2114 var obj = {
2163 var obj = {
2115 ranges: sel.ranges,
2164 ranges: sel.ranges,
2116 update: function(ranges) {
2165 update: function(ranges) {
2117 this.ranges = [];
2166 this.ranges = [];
2118 for (var i = 0; i < ranges.length; i++)
2167 for (var i = 0; i < ranges.length; i++)
2119 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2168 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2120 clipPos(doc, ranges[i].head));
2169 clipPos(doc, ranges[i].head));
2121 }
2170 },
2171 origin: options && options.origin
2122 };
2172 };
2123 signal(doc, "beforeSelectionChange", doc, obj);
2173 signal(doc, "beforeSelectionChange", doc, obj);
2124 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2174 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2125 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2175 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2126 else return sel;
2176 else return sel;
2127 }
2177 }
2128
2178
2129 function setSelectionReplaceHistory(doc, sel, options) {
2179 function setSelectionReplaceHistory(doc, sel, options) {
2130 var done = doc.history.done, last = lst(done);
2180 var done = doc.history.done, last = lst(done);
2131 if (last && last.ranges) {
2181 if (last && last.ranges) {
2132 done[done.length - 1] = sel;
2182 done[done.length - 1] = sel;
2133 setSelectionNoUndo(doc, sel, options);
2183 setSelectionNoUndo(doc, sel, options);
2134 } else {
2184 } else {
2135 setSelection(doc, sel, options);
2185 setSelection(doc, sel, options);
2136 }
2186 }
2137 }
2187 }
2138
2188
2139 // Set a new selection.
2189 // Set a new selection.
2140 function setSelection(doc, sel, options) {
2190 function setSelection(doc, sel, options) {
2141 setSelectionNoUndo(doc, sel, options);
2191 setSelectionNoUndo(doc, sel, options);
2142 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2192 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2143 }
2193 }
2144
2194
2145 function setSelectionNoUndo(doc, sel, options) {
2195 function setSelectionNoUndo(doc, sel, options) {
2146 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2196 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2147 sel = filterSelectionChange(doc, sel);
2197 sel = filterSelectionChange(doc, sel, options);
2148
2198
2149 var bias = options && options.bias ||
2199 var bias = options && options.bias ||
2150 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2200 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2151 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2201 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2152
2202
2153 if (!(options && options.scroll === false) && doc.cm)
2203 if (!(options && options.scroll === false) && doc.cm)
2154 ensureCursorVisible(doc.cm);
2204 ensureCursorVisible(doc.cm);
2155 }
2205 }
2156
2206
2157 function setSelectionInner(doc, sel) {
2207 function setSelectionInner(doc, sel) {
2158 if (sel.equals(doc.sel)) return;
2208 if (sel.equals(doc.sel)) return;
2159
2209
2160 doc.sel = sel;
2210 doc.sel = sel;
2161
2211
2162 if (doc.cm) {
2212 if (doc.cm) {
2163 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2213 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2164 signalCursorActivity(doc.cm);
2214 signalCursorActivity(doc.cm);
2165 }
2215 }
2166 signalLater(doc, "cursorActivity", doc);
2216 signalLater(doc, "cursorActivity", doc);
2167 }
2217 }
2168
2218
2169 // Verify that the selection does not partially select any atomic
2219 // Verify that the selection does not partially select any atomic
2170 // marked ranges.
2220 // marked ranges.
2171 function reCheckSelection(doc) {
2221 function reCheckSelection(doc) {
2172 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
2222 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
2173 }
2223 }
2174
2224
2175 // Return a selection that does not partially select any atomic
2225 // Return a selection that does not partially select any atomic
2176 // ranges.
2226 // ranges.
2177 function skipAtomicInSelection(doc, sel, bias, mayClear) {
2227 function skipAtomicInSelection(doc, sel, bias, mayClear) {
2178 var out;
2228 var out;
2179 for (var i = 0; i < sel.ranges.length; i++) {
2229 for (var i = 0; i < sel.ranges.length; i++) {
2180 var range = sel.ranges[i];
2230 var range = sel.ranges[i];
2181 var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
2231 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
2182 var newHead = skipAtomic(doc, range.head, bias, mayClear);
2232 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
2233 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
2183 if (out || newAnchor != range.anchor || newHead != range.head) {
2234 if (out || newAnchor != range.anchor || newHead != range.head) {
2184 if (!out) out = sel.ranges.slice(0, i);
2235 if (!out) out = sel.ranges.slice(0, i);
2185 out[i] = new Range(newAnchor, newHead);
2236 out[i] = new Range(newAnchor, newHead);
2186 }
2237 }
2187 }
2238 }
2188 return out ? normalizeSelection(out, sel.primIndex) : sel;
2239 return out ? normalizeSelection(out, sel.primIndex) : sel;
2189 }
2240 }
2190
2241
2191 // Ensure a given position is not inside an atomic range.
2242 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
2192 function skipAtomic(doc, pos, bias, mayClear) {
2243 var line = getLine(doc, pos.line);
2193 var flipped = false, curPos = pos;
2244 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
2194 var dir = bias || 1;
2245 var sp = line.markedSpans[i], m = sp.marker;
2195 doc.cantEdit = false;
2246 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
2196 search: for (;;) {
2247 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
2197 var line = getLine(doc, curPos.line);
2248 if (mayClear) {
2198 if (line.markedSpans) {
2249 signal(m, "beforeCursorEnter");
2199 for (var i = 0; i < line.markedSpans.length; ++i) {
2250 if (m.explicitlyCleared) {
2200 var sp = line.markedSpans[i], m = sp.marker;
2251 if (!line.markedSpans) break;
2201 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2252 else {--i; continue;}
2202 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2203 if (mayClear) {
2204 signal(m, "beforeCursorEnter");
2205 if (m.explicitlyCleared) {
2206 if (!line.markedSpans) break;
2207 else {--i; continue;}
2208 }
2209 }
2210 if (!m.atomic) continue;
2211 var newPos = m.find(dir < 0 ? -1 : 1);
2212 if (cmp(newPos, curPos) == 0) {
2213 newPos.ch += dir;
2214 if (newPos.ch < 0) {
2215 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2216 else newPos = null;
2217 } else if (newPos.ch > line.text.length) {
2218 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2219 else newPos = null;
2220 }
2221 if (!newPos) {
2222 if (flipped) {
2223 // Driven in a corner -- no valid cursor position found at all
2224 // -- try again *with* clearing, if we didn't already
2225 if (!mayClear) return skipAtomic(doc, pos, bias, true);
2226 // Otherwise, turn off editing until further notice, and return the start of the doc
2227 doc.cantEdit = true;
2228 return Pos(doc.first, 0);
2229 }
2230 flipped = true; newPos = pos; dir = -dir;
2231 }
2232 }
2233 curPos = newPos;
2234 continue search;
2235 }
2253 }
2236 }
2254 }
2237 }
2255 if (!m.atomic) continue;
2238 return curPos;
2256
2257 if (oldPos) {
2258 var near = m.find(dir < 0 ? 1 : -1), diff;
2259 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) near = movePos(doc, near, -dir, line);
2260 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
2261 return skipAtomicInner(doc, near, pos, dir, mayClear);
2262 }
2263
2264 var far = m.find(dir < 0 ? -1 : 1);
2265 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) far = movePos(doc, far, dir, line);
2266 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null;
2267 }
2268 }
2269 return pos;
2270 }
2271
2272 // Ensure a given position is not inside an atomic range.
2273 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
2274 var dir = bias || 1;
2275 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
2276 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
2277 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
2278 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
2279 if (!found) {
2280 doc.cantEdit = true;
2281 return Pos(doc.first, 0);
2282 }
2283 return found;
2284 }
2285
2286 function movePos(doc, pos, dir, line) {
2287 if (dir < 0 && pos.ch == 0) {
2288 if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1));
2289 else return null;
2290 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
2291 if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0);
2292 else return null;
2293 } else {
2294 return new Pos(pos.line, pos.ch + dir);
2239 }
2295 }
2240 }
2296 }
2241
2297
2242 // SELECTION DRAWING
2298 // SELECTION DRAWING
2243
2299
2244 function updateSelection(cm) {
2300 function updateSelection(cm) {
2245 cm.display.input.showSelection(cm.display.input.prepareSelection());
2301 cm.display.input.showSelection(cm.display.input.prepareSelection());
2246 }
2302 }
2247
2303
2248 function prepareSelection(cm, primary) {
2304 function prepareSelection(cm, primary) {
2249 var doc = cm.doc, result = {};
2305 var doc = cm.doc, result = {};
2250 var curFragment = result.cursors = document.createDocumentFragment();
2306 var curFragment = result.cursors = document.createDocumentFragment();
2251 var selFragment = result.selection = document.createDocumentFragment();
2307 var selFragment = result.selection = document.createDocumentFragment();
2252
2308
2253 for (var i = 0; i < doc.sel.ranges.length; i++) {
2309 for (var i = 0; i < doc.sel.ranges.length; i++) {
2254 if (primary === false && i == doc.sel.primIndex) continue;
2310 if (primary === false && i == doc.sel.primIndex) continue;
2255 var range = doc.sel.ranges[i];
2311 var range = doc.sel.ranges[i];
2256 var collapsed = range.empty();
2312 var collapsed = range.empty();
2257 if (collapsed || cm.options.showCursorWhenSelecting)
2313 if (collapsed || cm.options.showCursorWhenSelecting)
2258 drawSelectionCursor(cm, range, curFragment);
2314 drawSelectionCursor(cm, range.head, curFragment);
2259 if (!collapsed)
2315 if (!collapsed)
2260 drawSelectionRange(cm, range, selFragment);
2316 drawSelectionRange(cm, range, selFragment);
2261 }
2317 }
2262 return result;
2318 return result;
2263 }
2319 }
2264
2320
2265 // Draws a cursor for the given range
2321 // Draws a cursor for the given range
2266 function drawSelectionCursor(cm, range, output) {
2322 function drawSelectionCursor(cm, head, output) {
2267 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2323 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2268
2324
2269 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2325 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2270 cursor.style.left = pos.left + "px";
2326 cursor.style.left = pos.left + "px";
2271 cursor.style.top = pos.top + "px";
2327 cursor.style.top = pos.top + "px";
2272 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2328 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2273
2329
2274 if (pos.other) {
2330 if (pos.other) {
2275 // Secondary cursor, shown when on a 'jump' in bi-directional text
2331 // Secondary cursor, shown when on a 'jump' in bi-directional text
2276 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2332 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2277 otherCursor.style.display = "";
2333 otherCursor.style.display = "";
2278 otherCursor.style.left = pos.other.left + "px";
2334 otherCursor.style.left = pos.other.left + "px";
2279 otherCursor.style.top = pos.other.top + "px";
2335 otherCursor.style.top = pos.other.top + "px";
2280 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2336 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2281 }
2337 }
2282 }
2338 }
2283
2339
2284 // Draws the given range as a highlighted selection
2340 // Draws the given range as a highlighted selection
2285 function drawSelectionRange(cm, range, output) {
2341 function drawSelectionRange(cm, range, output) {
2286 var display = cm.display, doc = cm.doc;
2342 var display = cm.display, doc = cm.doc;
2287 var fragment = document.createDocumentFragment();
2343 var fragment = document.createDocumentFragment();
2288 var padding = paddingH(cm.display), leftSide = padding.left;
2344 var padding = paddingH(cm.display), leftSide = padding.left;
2289 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2345 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2290
2346
2291 function add(left, top, width, bottom) {
2347 function add(left, top, width, bottom) {
2292 if (top < 0) top = 0;
2348 if (top < 0) top = 0;
2293 top = Math.round(top);
2349 top = Math.round(top);
2294 bottom = Math.round(bottom);
2350 bottom = Math.round(bottom);
2295 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2351 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2296 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
2352 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
2297 "px; height: " + (bottom - top) + "px"));
2353 "px; height: " + (bottom - top) + "px"));
2298 }
2354 }
2299
2355
2300 function drawForLine(line, fromArg, toArg) {
2356 function drawForLine(line, fromArg, toArg) {
2301 var lineObj = getLine(doc, line);
2357 var lineObj = getLine(doc, line);
2302 var lineLen = lineObj.text.length;
2358 var lineLen = lineObj.text.length;
2303 var start, end;
2359 var start, end;
2304 function coords(ch, bias) {
2360 function coords(ch, bias) {
2305 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2361 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2306 }
2362 }
2307
2363
2308 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
2364 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
2309 var leftPos = coords(from, "left"), rightPos, left, right;
2365 var leftPos = coords(from, "left"), rightPos, left, right;
2310 if (from == to) {
2366 if (from == to) {
2311 rightPos = leftPos;
2367 rightPos = leftPos;
2312 left = right = leftPos.left;
2368 left = right = leftPos.left;
2313 } else {
2369 } else {
2314 rightPos = coords(to - 1, "right");
2370 rightPos = coords(to - 1, "right");
2315 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2371 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2316 left = leftPos.left;
2372 left = leftPos.left;
2317 right = rightPos.right;
2373 right = rightPos.right;
2318 }
2374 }
2319 if (fromArg == null && from == 0) left = leftSide;
2375 if (fromArg == null && from == 0) left = leftSide;
2320 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2376 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2321 add(left, leftPos.top, null, leftPos.bottom);
2377 add(left, leftPos.top, null, leftPos.bottom);
2322 left = leftSide;
2378 left = leftSide;
2323 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2379 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2324 }
2380 }
2325 if (toArg == null && to == lineLen) right = rightSide;
2381 if (toArg == null && to == lineLen) right = rightSide;
2326 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2382 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2327 start = leftPos;
2383 start = leftPos;
2328 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2384 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2329 end = rightPos;
2385 end = rightPos;
2330 if (left < leftSide + 1) left = leftSide;
2386 if (left < leftSide + 1) left = leftSide;
2331 add(left, rightPos.top, right - left, rightPos.bottom);
2387 add(left, rightPos.top, right - left, rightPos.bottom);
2332 });
2388 });
2333 return {start: start, end: end};
2389 return {start: start, end: end};
2334 }
2390 }
2335
2391
2336 var sFrom = range.from(), sTo = range.to();
2392 var sFrom = range.from(), sTo = range.to();
2337 if (sFrom.line == sTo.line) {
2393 if (sFrom.line == sTo.line) {
2338 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2394 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2339 } else {
2395 } else {
2340 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2396 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2341 var singleVLine = visualLine(fromLine) == visualLine(toLine);
2397 var singleVLine = visualLine(fromLine) == visualLine(toLine);
2342 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2398 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2343 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2399 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2344 if (singleVLine) {
2400 if (singleVLine) {
2345 if (leftEnd.top < rightStart.top - 2) {
2401 if (leftEnd.top < rightStart.top - 2) {
2346 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2402 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2347 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2403 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2348 } else {
2404 } else {
2349 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2405 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2350 }
2406 }
2351 }
2407 }
2352 if (leftEnd.bottom < rightStart.top)
2408 if (leftEnd.bottom < rightStart.top)
2353 add(leftSide, leftEnd.bottom, null, rightStart.top);
2409 add(leftSide, leftEnd.bottom, null, rightStart.top);
2354 }
2410 }
2355
2411
2356 output.appendChild(fragment);
2412 output.appendChild(fragment);
2357 }
2413 }
2358
2414
2359 // Cursor-blinking
2415 // Cursor-blinking
2360 function restartBlink(cm) {
2416 function restartBlink(cm) {
2361 if (!cm.state.focused) return;
2417 if (!cm.state.focused) return;
2362 var display = cm.display;
2418 var display = cm.display;
2363 clearInterval(display.blinker);
2419 clearInterval(display.blinker);
2364 var on = true;
2420 var on = true;
2365 display.cursorDiv.style.visibility = "";
2421 display.cursorDiv.style.visibility = "";
2366 if (cm.options.cursorBlinkRate > 0)
2422 if (cm.options.cursorBlinkRate > 0)
2367 display.blinker = setInterval(function() {
2423 display.blinker = setInterval(function() {
2368 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2424 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2369 }, cm.options.cursorBlinkRate);
2425 }, cm.options.cursorBlinkRate);
2370 else if (cm.options.cursorBlinkRate < 0)
2426 else if (cm.options.cursorBlinkRate < 0)
2371 display.cursorDiv.style.visibility = "hidden";
2427 display.cursorDiv.style.visibility = "hidden";
2372 }
2428 }
2373
2429
2374 // HIGHLIGHT WORKER
2430 // HIGHLIGHT WORKER
2375
2431
2376 function startWorker(cm, time) {
2432 function startWorker(cm, time) {
2377 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2433 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2378 cm.state.highlight.set(time, bind(highlightWorker, cm));
2434 cm.state.highlight.set(time, bind(highlightWorker, cm));
2379 }
2435 }
2380
2436
2381 function highlightWorker(cm) {
2437 function highlightWorker(cm) {
2382 var doc = cm.doc;
2438 var doc = cm.doc;
2383 if (doc.frontier < doc.first) doc.frontier = doc.first;
2439 if (doc.frontier < doc.first) doc.frontier = doc.first;
2384 if (doc.frontier >= cm.display.viewTo) return;
2440 if (doc.frontier >= cm.display.viewTo) return;
2385 var end = +new Date + cm.options.workTime;
2441 var end = +new Date + cm.options.workTime;
2386 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2442 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2387 var changedLines = [];
2443 var changedLines = [];
2388
2444
2389 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2445 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2390 if (doc.frontier >= cm.display.viewFrom) { // Visible
2446 if (doc.frontier >= cm.display.viewFrom) { // Visible
2391 var oldStyles = line.styles;
2447 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
2392 var highlighted = highlightLine(cm, line, state, true);
2448 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
2393 line.styles = highlighted.styles;
2449 line.styles = highlighted.styles;
2394 var oldCls = line.styleClasses, newCls = highlighted.classes;
2450 var oldCls = line.styleClasses, newCls = highlighted.classes;
2395 if (newCls) line.styleClasses = newCls;
2451 if (newCls) line.styleClasses = newCls;
2396 else if (oldCls) line.styleClasses = null;
2452 else if (oldCls) line.styleClasses = null;
2397 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2453 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2398 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2454 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2399 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2455 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2400 if (ischange) changedLines.push(doc.frontier);
2456 if (ischange) changedLines.push(doc.frontier);
2401 line.stateAfter = copyState(doc.mode, state);
2457 line.stateAfter = tooLong ? state : copyState(doc.mode, state);
2402 } else {
2458 } else {
2403 processLine(cm, line.text, state);
2459 if (line.text.length <= cm.options.maxHighlightLength)
2460 processLine(cm, line.text, state);
2404 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
2461 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
2405 }
2462 }
2406 ++doc.frontier;
2463 ++doc.frontier;
2407 if (+new Date > end) {
2464 if (+new Date > end) {
2408 startWorker(cm, cm.options.workDelay);
2465 startWorker(cm, cm.options.workDelay);
2409 return true;
2466 return true;
2410 }
2467 }
2411 });
2468 });
2412 if (changedLines.length) runInOp(cm, function() {
2469 if (changedLines.length) runInOp(cm, function() {
2413 for (var i = 0; i < changedLines.length; i++)
2470 for (var i = 0; i < changedLines.length; i++)
2414 regLineChange(cm, changedLines[i], "text");
2471 regLineChange(cm, changedLines[i], "text");
2415 });
2472 });
2416 }
2473 }
2417
2474
2418 // Finds the line to start with when starting a parse. Tries to
2475 // Finds the line to start with when starting a parse. Tries to
2419 // find a line with a stateAfter, so that it can start with a
2476 // find a line with a stateAfter, so that it can start with a
2420 // valid state. If that fails, it returns the line with the
2477 // valid state. If that fails, it returns the line with the
2421 // smallest indentation, which tends to need the least context to
2478 // smallest indentation, which tends to need the least context to
2422 // parse correctly.
2479 // parse correctly.
2423 function findStartLine(cm, n, precise) {
2480 function findStartLine(cm, n, precise) {
2424 var minindent, minline, doc = cm.doc;
2481 var minindent, minline, doc = cm.doc;
2425 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2482 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2426 for (var search = n; search > lim; --search) {
2483 for (var search = n; search > lim; --search) {
2427 if (search <= doc.first) return doc.first;
2484 if (search <= doc.first) return doc.first;
2428 var line = getLine(doc, search - 1);
2485 var line = getLine(doc, search - 1);
2429 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2486 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2430 var indented = countColumn(line.text, null, cm.options.tabSize);
2487 var indented = countColumn(line.text, null, cm.options.tabSize);
2431 if (minline == null || minindent > indented) {
2488 if (minline == null || minindent > indented) {
2432 minline = search - 1;
2489 minline = search - 1;
2433 minindent = indented;
2490 minindent = indented;
2434 }
2491 }
2435 }
2492 }
2436 return minline;
2493 return minline;
2437 }
2494 }
2438
2495
2439 function getStateBefore(cm, n, precise) {
2496 function getStateBefore(cm, n, precise) {
2440 var doc = cm.doc, display = cm.display;
2497 var doc = cm.doc, display = cm.display;
2441 if (!doc.mode.startState) return true;
2498 if (!doc.mode.startState) return true;
2442 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2499 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2443 if (!state) state = startState(doc.mode);
2500 if (!state) state = startState(doc.mode);
2444 else state = copyState(doc.mode, state);
2501 else state = copyState(doc.mode, state);
2445 doc.iter(pos, n, function(line) {
2502 doc.iter(pos, n, function(line) {
2446 processLine(cm, line.text, state);
2503 processLine(cm, line.text, state);
2447 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2504 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2448 line.stateAfter = save ? copyState(doc.mode, state) : null;
2505 line.stateAfter = save ? copyState(doc.mode, state) : null;
2449 ++pos;
2506 ++pos;
2450 });
2507 });
2451 if (precise) doc.frontier = pos;
2508 if (precise) doc.frontier = pos;
2452 return state;
2509 return state;
2453 }
2510 }
2454
2511
2455 // POSITION MEASUREMENT
2512 // POSITION MEASUREMENT
2456
2513
2457 function paddingTop(display) {return display.lineSpace.offsetTop;}
2514 function paddingTop(display) {return display.lineSpace.offsetTop;}
2458 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2515 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2459 function paddingH(display) {
2516 function paddingH(display) {
2460 if (display.cachedPaddingH) return display.cachedPaddingH;
2517 if (display.cachedPaddingH) return display.cachedPaddingH;
2461 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2518 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2462 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2519 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2463 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2520 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2464 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2521 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2465 return data;
2522 return data;
2466 }
2523 }
2467
2524
2468 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2525 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2469 function displayWidth(cm) {
2526 function displayWidth(cm) {
2470 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2527 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2471 }
2528 }
2472 function displayHeight(cm) {
2529 function displayHeight(cm) {
2473 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2530 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2474 }
2531 }
2475
2532
2476 // Ensure the lineView.wrapping.heights array is populated. This is
2533 // Ensure the lineView.wrapping.heights array is populated. This is
2477 // an array of bottom offsets for the lines that make up a drawn
2534 // an array of bottom offsets for the lines that make up a drawn
2478 // line. When lineWrapping is on, there might be more than one
2535 // line. When lineWrapping is on, there might be more than one
2479 // height.
2536 // height.
2480 function ensureLineHeights(cm, lineView, rect) {
2537 function ensureLineHeights(cm, lineView, rect) {
2481 var wrapping = cm.options.lineWrapping;
2538 var wrapping = cm.options.lineWrapping;
2482 var curWidth = wrapping && displayWidth(cm);
2539 var curWidth = wrapping && displayWidth(cm);
2483 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2540 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2484 var heights = lineView.measure.heights = [];
2541 var heights = lineView.measure.heights = [];
2485 if (wrapping) {
2542 if (wrapping) {
2486 lineView.measure.width = curWidth;
2543 lineView.measure.width = curWidth;
2487 var rects = lineView.text.firstChild.getClientRects();
2544 var rects = lineView.text.firstChild.getClientRects();
2488 for (var i = 0; i < rects.length - 1; i++) {
2545 for (var i = 0; i < rects.length - 1; i++) {
2489 var cur = rects[i], next = rects[i + 1];
2546 var cur = rects[i], next = rects[i + 1];
2490 if (Math.abs(cur.bottom - next.bottom) > 2)
2547 if (Math.abs(cur.bottom - next.bottom) > 2)
2491 heights.push((cur.bottom + next.top) / 2 - rect.top);
2548 heights.push((cur.bottom + next.top) / 2 - rect.top);
2492 }
2549 }
2493 }
2550 }
2494 heights.push(rect.bottom - rect.top);
2551 heights.push(rect.bottom - rect.top);
2495 }
2552 }
2496 }
2553 }
2497
2554
2498 // Find a line map (mapping character offsets to text nodes) and a
2555 // Find a line map (mapping character offsets to text nodes) and a
2499 // measurement cache for the given line number. (A line view might
2556 // measurement cache for the given line number. (A line view might
2500 // contain multiple lines when collapsed ranges are present.)
2557 // contain multiple lines when collapsed ranges are present.)
2501 function mapFromLineView(lineView, line, lineN) {
2558 function mapFromLineView(lineView, line, lineN) {
2502 if (lineView.line == line)
2559 if (lineView.line == line)
2503 return {map: lineView.measure.map, cache: lineView.measure.cache};
2560 return {map: lineView.measure.map, cache: lineView.measure.cache};
2504 for (var i = 0; i < lineView.rest.length; i++)
2561 for (var i = 0; i < lineView.rest.length; i++)
2505 if (lineView.rest[i] == line)
2562 if (lineView.rest[i] == line)
2506 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2563 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2507 for (var i = 0; i < lineView.rest.length; i++)
2564 for (var i = 0; i < lineView.rest.length; i++)
2508 if (lineNo(lineView.rest[i]) > lineN)
2565 if (lineNo(lineView.rest[i]) > lineN)
2509 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
2566 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
2510 }
2567 }
2511
2568
2512 // Render a line into the hidden node display.externalMeasured. Used
2569 // Render a line into the hidden node display.externalMeasured. Used
2513 // when measurement is needed for a line that's not in the viewport.
2570 // when measurement is needed for a line that's not in the viewport.
2514 function updateExternalMeasurement(cm, line) {
2571 function updateExternalMeasurement(cm, line) {
2515 line = visualLine(line);
2572 line = visualLine(line);
2516 var lineN = lineNo(line);
2573 var lineN = lineNo(line);
2517 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2574 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2518 view.lineN = lineN;
2575 view.lineN = lineN;
2519 var built = view.built = buildLineContent(cm, view);
2576 var built = view.built = buildLineContent(cm, view);
2520 view.text = built.pre;
2577 view.text = built.pre;
2521 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2578 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2522 return view;
2579 return view;
2523 }
2580 }
2524
2581
2525 // Get a {top, bottom, left, right} box (in line-local coordinates)
2582 // Get a {top, bottom, left, right} box (in line-local coordinates)
2526 // for a given character.
2583 // for a given character.
2527 function measureChar(cm, line, ch, bias) {
2584 function measureChar(cm, line, ch, bias) {
2528 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2585 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2529 }
2586 }
2530
2587
2531 // Find a line view that corresponds to the given line number.
2588 // Find a line view that corresponds to the given line number.
2532 function findViewForLine(cm, lineN) {
2589 function findViewForLine(cm, lineN) {
2533 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2590 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2534 return cm.display.view[findViewIndex(cm, lineN)];
2591 return cm.display.view[findViewIndex(cm, lineN)];
2535 var ext = cm.display.externalMeasured;
2592 var ext = cm.display.externalMeasured;
2536 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2593 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2537 return ext;
2594 return ext;
2538 }
2595 }
2539
2596
2540 // Measurement can be split in two steps, the set-up work that
2597 // Measurement can be split in two steps, the set-up work that
2541 // applies to the whole line, and the measurement of the actual
2598 // applies to the whole line, and the measurement of the actual
2542 // character. Functions like coordsChar, that need to do a lot of
2599 // character. Functions like coordsChar, that need to do a lot of
2543 // measurements in a row, can thus ensure that the set-up work is
2600 // measurements in a row, can thus ensure that the set-up work is
2544 // only done once.
2601 // only done once.
2545 function prepareMeasureForLine(cm, line) {
2602 function prepareMeasureForLine(cm, line) {
2546 var lineN = lineNo(line);
2603 var lineN = lineNo(line);
2547 var view = findViewForLine(cm, lineN);
2604 var view = findViewForLine(cm, lineN);
2548 if (view && !view.text)
2605 if (view && !view.text) {
2549 view = null;
2606 view = null;
2550 else if (view && view.changes)
2607 } else if (view && view.changes) {
2551 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2608 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2609 cm.curOp.forceUpdate = true;
2610 }
2552 if (!view)
2611 if (!view)
2553 view = updateExternalMeasurement(cm, line);
2612 view = updateExternalMeasurement(cm, line);
2554
2613
2555 var info = mapFromLineView(view, line, lineN);
2614 var info = mapFromLineView(view, line, lineN);
2556 return {
2615 return {
2557 line: line, view: view, rect: null,
2616 line: line, view: view, rect: null,
2558 map: info.map, cache: info.cache, before: info.before,
2617 map: info.map, cache: info.cache, before: info.before,
2559 hasHeights: false
2618 hasHeights: false
2560 };
2619 };
2561 }
2620 }
2562
2621
2563 // Given a prepared measurement object, measures the position of an
2622 // Given a prepared measurement object, measures the position of an
2564 // actual character (or fetches it from the cache).
2623 // actual character (or fetches it from the cache).
2565 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2624 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2566 if (prepared.before) ch = -1;
2625 if (prepared.before) ch = -1;
2567 var key = ch + (bias || ""), found;
2626 var key = ch + (bias || ""), found;
2568 if (prepared.cache.hasOwnProperty(key)) {
2627 if (prepared.cache.hasOwnProperty(key)) {
2569 found = prepared.cache[key];
2628 found = prepared.cache[key];
2570 } else {
2629 } else {
2571 if (!prepared.rect)
2630 if (!prepared.rect)
2572 prepared.rect = prepared.view.text.getBoundingClientRect();
2631 prepared.rect = prepared.view.text.getBoundingClientRect();
2573 if (!prepared.hasHeights) {
2632 if (!prepared.hasHeights) {
2574 ensureLineHeights(cm, prepared.view, prepared.rect);
2633 ensureLineHeights(cm, prepared.view, prepared.rect);
2575 prepared.hasHeights = true;
2634 prepared.hasHeights = true;
2576 }
2635 }
2577 found = measureCharInner(cm, prepared, ch, bias);
2636 found = measureCharInner(cm, prepared, ch, bias);
2578 if (!found.bogus) prepared.cache[key] = found;
2637 if (!found.bogus) prepared.cache[key] = found;
2579 }
2638 }
2580 return {left: found.left, right: found.right,
2639 return {left: found.left, right: found.right,
2581 top: varHeight ? found.rtop : found.top,
2640 top: varHeight ? found.rtop : found.top,
2582 bottom: varHeight ? found.rbottom : found.bottom};
2641 bottom: varHeight ? found.rbottom : found.bottom};
2583 }
2642 }
2584
2643
2585 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2644 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2586
2645
2587 function nodeAndOffsetInLineMap(map, ch, bias) {
2646 function nodeAndOffsetInLineMap(map, ch, bias) {
2588 var node, start, end, collapse;
2647 var node, start, end, collapse;
2589 // First, search the line map for the text node corresponding to,
2648 // First, search the line map for the text node corresponding to,
2590 // or closest to, the target character.
2649 // or closest to, the target character.
2591 for (var i = 0; i < map.length; i += 3) {
2650 for (var i = 0; i < map.length; i += 3) {
2592 var mStart = map[i], mEnd = map[i + 1];
2651 var mStart = map[i], mEnd = map[i + 1];
2593 if (ch < mStart) {
2652 if (ch < mStart) {
2594 start = 0; end = 1;
2653 start = 0; end = 1;
2595 collapse = "left";
2654 collapse = "left";
2596 } else if (ch < mEnd) {
2655 } else if (ch < mEnd) {
2597 start = ch - mStart;
2656 start = ch - mStart;
2598 end = start + 1;
2657 end = start + 1;
2599 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2658 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2600 end = mEnd - mStart;
2659 end = mEnd - mStart;
2601 start = end - 1;
2660 start = end - 1;
2602 if (ch >= mEnd) collapse = "right";
2661 if (ch >= mEnd) collapse = "right";
2603 }
2662 }
2604 if (start != null) {
2663 if (start != null) {
2605 node = map[i + 2];
2664 node = map[i + 2];
2606 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2665 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2607 collapse = bias;
2666 collapse = bias;
2608 if (bias == "left" && start == 0)
2667 if (bias == "left" && start == 0)
2609 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2668 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2610 node = map[(i -= 3) + 2];
2669 node = map[(i -= 3) + 2];
2611 collapse = "left";
2670 collapse = "left";
2612 }
2671 }
2613 if (bias == "right" && start == mEnd - mStart)
2672 if (bias == "right" && start == mEnd - mStart)
2614 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2673 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2615 node = map[(i += 3) + 2];
2674 node = map[(i += 3) + 2];
2616 collapse = "right";
2675 collapse = "right";
2617 }
2676 }
2618 break;
2677 break;
2619 }
2678 }
2620 }
2679 }
2621 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2680 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2622 }
2681 }
2623
2682
2624 function measureCharInner(cm, prepared, ch, bias) {
2683 function measureCharInner(cm, prepared, ch, bias) {
2625 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2684 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2626 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2685 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2627
2686
2628 var rect;
2687 var rect;
2629 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2688 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2630 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2689 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2631 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2690 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2632 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2691 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2633 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
2692 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
2634 rect = node.parentNode.getBoundingClientRect();
2693 rect = node.parentNode.getBoundingClientRect();
2635 } else if (ie && cm.options.lineWrapping) {
2694 } else if (ie && cm.options.lineWrapping) {
2636 var rects = range(node, start, end).getClientRects();
2695 var rects = range(node, start, end).getClientRects();
2637 if (rects.length)
2696 if (rects.length)
2638 rect = rects[bias == "right" ? rects.length - 1 : 0];
2697 rect = rects[bias == "right" ? rects.length - 1 : 0];
2639 else
2698 else
2640 rect = nullRect;
2699 rect = nullRect;
2641 } else {
2700 } else {
2642 rect = range(node, start, end).getBoundingClientRect() || nullRect;
2701 rect = range(node, start, end).getBoundingClientRect() || nullRect;
2643 }
2702 }
2644 if (rect.left || rect.right || start == 0) break;
2703 if (rect.left || rect.right || start == 0) break;
2645 end = start;
2704 end = start;
2646 start = start - 1;
2705 start = start - 1;
2647 collapse = "right";
2706 collapse = "right";
2648 }
2707 }
2649 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
2708 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
2650 } else { // If it is a widget, simply get the box for the whole widget.
2709 } else { // If it is a widget, simply get the box for the whole widget.
2651 if (start > 0) collapse = bias = "right";
2710 if (start > 0) collapse = bias = "right";
2652 var rects;
2711 var rects;
2653 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2712 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2654 rect = rects[bias == "right" ? rects.length - 1 : 0];
2713 rect = rects[bias == "right" ? rects.length - 1 : 0];
2655 else
2714 else
2656 rect = node.getBoundingClientRect();
2715 rect = node.getBoundingClientRect();
2657 }
2716 }
2658 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2717 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2659 var rSpan = node.parentNode.getClientRects()[0];
2718 var rSpan = node.parentNode.getClientRects()[0];
2660 if (rSpan)
2719 if (rSpan)
2661 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2720 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2662 else
2721 else
2663 rect = nullRect;
2722 rect = nullRect;
2664 }
2723 }
2665
2724
2666 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2725 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2667 var mid = (rtop + rbot) / 2;
2726 var mid = (rtop + rbot) / 2;
2668 var heights = prepared.view.measure.heights;
2727 var heights = prepared.view.measure.heights;
2669 for (var i = 0; i < heights.length - 1; i++)
2728 for (var i = 0; i < heights.length - 1; i++)
2670 if (mid < heights[i]) break;
2729 if (mid < heights[i]) break;
2671 var top = i ? heights[i - 1] : 0, bot = heights[i];
2730 var top = i ? heights[i - 1] : 0, bot = heights[i];
2672 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2731 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2673 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2732 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2674 top: top, bottom: bot};
2733 top: top, bottom: bot};
2675 if (!rect.left && !rect.right) result.bogus = true;
2734 if (!rect.left && !rect.right) result.bogus = true;
2676 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2735 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2677
2736
2678 return result;
2737 return result;
2679 }
2738 }
2680
2739
2681 // Work around problem with bounding client rects on ranges being
2740 // Work around problem with bounding client rects on ranges being
2682 // returned incorrectly when zoomed on IE10 and below.
2741 // returned incorrectly when zoomed on IE10 and below.
2683 function maybeUpdateRectForZooming(measure, rect) {
2742 function maybeUpdateRectForZooming(measure, rect) {
2684 if (!window.screen || screen.logicalXDPI == null ||
2743 if (!window.screen || screen.logicalXDPI == null ||
2685 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2744 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2686 return rect;
2745 return rect;
2687 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2746 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2688 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2747 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2689 return {left: rect.left * scaleX, right: rect.right * scaleX,
2748 return {left: rect.left * scaleX, right: rect.right * scaleX,
2690 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2749 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2691 }
2750 }
2692
2751
2693 function clearLineMeasurementCacheFor(lineView) {
2752 function clearLineMeasurementCacheFor(lineView) {
2694 if (lineView.measure) {
2753 if (lineView.measure) {
2695 lineView.measure.cache = {};
2754 lineView.measure.cache = {};
2696 lineView.measure.heights = null;
2755 lineView.measure.heights = null;
2697 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2756 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2698 lineView.measure.caches[i] = {};
2757 lineView.measure.caches[i] = {};
2699 }
2758 }
2700 }
2759 }
2701
2760
2702 function clearLineMeasurementCache(cm) {
2761 function clearLineMeasurementCache(cm) {
2703 cm.display.externalMeasure = null;
2762 cm.display.externalMeasure = null;
2704 removeChildren(cm.display.lineMeasure);
2763 removeChildren(cm.display.lineMeasure);
2705 for (var i = 0; i < cm.display.view.length; i++)
2764 for (var i = 0; i < cm.display.view.length; i++)
2706 clearLineMeasurementCacheFor(cm.display.view[i]);
2765 clearLineMeasurementCacheFor(cm.display.view[i]);
2707 }
2766 }
2708
2767
2709 function clearCaches(cm) {
2768 function clearCaches(cm) {
2710 clearLineMeasurementCache(cm);
2769 clearLineMeasurementCache(cm);
2711 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2770 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2712 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2771 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2713 cm.display.lineNumChars = null;
2772 cm.display.lineNumChars = null;
2714 }
2773 }
2715
2774
2716 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2775 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2717 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2776 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2718
2777
2719 // Converts a {top, bottom, left, right} box from line-local
2778 // Converts a {top, bottom, left, right} box from line-local
2720 // coordinates into another coordinate system. Context may be one of
2779 // coordinates into another coordinate system. Context may be one of
2721 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2780 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2722 // or "page".
2781 // or "page".
2723 function intoCoordSystem(cm, lineObj, rect, context) {
2782 function intoCoordSystem(cm, lineObj, rect, context) {
2724 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2783 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2725 var size = widgetHeight(lineObj.widgets[i]);
2784 var size = widgetHeight(lineObj.widgets[i]);
2726 rect.top += size; rect.bottom += size;
2785 rect.top += size; rect.bottom += size;
2727 }
2786 }
2728 if (context == "line") return rect;
2787 if (context == "line") return rect;
2729 if (!context) context = "local";
2788 if (!context) context = "local";
2730 var yOff = heightAtLine(lineObj);
2789 var yOff = heightAtLine(lineObj);
2731 if (context == "local") yOff += paddingTop(cm.display);
2790 if (context == "local") yOff += paddingTop(cm.display);
2732 else yOff -= cm.display.viewOffset;
2791 else yOff -= cm.display.viewOffset;
2733 if (context == "page" || context == "window") {
2792 if (context == "page" || context == "window") {
2734 var lOff = cm.display.lineSpace.getBoundingClientRect();
2793 var lOff = cm.display.lineSpace.getBoundingClientRect();
2735 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2794 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2736 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2795 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2737 rect.left += xOff; rect.right += xOff;
2796 rect.left += xOff; rect.right += xOff;
2738 }
2797 }
2739 rect.top += yOff; rect.bottom += yOff;
2798 rect.top += yOff; rect.bottom += yOff;
2740 return rect;
2799 return rect;
2741 }
2800 }
2742
2801
2743 // Coverts a box from "div" coords to another coordinate system.
2802 // Coverts a box from "div" coords to another coordinate system.
2744 // Context may be "window", "page", "div", or "local"/null.
2803 // Context may be "window", "page", "div", or "local"/null.
2745 function fromCoordSystem(cm, coords, context) {
2804 function fromCoordSystem(cm, coords, context) {
2746 if (context == "div") return coords;
2805 if (context == "div") return coords;
2747 var left = coords.left, top = coords.top;
2806 var left = coords.left, top = coords.top;
2748 // First move into "page" coordinate system
2807 // First move into "page" coordinate system
2749 if (context == "page") {
2808 if (context == "page") {
2750 left -= pageScrollX();
2809 left -= pageScrollX();
2751 top -= pageScrollY();
2810 top -= pageScrollY();
2752 } else if (context == "local" || !context) {
2811 } else if (context == "local" || !context) {
2753 var localBox = cm.display.sizer.getBoundingClientRect();
2812 var localBox = cm.display.sizer.getBoundingClientRect();
2754 left += localBox.left;
2813 left += localBox.left;
2755 top += localBox.top;
2814 top += localBox.top;
2756 }
2815 }
2757
2816
2758 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2817 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2759 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2818 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2760 }
2819 }
2761
2820
2762 function charCoords(cm, pos, context, lineObj, bias) {
2821 function charCoords(cm, pos, context, lineObj, bias) {
2763 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2822 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2764 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2823 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2765 }
2824 }
2766
2825
2767 // Returns a box for a given cursor position, which may have an
2826 // Returns a box for a given cursor position, which may have an
2768 // 'other' property containing the position of the secondary cursor
2827 // 'other' property containing the position of the secondary cursor
2769 // on a bidi boundary.
2828 // on a bidi boundary.
2770 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2829 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2771 lineObj = lineObj || getLine(cm.doc, pos.line);
2830 lineObj = lineObj || getLine(cm.doc, pos.line);
2772 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2831 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2773 function get(ch, right) {
2832 function get(ch, right) {
2774 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2833 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2775 if (right) m.left = m.right; else m.right = m.left;
2834 if (right) m.left = m.right; else m.right = m.left;
2776 return intoCoordSystem(cm, lineObj, m, context);
2835 return intoCoordSystem(cm, lineObj, m, context);
2777 }
2836 }
2778 function getBidi(ch, partPos) {
2837 function getBidi(ch, partPos) {
2779 var part = order[partPos], right = part.level % 2;
2838 var part = order[partPos], right = part.level % 2;
2780 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2839 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2781 part = order[--partPos];
2840 part = order[--partPos];
2782 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2841 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2783 right = true;
2842 right = true;
2784 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2843 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2785 part = order[++partPos];
2844 part = order[++partPos];
2786 ch = bidiLeft(part) - part.level % 2;
2845 ch = bidiLeft(part) - part.level % 2;
2787 right = false;
2846 right = false;
2788 }
2847 }
2789 if (right && ch == part.to && ch > part.from) return get(ch - 1);
2848 if (right && ch == part.to && ch > part.from) return get(ch - 1);
2790 return get(ch, right);
2849 return get(ch, right);
2791 }
2850 }
2792 var order = getOrder(lineObj), ch = pos.ch;
2851 var order = getOrder(lineObj), ch = pos.ch;
2793 if (!order) return get(ch);
2852 if (!order) return get(ch);
2794 var partPos = getBidiPartAt(order, ch);
2853 var partPos = getBidiPartAt(order, ch);
2795 var val = getBidi(ch, partPos);
2854 var val = getBidi(ch, partPos);
2796 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2855 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2797 return val;
2856 return val;
2798 }
2857 }
2799
2858
2800 // Used to cheaply estimate the coordinates for a position. Used for
2859 // Used to cheaply estimate the coordinates for a position. Used for
2801 // intermediate scroll updates.
2860 // intermediate scroll updates.
2802 function estimateCoords(cm, pos) {
2861 function estimateCoords(cm, pos) {
2803 var left = 0, pos = clipPos(cm.doc, pos);
2862 var left = 0, pos = clipPos(cm.doc, pos);
2804 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2863 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2805 var lineObj = getLine(cm.doc, pos.line);
2864 var lineObj = getLine(cm.doc, pos.line);
2806 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2865 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2807 return {left: left, right: left, top: top, bottom: top + lineObj.height};
2866 return {left: left, right: left, top: top, bottom: top + lineObj.height};
2808 }
2867 }
2809
2868
2810 // Positions returned by coordsChar contain some extra information.
2869 // Positions returned by coordsChar contain some extra information.
2811 // xRel is the relative x position of the input coordinates compared
2870 // xRel is the relative x position of the input coordinates compared
2812 // to the found position (so xRel > 0 means the coordinates are to
2871 // to the found position (so xRel > 0 means the coordinates are to
2813 // the right of the character position, for example). When outside
2872 // the right of the character position, for example). When outside
2814 // is true, that means the coordinates lie outside the line's
2873 // is true, that means the coordinates lie outside the line's
2815 // vertical range.
2874 // vertical range.
2816 function PosWithInfo(line, ch, outside, xRel) {
2875 function PosWithInfo(line, ch, outside, xRel) {
2817 var pos = Pos(line, ch);
2876 var pos = Pos(line, ch);
2818 pos.xRel = xRel;
2877 pos.xRel = xRel;
2819 if (outside) pos.outside = true;
2878 if (outside) pos.outside = true;
2820 return pos;
2879 return pos;
2821 }
2880 }
2822
2881
2823 // Compute the character position closest to the given coordinates.
2882 // Compute the character position closest to the given coordinates.
2824 // Input must be lineSpace-local ("div" coordinate system).
2883 // Input must be lineSpace-local ("div" coordinate system).
2825 function coordsChar(cm, x, y) {
2884 function coordsChar(cm, x, y) {
2826 var doc = cm.doc;
2885 var doc = cm.doc;
2827 y += cm.display.viewOffset;
2886 y += cm.display.viewOffset;
2828 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2887 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2829 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2888 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2830 if (lineN > last)
2889 if (lineN > last)
2831 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2890 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2832 if (x < 0) x = 0;
2891 if (x < 0) x = 0;
2833
2892
2834 var lineObj = getLine(doc, lineN);
2893 var lineObj = getLine(doc, lineN);
2835 for (;;) {
2894 for (;;) {
2836 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2895 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2837 var merged = collapsedSpanAtEnd(lineObj);
2896 var merged = collapsedSpanAtEnd(lineObj);
2838 var mergedPos = merged && merged.find(0, true);
2897 var mergedPos = merged && merged.find(0, true);
2839 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2898 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2840 lineN = lineNo(lineObj = mergedPos.to.line);
2899 lineN = lineNo(lineObj = mergedPos.to.line);
2841 else
2900 else
2842 return found;
2901 return found;
2843 }
2902 }
2844 }
2903 }
2845
2904
2846 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2905 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2847 var innerOff = y - heightAtLine(lineObj);
2906 var innerOff = y - heightAtLine(lineObj);
2848 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2907 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2849 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2908 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2850
2909
2851 function getX(ch) {
2910 function getX(ch) {
2852 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2911 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2853 wrongLine = true;
2912 wrongLine = true;
2854 if (innerOff > sp.bottom) return sp.left - adjust;
2913 if (innerOff > sp.bottom) return sp.left - adjust;
2855 else if (innerOff < sp.top) return sp.left + adjust;
2914 else if (innerOff < sp.top) return sp.left + adjust;
2856 else wrongLine = false;
2915 else wrongLine = false;
2857 return sp.left;
2916 return sp.left;
2858 }
2917 }
2859
2918
2860 var bidi = getOrder(lineObj), dist = lineObj.text.length;
2919 var bidi = getOrder(lineObj), dist = lineObj.text.length;
2861 var from = lineLeft(lineObj), to = lineRight(lineObj);
2920 var from = lineLeft(lineObj), to = lineRight(lineObj);
2862 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2921 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2863
2922
2864 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2923 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2865 // Do a binary search between these bounds.
2924 // Do a binary search between these bounds.
2866 for (;;) {
2925 for (;;) {
2867 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2926 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2868 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2927 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2869 var xDiff = x - (ch == from ? fromX : toX);
2928 var xDiff = x - (ch == from ? fromX : toX);
2870 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2929 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2871 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2930 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2872 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2931 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2873 return pos;
2932 return pos;
2874 }
2933 }
2875 var step = Math.ceil(dist / 2), middle = from + step;
2934 var step = Math.ceil(dist / 2), middle = from + step;
2876 if (bidi) {
2935 if (bidi) {
2877 middle = from;
2936 middle = from;
2878 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2937 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2879 }
2938 }
2880 var middleX = getX(middle);
2939 var middleX = getX(middle);
2881 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2940 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2882 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2941 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2883 }
2942 }
2884 }
2943 }
2885
2944
2886 var measureText;
2945 var measureText;
2887 // Compute the default text height.
2946 // Compute the default text height.
2888 function textHeight(display) {
2947 function textHeight(display) {
2889 if (display.cachedTextHeight != null) return display.cachedTextHeight;
2948 if (display.cachedTextHeight != null) return display.cachedTextHeight;
2890 if (measureText == null) {
2949 if (measureText == null) {
2891 measureText = elt("pre");
2950 measureText = elt("pre");
2892 // Measure a bunch of lines, for browsers that compute
2951 // Measure a bunch of lines, for browsers that compute
2893 // fractional heights.
2952 // fractional heights.
2894 for (var i = 0; i < 49; ++i) {
2953 for (var i = 0; i < 49; ++i) {
2895 measureText.appendChild(document.createTextNode("x"));
2954 measureText.appendChild(document.createTextNode("x"));
2896 measureText.appendChild(elt("br"));
2955 measureText.appendChild(elt("br"));
2897 }
2956 }
2898 measureText.appendChild(document.createTextNode("x"));
2957 measureText.appendChild(document.createTextNode("x"));
2899 }
2958 }
2900 removeChildrenAndAdd(display.measure, measureText);
2959 removeChildrenAndAdd(display.measure, measureText);
2901 var height = measureText.offsetHeight / 50;
2960 var height = measureText.offsetHeight / 50;
2902 if (height > 3) display.cachedTextHeight = height;
2961 if (height > 3) display.cachedTextHeight = height;
2903 removeChildren(display.measure);
2962 removeChildren(display.measure);
2904 return height || 1;
2963 return height || 1;
2905 }
2964 }
2906
2965
2907 // Compute the default character width.
2966 // Compute the default character width.
2908 function charWidth(display) {
2967 function charWidth(display) {
2909 if (display.cachedCharWidth != null) return display.cachedCharWidth;
2968 if (display.cachedCharWidth != null) return display.cachedCharWidth;
2910 var anchor = elt("span", "xxxxxxxxxx");
2969 var anchor = elt("span", "xxxxxxxxxx");
2911 var pre = elt("pre", [anchor]);
2970 var pre = elt("pre", [anchor]);
2912 removeChildrenAndAdd(display.measure, pre);
2971 removeChildrenAndAdd(display.measure, pre);
2913 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2972 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2914 if (width > 2) display.cachedCharWidth = width;
2973 if (width > 2) display.cachedCharWidth = width;
2915 return width || 10;
2974 return width || 10;
2916 }
2975 }
2917
2976
2918 // OPERATIONS
2977 // OPERATIONS
2919
2978
2920 // Operations are used to wrap a series of changes to the editor
2979 // Operations are used to wrap a series of changes to the editor
2921 // state in such a way that each change won't have to update the
2980 // state in such a way that each change won't have to update the
2922 // cursor and display (which would be awkward, slow, and
2981 // cursor and display (which would be awkward, slow, and
2923 // error-prone). Instead, display updates are batched and then all
2982 // error-prone). Instead, display updates are batched and then all
2924 // combined and executed at once.
2983 // combined and executed at once.
2925
2984
2926 var operationGroup = null;
2985 var operationGroup = null;
2927
2986
2928 var nextOpId = 0;
2987 var nextOpId = 0;
2929 // Start a new operation.
2988 // Start a new operation.
2930 function startOperation(cm) {
2989 function startOperation(cm) {
2931 cm.curOp = {
2990 cm.curOp = {
2932 cm: cm,
2991 cm: cm,
2933 viewChanged: false, // Flag that indicates that lines might need to be redrawn
2992 viewChanged: false, // Flag that indicates that lines might need to be redrawn
2934 startHeight: cm.doc.height, // Used to detect need to update scrollbar
2993 startHeight: cm.doc.height, // Used to detect need to update scrollbar
2935 forceUpdate: false, // Used to force a redraw
2994 forceUpdate: false, // Used to force a redraw
2936 updateInput: null, // Whether to reset the input textarea
2995 updateInput: null, // Whether to reset the input textarea
2937 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2996 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2938 changeObjs: null, // Accumulated changes, for firing change events
2997 changeObjs: null, // Accumulated changes, for firing change events
2939 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2998 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2940 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2999 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2941 selectionChanged: false, // Whether the selection needs to be redrawn
3000 selectionChanged: false, // Whether the selection needs to be redrawn
2942 updateMaxLine: false, // Set when the widest line needs to be determined anew
3001 updateMaxLine: false, // Set when the widest line needs to be determined anew
2943 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3002 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
2944 scrollToPos: null, // Used to scroll to a specific position
3003 scrollToPos: null, // Used to scroll to a specific position
2945 focus: false,
3004 focus: false,
2946 id: ++nextOpId // Unique ID
3005 id: ++nextOpId // Unique ID
2947 };
3006 };
2948 if (operationGroup) {
3007 if (operationGroup) {
2949 operationGroup.ops.push(cm.curOp);
3008 operationGroup.ops.push(cm.curOp);
2950 } else {
3009 } else {
2951 cm.curOp.ownsGroup = operationGroup = {
3010 cm.curOp.ownsGroup = operationGroup = {
2952 ops: [cm.curOp],
3011 ops: [cm.curOp],
2953 delayedCallbacks: []
3012 delayedCallbacks: []
2954 };
3013 };
2955 }
3014 }
2956 }
3015 }
2957
3016
2958 function fireCallbacksForOps(group) {
3017 function fireCallbacksForOps(group) {
2959 // Calls delayed callbacks and cursorActivity handlers until no
3018 // Calls delayed callbacks and cursorActivity handlers until no
2960 // new ones appear
3019 // new ones appear
2961 var callbacks = group.delayedCallbacks, i = 0;
3020 var callbacks = group.delayedCallbacks, i = 0;
2962 do {
3021 do {
2963 for (; i < callbacks.length; i++)
3022 for (; i < callbacks.length; i++)
2964 callbacks[i]();
3023 callbacks[i].call(null);
2965 for (var j = 0; j < group.ops.length; j++) {
3024 for (var j = 0; j < group.ops.length; j++) {
2966 var op = group.ops[j];
3025 var op = group.ops[j];
2967 if (op.cursorActivityHandlers)
3026 if (op.cursorActivityHandlers)
2968 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3027 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2969 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
3028 op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
2970 }
3029 }
2971 } while (i < callbacks.length);
3030 } while (i < callbacks.length);
2972 }
3031 }
2973
3032
2974 // Finish an operation, updating the display and signalling delayed events
3033 // Finish an operation, updating the display and signalling delayed events
2975 function endOperation(cm) {
3034 function endOperation(cm) {
2976 var op = cm.curOp, group = op.ownsGroup;
3035 var op = cm.curOp, group = op.ownsGroup;
2977 if (!group) return;
3036 if (!group) return;
2978
3037
2979 try { fireCallbacksForOps(group); }
3038 try { fireCallbacksForOps(group); }
2980 finally {
3039 finally {
2981 operationGroup = null;
3040 operationGroup = null;
2982 for (var i = 0; i < group.ops.length; i++)
3041 for (var i = 0; i < group.ops.length; i++)
2983 group.ops[i].cm.curOp = null;
3042 group.ops[i].cm.curOp = null;
2984 endOperations(group);
3043 endOperations(group);
2985 }
3044 }
2986 }
3045 }
2987
3046
2988 // The DOM updates done when an operation finishes are batched so
3047 // The DOM updates done when an operation finishes are batched so
2989 // that the minimum number of relayouts are required.
3048 // that the minimum number of relayouts are required.
2990 function endOperations(group) {
3049 function endOperations(group) {
2991 var ops = group.ops;
3050 var ops = group.ops;
2992 for (var i = 0; i < ops.length; i++) // Read DOM
3051 for (var i = 0; i < ops.length; i++) // Read DOM
2993 endOperation_R1(ops[i]);
3052 endOperation_R1(ops[i]);
2994 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3053 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2995 endOperation_W1(ops[i]);
3054 endOperation_W1(ops[i]);
2996 for (var i = 0; i < ops.length; i++) // Read DOM
3055 for (var i = 0; i < ops.length; i++) // Read DOM
2997 endOperation_R2(ops[i]);
3056 endOperation_R2(ops[i]);
2998 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3057 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2999 endOperation_W2(ops[i]);
3058 endOperation_W2(ops[i]);
3000 for (var i = 0; i < ops.length; i++) // Read DOM
3059 for (var i = 0; i < ops.length; i++) // Read DOM
3001 endOperation_finish(ops[i]);
3060 endOperation_finish(ops[i]);
3002 }
3061 }
3003
3062
3004 function endOperation_R1(op) {
3063 function endOperation_R1(op) {
3005 var cm = op.cm, display = cm.display;
3064 var cm = op.cm, display = cm.display;
3006 maybeClipScrollbars(cm);
3065 maybeClipScrollbars(cm);
3007 if (op.updateMaxLine) findMaxLine(cm);
3066 if (op.updateMaxLine) findMaxLine(cm);
3008
3067
3009 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3068 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3010 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3069 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3011 op.scrollToPos.to.line >= display.viewTo) ||
3070 op.scrollToPos.to.line >= display.viewTo) ||
3012 display.maxLineChanged && cm.options.lineWrapping;
3071 display.maxLineChanged && cm.options.lineWrapping;
3013 op.update = op.mustUpdate &&
3072 op.update = op.mustUpdate &&
3014 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3073 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3015 }
3074 }
3016
3075
3017 function endOperation_W1(op) {
3076 function endOperation_W1(op) {
3018 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3077 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3019 }
3078 }
3020
3079
3021 function endOperation_R2(op) {
3080 function endOperation_R2(op) {
3022 var cm = op.cm, display = cm.display;
3081 var cm = op.cm, display = cm.display;
3023 if (op.updatedDisplay) updateHeightsInViewport(cm);
3082 if (op.updatedDisplay) updateHeightsInViewport(cm);
3024
3083
3025 op.barMeasure = measureForScrollbars(cm);
3084 op.barMeasure = measureForScrollbars(cm);
3026
3085
3027 // If the max line changed since it was last measured, measure it,
3086 // If the max line changed since it was last measured, measure it,
3028 // and ensure the document's width matches it.
3087 // and ensure the document's width matches it.
3029 // updateDisplay_W2 will use these properties to do the actual resizing
3088 // updateDisplay_W2 will use these properties to do the actual resizing
3030 if (display.maxLineChanged && !cm.options.lineWrapping) {
3089 if (display.maxLineChanged && !cm.options.lineWrapping) {
3031 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3090 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3032 cm.display.sizerWidth = op.adjustWidthTo;
3091 cm.display.sizerWidth = op.adjustWidthTo;
3033 op.barMeasure.scrollWidth =
3092 op.barMeasure.scrollWidth =
3034 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3093 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3035 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3094 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3036 }
3095 }
3037
3096
3038 if (op.updatedDisplay || op.selectionChanged)
3097 if (op.updatedDisplay || op.selectionChanged)
3039 op.preparedSelection = display.input.prepareSelection();
3098 op.preparedSelection = display.input.prepareSelection();
3040 }
3099 }
3041
3100
3042 function endOperation_W2(op) {
3101 function endOperation_W2(op) {
3043 var cm = op.cm;
3102 var cm = op.cm;
3044
3103
3045 if (op.adjustWidthTo != null) {
3104 if (op.adjustWidthTo != null) {
3046 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3105 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3047 if (op.maxScrollLeft < cm.doc.scrollLeft)
3106 if (op.maxScrollLeft < cm.doc.scrollLeft)
3048 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3107 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3049 cm.display.maxLineChanged = false;
3108 cm.display.maxLineChanged = false;
3050 }
3109 }
3051
3110
3052 if (op.preparedSelection)
3111 if (op.preparedSelection)
3053 cm.display.input.showSelection(op.preparedSelection);
3112 cm.display.input.showSelection(op.preparedSelection);
3054 if (op.updatedDisplay)
3113 if (op.updatedDisplay)
3055 setDocumentHeight(cm, op.barMeasure);
3114 setDocumentHeight(cm, op.barMeasure);
3056 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3115 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3057 updateScrollbars(cm, op.barMeasure);
3116 updateScrollbars(cm, op.barMeasure);
3058
3117
3059 if (op.selectionChanged) restartBlink(cm);
3118 if (op.selectionChanged) restartBlink(cm);
3060
3119
3061 if (cm.state.focused && op.updateInput)
3120 if (cm.state.focused && op.updateInput)
3062 cm.display.input.reset(op.typing);
3121 cm.display.input.reset(op.typing);
3063 if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);
3122 if (op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()))
3123 ensureFocus(op.cm);
3064 }
3124 }
3065
3125
3066 function endOperation_finish(op) {
3126 function endOperation_finish(op) {
3067 var cm = op.cm, display = cm.display, doc = cm.doc;
3127 var cm = op.cm, display = cm.display, doc = cm.doc;
3068
3128
3069 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3129 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3070
3130
3071 // Abort mouse wheel delta measurement, when scrolling explicitly
3131 // Abort mouse wheel delta measurement, when scrolling explicitly
3072 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3132 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3073 display.wheelStartX = display.wheelStartY = null;
3133 display.wheelStartX = display.wheelStartY = null;
3074
3134
3075 // Propagate the scroll position to the actual DOM scroller
3135 // Propagate the scroll position to the actual DOM scroller
3076 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3136 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3077 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3137 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3078 display.scrollbars.setScrollTop(doc.scrollTop);
3138 display.scrollbars.setScrollTop(doc.scrollTop);
3079 display.scroller.scrollTop = doc.scrollTop;
3139 display.scroller.scrollTop = doc.scrollTop;
3080 }
3140 }
3081 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3141 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3082 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3142 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3083 display.scrollbars.setScrollLeft(doc.scrollLeft);
3143 display.scrollbars.setScrollLeft(doc.scrollLeft);
3084 display.scroller.scrollLeft = doc.scrollLeft;
3144 display.scroller.scrollLeft = doc.scrollLeft;
3085 alignHorizontally(cm);
3145 alignHorizontally(cm);
3086 }
3146 }
3087 // If we need to scroll a specific position into view, do so.
3147 // If we need to scroll a specific position into view, do so.
3088 if (op.scrollToPos) {
3148 if (op.scrollToPos) {
3089 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3149 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3090 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3150 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3091 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3151 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3092 }
3152 }
3093
3153
3094 // Fire events for markers that are hidden/unidden by editing or
3154 // Fire events for markers that are hidden/unidden by editing or
3095 // undoing
3155 // undoing
3096 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3156 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3097 if (hidden) for (var i = 0; i < hidden.length; ++i)
3157 if (hidden) for (var i = 0; i < hidden.length; ++i)
3098 if (!hidden[i].lines.length) signal(hidden[i], "hide");
3158 if (!hidden[i].lines.length) signal(hidden[i], "hide");
3099 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3159 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3100 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3160 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3101
3161
3102 if (display.wrapper.offsetHeight)
3162 if (display.wrapper.offsetHeight)
3103 doc.scrollTop = cm.display.scroller.scrollTop;
3163 doc.scrollTop = cm.display.scroller.scrollTop;
3104
3164
3105 // Fire change events, and delayed event handlers
3165 // Fire change events, and delayed event handlers
3106 if (op.changeObjs)
3166 if (op.changeObjs)
3107 signal(cm, "changes", cm, op.changeObjs);
3167 signal(cm, "changes", cm, op.changeObjs);
3108 if (op.update)
3168 if (op.update)
3109 op.update.finish();
3169 op.update.finish();
3110 }
3170 }
3111
3171
3112 // Run the given function in an operation
3172 // Run the given function in an operation
3113 function runInOp(cm, f) {
3173 function runInOp(cm, f) {
3114 if (cm.curOp) return f();
3174 if (cm.curOp) return f();
3115 startOperation(cm);
3175 startOperation(cm);
3116 try { return f(); }
3176 try { return f(); }
3117 finally { endOperation(cm); }
3177 finally { endOperation(cm); }
3118 }
3178 }
3119 // Wraps a function in an operation. Returns the wrapped function.
3179 // Wraps a function in an operation. Returns the wrapped function.
3120 function operation(cm, f) {
3180 function operation(cm, f) {
3121 return function() {
3181 return function() {
3122 if (cm.curOp) return f.apply(cm, arguments);
3182 if (cm.curOp) return f.apply(cm, arguments);
3123 startOperation(cm);
3183 startOperation(cm);
3124 try { return f.apply(cm, arguments); }
3184 try { return f.apply(cm, arguments); }
3125 finally { endOperation(cm); }
3185 finally { endOperation(cm); }
3126 };
3186 };
3127 }
3187 }
3128 // Used to add methods to editor and doc instances, wrapping them in
3188 // Used to add methods to editor and doc instances, wrapping them in
3129 // operations.
3189 // operations.
3130 function methodOp(f) {
3190 function methodOp(f) {
3131 return function() {
3191 return function() {
3132 if (this.curOp) return f.apply(this, arguments);
3192 if (this.curOp) return f.apply(this, arguments);
3133 startOperation(this);
3193 startOperation(this);
3134 try { return f.apply(this, arguments); }
3194 try { return f.apply(this, arguments); }
3135 finally { endOperation(this); }
3195 finally { endOperation(this); }
3136 };
3196 };
3137 }
3197 }
3138 function docMethodOp(f) {
3198 function docMethodOp(f) {
3139 return function() {
3199 return function() {
3140 var cm = this.cm;
3200 var cm = this.cm;
3141 if (!cm || cm.curOp) return f.apply(this, arguments);
3201 if (!cm || cm.curOp) return f.apply(this, arguments);
3142 startOperation(cm);
3202 startOperation(cm);
3143 try { return f.apply(this, arguments); }
3203 try { return f.apply(this, arguments); }
3144 finally { endOperation(cm); }
3204 finally { endOperation(cm); }
3145 };
3205 };
3146 }
3206 }
3147
3207
3148 // VIEW TRACKING
3208 // VIEW TRACKING
3149
3209
3150 // These objects are used to represent the visible (currently drawn)
3210 // These objects are used to represent the visible (currently drawn)
3151 // part of the document. A LineView may correspond to multiple
3211 // part of the document. A LineView may correspond to multiple
3152 // logical lines, if those are connected by collapsed ranges.
3212 // logical lines, if those are connected by collapsed ranges.
3153 function LineView(doc, line, lineN) {
3213 function LineView(doc, line, lineN) {
3154 // The starting line
3214 // The starting line
3155 this.line = line;
3215 this.line = line;
3156 // Continuing lines, if any
3216 // Continuing lines, if any
3157 this.rest = visualLineContinued(line);
3217 this.rest = visualLineContinued(line);
3158 // Number of logical lines in this visual line
3218 // Number of logical lines in this visual line
3159 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3219 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3160 this.node = this.text = null;
3220 this.node = this.text = null;
3161 this.hidden = lineIsHidden(doc, line);
3221 this.hidden = lineIsHidden(doc, line);
3162 }
3222 }
3163
3223
3164 // Create a range of LineView objects for the given lines.
3224 // Create a range of LineView objects for the given lines.
3165 function buildViewArray(cm, from, to) {
3225 function buildViewArray(cm, from, to) {
3166 var array = [], nextPos;
3226 var array = [], nextPos;
3167 for (var pos = from; pos < to; pos = nextPos) {
3227 for (var pos = from; pos < to; pos = nextPos) {
3168 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3228 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3169 nextPos = pos + view.size;
3229 nextPos = pos + view.size;
3170 array.push(view);
3230 array.push(view);
3171 }
3231 }
3172 return array;
3232 return array;
3173 }
3233 }
3174
3234
3175 // Updates the display.view data structure for a given change to the
3235 // Updates the display.view data structure for a given change to the
3176 // document. From and to are in pre-change coordinates. Lendiff is
3236 // document. From and to are in pre-change coordinates. Lendiff is
3177 // the amount of lines added or subtracted by the change. This is
3237 // the amount of lines added or subtracted by the change. This is
3178 // used for changes that span multiple lines, or change the way
3238 // used for changes that span multiple lines, or change the way
3179 // lines are divided into visual lines. regLineChange (below)
3239 // lines are divided into visual lines. regLineChange (below)
3180 // registers single-line changes.
3240 // registers single-line changes.
3181 function regChange(cm, from, to, lendiff) {
3241 function regChange(cm, from, to, lendiff) {
3182 if (from == null) from = cm.doc.first;
3242 if (from == null) from = cm.doc.first;
3183 if (to == null) to = cm.doc.first + cm.doc.size;
3243 if (to == null) to = cm.doc.first + cm.doc.size;
3184 if (!lendiff) lendiff = 0;
3244 if (!lendiff) lendiff = 0;
3185
3245
3186 var display = cm.display;
3246 var display = cm.display;
3187 if (lendiff && to < display.viewTo &&
3247 if (lendiff && to < display.viewTo &&
3188 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3248 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3189 display.updateLineNumbers = from;
3249 display.updateLineNumbers = from;
3190
3250
3191 cm.curOp.viewChanged = true;
3251 cm.curOp.viewChanged = true;
3192
3252
3193 if (from >= display.viewTo) { // Change after
3253 if (from >= display.viewTo) { // Change after
3194 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3254 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3195 resetView(cm);
3255 resetView(cm);
3196 } else if (to <= display.viewFrom) { // Change before
3256 } else if (to <= display.viewFrom) { // Change before
3197 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3257 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3198 resetView(cm);
3258 resetView(cm);
3199 } else {
3259 } else {
3200 display.viewFrom += lendiff;
3260 display.viewFrom += lendiff;
3201 display.viewTo += lendiff;
3261 display.viewTo += lendiff;
3202 }
3262 }
3203 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3263 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3204 resetView(cm);
3264 resetView(cm);
3205 } else if (from <= display.viewFrom) { // Top overlap
3265 } else if (from <= display.viewFrom) { // Top overlap
3206 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3266 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3207 if (cut) {
3267 if (cut) {
3208 display.view = display.view.slice(cut.index);
3268 display.view = display.view.slice(cut.index);
3209 display.viewFrom = cut.lineN;
3269 display.viewFrom = cut.lineN;
3210 display.viewTo += lendiff;
3270 display.viewTo += lendiff;
3211 } else {
3271 } else {
3212 resetView(cm);
3272 resetView(cm);
3213 }
3273 }
3214 } else if (to >= display.viewTo) { // Bottom overlap
3274 } else if (to >= display.viewTo) { // Bottom overlap
3215 var cut = viewCuttingPoint(cm, from, from, -1);
3275 var cut = viewCuttingPoint(cm, from, from, -1);
3216 if (cut) {
3276 if (cut) {
3217 display.view = display.view.slice(0, cut.index);
3277 display.view = display.view.slice(0, cut.index);
3218 display.viewTo = cut.lineN;
3278 display.viewTo = cut.lineN;
3219 } else {
3279 } else {
3220 resetView(cm);
3280 resetView(cm);
3221 }
3281 }
3222 } else { // Gap in the middle
3282 } else { // Gap in the middle
3223 var cutTop = viewCuttingPoint(cm, from, from, -1);
3283 var cutTop = viewCuttingPoint(cm, from, from, -1);
3224 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3284 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3225 if (cutTop && cutBot) {
3285 if (cutTop && cutBot) {
3226 display.view = display.view.slice(0, cutTop.index)
3286 display.view = display.view.slice(0, cutTop.index)
3227 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3287 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3228 .concat(display.view.slice(cutBot.index));
3288 .concat(display.view.slice(cutBot.index));
3229 display.viewTo += lendiff;
3289 display.viewTo += lendiff;
3230 } else {
3290 } else {
3231 resetView(cm);
3291 resetView(cm);
3232 }
3292 }
3233 }
3293 }
3234
3294
3235 var ext = display.externalMeasured;
3295 var ext = display.externalMeasured;
3236 if (ext) {
3296 if (ext) {
3237 if (to < ext.lineN)
3297 if (to < ext.lineN)
3238 ext.lineN += lendiff;
3298 ext.lineN += lendiff;
3239 else if (from < ext.lineN + ext.size)
3299 else if (from < ext.lineN + ext.size)
3240 display.externalMeasured = null;
3300 display.externalMeasured = null;
3241 }
3301 }
3242 }
3302 }
3243
3303
3244 // Register a change to a single line. Type must be one of "text",
3304 // Register a change to a single line. Type must be one of "text",
3245 // "gutter", "class", "widget"
3305 // "gutter", "class", "widget"
3246 function regLineChange(cm, line, type) {
3306 function regLineChange(cm, line, type) {
3247 cm.curOp.viewChanged = true;
3307 cm.curOp.viewChanged = true;
3248 var display = cm.display, ext = cm.display.externalMeasured;
3308 var display = cm.display, ext = cm.display.externalMeasured;
3249 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3309 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3250 display.externalMeasured = null;
3310 display.externalMeasured = null;
3251
3311
3252 if (line < display.viewFrom || line >= display.viewTo) return;
3312 if (line < display.viewFrom || line >= display.viewTo) return;
3253 var lineView = display.view[findViewIndex(cm, line)];
3313 var lineView = display.view[findViewIndex(cm, line)];
3254 if (lineView.node == null) return;
3314 if (lineView.node == null) return;
3255 var arr = lineView.changes || (lineView.changes = []);
3315 var arr = lineView.changes || (lineView.changes = []);
3256 if (indexOf(arr, type) == -1) arr.push(type);
3316 if (indexOf(arr, type) == -1) arr.push(type);
3257 }
3317 }
3258
3318
3259 // Clear the view.
3319 // Clear the view.
3260 function resetView(cm) {
3320 function resetView(cm) {
3261 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3321 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3262 cm.display.view = [];
3322 cm.display.view = [];
3263 cm.display.viewOffset = 0;
3323 cm.display.viewOffset = 0;
3264 }
3324 }
3265
3325
3266 // Find the view element corresponding to a given line. Return null
3326 // Find the view element corresponding to a given line. Return null
3267 // when the line isn't visible.
3327 // when the line isn't visible.
3268 function findViewIndex(cm, n) {
3328 function findViewIndex(cm, n) {
3269 if (n >= cm.display.viewTo) return null;
3329 if (n >= cm.display.viewTo) return null;
3270 n -= cm.display.viewFrom;
3330 n -= cm.display.viewFrom;
3271 if (n < 0) return null;
3331 if (n < 0) return null;
3272 var view = cm.display.view;
3332 var view = cm.display.view;
3273 for (var i = 0; i < view.length; i++) {
3333 for (var i = 0; i < view.length; i++) {
3274 n -= view[i].size;
3334 n -= view[i].size;
3275 if (n < 0) return i;
3335 if (n < 0) return i;
3276 }
3336 }
3277 }
3337 }
3278
3338
3279 function viewCuttingPoint(cm, oldN, newN, dir) {
3339 function viewCuttingPoint(cm, oldN, newN, dir) {
3280 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3340 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3281 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3341 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3282 return {index: index, lineN: newN};
3342 return {index: index, lineN: newN};
3283 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3343 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3284 n += view[i].size;
3344 n += view[i].size;
3285 if (n != oldN) {
3345 if (n != oldN) {
3286 if (dir > 0) {
3346 if (dir > 0) {
3287 if (index == view.length - 1) return null;
3347 if (index == view.length - 1) return null;
3288 diff = (n + view[index].size) - oldN;
3348 diff = (n + view[index].size) - oldN;
3289 index++;
3349 index++;
3290 } else {
3350 } else {
3291 diff = n - oldN;
3351 diff = n - oldN;
3292 }
3352 }
3293 oldN += diff; newN += diff;
3353 oldN += diff; newN += diff;
3294 }
3354 }
3295 while (visualLineNo(cm.doc, newN) != newN) {
3355 while (visualLineNo(cm.doc, newN) != newN) {
3296 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3356 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3297 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3357 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3298 index += dir;
3358 index += dir;
3299 }
3359 }
3300 return {index: index, lineN: newN};
3360 return {index: index, lineN: newN};
3301 }
3361 }
3302
3362
3303 // Force the view to cover a given range, adding empty view element
3363 // Force the view to cover a given range, adding empty view element
3304 // or clipping off existing ones as needed.
3364 // or clipping off existing ones as needed.
3305 function adjustView(cm, from, to) {
3365 function adjustView(cm, from, to) {
3306 var display = cm.display, view = display.view;
3366 var display = cm.display, view = display.view;
3307 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3367 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3308 display.view = buildViewArray(cm, from, to);
3368 display.view = buildViewArray(cm, from, to);
3309 display.viewFrom = from;
3369 display.viewFrom = from;
3310 } else {
3370 } else {
3311 if (display.viewFrom > from)
3371 if (display.viewFrom > from)
3312 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3372 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3313 else if (display.viewFrom < from)
3373 else if (display.viewFrom < from)
3314 display.view = display.view.slice(findViewIndex(cm, from));
3374 display.view = display.view.slice(findViewIndex(cm, from));
3315 display.viewFrom = from;
3375 display.viewFrom = from;
3316 if (display.viewTo < to)
3376 if (display.viewTo < to)
3317 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3377 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3318 else if (display.viewTo > to)
3378 else if (display.viewTo > to)
3319 display.view = display.view.slice(0, findViewIndex(cm, to));
3379 display.view = display.view.slice(0, findViewIndex(cm, to));
3320 }
3380 }
3321 display.viewTo = to;
3381 display.viewTo = to;
3322 }
3382 }
3323
3383
3324 // Count the number of lines in the view whose DOM representation is
3384 // Count the number of lines in the view whose DOM representation is
3325 // out of date (or nonexistent).
3385 // out of date (or nonexistent).
3326 function countDirtyView(cm) {
3386 function countDirtyView(cm) {
3327 var view = cm.display.view, dirty = 0;
3387 var view = cm.display.view, dirty = 0;
3328 for (var i = 0; i < view.length; i++) {
3388 for (var i = 0; i < view.length; i++) {
3329 var lineView = view[i];
3389 var lineView = view[i];
3330 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3390 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3331 }
3391 }
3332 return dirty;
3392 return dirty;
3333 }
3393 }
3334
3394
3335 // EVENT HANDLERS
3395 // EVENT HANDLERS
3336
3396
3337 // Attach the necessary event handlers when initializing the editor
3397 // Attach the necessary event handlers when initializing the editor
3338 function registerEventHandlers(cm) {
3398 function registerEventHandlers(cm) {
3339 var d = cm.display;
3399 var d = cm.display;
3340 on(d.scroller, "mousedown", operation(cm, onMouseDown));
3400 on(d.scroller, "mousedown", operation(cm, onMouseDown));
3341 // Older IE's will not fire a second mousedown for a double click
3401 // Older IE's will not fire a second mousedown for a double click
3342 if (ie && ie_version < 11)
3402 if (ie && ie_version < 11)
3343 on(d.scroller, "dblclick", operation(cm, function(e) {
3403 on(d.scroller, "dblclick", operation(cm, function(e) {
3344 if (signalDOMEvent(cm, e)) return;
3404 if (signalDOMEvent(cm, e)) return;
3345 var pos = posFromMouse(cm, e);
3405 var pos = posFromMouse(cm, e);
3346 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3406 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3347 e_preventDefault(e);
3407 e_preventDefault(e);
3348 var word = cm.findWordAt(pos);
3408 var word = cm.findWordAt(pos);
3349 extendSelection(cm.doc, word.anchor, word.head);
3409 extendSelection(cm.doc, word.anchor, word.head);
3350 }));
3410 }));
3351 else
3411 else
3352 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3412 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3353 // Some browsers fire contextmenu *after* opening the menu, at
3413 // Some browsers fire contextmenu *after* opening the menu, at
3354 // which point we can't mess with it anymore. Context menu is
3414 // which point we can't mess with it anymore. Context menu is
3355 // handled in onMouseDown for these browsers.
3415 // handled in onMouseDown for these browsers.
3356 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3416 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3357
3417
3358 // Used to suppress mouse event handling when a touch happens
3418 // Used to suppress mouse event handling when a touch happens
3359 var touchFinished, prevTouch = {end: 0};
3419 var touchFinished, prevTouch = {end: 0};
3360 function finishTouch() {
3420 function finishTouch() {
3361 if (d.activeTouch) {
3421 if (d.activeTouch) {
3362 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3422 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3363 prevTouch = d.activeTouch;
3423 prevTouch = d.activeTouch;
3364 prevTouch.end = +new Date;
3424 prevTouch.end = +new Date;
3365 }
3425 }
3366 };
3426 };
3367 function isMouseLikeTouchEvent(e) {
3427 function isMouseLikeTouchEvent(e) {
3368 if (e.touches.length != 1) return false;
3428 if (e.touches.length != 1) return false;
3369 var touch = e.touches[0];
3429 var touch = e.touches[0];
3370 return touch.radiusX <= 1 && touch.radiusY <= 1;
3430 return touch.radiusX <= 1 && touch.radiusY <= 1;
3371 }
3431 }
3372 function farAway(touch, other) {
3432 function farAway(touch, other) {
3373 if (other.left == null) return true;
3433 if (other.left == null) return true;
3374 var dx = other.left - touch.left, dy = other.top - touch.top;
3434 var dx = other.left - touch.left, dy = other.top - touch.top;
3375 return dx * dx + dy * dy > 20 * 20;
3435 return dx * dx + dy * dy > 20 * 20;
3376 }
3436 }
3377 on(d.scroller, "touchstart", function(e) {
3437 on(d.scroller, "touchstart", function(e) {
3378 if (!isMouseLikeTouchEvent(e)) {
3438 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
3379 clearTimeout(touchFinished);
3439 clearTimeout(touchFinished);
3380 var now = +new Date;
3440 var now = +new Date;
3381 d.activeTouch = {start: now, moved: false,
3441 d.activeTouch = {start: now, moved: false,
3382 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3442 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3383 if (e.touches.length == 1) {
3443 if (e.touches.length == 1) {
3384 d.activeTouch.left = e.touches[0].pageX;
3444 d.activeTouch.left = e.touches[0].pageX;
3385 d.activeTouch.top = e.touches[0].pageY;
3445 d.activeTouch.top = e.touches[0].pageY;
3386 }
3446 }
3387 }
3447 }
3388 });
3448 });
3389 on(d.scroller, "touchmove", function() {
3449 on(d.scroller, "touchmove", function() {
3390 if (d.activeTouch) d.activeTouch.moved = true;
3450 if (d.activeTouch) d.activeTouch.moved = true;
3391 });
3451 });
3392 on(d.scroller, "touchend", function(e) {
3452 on(d.scroller, "touchend", function(e) {
3393 var touch = d.activeTouch;
3453 var touch = d.activeTouch;
3394 if (touch && !eventInWidget(d, e) && touch.left != null &&
3454 if (touch && !eventInWidget(d, e) && touch.left != null &&
3395 !touch.moved && new Date - touch.start < 300) {
3455 !touch.moved && new Date - touch.start < 300) {
3396 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3456 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3397 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3457 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3398 range = new Range(pos, pos);
3458 range = new Range(pos, pos);
3399 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3459 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3400 range = cm.findWordAt(pos);
3460 range = cm.findWordAt(pos);
3401 else // Triple tap
3461 else // Triple tap
3402 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3462 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3403 cm.setSelection(range.anchor, range.head);
3463 cm.setSelection(range.anchor, range.head);
3404 cm.focus();
3464 cm.focus();
3405 e_preventDefault(e);
3465 e_preventDefault(e);
3406 }
3466 }
3407 finishTouch();
3467 finishTouch();
3408 });
3468 });
3409 on(d.scroller, "touchcancel", finishTouch);
3469 on(d.scroller, "touchcancel", finishTouch);
3410
3470
3411 // Sync scrolling between fake scrollbars and real scrollable
3471 // Sync scrolling between fake scrollbars and real scrollable
3412 // area, ensure viewport is updated when scrolling.
3472 // area, ensure viewport is updated when scrolling.
3413 on(d.scroller, "scroll", function() {
3473 on(d.scroller, "scroll", function() {
3414 if (d.scroller.clientHeight) {
3474 if (d.scroller.clientHeight) {
3415 setScrollTop(cm, d.scroller.scrollTop);
3475 setScrollTop(cm, d.scroller.scrollTop);
3416 setScrollLeft(cm, d.scroller.scrollLeft, true);
3476 setScrollLeft(cm, d.scroller.scrollLeft, true);
3417 signal(cm, "scroll", cm);
3477 signal(cm, "scroll", cm);
3418 }
3478 }
3419 });
3479 });
3420
3480
3421 // Listen to wheel events in order to try and update the viewport on time.
3481 // Listen to wheel events in order to try and update the viewport on time.
3422 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3482 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3423 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3483 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3424
3484
3425 // Prevent wrapper from ever scrolling
3485 // Prevent wrapper from ever scrolling
3426 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3486 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3427
3487
3428 d.dragFunctions = {
3488 d.dragFunctions = {
3429 simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3489 enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3490 over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
3430 start: function(e){onDragStart(cm, e);},
3491 start: function(e){onDragStart(cm, e);},
3431 drop: operation(cm, onDrop)
3492 drop: operation(cm, onDrop),
3493 leave: function() {clearDragCursor(cm);}
3432 };
3494 };
3433
3495
3434 var inp = d.input.getField();
3496 var inp = d.input.getField();
3435 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3497 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3436 on(inp, "keydown", operation(cm, onKeyDown));
3498 on(inp, "keydown", operation(cm, onKeyDown));
3437 on(inp, "keypress", operation(cm, onKeyPress));
3499 on(inp, "keypress", operation(cm, onKeyPress));
3438 on(inp, "focus", bind(onFocus, cm));
3500 on(inp, "focus", bind(onFocus, cm));
3439 on(inp, "blur", bind(onBlur, cm));
3501 on(inp, "blur", bind(onBlur, cm));
3440 }
3502 }
3441
3503
3442 function dragDropChanged(cm, value, old) {
3504 function dragDropChanged(cm, value, old) {
3443 var wasOn = old && old != CodeMirror.Init;
3505 var wasOn = old && old != CodeMirror.Init;
3444 if (!value != !wasOn) {
3506 if (!value != !wasOn) {
3445 var funcs = cm.display.dragFunctions;
3507 var funcs = cm.display.dragFunctions;
3446 var toggle = value ? on : off;
3508 var toggle = value ? on : off;
3447 toggle(cm.display.scroller, "dragstart", funcs.start);
3509 toggle(cm.display.scroller, "dragstart", funcs.start);
3448 toggle(cm.display.scroller, "dragenter", funcs.simple);
3510 toggle(cm.display.scroller, "dragenter", funcs.enter);
3449 toggle(cm.display.scroller, "dragover", funcs.simple);
3511 toggle(cm.display.scroller, "dragover", funcs.over);
3512 toggle(cm.display.scroller, "dragleave", funcs.leave);
3450 toggle(cm.display.scroller, "drop", funcs.drop);
3513 toggle(cm.display.scroller, "drop", funcs.drop);
3451 }
3514 }
3452 }
3515 }
3453
3516
3454 // Called when the window resizes
3517 // Called when the window resizes
3455 function onResize(cm) {
3518 function onResize(cm) {
3456 var d = cm.display;
3519 var d = cm.display;
3457 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3520 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3458 return;
3521 return;
3459 // Might be a text scaling operation, clear size caches.
3522 // Might be a text scaling operation, clear size caches.
3460 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3523 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3461 d.scrollbarsClipped = false;
3524 d.scrollbarsClipped = false;
3462 cm.setSize();
3525 cm.setSize();
3463 }
3526 }
3464
3527
3465 // MOUSE EVENTS
3528 // MOUSE EVENTS
3466
3529
3467 // Return true when the given mouse event happened in a widget
3530 // Return true when the given mouse event happened in a widget
3468 function eventInWidget(display, e) {
3531 function eventInWidget(display, e) {
3469 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3532 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3470 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3533 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3471 (n.parentNode == display.sizer && n != display.mover))
3534 (n.parentNode == display.sizer && n != display.mover))
3472 return true;
3535 return true;
3473 }
3536 }
3474 }
3537 }
3475
3538
3476 // Given a mouse event, find the corresponding position. If liberal
3539 // Given a mouse event, find the corresponding position. If liberal
3477 // is false, it checks whether a gutter or scrollbar was clicked,
3540 // is false, it checks whether a gutter or scrollbar was clicked,
3478 // and returns null if it was. forRect is used by rectangular
3541 // and returns null if it was. forRect is used by rectangular
3479 // selections, and tries to estimate a character position even for
3542 // selections, and tries to estimate a character position even for
3480 // coordinates beyond the right of the text.
3543 // coordinates beyond the right of the text.
3481 function posFromMouse(cm, e, liberal, forRect) {
3544 function posFromMouse(cm, e, liberal, forRect) {
3482 var display = cm.display;
3545 var display = cm.display;
3483 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3546 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3484
3547
3485 var x, y, space = display.lineSpace.getBoundingClientRect();
3548 var x, y, space = display.lineSpace.getBoundingClientRect();
3486 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3549 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3487 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3550 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3488 catch (e) { return null; }
3551 catch (e) { return null; }
3489 var coords = coordsChar(cm, x, y), line;
3552 var coords = coordsChar(cm, x, y), line;
3490 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3553 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3491 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3554 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3492 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3555 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3493 }
3556 }
3494 return coords;
3557 return coords;
3495 }
3558 }
3496
3559
3497 // A mouse down can be a single click, double click, triple click,
3560 // A mouse down can be a single click, double click, triple click,
3498 // start of selection drag, start of text drag, new cursor
3561 // start of selection drag, start of text drag, new cursor
3499 // (ctrl-click), rectangle drag (alt-drag), or xwin
3562 // (ctrl-click), rectangle drag (alt-drag), or xwin
3500 // middle-click-paste. Or it might be a click on something we should
3563 // middle-click-paste. Or it might be a click on something we should
3501 // not interfere with, such as a scrollbar or widget.
3564 // not interfere with, such as a scrollbar or widget.
3502 function onMouseDown(e) {
3565 function onMouseDown(e) {
3503 var cm = this, display = cm.display;
3566 var cm = this, display = cm.display;
3504 if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
3567 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return;
3505 display.shift = e.shiftKey;
3568 display.shift = e.shiftKey;
3506
3569
3507 if (eventInWidget(display, e)) {
3570 if (eventInWidget(display, e)) {
3508 if (!webkit) {
3571 if (!webkit) {
3509 // Briefly turn off draggability, to allow widgets to do
3572 // Briefly turn off draggability, to allow widgets to do
3510 // normal dragging things.
3573 // normal dragging things.
3511 display.scroller.draggable = false;
3574 display.scroller.draggable = false;
3512 setTimeout(function(){display.scroller.draggable = true;}, 100);
3575 setTimeout(function(){display.scroller.draggable = true;}, 100);
3513 }
3576 }
3514 return;
3577 return;
3515 }
3578 }
3516 if (clickInGutter(cm, e)) return;
3579 if (clickInGutter(cm, e)) return;
3517 var start = posFromMouse(cm, e);
3580 var start = posFromMouse(cm, e);
3518 window.focus();
3581 window.focus();
3519
3582
3520 switch (e_button(e)) {
3583 switch (e_button(e)) {
3521 case 1:
3584 case 1:
3522 if (start)
3585 // #3261: make sure, that we're not starting a second selection
3586 if (cm.state.selectingText)
3587 cm.state.selectingText(e);
3588 else if (start)
3523 leftButtonDown(cm, e, start);
3589 leftButtonDown(cm, e, start);
3524 else if (e_target(e) == display.scroller)
3590 else if (e_target(e) == display.scroller)
3525 e_preventDefault(e);
3591 e_preventDefault(e);
3526 break;
3592 break;
3527 case 2:
3593 case 2:
3528 if (webkit) cm.state.lastMiddleDown = +new Date;
3594 if (webkit) cm.state.lastMiddleDown = +new Date;
3529 if (start) extendSelection(cm.doc, start);
3595 if (start) extendSelection(cm.doc, start);
3530 setTimeout(function() {display.input.focus();}, 20);
3596 setTimeout(function() {display.input.focus();}, 20);
3531 e_preventDefault(e);
3597 e_preventDefault(e);
3532 break;
3598 break;
3533 case 3:
3599 case 3:
3534 if (captureRightClick) onContextMenu(cm, e);
3600 if (captureRightClick) onContextMenu(cm, e);
3535 else delayBlurEvent(cm);
3601 else delayBlurEvent(cm);
3536 break;
3602 break;
3537 }
3603 }
3538 }
3604 }
3539
3605
3540 var lastClick, lastDoubleClick;
3606 var lastClick, lastDoubleClick;
3541 function leftButtonDown(cm, e, start) {
3607 function leftButtonDown(cm, e, start) {
3542 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3608 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3543 else cm.curOp.focus = activeElt();
3609 else cm.curOp.focus = activeElt();
3544
3610
3545 var now = +new Date, type;
3611 var now = +new Date, type;
3546 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
3612 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
3547 type = "triple";
3613 type = "triple";
3548 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
3614 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
3549 type = "double";
3615 type = "double";
3550 lastDoubleClick = {time: now, pos: start};
3616 lastDoubleClick = {time: now, pos: start};
3551 } else {
3617 } else {
3552 type = "single";
3618 type = "single";
3553 lastClick = {time: now, pos: start};
3619 lastClick = {time: now, pos: start};
3554 }
3620 }
3555
3621
3556 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3622 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3557 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
3623 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
3558 type == "single" && (contained = sel.contains(start)) > -1 &&
3624 type == "single" && (contained = sel.contains(start)) > -1 &&
3559 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
3625 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
3560 (cmp(contained.to(), start) > 0 || start.xRel < 0))
3626 (cmp(contained.to(), start) > 0 || start.xRel < 0))
3561 leftButtonStartDrag(cm, e, start, modifier);
3627 leftButtonStartDrag(cm, e, start, modifier);
3562 else
3628 else
3563 leftButtonSelect(cm, e, start, type, modifier);
3629 leftButtonSelect(cm, e, start, type, modifier);
3564 }
3630 }
3565
3631
3566 // Start a text drag. When it ends, see if any dragging actually
3632 // Start a text drag. When it ends, see if any dragging actually
3567 // happen, and treat as a click if it didn't.
3633 // happen, and treat as a click if it didn't.
3568 function leftButtonStartDrag(cm, e, start, modifier) {
3634 function leftButtonStartDrag(cm, e, start, modifier) {
3569 var display = cm.display, startTime = +new Date;
3635 var display = cm.display, startTime = +new Date;
3570 var dragEnd = operation(cm, function(e2) {
3636 var dragEnd = operation(cm, function(e2) {
3571 if (webkit) display.scroller.draggable = false;
3637 if (webkit) display.scroller.draggable = false;
3572 cm.state.draggingText = false;
3638 cm.state.draggingText = false;
3573 off(document, "mouseup", dragEnd);
3639 off(document, "mouseup", dragEnd);
3574 off(display.scroller, "drop", dragEnd);
3640 off(display.scroller, "drop", dragEnd);
3575 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3641 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3576 e_preventDefault(e2);
3642 e_preventDefault(e2);
3577 if (!modifier && +new Date - 200 < startTime)
3643 if (!modifier && +new Date - 200 < startTime)
3578 extendSelection(cm.doc, start);
3644 extendSelection(cm.doc, start);
3579 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3645 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3580 if (webkit || ie && ie_version == 9)
3646 if (webkit || ie && ie_version == 9)
3581 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3647 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3582 else
3648 else
3583 display.input.focus();
3649 display.input.focus();
3584 }
3650 }
3585 });
3651 });
3586 // Let the drag handler handle this.
3652 // Let the drag handler handle this.
3587 if (webkit) display.scroller.draggable = true;
3653 if (webkit) display.scroller.draggable = true;
3588 cm.state.draggingText = dragEnd;
3654 cm.state.draggingText = dragEnd;
3589 // IE's approach to draggable
3655 // IE's approach to draggable
3590 if (display.scroller.dragDrop) display.scroller.dragDrop();
3656 if (display.scroller.dragDrop) display.scroller.dragDrop();
3591 on(document, "mouseup", dragEnd);
3657 on(document, "mouseup", dragEnd);
3592 on(display.scroller, "drop", dragEnd);
3658 on(display.scroller, "drop", dragEnd);
3593 }
3659 }
3594
3660
3595 // Normal selection, as opposed to text dragging.
3661 // Normal selection, as opposed to text dragging.
3596 function leftButtonSelect(cm, e, start, type, addNew) {
3662 function leftButtonSelect(cm, e, start, type, addNew) {
3597 var display = cm.display, doc = cm.doc;
3663 var display = cm.display, doc = cm.doc;
3598 e_preventDefault(e);
3664 e_preventDefault(e);
3599
3665
3600 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3666 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3601 if (addNew && !e.shiftKey) {
3667 if (addNew && !e.shiftKey) {
3602 ourIndex = doc.sel.contains(start);
3668 ourIndex = doc.sel.contains(start);
3603 if (ourIndex > -1)
3669 if (ourIndex > -1)
3604 ourRange = ranges[ourIndex];
3670 ourRange = ranges[ourIndex];
3605 else
3671 else
3606 ourRange = new Range(start, start);
3672 ourRange = new Range(start, start);
3607 } else {
3673 } else {
3608 ourRange = doc.sel.primary();
3674 ourRange = doc.sel.primary();
3609 ourIndex = doc.sel.primIndex;
3675 ourIndex = doc.sel.primIndex;
3610 }
3676 }
3611
3677
3612 if (e.altKey) {
3678 if (e.altKey) {
3613 type = "rect";
3679 type = "rect";
3614 if (!addNew) ourRange = new Range(start, start);
3680 if (!addNew) ourRange = new Range(start, start);
3615 start = posFromMouse(cm, e, true, true);
3681 start = posFromMouse(cm, e, true, true);
3616 ourIndex = -1;
3682 ourIndex = -1;
3617 } else if (type == "double") {
3683 } else if (type == "double") {
3618 var word = cm.findWordAt(start);
3684 var word = cm.findWordAt(start);
3619 if (cm.display.shift || doc.extend)
3685 if (cm.display.shift || doc.extend)
3620 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3686 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3621 else
3687 else
3622 ourRange = word;
3688 ourRange = word;
3623 } else if (type == "triple") {
3689 } else if (type == "triple") {
3624 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3690 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3625 if (cm.display.shift || doc.extend)
3691 if (cm.display.shift || doc.extend)
3626 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3692 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3627 else
3693 else
3628 ourRange = line;
3694 ourRange = line;
3629 } else {
3695 } else {
3630 ourRange = extendRange(doc, ourRange, start);
3696 ourRange = extendRange(doc, ourRange, start);
3631 }
3697 }
3632
3698
3633 if (!addNew) {
3699 if (!addNew) {
3634 ourIndex = 0;
3700 ourIndex = 0;
3635 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3701 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3636 startSel = doc.sel;
3702 startSel = doc.sel;
3637 } else if (ourIndex == -1) {
3703 } else if (ourIndex == -1) {
3638 ourIndex = ranges.length;
3704 ourIndex = ranges.length;
3639 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3705 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3640 {scroll: false, origin: "*mouse"});
3706 {scroll: false, origin: "*mouse"});
3641 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3707 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3642 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
3708 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
3709 {scroll: false, origin: "*mouse"});
3643 startSel = doc.sel;
3710 startSel = doc.sel;
3644 } else {
3711 } else {
3645 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3712 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3646 }
3713 }
3647
3714
3648 var lastPos = start;
3715 var lastPos = start;
3649 function extendTo(pos) {
3716 function extendTo(pos) {
3650 if (cmp(lastPos, pos) == 0) return;
3717 if (cmp(lastPos, pos) == 0) return;
3651 lastPos = pos;
3718 lastPos = pos;
3652
3719
3653 if (type == "rect") {
3720 if (type == "rect") {
3654 var ranges = [], tabSize = cm.options.tabSize;
3721 var ranges = [], tabSize = cm.options.tabSize;
3655 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3722 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3656 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3723 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3657 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3724 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3658 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3725 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3659 line <= end; line++) {
3726 line <= end; line++) {
3660 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3727 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3661 if (left == right)
3728 if (left == right)
3662 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3729 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3663 else if (text.length > leftPos)
3730 else if (text.length > leftPos)
3664 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3731 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3665 }
3732 }
3666 if (!ranges.length) ranges.push(new Range(start, start));
3733 if (!ranges.length) ranges.push(new Range(start, start));
3667 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3734 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3668 {origin: "*mouse", scroll: false});
3735 {origin: "*mouse", scroll: false});
3669 cm.scrollIntoView(pos);
3736 cm.scrollIntoView(pos);
3670 } else {
3737 } else {
3671 var oldRange = ourRange;
3738 var oldRange = ourRange;
3672 var anchor = oldRange.anchor, head = pos;
3739 var anchor = oldRange.anchor, head = pos;
3673 if (type != "single") {
3740 if (type != "single") {
3674 if (type == "double")
3741 if (type == "double")
3675 var range = cm.findWordAt(pos);
3742 var range = cm.findWordAt(pos);
3676 else
3743 else
3677 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3744 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3678 if (cmp(range.anchor, anchor) > 0) {
3745 if (cmp(range.anchor, anchor) > 0) {
3679 head = range.head;
3746 head = range.head;
3680 anchor = minPos(oldRange.from(), range.anchor);
3747 anchor = minPos(oldRange.from(), range.anchor);
3681 } else {
3748 } else {
3682 head = range.anchor;
3749 head = range.anchor;
3683 anchor = maxPos(oldRange.to(), range.head);
3750 anchor = maxPos(oldRange.to(), range.head);
3684 }
3751 }
3685 }
3752 }
3686 var ranges = startSel.ranges.slice(0);
3753 var ranges = startSel.ranges.slice(0);
3687 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3754 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3688 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3755 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3689 }
3756 }
3690 }
3757 }
3691
3758
3692 var editorSize = display.wrapper.getBoundingClientRect();
3759 var editorSize = display.wrapper.getBoundingClientRect();
3693 // Used to ensure timeout re-tries don't fire when another extend
3760 // Used to ensure timeout re-tries don't fire when another extend
3694 // happened in the meantime (clearTimeout isn't reliable -- at
3761 // happened in the meantime (clearTimeout isn't reliable -- at
3695 // least on Chrome, the timeouts still happen even when cleared,
3762 // least on Chrome, the timeouts still happen even when cleared,
3696 // if the clear happens after their scheduled firing time).
3763 // if the clear happens after their scheduled firing time).
3697 var counter = 0;
3764 var counter = 0;
3698
3765
3699 function extend(e) {
3766 function extend(e) {
3700 var curCount = ++counter;
3767 var curCount = ++counter;
3701 var cur = posFromMouse(cm, e, true, type == "rect");
3768 var cur = posFromMouse(cm, e, true, type == "rect");
3702 if (!cur) return;
3769 if (!cur) return;
3703 if (cmp(cur, lastPos) != 0) {
3770 if (cmp(cur, lastPos) != 0) {
3704 cm.curOp.focus = activeElt();
3771 cm.curOp.focus = activeElt();
3705 extendTo(cur);
3772 extendTo(cur);
3706 var visible = visibleLines(display, doc);
3773 var visible = visibleLines(display, doc);
3707 if (cur.line >= visible.to || cur.line < visible.from)
3774 if (cur.line >= visible.to || cur.line < visible.from)
3708 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3775 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3709 } else {
3776 } else {
3710 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3777 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3711 if (outside) setTimeout(operation(cm, function() {
3778 if (outside) setTimeout(operation(cm, function() {
3712 if (counter != curCount) return;
3779 if (counter != curCount) return;
3713 display.scroller.scrollTop += outside;
3780 display.scroller.scrollTop += outside;
3714 extend(e);
3781 extend(e);
3715 }), 50);
3782 }), 50);
3716 }
3783 }
3717 }
3784 }
3718
3785
3719 function done(e) {
3786 function done(e) {
3787 cm.state.selectingText = false;
3720 counter = Infinity;
3788 counter = Infinity;
3721 e_preventDefault(e);
3789 e_preventDefault(e);
3722 display.input.focus();
3790 display.input.focus();
3723 off(document, "mousemove", move);
3791 off(document, "mousemove", move);
3724 off(document, "mouseup", up);
3792 off(document, "mouseup", up);
3725 doc.history.lastSelOrigin = null;
3793 doc.history.lastSelOrigin = null;
3726 }
3794 }
3727
3795
3728 var move = operation(cm, function(e) {
3796 var move = operation(cm, function(e) {
3729 if (!e_button(e)) done(e);
3797 if (!e_button(e)) done(e);
3730 else extend(e);
3798 else extend(e);
3731 });
3799 });
3732 var up = operation(cm, done);
3800 var up = operation(cm, done);
3801 cm.state.selectingText = up;
3733 on(document, "mousemove", move);
3802 on(document, "mousemove", move);
3734 on(document, "mouseup", up);
3803 on(document, "mouseup", up);
3735 }
3804 }
3736
3805
3737 // Determines whether an event happened in the gutter, and fires the
3806 // Determines whether an event happened in the gutter, and fires the
3738 // handlers for the corresponding event.
3807 // handlers for the corresponding event.
3739 function gutterEvent(cm, e, type, prevent, signalfn) {
3808 function gutterEvent(cm, e, type, prevent) {
3740 try { var mX = e.clientX, mY = e.clientY; }
3809 try { var mX = e.clientX, mY = e.clientY; }
3741 catch(e) { return false; }
3810 catch(e) { return false; }
3742 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3811 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3743 if (prevent) e_preventDefault(e);
3812 if (prevent) e_preventDefault(e);
3744
3813
3745 var display = cm.display;
3814 var display = cm.display;
3746 var lineBox = display.lineDiv.getBoundingClientRect();
3815 var lineBox = display.lineDiv.getBoundingClientRect();
3747
3816
3748 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3817 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3749 mY -= lineBox.top - display.viewOffset;
3818 mY -= lineBox.top - display.viewOffset;
3750
3819
3751 for (var i = 0; i < cm.options.gutters.length; ++i) {
3820 for (var i = 0; i < cm.options.gutters.length; ++i) {
3752 var g = display.gutters.childNodes[i];
3821 var g = display.gutters.childNodes[i];
3753 if (g && g.getBoundingClientRect().right >= mX) {
3822 if (g && g.getBoundingClientRect().right >= mX) {
3754 var line = lineAtHeight(cm.doc, mY);
3823 var line = lineAtHeight(cm.doc, mY);
3755 var gutter = cm.options.gutters[i];
3824 var gutter = cm.options.gutters[i];
3756 signalfn(cm, type, cm, line, gutter, e);
3825 signal(cm, type, cm, line, gutter, e);
3757 return e_defaultPrevented(e);
3826 return e_defaultPrevented(e);
3758 }
3827 }
3759 }
3828 }
3760 }
3829 }
3761
3830
3762 function clickInGutter(cm, e) {
3831 function clickInGutter(cm, e) {
3763 return gutterEvent(cm, e, "gutterClick", true, signalLater);
3832 return gutterEvent(cm, e, "gutterClick", true);
3764 }
3833 }
3765
3834
3766 // Kludge to work around strange IE behavior where it'll sometimes
3835 // Kludge to work around strange IE behavior where it'll sometimes
3767 // re-fire a series of drag-related events right after the drop (#1551)
3836 // re-fire a series of drag-related events right after the drop (#1551)
3768 var lastDrop = 0;
3837 var lastDrop = 0;
3769
3838
3770 function onDrop(e) {
3839 function onDrop(e) {
3771 var cm = this;
3840 var cm = this;
3841 clearDragCursor(cm);
3772 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3842 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3773 return;
3843 return;
3774 e_preventDefault(e);
3844 e_preventDefault(e);
3775 if (ie) lastDrop = +new Date;
3845 if (ie) lastDrop = +new Date;
3776 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3846 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3777 if (!pos || isReadOnly(cm)) return;
3847 if (!pos || cm.isReadOnly()) return;
3778 // Might be a file drop, in which case we simply extract the text
3848 // Might be a file drop, in which case we simply extract the text
3779 // and insert it.
3849 // and insert it.
3780 if (files && files.length && window.FileReader && window.File) {
3850 if (files && files.length && window.FileReader && window.File) {
3781 var n = files.length, text = Array(n), read = 0;
3851 var n = files.length, text = Array(n), read = 0;
3782 var loadFile = function(file, i) {
3852 var loadFile = function(file, i) {
3853 if (cm.options.allowDropFileTypes &&
3854 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
3855 return;
3856
3783 var reader = new FileReader;
3857 var reader = new FileReader;
3784 reader.onload = operation(cm, function() {
3858 reader.onload = operation(cm, function() {
3785 text[i] = reader.result;
3859 var content = reader.result;
3860 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
3861 text[i] = content;
3786 if (++read == n) {
3862 if (++read == n) {
3787 pos = clipPos(cm.doc, pos);
3863 pos = clipPos(cm.doc, pos);
3788 var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"};
3864 var change = {from: pos, to: pos,
3865 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
3866 origin: "paste"};
3789 makeChange(cm.doc, change);
3867 makeChange(cm.doc, change);
3790 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3868 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3791 }
3869 }
3792 });
3870 });
3793 reader.readAsText(file);
3871 reader.readAsText(file);
3794 };
3872 };
3795 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3873 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3796 } else { // Normal drop
3874 } else { // Normal drop
3797 // Don't do a replace if the drop happened inside of the selected text.
3875 // Don't do a replace if the drop happened inside of the selected text.
3798 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3876 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3799 cm.state.draggingText(e);
3877 cm.state.draggingText(e);
3800 // Ensure the editor is re-focused
3878 // Ensure the editor is re-focused
3801 setTimeout(function() {cm.display.input.focus();}, 20);
3879 setTimeout(function() {cm.display.input.focus();}, 20);
3802 return;
3880 return;
3803 }
3881 }
3804 try {
3882 try {
3805 var text = e.dataTransfer.getData("Text");
3883 var text = e.dataTransfer.getData("Text");
3806 if (text) {
3884 if (text) {
3807 if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3885 if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3808 var selected = cm.listSelections();
3886 var selected = cm.listSelections();
3809 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3887 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3810 if (selected) for (var i = 0; i < selected.length; ++i)
3888 if (selected) for (var i = 0; i < selected.length; ++i)
3811 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3889 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3812 cm.replaceSelection(text, "around", "paste");
3890 cm.replaceSelection(text, "around", "paste");
3813 cm.display.input.focus();
3891 cm.display.input.focus();
3814 }
3892 }
3815 }
3893 }
3816 catch(e){}
3894 catch(e){}
3817 }
3895 }
3818 }
3896 }
3819
3897
3820 function onDragStart(cm, e) {
3898 function onDragStart(cm, e) {
3821 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3899 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3822 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3900 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3823
3901
3824 e.dataTransfer.setData("Text", cm.getSelection());
3902 e.dataTransfer.setData("Text", cm.getSelection());
3825
3903
3826 // Use dummy image instead of default browsers image.
3904 // Use dummy image instead of default browsers image.
3827 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3905 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3828 if (e.dataTransfer.setDragImage && !safari) {
3906 if (e.dataTransfer.setDragImage && !safari) {
3829 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3907 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3830 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3908 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3831 if (presto) {
3909 if (presto) {
3832 img.width = img.height = 1;
3910 img.width = img.height = 1;
3833 cm.display.wrapper.appendChild(img);
3911 cm.display.wrapper.appendChild(img);
3834 // Force a relayout, or Opera won't use our image for some obscure reason
3912 // Force a relayout, or Opera won't use our image for some obscure reason
3835 img._top = img.offsetTop;
3913 img._top = img.offsetTop;
3836 }
3914 }
3837 e.dataTransfer.setDragImage(img, 0, 0);
3915 e.dataTransfer.setDragImage(img, 0, 0);
3838 if (presto) img.parentNode.removeChild(img);
3916 if (presto) img.parentNode.removeChild(img);
3839 }
3917 }
3840 }
3918 }
3841
3919
3920 function onDragOver(cm, e) {
3921 var pos = posFromMouse(cm, e);
3922 if (!pos) return;
3923 var frag = document.createDocumentFragment();
3924 drawSelectionCursor(cm, pos, frag);
3925 if (!cm.display.dragCursor) {
3926 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
3927 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
3928 }
3929 removeChildrenAndAdd(cm.display.dragCursor, frag);
3930 }
3931
3932 function clearDragCursor(cm) {
3933 if (cm.display.dragCursor) {
3934 cm.display.lineSpace.removeChild(cm.display.dragCursor);
3935 cm.display.dragCursor = null;
3936 }
3937 }
3938
3842 // SCROLL EVENTS
3939 // SCROLL EVENTS
3843
3940
3844 // Sync the scrollable area and scrollbars, ensure the viewport
3941 // Sync the scrollable area and scrollbars, ensure the viewport
3845 // covers the visible area.
3942 // covers the visible area.
3846 function setScrollTop(cm, val) {
3943 function setScrollTop(cm, val) {
3847 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3944 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3848 cm.doc.scrollTop = val;
3945 cm.doc.scrollTop = val;
3849 if (!gecko) updateDisplaySimple(cm, {top: val});
3946 if (!gecko) updateDisplaySimple(cm, {top: val});
3850 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3947 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3851 cm.display.scrollbars.setScrollTop(val);
3948 cm.display.scrollbars.setScrollTop(val);
3852 if (gecko) updateDisplaySimple(cm);
3949 if (gecko) updateDisplaySimple(cm);
3853 startWorker(cm, 100);
3950 startWorker(cm, 100);
3854 }
3951 }
3855 // Sync scroller and scrollbar, ensure the gutter elements are
3952 // Sync scroller and scrollbar, ensure the gutter elements are
3856 // aligned.
3953 // aligned.
3857 function setScrollLeft(cm, val, isScroller) {
3954 function setScrollLeft(cm, val, isScroller) {
3858 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3955 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3859 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3956 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3860 cm.doc.scrollLeft = val;
3957 cm.doc.scrollLeft = val;
3861 alignHorizontally(cm);
3958 alignHorizontally(cm);
3862 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3959 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3863 cm.display.scrollbars.setScrollLeft(val);
3960 cm.display.scrollbars.setScrollLeft(val);
3864 }
3961 }
3865
3962
3866 // Since the delta values reported on mouse wheel events are
3963 // Since the delta values reported on mouse wheel events are
3867 // unstandardized between browsers and even browser versions, and
3964 // unstandardized between browsers and even browser versions, and
3868 // generally horribly unpredictable, this code starts by measuring
3965 // generally horribly unpredictable, this code starts by measuring
3869 // the scroll effect that the first few mouse wheel events have,
3966 // the scroll effect that the first few mouse wheel events have,
3870 // and, from that, detects the way it can convert deltas to pixel
3967 // and, from that, detects the way it can convert deltas to pixel
3871 // offsets afterwards.
3968 // offsets afterwards.
3872 //
3969 //
3873 // The reason we want to know the amount a wheel event will scroll
3970 // The reason we want to know the amount a wheel event will scroll
3874 // is that it gives us a chance to update the display before the
3971 // is that it gives us a chance to update the display before the
3875 // actual scrolling happens, reducing flickering.
3972 // actual scrolling happens, reducing flickering.
3876
3973
3877 var wheelSamples = 0, wheelPixelsPerUnit = null;
3974 var wheelSamples = 0, wheelPixelsPerUnit = null;
3878 // Fill in a browser-detected starting value on browsers where we
3975 // Fill in a browser-detected starting value on browsers where we
3879 // know one. These don't have to be accurate -- the result of them
3976 // know one. These don't have to be accurate -- the result of them
3880 // being wrong would just be a slight flicker on the first wheel
3977 // being wrong would just be a slight flicker on the first wheel
3881 // scroll (if it is large enough).
3978 // scroll (if it is large enough).
3882 if (ie) wheelPixelsPerUnit = -.53;
3979 if (ie) wheelPixelsPerUnit = -.53;
3883 else if (gecko) wheelPixelsPerUnit = 15;
3980 else if (gecko) wheelPixelsPerUnit = 15;
3884 else if (chrome) wheelPixelsPerUnit = -.7;
3981 else if (chrome) wheelPixelsPerUnit = -.7;
3885 else if (safari) wheelPixelsPerUnit = -1/3;
3982 else if (safari) wheelPixelsPerUnit = -1/3;
3886
3983
3887 var wheelEventDelta = function(e) {
3984 var wheelEventDelta = function(e) {
3888 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3985 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3889 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
3986 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
3890 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
3987 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
3891 else if (dy == null) dy = e.wheelDelta;
3988 else if (dy == null) dy = e.wheelDelta;
3892 return {x: dx, y: dy};
3989 return {x: dx, y: dy};
3893 };
3990 };
3894 CodeMirror.wheelEventPixels = function(e) {
3991 CodeMirror.wheelEventPixels = function(e) {
3895 var delta = wheelEventDelta(e);
3992 var delta = wheelEventDelta(e);
3896 delta.x *= wheelPixelsPerUnit;
3993 delta.x *= wheelPixelsPerUnit;
3897 delta.y *= wheelPixelsPerUnit;
3994 delta.y *= wheelPixelsPerUnit;
3898 return delta;
3995 return delta;
3899 };
3996 };
3900
3997
3901 function onScrollWheel(cm, e) {
3998 function onScrollWheel(cm, e) {
3902 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3999 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3903
4000
3904 var display = cm.display, scroll = display.scroller;
4001 var display = cm.display, scroll = display.scroller;
3905 // Quit if there's nothing to scroll here
4002 // Quit if there's nothing to scroll here
3906 if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
4003 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
3907 dy && scroll.scrollHeight > scroll.clientHeight)) return;
4004 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4005 if (!(dx && canScrollX || dy && canScrollY)) return;
3908
4006
3909 // Webkit browsers on OS X abort momentum scrolls when the target
4007 // Webkit browsers on OS X abort momentum scrolls when the target
3910 // of the scroll event is removed from the scrollable element.
4008 // of the scroll event is removed from the scrollable element.
3911 // This hack (see related code in patchDisplay) makes sure the
4009 // This hack (see related code in patchDisplay) makes sure the
3912 // element is kept around.
4010 // element is kept around.
3913 if (dy && mac && webkit) {
4011 if (dy && mac && webkit) {
3914 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4012 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3915 for (var i = 0; i < view.length; i++) {
4013 for (var i = 0; i < view.length; i++) {
3916 if (view[i].node == cur) {
4014 if (view[i].node == cur) {
3917 cm.display.currentWheelTarget = cur;
4015 cm.display.currentWheelTarget = cur;
3918 break outer;
4016 break outer;
3919 }
4017 }
3920 }
4018 }
3921 }
4019 }
3922 }
4020 }
3923
4021
3924 // On some browsers, horizontal scrolling will cause redraws to
4022 // On some browsers, horizontal scrolling will cause redraws to
3925 // happen before the gutter has been realigned, causing it to
4023 // happen before the gutter has been realigned, causing it to
3926 // wriggle around in a most unseemly way. When we have an
4024 // wriggle around in a most unseemly way. When we have an
3927 // estimated pixels/delta value, we just handle horizontal
4025 // estimated pixels/delta value, we just handle horizontal
3928 // scrolling entirely here. It'll be slightly off from native, but
4026 // scrolling entirely here. It'll be slightly off from native, but
3929 // better than glitching out.
4027 // better than glitching out.
3930 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4028 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3931 if (dy)
4029 if (dy && canScrollY)
3932 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
4030 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3933 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
4031 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3934 e_preventDefault(e);
4032 // Only prevent default scrolling if vertical scrolling is
4033 // actually possible. Otherwise, it causes vertical scroll
4034 // jitter on OSX trackpads when deltaX is small and deltaY
4035 // is large (issue #3579)
4036 if (!dy || (dy && canScrollY))
4037 e_preventDefault(e);
3935 display.wheelStartX = null; // Abort measurement, if in progress
4038 display.wheelStartX = null; // Abort measurement, if in progress
3936 return;
4039 return;
3937 }
4040 }
3938
4041
3939 // 'Project' the visible viewport to cover the area that is being
4042 // 'Project' the visible viewport to cover the area that is being
3940 // scrolled into view (if we know enough to estimate it).
4043 // scrolled into view (if we know enough to estimate it).
3941 if (dy && wheelPixelsPerUnit != null) {
4044 if (dy && wheelPixelsPerUnit != null) {
3942 var pixels = dy * wheelPixelsPerUnit;
4045 var pixels = dy * wheelPixelsPerUnit;
3943 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4046 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3944 if (pixels < 0) top = Math.max(0, top + pixels - 50);
4047 if (pixels < 0) top = Math.max(0, top + pixels - 50);
3945 else bot = Math.min(cm.doc.height, bot + pixels + 50);
4048 else bot = Math.min(cm.doc.height, bot + pixels + 50);
3946 updateDisplaySimple(cm, {top: top, bottom: bot});
4049 updateDisplaySimple(cm, {top: top, bottom: bot});
3947 }
4050 }
3948
4051
3949 if (wheelSamples < 20) {
4052 if (wheelSamples < 20) {
3950 if (display.wheelStartX == null) {
4053 if (display.wheelStartX == null) {
3951 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4054 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
3952 display.wheelDX = dx; display.wheelDY = dy;
4055 display.wheelDX = dx; display.wheelDY = dy;
3953 setTimeout(function() {
4056 setTimeout(function() {
3954 if (display.wheelStartX == null) return;
4057 if (display.wheelStartX == null) return;
3955 var movedX = scroll.scrollLeft - display.wheelStartX;
4058 var movedX = scroll.scrollLeft - display.wheelStartX;
3956 var movedY = scroll.scrollTop - display.wheelStartY;
4059 var movedY = scroll.scrollTop - display.wheelStartY;
3957 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4060 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3958 (movedX && display.wheelDX && movedX / display.wheelDX);
4061 (movedX && display.wheelDX && movedX / display.wheelDX);
3959 display.wheelStartX = display.wheelStartY = null;
4062 display.wheelStartX = display.wheelStartY = null;
3960 if (!sample) return;
4063 if (!sample) return;
3961 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4064 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
3962 ++wheelSamples;
4065 ++wheelSamples;
3963 }, 200);
4066 }, 200);
3964 } else {
4067 } else {
3965 display.wheelDX += dx; display.wheelDY += dy;
4068 display.wheelDX += dx; display.wheelDY += dy;
3966 }
4069 }
3967 }
4070 }
3968 }
4071 }
3969
4072
3970 // KEY EVENTS
4073 // KEY EVENTS
3971
4074
3972 // Run a handler that was bound to a key.
4075 // Run a handler that was bound to a key.
3973 function doHandleBinding(cm, bound, dropShift) {
4076 function doHandleBinding(cm, bound, dropShift) {
3974 if (typeof bound == "string") {
4077 if (typeof bound == "string") {
3975 bound = commands[bound];
4078 bound = commands[bound];
3976 if (!bound) return false;
4079 if (!bound) return false;
3977 }
4080 }
3978 // Ensure previous input has been read, so that the handler sees a
4081 // Ensure previous input has been read, so that the handler sees a
3979 // consistent view of the document
4082 // consistent view of the document
3980 cm.display.input.ensurePolled();
4083 cm.display.input.ensurePolled();
3981 var prevShift = cm.display.shift, done = false;
4084 var prevShift = cm.display.shift, done = false;
3982 try {
4085 try {
3983 if (isReadOnly(cm)) cm.state.suppressEdits = true;
4086 if (cm.isReadOnly()) cm.state.suppressEdits = true;
3984 if (dropShift) cm.display.shift = false;
4087 if (dropShift) cm.display.shift = false;
3985 done = bound(cm) != Pass;
4088 done = bound(cm) != Pass;
3986 } finally {
4089 } finally {
3987 cm.display.shift = prevShift;
4090 cm.display.shift = prevShift;
3988 cm.state.suppressEdits = false;
4091 cm.state.suppressEdits = false;
3989 }
4092 }
3990 return done;
4093 return done;
3991 }
4094 }
3992
4095
3993 function lookupKeyForEditor(cm, name, handle) {
4096 function lookupKeyForEditor(cm, name, handle) {
3994 for (var i = 0; i < cm.state.keyMaps.length; i++) {
4097 for (var i = 0; i < cm.state.keyMaps.length; i++) {
3995 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4098 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
3996 if (result) return result;
4099 if (result) return result;
3997 }
4100 }
3998 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
4101 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
3999 || lookupKey(name, cm.options.keyMap, handle, cm);
4102 || lookupKey(name, cm.options.keyMap, handle, cm);
4000 }
4103 }
4001
4104
4002 var stopSeq = new Delayed;
4105 var stopSeq = new Delayed;
4003 function dispatchKey(cm, name, e, handle) {
4106 function dispatchKey(cm, name, e, handle) {
4004 var seq = cm.state.keySeq;
4107 var seq = cm.state.keySeq;
4005 if (seq) {
4108 if (seq) {
4006 if (isModifierKey(name)) return "handled";
4109 if (isModifierKey(name)) return "handled";
4007 stopSeq.set(50, function() {
4110 stopSeq.set(50, function() {
4008 if (cm.state.keySeq == seq) {
4111 if (cm.state.keySeq == seq) {
4009 cm.state.keySeq = null;
4112 cm.state.keySeq = null;
4010 cm.display.input.reset();
4113 cm.display.input.reset();
4011 }
4114 }
4012 });
4115 });
4013 name = seq + " " + name;
4116 name = seq + " " + name;
4014 }
4117 }
4015 var result = lookupKeyForEditor(cm, name, handle);
4118 var result = lookupKeyForEditor(cm, name, handle);
4016
4119
4017 if (result == "multi")
4120 if (result == "multi")
4018 cm.state.keySeq = name;
4121 cm.state.keySeq = name;
4019 if (result == "handled")
4122 if (result == "handled")
4020 signalLater(cm, "keyHandled", cm, name, e);
4123 signalLater(cm, "keyHandled", cm, name, e);
4021
4124
4022 if (result == "handled" || result == "multi") {
4125 if (result == "handled" || result == "multi") {
4023 e_preventDefault(e);
4126 e_preventDefault(e);
4024 restartBlink(cm);
4127 restartBlink(cm);
4025 }
4128 }
4026
4129
4027 if (seq && !result && /\'$/.test(name)) {
4130 if (seq && !result && /\'$/.test(name)) {
4028 e_preventDefault(e);
4131 e_preventDefault(e);
4029 return true;
4132 return true;
4030 }
4133 }
4031 return !!result;
4134 return !!result;
4032 }
4135 }
4033
4136
4034 // Handle a key from the keydown event.
4137 // Handle a key from the keydown event.
4035 function handleKeyBinding(cm, e) {
4138 function handleKeyBinding(cm, e) {
4036 var name = keyName(e, true);
4139 var name = keyName(e, true);
4037 if (!name) return false;
4140 if (!name) return false;
4038
4141
4039 if (e.shiftKey && !cm.state.keySeq) {
4142 if (e.shiftKey && !cm.state.keySeq) {
4040 // First try to resolve full name (including 'Shift-'). Failing
4143 // First try to resolve full name (including 'Shift-'). Failing
4041 // that, see if there is a cursor-motion command (starting with
4144 // that, see if there is a cursor-motion command (starting with
4042 // 'go') bound to the keyname without 'Shift-'.
4145 // 'go') bound to the keyname without 'Shift-'.
4043 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4146 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4044 || dispatchKey(cm, name, e, function(b) {
4147 || dispatchKey(cm, name, e, function(b) {
4045 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4148 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4046 return doHandleBinding(cm, b);
4149 return doHandleBinding(cm, b);
4047 });
4150 });
4048 } else {
4151 } else {
4049 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4152 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4050 }
4153 }
4051 }
4154 }
4052
4155
4053 // Handle a key from the keypress event
4156 // Handle a key from the keypress event
4054 function handleCharBinding(cm, e, ch) {
4157 function handleCharBinding(cm, e, ch) {
4055 return dispatchKey(cm, "'" + ch + "'", e,
4158 return dispatchKey(cm, "'" + ch + "'", e,
4056 function(b) { return doHandleBinding(cm, b, true); });
4159 function(b) { return doHandleBinding(cm, b, true); });
4057 }
4160 }
4058
4161
4059 var lastStoppedKey = null;
4162 var lastStoppedKey = null;
4060 function onKeyDown(e) {
4163 function onKeyDown(e) {
4061 var cm = this;
4164 var cm = this;
4062 cm.curOp.focus = activeElt();
4165 cm.curOp.focus = activeElt();
4063 if (signalDOMEvent(cm, e)) return;
4166 if (signalDOMEvent(cm, e)) return;
4064 // IE does strange things with escape.
4167 // IE does strange things with escape.
4065 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4168 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4066 var code = e.keyCode;
4169 var code = e.keyCode;
4067 cm.display.shift = code == 16 || e.shiftKey;
4170 cm.display.shift = code == 16 || e.shiftKey;
4068 var handled = handleKeyBinding(cm, e);
4171 var handled = handleKeyBinding(cm, e);
4069 if (presto) {
4172 if (presto) {
4070 lastStoppedKey = handled ? code : null;
4173 lastStoppedKey = handled ? code : null;
4071 // Opera has no cut event... we try to at least catch the key combo
4174 // Opera has no cut event... we try to at least catch the key combo
4072 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4175 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4073 cm.replaceSelection("", null, "cut");
4176 cm.replaceSelection("", null, "cut");
4074 }
4177 }
4075
4178
4076 // Turn mouse into crosshair when Alt is held on Mac.
4179 // Turn mouse into crosshair when Alt is held on Mac.
4077 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4180 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4078 showCrossHair(cm);
4181 showCrossHair(cm);
4079 }
4182 }
4080
4183
4081 function showCrossHair(cm) {
4184 function showCrossHair(cm) {
4082 var lineDiv = cm.display.lineDiv;
4185 var lineDiv = cm.display.lineDiv;
4083 addClass(lineDiv, "CodeMirror-crosshair");
4186 addClass(lineDiv, "CodeMirror-crosshair");
4084
4187
4085 function up(e) {
4188 function up(e) {
4086 if (e.keyCode == 18 || !e.altKey) {
4189 if (e.keyCode == 18 || !e.altKey) {
4087 rmClass(lineDiv, "CodeMirror-crosshair");
4190 rmClass(lineDiv, "CodeMirror-crosshair");
4088 off(document, "keyup", up);
4191 off(document, "keyup", up);
4089 off(document, "mouseover", up);
4192 off(document, "mouseover", up);
4090 }
4193 }
4091 }
4194 }
4092 on(document, "keyup", up);
4195 on(document, "keyup", up);
4093 on(document, "mouseover", up);
4196 on(document, "mouseover", up);
4094 }
4197 }
4095
4198
4096 function onKeyUp(e) {
4199 function onKeyUp(e) {
4097 if (e.keyCode == 16) this.doc.sel.shift = false;
4200 if (e.keyCode == 16) this.doc.sel.shift = false;
4098 signalDOMEvent(this, e);
4201 signalDOMEvent(this, e);
4099 }
4202 }
4100
4203
4101 function onKeyPress(e) {
4204 function onKeyPress(e) {
4102 var cm = this;
4205 var cm = this;
4103 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4206 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4104 var keyCode = e.keyCode, charCode = e.charCode;
4207 var keyCode = e.keyCode, charCode = e.charCode;
4105 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4208 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4106 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4209 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4107 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4210 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4108 if (handleCharBinding(cm, e, ch)) return;
4211 if (handleCharBinding(cm, e, ch)) return;
4109 cm.display.input.onKeyPress(e);
4212 cm.display.input.onKeyPress(e);
4110 }
4213 }
4111
4214
4112 // FOCUS/BLUR EVENTS
4215 // FOCUS/BLUR EVENTS
4113
4216
4114 function delayBlurEvent(cm) {
4217 function delayBlurEvent(cm) {
4115 cm.state.delayingBlurEvent = true;
4218 cm.state.delayingBlurEvent = true;
4116 setTimeout(function() {
4219 setTimeout(function() {
4117 if (cm.state.delayingBlurEvent) {
4220 if (cm.state.delayingBlurEvent) {
4118 cm.state.delayingBlurEvent = false;
4221 cm.state.delayingBlurEvent = false;
4119 onBlur(cm);
4222 onBlur(cm);
4120 }
4223 }
4121 }, 100);
4224 }, 100);
4122 }
4225 }
4123
4226
4124 function onFocus(cm) {
4227 function onFocus(cm) {
4125 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4228 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4126
4229
4127 if (cm.options.readOnly == "nocursor") return;
4230 if (cm.options.readOnly == "nocursor") return;
4128 if (!cm.state.focused) {
4231 if (!cm.state.focused) {
4129 signal(cm, "focus", cm);
4232 signal(cm, "focus", cm);
4130 cm.state.focused = true;
4233 cm.state.focused = true;
4131 addClass(cm.display.wrapper, "CodeMirror-focused");
4234 addClass(cm.display.wrapper, "CodeMirror-focused");
4132 // This test prevents this from firing when a context
4235 // This test prevents this from firing when a context
4133 // menu is closed (since the input reset would kill the
4236 // menu is closed (since the input reset would kill the
4134 // select-all detection hack)
4237 // select-all detection hack)
4135 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4238 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4136 cm.display.input.reset();
4239 cm.display.input.reset();
4137 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4240 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4138 }
4241 }
4139 cm.display.input.receivedFocus();
4242 cm.display.input.receivedFocus();
4140 }
4243 }
4141 restartBlink(cm);
4244 restartBlink(cm);
4142 }
4245 }
4143 function onBlur(cm) {
4246 function onBlur(cm) {
4144 if (cm.state.delayingBlurEvent) return;
4247 if (cm.state.delayingBlurEvent) return;
4145
4248
4146 if (cm.state.focused) {
4249 if (cm.state.focused) {
4147 signal(cm, "blur", cm);
4250 signal(cm, "blur", cm);
4148 cm.state.focused = false;
4251 cm.state.focused = false;
4149 rmClass(cm.display.wrapper, "CodeMirror-focused");
4252 rmClass(cm.display.wrapper, "CodeMirror-focused");
4150 }
4253 }
4151 clearInterval(cm.display.blinker);
4254 clearInterval(cm.display.blinker);
4152 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4255 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4153 }
4256 }
4154
4257
4155 // CONTEXT MENU HANDLING
4258 // CONTEXT MENU HANDLING
4156
4259
4157 // To make the context menu work, we need to briefly unhide the
4260 // To make the context menu work, we need to briefly unhide the
4158 // textarea (making it as unobtrusive as possible) to let the
4261 // textarea (making it as unobtrusive as possible) to let the
4159 // right-click take effect on it.
4262 // right-click take effect on it.
4160 function onContextMenu(cm, e) {
4263 function onContextMenu(cm, e) {
4161 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4264 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4265 if (signalDOMEvent(cm, e, "contextmenu")) return;
4162 cm.display.input.onContextMenu(e);
4266 cm.display.input.onContextMenu(e);
4163 }
4267 }
4164
4268
4165 function contextMenuInGutter(cm, e) {
4269 function contextMenuInGutter(cm, e) {
4166 if (!hasHandler(cm, "gutterContextMenu")) return false;
4270 if (!hasHandler(cm, "gutterContextMenu")) return false;
4167 return gutterEvent(cm, e, "gutterContextMenu", false, signal);
4271 return gutterEvent(cm, e, "gutterContextMenu", false);
4168 }
4272 }
4169
4273
4170 // UPDATING
4274 // UPDATING
4171
4275
4172 // Compute the position of the end of a change (its 'to' property
4276 // Compute the position of the end of a change (its 'to' property
4173 // refers to the pre-change end).
4277 // refers to the pre-change end).
4174 var changeEnd = CodeMirror.changeEnd = function(change) {
4278 var changeEnd = CodeMirror.changeEnd = function(change) {
4175 if (!change.text) return change.to;
4279 if (!change.text) return change.to;
4176 return Pos(change.from.line + change.text.length - 1,
4280 return Pos(change.from.line + change.text.length - 1,
4177 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
4281 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
4178 };
4282 };
4179
4283
4180 // Adjust a position to refer to the post-change position of the
4284 // Adjust a position to refer to the post-change position of the
4181 // same text, or the end of the change if the change covers it.
4285 // same text, or the end of the change if the change covers it.
4182 function adjustForChange(pos, change) {
4286 function adjustForChange(pos, change) {
4183 if (cmp(pos, change.from) < 0) return pos;
4287 if (cmp(pos, change.from) < 0) return pos;
4184 if (cmp(pos, change.to) <= 0) return changeEnd(change);
4288 if (cmp(pos, change.to) <= 0) return changeEnd(change);
4185
4289
4186 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4290 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4187 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4291 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4188 return Pos(line, ch);
4292 return Pos(line, ch);
4189 }
4293 }
4190
4294
4191 function computeSelAfterChange(doc, change) {
4295 function computeSelAfterChange(doc, change) {
4192 var out = [];
4296 var out = [];
4193 for (var i = 0; i < doc.sel.ranges.length; i++) {
4297 for (var i = 0; i < doc.sel.ranges.length; i++) {
4194 var range = doc.sel.ranges[i];
4298 var range = doc.sel.ranges[i];
4195 out.push(new Range(adjustForChange(range.anchor, change),
4299 out.push(new Range(adjustForChange(range.anchor, change),
4196 adjustForChange(range.head, change)));
4300 adjustForChange(range.head, change)));
4197 }
4301 }
4198 return normalizeSelection(out, doc.sel.primIndex);
4302 return normalizeSelection(out, doc.sel.primIndex);
4199 }
4303 }
4200
4304
4201 function offsetPos(pos, old, nw) {
4305 function offsetPos(pos, old, nw) {
4202 if (pos.line == old.line)
4306 if (pos.line == old.line)
4203 return Pos(nw.line, pos.ch - old.ch + nw.ch);
4307 return Pos(nw.line, pos.ch - old.ch + nw.ch);
4204 else
4308 else
4205 return Pos(nw.line + (pos.line - old.line), pos.ch);
4309 return Pos(nw.line + (pos.line - old.line), pos.ch);
4206 }
4310 }
4207
4311
4208 // Used by replaceSelections to allow moving the selection to the
4312 // Used by replaceSelections to allow moving the selection to the
4209 // start or around the replaced test. Hint may be "start" or "around".
4313 // start or around the replaced test. Hint may be "start" or "around".
4210 function computeReplacedSel(doc, changes, hint) {
4314 function computeReplacedSel(doc, changes, hint) {
4211 var out = [];
4315 var out = [];
4212 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4316 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4213 for (var i = 0; i < changes.length; i++) {
4317 for (var i = 0; i < changes.length; i++) {
4214 var change = changes[i];
4318 var change = changes[i];
4215 var from = offsetPos(change.from, oldPrev, newPrev);
4319 var from = offsetPos(change.from, oldPrev, newPrev);
4216 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4320 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4217 oldPrev = change.to;
4321 oldPrev = change.to;
4218 newPrev = to;
4322 newPrev = to;
4219 if (hint == "around") {
4323 if (hint == "around") {
4220 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4324 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4221 out[i] = new Range(inv ? to : from, inv ? from : to);
4325 out[i] = new Range(inv ? to : from, inv ? from : to);
4222 } else {
4326 } else {
4223 out[i] = new Range(from, from);
4327 out[i] = new Range(from, from);
4224 }
4328 }
4225 }
4329 }
4226 return new Selection(out, doc.sel.primIndex);
4330 return new Selection(out, doc.sel.primIndex);
4227 }
4331 }
4228
4332
4229 // Allow "beforeChange" event handlers to influence a change
4333 // Allow "beforeChange" event handlers to influence a change
4230 function filterChange(doc, change, update) {
4334 function filterChange(doc, change, update) {
4231 var obj = {
4335 var obj = {
4232 canceled: false,
4336 canceled: false,
4233 from: change.from,
4337 from: change.from,
4234 to: change.to,
4338 to: change.to,
4235 text: change.text,
4339 text: change.text,
4236 origin: change.origin,
4340 origin: change.origin,
4237 cancel: function() { this.canceled = true; }
4341 cancel: function() { this.canceled = true; }
4238 };
4342 };
4239 if (update) obj.update = function(from, to, text, origin) {
4343 if (update) obj.update = function(from, to, text, origin) {
4240 if (from) this.from = clipPos(doc, from);
4344 if (from) this.from = clipPos(doc, from);
4241 if (to) this.to = clipPos(doc, to);
4345 if (to) this.to = clipPos(doc, to);
4242 if (text) this.text = text;
4346 if (text) this.text = text;
4243 if (origin !== undefined) this.origin = origin;
4347 if (origin !== undefined) this.origin = origin;
4244 };
4348 };
4245 signal(doc, "beforeChange", doc, obj);
4349 signal(doc, "beforeChange", doc, obj);
4246 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4350 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4247
4351
4248 if (obj.canceled) return null;
4352 if (obj.canceled) return null;
4249 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4353 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4250 }
4354 }
4251
4355
4252 // Apply a change to a document, and add it to the document's
4356 // Apply a change to a document, and add it to the document's
4253 // history, and propagating it to all linked documents.
4357 // history, and propagating it to all linked documents.
4254 function makeChange(doc, change, ignoreReadOnly) {
4358 function makeChange(doc, change, ignoreReadOnly) {
4255 if (doc.cm) {
4359 if (doc.cm) {
4256 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4360 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4257 if (doc.cm.state.suppressEdits) return;
4361 if (doc.cm.state.suppressEdits) return;
4258 }
4362 }
4259
4363
4260 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4364 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4261 change = filterChange(doc, change, true);
4365 change = filterChange(doc, change, true);
4262 if (!change) return;
4366 if (!change) return;
4263 }
4367 }
4264
4368
4265 // Possibly split or suppress the update based on the presence
4369 // Possibly split or suppress the update based on the presence
4266 // of read-only spans in its range.
4370 // of read-only spans in its range.
4267 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4371 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4268 if (split) {
4372 if (split) {
4269 for (var i = split.length - 1; i >= 0; --i)
4373 for (var i = split.length - 1; i >= 0; --i)
4270 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
4374 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
4271 } else {
4375 } else {
4272 makeChangeInner(doc, change);
4376 makeChangeInner(doc, change);
4273 }
4377 }
4274 }
4378 }
4275
4379
4276 function makeChangeInner(doc, change) {
4380 function makeChangeInner(doc, change) {
4277 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
4381 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
4278 var selAfter = computeSelAfterChange(doc, change);
4382 var selAfter = computeSelAfterChange(doc, change);
4279 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4383 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4280
4384
4281 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4385 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4282 var rebased = [];
4386 var rebased = [];
4283
4387
4284 linkedDocs(doc, function(doc, sharedHist) {
4388 linkedDocs(doc, function(doc, sharedHist) {
4285 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4389 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4286 rebaseHist(doc.history, change);
4390 rebaseHist(doc.history, change);
4287 rebased.push(doc.history);
4391 rebased.push(doc.history);
4288 }
4392 }
4289 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4393 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4290 });
4394 });
4291 }
4395 }
4292
4396
4293 // Revert a change stored in a document's history.
4397 // Revert a change stored in a document's history.
4294 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4398 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4295 if (doc.cm && doc.cm.state.suppressEdits) return;
4399 if (doc.cm && doc.cm.state.suppressEdits) return;
4296
4400
4297 var hist = doc.history, event, selAfter = doc.sel;
4401 var hist = doc.history, event, selAfter = doc.sel;
4298 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4402 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4299
4403
4300 // Verify that there is a useable event (so that ctrl-z won't
4404 // Verify that there is a useable event (so that ctrl-z won't
4301 // needlessly clear selection events)
4405 // needlessly clear selection events)
4302 for (var i = 0; i < source.length; i++) {
4406 for (var i = 0; i < source.length; i++) {
4303 event = source[i];
4407 event = source[i];
4304 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4408 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4305 break;
4409 break;
4306 }
4410 }
4307 if (i == source.length) return;
4411 if (i == source.length) return;
4308 hist.lastOrigin = hist.lastSelOrigin = null;
4412 hist.lastOrigin = hist.lastSelOrigin = null;
4309
4413
4310 for (;;) {
4414 for (;;) {
4311 event = source.pop();
4415 event = source.pop();
4312 if (event.ranges) {
4416 if (event.ranges) {
4313 pushSelectionToHistory(event, dest);
4417 pushSelectionToHistory(event, dest);
4314 if (allowSelectionOnly && !event.equals(doc.sel)) {
4418 if (allowSelectionOnly && !event.equals(doc.sel)) {
4315 setSelection(doc, event, {clearRedo: false});
4419 setSelection(doc, event, {clearRedo: false});
4316 return;
4420 return;
4317 }
4421 }
4318 selAfter = event;
4422 selAfter = event;
4319 }
4423 }
4320 else break;
4424 else break;
4321 }
4425 }
4322
4426
4323 // Build up a reverse change object to add to the opposite history
4427 // Build up a reverse change object to add to the opposite history
4324 // stack (redo when undoing, and vice versa).
4428 // stack (redo when undoing, and vice versa).
4325 var antiChanges = [];
4429 var antiChanges = [];
4326 pushSelectionToHistory(selAfter, dest);
4430 pushSelectionToHistory(selAfter, dest);
4327 dest.push({changes: antiChanges, generation: hist.generation});
4431 dest.push({changes: antiChanges, generation: hist.generation});
4328 hist.generation = event.generation || ++hist.maxGeneration;
4432 hist.generation = event.generation || ++hist.maxGeneration;
4329
4433
4330 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4434 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4331
4435
4332 for (var i = event.changes.length - 1; i >= 0; --i) {
4436 for (var i = event.changes.length - 1; i >= 0; --i) {
4333 var change = event.changes[i];
4437 var change = event.changes[i];
4334 change.origin = type;
4438 change.origin = type;
4335 if (filter && !filterChange(doc, change, false)) {
4439 if (filter && !filterChange(doc, change, false)) {
4336 source.length = 0;
4440 source.length = 0;
4337 return;
4441 return;
4338 }
4442 }
4339
4443
4340 antiChanges.push(historyChangeFromChange(doc, change));
4444 antiChanges.push(historyChangeFromChange(doc, change));
4341
4445
4342 var after = i ? computeSelAfterChange(doc, change) : lst(source);
4446 var after = i ? computeSelAfterChange(doc, change) : lst(source);
4343 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4447 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4344 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4448 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4345 var rebased = [];
4449 var rebased = [];
4346
4450
4347 // Propagate to the linked documents
4451 // Propagate to the linked documents
4348 linkedDocs(doc, function(doc, sharedHist) {
4452 linkedDocs(doc, function(doc, sharedHist) {
4349 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4453 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4350 rebaseHist(doc.history, change);
4454 rebaseHist(doc.history, change);
4351 rebased.push(doc.history);
4455 rebased.push(doc.history);
4352 }
4456 }
4353 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4457 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4354 });
4458 });
4355 }
4459 }
4356 }
4460 }
4357
4461
4358 // Sub-views need their line numbers shifted when text is added
4462 // Sub-views need their line numbers shifted when text is added
4359 // above or below them in the parent document.
4463 // above or below them in the parent document.
4360 function shiftDoc(doc, distance) {
4464 function shiftDoc(doc, distance) {
4361 if (distance == 0) return;
4465 if (distance == 0) return;
4362 doc.first += distance;
4466 doc.first += distance;
4363 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4467 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4364 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4468 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4365 Pos(range.head.line + distance, range.head.ch));
4469 Pos(range.head.line + distance, range.head.ch));
4366 }), doc.sel.primIndex);
4470 }), doc.sel.primIndex);
4367 if (doc.cm) {
4471 if (doc.cm) {
4368 regChange(doc.cm, doc.first, doc.first - distance, distance);
4472 regChange(doc.cm, doc.first, doc.first - distance, distance);
4369 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4473 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4370 regLineChange(doc.cm, l, "gutter");
4474 regLineChange(doc.cm, l, "gutter");
4371 }
4475 }
4372 }
4476 }
4373
4477
4374 // More lower-level change function, handling only a single document
4478 // More lower-level change function, handling only a single document
4375 // (not linked ones).
4479 // (not linked ones).
4376 function makeChangeSingleDoc(doc, change, selAfter, spans) {
4480 function makeChangeSingleDoc(doc, change, selAfter, spans) {
4377 if (doc.cm && !doc.cm.curOp)
4481 if (doc.cm && !doc.cm.curOp)
4378 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4482 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4379
4483
4380 if (change.to.line < doc.first) {
4484 if (change.to.line < doc.first) {
4381 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4485 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4382 return;
4486 return;
4383 }
4487 }
4384 if (change.from.line > doc.lastLine()) return;
4488 if (change.from.line > doc.lastLine()) return;
4385
4489
4386 // Clip the change to the size of this doc
4490 // Clip the change to the size of this doc
4387 if (change.from.line < doc.first) {
4491 if (change.from.line < doc.first) {
4388 var shift = change.text.length - 1 - (doc.first - change.from.line);
4492 var shift = change.text.length - 1 - (doc.first - change.from.line);
4389 shiftDoc(doc, shift);
4493 shiftDoc(doc, shift);
4390 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4494 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4391 text: [lst(change.text)], origin: change.origin};
4495 text: [lst(change.text)], origin: change.origin};
4392 }
4496 }
4393 var last = doc.lastLine();
4497 var last = doc.lastLine();
4394 if (change.to.line > last) {
4498 if (change.to.line > last) {
4395 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4499 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4396 text: [change.text[0]], origin: change.origin};
4500 text: [change.text[0]], origin: change.origin};
4397 }
4501 }
4398
4502
4399 change.removed = getBetween(doc, change.from, change.to);
4503 change.removed = getBetween(doc, change.from, change.to);
4400
4504
4401 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4505 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4402 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4506 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4403 else updateDoc(doc, change, spans);
4507 else updateDoc(doc, change, spans);
4404 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4508 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4405 }
4509 }
4406
4510
4407 // Handle the interaction of a change to a document with the editor
4511 // Handle the interaction of a change to a document with the editor
4408 // that this document is part of.
4512 // that this document is part of.
4409 function makeChangeSingleDocInEditor(cm, change, spans) {
4513 function makeChangeSingleDocInEditor(cm, change, spans) {
4410 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4514 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4411
4515
4412 var recomputeMaxLength = false, checkWidthStart = from.line;
4516 var recomputeMaxLength = false, checkWidthStart = from.line;
4413 if (!cm.options.lineWrapping) {
4517 if (!cm.options.lineWrapping) {
4414 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4518 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4415 doc.iter(checkWidthStart, to.line + 1, function(line) {
4519 doc.iter(checkWidthStart, to.line + 1, function(line) {
4416 if (line == display.maxLine) {
4520 if (line == display.maxLine) {
4417 recomputeMaxLength = true;
4521 recomputeMaxLength = true;
4418 return true;
4522 return true;
4419 }
4523 }
4420 });
4524 });
4421 }
4525 }
4422
4526
4423 if (doc.sel.contains(change.from, change.to) > -1)
4527 if (doc.sel.contains(change.from, change.to) > -1)
4424 signalCursorActivity(cm);
4528 signalCursorActivity(cm);
4425
4529
4426 updateDoc(doc, change, spans, estimateHeight(cm));
4530 updateDoc(doc, change, spans, estimateHeight(cm));
4427
4531
4428 if (!cm.options.lineWrapping) {
4532 if (!cm.options.lineWrapping) {
4429 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4533 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4430 var len = lineLength(line);
4534 var len = lineLength(line);
4431 if (len > display.maxLineLength) {
4535 if (len > display.maxLineLength) {
4432 display.maxLine = line;
4536 display.maxLine = line;
4433 display.maxLineLength = len;
4537 display.maxLineLength = len;
4434 display.maxLineChanged = true;
4538 display.maxLineChanged = true;
4435 recomputeMaxLength = false;
4539 recomputeMaxLength = false;
4436 }
4540 }
4437 });
4541 });
4438 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4542 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4439 }
4543 }
4440
4544
4441 // Adjust frontier, schedule worker
4545 // Adjust frontier, schedule worker
4442 doc.frontier = Math.min(doc.frontier, from.line);
4546 doc.frontier = Math.min(doc.frontier, from.line);
4443 startWorker(cm, 400);
4547 startWorker(cm, 400);
4444
4548
4445 var lendiff = change.text.length - (to.line - from.line) - 1;
4549 var lendiff = change.text.length - (to.line - from.line) - 1;
4446 // Remember that these lines changed, for updating the display
4550 // Remember that these lines changed, for updating the display
4447 if (change.full)
4551 if (change.full)
4448 regChange(cm);
4552 regChange(cm);
4449 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
4553 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
4450 regLineChange(cm, from.line, "text");
4554 regLineChange(cm, from.line, "text");
4451 else
4555 else
4452 regChange(cm, from.line, to.line + 1, lendiff);
4556 regChange(cm, from.line, to.line + 1, lendiff);
4453
4557
4454 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4558 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4455 if (changeHandler || changesHandler) {
4559 if (changeHandler || changesHandler) {
4456 var obj = {
4560 var obj = {
4457 from: from, to: to,
4561 from: from, to: to,
4458 text: change.text,
4562 text: change.text,
4459 removed: change.removed,
4563 removed: change.removed,
4460 origin: change.origin
4564 origin: change.origin
4461 };
4565 };
4462 if (changeHandler) signalLater(cm, "change", cm, obj);
4566 if (changeHandler) signalLater(cm, "change", cm, obj);
4463 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4567 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4464 }
4568 }
4465 cm.display.selForContextMenu = null;
4569 cm.display.selForContextMenu = null;
4466 }
4570 }
4467
4571
4468 function replaceRange(doc, code, from, to, origin) {
4572 function replaceRange(doc, code, from, to, origin) {
4469 if (!to) to = from;
4573 if (!to) to = from;
4470 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4574 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4471 if (typeof code == "string") code = splitLines(code);
4575 if (typeof code == "string") code = doc.splitLines(code);
4472 makeChange(doc, {from: from, to: to, text: code, origin: origin});
4576 makeChange(doc, {from: from, to: to, text: code, origin: origin});
4473 }
4577 }
4474
4578
4475 // SCROLLING THINGS INTO VIEW
4579 // SCROLLING THINGS INTO VIEW
4476
4580
4477 // If an editor sits on the top or bottom of the window, partially
4581 // If an editor sits on the top or bottom of the window, partially
4478 // scrolled out of view, this ensures that the cursor is visible.
4582 // scrolled out of view, this ensures that the cursor is visible.
4479 function maybeScrollWindow(cm, coords) {
4583 function maybeScrollWindow(cm, coords) {
4480 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4584 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4481
4585
4482 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4586 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4483 if (coords.top + box.top < 0) doScroll = true;
4587 if (coords.top + box.top < 0) doScroll = true;
4484 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4588 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4485 if (doScroll != null && !phantom) {
4589 if (doScroll != null && !phantom) {
4486 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4590 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4487 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4591 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4488 (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4592 (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4489 coords.left + "px; width: 2px;");
4593 coords.left + "px; width: 2px;");
4490 cm.display.lineSpace.appendChild(scrollNode);
4594 cm.display.lineSpace.appendChild(scrollNode);
4491 scrollNode.scrollIntoView(doScroll);
4595 scrollNode.scrollIntoView(doScroll);
4492 cm.display.lineSpace.removeChild(scrollNode);
4596 cm.display.lineSpace.removeChild(scrollNode);
4493 }
4597 }
4494 }
4598 }
4495
4599
4496 // Scroll a given position into view (immediately), verifying that
4600 // Scroll a given position into view (immediately), verifying that
4497 // it actually became visible (as line heights are accurately
4601 // it actually became visible (as line heights are accurately
4498 // measured, the position of something may 'drift' during drawing).
4602 // measured, the position of something may 'drift' during drawing).
4499 function scrollPosIntoView(cm, pos, end, margin) {
4603 function scrollPosIntoView(cm, pos, end, margin) {
4500 if (margin == null) margin = 0;
4604 if (margin == null) margin = 0;
4501 for (var limit = 0; limit < 5; limit++) {
4605 for (var limit = 0; limit < 5; limit++) {
4502 var changed = false, coords = cursorCoords(cm, pos);
4606 var changed = false, coords = cursorCoords(cm, pos);
4503 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4607 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4504 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4608 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4505 Math.min(coords.top, endCoords.top) - margin,
4609 Math.min(coords.top, endCoords.top) - margin,
4506 Math.max(coords.left, endCoords.left),
4610 Math.max(coords.left, endCoords.left),
4507 Math.max(coords.bottom, endCoords.bottom) + margin);
4611 Math.max(coords.bottom, endCoords.bottom) + margin);
4508 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4612 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4509 if (scrollPos.scrollTop != null) {
4613 if (scrollPos.scrollTop != null) {
4510 setScrollTop(cm, scrollPos.scrollTop);
4614 setScrollTop(cm, scrollPos.scrollTop);
4511 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4615 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4512 }
4616 }
4513 if (scrollPos.scrollLeft != null) {
4617 if (scrollPos.scrollLeft != null) {
4514 setScrollLeft(cm, scrollPos.scrollLeft);
4618 setScrollLeft(cm, scrollPos.scrollLeft);
4515 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4619 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4516 }
4620 }
4517 if (!changed) break;
4621 if (!changed) break;
4518 }
4622 }
4519 return coords;
4623 return coords;
4520 }
4624 }
4521
4625
4522 // Scroll a given set of coordinates into view (immediately).
4626 // Scroll a given set of coordinates into view (immediately).
4523 function scrollIntoView(cm, x1, y1, x2, y2) {
4627 function scrollIntoView(cm, x1, y1, x2, y2) {
4524 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4628 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4525 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4629 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4526 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4630 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4527 }
4631 }
4528
4632
4529 // Calculate a new scroll position needed to scroll the given
4633 // Calculate a new scroll position needed to scroll the given
4530 // rectangle into view. Returns an object with scrollTop and
4634 // rectangle into view. Returns an object with scrollTop and
4531 // scrollLeft properties. When these are undefined, the
4635 // scrollLeft properties. When these are undefined, the
4532 // vertical/horizontal position does not need to be adjusted.
4636 // vertical/horizontal position does not need to be adjusted.
4533 function calculateScrollPos(cm, x1, y1, x2, y2) {
4637 function calculateScrollPos(cm, x1, y1, x2, y2) {
4534 var display = cm.display, snapMargin = textHeight(cm.display);
4638 var display = cm.display, snapMargin = textHeight(cm.display);
4535 if (y1 < 0) y1 = 0;
4639 if (y1 < 0) y1 = 0;
4536 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4640 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4537 var screen = displayHeight(cm), result = {};
4641 var screen = displayHeight(cm), result = {};
4538 if (y2 - y1 > screen) y2 = y1 + screen;
4642 if (y2 - y1 > screen) y2 = y1 + screen;
4539 var docBottom = cm.doc.height + paddingVert(display);
4643 var docBottom = cm.doc.height + paddingVert(display);
4540 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4644 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4541 if (y1 < screentop) {
4645 if (y1 < screentop) {
4542 result.scrollTop = atTop ? 0 : y1;
4646 result.scrollTop = atTop ? 0 : y1;
4543 } else if (y2 > screentop + screen) {
4647 } else if (y2 > screentop + screen) {
4544 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4648 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4545 if (newTop != screentop) result.scrollTop = newTop;
4649 if (newTop != screentop) result.scrollTop = newTop;
4546 }
4650 }
4547
4651
4548 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4652 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4549 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4653 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4550 var tooWide = x2 - x1 > screenw;
4654 var tooWide = x2 - x1 > screenw;
4551 if (tooWide) x2 = x1 + screenw;
4655 if (tooWide) x2 = x1 + screenw;
4552 if (x1 < 10)
4656 if (x1 < 10)
4553 result.scrollLeft = 0;
4657 result.scrollLeft = 0;
4554 else if (x1 < screenleft)
4658 else if (x1 < screenleft)
4555 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4659 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4556 else if (x2 > screenw + screenleft - 3)
4660 else if (x2 > screenw + screenleft - 3)
4557 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4661 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4558 return result;
4662 return result;
4559 }
4663 }
4560
4664
4561 // Store a relative adjustment to the scroll position in the current
4665 // Store a relative adjustment to the scroll position in the current
4562 // operation (to be applied when the operation finishes).
4666 // operation (to be applied when the operation finishes).
4563 function addToScrollPos(cm, left, top) {
4667 function addToScrollPos(cm, left, top) {
4564 if (left != null || top != null) resolveScrollToPos(cm);
4668 if (left != null || top != null) resolveScrollToPos(cm);
4565 if (left != null)
4669 if (left != null)
4566 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
4670 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
4567 if (top != null)
4671 if (top != null)
4568 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4672 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4569 }
4673 }
4570
4674
4571 // Make sure that at the end of the operation the current cursor is
4675 // Make sure that at the end of the operation the current cursor is
4572 // shown.
4676 // shown.
4573 function ensureCursorVisible(cm) {
4677 function ensureCursorVisible(cm) {
4574 resolveScrollToPos(cm);
4678 resolveScrollToPos(cm);
4575 var cur = cm.getCursor(), from = cur, to = cur;
4679 var cur = cm.getCursor(), from = cur, to = cur;
4576 if (!cm.options.lineWrapping) {
4680 if (!cm.options.lineWrapping) {
4577 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4681 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4578 to = Pos(cur.line, cur.ch + 1);
4682 to = Pos(cur.line, cur.ch + 1);
4579 }
4683 }
4580 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4684 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4581 }
4685 }
4582
4686
4583 // When an operation has its scrollToPos property set, and another
4687 // When an operation has its scrollToPos property set, and another
4584 // scroll action is applied before the end of the operation, this
4688 // scroll action is applied before the end of the operation, this
4585 // 'simulates' scrolling that position into view in a cheap way, so
4689 // 'simulates' scrolling that position into view in a cheap way, so
4586 // that the effect of intermediate scroll commands is not ignored.
4690 // that the effect of intermediate scroll commands is not ignored.
4587 function resolveScrollToPos(cm) {
4691 function resolveScrollToPos(cm) {
4588 var range = cm.curOp.scrollToPos;
4692 var range = cm.curOp.scrollToPos;
4589 if (range) {
4693 if (range) {
4590 cm.curOp.scrollToPos = null;
4694 cm.curOp.scrollToPos = null;
4591 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4695 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4592 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4696 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4593 Math.min(from.top, to.top) - range.margin,
4697 Math.min(from.top, to.top) - range.margin,
4594 Math.max(from.right, to.right),
4698 Math.max(from.right, to.right),
4595 Math.max(from.bottom, to.bottom) + range.margin);
4699 Math.max(from.bottom, to.bottom) + range.margin);
4596 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4700 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4597 }
4701 }
4598 }
4702 }
4599
4703
4600 // API UTILITIES
4704 // API UTILITIES
4601
4705
4602 // Indent the given line. The how parameter can be "smart",
4706 // Indent the given line. The how parameter can be "smart",
4603 // "add"/null, "subtract", or "prev". When aggressive is false
4707 // "add"/null, "subtract", or "prev". When aggressive is false
4604 // (typically set to true for forced single-line indents), empty
4708 // (typically set to true for forced single-line indents), empty
4605 // lines are not indented, and places where the mode returns Pass
4709 // lines are not indented, and places where the mode returns Pass
4606 // are left alone.
4710 // are left alone.
4607 function indentLine(cm, n, how, aggressive) {
4711 function indentLine(cm, n, how, aggressive) {
4608 var doc = cm.doc, state;
4712 var doc = cm.doc, state;
4609 if (how == null) how = "add";
4713 if (how == null) how = "add";
4610 if (how == "smart") {
4714 if (how == "smart") {
4611 // Fall back to "prev" when the mode doesn't have an indentation
4715 // Fall back to "prev" when the mode doesn't have an indentation
4612 // method.
4716 // method.
4613 if (!doc.mode.indent) how = "prev";
4717 if (!doc.mode.indent) how = "prev";
4614 else state = getStateBefore(cm, n);
4718 else state = getStateBefore(cm, n);
4615 }
4719 }
4616
4720
4617 var tabSize = cm.options.tabSize;
4721 var tabSize = cm.options.tabSize;
4618 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4722 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4619 if (line.stateAfter) line.stateAfter = null;
4723 if (line.stateAfter) line.stateAfter = null;
4620 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4724 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4621 if (!aggressive && !/\S/.test(line.text)) {
4725 if (!aggressive && !/\S/.test(line.text)) {
4622 indentation = 0;
4726 indentation = 0;
4623 how = "not";
4727 how = "not";
4624 } else if (how == "smart") {
4728 } else if (how == "smart") {
4625 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
4729 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
4626 if (indentation == Pass || indentation > 150) {
4730 if (indentation == Pass || indentation > 150) {
4627 if (!aggressive) return;
4731 if (!aggressive) return;
4628 how = "prev";
4732 how = "prev";
4629 }
4733 }
4630 }
4734 }
4631 if (how == "prev") {
4735 if (how == "prev") {
4632 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4736 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4633 else indentation = 0;
4737 else indentation = 0;
4634 } else if (how == "add") {
4738 } else if (how == "add") {
4635 indentation = curSpace + cm.options.indentUnit;
4739 indentation = curSpace + cm.options.indentUnit;
4636 } else if (how == "subtract") {
4740 } else if (how == "subtract") {
4637 indentation = curSpace - cm.options.indentUnit;
4741 indentation = curSpace - cm.options.indentUnit;
4638 } else if (typeof how == "number") {
4742 } else if (typeof how == "number") {
4639 indentation = curSpace + how;
4743 indentation = curSpace + how;
4640 }
4744 }
4641 indentation = Math.max(0, indentation);
4745 indentation = Math.max(0, indentation);
4642
4746
4643 var indentString = "", pos = 0;
4747 var indentString = "", pos = 0;
4644 if (cm.options.indentWithTabs)
4748 if (cm.options.indentWithTabs)
4645 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4749 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4646 if (pos < indentation) indentString += spaceStr(indentation - pos);
4750 if (pos < indentation) indentString += spaceStr(indentation - pos);
4647
4751
4648 if (indentString != curSpaceString) {
4752 if (indentString != curSpaceString) {
4649 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4753 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4650 line.stateAfter = null;
4754 line.stateAfter = null;
4651 return true;
4755 return true;
4652 } else {
4756 } else {
4653 // Ensure that, if the cursor was in the whitespace at the start
4757 // Ensure that, if the cursor was in the whitespace at the start
4654 // of the line, it is moved to the end of that space.
4758 // of the line, it is moved to the end of that space.
4655 for (var i = 0; i < doc.sel.ranges.length; i++) {
4759 for (var i = 0; i < doc.sel.ranges.length; i++) {
4656 var range = doc.sel.ranges[i];
4760 var range = doc.sel.ranges[i];
4657 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4761 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4658 var pos = Pos(n, curSpaceString.length);
4762 var pos = Pos(n, curSpaceString.length);
4659 replaceOneSelection(doc, i, new Range(pos, pos));
4763 replaceOneSelection(doc, i, new Range(pos, pos));
4660 break;
4764 break;
4661 }
4765 }
4662 }
4766 }
4663 }
4767 }
4664 }
4768 }
4665
4769
4666 // Utility for applying a change to a line by handle or number,
4770 // Utility for applying a change to a line by handle or number,
4667 // returning the number and optionally registering the line as
4771 // returning the number and optionally registering the line as
4668 // changed.
4772 // changed.
4669 function changeLine(doc, handle, changeType, op) {
4773 function changeLine(doc, handle, changeType, op) {
4670 var no = handle, line = handle;
4774 var no = handle, line = handle;
4671 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4775 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4672 else no = lineNo(handle);
4776 else no = lineNo(handle);
4673 if (no == null) return null;
4777 if (no == null) return null;
4674 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4778 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4675 return line;
4779 return line;
4676 }
4780 }
4677
4781
4678 // Helper for deleting text near the selection(s), used to implement
4782 // Helper for deleting text near the selection(s), used to implement
4679 // backspace, delete, and similar functionality.
4783 // backspace, delete, and similar functionality.
4680 function deleteNearSelection(cm, compute) {
4784 function deleteNearSelection(cm, compute) {
4681 var ranges = cm.doc.sel.ranges, kill = [];
4785 var ranges = cm.doc.sel.ranges, kill = [];
4682 // Build up a set of ranges to kill first, merging overlapping
4786 // Build up a set of ranges to kill first, merging overlapping
4683 // ranges.
4787 // ranges.
4684 for (var i = 0; i < ranges.length; i++) {
4788 for (var i = 0; i < ranges.length; i++) {
4685 var toKill = compute(ranges[i]);
4789 var toKill = compute(ranges[i]);
4686 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4790 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4687 var replaced = kill.pop();
4791 var replaced = kill.pop();
4688 if (cmp(replaced.from, toKill.from) < 0) {
4792 if (cmp(replaced.from, toKill.from) < 0) {
4689 toKill.from = replaced.from;
4793 toKill.from = replaced.from;
4690 break;
4794 break;
4691 }
4795 }
4692 }
4796 }
4693 kill.push(toKill);
4797 kill.push(toKill);
4694 }
4798 }
4695 // Next, remove those actual ranges.
4799 // Next, remove those actual ranges.
4696 runInOp(cm, function() {
4800 runInOp(cm, function() {
4697 for (var i = kill.length - 1; i >= 0; i--)
4801 for (var i = kill.length - 1; i >= 0; i--)
4698 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4802 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4699 ensureCursorVisible(cm);
4803 ensureCursorVisible(cm);
4700 });
4804 });
4701 }
4805 }
4702
4806
4703 // Used for horizontal relative motion. Dir is -1 or 1 (left or
4807 // Used for horizontal relative motion. Dir is -1 or 1 (left or
4704 // right), unit can be "char", "column" (like char, but doesn't
4808 // right), unit can be "char", "column" (like char, but doesn't
4705 // cross line boundaries), "word" (across next word), or "group" (to
4809 // cross line boundaries), "word" (across next word), or "group" (to
4706 // the start of next group of word or non-word-non-whitespace
4810 // the start of next group of word or non-word-non-whitespace
4707 // chars). The visually param controls whether, in right-to-left
4811 // chars). The visually param controls whether, in right-to-left
4708 // text, direction 1 means to move towards the next index in the
4812 // text, direction 1 means to move towards the next index in the
4709 // string, or towards the character to the right of the current
4813 // string, or towards the character to the right of the current
4710 // position. The resulting position will have a hitSide=true
4814 // position. The resulting position will have a hitSide=true
4711 // property if it reached the end of the document.
4815 // property if it reached the end of the document.
4712 function findPosH(doc, pos, dir, unit, visually) {
4816 function findPosH(doc, pos, dir, unit, visually) {
4713 var line = pos.line, ch = pos.ch, origDir = dir;
4817 var line = pos.line, ch = pos.ch, origDir = dir;
4714 var lineObj = getLine(doc, line);
4818 var lineObj = getLine(doc, line);
4715 var possible = true;
4716 function findNextLine() {
4819 function findNextLine() {
4717 var l = line + dir;
4820 var l = line + dir;
4718 if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
4821 if (l < doc.first || l >= doc.first + doc.size) return false
4719 line = l;
4822 line = l;
4720 return lineObj = getLine(doc, l);
4823 return lineObj = getLine(doc, l);
4721 }
4824 }
4722 function moveOnce(boundToLine) {
4825 function moveOnce(boundToLine) {
4723 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4826 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4724 if (next == null) {
4827 if (next == null) {
4725 if (!boundToLine && findNextLine()) {
4828 if (!boundToLine && findNextLine()) {
4726 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4829 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4727 else ch = dir < 0 ? lineObj.text.length : 0;
4830 else ch = dir < 0 ? lineObj.text.length : 0;
4728 } else return (possible = false);
4831 } else return false
4729 } else ch = next;
4832 } else ch = next;
4730 return true;
4833 return true;
4731 }
4834 }
4732
4835
4733 if (unit == "char") moveOnce();
4836 if (unit == "char") {
4734 else if (unit == "column") moveOnce(true);
4837 moveOnce()
4735 else if (unit == "word" || unit == "group") {
4838 } else if (unit == "column") {
4839 moveOnce(true)
4840 } else if (unit == "word" || unit == "group") {
4736 var sawType = null, group = unit == "group";
4841 var sawType = null, group = unit == "group";
4737 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4842 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4738 for (var first = true;; first = false) {
4843 for (var first = true;; first = false) {
4739 if (dir < 0 && !moveOnce(!first)) break;
4844 if (dir < 0 && !moveOnce(!first)) break;
4740 var cur = lineObj.text.charAt(ch) || "\n";
4845 var cur = lineObj.text.charAt(ch) || "\n";
4741 var type = isWordChar(cur, helper) ? "w"
4846 var type = isWordChar(cur, helper) ? "w"
4742 : group && cur == "\n" ? "n"
4847 : group && cur == "\n" ? "n"
4743 : !group || /\s/.test(cur) ? null
4848 : !group || /\s/.test(cur) ? null
4744 : "p";
4849 : "p";
4745 if (group && !first && !type) type = "s";
4850 if (group && !first && !type) type = "s";
4746 if (sawType && sawType != type) {
4851 if (sawType && sawType != type) {
4747 if (dir < 0) {dir = 1; moveOnce();}
4852 if (dir < 0) {dir = 1; moveOnce();}
4748 break;
4853 break;
4749 }
4854 }
4750
4855
4751 if (type) sawType = type;
4856 if (type) sawType = type;
4752 if (dir > 0 && !moveOnce(!first)) break;
4857 if (dir > 0 && !moveOnce(!first)) break;
4753 }
4858 }
4754 }
4859 }
4755 var result = skipAtomic(doc, Pos(line, ch), origDir, true);
4860 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
4756 if (!possible) result.hitSide = true;
4861 if (!cmp(pos, result)) result.hitSide = true;
4757 return result;
4862 return result;
4758 }
4863 }
4759
4864
4760 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4865 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4761 // "page" or "line". The resulting position will have a hitSide=true
4866 // "page" or "line". The resulting position will have a hitSide=true
4762 // property if it reached the end of the document.
4867 // property if it reached the end of the document.
4763 function findPosV(cm, pos, dir, unit) {
4868 function findPosV(cm, pos, dir, unit) {
4764 var doc = cm.doc, x = pos.left, y;
4869 var doc = cm.doc, x = pos.left, y;
4765 if (unit == "page") {
4870 if (unit == "page") {
4766 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4871 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4767 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4872 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4768 } else if (unit == "line") {
4873 } else if (unit == "line") {
4769 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4874 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4770 }
4875 }
4771 for (;;) {
4876 for (;;) {
4772 var target = coordsChar(cm, x, y);
4877 var target = coordsChar(cm, x, y);
4773 if (!target.outside) break;
4878 if (!target.outside) break;
4774 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4879 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4775 y += dir * 5;
4880 y += dir * 5;
4776 }
4881 }
4777 return target;
4882 return target;
4778 }
4883 }
4779
4884
4780 // EDITOR METHODS
4885 // EDITOR METHODS
4781
4886
4782 // The publicly visible API. Note that methodOp(f) means
4887 // The publicly visible API. Note that methodOp(f) means
4783 // 'wrap f in an operation, performed on its `this` parameter'.
4888 // 'wrap f in an operation, performed on its `this` parameter'.
4784
4889
4785 // This is not the complete set of editor methods. Most of the
4890 // This is not the complete set of editor methods. Most of the
4786 // methods defined on the Doc type are also injected into
4891 // methods defined on the Doc type are also injected into
4787 // CodeMirror.prototype, for backwards compatibility and
4892 // CodeMirror.prototype, for backwards compatibility and
4788 // convenience.
4893 // convenience.
4789
4894
4790 CodeMirror.prototype = {
4895 CodeMirror.prototype = {
4791 constructor: CodeMirror,
4896 constructor: CodeMirror,
4792 focus: function(){window.focus(); this.display.input.focus();},
4897 focus: function(){window.focus(); this.display.input.focus();},
4793
4898
4794 setOption: function(option, value) {
4899 setOption: function(option, value) {
4795 var options = this.options, old = options[option];
4900 var options = this.options, old = options[option];
4796 if (options[option] == value && option != "mode") return;
4901 if (options[option] == value && option != "mode") return;
4797 options[option] = value;
4902 options[option] = value;
4798 if (optionHandlers.hasOwnProperty(option))
4903 if (optionHandlers.hasOwnProperty(option))
4799 operation(this, optionHandlers[option])(this, value, old);
4904 operation(this, optionHandlers[option])(this, value, old);
4800 },
4905 },
4801
4906
4802 getOption: function(option) {return this.options[option];},
4907 getOption: function(option) {return this.options[option];},
4803 getDoc: function() {return this.doc;},
4908 getDoc: function() {return this.doc;},
4804
4909
4805 addKeyMap: function(map, bottom) {
4910 addKeyMap: function(map, bottom) {
4806 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4911 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4807 },
4912 },
4808 removeKeyMap: function(map) {
4913 removeKeyMap: function(map) {
4809 var maps = this.state.keyMaps;
4914 var maps = this.state.keyMaps;
4810 for (var i = 0; i < maps.length; ++i)
4915 for (var i = 0; i < maps.length; ++i)
4811 if (maps[i] == map || maps[i].name == map) {
4916 if (maps[i] == map || maps[i].name == map) {
4812 maps.splice(i, 1);
4917 maps.splice(i, 1);
4813 return true;
4918 return true;
4814 }
4919 }
4815 },
4920 },
4816
4921
4817 addOverlay: methodOp(function(spec, options) {
4922 addOverlay: methodOp(function(spec, options) {
4818 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4923 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4819 if (mode.startState) throw new Error("Overlays may not be stateful.");
4924 if (mode.startState) throw new Error("Overlays may not be stateful.");
4820 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4925 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4821 this.state.modeGen++;
4926 this.state.modeGen++;
4822 regChange(this);
4927 regChange(this);
4823 }),
4928 }),
4824 removeOverlay: methodOp(function(spec) {
4929 removeOverlay: methodOp(function(spec) {
4825 var overlays = this.state.overlays;
4930 var overlays = this.state.overlays;
4826 for (var i = 0; i < overlays.length; ++i) {
4931 for (var i = 0; i < overlays.length; ++i) {
4827 var cur = overlays[i].modeSpec;
4932 var cur = overlays[i].modeSpec;
4828 if (cur == spec || typeof spec == "string" && cur.name == spec) {
4933 if (cur == spec || typeof spec == "string" && cur.name == spec) {
4829 overlays.splice(i, 1);
4934 overlays.splice(i, 1);
4830 this.state.modeGen++;
4935 this.state.modeGen++;
4831 regChange(this);
4936 regChange(this);
4832 return;
4937 return;
4833 }
4938 }
4834 }
4939 }
4835 }),
4940 }),
4836
4941
4837 indentLine: methodOp(function(n, dir, aggressive) {
4942 indentLine: methodOp(function(n, dir, aggressive) {
4838 if (typeof dir != "string" && typeof dir != "number") {
4943 if (typeof dir != "string" && typeof dir != "number") {
4839 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4944 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4840 else dir = dir ? "add" : "subtract";
4945 else dir = dir ? "add" : "subtract";
4841 }
4946 }
4842 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4947 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4843 }),
4948 }),
4844 indentSelection: methodOp(function(how) {
4949 indentSelection: methodOp(function(how) {
4845 var ranges = this.doc.sel.ranges, end = -1;
4950 var ranges = this.doc.sel.ranges, end = -1;
4846 for (var i = 0; i < ranges.length; i++) {
4951 for (var i = 0; i < ranges.length; i++) {
4847 var range = ranges[i];
4952 var range = ranges[i];
4848 if (!range.empty()) {
4953 if (!range.empty()) {
4849 var from = range.from(), to = range.to();
4954 var from = range.from(), to = range.to();
4850 var start = Math.max(end, from.line);
4955 var start = Math.max(end, from.line);
4851 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4956 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4852 for (var j = start; j < end; ++j)
4957 for (var j = start; j < end; ++j)
4853 indentLine(this, j, how);
4958 indentLine(this, j, how);
4854 var newRanges = this.doc.sel.ranges;
4959 var newRanges = this.doc.sel.ranges;
4855 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4960 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4856 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4961 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4857 } else if (range.head.line > end) {
4962 } else if (range.head.line > end) {
4858 indentLine(this, range.head.line, how, true);
4963 indentLine(this, range.head.line, how, true);
4859 end = range.head.line;
4964 end = range.head.line;
4860 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4965 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4861 }
4966 }
4862 }
4967 }
4863 }),
4968 }),
4864
4969
4865 // Fetch the parser token for a given character. Useful for hacks
4970 // Fetch the parser token for a given character. Useful for hacks
4866 // that want to inspect the mode state (say, for completion).
4971 // that want to inspect the mode state (say, for completion).
4867 getTokenAt: function(pos, precise) {
4972 getTokenAt: function(pos, precise) {
4868 return takeToken(this, pos, precise);
4973 return takeToken(this, pos, precise);
4869 },
4974 },
4870
4975
4871 getLineTokens: function(line, precise) {
4976 getLineTokens: function(line, precise) {
4872 return takeToken(this, Pos(line), precise, true);
4977 return takeToken(this, Pos(line), precise, true);
4873 },
4978 },
4874
4979
4875 getTokenTypeAt: function(pos) {
4980 getTokenTypeAt: function(pos) {
4876 pos = clipPos(this.doc, pos);
4981 pos = clipPos(this.doc, pos);
4877 var styles = getLineStyles(this, getLine(this.doc, pos.line));
4982 var styles = getLineStyles(this, getLine(this.doc, pos.line));
4878 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4983 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4879 var type;
4984 var type;
4880 if (ch == 0) type = styles[2];
4985 if (ch == 0) type = styles[2];
4881 else for (;;) {
4986 else for (;;) {
4882 var mid = (before + after) >> 1;
4987 var mid = (before + after) >> 1;
4883 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4988 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4884 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4989 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4885 else { type = styles[mid * 2 + 2]; break; }
4990 else { type = styles[mid * 2 + 2]; break; }
4886 }
4991 }
4887 var cut = type ? type.indexOf("cm-overlay ") : -1;
4992 var cut = type ? type.indexOf("cm-overlay ") : -1;
4888 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
4993 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
4889 },
4994 },
4890
4995
4891 getModeAt: function(pos) {
4996 getModeAt: function(pos) {
4892 var mode = this.doc.mode;
4997 var mode = this.doc.mode;
4893 if (!mode.innerMode) return mode;
4998 if (!mode.innerMode) return mode;
4894 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4999 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4895 },
5000 },
4896
5001
4897 getHelper: function(pos, type) {
5002 getHelper: function(pos, type) {
4898 return this.getHelpers(pos, type)[0];
5003 return this.getHelpers(pos, type)[0];
4899 },
5004 },
4900
5005
4901 getHelpers: function(pos, type) {
5006 getHelpers: function(pos, type) {
4902 var found = [];
5007 var found = [];
4903 if (!helpers.hasOwnProperty(type)) return found;
5008 if (!helpers.hasOwnProperty(type)) return found;
4904 var help = helpers[type], mode = this.getModeAt(pos);
5009 var help = helpers[type], mode = this.getModeAt(pos);
4905 if (typeof mode[type] == "string") {
5010 if (typeof mode[type] == "string") {
4906 if (help[mode[type]]) found.push(help[mode[type]]);
5011 if (help[mode[type]]) found.push(help[mode[type]]);
4907 } else if (mode[type]) {
5012 } else if (mode[type]) {
4908 for (var i = 0; i < mode[type].length; i++) {
5013 for (var i = 0; i < mode[type].length; i++) {
4909 var val = help[mode[type][i]];
5014 var val = help[mode[type][i]];
4910 if (val) found.push(val);
5015 if (val) found.push(val);
4911 }
5016 }
4912 } else if (mode.helperType && help[mode.helperType]) {
5017 } else if (mode.helperType && help[mode.helperType]) {
4913 found.push(help[mode.helperType]);
5018 found.push(help[mode.helperType]);
4914 } else if (help[mode.name]) {
5019 } else if (help[mode.name]) {
4915 found.push(help[mode.name]);
5020 found.push(help[mode.name]);
4916 }
5021 }
4917 for (var i = 0; i < help._global.length; i++) {
5022 for (var i = 0; i < help._global.length; i++) {
4918 var cur = help._global[i];
5023 var cur = help._global[i];
4919 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
5024 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
4920 found.push(cur.val);
5025 found.push(cur.val);
4921 }
5026 }
4922 return found;
5027 return found;
4923 },
5028 },
4924
5029
4925 getStateAfter: function(line, precise) {
5030 getStateAfter: function(line, precise) {
4926 var doc = this.doc;
5031 var doc = this.doc;
4927 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
5032 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
4928 return getStateBefore(this, line + 1, precise);
5033 return getStateBefore(this, line + 1, precise);
4929 },
5034 },
4930
5035
4931 cursorCoords: function(start, mode) {
5036 cursorCoords: function(start, mode) {
4932 var pos, range = this.doc.sel.primary();
5037 var pos, range = this.doc.sel.primary();
4933 if (start == null) pos = range.head;
5038 if (start == null) pos = range.head;
4934 else if (typeof start == "object") pos = clipPos(this.doc, start);
5039 else if (typeof start == "object") pos = clipPos(this.doc, start);
4935 else pos = start ? range.from() : range.to();
5040 else pos = start ? range.from() : range.to();
4936 return cursorCoords(this, pos, mode || "page");
5041 return cursorCoords(this, pos, mode || "page");
4937 },
5042 },
4938
5043
4939 charCoords: function(pos, mode) {
5044 charCoords: function(pos, mode) {
4940 return charCoords(this, clipPos(this.doc, pos), mode || "page");
5045 return charCoords(this, clipPos(this.doc, pos), mode || "page");
4941 },
5046 },
4942
5047
4943 coordsChar: function(coords, mode) {
5048 coordsChar: function(coords, mode) {
4944 coords = fromCoordSystem(this, coords, mode || "page");
5049 coords = fromCoordSystem(this, coords, mode || "page");
4945 return coordsChar(this, coords.left, coords.top);
5050 return coordsChar(this, coords.left, coords.top);
4946 },
5051 },
4947
5052
4948 lineAtHeight: function(height, mode) {
5053 lineAtHeight: function(height, mode) {
4949 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
5054 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
4950 return lineAtHeight(this.doc, height + this.display.viewOffset);
5055 return lineAtHeight(this.doc, height + this.display.viewOffset);
4951 },
5056 },
4952 heightAtLine: function(line, mode) {
5057 heightAtLine: function(line, mode) {
4953 var end = false, lineObj;
5058 var end = false, lineObj;
4954 if (typeof line == "number") {
5059 if (typeof line == "number") {
4955 var last = this.doc.first + this.doc.size - 1;
5060 var last = this.doc.first + this.doc.size - 1;
4956 if (line < this.doc.first) line = this.doc.first;
5061 if (line < this.doc.first) line = this.doc.first;
4957 else if (line > last) { line = last; end = true; }
5062 else if (line > last) { line = last; end = true; }
4958 lineObj = getLine(this.doc, line);
5063 lineObj = getLine(this.doc, line);
4959 } else {
5064 } else {
4960 lineObj = line;
5065 lineObj = line;
4961 }
5066 }
4962 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
5067 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
4963 (end ? this.doc.height - heightAtLine(lineObj) : 0);
5068 (end ? this.doc.height - heightAtLine(lineObj) : 0);
4964 },
5069 },
4965
5070
4966 defaultTextHeight: function() { return textHeight(this.display); },
5071 defaultTextHeight: function() { return textHeight(this.display); },
4967 defaultCharWidth: function() { return charWidth(this.display); },
5072 defaultCharWidth: function() { return charWidth(this.display); },
4968
5073
4969 setGutterMarker: methodOp(function(line, gutterID, value) {
5074 setGutterMarker: methodOp(function(line, gutterID, value) {
4970 return changeLine(this.doc, line, "gutter", function(line) {
5075 return changeLine(this.doc, line, "gutter", function(line) {
4971 var markers = line.gutterMarkers || (line.gutterMarkers = {});
5076 var markers = line.gutterMarkers || (line.gutterMarkers = {});
4972 markers[gutterID] = value;
5077 markers[gutterID] = value;
4973 if (!value && isEmpty(markers)) line.gutterMarkers = null;
5078 if (!value && isEmpty(markers)) line.gutterMarkers = null;
4974 return true;
5079 return true;
4975 });
5080 });
4976 }),
5081 }),
4977
5082
4978 clearGutter: methodOp(function(gutterID) {
5083 clearGutter: methodOp(function(gutterID) {
4979 var cm = this, doc = cm.doc, i = doc.first;
5084 var cm = this, doc = cm.doc, i = doc.first;
4980 doc.iter(function(line) {
5085 doc.iter(function(line) {
4981 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5086 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
4982 line.gutterMarkers[gutterID] = null;
5087 line.gutterMarkers[gutterID] = null;
4983 regLineChange(cm, i, "gutter");
5088 regLineChange(cm, i, "gutter");
4984 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
5089 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
4985 }
5090 }
4986 ++i;
5091 ++i;
4987 });
5092 });
4988 }),
5093 }),
4989
5094
4990 lineInfo: function(line) {
5095 lineInfo: function(line) {
4991 if (typeof line == "number") {
5096 if (typeof line == "number") {
4992 if (!isLine(this.doc, line)) return null;
5097 if (!isLine(this.doc, line)) return null;
4993 var n = line;
5098 var n = line;
4994 line = getLine(this.doc, line);
5099 line = getLine(this.doc, line);
4995 if (!line) return null;
5100 if (!line) return null;
4996 } else {
5101 } else {
4997 var n = lineNo(line);
5102 var n = lineNo(line);
4998 if (n == null) return null;
5103 if (n == null) return null;
4999 }
5104 }
5000 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5105 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5001 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5106 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5002 widgets: line.widgets};
5107 widgets: line.widgets};
5003 },
5108 },
5004
5109
5005 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5110 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5006
5111
5007 addWidget: function(pos, node, scroll, vert, horiz) {
5112 addWidget: function(pos, node, scroll, vert, horiz) {
5008 var display = this.display;
5113 var display = this.display;
5009 pos = cursorCoords(this, clipPos(this.doc, pos));
5114 pos = cursorCoords(this, clipPos(this.doc, pos));
5010 var top = pos.bottom, left = pos.left;
5115 var top = pos.bottom, left = pos.left;
5011 node.style.position = "absolute";
5116 node.style.position = "absolute";
5012 node.setAttribute("cm-ignore-events", "true");
5117 node.setAttribute("cm-ignore-events", "true");
5013 this.display.input.setUneditable(node);
5118 this.display.input.setUneditable(node);
5014 display.sizer.appendChild(node);
5119 display.sizer.appendChild(node);
5015 if (vert == "over") {
5120 if (vert == "over") {
5016 top = pos.top;
5121 top = pos.top;
5017 } else if (vert == "above" || vert == "near") {
5122 } else if (vert == "above" || vert == "near") {
5018 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5123 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5019 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5124 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5020 // Default to positioning above (if specified and possible); otherwise default to positioning below
5125 // Default to positioning above (if specified and possible); otherwise default to positioning below
5021 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5126 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5022 top = pos.top - node.offsetHeight;
5127 top = pos.top - node.offsetHeight;
5023 else if (pos.bottom + node.offsetHeight <= vspace)
5128 else if (pos.bottom + node.offsetHeight <= vspace)
5024 top = pos.bottom;
5129 top = pos.bottom;
5025 if (left + node.offsetWidth > hspace)
5130 if (left + node.offsetWidth > hspace)
5026 left = hspace - node.offsetWidth;
5131 left = hspace - node.offsetWidth;
5027 }
5132 }
5028 node.style.top = top + "px";
5133 node.style.top = top + "px";
5029 node.style.left = node.style.right = "";
5134 node.style.left = node.style.right = "";
5030 if (horiz == "right") {
5135 if (horiz == "right") {
5031 left = display.sizer.clientWidth - node.offsetWidth;
5136 left = display.sizer.clientWidth - node.offsetWidth;
5032 node.style.right = "0px";
5137 node.style.right = "0px";
5033 } else {
5138 } else {
5034 if (horiz == "left") left = 0;
5139 if (horiz == "left") left = 0;
5035 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5140 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5036 node.style.left = left + "px";
5141 node.style.left = left + "px";
5037 }
5142 }
5038 if (scroll)
5143 if (scroll)
5039 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5144 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5040 },
5145 },
5041
5146
5042 triggerOnKeyDown: methodOp(onKeyDown),
5147 triggerOnKeyDown: methodOp(onKeyDown),
5043 triggerOnKeyPress: methodOp(onKeyPress),
5148 triggerOnKeyPress: methodOp(onKeyPress),
5044 triggerOnKeyUp: onKeyUp,
5149 triggerOnKeyUp: onKeyUp,
5045
5150
5046 execCommand: function(cmd) {
5151 execCommand: function(cmd) {
5047 if (commands.hasOwnProperty(cmd))
5152 if (commands.hasOwnProperty(cmd))
5048 return commands[cmd](this);
5153 return commands[cmd].call(null, this);
5049 },
5154 },
5050
5155
5051 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5156 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5052
5157
5053 findPosH: function(from, amount, unit, visually) {
5158 findPosH: function(from, amount, unit, visually) {
5054 var dir = 1;
5159 var dir = 1;
5055 if (amount < 0) { dir = -1; amount = -amount; }
5160 if (amount < 0) { dir = -1; amount = -amount; }
5056 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5161 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5057 cur = findPosH(this.doc, cur, dir, unit, visually);
5162 cur = findPosH(this.doc, cur, dir, unit, visually);
5058 if (cur.hitSide) break;
5163 if (cur.hitSide) break;
5059 }
5164 }
5060 return cur;
5165 return cur;
5061 },
5166 },
5062
5167
5063 moveH: methodOp(function(dir, unit) {
5168 moveH: methodOp(function(dir, unit) {
5064 var cm = this;
5169 var cm = this;
5065 cm.extendSelectionsBy(function(range) {
5170 cm.extendSelectionsBy(function(range) {
5066 if (cm.display.shift || cm.doc.extend || range.empty())
5171 if (cm.display.shift || cm.doc.extend || range.empty())
5067 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5172 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5068 else
5173 else
5069 return dir < 0 ? range.from() : range.to();
5174 return dir < 0 ? range.from() : range.to();
5070 }, sel_move);
5175 }, sel_move);
5071 }),
5176 }),
5072
5177
5073 deleteH: methodOp(function(dir, unit) {
5178 deleteH: methodOp(function(dir, unit) {
5074 var sel = this.doc.sel, doc = this.doc;
5179 var sel = this.doc.sel, doc = this.doc;
5075 if (sel.somethingSelected())
5180 if (sel.somethingSelected())
5076 doc.replaceSelection("", null, "+delete");
5181 doc.replaceSelection("", null, "+delete");
5077 else
5182 else
5078 deleteNearSelection(this, function(range) {
5183 deleteNearSelection(this, function(range) {
5079 var other = findPosH(doc, range.head, dir, unit, false);
5184 var other = findPosH(doc, range.head, dir, unit, false);
5080 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5185 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5081 });
5186 });
5082 }),
5187 }),
5083
5188
5084 findPosV: function(from, amount, unit, goalColumn) {
5189 findPosV: function(from, amount, unit, goalColumn) {
5085 var dir = 1, x = goalColumn;
5190 var dir = 1, x = goalColumn;
5086 if (amount < 0) { dir = -1; amount = -amount; }
5191 if (amount < 0) { dir = -1; amount = -amount; }
5087 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5192 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5088 var coords = cursorCoords(this, cur, "div");
5193 var coords = cursorCoords(this, cur, "div");
5089 if (x == null) x = coords.left;
5194 if (x == null) x = coords.left;
5090 else coords.left = x;
5195 else coords.left = x;
5091 cur = findPosV(this, coords, dir, unit);
5196 cur = findPosV(this, coords, dir, unit);
5092 if (cur.hitSide) break;
5197 if (cur.hitSide) break;
5093 }
5198 }
5094 return cur;
5199 return cur;
5095 },
5200 },
5096
5201
5097 moveV: methodOp(function(dir, unit) {
5202 moveV: methodOp(function(dir, unit) {
5098 var cm = this, doc = this.doc, goals = [];
5203 var cm = this, doc = this.doc, goals = [];
5099 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5204 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5100 doc.extendSelectionsBy(function(range) {
5205 doc.extendSelectionsBy(function(range) {
5101 if (collapse)
5206 if (collapse)
5102 return dir < 0 ? range.from() : range.to();
5207 return dir < 0 ? range.from() : range.to();
5103 var headPos = cursorCoords(cm, range.head, "div");
5208 var headPos = cursorCoords(cm, range.head, "div");
5104 if (range.goalColumn != null) headPos.left = range.goalColumn;
5209 if (range.goalColumn != null) headPos.left = range.goalColumn;
5105 goals.push(headPos.left);
5210 goals.push(headPos.left);
5106 var pos = findPosV(cm, headPos, dir, unit);
5211 var pos = findPosV(cm, headPos, dir, unit);
5107 if (unit == "page" && range == doc.sel.primary())
5212 if (unit == "page" && range == doc.sel.primary())
5108 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5213 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5109 return pos;
5214 return pos;
5110 }, sel_move);
5215 }, sel_move);
5111 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5216 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5112 doc.sel.ranges[i].goalColumn = goals[i];
5217 doc.sel.ranges[i].goalColumn = goals[i];
5113 }),
5218 }),
5114
5219
5115 // Find the word at the given position (as returned by coordsChar).
5220 // Find the word at the given position (as returned by coordsChar).
5116 findWordAt: function(pos) {
5221 findWordAt: function(pos) {
5117 var doc = this.doc, line = getLine(doc, pos.line).text;
5222 var doc = this.doc, line = getLine(doc, pos.line).text;
5118 var start = pos.ch, end = pos.ch;
5223 var start = pos.ch, end = pos.ch;
5119 if (line) {
5224 if (line) {
5120 var helper = this.getHelper(pos, "wordChars");
5225 var helper = this.getHelper(pos, "wordChars");
5121 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5226 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5122 var startChar = line.charAt(start);
5227 var startChar = line.charAt(start);
5123 var check = isWordChar(startChar, helper)
5228 var check = isWordChar(startChar, helper)
5124 ? function(ch) { return isWordChar(ch, helper); }
5229 ? function(ch) { return isWordChar(ch, helper); }
5125 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5230 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5126 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5231 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5127 while (start > 0 && check(line.charAt(start - 1))) --start;
5232 while (start > 0 && check(line.charAt(start - 1))) --start;
5128 while (end < line.length && check(line.charAt(end))) ++end;
5233 while (end < line.length && check(line.charAt(end))) ++end;
5129 }
5234 }
5130 return new Range(Pos(pos.line, start), Pos(pos.line, end));
5235 return new Range(Pos(pos.line, start), Pos(pos.line, end));
5131 },
5236 },
5132
5237
5133 toggleOverwrite: function(value) {
5238 toggleOverwrite: function(value) {
5134 if (value != null && value == this.state.overwrite) return;
5239 if (value != null && value == this.state.overwrite) return;
5135 if (this.state.overwrite = !this.state.overwrite)
5240 if (this.state.overwrite = !this.state.overwrite)
5136 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5241 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5137 else
5242 else
5138 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5243 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5139
5244
5140 signal(this, "overwriteToggle", this, this.state.overwrite);
5245 signal(this, "overwriteToggle", this, this.state.overwrite);
5141 },
5246 },
5142 hasFocus: function() { return this.display.input.getField() == activeElt(); },
5247 hasFocus: function() { return this.display.input.getField() == activeElt(); },
5248 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); },
5143
5249
5144 scrollTo: methodOp(function(x, y) {
5250 scrollTo: methodOp(function(x, y) {
5145 if (x != null || y != null) resolveScrollToPos(this);
5251 if (x != null || y != null) resolveScrollToPos(this);
5146 if (x != null) this.curOp.scrollLeft = x;
5252 if (x != null) this.curOp.scrollLeft = x;
5147 if (y != null) this.curOp.scrollTop = y;
5253 if (y != null) this.curOp.scrollTop = y;
5148 }),
5254 }),
5149 getScrollInfo: function() {
5255 getScrollInfo: function() {
5150 var scroller = this.display.scroller;
5256 var scroller = this.display.scroller;
5151 return {left: scroller.scrollLeft, top: scroller.scrollTop,
5257 return {left: scroller.scrollLeft, top: scroller.scrollTop,
5152 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5258 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5153 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5259 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5154 clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5260 clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5155 },
5261 },
5156
5262
5157 scrollIntoView: methodOp(function(range, margin) {
5263 scrollIntoView: methodOp(function(range, margin) {
5158 if (range == null) {
5264 if (range == null) {
5159 range = {from: this.doc.sel.primary().head, to: null};
5265 range = {from: this.doc.sel.primary().head, to: null};
5160 if (margin == null) margin = this.options.cursorScrollMargin;
5266 if (margin == null) margin = this.options.cursorScrollMargin;
5161 } else if (typeof range == "number") {
5267 } else if (typeof range == "number") {
5162 range = {from: Pos(range, 0), to: null};
5268 range = {from: Pos(range, 0), to: null};
5163 } else if (range.from == null) {
5269 } else if (range.from == null) {
5164 range = {from: range, to: null};
5270 range = {from: range, to: null};
5165 }
5271 }
5166 if (!range.to) range.to = range.from;
5272 if (!range.to) range.to = range.from;
5167 range.margin = margin || 0;
5273 range.margin = margin || 0;
5168
5274
5169 if (range.from.line != null) {
5275 if (range.from.line != null) {
5170 resolveScrollToPos(this);
5276 resolveScrollToPos(this);
5171 this.curOp.scrollToPos = range;
5277 this.curOp.scrollToPos = range;
5172 } else {
5278 } else {
5173 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5279 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5174 Math.min(range.from.top, range.to.top) - range.margin,
5280 Math.min(range.from.top, range.to.top) - range.margin,
5175 Math.max(range.from.right, range.to.right),
5281 Math.max(range.from.right, range.to.right),
5176 Math.max(range.from.bottom, range.to.bottom) + range.margin);
5282 Math.max(range.from.bottom, range.to.bottom) + range.margin);
5177 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5283 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5178 }
5284 }
5179 }),
5285 }),
5180
5286
5181 setSize: methodOp(function(width, height) {
5287 setSize: methodOp(function(width, height) {
5182 var cm = this;
5288 var cm = this;
5183 function interpret(val) {
5289 function interpret(val) {
5184 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5290 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5185 }
5291 }
5186 if (width != null) cm.display.wrapper.style.width = interpret(width);
5292 if (width != null) cm.display.wrapper.style.width = interpret(width);
5187 if (height != null) cm.display.wrapper.style.height = interpret(height);
5293 if (height != null) cm.display.wrapper.style.height = interpret(height);
5188 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5294 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5189 var lineNo = cm.display.viewFrom;
5295 var lineNo = cm.display.viewFrom;
5190 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5296 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5191 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5297 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5192 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5298 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5193 ++lineNo;
5299 ++lineNo;
5194 });
5300 });
5195 cm.curOp.forceUpdate = true;
5301 cm.curOp.forceUpdate = true;
5196 signal(cm, "refresh", this);
5302 signal(cm, "refresh", this);
5197 }),
5303 }),
5198
5304
5199 operation: function(f){return runInOp(this, f);},
5305 operation: function(f){return runInOp(this, f);},
5200
5306
5201 refresh: methodOp(function() {
5307 refresh: methodOp(function() {
5202 var oldHeight = this.display.cachedTextHeight;
5308 var oldHeight = this.display.cachedTextHeight;
5203 regChange(this);
5309 regChange(this);
5204 this.curOp.forceUpdate = true;
5310 this.curOp.forceUpdate = true;
5205 clearCaches(this);
5311 clearCaches(this);
5206 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5312 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5207 updateGutterSpace(this);
5313 updateGutterSpace(this);
5208 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
5314 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
5209 estimateLineHeights(this);
5315 estimateLineHeights(this);
5210 signal(this, "refresh", this);
5316 signal(this, "refresh", this);
5211 }),
5317 }),
5212
5318
5213 swapDoc: methodOp(function(doc) {
5319 swapDoc: methodOp(function(doc) {
5214 var old = this.doc;
5320 var old = this.doc;
5215 old.cm = null;
5321 old.cm = null;
5216 attachDoc(this, doc);
5322 attachDoc(this, doc);
5217 clearCaches(this);
5323 clearCaches(this);
5218 this.display.input.reset();
5324 this.display.input.reset();
5219 this.scrollTo(doc.scrollLeft, doc.scrollTop);
5325 this.scrollTo(doc.scrollLeft, doc.scrollTop);
5220 this.curOp.forceScroll = true;
5326 this.curOp.forceScroll = true;
5221 signalLater(this, "swapDoc", this, old);
5327 signalLater(this, "swapDoc", this, old);
5222 return old;
5328 return old;
5223 }),
5329 }),
5224
5330
5225 getInputField: function(){return this.display.input.getField();},
5331 getInputField: function(){return this.display.input.getField();},
5226 getWrapperElement: function(){return this.display.wrapper;},
5332 getWrapperElement: function(){return this.display.wrapper;},
5227 getScrollerElement: function(){return this.display.scroller;},
5333 getScrollerElement: function(){return this.display.scroller;},
5228 getGutterElement: function(){return this.display.gutters;}
5334 getGutterElement: function(){return this.display.gutters;}
5229 };
5335 };
5230 eventMixin(CodeMirror);
5336 eventMixin(CodeMirror);
5231
5337
5232 // OPTION DEFAULTS
5338 // OPTION DEFAULTS
5233
5339
5234 // The default configuration options.
5340 // The default configuration options.
5235 var defaults = CodeMirror.defaults = {};
5341 var defaults = CodeMirror.defaults = {};
5236 // Functions to run when options are changed.
5342 // Functions to run when options are changed.
5237 var optionHandlers = CodeMirror.optionHandlers = {};
5343 var optionHandlers = CodeMirror.optionHandlers = {};
5238
5344
5239 function option(name, deflt, handle, notOnInit) {
5345 function option(name, deflt, handle, notOnInit) {
5240 CodeMirror.defaults[name] = deflt;
5346 CodeMirror.defaults[name] = deflt;
5241 if (handle) optionHandlers[name] =
5347 if (handle) optionHandlers[name] =
5242 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5348 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5243 }
5349 }
5244
5350
5245 // Passed to option handlers when there is no old value.
5351 // Passed to option handlers when there is no old value.
5246 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5352 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5247
5353
5248 // These two are, on init, called from the constructor because they
5354 // These two are, on init, called from the constructor because they
5249 // have to be initialized before the editor can start at all.
5355 // have to be initialized before the editor can start at all.
5250 option("value", "", function(cm, val) {
5356 option("value", "", function(cm, val) {
5251 cm.setValue(val);
5357 cm.setValue(val);
5252 }, true);
5358 }, true);
5253 option("mode", null, function(cm, val) {
5359 option("mode", null, function(cm, val) {
5254 cm.doc.modeOption = val;
5360 cm.doc.modeOption = val;
5255 loadMode(cm);
5361 loadMode(cm);
5256 }, true);
5362 }, true);
5257
5363
5258 option("indentUnit", 2, loadMode, true);
5364 option("indentUnit", 2, loadMode, true);
5259 option("indentWithTabs", false);
5365 option("indentWithTabs", false);
5260 option("smartIndent", true);
5366 option("smartIndent", true);
5261 option("tabSize", 4, function(cm) {
5367 option("tabSize", 4, function(cm) {
5262 resetModeState(cm);
5368 resetModeState(cm);
5263 clearCaches(cm);
5369 clearCaches(cm);
5264 regChange(cm);
5370 regChange(cm);
5265 }, true);
5371 }, true);
5372 option("lineSeparator", null, function(cm, val) {
5373 cm.doc.lineSep = val;
5374 if (!val) return;
5375 var newBreaks = [], lineNo = cm.doc.first;
5376 cm.doc.iter(function(line) {
5377 for (var pos = 0;;) {
5378 var found = line.text.indexOf(val, pos);
5379 if (found == -1) break;
5380 pos = found + val.length;
5381 newBreaks.push(Pos(lineNo, found));
5382 }
5383 lineNo++;
5384 });
5385 for (var i = newBreaks.length - 1; i >= 0; i--)
5386 replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
5387 });
5266 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5388 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5267 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5389 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5268 if (old != CodeMirror.Init) cm.refresh();
5390 if (old != CodeMirror.Init) cm.refresh();
5269 });
5391 });
5270 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5392 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5271 option("electricChars", true);
5393 option("electricChars", true);
5272 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5394 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5273 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5395 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5274 }, true);
5396 }, true);
5275 option("rtlMoveVisually", !windows);
5397 option("rtlMoveVisually", !windows);
5276 option("wholeLineUpdateBefore", true);
5398 option("wholeLineUpdateBefore", true);
5277
5399
5278 option("theme", "default", function(cm) {
5400 option("theme", "default", function(cm) {
5279 themeChanged(cm);
5401 themeChanged(cm);
5280 guttersChanged(cm);
5402 guttersChanged(cm);
5281 }, true);
5403 }, true);
5282 option("keyMap", "default", function(cm, val, old) {
5404 option("keyMap", "default", function(cm, val, old) {
5283 var next = getKeyMap(val);
5405 var next = getKeyMap(val);
5284 var prev = old != CodeMirror.Init && getKeyMap(old);
5406 var prev = old != CodeMirror.Init && getKeyMap(old);
5285 if (prev && prev.detach) prev.detach(cm, next);
5407 if (prev && prev.detach) prev.detach(cm, next);
5286 if (next.attach) next.attach(cm, prev || null);
5408 if (next.attach) next.attach(cm, prev || null);
5287 });
5409 });
5288 option("extraKeys", null);
5410 option("extraKeys", null);
5289
5411
5290 option("lineWrapping", false, wrappingChanged, true);
5412 option("lineWrapping", false, wrappingChanged, true);
5291 option("gutters", [], function(cm) {
5413 option("gutters", [], function(cm) {
5292 setGuttersForLineNumbers(cm.options);
5414 setGuttersForLineNumbers(cm.options);
5293 guttersChanged(cm);
5415 guttersChanged(cm);
5294 }, true);
5416 }, true);
5295 option("fixedGutter", true, function(cm, val) {
5417 option("fixedGutter", true, function(cm, val) {
5296 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5418 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5297 cm.refresh();
5419 cm.refresh();
5298 }, true);
5420 }, true);
5299 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5421 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5300 option("scrollbarStyle", "native", function(cm) {
5422 option("scrollbarStyle", "native", function(cm) {
5301 initScrollbars(cm);
5423 initScrollbars(cm);
5302 updateScrollbars(cm);
5424 updateScrollbars(cm);
5303 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5425 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5304 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5426 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5305 }, true);
5427 }, true);
5306 option("lineNumbers", false, function(cm) {
5428 option("lineNumbers", false, function(cm) {
5307 setGuttersForLineNumbers(cm.options);
5429 setGuttersForLineNumbers(cm.options);
5308 guttersChanged(cm);
5430 guttersChanged(cm);
5309 }, true);
5431 }, true);
5310 option("firstLineNumber", 1, guttersChanged, true);
5432 option("firstLineNumber", 1, guttersChanged, true);
5311 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5433 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5312 option("showCursorWhenSelecting", false, updateSelection, true);
5434 option("showCursorWhenSelecting", false, updateSelection, true);
5313
5435
5314 option("resetSelectionOnContextMenu", true);
5436 option("resetSelectionOnContextMenu", true);
5315 option("lineWiseCopyCut", true);
5437 option("lineWiseCopyCut", true);
5316
5438
5317 option("readOnly", false, function(cm, val) {
5439 option("readOnly", false, function(cm, val) {
5318 if (val == "nocursor") {
5440 if (val == "nocursor") {
5319 onBlur(cm);
5441 onBlur(cm);
5320 cm.display.input.blur();
5442 cm.display.input.blur();
5321 cm.display.disabled = true;
5443 cm.display.disabled = true;
5322 } else {
5444 } else {
5323 cm.display.disabled = false;
5445 cm.display.disabled = false;
5324 if (!val) cm.display.input.reset();
5446 }
5325 }
5447 cm.display.input.readOnlyChanged(val)
5326 });
5448 });
5327 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5449 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5328 option("dragDrop", true, dragDropChanged);
5450 option("dragDrop", true, dragDropChanged);
5451 option("allowDropFileTypes", null);
5329
5452
5330 option("cursorBlinkRate", 530);
5453 option("cursorBlinkRate", 530);
5331 option("cursorScrollMargin", 0);
5454 option("cursorScrollMargin", 0);
5332 option("cursorHeight", 1, updateSelection, true);
5455 option("cursorHeight", 1, updateSelection, true);
5333 option("singleCursorHeightPerLine", true, updateSelection, true);
5456 option("singleCursorHeightPerLine", true, updateSelection, true);
5334 option("workTime", 100);
5457 option("workTime", 100);
5335 option("workDelay", 100);
5458 option("workDelay", 100);
5336 option("flattenSpans", true, resetModeState, true);
5459 option("flattenSpans", true, resetModeState, true);
5337 option("addModeClass", false, resetModeState, true);
5460 option("addModeClass", false, resetModeState, true);
5338 option("pollInterval", 100);
5461 option("pollInterval", 100);
5339 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5462 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5340 option("historyEventDelay", 1250);
5463 option("historyEventDelay", 1250);
5341 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5464 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5342 option("maxHighlightLength", 10000, resetModeState, true);
5465 option("maxHighlightLength", 10000, resetModeState, true);
5343 option("moveInputWithCursor", true, function(cm, val) {
5466 option("moveInputWithCursor", true, function(cm, val) {
5344 if (!val) cm.display.input.resetPosition();
5467 if (!val) cm.display.input.resetPosition();
5345 });
5468 });
5346
5469
5347 option("tabindex", null, function(cm, val) {
5470 option("tabindex", null, function(cm, val) {
5348 cm.display.input.getField().tabIndex = val || "";
5471 cm.display.input.getField().tabIndex = val || "";
5349 });
5472 });
5350 option("autofocus", null);
5473 option("autofocus", null);
5351
5474
5352 // MODE DEFINITION AND QUERYING
5475 // MODE DEFINITION AND QUERYING
5353
5476
5354 // Known modes, by name and by MIME
5477 // Known modes, by name and by MIME
5355 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5478 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5356
5479
5357 // Extra arguments are stored as the mode's dependencies, which is
5480 // Extra arguments are stored as the mode's dependencies, which is
5358 // used by (legacy) mechanisms like loadmode.js to automatically
5481 // used by (legacy) mechanisms like loadmode.js to automatically
5359 // load a mode. (Preferred mechanism is the require/define calls.)
5482 // load a mode. (Preferred mechanism is the require/define calls.)
5360 CodeMirror.defineMode = function(name, mode) {
5483 CodeMirror.defineMode = function(name, mode) {
5361 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5484 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5362 if (arguments.length > 2)
5485 if (arguments.length > 2)
5363 mode.dependencies = Array.prototype.slice.call(arguments, 2);
5486 mode.dependencies = Array.prototype.slice.call(arguments, 2);
5364 modes[name] = mode;
5487 modes[name] = mode;
5365 };
5488 };
5366
5489
5367 CodeMirror.defineMIME = function(mime, spec) {
5490 CodeMirror.defineMIME = function(mime, spec) {
5368 mimeModes[mime] = spec;
5491 mimeModes[mime] = spec;
5369 };
5492 };
5370
5493
5371 // Given a MIME type, a {name, ...options} config object, or a name
5494 // Given a MIME type, a {name, ...options} config object, or a name
5372 // string, return a mode config object.
5495 // string, return a mode config object.
5373 CodeMirror.resolveMode = function(spec) {
5496 CodeMirror.resolveMode = function(spec) {
5374 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5497 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5375 spec = mimeModes[spec];
5498 spec = mimeModes[spec];
5376 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5499 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5377 var found = mimeModes[spec.name];
5500 var found = mimeModes[spec.name];
5378 if (typeof found == "string") found = {name: found};
5501 if (typeof found == "string") found = {name: found};
5379 spec = createObj(found, spec);
5502 spec = createObj(found, spec);
5380 spec.name = found.name;
5503 spec.name = found.name;
5381 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5504 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5382 return CodeMirror.resolveMode("application/xml");
5505 return CodeMirror.resolveMode("application/xml");
5383 }
5506 }
5384 if (typeof spec == "string") return {name: spec};
5507 if (typeof spec == "string") return {name: spec};
5385 else return spec || {name: "null"};
5508 else return spec || {name: "null"};
5386 };
5509 };
5387
5510
5388 // Given a mode spec (anything that resolveMode accepts), find and
5511 // Given a mode spec (anything that resolveMode accepts), find and
5389 // initialize an actual mode object.
5512 // initialize an actual mode object.
5390 CodeMirror.getMode = function(options, spec) {
5513 CodeMirror.getMode = function(options, spec) {
5391 var spec = CodeMirror.resolveMode(spec);
5514 var spec = CodeMirror.resolveMode(spec);
5392 var mfactory = modes[spec.name];
5515 var mfactory = modes[spec.name];
5393 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5516 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5394 var modeObj = mfactory(options, spec);
5517 var modeObj = mfactory(options, spec);
5395 if (modeExtensions.hasOwnProperty(spec.name)) {
5518 if (modeExtensions.hasOwnProperty(spec.name)) {
5396 var exts = modeExtensions[spec.name];
5519 var exts = modeExtensions[spec.name];
5397 for (var prop in exts) {
5520 for (var prop in exts) {
5398 if (!exts.hasOwnProperty(prop)) continue;
5521 if (!exts.hasOwnProperty(prop)) continue;
5399 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5522 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5400 modeObj[prop] = exts[prop];
5523 modeObj[prop] = exts[prop];
5401 }
5524 }
5402 }
5525 }
5403 modeObj.name = spec.name;
5526 modeObj.name = spec.name;
5404 if (spec.helperType) modeObj.helperType = spec.helperType;
5527 if (spec.helperType) modeObj.helperType = spec.helperType;
5405 if (spec.modeProps) for (var prop in spec.modeProps)
5528 if (spec.modeProps) for (var prop in spec.modeProps)
5406 modeObj[prop] = spec.modeProps[prop];
5529 modeObj[prop] = spec.modeProps[prop];
5407
5530
5408 return modeObj;
5531 return modeObj;
5409 };
5532 };
5410
5533
5411 // Minimal default mode.
5534 // Minimal default mode.
5412 CodeMirror.defineMode("null", function() {
5535 CodeMirror.defineMode("null", function() {
5413 return {token: function(stream) {stream.skipToEnd();}};
5536 return {token: function(stream) {stream.skipToEnd();}};
5414 });
5537 });
5415 CodeMirror.defineMIME("text/plain", "null");
5538 CodeMirror.defineMIME("text/plain", "null");
5416
5539
5417 // This can be used to attach properties to mode objects from
5540 // This can be used to attach properties to mode objects from
5418 // outside the actual mode definition.
5541 // outside the actual mode definition.
5419 var modeExtensions = CodeMirror.modeExtensions = {};
5542 var modeExtensions = CodeMirror.modeExtensions = {};
5420 CodeMirror.extendMode = function(mode, properties) {
5543 CodeMirror.extendMode = function(mode, properties) {
5421 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5544 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5422 copyObj(properties, exts);
5545 copyObj(properties, exts);
5423 };
5546 };
5424
5547
5425 // EXTENSIONS
5548 // EXTENSIONS
5426
5549
5427 CodeMirror.defineExtension = function(name, func) {
5550 CodeMirror.defineExtension = function(name, func) {
5428 CodeMirror.prototype[name] = func;
5551 CodeMirror.prototype[name] = func;
5429 };
5552 };
5430 CodeMirror.defineDocExtension = function(name, func) {
5553 CodeMirror.defineDocExtension = function(name, func) {
5431 Doc.prototype[name] = func;
5554 Doc.prototype[name] = func;
5432 };
5555 };
5433 CodeMirror.defineOption = option;
5556 CodeMirror.defineOption = option;
5434
5557
5435 var initHooks = [];
5558 var initHooks = [];
5436 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5559 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5437
5560
5438 var helpers = CodeMirror.helpers = {};
5561 var helpers = CodeMirror.helpers = {};
5439 CodeMirror.registerHelper = function(type, name, value) {
5562 CodeMirror.registerHelper = function(type, name, value) {
5440 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5563 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5441 helpers[type][name] = value;
5564 helpers[type][name] = value;
5442 };
5565 };
5443 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5566 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5444 CodeMirror.registerHelper(type, name, value);
5567 CodeMirror.registerHelper(type, name, value);
5445 helpers[type]._global.push({pred: predicate, val: value});
5568 helpers[type]._global.push({pred: predicate, val: value});
5446 };
5569 };
5447
5570
5448 // MODE STATE HANDLING
5571 // MODE STATE HANDLING
5449
5572
5450 // Utility functions for working with state. Exported because nested
5573 // Utility functions for working with state. Exported because nested
5451 // modes need to do this for their inner modes.
5574 // modes need to do this for their inner modes.
5452
5575
5453 var copyState = CodeMirror.copyState = function(mode, state) {
5576 var copyState = CodeMirror.copyState = function(mode, state) {
5454 if (state === true) return state;
5577 if (state === true) return state;
5455 if (mode.copyState) return mode.copyState(state);
5578 if (mode.copyState) return mode.copyState(state);
5456 var nstate = {};
5579 var nstate = {};
5457 for (var n in state) {
5580 for (var n in state) {
5458 var val = state[n];
5581 var val = state[n];
5459 if (val instanceof Array) val = val.concat([]);
5582 if (val instanceof Array) val = val.concat([]);
5460 nstate[n] = val;
5583 nstate[n] = val;
5461 }
5584 }
5462 return nstate;
5585 return nstate;
5463 };
5586 };
5464
5587
5465 var startState = CodeMirror.startState = function(mode, a1, a2) {
5588 var startState = CodeMirror.startState = function(mode, a1, a2) {
5466 return mode.startState ? mode.startState(a1, a2) : true;
5589 return mode.startState ? mode.startState(a1, a2) : true;
5467 };
5590 };
5468
5591
5469 // Given a mode and a state (for that mode), find the inner mode and
5592 // Given a mode and a state (for that mode), find the inner mode and
5470 // state at the position that the state refers to.
5593 // state at the position that the state refers to.
5471 CodeMirror.innerMode = function(mode, state) {
5594 CodeMirror.innerMode = function(mode, state) {
5472 while (mode.innerMode) {
5595 while (mode.innerMode) {
5473 var info = mode.innerMode(state);
5596 var info = mode.innerMode(state);
5474 if (!info || info.mode == mode) break;
5597 if (!info || info.mode == mode) break;
5475 state = info.state;
5598 state = info.state;
5476 mode = info.mode;
5599 mode = info.mode;
5477 }
5600 }
5478 return info || {mode: mode, state: state};
5601 return info || {mode: mode, state: state};
5479 };
5602 };
5480
5603
5481 // STANDARD COMMANDS
5604 // STANDARD COMMANDS
5482
5605
5483 // Commands are parameter-less actions that can be performed on an
5606 // Commands are parameter-less actions that can be performed on an
5484 // editor, mostly used for keybindings.
5607 // editor, mostly used for keybindings.
5485 var commands = CodeMirror.commands = {
5608 var commands = CodeMirror.commands = {
5486 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5609 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5487 singleSelection: function(cm) {
5610 singleSelection: function(cm) {
5488 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5611 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5489 },
5612 },
5490 killLine: function(cm) {
5613 killLine: function(cm) {
5491 deleteNearSelection(cm, function(range) {
5614 deleteNearSelection(cm, function(range) {
5492 if (range.empty()) {
5615 if (range.empty()) {
5493 var len = getLine(cm.doc, range.head.line).text.length;
5616 var len = getLine(cm.doc, range.head.line).text.length;
5494 if (range.head.ch == len && range.head.line < cm.lastLine())
5617 if (range.head.ch == len && range.head.line < cm.lastLine())
5495 return {from: range.head, to: Pos(range.head.line + 1, 0)};
5618 return {from: range.head, to: Pos(range.head.line + 1, 0)};
5496 else
5619 else
5497 return {from: range.head, to: Pos(range.head.line, len)};
5620 return {from: range.head, to: Pos(range.head.line, len)};
5498 } else {
5621 } else {
5499 return {from: range.from(), to: range.to()};
5622 return {from: range.from(), to: range.to()};
5500 }
5623 }
5501 });
5624 });
5502 },
5625 },
5503 deleteLine: function(cm) {
5626 deleteLine: function(cm) {
5504 deleteNearSelection(cm, function(range) {
5627 deleteNearSelection(cm, function(range) {
5505 return {from: Pos(range.from().line, 0),
5628 return {from: Pos(range.from().line, 0),
5506 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5629 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5507 });
5630 });
5508 },
5631 },
5509 delLineLeft: function(cm) {
5632 delLineLeft: function(cm) {
5510 deleteNearSelection(cm, function(range) {
5633 deleteNearSelection(cm, function(range) {
5511 return {from: Pos(range.from().line, 0), to: range.from()};
5634 return {from: Pos(range.from().line, 0), to: range.from()};
5512 });
5635 });
5513 },
5636 },
5514 delWrappedLineLeft: function(cm) {
5637 delWrappedLineLeft: function(cm) {
5515 deleteNearSelection(cm, function(range) {
5638 deleteNearSelection(cm, function(range) {
5516 var top = cm.charCoords(range.head, "div").top + 5;
5639 var top = cm.charCoords(range.head, "div").top + 5;
5517 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5640 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5518 return {from: leftPos, to: range.from()};
5641 return {from: leftPos, to: range.from()};
5519 });
5642 });
5520 },
5643 },
5521 delWrappedLineRight: function(cm) {
5644 delWrappedLineRight: function(cm) {
5522 deleteNearSelection(cm, function(range) {
5645 deleteNearSelection(cm, function(range) {
5523 var top = cm.charCoords(range.head, "div").top + 5;
5646 var top = cm.charCoords(range.head, "div").top + 5;
5524 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5647 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5525 return {from: range.from(), to: rightPos };
5648 return {from: range.from(), to: rightPos };
5526 });
5649 });
5527 },
5650 },
5528 undo: function(cm) {cm.undo();},
5651 undo: function(cm) {cm.undo();},
5529 redo: function(cm) {cm.redo();},
5652 redo: function(cm) {cm.redo();},
5530 undoSelection: function(cm) {cm.undoSelection();},
5653 undoSelection: function(cm) {cm.undoSelection();},
5531 redoSelection: function(cm) {cm.redoSelection();},
5654 redoSelection: function(cm) {cm.redoSelection();},
5532 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5655 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5533 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5656 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5534 goLineStart: function(cm) {
5657 goLineStart: function(cm) {
5535 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5658 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5536 {origin: "+move", bias: 1});
5659 {origin: "+move", bias: 1});
5537 },
5660 },
5538 goLineStartSmart: function(cm) {
5661 goLineStartSmart: function(cm) {
5539 cm.extendSelectionsBy(function(range) {
5662 cm.extendSelectionsBy(function(range) {
5540 return lineStartSmart(cm, range.head);
5663 return lineStartSmart(cm, range.head);
5541 }, {origin: "+move", bias: 1});
5664 }, {origin: "+move", bias: 1});
5542 },
5665 },
5543 goLineEnd: function(cm) {
5666 goLineEnd: function(cm) {
5544 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5667 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5545 {origin: "+move", bias: -1});
5668 {origin: "+move", bias: -1});
5546 },
5669 },
5547 goLineRight: function(cm) {
5670 goLineRight: function(cm) {
5548 cm.extendSelectionsBy(function(range) {
5671 cm.extendSelectionsBy(function(range) {
5549 var top = cm.charCoords(range.head, "div").top + 5;
5672 var top = cm.charCoords(range.head, "div").top + 5;
5550 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5673 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5551 }, sel_move);
5674 }, sel_move);
5552 },
5675 },
5553 goLineLeft: function(cm) {
5676 goLineLeft: function(cm) {
5554 cm.extendSelectionsBy(function(range) {
5677 cm.extendSelectionsBy(function(range) {
5555 var top = cm.charCoords(range.head, "div").top + 5;
5678 var top = cm.charCoords(range.head, "div").top + 5;
5556 return cm.coordsChar({left: 0, top: top}, "div");
5679 return cm.coordsChar({left: 0, top: top}, "div");
5557 }, sel_move);
5680 }, sel_move);
5558 },
5681 },
5559 goLineLeftSmart: function(cm) {
5682 goLineLeftSmart: function(cm) {
5560 cm.extendSelectionsBy(function(range) {
5683 cm.extendSelectionsBy(function(range) {
5561 var top = cm.charCoords(range.head, "div").top + 5;
5684 var top = cm.charCoords(range.head, "div").top + 5;
5562 var pos = cm.coordsChar({left: 0, top: top}, "div");
5685 var pos = cm.coordsChar({left: 0, top: top}, "div");
5563 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5686 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5564 return pos;
5687 return pos;
5565 }, sel_move);
5688 }, sel_move);
5566 },
5689 },
5567 goLineUp: function(cm) {cm.moveV(-1, "line");},
5690 goLineUp: function(cm) {cm.moveV(-1, "line");},
5568 goLineDown: function(cm) {cm.moveV(1, "line");},
5691 goLineDown: function(cm) {cm.moveV(1, "line");},
5569 goPageUp: function(cm) {cm.moveV(-1, "page");},
5692 goPageUp: function(cm) {cm.moveV(-1, "page");},
5570 goPageDown: function(cm) {cm.moveV(1, "page");},
5693 goPageDown: function(cm) {cm.moveV(1, "page");},
5571 goCharLeft: function(cm) {cm.moveH(-1, "char");},
5694 goCharLeft: function(cm) {cm.moveH(-1, "char");},
5572 goCharRight: function(cm) {cm.moveH(1, "char");},
5695 goCharRight: function(cm) {cm.moveH(1, "char");},
5573 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5696 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5574 goColumnRight: function(cm) {cm.moveH(1, "column");},
5697 goColumnRight: function(cm) {cm.moveH(1, "column");},
5575 goWordLeft: function(cm) {cm.moveH(-1, "word");},
5698 goWordLeft: function(cm) {cm.moveH(-1, "word");},
5576 goGroupRight: function(cm) {cm.moveH(1, "group");},
5699 goGroupRight: function(cm) {cm.moveH(1, "group");},
5577 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5700 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5578 goWordRight: function(cm) {cm.moveH(1, "word");},
5701 goWordRight: function(cm) {cm.moveH(1, "word");},
5579 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5702 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5580 delCharAfter: function(cm) {cm.deleteH(1, "char");},
5703 delCharAfter: function(cm) {cm.deleteH(1, "char");},
5581 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5704 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5582 delWordAfter: function(cm) {cm.deleteH(1, "word");},
5705 delWordAfter: function(cm) {cm.deleteH(1, "word");},
5583 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5706 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5584 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5707 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5585 indentAuto: function(cm) {cm.indentSelection("smart");},
5708 indentAuto: function(cm) {cm.indentSelection("smart");},
5586 indentMore: function(cm) {cm.indentSelection("add");},
5709 indentMore: function(cm) {cm.indentSelection("add");},
5587 indentLess: function(cm) {cm.indentSelection("subtract");},
5710 indentLess: function(cm) {cm.indentSelection("subtract");},
5588 insertTab: function(cm) {cm.replaceSelection("\t");},
5711 insertTab: function(cm) {cm.replaceSelection("\t");},
5589 insertSoftTab: function(cm) {
5712 insertSoftTab: function(cm) {
5590 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5713 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5591 for (var i = 0; i < ranges.length; i++) {
5714 for (var i = 0; i < ranges.length; i++) {
5592 var pos = ranges[i].from();
5715 var pos = ranges[i].from();
5593 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5716 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5594 spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5717 spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5595 }
5718 }
5596 cm.replaceSelections(spaces);
5719 cm.replaceSelections(spaces);
5597 },
5720 },
5598 defaultTab: function(cm) {
5721 defaultTab: function(cm) {
5599 if (cm.somethingSelected()) cm.indentSelection("add");
5722 if (cm.somethingSelected()) cm.indentSelection("add");
5600 else cm.execCommand("insertTab");
5723 else cm.execCommand("insertTab");
5601 },
5724 },
5602 transposeChars: function(cm) {
5725 transposeChars: function(cm) {
5603 runInOp(cm, function() {
5726 runInOp(cm, function() {
5604 var ranges = cm.listSelections(), newSel = [];
5727 var ranges = cm.listSelections(), newSel = [];
5605 for (var i = 0; i < ranges.length; i++) {
5728 for (var i = 0; i < ranges.length; i++) {
5606 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5729 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5607 if (line) {
5730 if (line) {
5608 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5731 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5609 if (cur.ch > 0) {
5732 if (cur.ch > 0) {
5610 cur = new Pos(cur.line, cur.ch + 1);
5733 cur = new Pos(cur.line, cur.ch + 1);
5611 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5734 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5612 Pos(cur.line, cur.ch - 2), cur, "+transpose");
5735 Pos(cur.line, cur.ch - 2), cur, "+transpose");
5613 } else if (cur.line > cm.doc.first) {
5736 } else if (cur.line > cm.doc.first) {
5614 var prev = getLine(cm.doc, cur.line - 1).text;
5737 var prev = getLine(cm.doc, cur.line - 1).text;
5615 if (prev)
5738 if (prev)
5616 cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1),
5739 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
5740 prev.charAt(prev.length - 1),
5617 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5741 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5618 }
5742 }
5619 }
5743 }
5620 newSel.push(new Range(cur, cur));
5744 newSel.push(new Range(cur, cur));
5621 }
5745 }
5622 cm.setSelections(newSel);
5746 cm.setSelections(newSel);
5623 });
5747 });
5624 },
5748 },
5625 newlineAndIndent: function(cm) {
5749 newlineAndIndent: function(cm) {
5626 runInOp(cm, function() {
5750 runInOp(cm, function() {
5627 var len = cm.listSelections().length;
5751 var len = cm.listSelections().length;
5628 for (var i = 0; i < len; i++) {
5752 for (var i = 0; i < len; i++) {
5629 var range = cm.listSelections()[i];
5753 var range = cm.listSelections()[i];
5630 cm.replaceRange("\n", range.anchor, range.head, "+input");
5754 cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
5631 cm.indentLine(range.from().line + 1, null, true);
5755 cm.indentLine(range.from().line + 1, null, true);
5632 ensureCursorVisible(cm);
5633 }
5756 }
5757 ensureCursorVisible(cm);
5634 });
5758 });
5635 },
5759 },
5636 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5760 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5637 };
5761 };
5638
5762
5639
5763
5640 // STANDARD KEYMAPS
5764 // STANDARD KEYMAPS
5641
5765
5642 var keyMap = CodeMirror.keyMap = {};
5766 var keyMap = CodeMirror.keyMap = {};
5643
5767
5644 keyMap.basic = {
5768 keyMap.basic = {
5645 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5769 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5646 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5770 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5647 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5771 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5648 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5772 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5649 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5773 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5650 "Esc": "singleSelection"
5774 "Esc": "singleSelection"
5651 };
5775 };
5652 // Note that the save and find-related commands aren't defined by
5776 // Note that the save and find-related commands aren't defined by
5653 // default. User code or addons can define them. Unknown commands
5777 // default. User code or addons can define them. Unknown commands
5654 // are simply ignored.
5778 // are simply ignored.
5655 keyMap.pcDefault = {
5779 keyMap.pcDefault = {
5656 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5780 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5657 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5781 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5658 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5782 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5659 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5783 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5660 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5784 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5661 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5785 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5662 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5786 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5663 fallthrough: "basic"
5787 fallthrough: "basic"
5664 };
5788 };
5665 // Very basic readline/emacs-style bindings, which are standard on Mac.
5789 // Very basic readline/emacs-style bindings, which are standard on Mac.
5666 keyMap.emacsy = {
5790 keyMap.emacsy = {
5667 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5791 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5668 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5792 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5669 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5793 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5670 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5794 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5671 };
5795 };
5672 keyMap.macDefault = {
5796 keyMap.macDefault = {
5673 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5797 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5674 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5798 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5675 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5799 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5676 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5800 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5677 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5801 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5678 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5802 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5679 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5803 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5680 fallthrough: ["basic", "emacsy"]
5804 fallthrough: ["basic", "emacsy"]
5681 };
5805 };
5682 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5806 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5683
5807
5684 // KEYMAP DISPATCH
5808 // KEYMAP DISPATCH
5685
5809
5686 function normalizeKeyName(name) {
5810 function normalizeKeyName(name) {
5687 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5811 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5688 var alt, ctrl, shift, cmd;
5812 var alt, ctrl, shift, cmd;
5689 for (var i = 0; i < parts.length - 1; i++) {
5813 for (var i = 0; i < parts.length - 1; i++) {
5690 var mod = parts[i];
5814 var mod = parts[i];
5691 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5815 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5692 else if (/^a(lt)?$/i.test(mod)) alt = true;
5816 else if (/^a(lt)?$/i.test(mod)) alt = true;
5693 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5817 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5694 else if (/^s(hift)$/i.test(mod)) shift = true;
5818 else if (/^s(hift)$/i.test(mod)) shift = true;
5695 else throw new Error("Unrecognized modifier name: " + mod);
5819 else throw new Error("Unrecognized modifier name: " + mod);
5696 }
5820 }
5697 if (alt) name = "Alt-" + name;
5821 if (alt) name = "Alt-" + name;
5698 if (ctrl) name = "Ctrl-" + name;
5822 if (ctrl) name = "Ctrl-" + name;
5699 if (cmd) name = "Cmd-" + name;
5823 if (cmd) name = "Cmd-" + name;
5700 if (shift) name = "Shift-" + name;
5824 if (shift) name = "Shift-" + name;
5701 return name;
5825 return name;
5702 }
5826 }
5703
5827
5704 // This is a kludge to keep keymaps mostly working as raw objects
5828 // This is a kludge to keep keymaps mostly working as raw objects
5705 // (backwards compatibility) while at the same time support features
5829 // (backwards compatibility) while at the same time support features
5706 // like normalization and multi-stroke key bindings. It compiles a
5830 // like normalization and multi-stroke key bindings. It compiles a
5707 // new normalized keymap, and then updates the old object to reflect
5831 // new normalized keymap, and then updates the old object to reflect
5708 // this.
5832 // this.
5709 CodeMirror.normalizeKeyMap = function(keymap) {
5833 CodeMirror.normalizeKeyMap = function(keymap) {
5710 var copy = {};
5834 var copy = {};
5711 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5835 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5712 var value = keymap[keyname];
5836 var value = keymap[keyname];
5713 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5837 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5714 if (value == "...") { delete keymap[keyname]; continue; }
5838 if (value == "...") { delete keymap[keyname]; continue; }
5715
5839
5716 var keys = map(keyname.split(" "), normalizeKeyName);
5840 var keys = map(keyname.split(" "), normalizeKeyName);
5717 for (var i = 0; i < keys.length; i++) {
5841 for (var i = 0; i < keys.length; i++) {
5718 var val, name;
5842 var val, name;
5719 if (i == keys.length - 1) {
5843 if (i == keys.length - 1) {
5720 name = keys.join(" ");
5844 name = keys.join(" ");
5721 val = value;
5845 val = value;
5722 } else {
5846 } else {
5723 name = keys.slice(0, i + 1).join(" ");
5847 name = keys.slice(0, i + 1).join(" ");
5724 val = "...";
5848 val = "...";
5725 }
5849 }
5726 var prev = copy[name];
5850 var prev = copy[name];
5727 if (!prev) copy[name] = val;
5851 if (!prev) copy[name] = val;
5728 else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5852 else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5729 }
5853 }
5730 delete keymap[keyname];
5854 delete keymap[keyname];
5731 }
5855 }
5732 for (var prop in copy) keymap[prop] = copy[prop];
5856 for (var prop in copy) keymap[prop] = copy[prop];
5733 return keymap;
5857 return keymap;
5734 };
5858 };
5735
5859
5736 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5860 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5737 map = getKeyMap(map);
5861 map = getKeyMap(map);
5738 var found = map.call ? map.call(key, context) : map[key];
5862 var found = map.call ? map.call(key, context) : map[key];
5739 if (found === false) return "nothing";
5863 if (found === false) return "nothing";
5740 if (found === "...") return "multi";
5864 if (found === "...") return "multi";
5741 if (found != null && handle(found)) return "handled";
5865 if (found != null && handle(found)) return "handled";
5742
5866
5743 if (map.fallthrough) {
5867 if (map.fallthrough) {
5744 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5868 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5745 return lookupKey(key, map.fallthrough, handle, context);
5869 return lookupKey(key, map.fallthrough, handle, context);
5746 for (var i = 0; i < map.fallthrough.length; i++) {
5870 for (var i = 0; i < map.fallthrough.length; i++) {
5747 var result = lookupKey(key, map.fallthrough[i], handle, context);
5871 var result = lookupKey(key, map.fallthrough[i], handle, context);
5748 if (result) return result;
5872 if (result) return result;
5749 }
5873 }
5750 }
5874 }
5751 };
5875 };
5752
5876
5753 // Modifier key presses don't count as 'real' key presses for the
5877 // Modifier key presses don't count as 'real' key presses for the
5754 // purpose of keymap fallthrough.
5878 // purpose of keymap fallthrough.
5755 var isModifierKey = CodeMirror.isModifierKey = function(value) {
5879 var isModifierKey = CodeMirror.isModifierKey = function(value) {
5756 var name = typeof value == "string" ? value : keyNames[value.keyCode];
5880 var name = typeof value == "string" ? value : keyNames[value.keyCode];
5757 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5881 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5758 };
5882 };
5759
5883
5760 // Look up the name of a key as indicated by an event object.
5884 // Look up the name of a key as indicated by an event object.
5761 var keyName = CodeMirror.keyName = function(event, noShift) {
5885 var keyName = CodeMirror.keyName = function(event, noShift) {
5762 if (presto && event.keyCode == 34 && event["char"]) return false;
5886 if (presto && event.keyCode == 34 && event["char"]) return false;
5763 var base = keyNames[event.keyCode], name = base;
5887 var base = keyNames[event.keyCode], name = base;
5764 if (name == null || event.altGraphKey) return false;
5888 if (name == null || event.altGraphKey) return false;
5765 if (event.altKey && base != "Alt") name = "Alt-" + name;
5889 if (event.altKey && base != "Alt") name = "Alt-" + name;
5766 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5890 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5767 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5891 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5768 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5892 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5769 return name;
5893 return name;
5770 };
5894 };
5771
5895
5772 function getKeyMap(val) {
5896 function getKeyMap(val) {
5773 return typeof val == "string" ? keyMap[val] : val;
5897 return typeof val == "string" ? keyMap[val] : val;
5774 }
5898 }
5775
5899
5776 // FROMTEXTAREA
5900 // FROMTEXTAREA
5777
5901
5778 CodeMirror.fromTextArea = function(textarea, options) {
5902 CodeMirror.fromTextArea = function(textarea, options) {
5779 options = options ? copyObj(options) : {};
5903 options = options ? copyObj(options) : {};
5780 options.value = textarea.value;
5904 options.value = textarea.value;
5781 if (!options.tabindex && textarea.tabIndex)
5905 if (!options.tabindex && textarea.tabIndex)
5782 options.tabindex = textarea.tabIndex;
5906 options.tabindex = textarea.tabIndex;
5783 if (!options.placeholder && textarea.placeholder)
5907 if (!options.placeholder && textarea.placeholder)
5784 options.placeholder = textarea.placeholder;
5908 options.placeholder = textarea.placeholder;
5785 // Set autofocus to true if this textarea is focused, or if it has
5909 // Set autofocus to true if this textarea is focused, or if it has
5786 // autofocus and no other element is focused.
5910 // autofocus and no other element is focused.
5787 if (options.autofocus == null) {
5911 if (options.autofocus == null) {
5788 var hasFocus = activeElt();
5912 var hasFocus = activeElt();
5789 options.autofocus = hasFocus == textarea ||
5913 options.autofocus = hasFocus == textarea ||
5790 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5914 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5791 }
5915 }
5792
5916
5793 function save() {textarea.value = cm.getValue();}
5917 function save() {textarea.value = cm.getValue();}
5794 if (textarea.form) {
5918 if (textarea.form) {
5795 on(textarea.form, "submit", save);
5919 on(textarea.form, "submit", save);
5796 // Deplorable hack to make the submit method do the right thing.
5920 // Deplorable hack to make the submit method do the right thing.
5797 if (!options.leaveSubmitMethodAlone) {
5921 if (!options.leaveSubmitMethodAlone) {
5798 var form = textarea.form, realSubmit = form.submit;
5922 var form = textarea.form, realSubmit = form.submit;
5799 try {
5923 try {
5800 var wrappedSubmit = form.submit = function() {
5924 var wrappedSubmit = form.submit = function() {
5801 save();
5925 save();
5802 form.submit = realSubmit;
5926 form.submit = realSubmit;
5803 form.submit();
5927 form.submit();
5804 form.submit = wrappedSubmit;
5928 form.submit = wrappedSubmit;
5805 };
5929 };
5806 } catch(e) {}
5930 } catch(e) {}
5807 }
5931 }
5808 }
5932 }
5809
5933
5810 options.finishInit = function(cm) {
5934 options.finishInit = function(cm) {
5811 cm.save = save;
5935 cm.save = save;
5812 cm.getTextArea = function() { return textarea; };
5936 cm.getTextArea = function() { return textarea; };
5813 cm.toTextArea = function() {
5937 cm.toTextArea = function() {
5814 cm.toTextArea = isNaN; // Prevent this from being ran twice
5938 cm.toTextArea = isNaN; // Prevent this from being ran twice
5815 save();
5939 save();
5816 textarea.parentNode.removeChild(cm.getWrapperElement());
5940 textarea.parentNode.removeChild(cm.getWrapperElement());
5817 textarea.style.display = "";
5941 textarea.style.display = "";
5818 if (textarea.form) {
5942 if (textarea.form) {
5819 off(textarea.form, "submit", save);
5943 off(textarea.form, "submit", save);
5820 if (typeof textarea.form.submit == "function")
5944 if (typeof textarea.form.submit == "function")
5821 textarea.form.submit = realSubmit;
5945 textarea.form.submit = realSubmit;
5822 }
5946 }
5823 };
5947 };
5824 };
5948 };
5825
5949
5826 textarea.style.display = "none";
5950 textarea.style.display = "none";
5827 var cm = CodeMirror(function(node) {
5951 var cm = CodeMirror(function(node) {
5828 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5952 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5829 }, options);
5953 }, options);
5830 return cm;
5954 return cm;
5831 };
5955 };
5832
5956
5833 // STRING STREAM
5957 // STRING STREAM
5834
5958
5835 // Fed to the mode parsers, provides helper functions to make
5959 // Fed to the mode parsers, provides helper functions to make
5836 // parsers more succinct.
5960 // parsers more succinct.
5837
5961
5838 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5962 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5839 this.pos = this.start = 0;
5963 this.pos = this.start = 0;
5840 this.string = string;
5964 this.string = string;
5841 this.tabSize = tabSize || 8;
5965 this.tabSize = tabSize || 8;
5842 this.lastColumnPos = this.lastColumnValue = 0;
5966 this.lastColumnPos = this.lastColumnValue = 0;
5843 this.lineStart = 0;
5967 this.lineStart = 0;
5844 };
5968 };
5845
5969
5846 StringStream.prototype = {
5970 StringStream.prototype = {
5847 eol: function() {return this.pos >= this.string.length;},
5971 eol: function() {return this.pos >= this.string.length;},
5848 sol: function() {return this.pos == this.lineStart;},
5972 sol: function() {return this.pos == this.lineStart;},
5849 peek: function() {return this.string.charAt(this.pos) || undefined;},
5973 peek: function() {return this.string.charAt(this.pos) || undefined;},
5850 next: function() {
5974 next: function() {
5851 if (this.pos < this.string.length)
5975 if (this.pos < this.string.length)
5852 return this.string.charAt(this.pos++);
5976 return this.string.charAt(this.pos++);
5853 },
5977 },
5854 eat: function(match) {
5978 eat: function(match) {
5855 var ch = this.string.charAt(this.pos);
5979 var ch = this.string.charAt(this.pos);
5856 if (typeof match == "string") var ok = ch == match;
5980 if (typeof match == "string") var ok = ch == match;
5857 else var ok = ch && (match.test ? match.test(ch) : match(ch));
5981 else var ok = ch && (match.test ? match.test(ch) : match(ch));
5858 if (ok) {++this.pos; return ch;}
5982 if (ok) {++this.pos; return ch;}
5859 },
5983 },
5860 eatWhile: function(match) {
5984 eatWhile: function(match) {
5861 var start = this.pos;
5985 var start = this.pos;
5862 while (this.eat(match)){}
5986 while (this.eat(match)){}
5863 return this.pos > start;
5987 return this.pos > start;
5864 },
5988 },
5865 eatSpace: function() {
5989 eatSpace: function() {
5866 var start = this.pos;
5990 var start = this.pos;
5867 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5991 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5868 return this.pos > start;
5992 return this.pos > start;
5869 },
5993 },
5870 skipToEnd: function() {this.pos = this.string.length;},
5994 skipToEnd: function() {this.pos = this.string.length;},
5871 skipTo: function(ch) {
5995 skipTo: function(ch) {
5872 var found = this.string.indexOf(ch, this.pos);
5996 var found = this.string.indexOf(ch, this.pos);
5873 if (found > -1) {this.pos = found; return true;}
5997 if (found > -1) {this.pos = found; return true;}
5874 },
5998 },
5875 backUp: function(n) {this.pos -= n;},
5999 backUp: function(n) {this.pos -= n;},
5876 column: function() {
6000 column: function() {
5877 if (this.lastColumnPos < this.start) {
6001 if (this.lastColumnPos < this.start) {
5878 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
6002 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
5879 this.lastColumnPos = this.start;
6003 this.lastColumnPos = this.start;
5880 }
6004 }
5881 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6005 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5882 },
6006 },
5883 indentation: function() {
6007 indentation: function() {
5884 return countColumn(this.string, null, this.tabSize) -
6008 return countColumn(this.string, null, this.tabSize) -
5885 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6009 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
5886 },
6010 },
5887 match: function(pattern, consume, caseInsensitive) {
6011 match: function(pattern, consume, caseInsensitive) {
5888 if (typeof pattern == "string") {
6012 if (typeof pattern == "string") {
5889 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
6013 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
5890 var substr = this.string.substr(this.pos, pattern.length);
6014 var substr = this.string.substr(this.pos, pattern.length);
5891 if (cased(substr) == cased(pattern)) {
6015 if (cased(substr) == cased(pattern)) {
5892 if (consume !== false) this.pos += pattern.length;
6016 if (consume !== false) this.pos += pattern.length;
5893 return true;
6017 return true;
5894 }
6018 }
5895 } else {
6019 } else {
5896 var match = this.string.slice(this.pos).match(pattern);
6020 var match = this.string.slice(this.pos).match(pattern);
5897 if (match && match.index > 0) return null;
6021 if (match && match.index > 0) return null;
5898 if (match && consume !== false) this.pos += match[0].length;
6022 if (match && consume !== false) this.pos += match[0].length;
5899 return match;
6023 return match;
5900 }
6024 }
5901 },
6025 },
5902 current: function(){return this.string.slice(this.start, this.pos);},
6026 current: function(){return this.string.slice(this.start, this.pos);},
5903 hideFirstChars: function(n, inner) {
6027 hideFirstChars: function(n, inner) {
5904 this.lineStart += n;
6028 this.lineStart += n;
5905 try { return inner(); }
6029 try { return inner(); }
5906 finally { this.lineStart -= n; }
6030 finally { this.lineStart -= n; }
5907 }
6031 }
5908 };
6032 };
5909
6033
5910 // TEXTMARKERS
6034 // TEXTMARKERS
5911
6035
5912 // Created with markText and setBookmark methods. A TextMarker is a
6036 // Created with markText and setBookmark methods. A TextMarker is a
5913 // handle that can be used to clear or find a marked position in the
6037 // handle that can be used to clear or find a marked position in the
5914 // document. Line objects hold arrays (markedSpans) containing
6038 // document. Line objects hold arrays (markedSpans) containing
5915 // {from, to, marker} object pointing to such marker objects, and
6039 // {from, to, marker} object pointing to such marker objects, and
5916 // indicating that such a marker is present on that line. Multiple
6040 // indicating that such a marker is present on that line. Multiple
5917 // lines may point to the same marker when it spans across lines.
6041 // lines may point to the same marker when it spans across lines.
5918 // The spans will have null for their from/to properties when the
6042 // The spans will have null for their from/to properties when the
5919 // marker continues beyond the start/end of the line. Markers have
6043 // marker continues beyond the start/end of the line. Markers have
5920 // links back to the lines they currently touch.
6044 // links back to the lines they currently touch.
5921
6045
5922 var nextMarkerId = 0;
6046 var nextMarkerId = 0;
5923
6047
5924 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
6048 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
5925 this.lines = [];
6049 this.lines = [];
5926 this.type = type;
6050 this.type = type;
5927 this.doc = doc;
6051 this.doc = doc;
5928 this.id = ++nextMarkerId;
6052 this.id = ++nextMarkerId;
5929 };
6053 };
5930 eventMixin(TextMarker);
6054 eventMixin(TextMarker);
5931
6055
5932 // Clear the marker.
6056 // Clear the marker.
5933 TextMarker.prototype.clear = function() {
6057 TextMarker.prototype.clear = function() {
5934 if (this.explicitlyCleared) return;
6058 if (this.explicitlyCleared) return;
5935 var cm = this.doc.cm, withOp = cm && !cm.curOp;
6059 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5936 if (withOp) startOperation(cm);
6060 if (withOp) startOperation(cm);
5937 if (hasHandler(this, "clear")) {
6061 if (hasHandler(this, "clear")) {
5938 var found = this.find();
6062 var found = this.find();
5939 if (found) signalLater(this, "clear", found.from, found.to);
6063 if (found) signalLater(this, "clear", found.from, found.to);
5940 }
6064 }
5941 var min = null, max = null;
6065 var min = null, max = null;
5942 for (var i = 0; i < this.lines.length; ++i) {
6066 for (var i = 0; i < this.lines.length; ++i) {
5943 var line = this.lines[i];
6067 var line = this.lines[i];
5944 var span = getMarkedSpanFor(line.markedSpans, this);
6068 var span = getMarkedSpanFor(line.markedSpans, this);
5945 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
6069 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
5946 else if (cm) {
6070 else if (cm) {
5947 if (span.to != null) max = lineNo(line);
6071 if (span.to != null) max = lineNo(line);
5948 if (span.from != null) min = lineNo(line);
6072 if (span.from != null) min = lineNo(line);
5949 }
6073 }
5950 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
6074 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5951 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
6075 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
5952 updateLineHeight(line, textHeight(cm.display));
6076 updateLineHeight(line, textHeight(cm.display));
5953 }
6077 }
5954 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
6078 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
5955 var visual = visualLine(this.lines[i]), len = lineLength(visual);
6079 var visual = visualLine(this.lines[i]), len = lineLength(visual);
5956 if (len > cm.display.maxLineLength) {
6080 if (len > cm.display.maxLineLength) {
5957 cm.display.maxLine = visual;
6081 cm.display.maxLine = visual;
5958 cm.display.maxLineLength = len;
6082 cm.display.maxLineLength = len;
5959 cm.display.maxLineChanged = true;
6083 cm.display.maxLineChanged = true;
5960 }
6084 }
5961 }
6085 }
5962
6086
5963 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
6087 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
5964 this.lines.length = 0;
6088 this.lines.length = 0;
5965 this.explicitlyCleared = true;
6089 this.explicitlyCleared = true;
5966 if (this.atomic && this.doc.cantEdit) {
6090 if (this.atomic && this.doc.cantEdit) {
5967 this.doc.cantEdit = false;
6091 this.doc.cantEdit = false;
5968 if (cm) reCheckSelection(cm.doc);
6092 if (cm) reCheckSelection(cm.doc);
5969 }
6093 }
5970 if (cm) signalLater(cm, "markerCleared", cm, this);
6094 if (cm) signalLater(cm, "markerCleared", cm, this);
5971 if (withOp) endOperation(cm);
6095 if (withOp) endOperation(cm);
5972 if (this.parent) this.parent.clear();
6096 if (this.parent) this.parent.clear();
5973 };
6097 };
5974
6098
5975 // Find the position of the marker in the document. Returns a {from,
6099 // Find the position of the marker in the document. Returns a {from,
5976 // to} object by default. Side can be passed to get a specific side
6100 // to} object by default. Side can be passed to get a specific side
5977 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
6101 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5978 // Pos objects returned contain a line object, rather than a line
6102 // Pos objects returned contain a line object, rather than a line
5979 // number (used to prevent looking up the same line twice).
6103 // number (used to prevent looking up the same line twice).
5980 TextMarker.prototype.find = function(side, lineObj) {
6104 TextMarker.prototype.find = function(side, lineObj) {
5981 if (side == null && this.type == "bookmark") side = 1;
6105 if (side == null && this.type == "bookmark") side = 1;
5982 var from, to;
6106 var from, to;
5983 for (var i = 0; i < this.lines.length; ++i) {
6107 for (var i = 0; i < this.lines.length; ++i) {
5984 var line = this.lines[i];
6108 var line = this.lines[i];
5985 var span = getMarkedSpanFor(line.markedSpans, this);
6109 var span = getMarkedSpanFor(line.markedSpans, this);
5986 if (span.from != null) {
6110 if (span.from != null) {
5987 from = Pos(lineObj ? line : lineNo(line), span.from);
6111 from = Pos(lineObj ? line : lineNo(line), span.from);
5988 if (side == -1) return from;
6112 if (side == -1) return from;
5989 }
6113 }
5990 if (span.to != null) {
6114 if (span.to != null) {
5991 to = Pos(lineObj ? line : lineNo(line), span.to);
6115 to = Pos(lineObj ? line : lineNo(line), span.to);
5992 if (side == 1) return to;
6116 if (side == 1) return to;
5993 }
6117 }
5994 }
6118 }
5995 return from && {from: from, to: to};
6119 return from && {from: from, to: to};
5996 };
6120 };
5997
6121
5998 // Signals that the marker's widget changed, and surrounding layout
6122 // Signals that the marker's widget changed, and surrounding layout
5999 // should be recomputed.
6123 // should be recomputed.
6000 TextMarker.prototype.changed = function() {
6124 TextMarker.prototype.changed = function() {
6001 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6125 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6002 if (!pos || !cm) return;
6126 if (!pos || !cm) return;
6003 runInOp(cm, function() {
6127 runInOp(cm, function() {
6004 var line = pos.line, lineN = lineNo(pos.line);
6128 var line = pos.line, lineN = lineNo(pos.line);
6005 var view = findViewForLine(cm, lineN);
6129 var view = findViewForLine(cm, lineN);
6006 if (view) {
6130 if (view) {
6007 clearLineMeasurementCacheFor(view);
6131 clearLineMeasurementCacheFor(view);
6008 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6132 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6009 }
6133 }
6010 cm.curOp.updateMaxLine = true;
6134 cm.curOp.updateMaxLine = true;
6011 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6135 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6012 var oldHeight = widget.height;
6136 var oldHeight = widget.height;
6013 widget.height = null;
6137 widget.height = null;
6014 var dHeight = widgetHeight(widget) - oldHeight;
6138 var dHeight = widgetHeight(widget) - oldHeight;
6015 if (dHeight)
6139 if (dHeight)
6016 updateLineHeight(line, line.height + dHeight);
6140 updateLineHeight(line, line.height + dHeight);
6017 }
6141 }
6018 });
6142 });
6019 };
6143 };
6020
6144
6021 TextMarker.prototype.attachLine = function(line) {
6145 TextMarker.prototype.attachLine = function(line) {
6022 if (!this.lines.length && this.doc.cm) {
6146 if (!this.lines.length && this.doc.cm) {
6023 var op = this.doc.cm.curOp;
6147 var op = this.doc.cm.curOp;
6024 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6148 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6025 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6149 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6026 }
6150 }
6027 this.lines.push(line);
6151 this.lines.push(line);
6028 };
6152 };
6029 TextMarker.prototype.detachLine = function(line) {
6153 TextMarker.prototype.detachLine = function(line) {
6030 this.lines.splice(indexOf(this.lines, line), 1);
6154 this.lines.splice(indexOf(this.lines, line), 1);
6031 if (!this.lines.length && this.doc.cm) {
6155 if (!this.lines.length && this.doc.cm) {
6032 var op = this.doc.cm.curOp;
6156 var op = this.doc.cm.curOp;
6033 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6157 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6034 }
6158 }
6035 };
6159 };
6036
6160
6037 // Collapsed markers have unique ids, in order to be able to order
6161 // Collapsed markers have unique ids, in order to be able to order
6038 // them, which is needed for uniquely determining an outer marker
6162 // them, which is needed for uniquely determining an outer marker
6039 // when they overlap (they may nest, but not partially overlap).
6163 // when they overlap (they may nest, but not partially overlap).
6040 var nextMarkerId = 0;
6164 var nextMarkerId = 0;
6041
6165
6042 // Create a marker, wire it up to the right lines, and
6166 // Create a marker, wire it up to the right lines, and
6043 function markText(doc, from, to, options, type) {
6167 function markText(doc, from, to, options, type) {
6044 // Shared markers (across linked documents) are handled separately
6168 // Shared markers (across linked documents) are handled separately
6045 // (markTextShared will call out to this again, once per
6169 // (markTextShared will call out to this again, once per
6046 // document).
6170 // document).
6047 if (options && options.shared) return markTextShared(doc, from, to, options, type);
6171 if (options && options.shared) return markTextShared(doc, from, to, options, type);
6048 // Ensure we are in an operation.
6172 // Ensure we are in an operation.
6049 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6173 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6050
6174
6051 var marker = new TextMarker(doc, type), diff = cmp(from, to);
6175 var marker = new TextMarker(doc, type), diff = cmp(from, to);
6052 if (options) copyObj(options, marker, false);
6176 if (options) copyObj(options, marker, false);
6053 // Don't connect empty markers unless clearWhenEmpty is false
6177 // Don't connect empty markers unless clearWhenEmpty is false
6054 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6178 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6055 return marker;
6179 return marker;
6056 if (marker.replacedWith) {
6180 if (marker.replacedWith) {
6057 // Showing up as a widget implies collapsed (widget replaces text)
6181 // Showing up as a widget implies collapsed (widget replaces text)
6058 marker.collapsed = true;
6182 marker.collapsed = true;
6059 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6183 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6060 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6184 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6061 if (options.insertLeft) marker.widgetNode.insertLeft = true;
6185 if (options.insertLeft) marker.widgetNode.insertLeft = true;
6062 }
6186 }
6063 if (marker.collapsed) {
6187 if (marker.collapsed) {
6064 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6188 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6065 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6189 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6066 throw new Error("Inserting collapsed marker partially overlapping an existing one");
6190 throw new Error("Inserting collapsed marker partially overlapping an existing one");
6067 sawCollapsedSpans = true;
6191 sawCollapsedSpans = true;
6068 }
6192 }
6069
6193
6070 if (marker.addToHistory)
6194 if (marker.addToHistory)
6071 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6195 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6072
6196
6073 var curLine = from.line, cm = doc.cm, updateMaxLine;
6197 var curLine = from.line, cm = doc.cm, updateMaxLine;
6074 doc.iter(curLine, to.line + 1, function(line) {
6198 doc.iter(curLine, to.line + 1, function(line) {
6075 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6199 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6076 updateMaxLine = true;
6200 updateMaxLine = true;
6077 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6201 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6078 addMarkedSpan(line, new MarkedSpan(marker,
6202 addMarkedSpan(line, new MarkedSpan(marker,
6079 curLine == from.line ? from.ch : null,
6203 curLine == from.line ? from.ch : null,
6080 curLine == to.line ? to.ch : null));
6204 curLine == to.line ? to.ch : null));
6081 ++curLine;
6205 ++curLine;
6082 });
6206 });
6083 // lineIsHidden depends on the presence of the spans, so needs a second pass
6207 // lineIsHidden depends on the presence of the spans, so needs a second pass
6084 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6208 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6085 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6209 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6086 });
6210 });
6087
6211
6088 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6212 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6089
6213
6090 if (marker.readOnly) {
6214 if (marker.readOnly) {
6091 sawReadOnlySpans = true;
6215 sawReadOnlySpans = true;
6092 if (doc.history.done.length || doc.history.undone.length)
6216 if (doc.history.done.length || doc.history.undone.length)
6093 doc.clearHistory();
6217 doc.clearHistory();
6094 }
6218 }
6095 if (marker.collapsed) {
6219 if (marker.collapsed) {
6096 marker.id = ++nextMarkerId;
6220 marker.id = ++nextMarkerId;
6097 marker.atomic = true;
6221 marker.atomic = true;
6098 }
6222 }
6099 if (cm) {
6223 if (cm) {
6100 // Sync editor state
6224 // Sync editor state
6101 if (updateMaxLine) cm.curOp.updateMaxLine = true;
6225 if (updateMaxLine) cm.curOp.updateMaxLine = true;
6102 if (marker.collapsed)
6226 if (marker.collapsed)
6103 regChange(cm, from.line, to.line + 1);
6227 regChange(cm, from.line, to.line + 1);
6104 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6228 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6105 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6229 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6106 if (marker.atomic) reCheckSelection(cm.doc);
6230 if (marker.atomic) reCheckSelection(cm.doc);
6107 signalLater(cm, "markerAdded", cm, marker);
6231 signalLater(cm, "markerAdded", cm, marker);
6108 }
6232 }
6109 return marker;
6233 return marker;
6110 }
6234 }
6111
6235
6112 // SHARED TEXTMARKERS
6236 // SHARED TEXTMARKERS
6113
6237
6114 // A shared marker spans multiple linked documents. It is
6238 // A shared marker spans multiple linked documents. It is
6115 // implemented as a meta-marker-object controlling multiple normal
6239 // implemented as a meta-marker-object controlling multiple normal
6116 // markers.
6240 // markers.
6117 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6241 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6118 this.markers = markers;
6242 this.markers = markers;
6119 this.primary = primary;
6243 this.primary = primary;
6120 for (var i = 0; i < markers.length; ++i)
6244 for (var i = 0; i < markers.length; ++i)
6121 markers[i].parent = this;
6245 markers[i].parent = this;
6122 };
6246 };
6123 eventMixin(SharedTextMarker);
6247 eventMixin(SharedTextMarker);
6124
6248
6125 SharedTextMarker.prototype.clear = function() {
6249 SharedTextMarker.prototype.clear = function() {
6126 if (this.explicitlyCleared) return;
6250 if (this.explicitlyCleared) return;
6127 this.explicitlyCleared = true;
6251 this.explicitlyCleared = true;
6128 for (var i = 0; i < this.markers.length; ++i)
6252 for (var i = 0; i < this.markers.length; ++i)
6129 this.markers[i].clear();
6253 this.markers[i].clear();
6130 signalLater(this, "clear");
6254 signalLater(this, "clear");
6131 };
6255 };
6132 SharedTextMarker.prototype.find = function(side, lineObj) {
6256 SharedTextMarker.prototype.find = function(side, lineObj) {
6133 return this.primary.find(side, lineObj);
6257 return this.primary.find(side, lineObj);
6134 };
6258 };
6135
6259
6136 function markTextShared(doc, from, to, options, type) {
6260 function markTextShared(doc, from, to, options, type) {
6137 options = copyObj(options);
6261 options = copyObj(options);
6138 options.shared = false;
6262 options.shared = false;
6139 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6263 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6140 var widget = options.widgetNode;
6264 var widget = options.widgetNode;
6141 linkedDocs(doc, function(doc) {
6265 linkedDocs(doc, function(doc) {
6142 if (widget) options.widgetNode = widget.cloneNode(true);
6266 if (widget) options.widgetNode = widget.cloneNode(true);
6143 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6267 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6144 for (var i = 0; i < doc.linked.length; ++i)
6268 for (var i = 0; i < doc.linked.length; ++i)
6145 if (doc.linked[i].isParent) return;
6269 if (doc.linked[i].isParent) return;
6146 primary = lst(markers);
6270 primary = lst(markers);
6147 });
6271 });
6148 return new SharedTextMarker(markers, primary);
6272 return new SharedTextMarker(markers, primary);
6149 }
6273 }
6150
6274
6151 function findSharedMarkers(doc) {
6275 function findSharedMarkers(doc) {
6152 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6276 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6153 function(m) { return m.parent; });
6277 function(m) { return m.parent; });
6154 }
6278 }
6155
6279
6156 function copySharedMarkers(doc, markers) {
6280 function copySharedMarkers(doc, markers) {
6157 for (var i = 0; i < markers.length; i++) {
6281 for (var i = 0; i < markers.length; i++) {
6158 var marker = markers[i], pos = marker.find();
6282 var marker = markers[i], pos = marker.find();
6159 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6283 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6160 if (cmp(mFrom, mTo)) {
6284 if (cmp(mFrom, mTo)) {
6161 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6285 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6162 marker.markers.push(subMark);
6286 marker.markers.push(subMark);
6163 subMark.parent = marker;
6287 subMark.parent = marker;
6164 }
6288 }
6165 }
6289 }
6166 }
6290 }
6167
6291
6168 function detachSharedMarkers(markers) {
6292 function detachSharedMarkers(markers) {
6169 for (var i = 0; i < markers.length; i++) {
6293 for (var i = 0; i < markers.length; i++) {
6170 var marker = markers[i], linked = [marker.primary.doc];;
6294 var marker = markers[i], linked = [marker.primary.doc];;
6171 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6295 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6172 for (var j = 0; j < marker.markers.length; j++) {
6296 for (var j = 0; j < marker.markers.length; j++) {
6173 var subMarker = marker.markers[j];
6297 var subMarker = marker.markers[j];
6174 if (indexOf(linked, subMarker.doc) == -1) {
6298 if (indexOf(linked, subMarker.doc) == -1) {
6175 subMarker.parent = null;
6299 subMarker.parent = null;
6176 marker.markers.splice(j--, 1);
6300 marker.markers.splice(j--, 1);
6177 }
6301 }
6178 }
6302 }
6179 }
6303 }
6180 }
6304 }
6181
6305
6182 // TEXTMARKER SPANS
6306 // TEXTMARKER SPANS
6183
6307
6184 function MarkedSpan(marker, from, to) {
6308 function MarkedSpan(marker, from, to) {
6185 this.marker = marker;
6309 this.marker = marker;
6186 this.from = from; this.to = to;
6310 this.from = from; this.to = to;
6187 }
6311 }
6188
6312
6189 // Search an array of spans for a span matching the given marker.
6313 // Search an array of spans for a span matching the given marker.
6190 function getMarkedSpanFor(spans, marker) {
6314 function getMarkedSpanFor(spans, marker) {
6191 if (spans) for (var i = 0; i < spans.length; ++i) {
6315 if (spans) for (var i = 0; i < spans.length; ++i) {
6192 var span = spans[i];
6316 var span = spans[i];
6193 if (span.marker == marker) return span;
6317 if (span.marker == marker) return span;
6194 }
6318 }
6195 }
6319 }
6196 // Remove a span from an array, returning undefined if no spans are
6320 // Remove a span from an array, returning undefined if no spans are
6197 // left (we don't store arrays for lines without spans).
6321 // left (we don't store arrays for lines without spans).
6198 function removeMarkedSpan(spans, span) {
6322 function removeMarkedSpan(spans, span) {
6199 for (var r, i = 0; i < spans.length; ++i)
6323 for (var r, i = 0; i < spans.length; ++i)
6200 if (spans[i] != span) (r || (r = [])).push(spans[i]);
6324 if (spans[i] != span) (r || (r = [])).push(spans[i]);
6201 return r;
6325 return r;
6202 }
6326 }
6203 // Add a span to a line.
6327 // Add a span to a line.
6204 function addMarkedSpan(line, span) {
6328 function addMarkedSpan(line, span) {
6205 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6329 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6206 span.marker.attachLine(line);
6330 span.marker.attachLine(line);
6207 }
6331 }
6208
6332
6209 // Used for the algorithm that adjusts markers for a change in the
6333 // Used for the algorithm that adjusts markers for a change in the
6210 // document. These functions cut an array of spans at a given
6334 // document. These functions cut an array of spans at a given
6211 // character position, returning an array of remaining chunks (or
6335 // character position, returning an array of remaining chunks (or
6212 // undefined if nothing remains).
6336 // undefined if nothing remains).
6213 function markedSpansBefore(old, startCh, isInsert) {
6337 function markedSpansBefore(old, startCh, isInsert) {
6214 if (old) for (var i = 0, nw; i < old.length; ++i) {
6338 if (old) for (var i = 0, nw; i < old.length; ++i) {
6215 var span = old[i], marker = span.marker;
6339 var span = old[i], marker = span.marker;
6216 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
6340 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
6217 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6341 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6218 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
6342 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
6219 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6343 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6220 }
6344 }
6221 }
6345 }
6222 return nw;
6346 return nw;
6223 }
6347 }
6224 function markedSpansAfter(old, endCh, isInsert) {
6348 function markedSpansAfter(old, endCh, isInsert) {
6225 if (old) for (var i = 0, nw; i < old.length; ++i) {
6349 if (old) for (var i = 0, nw; i < old.length; ++i) {
6226 var span = old[i], marker = span.marker;
6350 var span = old[i], marker = span.marker;
6227 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
6351 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
6228 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6352 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6229 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
6353 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
6230 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6354 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6231 span.to == null ? null : span.to - endCh));
6355 span.to == null ? null : span.to - endCh));
6232 }
6356 }
6233 }
6357 }
6234 return nw;
6358 return nw;
6235 }
6359 }
6236
6360
6237 // Given a change object, compute the new set of marker spans that
6361 // Given a change object, compute the new set of marker spans that
6238 // cover the line in which the change took place. Removes spans
6362 // cover the line in which the change took place. Removes spans
6239 // entirely within the change, reconnects spans belonging to the
6363 // entirely within the change, reconnects spans belonging to the
6240 // same marker that appear on both sides of the change, and cuts off
6364 // same marker that appear on both sides of the change, and cuts off
6241 // spans partially within the change. Returns an array of span
6365 // spans partially within the change. Returns an array of span
6242 // arrays with one element for each line in (after) the change.
6366 // arrays with one element for each line in (after) the change.
6243 function stretchSpansOverChange(doc, change) {
6367 function stretchSpansOverChange(doc, change) {
6244 if (change.full) return null;
6368 if (change.full) return null;
6245 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6369 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6246 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6370 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6247 if (!oldFirst && !oldLast) return null;
6371 if (!oldFirst && !oldLast) return null;
6248
6372
6249 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
6373 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
6250 // Get the spans that 'stick out' on both sides
6374 // Get the spans that 'stick out' on both sides
6251 var first = markedSpansBefore(oldFirst, startCh, isInsert);
6375 var first = markedSpansBefore(oldFirst, startCh, isInsert);
6252 var last = markedSpansAfter(oldLast, endCh, isInsert);
6376 var last = markedSpansAfter(oldLast, endCh, isInsert);
6253
6377
6254 // Next, merge those two ends
6378 // Next, merge those two ends
6255 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
6379 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
6256 if (first) {
6380 if (first) {
6257 // Fix up .to properties of first
6381 // Fix up .to properties of first
6258 for (var i = 0; i < first.length; ++i) {
6382 for (var i = 0; i < first.length; ++i) {
6259 var span = first[i];
6383 var span = first[i];
6260 if (span.to == null) {
6384 if (span.to == null) {
6261 var found = getMarkedSpanFor(last, span.marker);
6385 var found = getMarkedSpanFor(last, span.marker);
6262 if (!found) span.to = startCh;
6386 if (!found) span.to = startCh;
6263 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
6387 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
6264 }
6388 }
6265 }
6389 }
6266 }
6390 }
6267 if (last) {
6391 if (last) {
6268 // Fix up .from in last (or move them into first in case of sameLine)
6392 // Fix up .from in last (or move them into first in case of sameLine)
6269 for (var i = 0; i < last.length; ++i) {
6393 for (var i = 0; i < last.length; ++i) {
6270 var span = last[i];
6394 var span = last[i];
6271 if (span.to != null) span.to += offset;
6395 if (span.to != null) span.to += offset;
6272 if (span.from == null) {
6396 if (span.from == null) {
6273 var found = getMarkedSpanFor(first, span.marker);
6397 var found = getMarkedSpanFor(first, span.marker);
6274 if (!found) {
6398 if (!found) {
6275 span.from = offset;
6399 span.from = offset;
6276 if (sameLine) (first || (first = [])).push(span);
6400 if (sameLine) (first || (first = [])).push(span);
6277 }
6401 }
6278 } else {
6402 } else {
6279 span.from += offset;
6403 span.from += offset;
6280 if (sameLine) (first || (first = [])).push(span);
6404 if (sameLine) (first || (first = [])).push(span);
6281 }
6405 }
6282 }
6406 }
6283 }
6407 }
6284 // Make sure we didn't create any zero-length spans
6408 // Make sure we didn't create any zero-length spans
6285 if (first) first = clearEmptySpans(first);
6409 if (first) first = clearEmptySpans(first);
6286 if (last && last != first) last = clearEmptySpans(last);
6410 if (last && last != first) last = clearEmptySpans(last);
6287
6411
6288 var newMarkers = [first];
6412 var newMarkers = [first];
6289 if (!sameLine) {
6413 if (!sameLine) {
6290 // Fill gap with whole-line-spans
6414 // Fill gap with whole-line-spans
6291 var gap = change.text.length - 2, gapMarkers;
6415 var gap = change.text.length - 2, gapMarkers;
6292 if (gap > 0 && first)
6416 if (gap > 0 && first)
6293 for (var i = 0; i < first.length; ++i)
6417 for (var i = 0; i < first.length; ++i)
6294 if (first[i].to == null)
6418 if (first[i].to == null)
6295 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6419 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6296 for (var i = 0; i < gap; ++i)
6420 for (var i = 0; i < gap; ++i)
6297 newMarkers.push(gapMarkers);
6421 newMarkers.push(gapMarkers);
6298 newMarkers.push(last);
6422 newMarkers.push(last);
6299 }
6423 }
6300 return newMarkers;
6424 return newMarkers;
6301 }
6425 }
6302
6426
6303 // Remove spans that are empty and don't have a clearWhenEmpty
6427 // Remove spans that are empty and don't have a clearWhenEmpty
6304 // option of false.
6428 // option of false.
6305 function clearEmptySpans(spans) {
6429 function clearEmptySpans(spans) {
6306 for (var i = 0; i < spans.length; ++i) {
6430 for (var i = 0; i < spans.length; ++i) {
6307 var span = spans[i];
6431 var span = spans[i];
6308 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
6432 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
6309 spans.splice(i--, 1);
6433 spans.splice(i--, 1);
6310 }
6434 }
6311 if (!spans.length) return null;
6435 if (!spans.length) return null;
6312 return spans;
6436 return spans;
6313 }
6437 }
6314
6438
6315 // Used for un/re-doing changes from the history. Combines the
6439 // Used for un/re-doing changes from the history. Combines the
6316 // result of computing the existing spans with the set of spans that
6440 // result of computing the existing spans with the set of spans that
6317 // existed in the history (so that deleting around a span and then
6441 // existed in the history (so that deleting around a span and then
6318 // undoing brings back the span).
6442 // undoing brings back the span).
6319 function mergeOldSpans(doc, change) {
6443 function mergeOldSpans(doc, change) {
6320 var old = getOldSpans(doc, change);
6444 var old = getOldSpans(doc, change);
6321 var stretched = stretchSpansOverChange(doc, change);
6445 var stretched = stretchSpansOverChange(doc, change);
6322 if (!old) return stretched;
6446 if (!old) return stretched;
6323 if (!stretched) return old;
6447 if (!stretched) return old;
6324
6448
6325 for (var i = 0; i < old.length; ++i) {
6449 for (var i = 0; i < old.length; ++i) {
6326 var oldCur = old[i], stretchCur = stretched[i];
6450 var oldCur = old[i], stretchCur = stretched[i];
6327 if (oldCur && stretchCur) {
6451 if (oldCur && stretchCur) {
6328 spans: for (var j = 0; j < stretchCur.length; ++j) {
6452 spans: for (var j = 0; j < stretchCur.length; ++j) {
6329 var span = stretchCur[j];
6453 var span = stretchCur[j];
6330 for (var k = 0; k < oldCur.length; ++k)
6454 for (var k = 0; k < oldCur.length; ++k)
6331 if (oldCur[k].marker == span.marker) continue spans;
6455 if (oldCur[k].marker == span.marker) continue spans;
6332 oldCur.push(span);
6456 oldCur.push(span);
6333 }
6457 }
6334 } else if (stretchCur) {
6458 } else if (stretchCur) {
6335 old[i] = stretchCur;
6459 old[i] = stretchCur;
6336 }
6460 }
6337 }
6461 }
6338 return old;
6462 return old;
6339 }
6463 }
6340
6464
6341 // Used to 'clip' out readOnly ranges when making a change.
6465 // Used to 'clip' out readOnly ranges when making a change.
6342 function removeReadOnlyRanges(doc, from, to) {
6466 function removeReadOnlyRanges(doc, from, to) {
6343 var markers = null;
6467 var markers = null;
6344 doc.iter(from.line, to.line + 1, function(line) {
6468 doc.iter(from.line, to.line + 1, function(line) {
6345 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6469 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6346 var mark = line.markedSpans[i].marker;
6470 var mark = line.markedSpans[i].marker;
6347 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
6471 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
6348 (markers || (markers = [])).push(mark);
6472 (markers || (markers = [])).push(mark);
6349 }
6473 }
6350 });
6474 });
6351 if (!markers) return null;
6475 if (!markers) return null;
6352 var parts = [{from: from, to: to}];
6476 var parts = [{from: from, to: to}];
6353 for (var i = 0; i < markers.length; ++i) {
6477 for (var i = 0; i < markers.length; ++i) {
6354 var mk = markers[i], m = mk.find(0);
6478 var mk = markers[i], m = mk.find(0);
6355 for (var j = 0; j < parts.length; ++j) {
6479 for (var j = 0; j < parts.length; ++j) {
6356 var p = parts[j];
6480 var p = parts[j];
6357 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6481 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6358 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6482 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6359 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6483 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6360 newParts.push({from: p.from, to: m.from});
6484 newParts.push({from: p.from, to: m.from});
6361 if (dto > 0 || !mk.inclusiveRight && !dto)
6485 if (dto > 0 || !mk.inclusiveRight && !dto)
6362 newParts.push({from: m.to, to: p.to});
6486 newParts.push({from: m.to, to: p.to});
6363 parts.splice.apply(parts, newParts);
6487 parts.splice.apply(parts, newParts);
6364 j += newParts.length - 1;
6488 j += newParts.length - 1;
6365 }
6489 }
6366 }
6490 }
6367 return parts;
6491 return parts;
6368 }
6492 }
6369
6493
6370 // Connect or disconnect spans from a line.
6494 // Connect or disconnect spans from a line.
6371 function detachMarkedSpans(line) {
6495 function detachMarkedSpans(line) {
6372 var spans = line.markedSpans;
6496 var spans = line.markedSpans;
6373 if (!spans) return;
6497 if (!spans) return;
6374 for (var i = 0; i < spans.length; ++i)
6498 for (var i = 0; i < spans.length; ++i)
6375 spans[i].marker.detachLine(line);
6499 spans[i].marker.detachLine(line);
6376 line.markedSpans = null;
6500 line.markedSpans = null;
6377 }
6501 }
6378 function attachMarkedSpans(line, spans) {
6502 function attachMarkedSpans(line, spans) {
6379 if (!spans) return;
6503 if (!spans) return;
6380 for (var i = 0; i < spans.length; ++i)
6504 for (var i = 0; i < spans.length; ++i)
6381 spans[i].marker.attachLine(line);
6505 spans[i].marker.attachLine(line);
6382 line.markedSpans = spans;
6506 line.markedSpans = spans;
6383 }
6507 }
6384
6508
6385 // Helpers used when computing which overlapping collapsed span
6509 // Helpers used when computing which overlapping collapsed span
6386 // counts as the larger one.
6510 // counts as the larger one.
6387 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6511 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6388 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6512 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6389
6513
6390 // Returns a number indicating which of two overlapping collapsed
6514 // Returns a number indicating which of two overlapping collapsed
6391 // spans is larger (and thus includes the other). Falls back to
6515 // spans is larger (and thus includes the other). Falls back to
6392 // comparing ids when the spans cover exactly the same range.
6516 // comparing ids when the spans cover exactly the same range.
6393 function compareCollapsedMarkers(a, b) {
6517 function compareCollapsedMarkers(a, b) {
6394 var lenDiff = a.lines.length - b.lines.length;
6518 var lenDiff = a.lines.length - b.lines.length;
6395 if (lenDiff != 0) return lenDiff;
6519 if (lenDiff != 0) return lenDiff;
6396 var aPos = a.find(), bPos = b.find();
6520 var aPos = a.find(), bPos = b.find();
6397 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6521 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6398 if (fromCmp) return -fromCmp;
6522 if (fromCmp) return -fromCmp;
6399 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6523 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6400 if (toCmp) return toCmp;
6524 if (toCmp) return toCmp;
6401 return b.id - a.id;
6525 return b.id - a.id;
6402 }
6526 }
6403
6527
6404 // Find out whether a line ends or starts in a collapsed span. If
6528 // Find out whether a line ends or starts in a collapsed span. If
6405 // so, return the marker for that span.
6529 // so, return the marker for that span.
6406 function collapsedSpanAtSide(line, start) {
6530 function collapsedSpanAtSide(line, start) {
6407 var sps = sawCollapsedSpans && line.markedSpans, found;
6531 var sps = sawCollapsedSpans && line.markedSpans, found;
6408 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6532 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6409 sp = sps[i];
6533 sp = sps[i];
6410 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6534 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6411 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6535 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6412 found = sp.marker;
6536 found = sp.marker;
6413 }
6537 }
6414 return found;
6538 return found;
6415 }
6539 }
6416 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6540 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6417 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6541 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6418
6542
6419 // Test whether there exists a collapsed span that partially
6543 // Test whether there exists a collapsed span that partially
6420 // overlaps (covers the start or end, but not both) of a new span.
6544 // overlaps (covers the start or end, but not both) of a new span.
6421 // Such overlap is not allowed.
6545 // Such overlap is not allowed.
6422 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6546 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6423 var line = getLine(doc, lineNo);
6547 var line = getLine(doc, lineNo);
6424 var sps = sawCollapsedSpans && line.markedSpans;
6548 var sps = sawCollapsedSpans && line.markedSpans;
6425 if (sps) for (var i = 0; i < sps.length; ++i) {
6549 if (sps) for (var i = 0; i < sps.length; ++i) {
6426 var sp = sps[i];
6550 var sp = sps[i];
6427 if (!sp.marker.collapsed) continue;
6551 if (!sp.marker.collapsed) continue;
6428 var found = sp.marker.find(0);
6552 var found = sp.marker.find(0);
6429 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6553 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6430 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6554 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6431 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6555 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6432 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6556 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6433 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6557 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6434 return true;
6558 return true;
6435 }
6559 }
6436 }
6560 }
6437
6561
6438 // A visual line is a line as drawn on the screen. Folding, for
6562 // A visual line is a line as drawn on the screen. Folding, for
6439 // example, can cause multiple logical lines to appear on the same
6563 // example, can cause multiple logical lines to appear on the same
6440 // visual line. This finds the start of the visual line that the
6564 // visual line. This finds the start of the visual line that the
6441 // given line is part of (usually that is the line itself).
6565 // given line is part of (usually that is the line itself).
6442 function visualLine(line) {
6566 function visualLine(line) {
6443 var merged;
6567 var merged;
6444 while (merged = collapsedSpanAtStart(line))
6568 while (merged = collapsedSpanAtStart(line))
6445 line = merged.find(-1, true).line;
6569 line = merged.find(-1, true).line;
6446 return line;
6570 return line;
6447 }
6571 }
6448
6572
6449 // Returns an array of logical lines that continue the visual line
6573 // Returns an array of logical lines that continue the visual line
6450 // started by the argument, or undefined if there are no such lines.
6574 // started by the argument, or undefined if there are no such lines.
6451 function visualLineContinued(line) {
6575 function visualLineContinued(line) {
6452 var merged, lines;
6576 var merged, lines;
6453 while (merged = collapsedSpanAtEnd(line)) {
6577 while (merged = collapsedSpanAtEnd(line)) {
6454 line = merged.find(1, true).line;
6578 line = merged.find(1, true).line;
6455 (lines || (lines = [])).push(line);
6579 (lines || (lines = [])).push(line);
6456 }
6580 }
6457 return lines;
6581 return lines;
6458 }
6582 }
6459
6583
6460 // Get the line number of the start of the visual line that the
6584 // Get the line number of the start of the visual line that the
6461 // given line number is part of.
6585 // given line number is part of.
6462 function visualLineNo(doc, lineN) {
6586 function visualLineNo(doc, lineN) {
6463 var line = getLine(doc, lineN), vis = visualLine(line);
6587 var line = getLine(doc, lineN), vis = visualLine(line);
6464 if (line == vis) return lineN;
6588 if (line == vis) return lineN;
6465 return lineNo(vis);
6589 return lineNo(vis);
6466 }
6590 }
6467 // Get the line number of the start of the next visual line after
6591 // Get the line number of the start of the next visual line after
6468 // the given line.
6592 // the given line.
6469 function visualLineEndNo(doc, lineN) {
6593 function visualLineEndNo(doc, lineN) {
6470 if (lineN > doc.lastLine()) return lineN;
6594 if (lineN > doc.lastLine()) return lineN;
6471 var line = getLine(doc, lineN), merged;
6595 var line = getLine(doc, lineN), merged;
6472 if (!lineIsHidden(doc, line)) return lineN;
6596 if (!lineIsHidden(doc, line)) return lineN;
6473 while (merged = collapsedSpanAtEnd(line))
6597 while (merged = collapsedSpanAtEnd(line))
6474 line = merged.find(1, true).line;
6598 line = merged.find(1, true).line;
6475 return lineNo(line) + 1;
6599 return lineNo(line) + 1;
6476 }
6600 }
6477
6601
6478 // Compute whether a line is hidden. Lines count as hidden when they
6602 // Compute whether a line is hidden. Lines count as hidden when they
6479 // are part of a visual line that starts with another line, or when
6603 // are part of a visual line that starts with another line, or when
6480 // they are entirely covered by collapsed, non-widget span.
6604 // they are entirely covered by collapsed, non-widget span.
6481 function lineIsHidden(doc, line) {
6605 function lineIsHidden(doc, line) {
6482 var sps = sawCollapsedSpans && line.markedSpans;
6606 var sps = sawCollapsedSpans && line.markedSpans;
6483 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6607 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6484 sp = sps[i];
6608 sp = sps[i];
6485 if (!sp.marker.collapsed) continue;
6609 if (!sp.marker.collapsed) continue;
6486 if (sp.from == null) return true;
6610 if (sp.from == null) return true;
6487 if (sp.marker.widgetNode) continue;
6611 if (sp.marker.widgetNode) continue;
6488 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
6612 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
6489 return true;
6613 return true;
6490 }
6614 }
6491 }
6615 }
6492 function lineIsHiddenInner(doc, line, span) {
6616 function lineIsHiddenInner(doc, line, span) {
6493 if (span.to == null) {
6617 if (span.to == null) {
6494 var end = span.marker.find(1, true);
6618 var end = span.marker.find(1, true);
6495 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6619 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6496 }
6620 }
6497 if (span.marker.inclusiveRight && span.to == line.text.length)
6621 if (span.marker.inclusiveRight && span.to == line.text.length)
6498 return true;
6622 return true;
6499 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6623 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6500 sp = line.markedSpans[i];
6624 sp = line.markedSpans[i];
6501 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6625 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6502 (sp.to == null || sp.to != span.from) &&
6626 (sp.to == null || sp.to != span.from) &&
6503 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6627 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6504 lineIsHiddenInner(doc, line, sp)) return true;
6628 lineIsHiddenInner(doc, line, sp)) return true;
6505 }
6629 }
6506 }
6630 }
6507
6631
6508 // LINE WIDGETS
6632 // LINE WIDGETS
6509
6633
6510 // Line widgets are block elements displayed above or below a line.
6634 // Line widgets are block elements displayed above or below a line.
6511
6635
6512 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6636 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6513 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6637 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6514 this[opt] = options[opt];
6638 this[opt] = options[opt];
6515 this.doc = doc;
6639 this.doc = doc;
6516 this.node = node;
6640 this.node = node;
6517 };
6641 };
6518 eventMixin(LineWidget);
6642 eventMixin(LineWidget);
6519
6643
6520 function adjustScrollWhenAboveVisible(cm, line, diff) {
6644 function adjustScrollWhenAboveVisible(cm, line, diff) {
6521 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6645 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6522 addToScrollPos(cm, null, diff);
6646 addToScrollPos(cm, null, diff);
6523 }
6647 }
6524
6648
6525 LineWidget.prototype.clear = function() {
6649 LineWidget.prototype.clear = function() {
6526 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6650 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6527 if (no == null || !ws) return;
6651 if (no == null || !ws) return;
6528 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6652 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6529 if (!ws.length) line.widgets = null;
6653 if (!ws.length) line.widgets = null;
6530 var height = widgetHeight(this);
6654 var height = widgetHeight(this);
6531 updateLineHeight(line, Math.max(0, line.height - height));
6655 updateLineHeight(line, Math.max(0, line.height - height));
6532 if (cm) runInOp(cm, function() {
6656 if (cm) runInOp(cm, function() {
6533 adjustScrollWhenAboveVisible(cm, line, -height);
6657 adjustScrollWhenAboveVisible(cm, line, -height);
6534 regLineChange(cm, no, "widget");
6658 regLineChange(cm, no, "widget");
6535 });
6659 });
6536 };
6660 };
6537 LineWidget.prototype.changed = function() {
6661 LineWidget.prototype.changed = function() {
6538 var oldH = this.height, cm = this.doc.cm, line = this.line;
6662 var oldH = this.height, cm = this.doc.cm, line = this.line;
6539 this.height = null;
6663 this.height = null;
6540 var diff = widgetHeight(this) - oldH;
6664 var diff = widgetHeight(this) - oldH;
6541 if (!diff) return;
6665 if (!diff) return;
6542 updateLineHeight(line, line.height + diff);
6666 updateLineHeight(line, line.height + diff);
6543 if (cm) runInOp(cm, function() {
6667 if (cm) runInOp(cm, function() {
6544 cm.curOp.forceUpdate = true;
6668 cm.curOp.forceUpdate = true;
6545 adjustScrollWhenAboveVisible(cm, line, diff);
6669 adjustScrollWhenAboveVisible(cm, line, diff);
6546 });
6670 });
6547 };
6671 };
6548
6672
6549 function widgetHeight(widget) {
6673 function widgetHeight(widget) {
6550 if (widget.height != null) return widget.height;
6674 if (widget.height != null) return widget.height;
6551 var cm = widget.doc.cm;
6675 var cm = widget.doc.cm;
6552 if (!cm) return 0;
6676 if (!cm) return 0;
6553 if (!contains(document.body, widget.node)) {
6677 if (!contains(document.body, widget.node)) {
6554 var parentStyle = "position: relative;";
6678 var parentStyle = "position: relative;";
6555 if (widget.coverGutter)
6679 if (widget.coverGutter)
6556 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6680 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6557 if (widget.noHScroll)
6681 if (widget.noHScroll)
6558 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6682 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6559 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6683 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6560 }
6684 }
6561 return widget.height = widget.node.offsetHeight;
6685 return widget.height = widget.node.parentNode.offsetHeight;
6562 }
6686 }
6563
6687
6564 function addLineWidget(doc, handle, node, options) {
6688 function addLineWidget(doc, handle, node, options) {
6565 var widget = new LineWidget(doc, node, options);
6689 var widget = new LineWidget(doc, node, options);
6566 var cm = doc.cm;
6690 var cm = doc.cm;
6567 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6691 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6568 changeLine(doc, handle, "widget", function(line) {
6692 changeLine(doc, handle, "widget", function(line) {
6569 var widgets = line.widgets || (line.widgets = []);
6693 var widgets = line.widgets || (line.widgets = []);
6570 if (widget.insertAt == null) widgets.push(widget);
6694 if (widget.insertAt == null) widgets.push(widget);
6571 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6695 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6572 widget.line = line;
6696 widget.line = line;
6573 if (cm && !lineIsHidden(doc, line)) {
6697 if (cm && !lineIsHidden(doc, line)) {
6574 var aboveVisible = heightAtLine(line) < doc.scrollTop;
6698 var aboveVisible = heightAtLine(line) < doc.scrollTop;
6575 updateLineHeight(line, line.height + widgetHeight(widget));
6699 updateLineHeight(line, line.height + widgetHeight(widget));
6576 if (aboveVisible) addToScrollPos(cm, null, widget.height);
6700 if (aboveVisible) addToScrollPos(cm, null, widget.height);
6577 cm.curOp.forceUpdate = true;
6701 cm.curOp.forceUpdate = true;
6578 }
6702 }
6579 return true;
6703 return true;
6580 });
6704 });
6581 return widget;
6705 return widget;
6582 }
6706 }
6583
6707
6584 // LINE DATA STRUCTURE
6708 // LINE DATA STRUCTURE
6585
6709
6586 // Line objects. These hold state related to a line, including
6710 // Line objects. These hold state related to a line, including
6587 // highlighting info (the styles array).
6711 // highlighting info (the styles array).
6588 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6712 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6589 this.text = text;
6713 this.text = text;
6590 attachMarkedSpans(this, markedSpans);
6714 attachMarkedSpans(this, markedSpans);
6591 this.height = estimateHeight ? estimateHeight(this) : 1;
6715 this.height = estimateHeight ? estimateHeight(this) : 1;
6592 };
6716 };
6593 eventMixin(Line);
6717 eventMixin(Line);
6594 Line.prototype.lineNo = function() { return lineNo(this); };
6718 Line.prototype.lineNo = function() { return lineNo(this); };
6595
6719
6596 // Change the content (text, markers) of a line. Automatically
6720 // Change the content (text, markers) of a line. Automatically
6597 // invalidates cached information and tries to re-estimate the
6721 // invalidates cached information and tries to re-estimate the
6598 // line's height.
6722 // line's height.
6599 function updateLine(line, text, markedSpans, estimateHeight) {
6723 function updateLine(line, text, markedSpans, estimateHeight) {
6600 line.text = text;
6724 line.text = text;
6601 if (line.stateAfter) line.stateAfter = null;
6725 if (line.stateAfter) line.stateAfter = null;
6602 if (line.styles) line.styles = null;
6726 if (line.styles) line.styles = null;
6603 if (line.order != null) line.order = null;
6727 if (line.order != null) line.order = null;
6604 detachMarkedSpans(line);
6728 detachMarkedSpans(line);
6605 attachMarkedSpans(line, markedSpans);
6729 attachMarkedSpans(line, markedSpans);
6606 var estHeight = estimateHeight ? estimateHeight(line) : 1;
6730 var estHeight = estimateHeight ? estimateHeight(line) : 1;
6607 if (estHeight != line.height) updateLineHeight(line, estHeight);
6731 if (estHeight != line.height) updateLineHeight(line, estHeight);
6608 }
6732 }
6609
6733
6610 // Detach a line from the document tree and its markers.
6734 // Detach a line from the document tree and its markers.
6611 function cleanUpLine(line) {
6735 function cleanUpLine(line) {
6612 line.parent = null;
6736 line.parent = null;
6613 detachMarkedSpans(line);
6737 detachMarkedSpans(line);
6614 }
6738 }
6615
6739
6616 function extractLineClasses(type, output) {
6740 function extractLineClasses(type, output) {
6617 if (type) for (;;) {
6741 if (type) for (;;) {
6618 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6742 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6619 if (!lineClass) break;
6743 if (!lineClass) break;
6620 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6744 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6621 var prop = lineClass[1] ? "bgClass" : "textClass";
6745 var prop = lineClass[1] ? "bgClass" : "textClass";
6622 if (output[prop] == null)
6746 if (output[prop] == null)
6623 output[prop] = lineClass[2];
6747 output[prop] = lineClass[2];
6624 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6748 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6625 output[prop] += " " + lineClass[2];
6749 output[prop] += " " + lineClass[2];
6626 }
6750 }
6627 return type;
6751 return type;
6628 }
6752 }
6629
6753
6630 function callBlankLine(mode, state) {
6754 function callBlankLine(mode, state) {
6631 if (mode.blankLine) return mode.blankLine(state);
6755 if (mode.blankLine) return mode.blankLine(state);
6632 if (!mode.innerMode) return;
6756 if (!mode.innerMode) return;
6633 var inner = CodeMirror.innerMode(mode, state);
6757 var inner = CodeMirror.innerMode(mode, state);
6634 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6758 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6635 }
6759 }
6636
6760
6637 function readToken(mode, stream, state, inner) {
6761 function readToken(mode, stream, state, inner) {
6638 for (var i = 0; i < 10; i++) {
6762 for (var i = 0; i < 10; i++) {
6639 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6763 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6640 var style = mode.token(stream, state);
6764 var style = mode.token(stream, state);
6641 if (stream.pos > stream.start) return style;
6765 if (stream.pos > stream.start) return style;
6642 }
6766 }
6643 throw new Error("Mode " + mode.name + " failed to advance stream.");
6767 throw new Error("Mode " + mode.name + " failed to advance stream.");
6644 }
6768 }
6645
6769
6646 // Utility for getTokenAt and getLineTokens
6770 // Utility for getTokenAt and getLineTokens
6647 function takeToken(cm, pos, precise, asArray) {
6771 function takeToken(cm, pos, precise, asArray) {
6648 function getObj(copy) {
6772 function getObj(copy) {
6649 return {start: stream.start, end: stream.pos,
6773 return {start: stream.start, end: stream.pos,
6650 string: stream.current(),
6774 string: stream.current(),
6651 type: style || null,
6775 type: style || null,
6652 state: copy ? copyState(doc.mode, state) : state};
6776 state: copy ? copyState(doc.mode, state) : state};
6653 }
6777 }
6654
6778
6655 var doc = cm.doc, mode = doc.mode, style;
6779 var doc = cm.doc, mode = doc.mode, style;
6656 pos = clipPos(doc, pos);
6780 pos = clipPos(doc, pos);
6657 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6781 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6658 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6782 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6659 if (asArray) tokens = [];
6783 if (asArray) tokens = [];
6660 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6784 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6661 stream.start = stream.pos;
6785 stream.start = stream.pos;
6662 style = readToken(mode, stream, state);
6786 style = readToken(mode, stream, state);
6663 if (asArray) tokens.push(getObj(true));
6787 if (asArray) tokens.push(getObj(true));
6664 }
6788 }
6665 return asArray ? tokens : getObj();
6789 return asArray ? tokens : getObj();
6666 }
6790 }
6667
6791
6668 // Run the given mode's parser over a line, calling f for each token.
6792 // Run the given mode's parser over a line, calling f for each token.
6669 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6793 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6670 var flattenSpans = mode.flattenSpans;
6794 var flattenSpans = mode.flattenSpans;
6671 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6795 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6672 var curStart = 0, curStyle = null;
6796 var curStart = 0, curStyle = null;
6673 var stream = new StringStream(text, cm.options.tabSize), style;
6797 var stream = new StringStream(text, cm.options.tabSize), style;
6674 var inner = cm.options.addModeClass && [null];
6798 var inner = cm.options.addModeClass && [null];
6675 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6799 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6676 while (!stream.eol()) {
6800 while (!stream.eol()) {
6677 if (stream.pos > cm.options.maxHighlightLength) {
6801 if (stream.pos > cm.options.maxHighlightLength) {
6678 flattenSpans = false;
6802 flattenSpans = false;
6679 if (forceToEnd) processLine(cm, text, state, stream.pos);
6803 if (forceToEnd) processLine(cm, text, state, stream.pos);
6680 stream.pos = text.length;
6804 stream.pos = text.length;
6681 style = null;
6805 style = null;
6682 } else {
6806 } else {
6683 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6807 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6684 }
6808 }
6685 if (inner) {
6809 if (inner) {
6686 var mName = inner[0].name;
6810 var mName = inner[0].name;
6687 if (mName) style = "m-" + (style ? mName + " " + style : mName);
6811 if (mName) style = "m-" + (style ? mName + " " + style : mName);
6688 }
6812 }
6689 if (!flattenSpans || curStyle != style) {
6813 if (!flattenSpans || curStyle != style) {
6690 while (curStart < stream.start) {
6814 while (curStart < stream.start) {
6691 curStart = Math.min(stream.start, curStart + 50000);
6815 curStart = Math.min(stream.start, curStart + 50000);
6692 f(curStart, curStyle);
6816 f(curStart, curStyle);
6693 }
6817 }
6694 curStyle = style;
6818 curStyle = style;
6695 }
6819 }
6696 stream.start = stream.pos;
6820 stream.start = stream.pos;
6697 }
6821 }
6698 while (curStart < stream.pos) {
6822 while (curStart < stream.pos) {
6699 // Webkit seems to refuse to render text nodes longer than 57444 characters
6823 // Webkit seems to refuse to render text nodes longer than 57444 characters
6700 var pos = Math.min(stream.pos, curStart + 50000);
6824 var pos = Math.min(stream.pos, curStart + 50000);
6701 f(pos, curStyle);
6825 f(pos, curStyle);
6702 curStart = pos;
6826 curStart = pos;
6703 }
6827 }
6704 }
6828 }
6705
6829
6706 // Compute a style array (an array starting with a mode generation
6830 // Compute a style array (an array starting with a mode generation
6707 // -- for invalidation -- followed by pairs of end positions and
6831 // -- for invalidation -- followed by pairs of end positions and
6708 // style strings), which is used to highlight the tokens on the
6832 // style strings), which is used to highlight the tokens on the
6709 // line.
6833 // line.
6710 function highlightLine(cm, line, state, forceToEnd) {
6834 function highlightLine(cm, line, state, forceToEnd) {
6711 // A styles array always starts with a number identifying the
6835 // A styles array always starts with a number identifying the
6712 // mode/overlays that it is based on (for easy invalidation).
6836 // mode/overlays that it is based on (for easy invalidation).
6713 var st = [cm.state.modeGen], lineClasses = {};
6837 var st = [cm.state.modeGen], lineClasses = {};
6714 // Compute the base array of styles
6838 // Compute the base array of styles
6715 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6839 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6716 st.push(end, style);
6840 st.push(end, style);
6717 }, lineClasses, forceToEnd);
6841 }, lineClasses, forceToEnd);
6718
6842
6719 // Run overlays, adjust style array.
6843 // Run overlays, adjust style array.
6720 for (var o = 0; o < cm.state.overlays.length; ++o) {
6844 for (var o = 0; o < cm.state.overlays.length; ++o) {
6721 var overlay = cm.state.overlays[o], i = 1, at = 0;
6845 var overlay = cm.state.overlays[o], i = 1, at = 0;
6722 runMode(cm, line.text, overlay.mode, true, function(end, style) {
6846 runMode(cm, line.text, overlay.mode, true, function(end, style) {
6723 var start = i;
6847 var start = i;
6724 // Ensure there's a token end at the current position, and that i points at it
6848 // Ensure there's a token end at the current position, and that i points at it
6725 while (at < end) {
6849 while (at < end) {
6726 var i_end = st[i];
6850 var i_end = st[i];
6727 if (i_end > end)
6851 if (i_end > end)
6728 st.splice(i, 1, end, st[i+1], i_end);
6852 st.splice(i, 1, end, st[i+1], i_end);
6729 i += 2;
6853 i += 2;
6730 at = Math.min(end, i_end);
6854 at = Math.min(end, i_end);
6731 }
6855 }
6732 if (!style) return;
6856 if (!style) return;
6733 if (overlay.opaque) {
6857 if (overlay.opaque) {
6734 st.splice(start, i - start, end, "cm-overlay " + style);
6858 st.splice(start, i - start, end, "cm-overlay " + style);
6735 i = start + 2;
6859 i = start + 2;
6736 } else {
6860 } else {
6737 for (; start < i; start += 2) {
6861 for (; start < i; start += 2) {
6738 var cur = st[start+1];
6862 var cur = st[start+1];
6739 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6863 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6740 }
6864 }
6741 }
6865 }
6742 }, lineClasses);
6866 }, lineClasses);
6743 }
6867 }
6744
6868
6745 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6869 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6746 }
6870 }
6747
6871
6748 function getLineStyles(cm, line, updateFrontier) {
6872 function getLineStyles(cm, line, updateFrontier) {
6749 if (!line.styles || line.styles[0] != cm.state.modeGen) {
6873 if (!line.styles || line.styles[0] != cm.state.modeGen) {
6750 var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
6874 var state = getStateBefore(cm, lineNo(line));
6875 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
6876 line.stateAfter = state;
6751 line.styles = result.styles;
6877 line.styles = result.styles;
6752 if (result.classes) line.styleClasses = result.classes;
6878 if (result.classes) line.styleClasses = result.classes;
6753 else if (line.styleClasses) line.styleClasses = null;
6879 else if (line.styleClasses) line.styleClasses = null;
6754 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6880 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6755 }
6881 }
6756 return line.styles;
6882 return line.styles;
6757 }
6883 }
6758
6884
6759 // Lightweight form of highlight -- proceed over this line and
6885 // Lightweight form of highlight -- proceed over this line and
6760 // update state, but don't save a style array. Used for lines that
6886 // update state, but don't save a style array. Used for lines that
6761 // aren't currently visible.
6887 // aren't currently visible.
6762 function processLine(cm, text, state, startAt) {
6888 function processLine(cm, text, state, startAt) {
6763 var mode = cm.doc.mode;
6889 var mode = cm.doc.mode;
6764 var stream = new StringStream(text, cm.options.tabSize);
6890 var stream = new StringStream(text, cm.options.tabSize);
6765 stream.start = stream.pos = startAt || 0;
6891 stream.start = stream.pos = startAt || 0;
6766 if (text == "") callBlankLine(mode, state);
6892 if (text == "") callBlankLine(mode, state);
6767 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
6893 while (!stream.eol()) {
6768 readToken(mode, stream, state);
6894 readToken(mode, stream, state);
6769 stream.start = stream.pos;
6895 stream.start = stream.pos;
6770 }
6896 }
6771 }
6897 }
6772
6898
6773 // Convert a style as returned by a mode (either null, or a string
6899 // Convert a style as returned by a mode (either null, or a string
6774 // containing one or more styles) to a CSS style. This is cached,
6900 // containing one or more styles) to a CSS style. This is cached,
6775 // and also looks for line-wide styles.
6901 // and also looks for line-wide styles.
6776 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6902 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6777 function interpretTokenStyle(style, options) {
6903 function interpretTokenStyle(style, options) {
6778 if (!style || /^\s*$/.test(style)) return null;
6904 if (!style || /^\s*$/.test(style)) return null;
6779 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6905 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6780 return cache[style] ||
6906 return cache[style] ||
6781 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6907 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6782 }
6908 }
6783
6909
6784 // Render the DOM representation of the text of a line. Also builds
6910 // Render the DOM representation of the text of a line. Also builds
6785 // up a 'line map', which points at the DOM nodes that represent
6911 // up a 'line map', which points at the DOM nodes that represent
6786 // specific stretches of text, and is used by the measuring code.
6912 // specific stretches of text, and is used by the measuring code.
6787 // The returned object contains the DOM node, this map, and
6913 // The returned object contains the DOM node, this map, and
6788 // information about line-wide styles that were set by the mode.
6914 // information about line-wide styles that were set by the mode.
6789 function buildLineContent(cm, lineView) {
6915 function buildLineContent(cm, lineView) {
6790 // The padding-right forces the element to have a 'border', which
6916 // The padding-right forces the element to have a 'border', which
6791 // is needed on Webkit to be able to get line-level bounding
6917 // is needed on Webkit to be able to get line-level bounding
6792 // rectangles for it (in measureChar).
6918 // rectangles for it (in measureChar).
6793 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6919 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6794 var builder = {pre: elt("pre", [content]), content: content,
6920 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
6795 col: 0, pos: 0, cm: cm,
6921 col: 0, pos: 0, cm: cm,
6796 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6922 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6797 lineView.measure = {};
6923 lineView.measure = {};
6798
6924
6799 // Iterate over the logical lines that make up this visual line.
6925 // Iterate over the logical lines that make up this visual line.
6800 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6926 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6801 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6927 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6802 builder.pos = 0;
6928 builder.pos = 0;
6803 builder.addToken = buildToken;
6929 builder.addToken = buildToken;
6804 // Optionally wire in some hacks into the token-rendering
6930 // Optionally wire in some hacks into the token-rendering
6805 // algorithm, to deal with browser quirks.
6931 // algorithm, to deal with browser quirks.
6806 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6932 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6807 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6933 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6808 builder.map = [];
6934 builder.map = [];
6809 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6935 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6810 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6936 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6811 if (line.styleClasses) {
6937 if (line.styleClasses) {
6812 if (line.styleClasses.bgClass)
6938 if (line.styleClasses.bgClass)
6813 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6939 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6814 if (line.styleClasses.textClass)
6940 if (line.styleClasses.textClass)
6815 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6941 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6816 }
6942 }
6817
6943
6818 // Ensure at least a single node is present, for measuring.
6944 // Ensure at least a single node is present, for measuring.
6819 if (builder.map.length == 0)
6945 if (builder.map.length == 0)
6820 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6946 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6821
6947
6822 // Store the map and a cache object for the current logical line
6948 // Store the map and a cache object for the current logical line
6823 if (i == 0) {
6949 if (i == 0) {
6824 lineView.measure.map = builder.map;
6950 lineView.measure.map = builder.map;
6825 lineView.measure.cache = {};
6951 lineView.measure.cache = {};
6826 } else {
6952 } else {
6827 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6953 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6828 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6954 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6829 }
6955 }
6830 }
6956 }
6831
6957
6832 // See issue #2901
6958 // See issue #2901
6833 if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6959 if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6834 builder.content.className = "cm-tab-wrap-hack";
6960 builder.content.className = "cm-tab-wrap-hack";
6835
6961
6836 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6962 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6837 if (builder.pre.className)
6963 if (builder.pre.className)
6838 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6964 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6839
6965
6840 return builder;
6966 return builder;
6841 }
6967 }
6842
6968
6843 function defaultSpecialCharPlaceholder(ch) {
6969 function defaultSpecialCharPlaceholder(ch) {
6844 var token = elt("span", "\u2022", "cm-invalidchar");
6970 var token = elt("span", "\u2022", "cm-invalidchar");
6845 token.title = "\\u" + ch.charCodeAt(0).toString(16);
6971 token.title = "\\u" + ch.charCodeAt(0).toString(16);
6846 token.setAttribute("aria-label", token.title);
6972 token.setAttribute("aria-label", token.title);
6847 return token;
6973 return token;
6848 }
6974 }
6849
6975
6850 // Build up the DOM representation for a single token, and add it to
6976 // Build up the DOM representation for a single token, and add it to
6851 // the line map. Takes care to render special characters separately.
6977 // the line map. Takes care to render special characters separately.
6852 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6978 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6853 if (!text) return;
6979 if (!text) return;
6854 var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6980 var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6855 var special = builder.cm.state.specialChars, mustWrap = false;
6981 var special = builder.cm.state.specialChars, mustWrap = false;
6856 if (!special.test(text)) {
6982 if (!special.test(text)) {
6857 builder.col += text.length;
6983 builder.col += text.length;
6858 var content = document.createTextNode(displayText);
6984 var content = document.createTextNode(displayText);
6859 builder.map.push(builder.pos, builder.pos + text.length, content);
6985 builder.map.push(builder.pos, builder.pos + text.length, content);
6860 if (ie && ie_version < 9) mustWrap = true;
6986 if (ie && ie_version < 9) mustWrap = true;
6861 builder.pos += text.length;
6987 builder.pos += text.length;
6862 } else {
6988 } else {
6863 var content = document.createDocumentFragment(), pos = 0;
6989 var content = document.createDocumentFragment(), pos = 0;
6864 while (true) {
6990 while (true) {
6865 special.lastIndex = pos;
6991 special.lastIndex = pos;
6866 var m = special.exec(text);
6992 var m = special.exec(text);
6867 var skipped = m ? m.index - pos : text.length - pos;
6993 var skipped = m ? m.index - pos : text.length - pos;
6868 if (skipped) {
6994 if (skipped) {
6869 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6995 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6870 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6996 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6871 else content.appendChild(txt);
6997 else content.appendChild(txt);
6872 builder.map.push(builder.pos, builder.pos + skipped, txt);
6998 builder.map.push(builder.pos, builder.pos + skipped, txt);
6873 builder.col += skipped;
6999 builder.col += skipped;
6874 builder.pos += skipped;
7000 builder.pos += skipped;
6875 }
7001 }
6876 if (!m) break;
7002 if (!m) break;
6877 pos += skipped + 1;
7003 pos += skipped + 1;
6878 if (m[0] == "\t") {
7004 if (m[0] == "\t") {
6879 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
7005 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
6880 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
7006 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
6881 txt.setAttribute("role", "presentation");
7007 txt.setAttribute("role", "presentation");
6882 txt.setAttribute("cm-text", "\t");
7008 txt.setAttribute("cm-text", "\t");
6883 builder.col += tabWidth;
7009 builder.col += tabWidth;
7010 } else if (m[0] == "\r" || m[0] == "\n") {
7011 var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
7012 txt.setAttribute("cm-text", m[0]);
7013 builder.col += 1;
6884 } else {
7014 } else {
6885 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
7015 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
6886 txt.setAttribute("cm-text", m[0]);
7016 txt.setAttribute("cm-text", m[0]);
6887 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
7017 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6888 else content.appendChild(txt);
7018 else content.appendChild(txt);
6889 builder.col += 1;
7019 builder.col += 1;
6890 }
7020 }
6891 builder.map.push(builder.pos, builder.pos + 1, txt);
7021 builder.map.push(builder.pos, builder.pos + 1, txt);
6892 builder.pos++;
7022 builder.pos++;
6893 }
7023 }
6894 }
7024 }
6895 if (style || startStyle || endStyle || mustWrap || css) {
7025 if (style || startStyle || endStyle || mustWrap || css) {
6896 var fullStyle = style || "";
7026 var fullStyle = style || "";
6897 if (startStyle) fullStyle += startStyle;
7027 if (startStyle) fullStyle += startStyle;
6898 if (endStyle) fullStyle += endStyle;
7028 if (endStyle) fullStyle += endStyle;
6899 var token = elt("span", [content], fullStyle, css);
7029 var token = elt("span", [content], fullStyle, css);
6900 if (title) token.title = title;
7030 if (title) token.title = title;
6901 return builder.content.appendChild(token);
7031 return builder.content.appendChild(token);
6902 }
7032 }
6903 builder.content.appendChild(content);
7033 builder.content.appendChild(content);
6904 }
7034 }
6905
7035
6906 function splitSpaces(old) {
7036 function splitSpaces(old) {
6907 var out = " ";
7037 var out = " ";
6908 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
7038 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
6909 out += " ";
7039 out += " ";
6910 return out;
7040 return out;
6911 }
7041 }
6912
7042
6913 // Work around nonsense dimensions being reported for stretches of
7043 // Work around nonsense dimensions being reported for stretches of
6914 // right-to-left text.
7044 // right-to-left text.
6915 function buildTokenBadBidi(inner, order) {
7045 function buildTokenBadBidi(inner, order) {
6916 return function(builder, text, style, startStyle, endStyle, title, css) {
7046 return function(builder, text, style, startStyle, endStyle, title, css) {
6917 style = style ? style + " cm-force-border" : "cm-force-border";
7047 style = style ? style + " cm-force-border" : "cm-force-border";
6918 var start = builder.pos, end = start + text.length;
7048 var start = builder.pos, end = start + text.length;
6919 for (;;) {
7049 for (;;) {
6920 // Find the part that overlaps with the start of this text
7050 // Find the part that overlaps with the start of this text
6921 for (var i = 0; i < order.length; i++) {
7051 for (var i = 0; i < order.length; i++) {
6922 var part = order[i];
7052 var part = order[i];
6923 if (part.to > start && part.from <= start) break;
7053 if (part.to > start && part.from <= start) break;
6924 }
7054 }
6925 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
7055 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
6926 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
7056 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
6927 startStyle = null;
7057 startStyle = null;
6928 text = text.slice(part.to - start);
7058 text = text.slice(part.to - start);
6929 start = part.to;
7059 start = part.to;
6930 }
7060 }
6931 };
7061 };
6932 }
7062 }
6933
7063
6934 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
7064 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6935 var widget = !ignoreWidget && marker.widgetNode;
7065 var widget = !ignoreWidget && marker.widgetNode;
6936 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
7066 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
6937 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
7067 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
6938 if (!widget)
7068 if (!widget)
6939 widget = builder.content.appendChild(document.createElement("span"));
7069 widget = builder.content.appendChild(document.createElement("span"));
6940 widget.setAttribute("cm-marker", marker.id);
7070 widget.setAttribute("cm-marker", marker.id);
6941 }
7071 }
6942 if (widget) {
7072 if (widget) {
6943 builder.cm.display.input.setUneditable(widget);
7073 builder.cm.display.input.setUneditable(widget);
6944 builder.content.appendChild(widget);
7074 builder.content.appendChild(widget);
6945 }
7075 }
6946 builder.pos += size;
7076 builder.pos += size;
6947 }
7077 }
6948
7078
6949 // Outputs a number of spans to make up a line, taking highlighting
7079 // Outputs a number of spans to make up a line, taking highlighting
6950 // and marked text into account.
7080 // and marked text into account.
6951 function insertLineContent(line, builder, styles) {
7081 function insertLineContent(line, builder, styles) {
6952 var spans = line.markedSpans, allText = line.text, at = 0;
7082 var spans = line.markedSpans, allText = line.text, at = 0;
6953 if (!spans) {
7083 if (!spans) {
6954 for (var i = 1; i < styles.length; i+=2)
7084 for (var i = 1; i < styles.length; i+=2)
6955 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
7085 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
6956 return;
7086 return;
6957 }
7087 }
6958
7088
6959 var len = allText.length, pos = 0, i = 1, text = "", style, css;
7089 var len = allText.length, pos = 0, i = 1, text = "", style, css;
6960 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
7090 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
6961 for (;;) {
7091 for (;;) {
6962 if (nextChange == pos) { // Update current marker set
7092 if (nextChange == pos) { // Update current marker set
6963 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
7093 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
6964 collapsed = null; nextChange = Infinity;
7094 collapsed = null; nextChange = Infinity;
6965 var foundBookmarks = [];
7095 var foundBookmarks = [], endStyles
6966 for (var j = 0; j < spans.length; ++j) {
7096 for (var j = 0; j < spans.length; ++j) {
6967 var sp = spans[j], m = sp.marker;
7097 var sp = spans[j], m = sp.marker;
6968 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
7098 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
6969 foundBookmarks.push(m);
7099 foundBookmarks.push(m);
6970 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
7100 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
6971 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7101 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
6972 nextChange = sp.to;
7102 nextChange = sp.to;
6973 spanEndStyle = "";
7103 spanEndStyle = "";
6974 }
7104 }
6975 if (m.className) spanStyle += " " + m.className;
7105 if (m.className) spanStyle += " " + m.className;
6976 if (m.css) css = m.css;
7106 if (m.css) css = (css ? css + ";" : "") + m.css;
6977 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
7107 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
6978 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
7108 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to)
6979 if (m.title && !title) title = m.title;
7109 if (m.title && !title) title = m.title;
6980 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
7110 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
6981 collapsed = sp;
7111 collapsed = sp;
6982 } else if (sp.from > pos && nextChange > sp.from) {
7112 } else if (sp.from > pos && nextChange > sp.from) {
6983 nextChange = sp.from;
7113 nextChange = sp.from;
6984 }
7114 }
6985 }
7115 }
7116 if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
7117 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]
7118
7119 if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j)
7120 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6986 if (collapsed && (collapsed.from || 0) == pos) {
7121 if (collapsed && (collapsed.from || 0) == pos) {
6987 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
7122 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
6988 collapsed.marker, collapsed.from == null);
7123 collapsed.marker, collapsed.from == null);
6989 if (collapsed.to == null) return;
7124 if (collapsed.to == null) return;
6990 if (collapsed.to == pos) collapsed = false;
7125 if (collapsed.to == pos) collapsed = false;
6991 }
7126 }
6992 if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
6993 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6994 }
7127 }
6995 if (pos >= len) break;
7128 if (pos >= len) break;
6996
7129
6997 var upto = Math.min(len, nextChange);
7130 var upto = Math.min(len, nextChange);
6998 while (true) {
7131 while (true) {
6999 if (text) {
7132 if (text) {
7000 var end = pos + text.length;
7133 var end = pos + text.length;
7001 if (!collapsed) {
7134 if (!collapsed) {
7002 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7135 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7003 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
7136 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
7004 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
7137 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
7005 }
7138 }
7006 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7139 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7007 pos = end;
7140 pos = end;
7008 spanStartStyle = "";
7141 spanStartStyle = "";
7009 }
7142 }
7010 text = allText.slice(at, at = styles[i++]);
7143 text = allText.slice(at, at = styles[i++]);
7011 style = interpretTokenStyle(styles[i++], builder.cm.options);
7144 style = interpretTokenStyle(styles[i++], builder.cm.options);
7012 }
7145 }
7013 }
7146 }
7014 }
7147 }
7015
7148
7016 // DOCUMENT DATA STRUCTURE
7149 // DOCUMENT DATA STRUCTURE
7017
7150
7018 // By default, updates that start and end at the beginning of a line
7151 // By default, updates that start and end at the beginning of a line
7019 // are treated specially, in order to make the association of line
7152 // are treated specially, in order to make the association of line
7020 // widgets and marker elements with the text behave more intuitive.
7153 // widgets and marker elements with the text behave more intuitive.
7021 function isWholeLineUpdate(doc, change) {
7154 function isWholeLineUpdate(doc, change) {
7022 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7155 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7023 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7156 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7024 }
7157 }
7025
7158
7026 // Perform a change on the document data structure.
7159 // Perform a change on the document data structure.
7027 function updateDoc(doc, change, markedSpans, estimateHeight) {
7160 function updateDoc(doc, change, markedSpans, estimateHeight) {
7028 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7161 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7029 function update(line, text, spans) {
7162 function update(line, text, spans) {
7030 updateLine(line, text, spans, estimateHeight);
7163 updateLine(line, text, spans, estimateHeight);
7031 signalLater(line, "change", line, change);
7164 signalLater(line, "change", line, change);
7032 }
7165 }
7033 function linesFor(start, end) {
7166 function linesFor(start, end) {
7034 for (var i = start, result = []; i < end; ++i)
7167 for (var i = start, result = []; i < end; ++i)
7035 result.push(new Line(text[i], spansFor(i), estimateHeight));
7168 result.push(new Line(text[i], spansFor(i), estimateHeight));
7036 return result;
7169 return result;
7037 }
7170 }
7038
7171
7039 var from = change.from, to = change.to, text = change.text;
7172 var from = change.from, to = change.to, text = change.text;
7040 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7173 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7041 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
7174 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
7042
7175
7043 // Adjust the line structure
7176 // Adjust the line structure
7044 if (change.full) {
7177 if (change.full) {
7045 doc.insert(0, linesFor(0, text.length));
7178 doc.insert(0, linesFor(0, text.length));
7046 doc.remove(text.length, doc.size - text.length);
7179 doc.remove(text.length, doc.size - text.length);
7047 } else if (isWholeLineUpdate(doc, change)) {
7180 } else if (isWholeLineUpdate(doc, change)) {
7048 // This is a whole-line replace. Treated specially to make
7181 // This is a whole-line replace. Treated specially to make
7049 // sure line objects move the way they are supposed to.
7182 // sure line objects move the way they are supposed to.
7050 var added = linesFor(0, text.length - 1);
7183 var added = linesFor(0, text.length - 1);
7051 update(lastLine, lastLine.text, lastSpans);
7184 update(lastLine, lastLine.text, lastSpans);
7052 if (nlines) doc.remove(from.line, nlines);
7185 if (nlines) doc.remove(from.line, nlines);
7053 if (added.length) doc.insert(from.line, added);
7186 if (added.length) doc.insert(from.line, added);
7054 } else if (firstLine == lastLine) {
7187 } else if (firstLine == lastLine) {
7055 if (text.length == 1) {
7188 if (text.length == 1) {
7056 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7189 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7057 } else {
7190 } else {
7058 var added = linesFor(1, text.length - 1);
7191 var added = linesFor(1, text.length - 1);
7059 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
7192 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
7060 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7193 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7061 doc.insert(from.line + 1, added);
7194 doc.insert(from.line + 1, added);
7062 }
7195 }
7063 } else if (text.length == 1) {
7196 } else if (text.length == 1) {
7064 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
7197 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
7065 doc.remove(from.line + 1, nlines);
7198 doc.remove(from.line + 1, nlines);
7066 } else {
7199 } else {
7067 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7200 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7068 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7201 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7069 var added = linesFor(1, text.length - 1);
7202 var added = linesFor(1, text.length - 1);
7070 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7203 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7071 doc.insert(from.line + 1, added);
7204 doc.insert(from.line + 1, added);
7072 }
7205 }
7073
7206
7074 signalLater(doc, "change", doc, change);
7207 signalLater(doc, "change", doc, change);
7075 }
7208 }
7076
7209
7077 // The document is represented as a BTree consisting of leaves, with
7210 // The document is represented as a BTree consisting of leaves, with
7078 // chunk of lines in them, and branches, with up to ten leaves or
7211 // chunk of lines in them, and branches, with up to ten leaves or
7079 // other branch nodes below them. The top node is always a branch
7212 // other branch nodes below them. The top node is always a branch
7080 // node, and is the document object itself (meaning it has
7213 // node, and is the document object itself (meaning it has
7081 // additional methods and properties).
7214 // additional methods and properties).
7082 //
7215 //
7083 // All nodes have parent links. The tree is used both to go from
7216 // All nodes have parent links. The tree is used both to go from
7084 // line numbers to line objects, and to go from objects to numbers.
7217 // line numbers to line objects, and to go from objects to numbers.
7085 // It also indexes by height, and is used to convert between height
7218 // It also indexes by height, and is used to convert between height
7086 // and line object, and to find the total height of the document.
7219 // and line object, and to find the total height of the document.
7087 //
7220 //
7088 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7221 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7089
7222
7090 function LeafChunk(lines) {
7223 function LeafChunk(lines) {
7091 this.lines = lines;
7224 this.lines = lines;
7092 this.parent = null;
7225 this.parent = null;
7093 for (var i = 0, height = 0; i < lines.length; ++i) {
7226 for (var i = 0, height = 0; i < lines.length; ++i) {
7094 lines[i].parent = this;
7227 lines[i].parent = this;
7095 height += lines[i].height;
7228 height += lines[i].height;
7096 }
7229 }
7097 this.height = height;
7230 this.height = height;
7098 }
7231 }
7099
7232
7100 LeafChunk.prototype = {
7233 LeafChunk.prototype = {
7101 chunkSize: function() { return this.lines.length; },
7234 chunkSize: function() { return this.lines.length; },
7102 // Remove the n lines at offset 'at'.
7235 // Remove the n lines at offset 'at'.
7103 removeInner: function(at, n) {
7236 removeInner: function(at, n) {
7104 for (var i = at, e = at + n; i < e; ++i) {
7237 for (var i = at, e = at + n; i < e; ++i) {
7105 var line = this.lines[i];
7238 var line = this.lines[i];
7106 this.height -= line.height;
7239 this.height -= line.height;
7107 cleanUpLine(line);
7240 cleanUpLine(line);
7108 signalLater(line, "delete");
7241 signalLater(line, "delete");
7109 }
7242 }
7110 this.lines.splice(at, n);
7243 this.lines.splice(at, n);
7111 },
7244 },
7112 // Helper used to collapse a small branch into a single leaf.
7245 // Helper used to collapse a small branch into a single leaf.
7113 collapse: function(lines) {
7246 collapse: function(lines) {
7114 lines.push.apply(lines, this.lines);
7247 lines.push.apply(lines, this.lines);
7115 },
7248 },
7116 // Insert the given array of lines at offset 'at', count them as
7249 // Insert the given array of lines at offset 'at', count them as
7117 // having the given height.
7250 // having the given height.
7118 insertInner: function(at, lines, height) {
7251 insertInner: function(at, lines, height) {
7119 this.height += height;
7252 this.height += height;
7120 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7253 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7121 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7254 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7122 },
7255 },
7123 // Used to iterate over a part of the tree.
7256 // Used to iterate over a part of the tree.
7124 iterN: function(at, n, op) {
7257 iterN: function(at, n, op) {
7125 for (var e = at + n; at < e; ++at)
7258 for (var e = at + n; at < e; ++at)
7126 if (op(this.lines[at])) return true;
7259 if (op(this.lines[at])) return true;
7127 }
7260 }
7128 };
7261 };
7129
7262
7130 function BranchChunk(children) {
7263 function BranchChunk(children) {
7131 this.children = children;
7264 this.children = children;
7132 var size = 0, height = 0;
7265 var size = 0, height = 0;
7133 for (var i = 0; i < children.length; ++i) {
7266 for (var i = 0; i < children.length; ++i) {
7134 var ch = children[i];
7267 var ch = children[i];
7135 size += ch.chunkSize(); height += ch.height;
7268 size += ch.chunkSize(); height += ch.height;
7136 ch.parent = this;
7269 ch.parent = this;
7137 }
7270 }
7138 this.size = size;
7271 this.size = size;
7139 this.height = height;
7272 this.height = height;
7140 this.parent = null;
7273 this.parent = null;
7141 }
7274 }
7142
7275
7143 BranchChunk.prototype = {
7276 BranchChunk.prototype = {
7144 chunkSize: function() { return this.size; },
7277 chunkSize: function() { return this.size; },
7145 removeInner: function(at, n) {
7278 removeInner: function(at, n) {
7146 this.size -= n;
7279 this.size -= n;
7147 for (var i = 0; i < this.children.length; ++i) {
7280 for (var i = 0; i < this.children.length; ++i) {
7148 var child = this.children[i], sz = child.chunkSize();
7281 var child = this.children[i], sz = child.chunkSize();
7149 if (at < sz) {
7282 if (at < sz) {
7150 var rm = Math.min(n, sz - at), oldHeight = child.height;
7283 var rm = Math.min(n, sz - at), oldHeight = child.height;
7151 child.removeInner(at, rm);
7284 child.removeInner(at, rm);
7152 this.height -= oldHeight - child.height;
7285 this.height -= oldHeight - child.height;
7153 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7286 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7154 if ((n -= rm) == 0) break;
7287 if ((n -= rm) == 0) break;
7155 at = 0;
7288 at = 0;
7156 } else at -= sz;
7289 } else at -= sz;
7157 }
7290 }
7158 // If the result is smaller than 25 lines, ensure that it is a
7291 // If the result is smaller than 25 lines, ensure that it is a
7159 // single leaf node.
7292 // single leaf node.
7160 if (this.size - n < 25 &&
7293 if (this.size - n < 25 &&
7161 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7294 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7162 var lines = [];
7295 var lines = [];
7163 this.collapse(lines);
7296 this.collapse(lines);
7164 this.children = [new LeafChunk(lines)];
7297 this.children = [new LeafChunk(lines)];
7165 this.children[0].parent = this;
7298 this.children[0].parent = this;
7166 }
7299 }
7167 },
7300 },
7168 collapse: function(lines) {
7301 collapse: function(lines) {
7169 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7302 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7170 },
7303 },
7171 insertInner: function(at, lines, height) {
7304 insertInner: function(at, lines, height) {
7172 this.size += lines.length;
7305 this.size += lines.length;
7173 this.height += height;
7306 this.height += height;
7174 for (var i = 0; i < this.children.length; ++i) {
7307 for (var i = 0; i < this.children.length; ++i) {
7175 var child = this.children[i], sz = child.chunkSize();
7308 var child = this.children[i], sz = child.chunkSize();
7176 if (at <= sz) {
7309 if (at <= sz) {
7177 child.insertInner(at, lines, height);
7310 child.insertInner(at, lines, height);
7178 if (child.lines && child.lines.length > 50) {
7311 if (child.lines && child.lines.length > 50) {
7179 while (child.lines.length > 50) {
7312 while (child.lines.length > 50) {
7180 var spilled = child.lines.splice(child.lines.length - 25, 25);
7313 var spilled = child.lines.splice(child.lines.length - 25, 25);
7181 var newleaf = new LeafChunk(spilled);
7314 var newleaf = new LeafChunk(spilled);
7182 child.height -= newleaf.height;
7315 child.height -= newleaf.height;
7183 this.children.splice(i + 1, 0, newleaf);
7316 this.children.splice(i + 1, 0, newleaf);
7184 newleaf.parent = this;
7317 newleaf.parent = this;
7185 }
7318 }
7186 this.maybeSpill();
7319 this.maybeSpill();
7187 }
7320 }
7188 break;
7321 break;
7189 }
7322 }
7190 at -= sz;
7323 at -= sz;
7191 }
7324 }
7192 },
7325 },
7193 // When a node has grown, check whether it should be split.
7326 // When a node has grown, check whether it should be split.
7194 maybeSpill: function() {
7327 maybeSpill: function() {
7195 if (this.children.length <= 10) return;
7328 if (this.children.length <= 10) return;
7196 var me = this;
7329 var me = this;
7197 do {
7330 do {
7198 var spilled = me.children.splice(me.children.length - 5, 5);
7331 var spilled = me.children.splice(me.children.length - 5, 5);
7199 var sibling = new BranchChunk(spilled);
7332 var sibling = new BranchChunk(spilled);
7200 if (!me.parent) { // Become the parent node
7333 if (!me.parent) { // Become the parent node
7201 var copy = new BranchChunk(me.children);
7334 var copy = new BranchChunk(me.children);
7202 copy.parent = me;
7335 copy.parent = me;
7203 me.children = [copy, sibling];
7336 me.children = [copy, sibling];
7204 me = copy;
7337 me = copy;
7205 } else {
7338 } else {
7206 me.size -= sibling.size;
7339 me.size -= sibling.size;
7207 me.height -= sibling.height;
7340 me.height -= sibling.height;
7208 var myIndex = indexOf(me.parent.children, me);
7341 var myIndex = indexOf(me.parent.children, me);
7209 me.parent.children.splice(myIndex + 1, 0, sibling);
7342 me.parent.children.splice(myIndex + 1, 0, sibling);
7210 }
7343 }
7211 sibling.parent = me.parent;
7344 sibling.parent = me.parent;
7212 } while (me.children.length > 10);
7345 } while (me.children.length > 10);
7213 me.parent.maybeSpill();
7346 me.parent.maybeSpill();
7214 },
7347 },
7215 iterN: function(at, n, op) {
7348 iterN: function(at, n, op) {
7216 for (var i = 0; i < this.children.length; ++i) {
7349 for (var i = 0; i < this.children.length; ++i) {
7217 var child = this.children[i], sz = child.chunkSize();
7350 var child = this.children[i], sz = child.chunkSize();
7218 if (at < sz) {
7351 if (at < sz) {
7219 var used = Math.min(n, sz - at);
7352 var used = Math.min(n, sz - at);
7220 if (child.iterN(at, used, op)) return true;
7353 if (child.iterN(at, used, op)) return true;
7221 if ((n -= used) == 0) break;
7354 if ((n -= used) == 0) break;
7222 at = 0;
7355 at = 0;
7223 } else at -= sz;
7356 } else at -= sz;
7224 }
7357 }
7225 }
7358 }
7226 };
7359 };
7227
7360
7228 var nextDocId = 0;
7361 var nextDocId = 0;
7229 var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
7362 var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
7230 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
7363 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
7231 if (firstLine == null) firstLine = 0;
7364 if (firstLine == null) firstLine = 0;
7232
7365
7233 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7366 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7234 this.first = firstLine;
7367 this.first = firstLine;
7235 this.scrollTop = this.scrollLeft = 0;
7368 this.scrollTop = this.scrollLeft = 0;
7236 this.cantEdit = false;
7369 this.cantEdit = false;
7237 this.cleanGeneration = 1;
7370 this.cleanGeneration = 1;
7238 this.frontier = firstLine;
7371 this.frontier = firstLine;
7239 var start = Pos(firstLine, 0);
7372 var start = Pos(firstLine, 0);
7240 this.sel = simpleSelection(start);
7373 this.sel = simpleSelection(start);
7241 this.history = new History(null);
7374 this.history = new History(null);
7242 this.id = ++nextDocId;
7375 this.id = ++nextDocId;
7243 this.modeOption = mode;
7376 this.modeOption = mode;
7244
7377 this.lineSep = lineSep;
7245 if (typeof text == "string") text = splitLines(text);
7378 this.extend = false;
7379
7380 if (typeof text == "string") text = this.splitLines(text);
7246 updateDoc(this, {from: start, to: start, text: text});
7381 updateDoc(this, {from: start, to: start, text: text});
7247 setSelection(this, simpleSelection(start), sel_dontScroll);
7382 setSelection(this, simpleSelection(start), sel_dontScroll);
7248 };
7383 };
7249
7384
7250 Doc.prototype = createObj(BranchChunk.prototype, {
7385 Doc.prototype = createObj(BranchChunk.prototype, {
7251 constructor: Doc,
7386 constructor: Doc,
7252 // Iterate over the document. Supports two forms -- with only one
7387 // Iterate over the document. Supports two forms -- with only one
7253 // argument, it calls that for each line in the document. With
7388 // argument, it calls that for each line in the document. With
7254 // three, it iterates over the range given by the first two (with
7389 // three, it iterates over the range given by the first two (with
7255 // the second being non-inclusive).
7390 // the second being non-inclusive).
7256 iter: function(from, to, op) {
7391 iter: function(from, to, op) {
7257 if (op) this.iterN(from - this.first, to - from, op);
7392 if (op) this.iterN(from - this.first, to - from, op);
7258 else this.iterN(this.first, this.first + this.size, from);
7393 else this.iterN(this.first, this.first + this.size, from);
7259 },
7394 },
7260
7395
7261 // Non-public interface for adding and removing lines.
7396 // Non-public interface for adding and removing lines.
7262 insert: function(at, lines) {
7397 insert: function(at, lines) {
7263 var height = 0;
7398 var height = 0;
7264 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7399 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7265 this.insertInner(at - this.first, lines, height);
7400 this.insertInner(at - this.first, lines, height);
7266 },
7401 },
7267 remove: function(at, n) { this.removeInner(at - this.first, n); },
7402 remove: function(at, n) { this.removeInner(at - this.first, n); },
7268
7403
7269 // From here, the methods are part of the public interface. Most
7404 // From here, the methods are part of the public interface. Most
7270 // are also available from CodeMirror (editor) instances.
7405 // are also available from CodeMirror (editor) instances.
7271
7406
7272 getValue: function(lineSep) {
7407 getValue: function(lineSep) {
7273 var lines = getLines(this, this.first, this.first + this.size);
7408 var lines = getLines(this, this.first, this.first + this.size);
7274 if (lineSep === false) return lines;
7409 if (lineSep === false) return lines;
7275 return lines.join(lineSep || "\n");
7410 return lines.join(lineSep || this.lineSeparator());
7276 },
7411 },
7277 setValue: docMethodOp(function(code) {
7412 setValue: docMethodOp(function(code) {
7278 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7413 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7279 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7414 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7280 text: splitLines(code), origin: "setValue", full: true}, true);
7415 text: this.splitLines(code), origin: "setValue", full: true}, true);
7281 setSelection(this, simpleSelection(top));
7416 setSelection(this, simpleSelection(top));
7282 }),
7417 }),
7283 replaceRange: function(code, from, to, origin) {
7418 replaceRange: function(code, from, to, origin) {
7284 from = clipPos(this, from);
7419 from = clipPos(this, from);
7285 to = to ? clipPos(this, to) : from;
7420 to = to ? clipPos(this, to) : from;
7286 replaceRange(this, code, from, to, origin);
7421 replaceRange(this, code, from, to, origin);
7287 },
7422 },
7288 getRange: function(from, to, lineSep) {
7423 getRange: function(from, to, lineSep) {
7289 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7424 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7290 if (lineSep === false) return lines;
7425 if (lineSep === false) return lines;
7291 return lines.join(lineSep || "\n");
7426 return lines.join(lineSep || this.lineSeparator());
7292 },
7427 },
7293
7428
7294 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7429 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7295
7430
7296 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7431 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7297 getLineNumber: function(line) {return lineNo(line);},
7432 getLineNumber: function(line) {return lineNo(line);},
7298
7433
7299 getLineHandleVisualStart: function(line) {
7434 getLineHandleVisualStart: function(line) {
7300 if (typeof line == "number") line = getLine(this, line);
7435 if (typeof line == "number") line = getLine(this, line);
7301 return visualLine(line);
7436 return visualLine(line);
7302 },
7437 },
7303
7438
7304 lineCount: function() {return this.size;},
7439 lineCount: function() {return this.size;},
7305 firstLine: function() {return this.first;},
7440 firstLine: function() {return this.first;},
7306 lastLine: function() {return this.first + this.size - 1;},
7441 lastLine: function() {return this.first + this.size - 1;},
7307
7442
7308 clipPos: function(pos) {return clipPos(this, pos);},
7443 clipPos: function(pos) {return clipPos(this, pos);},
7309
7444
7310 getCursor: function(start) {
7445 getCursor: function(start) {
7311 var range = this.sel.primary(), pos;
7446 var range = this.sel.primary(), pos;
7312 if (start == null || start == "head") pos = range.head;
7447 if (start == null || start == "head") pos = range.head;
7313 else if (start == "anchor") pos = range.anchor;
7448 else if (start == "anchor") pos = range.anchor;
7314 else if (start == "end" || start == "to" || start === false) pos = range.to();
7449 else if (start == "end" || start == "to" || start === false) pos = range.to();
7315 else pos = range.from();
7450 else pos = range.from();
7316 return pos;
7451 return pos;
7317 },
7452 },
7318 listSelections: function() { return this.sel.ranges; },
7453 listSelections: function() { return this.sel.ranges; },
7319 somethingSelected: function() {return this.sel.somethingSelected();},
7454 somethingSelected: function() {return this.sel.somethingSelected();},
7320
7455
7321 setCursor: docMethodOp(function(line, ch, options) {
7456 setCursor: docMethodOp(function(line, ch, options) {
7322 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7457 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7323 }),
7458 }),
7324 setSelection: docMethodOp(function(anchor, head, options) {
7459 setSelection: docMethodOp(function(anchor, head, options) {
7325 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7460 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7326 }),
7461 }),
7327 extendSelection: docMethodOp(function(head, other, options) {
7462 extendSelection: docMethodOp(function(head, other, options) {
7328 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7463 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7329 }),
7464 }),
7330 extendSelections: docMethodOp(function(heads, options) {
7465 extendSelections: docMethodOp(function(heads, options) {
7331 extendSelections(this, clipPosArray(this, heads, options));
7466 extendSelections(this, clipPosArray(this, heads), options);
7332 }),
7467 }),
7333 extendSelectionsBy: docMethodOp(function(f, options) {
7468 extendSelectionsBy: docMethodOp(function(f, options) {
7334 extendSelections(this, map(this.sel.ranges, f), options);
7469 var heads = map(this.sel.ranges, f);
7470 extendSelections(this, clipPosArray(this, heads), options);
7335 }),
7471 }),
7336 setSelections: docMethodOp(function(ranges, primary, options) {
7472 setSelections: docMethodOp(function(ranges, primary, options) {
7337 if (!ranges.length) return;
7473 if (!ranges.length) return;
7338 for (var i = 0, out = []; i < ranges.length; i++)
7474 for (var i = 0, out = []; i < ranges.length; i++)
7339 out[i] = new Range(clipPos(this, ranges[i].anchor),
7475 out[i] = new Range(clipPos(this, ranges[i].anchor),
7340 clipPos(this, ranges[i].head));
7476 clipPos(this, ranges[i].head));
7341 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
7477 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
7342 setSelection(this, normalizeSelection(out, primary), options);
7478 setSelection(this, normalizeSelection(out, primary), options);
7343 }),
7479 }),
7344 addSelection: docMethodOp(function(anchor, head, options) {
7480 addSelection: docMethodOp(function(anchor, head, options) {
7345 var ranges = this.sel.ranges.slice(0);
7481 var ranges = this.sel.ranges.slice(0);
7346 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7482 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7347 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7483 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7348 }),
7484 }),
7349
7485
7350 getSelection: function(lineSep) {
7486 getSelection: function(lineSep) {
7351 var ranges = this.sel.ranges, lines;
7487 var ranges = this.sel.ranges, lines;
7352 for (var i = 0; i < ranges.length; i++) {
7488 for (var i = 0; i < ranges.length; i++) {
7353 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7489 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7354 lines = lines ? lines.concat(sel) : sel;
7490 lines = lines ? lines.concat(sel) : sel;
7355 }
7491 }
7356 if (lineSep === false) return lines;
7492 if (lineSep === false) return lines;
7357 else return lines.join(lineSep || "\n");
7493 else return lines.join(lineSep || this.lineSeparator());
7358 },
7494 },
7359 getSelections: function(lineSep) {
7495 getSelections: function(lineSep) {
7360 var parts = [], ranges = this.sel.ranges;
7496 var parts = [], ranges = this.sel.ranges;
7361 for (var i = 0; i < ranges.length; i++) {
7497 for (var i = 0; i < ranges.length; i++) {
7362 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7498 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7363 if (lineSep !== false) sel = sel.join(lineSep || "\n");
7499 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
7364 parts[i] = sel;
7500 parts[i] = sel;
7365 }
7501 }
7366 return parts;
7502 return parts;
7367 },
7503 },
7368 replaceSelection: function(code, collapse, origin) {
7504 replaceSelection: function(code, collapse, origin) {
7369 var dup = [];
7505 var dup = [];
7370 for (var i = 0; i < this.sel.ranges.length; i++)
7506 for (var i = 0; i < this.sel.ranges.length; i++)
7371 dup[i] = code;
7507 dup[i] = code;
7372 this.replaceSelections(dup, collapse, origin || "+input");
7508 this.replaceSelections(dup, collapse, origin || "+input");
7373 },
7509 },
7374 replaceSelections: docMethodOp(function(code, collapse, origin) {
7510 replaceSelections: docMethodOp(function(code, collapse, origin) {
7375 var changes = [], sel = this.sel;
7511 var changes = [], sel = this.sel;
7376 for (var i = 0; i < sel.ranges.length; i++) {
7512 for (var i = 0; i < sel.ranges.length; i++) {
7377 var range = sel.ranges[i];
7513 var range = sel.ranges[i];
7378 changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
7514 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
7379 }
7515 }
7380 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7516 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7381 for (var i = changes.length - 1; i >= 0; i--)
7517 for (var i = changes.length - 1; i >= 0; i--)
7382 makeChange(this, changes[i]);
7518 makeChange(this, changes[i]);
7383 if (newSel) setSelectionReplaceHistory(this, newSel);
7519 if (newSel) setSelectionReplaceHistory(this, newSel);
7384 else if (this.cm) ensureCursorVisible(this.cm);
7520 else if (this.cm) ensureCursorVisible(this.cm);
7385 }),
7521 }),
7386 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7522 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7387 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7523 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7388 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7524 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7389 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7525 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7390
7526
7391 setExtending: function(val) {this.extend = val;},
7527 setExtending: function(val) {this.extend = val;},
7392 getExtending: function() {return this.extend;},
7528 getExtending: function() {return this.extend;},
7393
7529
7394 historySize: function() {
7530 historySize: function() {
7395 var hist = this.history, done = 0, undone = 0;
7531 var hist = this.history, done = 0, undone = 0;
7396 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7532 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7397 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
7533 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
7398 return {undo: done, redo: undone};
7534 return {undo: done, redo: undone};
7399 },
7535 },
7400 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7536 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7401
7537
7402 markClean: function() {
7538 markClean: function() {
7403 this.cleanGeneration = this.changeGeneration(true);
7539 this.cleanGeneration = this.changeGeneration(true);
7404 },
7540 },
7405 changeGeneration: function(forceSplit) {
7541 changeGeneration: function(forceSplit) {
7406 if (forceSplit)
7542 if (forceSplit)
7407 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7543 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7408 return this.history.generation;
7544 return this.history.generation;
7409 },
7545 },
7410 isClean: function (gen) {
7546 isClean: function (gen) {
7411 return this.history.generation == (gen || this.cleanGeneration);
7547 return this.history.generation == (gen || this.cleanGeneration);
7412 },
7548 },
7413
7549
7414 getHistory: function() {
7550 getHistory: function() {
7415 return {done: copyHistoryArray(this.history.done),
7551 return {done: copyHistoryArray(this.history.done),
7416 undone: copyHistoryArray(this.history.undone)};
7552 undone: copyHistoryArray(this.history.undone)};
7417 },
7553 },
7418 setHistory: function(histData) {
7554 setHistory: function(histData) {
7419 var hist = this.history = new History(this.history.maxGeneration);
7555 var hist = this.history = new History(this.history.maxGeneration);
7420 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7556 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7421 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7557 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7422 },
7558 },
7423
7559
7424 addLineClass: docMethodOp(function(handle, where, cls) {
7560 addLineClass: docMethodOp(function(handle, where, cls) {
7425 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7561 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7426 var prop = where == "text" ? "textClass"
7562 var prop = where == "text" ? "textClass"
7427 : where == "background" ? "bgClass"
7563 : where == "background" ? "bgClass"
7428 : where == "gutter" ? "gutterClass" : "wrapClass";
7564 : where == "gutter" ? "gutterClass" : "wrapClass";
7429 if (!line[prop]) line[prop] = cls;
7565 if (!line[prop]) line[prop] = cls;
7430 else if (classTest(cls).test(line[prop])) return false;
7566 else if (classTest(cls).test(line[prop])) return false;
7431 else line[prop] += " " + cls;
7567 else line[prop] += " " + cls;
7432 return true;
7568 return true;
7433 });
7569 });
7434 }),
7570 }),
7435 removeLineClass: docMethodOp(function(handle, where, cls) {
7571 removeLineClass: docMethodOp(function(handle, where, cls) {
7436 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7572 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7437 var prop = where == "text" ? "textClass"
7573 var prop = where == "text" ? "textClass"
7438 : where == "background" ? "bgClass"
7574 : where == "background" ? "bgClass"
7439 : where == "gutter" ? "gutterClass" : "wrapClass";
7575 : where == "gutter" ? "gutterClass" : "wrapClass";
7440 var cur = line[prop];
7576 var cur = line[prop];
7441 if (!cur) return false;
7577 if (!cur) return false;
7442 else if (cls == null) line[prop] = null;
7578 else if (cls == null) line[prop] = null;
7443 else {
7579 else {
7444 var found = cur.match(classTest(cls));
7580 var found = cur.match(classTest(cls));
7445 if (!found) return false;
7581 if (!found) return false;
7446 var end = found.index + found[0].length;
7582 var end = found.index + found[0].length;
7447 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7583 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7448 }
7584 }
7449 return true;
7585 return true;
7450 });
7586 });
7451 }),
7587 }),
7452
7588
7453 addLineWidget: docMethodOp(function(handle, node, options) {
7589 addLineWidget: docMethodOp(function(handle, node, options) {
7454 return addLineWidget(this, handle, node, options);
7590 return addLineWidget(this, handle, node, options);
7455 }),
7591 }),
7456 removeLineWidget: function(widget) { widget.clear(); },
7592 removeLineWidget: function(widget) { widget.clear(); },
7457
7593
7458 markText: function(from, to, options) {
7594 markText: function(from, to, options) {
7459 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
7595 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range");
7460 },
7596 },
7461 setBookmark: function(pos, options) {
7597 setBookmark: function(pos, options) {
7462 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7598 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7463 insertLeft: options && options.insertLeft,
7599 insertLeft: options && options.insertLeft,
7464 clearWhenEmpty: false, shared: options && options.shared,
7600 clearWhenEmpty: false, shared: options && options.shared,
7465 handleMouseEvents: options && options.handleMouseEvents};
7601 handleMouseEvents: options && options.handleMouseEvents};
7466 pos = clipPos(this, pos);
7602 pos = clipPos(this, pos);
7467 return markText(this, pos, pos, realOpts, "bookmark");
7603 return markText(this, pos, pos, realOpts, "bookmark");
7468 },
7604 },
7469 findMarksAt: function(pos) {
7605 findMarksAt: function(pos) {
7470 pos = clipPos(this, pos);
7606 pos = clipPos(this, pos);
7471 var markers = [], spans = getLine(this, pos.line).markedSpans;
7607 var markers = [], spans = getLine(this, pos.line).markedSpans;
7472 if (spans) for (var i = 0; i < spans.length; ++i) {
7608 if (spans) for (var i = 0; i < spans.length; ++i) {
7473 var span = spans[i];
7609 var span = spans[i];
7474 if ((span.from == null || span.from <= pos.ch) &&
7610 if ((span.from == null || span.from <= pos.ch) &&
7475 (span.to == null || span.to >= pos.ch))
7611 (span.to == null || span.to >= pos.ch))
7476 markers.push(span.marker.parent || span.marker);
7612 markers.push(span.marker.parent || span.marker);
7477 }
7613 }
7478 return markers;
7614 return markers;
7479 },
7615 },
7480 findMarks: function(from, to, filter) {
7616 findMarks: function(from, to, filter) {
7481 from = clipPos(this, from); to = clipPos(this, to);
7617 from = clipPos(this, from); to = clipPos(this, to);
7482 var found = [], lineNo = from.line;
7618 var found = [], lineNo = from.line;
7483 this.iter(from.line, to.line + 1, function(line) {
7619 this.iter(from.line, to.line + 1, function(line) {
7484 var spans = line.markedSpans;
7620 var spans = line.markedSpans;
7485 if (spans) for (var i = 0; i < spans.length; i++) {
7621 if (spans) for (var i = 0; i < spans.length; i++) {
7486 var span = spans[i];
7622 var span = spans[i];
7487 if (!(lineNo == from.line && from.ch > span.to ||
7623 if (!(lineNo == from.line && from.ch > span.to ||
7488 span.from == null && lineNo != from.line||
7624 span.from == null && lineNo != from.line||
7489 lineNo == to.line && span.from > to.ch) &&
7625 lineNo == to.line && span.from > to.ch) &&
7490 (!filter || filter(span.marker)))
7626 (!filter || filter(span.marker)))
7491 found.push(span.marker.parent || span.marker);
7627 found.push(span.marker.parent || span.marker);
7492 }
7628 }
7493 ++lineNo;
7629 ++lineNo;
7494 });
7630 });
7495 return found;
7631 return found;
7496 },
7632 },
7497 getAllMarks: function() {
7633 getAllMarks: function() {
7498 var markers = [];
7634 var markers = [];
7499 this.iter(function(line) {
7635 this.iter(function(line) {
7500 var sps = line.markedSpans;
7636 var sps = line.markedSpans;
7501 if (sps) for (var i = 0; i < sps.length; ++i)
7637 if (sps) for (var i = 0; i < sps.length; ++i)
7502 if (sps[i].from != null) markers.push(sps[i].marker);
7638 if (sps[i].from != null) markers.push(sps[i].marker);
7503 });
7639 });
7504 return markers;
7640 return markers;
7505 },
7641 },
7506
7642
7507 posFromIndex: function(off) {
7643 posFromIndex: function(off) {
7508 var ch, lineNo = this.first;
7644 var ch, lineNo = this.first;
7509 this.iter(function(line) {
7645 this.iter(function(line) {
7510 var sz = line.text.length + 1;
7646 var sz = line.text.length + 1;
7511 if (sz > off) { ch = off; return true; }
7647 if (sz > off) { ch = off; return true; }
7512 off -= sz;
7648 off -= sz;
7513 ++lineNo;
7649 ++lineNo;
7514 });
7650 });
7515 return clipPos(this, Pos(lineNo, ch));
7651 return clipPos(this, Pos(lineNo, ch));
7516 },
7652 },
7517 indexFromPos: function (coords) {
7653 indexFromPos: function (coords) {
7518 coords = clipPos(this, coords);
7654 coords = clipPos(this, coords);
7519 var index = coords.ch;
7655 var index = coords.ch;
7520 if (coords.line < this.first || coords.ch < 0) return 0;
7656 if (coords.line < this.first || coords.ch < 0) return 0;
7521 this.iter(this.first, coords.line, function (line) {
7657 this.iter(this.first, coords.line, function (line) {
7522 index += line.text.length + 1;
7658 index += line.text.length + 1;
7523 });
7659 });
7524 return index;
7660 return index;
7525 },
7661 },
7526
7662
7527 copy: function(copyHistory) {
7663 copy: function(copyHistory) {
7528 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
7664 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7665 this.modeOption, this.first, this.lineSep);
7529 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7666 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7530 doc.sel = this.sel;
7667 doc.sel = this.sel;
7531 doc.extend = false;
7668 doc.extend = false;
7532 if (copyHistory) {
7669 if (copyHistory) {
7533 doc.history.undoDepth = this.history.undoDepth;
7670 doc.history.undoDepth = this.history.undoDepth;
7534 doc.setHistory(this.getHistory());
7671 doc.setHistory(this.getHistory());
7535 }
7672 }
7536 return doc;
7673 return doc;
7537 },
7674 },
7538
7675
7539 linkedDoc: function(options) {
7676 linkedDoc: function(options) {
7540 if (!options) options = {};
7677 if (!options) options = {};
7541 var from = this.first, to = this.first + this.size;
7678 var from = this.first, to = this.first + this.size;
7542 if (options.from != null && options.from > from) from = options.from;
7679 if (options.from != null && options.from > from) from = options.from;
7543 if (options.to != null && options.to < to) to = options.to;
7680 if (options.to != null && options.to < to) to = options.to;
7544 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
7681 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep);
7545 if (options.sharedHist) copy.history = this.history;
7682 if (options.sharedHist) copy.history = this.history;
7546 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7683 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7547 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7684 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7548 copySharedMarkers(copy, findSharedMarkers(this));
7685 copySharedMarkers(copy, findSharedMarkers(this));
7549 return copy;
7686 return copy;
7550 },
7687 },
7551 unlinkDoc: function(other) {
7688 unlinkDoc: function(other) {
7552 if (other instanceof CodeMirror) other = other.doc;
7689 if (other instanceof CodeMirror) other = other.doc;
7553 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7690 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7554 var link = this.linked[i];
7691 var link = this.linked[i];
7555 if (link.doc != other) continue;
7692 if (link.doc != other) continue;
7556 this.linked.splice(i, 1);
7693 this.linked.splice(i, 1);
7557 other.unlinkDoc(this);
7694 other.unlinkDoc(this);
7558 detachSharedMarkers(findSharedMarkers(this));
7695 detachSharedMarkers(findSharedMarkers(this));
7559 break;
7696 break;
7560 }
7697 }
7561 // If the histories were shared, split them again
7698 // If the histories were shared, split them again
7562 if (other.history == this.history) {
7699 if (other.history == this.history) {
7563 var splitIds = [other.id];
7700 var splitIds = [other.id];
7564 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7701 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7565 other.history = new History(null);
7702 other.history = new History(null);
7566 other.history.done = copyHistoryArray(this.history.done, splitIds);
7703 other.history.done = copyHistoryArray(this.history.done, splitIds);
7567 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7704 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7568 }
7705 }
7569 },
7706 },
7570 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7707 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7571
7708
7572 getMode: function() {return this.mode;},
7709 getMode: function() {return this.mode;},
7573 getEditor: function() {return this.cm;}
7710 getEditor: function() {return this.cm;},
7711
7712 splitLines: function(str) {
7713 if (this.lineSep) return str.split(this.lineSep);
7714 return splitLinesAuto(str);
7715 },
7716 lineSeparator: function() { return this.lineSep || "\n"; }
7574 });
7717 });
7575
7718
7576 // Public alias.
7719 // Public alias.
7577 Doc.prototype.eachLine = Doc.prototype.iter;
7720 Doc.prototype.eachLine = Doc.prototype.iter;
7578
7721
7579 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7722 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7580 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7723 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7581 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7724 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7582 CodeMirror.prototype[prop] = (function(method) {
7725 CodeMirror.prototype[prop] = (function(method) {
7583 return function() {return method.apply(this.doc, arguments);};
7726 return function() {return method.apply(this.doc, arguments);};
7584 })(Doc.prototype[prop]);
7727 })(Doc.prototype[prop]);
7585
7728
7586 eventMixin(Doc);
7729 eventMixin(Doc);
7587
7730
7588 // Call f for all linked documents.
7731 // Call f for all linked documents.
7589 function linkedDocs(doc, f, sharedHistOnly) {
7732 function linkedDocs(doc, f, sharedHistOnly) {
7590 function propagate(doc, skip, sharedHist) {
7733 function propagate(doc, skip, sharedHist) {
7591 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7734 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7592 var rel = doc.linked[i];
7735 var rel = doc.linked[i];
7593 if (rel.doc == skip) continue;
7736 if (rel.doc == skip) continue;
7594 var shared = sharedHist && rel.sharedHist;
7737 var shared = sharedHist && rel.sharedHist;
7595 if (sharedHistOnly && !shared) continue;
7738 if (sharedHistOnly && !shared) continue;
7596 f(rel.doc, shared);
7739 f(rel.doc, shared);
7597 propagate(rel.doc, doc, shared);
7740 propagate(rel.doc, doc, shared);
7598 }
7741 }
7599 }
7742 }
7600 propagate(doc, null, true);
7743 propagate(doc, null, true);
7601 }
7744 }
7602
7745
7603 // Attach a document to an editor.
7746 // Attach a document to an editor.
7604 function attachDoc(cm, doc) {
7747 function attachDoc(cm, doc) {
7605 if (doc.cm) throw new Error("This document is already in use.");
7748 if (doc.cm) throw new Error("This document is already in use.");
7606 cm.doc = doc;
7749 cm.doc = doc;
7607 doc.cm = cm;
7750 doc.cm = cm;
7608 estimateLineHeights(cm);
7751 estimateLineHeights(cm);
7609 loadMode(cm);
7752 loadMode(cm);
7610 if (!cm.options.lineWrapping) findMaxLine(cm);
7753 if (!cm.options.lineWrapping) findMaxLine(cm);
7611 cm.options.mode = doc.modeOption;
7754 cm.options.mode = doc.modeOption;
7612 regChange(cm);
7755 regChange(cm);
7613 }
7756 }
7614
7757
7615 // LINE UTILITIES
7758 // LINE UTILITIES
7616
7759
7617 // Find the line object corresponding to the given line number.
7760 // Find the line object corresponding to the given line number.
7618 function getLine(doc, n) {
7761 function getLine(doc, n) {
7619 n -= doc.first;
7762 n -= doc.first;
7620 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7763 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7621 for (var chunk = doc; !chunk.lines;) {
7764 for (var chunk = doc; !chunk.lines;) {
7622 for (var i = 0;; ++i) {
7765 for (var i = 0;; ++i) {
7623 var child = chunk.children[i], sz = child.chunkSize();
7766 var child = chunk.children[i], sz = child.chunkSize();
7624 if (n < sz) { chunk = child; break; }
7767 if (n < sz) { chunk = child; break; }
7625 n -= sz;
7768 n -= sz;
7626 }
7769 }
7627 }
7770 }
7628 return chunk.lines[n];
7771 return chunk.lines[n];
7629 }
7772 }
7630
7773
7631 // Get the part of a document between two positions, as an array of
7774 // Get the part of a document between two positions, as an array of
7632 // strings.
7775 // strings.
7633 function getBetween(doc, start, end) {
7776 function getBetween(doc, start, end) {
7634 var out = [], n = start.line;
7777 var out = [], n = start.line;
7635 doc.iter(start.line, end.line + 1, function(line) {
7778 doc.iter(start.line, end.line + 1, function(line) {
7636 var text = line.text;
7779 var text = line.text;
7637 if (n == end.line) text = text.slice(0, end.ch);
7780 if (n == end.line) text = text.slice(0, end.ch);
7638 if (n == start.line) text = text.slice(start.ch);
7781 if (n == start.line) text = text.slice(start.ch);
7639 out.push(text);
7782 out.push(text);
7640 ++n;
7783 ++n;
7641 });
7784 });
7642 return out;
7785 return out;
7643 }
7786 }
7644 // Get the lines between from and to, as array of strings.
7787 // Get the lines between from and to, as array of strings.
7645 function getLines(doc, from, to) {
7788 function getLines(doc, from, to) {
7646 var out = [];
7789 var out = [];
7647 doc.iter(from, to, function(line) { out.push(line.text); });
7790 doc.iter(from, to, function(line) { out.push(line.text); });
7648 return out;
7791 return out;
7649 }
7792 }
7650
7793
7651 // Update the height of a line, propagating the height change
7794 // Update the height of a line, propagating the height change
7652 // upwards to parent nodes.
7795 // upwards to parent nodes.
7653 function updateLineHeight(line, height) {
7796 function updateLineHeight(line, height) {
7654 var diff = height - line.height;
7797 var diff = height - line.height;
7655 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7798 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7656 }
7799 }
7657
7800
7658 // Given a line object, find its line number by walking up through
7801 // Given a line object, find its line number by walking up through
7659 // its parent links.
7802 // its parent links.
7660 function lineNo(line) {
7803 function lineNo(line) {
7661 if (line.parent == null) return null;
7804 if (line.parent == null) return null;
7662 var cur = line.parent, no = indexOf(cur.lines, line);
7805 var cur = line.parent, no = indexOf(cur.lines, line);
7663 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7806 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7664 for (var i = 0;; ++i) {
7807 for (var i = 0;; ++i) {
7665 if (chunk.children[i] == cur) break;
7808 if (chunk.children[i] == cur) break;
7666 no += chunk.children[i].chunkSize();
7809 no += chunk.children[i].chunkSize();
7667 }
7810 }
7668 }
7811 }
7669 return no + cur.first;
7812 return no + cur.first;
7670 }
7813 }
7671
7814
7672 // Find the line at the given vertical position, using the height
7815 // Find the line at the given vertical position, using the height
7673 // information in the document tree.
7816 // information in the document tree.
7674 function lineAtHeight(chunk, h) {
7817 function lineAtHeight(chunk, h) {
7675 var n = chunk.first;
7818 var n = chunk.first;
7676 outer: do {
7819 outer: do {
7677 for (var i = 0; i < chunk.children.length; ++i) {
7820 for (var i = 0; i < chunk.children.length; ++i) {
7678 var child = chunk.children[i], ch = child.height;
7821 var child = chunk.children[i], ch = child.height;
7679 if (h < ch) { chunk = child; continue outer; }
7822 if (h < ch) { chunk = child; continue outer; }
7680 h -= ch;
7823 h -= ch;
7681 n += child.chunkSize();
7824 n += child.chunkSize();
7682 }
7825 }
7683 return n;
7826 return n;
7684 } while (!chunk.lines);
7827 } while (!chunk.lines);
7685 for (var i = 0; i < chunk.lines.length; ++i) {
7828 for (var i = 0; i < chunk.lines.length; ++i) {
7686 var line = chunk.lines[i], lh = line.height;
7829 var line = chunk.lines[i], lh = line.height;
7687 if (h < lh) break;
7830 if (h < lh) break;
7688 h -= lh;
7831 h -= lh;
7689 }
7832 }
7690 return n + i;
7833 return n + i;
7691 }
7834 }
7692
7835
7693
7836
7694 // Find the height above the given line.
7837 // Find the height above the given line.
7695 function heightAtLine(lineObj) {
7838 function heightAtLine(lineObj) {
7696 lineObj = visualLine(lineObj);
7839 lineObj = visualLine(lineObj);
7697
7840
7698 var h = 0, chunk = lineObj.parent;
7841 var h = 0, chunk = lineObj.parent;
7699 for (var i = 0; i < chunk.lines.length; ++i) {
7842 for (var i = 0; i < chunk.lines.length; ++i) {
7700 var line = chunk.lines[i];
7843 var line = chunk.lines[i];
7701 if (line == lineObj) break;
7844 if (line == lineObj) break;
7702 else h += line.height;
7845 else h += line.height;
7703 }
7846 }
7704 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7847 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7705 for (var i = 0; i < p.children.length; ++i) {
7848 for (var i = 0; i < p.children.length; ++i) {
7706 var cur = p.children[i];
7849 var cur = p.children[i];
7707 if (cur == chunk) break;
7850 if (cur == chunk) break;
7708 else h += cur.height;
7851 else h += cur.height;
7709 }
7852 }
7710 }
7853 }
7711 return h;
7854 return h;
7712 }
7855 }
7713
7856
7714 // Get the bidi ordering for the given line (and cache it). Returns
7857 // Get the bidi ordering for the given line (and cache it). Returns
7715 // false for lines that are fully left-to-right, and an array of
7858 // false for lines that are fully left-to-right, and an array of
7716 // BidiSpan objects otherwise.
7859 // BidiSpan objects otherwise.
7717 function getOrder(line) {
7860 function getOrder(line) {
7718 var order = line.order;
7861 var order = line.order;
7719 if (order == null) order = line.order = bidiOrdering(line.text);
7862 if (order == null) order = line.order = bidiOrdering(line.text);
7720 return order;
7863 return order;
7721 }
7864 }
7722
7865
7723 // HISTORY
7866 // HISTORY
7724
7867
7725 function History(startGen) {
7868 function History(startGen) {
7726 // Arrays of change events and selections. Doing something adds an
7869 // Arrays of change events and selections. Doing something adds an
7727 // event to done and clears undo. Undoing moves events from done
7870 // event to done and clears undo. Undoing moves events from done
7728 // to undone, redoing moves them in the other direction.
7871 // to undone, redoing moves them in the other direction.
7729 this.done = []; this.undone = [];
7872 this.done = []; this.undone = [];
7730 this.undoDepth = Infinity;
7873 this.undoDepth = Infinity;
7731 // Used to track when changes can be merged into a single undo
7874 // Used to track when changes can be merged into a single undo
7732 // event
7875 // event
7733 this.lastModTime = this.lastSelTime = 0;
7876 this.lastModTime = this.lastSelTime = 0;
7734 this.lastOp = this.lastSelOp = null;
7877 this.lastOp = this.lastSelOp = null;
7735 this.lastOrigin = this.lastSelOrigin = null;
7878 this.lastOrigin = this.lastSelOrigin = null;
7736 // Used by the isClean() method
7879 // Used by the isClean() method
7737 this.generation = this.maxGeneration = startGen || 1;
7880 this.generation = this.maxGeneration = startGen || 1;
7738 }
7881 }
7739
7882
7740 // Create a history change event from an updateDoc-style change
7883 // Create a history change event from an updateDoc-style change
7741 // object.
7884 // object.
7742 function historyChangeFromChange(doc, change) {
7885 function historyChangeFromChange(doc, change) {
7743 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7886 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7744 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7887 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7745 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7888 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7746 return histChange;
7889 return histChange;
7747 }
7890 }
7748
7891
7749 // Pop all selection events off the end of a history array. Stop at
7892 // Pop all selection events off the end of a history array. Stop at
7750 // a change event.
7893 // a change event.
7751 function clearSelectionEvents(array) {
7894 function clearSelectionEvents(array) {
7752 while (array.length) {
7895 while (array.length) {
7753 var last = lst(array);
7896 var last = lst(array);
7754 if (last.ranges) array.pop();
7897 if (last.ranges) array.pop();
7755 else break;
7898 else break;
7756 }
7899 }
7757 }
7900 }
7758
7901
7759 // Find the top change event in the history. Pop off selection
7902 // Find the top change event in the history. Pop off selection
7760 // events that are in the way.
7903 // events that are in the way.
7761 function lastChangeEvent(hist, force) {
7904 function lastChangeEvent(hist, force) {
7762 if (force) {
7905 if (force) {
7763 clearSelectionEvents(hist.done);
7906 clearSelectionEvents(hist.done);
7764 return lst(hist.done);
7907 return lst(hist.done);
7765 } else if (hist.done.length && !lst(hist.done).ranges) {
7908 } else if (hist.done.length && !lst(hist.done).ranges) {
7766 return lst(hist.done);
7909 return lst(hist.done);
7767 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7910 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7768 hist.done.pop();
7911 hist.done.pop();
7769 return lst(hist.done);
7912 return lst(hist.done);
7770 }
7913 }
7771 }
7914 }
7772
7915
7773 // Register a change in the history. Merges changes that are within
7916 // Register a change in the history. Merges changes that are within
7774 // a single operation, ore are close together with an origin that
7917 // a single operation, ore are close together with an origin that
7775 // allows merging (starting with "+") into a single event.
7918 // allows merging (starting with "+") into a single event.
7776 function addChangeToHistory(doc, change, selAfter, opId) {
7919 function addChangeToHistory(doc, change, selAfter, opId) {
7777 var hist = doc.history;
7920 var hist = doc.history;
7778 hist.undone.length = 0;
7921 hist.undone.length = 0;
7779 var time = +new Date, cur;
7922 var time = +new Date, cur;
7780
7923
7781 if ((hist.lastOp == opId ||
7924 if ((hist.lastOp == opId ||
7782 hist.lastOrigin == change.origin && change.origin &&
7925 hist.lastOrigin == change.origin && change.origin &&
7783 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7926 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7784 change.origin.charAt(0) == "*")) &&
7927 change.origin.charAt(0) == "*")) &&
7785 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7928 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7786 // Merge this change into the last event
7929 // Merge this change into the last event
7787 var last = lst(cur.changes);
7930 var last = lst(cur.changes);
7788 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7931 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7789 // Optimized case for simple insertion -- don't want to add
7932 // Optimized case for simple insertion -- don't want to add
7790 // new changesets for every character typed
7933 // new changesets for every character typed
7791 last.to = changeEnd(change);
7934 last.to = changeEnd(change);
7792 } else {
7935 } else {
7793 // Add new sub-event
7936 // Add new sub-event
7794 cur.changes.push(historyChangeFromChange(doc, change));
7937 cur.changes.push(historyChangeFromChange(doc, change));
7795 }
7938 }
7796 } else {
7939 } else {
7797 // Can not be merged, start a new event.
7940 // Can not be merged, start a new event.
7798 var before = lst(hist.done);
7941 var before = lst(hist.done);
7799 if (!before || !before.ranges)
7942 if (!before || !before.ranges)
7800 pushSelectionToHistory(doc.sel, hist.done);
7943 pushSelectionToHistory(doc.sel, hist.done);
7801 cur = {changes: [historyChangeFromChange(doc, change)],
7944 cur = {changes: [historyChangeFromChange(doc, change)],
7802 generation: hist.generation};
7945 generation: hist.generation};
7803 hist.done.push(cur);
7946 hist.done.push(cur);
7804 while (hist.done.length > hist.undoDepth) {
7947 while (hist.done.length > hist.undoDepth) {
7805 hist.done.shift();
7948 hist.done.shift();
7806 if (!hist.done[0].ranges) hist.done.shift();
7949 if (!hist.done[0].ranges) hist.done.shift();
7807 }
7950 }
7808 }
7951 }
7809 hist.done.push(selAfter);
7952 hist.done.push(selAfter);
7810 hist.generation = ++hist.maxGeneration;
7953 hist.generation = ++hist.maxGeneration;
7811 hist.lastModTime = hist.lastSelTime = time;
7954 hist.lastModTime = hist.lastSelTime = time;
7812 hist.lastOp = hist.lastSelOp = opId;
7955 hist.lastOp = hist.lastSelOp = opId;
7813 hist.lastOrigin = hist.lastSelOrigin = change.origin;
7956 hist.lastOrigin = hist.lastSelOrigin = change.origin;
7814
7957
7815 if (!last) signal(doc, "historyAdded");
7958 if (!last) signal(doc, "historyAdded");
7816 }
7959 }
7817
7960
7818 function selectionEventCanBeMerged(doc, origin, prev, sel) {
7961 function selectionEventCanBeMerged(doc, origin, prev, sel) {
7819 var ch = origin.charAt(0);
7962 var ch = origin.charAt(0);
7820 return ch == "*" ||
7963 return ch == "*" ||
7821 ch == "+" &&
7964 ch == "+" &&
7822 prev.ranges.length == sel.ranges.length &&
7965 prev.ranges.length == sel.ranges.length &&
7823 prev.somethingSelected() == sel.somethingSelected() &&
7966 prev.somethingSelected() == sel.somethingSelected() &&
7824 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7967 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7825 }
7968 }
7826
7969
7827 // Called whenever the selection changes, sets the new selection as
7970 // Called whenever the selection changes, sets the new selection as
7828 // the pending selection in the history, and pushes the old pending
7971 // the pending selection in the history, and pushes the old pending
7829 // selection into the 'done' array when it was significantly
7972 // selection into the 'done' array when it was significantly
7830 // different (in number of selected ranges, emptiness, or time).
7973 // different (in number of selected ranges, emptiness, or time).
7831 function addSelectionToHistory(doc, sel, opId, options) {
7974 function addSelectionToHistory(doc, sel, opId, options) {
7832 var hist = doc.history, origin = options && options.origin;
7975 var hist = doc.history, origin = options && options.origin;
7833
7976
7834 // A new event is started when the previous origin does not match
7977 // A new event is started when the previous origin does not match
7835 // the current, or the origins don't allow matching. Origins
7978 // the current, or the origins don't allow matching. Origins
7836 // starting with * are always merged, those starting with + are
7979 // starting with * are always merged, those starting with + are
7837 // merged when similar and close together in time.
7980 // merged when similar and close together in time.
7838 if (opId == hist.lastSelOp ||
7981 if (opId == hist.lastSelOp ||
7839 (origin && hist.lastSelOrigin == origin &&
7982 (origin && hist.lastSelOrigin == origin &&
7840 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7983 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7841 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7984 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7842 hist.done[hist.done.length - 1] = sel;
7985 hist.done[hist.done.length - 1] = sel;
7843 else
7986 else
7844 pushSelectionToHistory(sel, hist.done);
7987 pushSelectionToHistory(sel, hist.done);
7845
7988
7846 hist.lastSelTime = +new Date;
7989 hist.lastSelTime = +new Date;
7847 hist.lastSelOrigin = origin;
7990 hist.lastSelOrigin = origin;
7848 hist.lastSelOp = opId;
7991 hist.lastSelOp = opId;
7849 if (options && options.clearRedo !== false)
7992 if (options && options.clearRedo !== false)
7850 clearSelectionEvents(hist.undone);
7993 clearSelectionEvents(hist.undone);
7851 }
7994 }
7852
7995
7853 function pushSelectionToHistory(sel, dest) {
7996 function pushSelectionToHistory(sel, dest) {
7854 var top = lst(dest);
7997 var top = lst(dest);
7855 if (!(top && top.ranges && top.equals(sel)))
7998 if (!(top && top.ranges && top.equals(sel)))
7856 dest.push(sel);
7999 dest.push(sel);
7857 }
8000 }
7858
8001
7859 // Used to store marked span information in the history.
8002 // Used to store marked span information in the history.
7860 function attachLocalSpans(doc, change, from, to) {
8003 function attachLocalSpans(doc, change, from, to) {
7861 var existing = change["spans_" + doc.id], n = 0;
8004 var existing = change["spans_" + doc.id], n = 0;
7862 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
8005 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
7863 if (line.markedSpans)
8006 if (line.markedSpans)
7864 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
8007 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
7865 ++n;
8008 ++n;
7866 });
8009 });
7867 }
8010 }
7868
8011
7869 // When un/re-doing restores text containing marked spans, those
8012 // When un/re-doing restores text containing marked spans, those
7870 // that have been explicitly cleared should not be restored.
8013 // that have been explicitly cleared should not be restored.
7871 function removeClearedSpans(spans) {
8014 function removeClearedSpans(spans) {
7872 if (!spans) return null;
8015 if (!spans) return null;
7873 for (var i = 0, out; i < spans.length; ++i) {
8016 for (var i = 0, out; i < spans.length; ++i) {
7874 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
8017 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
7875 else if (out) out.push(spans[i]);
8018 else if (out) out.push(spans[i]);
7876 }
8019 }
7877 return !out ? spans : out.length ? out : null;
8020 return !out ? spans : out.length ? out : null;
7878 }
8021 }
7879
8022
7880 // Retrieve and filter the old marked spans stored in a change event.
8023 // Retrieve and filter the old marked spans stored in a change event.
7881 function getOldSpans(doc, change) {
8024 function getOldSpans(doc, change) {
7882 var found = change["spans_" + doc.id];
8025 var found = change["spans_" + doc.id];
7883 if (!found) return null;
8026 if (!found) return null;
7884 for (var i = 0, nw = []; i < change.text.length; ++i)
8027 for (var i = 0, nw = []; i < change.text.length; ++i)
7885 nw.push(removeClearedSpans(found[i]));
8028 nw.push(removeClearedSpans(found[i]));
7886 return nw;
8029 return nw;
7887 }
8030 }
7888
8031
7889 // Used both to provide a JSON-safe object in .getHistory, and, when
8032 // Used both to provide a JSON-safe object in .getHistory, and, when
7890 // detaching a document, to split the history in two
8033 // detaching a document, to split the history in two
7891 function copyHistoryArray(events, newGroup, instantiateSel) {
8034 function copyHistoryArray(events, newGroup, instantiateSel) {
7892 for (var i = 0, copy = []; i < events.length; ++i) {
8035 for (var i = 0, copy = []; i < events.length; ++i) {
7893 var event = events[i];
8036 var event = events[i];
7894 if (event.ranges) {
8037 if (event.ranges) {
7895 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
8038 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
7896 continue;
8039 continue;
7897 }
8040 }
7898 var changes = event.changes, newChanges = [];
8041 var changes = event.changes, newChanges = [];
7899 copy.push({changes: newChanges});
8042 copy.push({changes: newChanges});
7900 for (var j = 0; j < changes.length; ++j) {
8043 for (var j = 0; j < changes.length; ++j) {
7901 var change = changes[j], m;
8044 var change = changes[j], m;
7902 newChanges.push({from: change.from, to: change.to, text: change.text});
8045 newChanges.push({from: change.from, to: change.to, text: change.text});
7903 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
8046 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
7904 if (indexOf(newGroup, Number(m[1])) > -1) {
8047 if (indexOf(newGroup, Number(m[1])) > -1) {
7905 lst(newChanges)[prop] = change[prop];
8048 lst(newChanges)[prop] = change[prop];
7906 delete change[prop];
8049 delete change[prop];
7907 }
8050 }
7908 }
8051 }
7909 }
8052 }
7910 }
8053 }
7911 return copy;
8054 return copy;
7912 }
8055 }
7913
8056
7914 // Rebasing/resetting history to deal with externally-sourced changes
8057 // Rebasing/resetting history to deal with externally-sourced changes
7915
8058
7916 function rebaseHistSelSingle(pos, from, to, diff) {
8059 function rebaseHistSelSingle(pos, from, to, diff) {
7917 if (to < pos.line) {
8060 if (to < pos.line) {
7918 pos.line += diff;
8061 pos.line += diff;
7919 } else if (from < pos.line) {
8062 } else if (from < pos.line) {
7920 pos.line = from;
8063 pos.line = from;
7921 pos.ch = 0;
8064 pos.ch = 0;
7922 }
8065 }
7923 }
8066 }
7924
8067
7925 // Tries to rebase an array of history events given a change in the
8068 // Tries to rebase an array of history events given a change in the
7926 // document. If the change touches the same lines as the event, the
8069 // document. If the change touches the same lines as the event, the
7927 // event, and everything 'behind' it, is discarded. If the change is
8070 // event, and everything 'behind' it, is discarded. If the change is
7928 // before the event, the event's positions are updated. Uses a
8071 // before the event, the event's positions are updated. Uses a
7929 // copy-on-write scheme for the positions, to avoid having to
8072 // copy-on-write scheme for the positions, to avoid having to
7930 // reallocate them all on every rebase, but also avoid problems with
8073 // reallocate them all on every rebase, but also avoid problems with
7931 // shared position objects being unsafely updated.
8074 // shared position objects being unsafely updated.
7932 function rebaseHistArray(array, from, to, diff) {
8075 function rebaseHistArray(array, from, to, diff) {
7933 for (var i = 0; i < array.length; ++i) {
8076 for (var i = 0; i < array.length; ++i) {
7934 var sub = array[i], ok = true;
8077 var sub = array[i], ok = true;
7935 if (sub.ranges) {
8078 if (sub.ranges) {
7936 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
8079 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
7937 for (var j = 0; j < sub.ranges.length; j++) {
8080 for (var j = 0; j < sub.ranges.length; j++) {
7938 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
8081 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
7939 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
8082 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
7940 }
8083 }
7941 continue;
8084 continue;
7942 }
8085 }
7943 for (var j = 0; j < sub.changes.length; ++j) {
8086 for (var j = 0; j < sub.changes.length; ++j) {
7944 var cur = sub.changes[j];
8087 var cur = sub.changes[j];
7945 if (to < cur.from.line) {
8088 if (to < cur.from.line) {
7946 cur.from = Pos(cur.from.line + diff, cur.from.ch);
8089 cur.from = Pos(cur.from.line + diff, cur.from.ch);
7947 cur.to = Pos(cur.to.line + diff, cur.to.ch);
8090 cur.to = Pos(cur.to.line + diff, cur.to.ch);
7948 } else if (from <= cur.to.line) {
8091 } else if (from <= cur.to.line) {
7949 ok = false;
8092 ok = false;
7950 break;
8093 break;
7951 }
8094 }
7952 }
8095 }
7953 if (!ok) {
8096 if (!ok) {
7954 array.splice(0, i + 1);
8097 array.splice(0, i + 1);
7955 i = 0;
8098 i = 0;
7956 }
8099 }
7957 }
8100 }
7958 }
8101 }
7959
8102
7960 function rebaseHist(hist, change) {
8103 function rebaseHist(hist, change) {
7961 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
8104 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
7962 rebaseHistArray(hist.done, from, to, diff);
8105 rebaseHistArray(hist.done, from, to, diff);
7963 rebaseHistArray(hist.undone, from, to, diff);
8106 rebaseHistArray(hist.undone, from, to, diff);
7964 }
8107 }
7965
8108
7966 // EVENT UTILITIES
8109 // EVENT UTILITIES
7967
8110
7968 // Due to the fact that we still support jurassic IE versions, some
8111 // Due to the fact that we still support jurassic IE versions, some
7969 // compatibility wrappers are needed.
8112 // compatibility wrappers are needed.
7970
8113
7971 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
8114 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
7972 if (e.preventDefault) e.preventDefault();
8115 if (e.preventDefault) e.preventDefault();
7973 else e.returnValue = false;
8116 else e.returnValue = false;
7974 };
8117 };
7975 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
8118 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
7976 if (e.stopPropagation) e.stopPropagation();
8119 if (e.stopPropagation) e.stopPropagation();
7977 else e.cancelBubble = true;
8120 else e.cancelBubble = true;
7978 };
8121 };
7979 function e_defaultPrevented(e) {
8122 function e_defaultPrevented(e) {
7980 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
8123 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
7981 }
8124 }
7982 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
8125 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
7983
8126
7984 function e_target(e) {return e.target || e.srcElement;}
8127 function e_target(e) {return e.target || e.srcElement;}
7985 function e_button(e) {
8128 function e_button(e) {
7986 var b = e.which;
8129 var b = e.which;
7987 if (b == null) {
8130 if (b == null) {
7988 if (e.button & 1) b = 1;
8131 if (e.button & 1) b = 1;
7989 else if (e.button & 2) b = 3;
8132 else if (e.button & 2) b = 3;
7990 else if (e.button & 4) b = 2;
8133 else if (e.button & 4) b = 2;
7991 }
8134 }
7992 if (mac && e.ctrlKey && b == 1) b = 3;
8135 if (mac && e.ctrlKey && b == 1) b = 3;
7993 return b;
8136 return b;
7994 }
8137 }
7995
8138
7996 // EVENT HANDLING
8139 // EVENT HANDLING
7997
8140
7998 // Lightweight event framework. on/off also work on DOM nodes,
8141 // Lightweight event framework. on/off also work on DOM nodes,
7999 // registering native DOM handlers.
8142 // registering native DOM handlers.
8000
8143
8001 var on = CodeMirror.on = function(emitter, type, f) {
8144 var on = CodeMirror.on = function(emitter, type, f) {
8002 if (emitter.addEventListener)
8145 if (emitter.addEventListener)
8003 emitter.addEventListener(type, f, false);
8146 emitter.addEventListener(type, f, false);
8004 else if (emitter.attachEvent)
8147 else if (emitter.attachEvent)
8005 emitter.attachEvent("on" + type, f);
8148 emitter.attachEvent("on" + type, f);
8006 else {
8149 else {
8007 var map = emitter._handlers || (emitter._handlers = {});
8150 var map = emitter._handlers || (emitter._handlers = {});
8008 var arr = map[type] || (map[type] = []);
8151 var arr = map[type] || (map[type] = []);
8009 arr.push(f);
8152 arr.push(f);
8010 }
8153 }
8011 };
8154 };
8012
8155
8156 var noHandlers = []
8157 function getHandlers(emitter, type, copy) {
8158 var arr = emitter._handlers && emitter._handlers[type]
8159 if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
8160 else return arr || noHandlers
8161 }
8162
8013 var off = CodeMirror.off = function(emitter, type, f) {
8163 var off = CodeMirror.off = function(emitter, type, f) {
8014 if (emitter.removeEventListener)
8164 if (emitter.removeEventListener)
8015 emitter.removeEventListener(type, f, false);
8165 emitter.removeEventListener(type, f, false);
8016 else if (emitter.detachEvent)
8166 else if (emitter.detachEvent)
8017 emitter.detachEvent("on" + type, f);
8167 emitter.detachEvent("on" + type, f);
8018 else {
8168 else {
8019 var arr = emitter._handlers && emitter._handlers[type];
8169 var handlers = getHandlers(emitter, type, false)
8020 if (!arr) return;
8170 for (var i = 0; i < handlers.length; ++i)
8021 for (var i = 0; i < arr.length; ++i)
8171 if (handlers[i] == f) { handlers.splice(i, 1); break; }
8022 if (arr[i] == f) { arr.splice(i, 1); break; }
8023 }
8172 }
8024 };
8173 };
8025
8174
8026 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8175 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8027 var arr = emitter._handlers && emitter._handlers[type];
8176 var handlers = getHandlers(emitter, type, true)
8028 if (!arr) return;
8177 if (!handlers.length) return;
8029 var args = Array.prototype.slice.call(arguments, 2);
8178 var args = Array.prototype.slice.call(arguments, 2);
8030 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
8179 for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
8031 };
8180 };
8032
8181
8033 var orphanDelayedCallbacks = null;
8182 var orphanDelayedCallbacks = null;
8034
8183
8035 // Often, we want to signal events at a point where we are in the
8184 // Often, we want to signal events at a point where we are in the
8036 // middle of some work, but don't want the handler to start calling
8185 // middle of some work, but don't want the handler to start calling
8037 // other methods on the editor, which might be in an inconsistent
8186 // other methods on the editor, which might be in an inconsistent
8038 // state or simply not expect any other events to happen.
8187 // state or simply not expect any other events to happen.
8039 // signalLater looks whether there are any handlers, and schedules
8188 // signalLater looks whether there are any handlers, and schedules
8040 // them to be executed when the last operation ends, or, if no
8189 // them to be executed when the last operation ends, or, if no
8041 // operation is active, when a timeout fires.
8190 // operation is active, when a timeout fires.
8042 function signalLater(emitter, type /*, values...*/) {
8191 function signalLater(emitter, type /*, values...*/) {
8043 var arr = emitter._handlers && emitter._handlers[type];
8192 var arr = getHandlers(emitter, type, false)
8044 if (!arr) return;
8193 if (!arr.length) return;
8045 var args = Array.prototype.slice.call(arguments, 2), list;
8194 var args = Array.prototype.slice.call(arguments, 2), list;
8046 if (operationGroup) {
8195 if (operationGroup) {
8047 list = operationGroup.delayedCallbacks;
8196 list = operationGroup.delayedCallbacks;
8048 } else if (orphanDelayedCallbacks) {
8197 } else if (orphanDelayedCallbacks) {
8049 list = orphanDelayedCallbacks;
8198 list = orphanDelayedCallbacks;
8050 } else {
8199 } else {
8051 list = orphanDelayedCallbacks = [];
8200 list = orphanDelayedCallbacks = [];
8052 setTimeout(fireOrphanDelayed, 0);
8201 setTimeout(fireOrphanDelayed, 0);
8053 }
8202 }
8054 function bnd(f) {return function(){f.apply(null, args);};};
8203 function bnd(f) {return function(){f.apply(null, args);};};
8055 for (var i = 0; i < arr.length; ++i)
8204 for (var i = 0; i < arr.length; ++i)
8056 list.push(bnd(arr[i]));
8205 list.push(bnd(arr[i]));
8057 }
8206 }
8058
8207
8059 function fireOrphanDelayed() {
8208 function fireOrphanDelayed() {
8060 var delayed = orphanDelayedCallbacks;
8209 var delayed = orphanDelayedCallbacks;
8061 orphanDelayedCallbacks = null;
8210 orphanDelayedCallbacks = null;
8062 for (var i = 0; i < delayed.length; ++i) delayed[i]();
8211 for (var i = 0; i < delayed.length; ++i) delayed[i]();
8063 }
8212 }
8064
8213
8065 // The DOM events that CodeMirror handles can be overridden by
8214 // The DOM events that CodeMirror handles can be overridden by
8066 // registering a (non-DOM) handler on the editor for the event name,
8215 // registering a (non-DOM) handler on the editor for the event name,
8067 // and preventDefault-ing the event in that handler.
8216 // and preventDefault-ing the event in that handler.
8068 function signalDOMEvent(cm, e, override) {
8217 function signalDOMEvent(cm, e, override) {
8069 if (typeof e == "string")
8218 if (typeof e == "string")
8070 e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8219 e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8071 signal(cm, override || e.type, cm, e);
8220 signal(cm, override || e.type, cm, e);
8072 return e_defaultPrevented(e) || e.codemirrorIgnore;
8221 return e_defaultPrevented(e) || e.codemirrorIgnore;
8073 }
8222 }
8074
8223
8075 function signalCursorActivity(cm) {
8224 function signalCursorActivity(cm) {
8076 var arr = cm._handlers && cm._handlers.cursorActivity;
8225 var arr = cm._handlers && cm._handlers.cursorActivity;
8077 if (!arr) return;
8226 if (!arr) return;
8078 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8227 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8079 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8228 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8080 set.push(arr[i]);
8229 set.push(arr[i]);
8081 }
8230 }
8082
8231
8083 function hasHandler(emitter, type) {
8232 function hasHandler(emitter, type) {
8084 var arr = emitter._handlers && emitter._handlers[type];
8233 return getHandlers(emitter, type).length > 0
8085 return arr && arr.length > 0;
8086 }
8234 }
8087
8235
8088 // Add on and off methods to a constructor's prototype, to make
8236 // Add on and off methods to a constructor's prototype, to make
8089 // registering events on such objects more convenient.
8237 // registering events on such objects more convenient.
8090 function eventMixin(ctor) {
8238 function eventMixin(ctor) {
8091 ctor.prototype.on = function(type, f) {on(this, type, f);};
8239 ctor.prototype.on = function(type, f) {on(this, type, f);};
8092 ctor.prototype.off = function(type, f) {off(this, type, f);};
8240 ctor.prototype.off = function(type, f) {off(this, type, f);};
8093 }
8241 }
8094
8242
8095 // MISC UTILITIES
8243 // MISC UTILITIES
8096
8244
8097 // Number of pixels added to scroller and sizer to hide scrollbar
8245 // Number of pixels added to scroller and sizer to hide scrollbar
8098 var scrollerGap = 30;
8246 var scrollerGap = 30;
8099
8247
8100 // Returned or thrown by various protocols to signal 'I'm not
8248 // Returned or thrown by various protocols to signal 'I'm not
8101 // handling this'.
8249 // handling this'.
8102 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8250 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8103
8251
8104 // Reused option objects for setSelection & friends
8252 // Reused option objects for setSelection & friends
8105 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8253 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8106
8254
8107 function Delayed() {this.id = null;}
8255 function Delayed() {this.id = null;}
8108 Delayed.prototype.set = function(ms, f) {
8256 Delayed.prototype.set = function(ms, f) {
8109 clearTimeout(this.id);
8257 clearTimeout(this.id);
8110 this.id = setTimeout(f, ms);
8258 this.id = setTimeout(f, ms);
8111 };
8259 };
8112
8260
8113 // Counts the column offset in a string, taking tabs into account.
8261 // Counts the column offset in a string, taking tabs into account.
8114 // Used mostly to find indentation.
8262 // Used mostly to find indentation.
8115 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8263 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8116 if (end == null) {
8264 if (end == null) {
8117 end = string.search(/[^\s\u00a0]/);
8265 end = string.search(/[^\s\u00a0]/);
8118 if (end == -1) end = string.length;
8266 if (end == -1) end = string.length;
8119 }
8267 }
8120 for (var i = startIndex || 0, n = startValue || 0;;) {
8268 for (var i = startIndex || 0, n = startValue || 0;;) {
8121 var nextTab = string.indexOf("\t", i);
8269 var nextTab = string.indexOf("\t", i);
8122 if (nextTab < 0 || nextTab >= end)
8270 if (nextTab < 0 || nextTab >= end)
8123 return n + (end - i);
8271 return n + (end - i);
8124 n += nextTab - i;
8272 n += nextTab - i;
8125 n += tabSize - (n % tabSize);
8273 n += tabSize - (n % tabSize);
8126 i = nextTab + 1;
8274 i = nextTab + 1;
8127 }
8275 }
8128 };
8276 };
8129
8277
8130 // The inverse of countColumn -- find the offset that corresponds to
8278 // The inverse of countColumn -- find the offset that corresponds to
8131 // a particular column.
8279 // a particular column.
8132 function findColumn(string, goal, tabSize) {
8280 var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
8133 for (var pos = 0, col = 0;;) {
8281 for (var pos = 0, col = 0;;) {
8134 var nextTab = string.indexOf("\t", pos);
8282 var nextTab = string.indexOf("\t", pos);
8135 if (nextTab == -1) nextTab = string.length;
8283 if (nextTab == -1) nextTab = string.length;
8136 var skipped = nextTab - pos;
8284 var skipped = nextTab - pos;
8137 if (nextTab == string.length || col + skipped >= goal)
8285 if (nextTab == string.length || col + skipped >= goal)
8138 return pos + Math.min(skipped, goal - col);
8286 return pos + Math.min(skipped, goal - col);
8139 col += nextTab - pos;
8287 col += nextTab - pos;
8140 col += tabSize - (col % tabSize);
8288 col += tabSize - (col % tabSize);
8141 pos = nextTab + 1;
8289 pos = nextTab + 1;
8142 if (col >= goal) return pos;
8290 if (col >= goal) return pos;
8143 }
8291 }
8144 }
8292 }
8145
8293
8146 var spaceStrs = [""];
8294 var spaceStrs = [""];
8147 function spaceStr(n) {
8295 function spaceStr(n) {
8148 while (spaceStrs.length <= n)
8296 while (spaceStrs.length <= n)
8149 spaceStrs.push(lst(spaceStrs) + " ");
8297 spaceStrs.push(lst(spaceStrs) + " ");
8150 return spaceStrs[n];
8298 return spaceStrs[n];
8151 }
8299 }
8152
8300
8153 function lst(arr) { return arr[arr.length-1]; }
8301 function lst(arr) { return arr[arr.length-1]; }
8154
8302
8155 var selectInput = function(node) { node.select(); };
8303 var selectInput = function(node) { node.select(); };
8156 if (ios) // Mobile Safari apparently has a bug where select() is broken.
8304 if (ios) // Mobile Safari apparently has a bug where select() is broken.
8157 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8305 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8158 else if (ie) // Suppress mysterious IE10 errors
8306 else if (ie) // Suppress mysterious IE10 errors
8159 selectInput = function(node) { try { node.select(); } catch(_e) {} };
8307 selectInput = function(node) { try { node.select(); } catch(_e) {} };
8160
8308
8161 function indexOf(array, elt) {
8309 function indexOf(array, elt) {
8162 for (var i = 0; i < array.length; ++i)
8310 for (var i = 0; i < array.length; ++i)
8163 if (array[i] == elt) return i;
8311 if (array[i] == elt) return i;
8164 return -1;
8312 return -1;
8165 }
8313 }
8166 function map(array, f) {
8314 function map(array, f) {
8167 var out = [];
8315 var out = [];
8168 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8316 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8169 return out;
8317 return out;
8170 }
8318 }
8171
8319
8172 function nothing() {}
8320 function nothing() {}
8173
8321
8174 function createObj(base, props) {
8322 function createObj(base, props) {
8175 var inst;
8323 var inst;
8176 if (Object.create) {
8324 if (Object.create) {
8177 inst = Object.create(base);
8325 inst = Object.create(base);
8178 } else {
8326 } else {
8179 nothing.prototype = base;
8327 nothing.prototype = base;
8180 inst = new nothing();
8328 inst = new nothing();
8181 }
8329 }
8182 if (props) copyObj(props, inst);
8330 if (props) copyObj(props, inst);
8183 return inst;
8331 return inst;
8184 };
8332 };
8185
8333
8186 function copyObj(obj, target, overwrite) {
8334 function copyObj(obj, target, overwrite) {
8187 if (!target) target = {};
8335 if (!target) target = {};
8188 for (var prop in obj)
8336 for (var prop in obj)
8189 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8337 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8190 target[prop] = obj[prop];
8338 target[prop] = obj[prop];
8191 return target;
8339 return target;
8192 }
8340 }
8193
8341
8194 function bind(f) {
8342 function bind(f) {
8195 var args = Array.prototype.slice.call(arguments, 1);
8343 var args = Array.prototype.slice.call(arguments, 1);
8196 return function(){return f.apply(null, args);};
8344 return function(){return f.apply(null, args);};
8197 }
8345 }
8198
8346
8199 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8347 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8200 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8348 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8201 return /\w/.test(ch) || ch > "\x80" &&
8349 return /\w/.test(ch) || ch > "\x80" &&
8202 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8350 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8203 };
8351 };
8204 function isWordChar(ch, helper) {
8352 function isWordChar(ch, helper) {
8205 if (!helper) return isWordCharBasic(ch);
8353 if (!helper) return isWordCharBasic(ch);
8206 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8354 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8207 return helper.test(ch);
8355 return helper.test(ch);
8208 }
8356 }
8209
8357
8210 function isEmpty(obj) {
8358 function isEmpty(obj) {
8211 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8359 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8212 return true;
8360 return true;
8213 }
8361 }
8214
8362
8215 // Extending unicode characters. A series of a non-extending char +
8363 // Extending unicode characters. A series of a non-extending char +
8216 // any number of extending chars is treated as a single unit as far
8364 // any number of extending chars is treated as a single unit as far
8217 // as editing and measuring is concerned. This is not fully correct,
8365 // as editing and measuring is concerned. This is not fully correct,
8218 // since some scripts/fonts/browsers also treat other configurations
8366 // since some scripts/fonts/browsers also treat other configurations
8219 // of code points as a group.
8367 // of code points as a group.
8220 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]/;
8368 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]/;
8221 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8369 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8222
8370
8223 // DOM UTILITIES
8371 // DOM UTILITIES
8224
8372
8225 function elt(tag, content, className, style) {
8373 function elt(tag, content, className, style) {
8226 var e = document.createElement(tag);
8374 var e = document.createElement(tag);
8227 if (className) e.className = className;
8375 if (className) e.className = className;
8228 if (style) e.style.cssText = style;
8376 if (style) e.style.cssText = style;
8229 if (typeof content == "string") e.appendChild(document.createTextNode(content));
8377 if (typeof content == "string") e.appendChild(document.createTextNode(content));
8230 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8378 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8231 return e;
8379 return e;
8232 }
8380 }
8233
8381
8234 var range;
8382 var range;
8235 if (document.createRange) range = function(node, start, end, endNode) {
8383 if (document.createRange) range = function(node, start, end, endNode) {
8236 var r = document.createRange();
8384 var r = document.createRange();
8237 r.setEnd(endNode || node, end);
8385 r.setEnd(endNode || node, end);
8238 r.setStart(node, start);
8386 r.setStart(node, start);
8239 return r;
8387 return r;
8240 };
8388 };
8241 else range = function(node, start, end) {
8389 else range = function(node, start, end) {
8242 var r = document.body.createTextRange();
8390 var r = document.body.createTextRange();
8243 try { r.moveToElementText(node.parentNode); }
8391 try { r.moveToElementText(node.parentNode); }
8244 catch(e) { return r; }
8392 catch(e) { return r; }
8245 r.collapse(true);
8393 r.collapse(true);
8246 r.moveEnd("character", end);
8394 r.moveEnd("character", end);
8247 r.moveStart("character", start);
8395 r.moveStart("character", start);
8248 return r;
8396 return r;
8249 };
8397 };
8250
8398
8251 function removeChildren(e) {
8399 function removeChildren(e) {
8252 for (var count = e.childNodes.length; count > 0; --count)
8400 for (var count = e.childNodes.length; count > 0; --count)
8253 e.removeChild(e.firstChild);
8401 e.removeChild(e.firstChild);
8254 return e;
8402 return e;
8255 }
8403 }
8256
8404
8257 function removeChildrenAndAdd(parent, e) {
8405 function removeChildrenAndAdd(parent, e) {
8258 return removeChildren(parent).appendChild(e);
8406 return removeChildren(parent).appendChild(e);
8259 }
8407 }
8260
8408
8261 var contains = CodeMirror.contains = function(parent, child) {
8409 var contains = CodeMirror.contains = function(parent, child) {
8262 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8410 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8263 child = child.parentNode;
8411 child = child.parentNode;
8264 if (parent.contains)
8412 if (parent.contains)
8265 return parent.contains(child);
8413 return parent.contains(child);
8266 do {
8414 do {
8267 if (child.nodeType == 11) child = child.host;
8415 if (child.nodeType == 11) child = child.host;
8268 if (child == parent) return true;
8416 if (child == parent) return true;
8269 } while (child = child.parentNode);
8417 } while (child = child.parentNode);
8270 };
8418 };
8271
8419
8272 function activeElt() { return document.activeElement; }
8420 function activeElt() {
8421 var activeElement = document.activeElement;
8422 while (activeElement && activeElement.root && activeElement.root.activeElement)
8423 activeElement = activeElement.root.activeElement;
8424 return activeElement;
8425 }
8273 // Older versions of IE throws unspecified error when touching
8426 // Older versions of IE throws unspecified error when touching
8274 // document.activeElement in some cases (during loading, in iframe)
8427 // document.activeElement in some cases (during loading, in iframe)
8275 if (ie && ie_version < 11) activeElt = function() {
8428 if (ie && ie_version < 11) activeElt = function() {
8276 try { return document.activeElement; }
8429 try { return document.activeElement; }
8277 catch(e) { return document.body; }
8430 catch(e) { return document.body; }
8278 };
8431 };
8279
8432
8280 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8433 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8281 var rmClass = CodeMirror.rmClass = function(node, cls) {
8434 var rmClass = CodeMirror.rmClass = function(node, cls) {
8282 var current = node.className;
8435 var current = node.className;
8283 var match = classTest(cls).exec(current);
8436 var match = classTest(cls).exec(current);
8284 if (match) {
8437 if (match) {
8285 var after = current.slice(match.index + match[0].length);
8438 var after = current.slice(match.index + match[0].length);
8286 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8439 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8287 }
8440 }
8288 };
8441 };
8289 var addClass = CodeMirror.addClass = function(node, cls) {
8442 var addClass = CodeMirror.addClass = function(node, cls) {
8290 var current = node.className;
8443 var current = node.className;
8291 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8444 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8292 };
8445 };
8293 function joinClasses(a, b) {
8446 function joinClasses(a, b) {
8294 var as = a.split(" ");
8447 var as = a.split(" ");
8295 for (var i = 0; i < as.length; i++)
8448 for (var i = 0; i < as.length; i++)
8296 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8449 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8297 return b;
8450 return b;
8298 }
8451 }
8299
8452
8300 // WINDOW-WIDE EVENTS
8453 // WINDOW-WIDE EVENTS
8301
8454
8302 // These must be handled carefully, because naively registering a
8455 // These must be handled carefully, because naively registering a
8303 // handler for each editor will cause the editors to never be
8456 // handler for each editor will cause the editors to never be
8304 // garbage collected.
8457 // garbage collected.
8305
8458
8306 function forEachCodeMirror(f) {
8459 function forEachCodeMirror(f) {
8307 if (!document.body.getElementsByClassName) return;
8460 if (!document.body.getElementsByClassName) return;
8308 var byClass = document.body.getElementsByClassName("CodeMirror");
8461 var byClass = document.body.getElementsByClassName("CodeMirror");
8309 for (var i = 0; i < byClass.length; i++) {
8462 for (var i = 0; i < byClass.length; i++) {
8310 var cm = byClass[i].CodeMirror;
8463 var cm = byClass[i].CodeMirror;
8311 if (cm) f(cm);
8464 if (cm) f(cm);
8312 }
8465 }
8313 }
8466 }
8314
8467
8315 var globalsRegistered = false;
8468 var globalsRegistered = false;
8316 function ensureGlobalHandlers() {
8469 function ensureGlobalHandlers() {
8317 if (globalsRegistered) return;
8470 if (globalsRegistered) return;
8318 registerGlobalHandlers();
8471 registerGlobalHandlers();
8319 globalsRegistered = true;
8472 globalsRegistered = true;
8320 }
8473 }
8321 function registerGlobalHandlers() {
8474 function registerGlobalHandlers() {
8322 // When the window resizes, we need to refresh active editors.
8475 // When the window resizes, we need to refresh active editors.
8323 var resizeTimer;
8476 var resizeTimer;
8324 on(window, "resize", function() {
8477 on(window, "resize", function() {
8325 if (resizeTimer == null) resizeTimer = setTimeout(function() {
8478 if (resizeTimer == null) resizeTimer = setTimeout(function() {
8326 resizeTimer = null;
8479 resizeTimer = null;
8327 forEachCodeMirror(onResize);
8480 forEachCodeMirror(onResize);
8328 }, 100);
8481 }, 100);
8329 });
8482 });
8330 // When the window loses focus, we want to show the editor as blurred
8483 // When the window loses focus, we want to show the editor as blurred
8331 on(window, "blur", function() {
8484 on(window, "blur", function() {
8332 forEachCodeMirror(onBlur);
8485 forEachCodeMirror(onBlur);
8333 });
8486 });
8334 }
8487 }
8335
8488
8336 // FEATURE DETECTION
8489 // FEATURE DETECTION
8337
8490
8338 // Detect drag-and-drop
8491 // Detect drag-and-drop
8339 var dragAndDrop = function() {
8492 var dragAndDrop = function() {
8340 // There is *some* kind of drag-and-drop support in IE6-8, but I
8493 // There is *some* kind of drag-and-drop support in IE6-8, but I
8341 // couldn't get it to work yet.
8494 // couldn't get it to work yet.
8342 if (ie && ie_version < 9) return false;
8495 if (ie && ie_version < 9) return false;
8343 var div = elt('div');
8496 var div = elt('div');
8344 return "draggable" in div || "dragDrop" in div;
8497 return "draggable" in div || "dragDrop" in div;
8345 }();
8498 }();
8346
8499
8347 var zwspSupported;
8500 var zwspSupported;
8348 function zeroWidthElement(measure) {
8501 function zeroWidthElement(measure) {
8349 if (zwspSupported == null) {
8502 if (zwspSupported == null) {
8350 var test = elt("span", "\u200b");
8503 var test = elt("span", "\u200b");
8351 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8504 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8352 if (measure.firstChild.offsetHeight != 0)
8505 if (measure.firstChild.offsetHeight != 0)
8353 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8506 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8354 }
8507 }
8355 var node = zwspSupported ? elt("span", "\u200b") :
8508 var node = zwspSupported ? elt("span", "\u200b") :
8356 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8509 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8357 node.setAttribute("cm-text", "");
8510 node.setAttribute("cm-text", "");
8358 return node;
8511 return node;
8359 }
8512 }
8360
8513
8361 // Feature-detect IE's crummy client rect reporting for bidi text
8514 // Feature-detect IE's crummy client rect reporting for bidi text
8362 var badBidiRects;
8515 var badBidiRects;
8363 function hasBadBidiRects(measure) {
8516 function hasBadBidiRects(measure) {
8364 if (badBidiRects != null) return badBidiRects;
8517 if (badBidiRects != null) return badBidiRects;
8365 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8518 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8366 var r0 = range(txt, 0, 1).getBoundingClientRect();
8519 var r0 = range(txt, 0, 1).getBoundingClientRect();
8367 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8520 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8368 var r1 = range(txt, 1, 2).getBoundingClientRect();
8521 var r1 = range(txt, 1, 2).getBoundingClientRect();
8369 return badBidiRects = (r1.right - r0.right < 3);
8522 return badBidiRects = (r1.right - r0.right < 3);
8370 }
8523 }
8371
8524
8372 // See if "".split is the broken IE version, if so, provide an
8525 // See if "".split is the broken IE version, if so, provide an
8373 // alternative way to split lines.
8526 // alternative way to split lines.
8374 var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8527 var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8375 var pos = 0, result = [], l = string.length;
8528 var pos = 0, result = [], l = string.length;
8376 while (pos <= l) {
8529 while (pos <= l) {
8377 var nl = string.indexOf("\n", pos);
8530 var nl = string.indexOf("\n", pos);
8378 if (nl == -1) nl = string.length;
8531 if (nl == -1) nl = string.length;
8379 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8532 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8380 var rt = line.indexOf("\r");
8533 var rt = line.indexOf("\r");
8381 if (rt != -1) {
8534 if (rt != -1) {
8382 result.push(line.slice(0, rt));
8535 result.push(line.slice(0, rt));
8383 pos += rt + 1;
8536 pos += rt + 1;
8384 } else {
8537 } else {
8385 result.push(line);
8538 result.push(line);
8386 pos = nl + 1;
8539 pos = nl + 1;
8387 }
8540 }
8388 }
8541 }
8389 return result;
8542 return result;
8390 } : function(string){return string.split(/\r\n?|\n/);};
8543 } : function(string){return string.split(/\r\n?|\n/);};
8391
8544
8392 var hasSelection = window.getSelection ? function(te) {
8545 var hasSelection = window.getSelection ? function(te) {
8393 try { return te.selectionStart != te.selectionEnd; }
8546 try { return te.selectionStart != te.selectionEnd; }
8394 catch(e) { return false; }
8547 catch(e) { return false; }
8395 } : function(te) {
8548 } : function(te) {
8396 try {var range = te.ownerDocument.selection.createRange();}
8549 try {var range = te.ownerDocument.selection.createRange();}
8397 catch(e) {}
8550 catch(e) {}
8398 if (!range || range.parentElement() != te) return false;
8551 if (!range || range.parentElement() != te) return false;
8399 return range.compareEndPoints("StartToEnd", range) != 0;
8552 return range.compareEndPoints("StartToEnd", range) != 0;
8400 };
8553 };
8401
8554
8402 var hasCopyEvent = (function() {
8555 var hasCopyEvent = (function() {
8403 var e = elt("div");
8556 var e = elt("div");
8404 if ("oncopy" in e) return true;
8557 if ("oncopy" in e) return true;
8405 e.setAttribute("oncopy", "return;");
8558 e.setAttribute("oncopy", "return;");
8406 return typeof e.oncopy == "function";
8559 return typeof e.oncopy == "function";
8407 })();
8560 })();
8408
8561
8409 var badZoomedRects = null;
8562 var badZoomedRects = null;
8410 function hasBadZoomedRects(measure) {
8563 function hasBadZoomedRects(measure) {
8411 if (badZoomedRects != null) return badZoomedRects;
8564 if (badZoomedRects != null) return badZoomedRects;
8412 var node = removeChildrenAndAdd(measure, elt("span", "x"));
8565 var node = removeChildrenAndAdd(measure, elt("span", "x"));
8413 var normal = node.getBoundingClientRect();
8566 var normal = node.getBoundingClientRect();
8414 var fromRange = range(node, 0, 1).getBoundingClientRect();
8567 var fromRange = range(node, 0, 1).getBoundingClientRect();
8415 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8568 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8416 }
8569 }
8417
8570
8418 // KEY NAMES
8571 // KEY NAMES
8419
8572
8420 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8573 var keyNames = CodeMirror.keyNames = {
8421 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8574 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8422 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8575 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8423 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete",
8576 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8424 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8577 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8425 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8578 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
8426 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"};
8579 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8427 CodeMirror.keyNames = keyNames;
8580 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8581 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8582 };
8428 (function() {
8583 (function() {
8429 // Number keys
8584 // Number keys
8430 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8585 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8431 // Alphabetic keys
8586 // Alphabetic keys
8432 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8587 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8433 // Function keys
8588 // Function keys
8434 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8589 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8435 })();
8590 })();
8436
8591
8437 // BIDI HELPERS
8592 // BIDI HELPERS
8438
8593
8439 function iterateBidiSections(order, from, to, f) {
8594 function iterateBidiSections(order, from, to, f) {
8440 if (!order) return f(from, to, "ltr");
8595 if (!order) return f(from, to, "ltr");
8441 var found = false;
8596 var found = false;
8442 for (var i = 0; i < order.length; ++i) {
8597 for (var i = 0; i < order.length; ++i) {
8443 var part = order[i];
8598 var part = order[i];
8444 if (part.from < to && part.to > from || from == to && part.to == from) {
8599 if (part.from < to && part.to > from || from == to && part.to == from) {
8445 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
8600 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
8446 found = true;
8601 found = true;
8447 }
8602 }
8448 }
8603 }
8449 if (!found) f(from, to, "ltr");
8604 if (!found) f(from, to, "ltr");
8450 }
8605 }
8451
8606
8452 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8607 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8453 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8608 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8454
8609
8455 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8610 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8456 function lineRight(line) {
8611 function lineRight(line) {
8457 var order = getOrder(line);
8612 var order = getOrder(line);
8458 if (!order) return line.text.length;
8613 if (!order) return line.text.length;
8459 return bidiRight(lst(order));
8614 return bidiRight(lst(order));
8460 }
8615 }
8461
8616
8462 function lineStart(cm, lineN) {
8617 function lineStart(cm, lineN) {
8463 var line = getLine(cm.doc, lineN);
8618 var line = getLine(cm.doc, lineN);
8464 var visual = visualLine(line);
8619 var visual = visualLine(line);
8465 if (visual != line) lineN = lineNo(visual);
8620 if (visual != line) lineN = lineNo(visual);
8466 var order = getOrder(visual);
8621 var order = getOrder(visual);
8467 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8622 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8468 return Pos(lineN, ch);
8623 return Pos(lineN, ch);
8469 }
8624 }
8470 function lineEnd(cm, lineN) {
8625 function lineEnd(cm, lineN) {
8471 var merged, line = getLine(cm.doc, lineN);
8626 var merged, line = getLine(cm.doc, lineN);
8472 while (merged = collapsedSpanAtEnd(line)) {
8627 while (merged = collapsedSpanAtEnd(line)) {
8473 line = merged.find(1, true).line;
8628 line = merged.find(1, true).line;
8474 lineN = null;
8629 lineN = null;
8475 }
8630 }
8476 var order = getOrder(line);
8631 var order = getOrder(line);
8477 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8632 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8478 return Pos(lineN == null ? lineNo(line) : lineN, ch);
8633 return Pos(lineN == null ? lineNo(line) : lineN, ch);
8479 }
8634 }
8480 function lineStartSmart(cm, pos) {
8635 function lineStartSmart(cm, pos) {
8481 var start = lineStart(cm, pos.line);
8636 var start = lineStart(cm, pos.line);
8482 var line = getLine(cm.doc, start.line);
8637 var line = getLine(cm.doc, start.line);
8483 var order = getOrder(line);
8638 var order = getOrder(line);
8484 if (!order || order[0].level == 0) {
8639 if (!order || order[0].level == 0) {
8485 var firstNonWS = Math.max(0, line.text.search(/\S/));
8640 var firstNonWS = Math.max(0, line.text.search(/\S/));
8486 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8641 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8487 return Pos(start.line, inWS ? 0 : firstNonWS);
8642 return Pos(start.line, inWS ? 0 : firstNonWS);
8488 }
8643 }
8489 return start;
8644 return start;
8490 }
8645 }
8491
8646
8492 function compareBidiLevel(order, a, b) {
8647 function compareBidiLevel(order, a, b) {
8493 var linedir = order[0].level;
8648 var linedir = order[0].level;
8494 if (a == linedir) return true;
8649 if (a == linedir) return true;
8495 if (b == linedir) return false;
8650 if (b == linedir) return false;
8496 return a < b;
8651 return a < b;
8497 }
8652 }
8498 var bidiOther;
8653 var bidiOther;
8499 function getBidiPartAt(order, pos) {
8654 function getBidiPartAt(order, pos) {
8500 bidiOther = null;
8655 bidiOther = null;
8501 for (var i = 0, found; i < order.length; ++i) {
8656 for (var i = 0, found; i < order.length; ++i) {
8502 var cur = order[i];
8657 var cur = order[i];
8503 if (cur.from < pos && cur.to > pos) return i;
8658 if (cur.from < pos && cur.to > pos) return i;
8504 if ((cur.from == pos || cur.to == pos)) {
8659 if ((cur.from == pos || cur.to == pos)) {
8505 if (found == null) {
8660 if (found == null) {
8506 found = i;
8661 found = i;
8507 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8662 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8508 if (cur.from != cur.to) bidiOther = found;
8663 if (cur.from != cur.to) bidiOther = found;
8509 return i;
8664 return i;
8510 } else {
8665 } else {
8511 if (cur.from != cur.to) bidiOther = i;
8666 if (cur.from != cur.to) bidiOther = i;
8512 return found;
8667 return found;
8513 }
8668 }
8514 }
8669 }
8515 }
8670 }
8516 return found;
8671 return found;
8517 }
8672 }
8518
8673
8519 function moveInLine(line, pos, dir, byUnit) {
8674 function moveInLine(line, pos, dir, byUnit) {
8520 if (!byUnit) return pos + dir;
8675 if (!byUnit) return pos + dir;
8521 do pos += dir;
8676 do pos += dir;
8522 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8677 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8523 return pos;
8678 return pos;
8524 }
8679 }
8525
8680
8526 // This is needed in order to move 'visually' through bi-directional
8681 // This is needed in order to move 'visually' through bi-directional
8527 // text -- i.e., pressing left should make the cursor go left, even
8682 // text -- i.e., pressing left should make the cursor go left, even
8528 // when in RTL text. The tricky part is the 'jumps', where RTL and
8683 // when in RTL text. The tricky part is the 'jumps', where RTL and
8529 // LTR text touch each other. This often requires the cursor offset
8684 // LTR text touch each other. This often requires the cursor offset
8530 // to move more than one unit, in order to visually move one unit.
8685 // to move more than one unit, in order to visually move one unit.
8531 function moveVisually(line, start, dir, byUnit) {
8686 function moveVisually(line, start, dir, byUnit) {
8532 var bidi = getOrder(line);
8687 var bidi = getOrder(line);
8533 if (!bidi) return moveLogically(line, start, dir, byUnit);
8688 if (!bidi) return moveLogically(line, start, dir, byUnit);
8534 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8689 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8535 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8690 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8536
8691
8537 for (;;) {
8692 for (;;) {
8538 if (target > part.from && target < part.to) return target;
8693 if (target > part.from && target < part.to) return target;
8539 if (target == part.from || target == part.to) {
8694 if (target == part.from || target == part.to) {
8540 if (getBidiPartAt(bidi, target) == pos) return target;
8695 if (getBidiPartAt(bidi, target) == pos) return target;
8541 part = bidi[pos += dir];
8696 part = bidi[pos += dir];
8542 return (dir > 0) == part.level % 2 ? part.to : part.from;
8697 return (dir > 0) == part.level % 2 ? part.to : part.from;
8543 } else {
8698 } else {
8544 part = bidi[pos += dir];
8699 part = bidi[pos += dir];
8545 if (!part) return null;
8700 if (!part) return null;
8546 if ((dir > 0) == part.level % 2)
8701 if ((dir > 0) == part.level % 2)
8547 target = moveInLine(line, part.to, -1, byUnit);
8702 target = moveInLine(line, part.to, -1, byUnit);
8548 else
8703 else
8549 target = moveInLine(line, part.from, 1, byUnit);
8704 target = moveInLine(line, part.from, 1, byUnit);
8550 }
8705 }
8551 }
8706 }
8552 }
8707 }
8553
8708
8554 function moveLogically(line, start, dir, byUnit) {
8709 function moveLogically(line, start, dir, byUnit) {
8555 var target = start + dir;
8710 var target = start + dir;
8556 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8711 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8557 return target < 0 || target > line.text.length ? null : target;
8712 return target < 0 || target > line.text.length ? null : target;
8558 }
8713 }
8559
8714
8560 // Bidirectional ordering algorithm
8715 // Bidirectional ordering algorithm
8561 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8716 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8562 // that this (partially) implements.
8717 // that this (partially) implements.
8563
8718
8564 // One-char codes used for character types:
8719 // One-char codes used for character types:
8565 // L (L): Left-to-Right
8720 // L (L): Left-to-Right
8566 // R (R): Right-to-Left
8721 // R (R): Right-to-Left
8567 // r (AL): Right-to-Left Arabic
8722 // r (AL): Right-to-Left Arabic
8568 // 1 (EN): European Number
8723 // 1 (EN): European Number
8569 // + (ES): European Number Separator
8724 // + (ES): European Number Separator
8570 // % (ET): European Number Terminator
8725 // % (ET): European Number Terminator
8571 // n (AN): Arabic Number
8726 // n (AN): Arabic Number
8572 // , (CS): Common Number Separator
8727 // , (CS): Common Number Separator
8573 // m (NSM): Non-Spacing Mark
8728 // m (NSM): Non-Spacing Mark
8574 // b (BN): Boundary Neutral
8729 // b (BN): Boundary Neutral
8575 // s (B): Paragraph Separator
8730 // s (B): Paragraph Separator
8576 // t (S): Segment Separator
8731 // t (S): Segment Separator
8577 // w (WS): Whitespace
8732 // w (WS): Whitespace
8578 // N (ON): Other Neutrals
8733 // N (ON): Other Neutrals
8579
8734
8580 // Returns null if characters are ordered as they appear
8735 // Returns null if characters are ordered as they appear
8581 // (left-to-right), or an array of sections ({from, to, level}
8736 // (left-to-right), or an array of sections ({from, to, level}
8582 // objects) in the order in which they occur visually.
8737 // objects) in the order in which they occur visually.
8583 var bidiOrdering = (function() {
8738 var bidiOrdering = (function() {
8584 // Character types for codepoints 0 to 0xff
8739 // Character types for codepoints 0 to 0xff
8585 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8740 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8586 // Character types for codepoints 0x600 to 0x6ff
8741 // Character types for codepoints 0x600 to 0x6ff
8587 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8742 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8588 function charType(code) {
8743 function charType(code) {
8589 if (code <= 0xf7) return lowTypes.charAt(code);
8744 if (code <= 0xf7) return lowTypes.charAt(code);
8590 else if (0x590 <= code && code <= 0x5f4) return "R";
8745 else if (0x590 <= code && code <= 0x5f4) return "R";
8591 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8746 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8592 else if (0x6ee <= code && code <= 0x8ac) return "r";
8747 else if (0x6ee <= code && code <= 0x8ac) return "r";
8593 else if (0x2000 <= code && code <= 0x200b) return "w";
8748 else if (0x2000 <= code && code <= 0x200b) return "w";
8594 else if (code == 0x200c) return "b";
8749 else if (code == 0x200c) return "b";
8595 else return "L";
8750 else return "L";
8596 }
8751 }
8597
8752
8598 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8753 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8599 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8754 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8600 // Browsers seem to always treat the boundaries of block elements as being L.
8755 // Browsers seem to always treat the boundaries of block elements as being L.
8601 var outerType = "L";
8756 var outerType = "L";
8602
8757
8603 function BidiSpan(level, from, to) {
8758 function BidiSpan(level, from, to) {
8604 this.level = level;
8759 this.level = level;
8605 this.from = from; this.to = to;
8760 this.from = from; this.to = to;
8606 }
8761 }
8607
8762
8608 return function(str) {
8763 return function(str) {
8609 if (!bidiRE.test(str)) return false;
8764 if (!bidiRE.test(str)) return false;
8610 var len = str.length, types = [];
8765 var len = str.length, types = [];
8611 for (var i = 0, type; i < len; ++i)
8766 for (var i = 0, type; i < len; ++i)
8612 types.push(type = charType(str.charCodeAt(i)));
8767 types.push(type = charType(str.charCodeAt(i)));
8613
8768
8614 // W1. Examine each non-spacing mark (NSM) in the level run, and
8769 // W1. Examine each non-spacing mark (NSM) in the level run, and
8615 // change the type of the NSM to the type of the previous
8770 // change the type of the NSM to the type of the previous
8616 // character. If the NSM is at the start of the level run, it will
8771 // character. If the NSM is at the start of the level run, it will
8617 // get the type of sor.
8772 // get the type of sor.
8618 for (var i = 0, prev = outerType; i < len; ++i) {
8773 for (var i = 0, prev = outerType; i < len; ++i) {
8619 var type = types[i];
8774 var type = types[i];
8620 if (type == "m") types[i] = prev;
8775 if (type == "m") types[i] = prev;
8621 else prev = type;
8776 else prev = type;
8622 }
8777 }
8623
8778
8624 // W2. Search backwards from each instance of a European number
8779 // W2. Search backwards from each instance of a European number
8625 // until the first strong type (R, L, AL, or sor) is found. If an
8780 // until the first strong type (R, L, AL, or sor) is found. If an
8626 // AL is found, change the type of the European number to Arabic
8781 // AL is found, change the type of the European number to Arabic
8627 // number.
8782 // number.
8628 // W3. Change all ALs to R.
8783 // W3. Change all ALs to R.
8629 for (var i = 0, cur = outerType; i < len; ++i) {
8784 for (var i = 0, cur = outerType; i < len; ++i) {
8630 var type = types[i];
8785 var type = types[i];
8631 if (type == "1" && cur == "r") types[i] = "n";
8786 if (type == "1" && cur == "r") types[i] = "n";
8632 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8787 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8633 }
8788 }
8634
8789
8635 // W4. A single European separator between two European numbers
8790 // W4. A single European separator between two European numbers
8636 // changes to a European number. A single common separator between
8791 // changes to a European number. A single common separator between
8637 // two numbers of the same type changes to that type.
8792 // two numbers of the same type changes to that type.
8638 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8793 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8639 var type = types[i];
8794 var type = types[i];
8640 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8795 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8641 else if (type == "," && prev == types[i+1] &&
8796 else if (type == "," && prev == types[i+1] &&
8642 (prev == "1" || prev == "n")) types[i] = prev;
8797 (prev == "1" || prev == "n")) types[i] = prev;
8643 prev = type;
8798 prev = type;
8644 }
8799 }
8645
8800
8646 // W5. A sequence of European terminators adjacent to European
8801 // W5. A sequence of European terminators adjacent to European
8647 // numbers changes to all European numbers.
8802 // numbers changes to all European numbers.
8648 // W6. Otherwise, separators and terminators change to Other
8803 // W6. Otherwise, separators and terminators change to Other
8649 // Neutral.
8804 // Neutral.
8650 for (var i = 0; i < len; ++i) {
8805 for (var i = 0; i < len; ++i) {
8651 var type = types[i];
8806 var type = types[i];
8652 if (type == ",") types[i] = "N";
8807 if (type == ",") types[i] = "N";
8653 else if (type == "%") {
8808 else if (type == "%") {
8654 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8809 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8655 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8810 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8656 for (var j = i; j < end; ++j) types[j] = replace;
8811 for (var j = i; j < end; ++j) types[j] = replace;
8657 i = end - 1;
8812 i = end - 1;
8658 }
8813 }
8659 }
8814 }
8660
8815
8661 // W7. Search backwards from each instance of a European number
8816 // W7. Search backwards from each instance of a European number
8662 // until the first strong type (R, L, or sor) is found. If an L is
8817 // until the first strong type (R, L, or sor) is found. If an L is
8663 // found, then change the type of the European number to L.
8818 // found, then change the type of the European number to L.
8664 for (var i = 0, cur = outerType; i < len; ++i) {
8819 for (var i = 0, cur = outerType; i < len; ++i) {
8665 var type = types[i];
8820 var type = types[i];
8666 if (cur == "L" && type == "1") types[i] = "L";
8821 if (cur == "L" && type == "1") types[i] = "L";
8667 else if (isStrong.test(type)) cur = type;
8822 else if (isStrong.test(type)) cur = type;
8668 }
8823 }
8669
8824
8670 // N1. A sequence of neutrals takes the direction of the
8825 // N1. A sequence of neutrals takes the direction of the
8671 // surrounding strong text if the text on both sides has the same
8826 // surrounding strong text if the text on both sides has the same
8672 // direction. European and Arabic numbers act as if they were R in
8827 // direction. European and Arabic numbers act as if they were R in
8673 // terms of their influence on neutrals. Start-of-level-run (sor)
8828 // terms of their influence on neutrals. Start-of-level-run (sor)
8674 // and end-of-level-run (eor) are used at level run boundaries.
8829 // and end-of-level-run (eor) are used at level run boundaries.
8675 // N2. Any remaining neutrals take the embedding direction.
8830 // N2. Any remaining neutrals take the embedding direction.
8676 for (var i = 0; i < len; ++i) {
8831 for (var i = 0; i < len; ++i) {
8677 if (isNeutral.test(types[i])) {
8832 if (isNeutral.test(types[i])) {
8678 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8833 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8679 var before = (i ? types[i-1] : outerType) == "L";
8834 var before = (i ? types[i-1] : outerType) == "L";
8680 var after = (end < len ? types[end] : outerType) == "L";
8835 var after = (end < len ? types[end] : outerType) == "L";
8681 var replace = before || after ? "L" : "R";
8836 var replace = before || after ? "L" : "R";
8682 for (var j = i; j < end; ++j) types[j] = replace;
8837 for (var j = i; j < end; ++j) types[j] = replace;
8683 i = end - 1;
8838 i = end - 1;
8684 }
8839 }
8685 }
8840 }
8686
8841
8687 // Here we depart from the documented algorithm, in order to avoid
8842 // Here we depart from the documented algorithm, in order to avoid
8688 // building up an actual levels array. Since there are only three
8843 // building up an actual levels array. Since there are only three
8689 // levels (0, 1, 2) in an implementation that doesn't take
8844 // levels (0, 1, 2) in an implementation that doesn't take
8690 // explicit embedding into account, we can build up the order on
8845 // explicit embedding into account, we can build up the order on
8691 // the fly, without following the level-based algorithm.
8846 // the fly, without following the level-based algorithm.
8692 var order = [], m;
8847 var order = [], m;
8693 for (var i = 0; i < len;) {
8848 for (var i = 0; i < len;) {
8694 if (countsAsLeft.test(types[i])) {
8849 if (countsAsLeft.test(types[i])) {
8695 var start = i;
8850 var start = i;
8696 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8851 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8697 order.push(new BidiSpan(0, start, i));
8852 order.push(new BidiSpan(0, start, i));
8698 } else {
8853 } else {
8699 var pos = i, at = order.length;
8854 var pos = i, at = order.length;
8700 for (++i; i < len && types[i] != "L"; ++i) {}
8855 for (++i; i < len && types[i] != "L"; ++i) {}
8701 for (var j = pos; j < i;) {
8856 for (var j = pos; j < i;) {
8702 if (countsAsNum.test(types[j])) {
8857 if (countsAsNum.test(types[j])) {
8703 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8858 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8704 var nstart = j;
8859 var nstart = j;
8705 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8860 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8706 order.splice(at, 0, new BidiSpan(2, nstart, j));
8861 order.splice(at, 0, new BidiSpan(2, nstart, j));
8707 pos = j;
8862 pos = j;
8708 } else ++j;
8863 } else ++j;
8709 }
8864 }
8710 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8865 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8711 }
8866 }
8712 }
8867 }
8713 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8868 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8714 order[0].from = m[0].length;
8869 order[0].from = m[0].length;
8715 order.unshift(new BidiSpan(0, 0, m[0].length));
8870 order.unshift(new BidiSpan(0, 0, m[0].length));
8716 }
8871 }
8717 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8872 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8718 lst(order).to -= m[0].length;
8873 lst(order).to -= m[0].length;
8719 order.push(new BidiSpan(0, len - m[0].length, len));
8874 order.push(new BidiSpan(0, len - m[0].length, len));
8720 }
8875 }
8721 if (order[0].level == 2)
8876 if (order[0].level == 2)
8722 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8877 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8723 if (order[0].level != lst(order).level)
8878 if (order[0].level != lst(order).level)
8724 order.push(new BidiSpan(order[0].level, len, len));
8879 order.push(new BidiSpan(order[0].level, len, len));
8725
8880
8726 return order;
8881 return order;
8727 };
8882 };
8728 })();
8883 })();
8729
8884
8730 // THE END
8885 // THE END
8731
8886
8732 CodeMirror.version = "5.4.0";
8887 CodeMirror.version = "5.11.0";
8733
8888
8734 return CodeMirror;
8889 return CodeMirror;
8735 });
8890 });
@@ -1,383 +1,440 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 "use strict";
12 "use strict";
13
13
14 var HINT_ELEMENT_CLASS = "CodeMirror-hint";
14 var HINT_ELEMENT_CLASS = "CodeMirror-hint";
15 var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
15 var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
16
16
17 // This is the old interface, kept around for now to stay
17 // This is the old interface, kept around for now to stay
18 // backwards-compatible.
18 // backwards-compatible.
19 CodeMirror.showHint = function(cm, getHints, options) {
19 CodeMirror.showHint = function(cm, getHints, options) {
20 if (!getHints) return cm.showHint(options);
20 if (!getHints) return cm.showHint(options);
21 if (options && options.async) getHints.async = true;
21 if (options && options.async) getHints.async = true;
22 var newOpts = {hint: getHints};
22 var newOpts = {hint: getHints};
23 if (options) for (var prop in options) newOpts[prop] = options[prop];
23 if (options) for (var prop in options) newOpts[prop] = options[prop];
24 return cm.showHint(newOpts);
24 return cm.showHint(newOpts);
25 };
25 };
26
26
27 CodeMirror.defineExtension("showHint", function(options) {
27 CodeMirror.defineExtension("showHint", function(options) {
28 // We want a single cursor position.
28 options = parseOptions(this, this.getCursor("start"), options);
29 if (this.listSelections().length > 1 || this.somethingSelected()) return;
29 var selections = this.listSelections()
30 if (selections.length > 1) return;
31 // By default, don't allow completion when something is selected.
32 // A hint function can have a `supportsSelection` property to
33 // indicate that it can handle selections.
34 if (this.somethingSelected()) {
35 if (!options.hint.supportsSelection) return;
36 // Don't try with cross-line selections
37 for (var i = 0; i < selections.length; i++)
38 if (selections[i].head.line != selections[i].anchor.line) return;
39 }
30
40
31 if (this.state.completionActive) this.state.completionActive.close();
41 if (this.state.completionActive) this.state.completionActive.close();
32 var completion = this.state.completionActive = new Completion(this, options);
42 var completion = this.state.completionActive = new Completion(this, options);
33 if (!completion.options.hint) return;
43 if (!completion.options.hint) return;
34
44
35 CodeMirror.signal(this, "startCompletion", this);
45 CodeMirror.signal(this, "startCompletion", this);
36 completion.update(true);
46 completion.update(true);
37 });
47 });
38
48
39 function Completion(cm, options) {
49 function Completion(cm, options) {
40 this.cm = cm;
50 this.cm = cm;
41 this.options = this.buildOptions(options);
51 this.options = options;
42 this.widget = null;
52 this.widget = null;
43 this.debounce = 0;
53 this.debounce = 0;
44 this.tick = 0;
54 this.tick = 0;
45 this.startPos = this.cm.getCursor();
55 this.startPos = this.cm.getCursor("start");
46 this.startLen = this.cm.getLine(this.startPos.line).length;
56 this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
47
57
48 var self = this;
58 var self = this;
49 cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
59 cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
50 }
60 }
51
61
52 var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
62 var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
53 return setTimeout(fn, 1000/60);
63 return setTimeout(fn, 1000/60);
54 };
64 };
55 var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
65 var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
56
66
57 Completion.prototype = {
67 Completion.prototype = {
58 close: function() {
68 close: function() {
59 if (!this.active()) return;
69 if (!this.active()) return;
60 this.cm.state.completionActive = null;
70 this.cm.state.completionActive = null;
61 this.tick = null;
71 this.tick = null;
62 this.cm.off("cursorActivity", this.activityFunc);
72 this.cm.off("cursorActivity", this.activityFunc);
63
73
64 if (this.widget && this.data) CodeMirror.signal(this.data, "close");
74 if (this.widget && this.data) CodeMirror.signal(this.data, "close");
65 if (this.widget) this.widget.close();
75 if (this.widget) this.widget.close();
66 CodeMirror.signal(this.cm, "endCompletion", this.cm);
76 CodeMirror.signal(this.cm, "endCompletion", this.cm);
67 },
77 },
68
78
69 active: function() {
79 active: function() {
70 return this.cm.state.completionActive == this;
80 return this.cm.state.completionActive == this;
71 },
81 },
72
82
73 pick: function(data, i) {
83 pick: function(data, i) {
74 var completion = data.list[i];
84 var completion = data.list[i];
75 if (completion.hint) completion.hint(this.cm, data, completion);
85 if (completion.hint) completion.hint(this.cm, data, completion);
76 else this.cm.replaceRange(getText(completion), completion.from || data.from,
86 else this.cm.replaceRange(getText(completion), completion.from || data.from,
77 completion.to || data.to, "complete");
87 completion.to || data.to, "complete");
78 CodeMirror.signal(data, "pick", completion);
88 CodeMirror.signal(data, "pick", completion);
79 this.close();
89 this.close();
80 },
90 },
81
91
82 cursorActivity: function() {
92 cursorActivity: function() {
83 if (this.debounce) {
93 if (this.debounce) {
84 cancelAnimationFrame(this.debounce);
94 cancelAnimationFrame(this.debounce);
85 this.debounce = 0;
95 this.debounce = 0;
86 }
96 }
87
97
88 var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
98 var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
89 if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
99 if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
90 pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
100 pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
91 (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
101 (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
92 this.close();
102 this.close();
93 } else {
103 } else {
94 var self = this;
104 var self = this;
95 this.debounce = requestAnimationFrame(function() {self.update();});
105 this.debounce = requestAnimationFrame(function() {self.update();});
96 if (this.widget) this.widget.disable();
106 if (this.widget) this.widget.disable();
97 }
107 }
98 },
108 },
99
109
100 update: function(first) {
110 update: function(first) {
101 if (this.tick == null) return;
111 if (this.tick == null) return;
102 if (this.data) CodeMirror.signal(this.data, "update");
103 if (!this.options.hint.async) {
112 if (!this.options.hint.async) {
104 this.finishUpdate(this.options.hint(this.cm, this.options), first);
113 this.finishUpdate(this.options.hint(this.cm, this.options), first);
105 } else {
114 } else {
106 var myTick = ++this.tick, self = this;
115 var myTick = ++this.tick, self = this;
107 this.options.hint(this.cm, function(data) {
116 this.options.hint(this.cm, function(data) {
108 if (self.tick == myTick) self.finishUpdate(data, first);
117 if (self.tick == myTick) self.finishUpdate(data, first);
109 }, this.options);
118 }, this.options);
110 }
119 }
111 },
120 },
112
121
113 finishUpdate: function(data, first) {
122 finishUpdate: function(data, first) {
123 if (this.data) CodeMirror.signal(this.data, "update");
124 if (data && this.data && CodeMirror.cmpPos(data.from, this.data.from)) data = null;
114 this.data = data;
125 this.data = data;
115
126
116 var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
127 var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
117 if (this.widget) this.widget.close();
128 if (this.widget) this.widget.close();
118 if (data && data.list.length) {
129 if (data && data.list.length) {
119 if (picked && data.list.length == 1) {
130 if (picked && data.list.length == 1) {
120 this.pick(data, 0);
131 this.pick(data, 0);
121 } else {
132 } else {
122 this.widget = new Widget(this, data);
133 this.widget = new Widget(this, data);
123 CodeMirror.signal(data, "shown");
134 CodeMirror.signal(data, "shown");
124 }
135 }
125 }
136 }
126 },
127
128 buildOptions: function(options) {
129 var editor = this.cm.options.hintOptions;
130 var out = {};
131 for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
132 if (editor) for (var prop in editor)
133 if (editor[prop] !== undefined) out[prop] = editor[prop];
134 if (options) for (var prop in options)
135 if (options[prop] !== undefined) out[prop] = options[prop];
136 return out;
137 }
137 }
138 };
138 };
139
139
140 function parseOptions(cm, pos, options) {
141 var editor = cm.options.hintOptions;
142 var out = {};
143 for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
144 if (editor) for (var prop in editor)
145 if (editor[prop] !== undefined) out[prop] = editor[prop];
146 if (options) for (var prop in options)
147 if (options[prop] !== undefined) out[prop] = options[prop];
148 if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
149 return out;
150 }
151
140 function getText(completion) {
152 function getText(completion) {
141 if (typeof completion == "string") return completion;
153 if (typeof completion == "string") return completion;
142 else return completion.text;
154 else return completion.text;
143 }
155 }
144
156
145 function buildKeyMap(completion, handle) {
157 function buildKeyMap(completion, handle) {
146 var baseMap = {
158 var baseMap = {
147 Up: function() {handle.moveFocus(-1);},
159 Up: function() {handle.moveFocus(-1);},
148 Down: function() {handle.moveFocus(1);},
160 Down: function() {handle.moveFocus(1);},
149 PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
161 PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
150 PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
162 PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
151 Home: function() {handle.setFocus(0);},
163 Home: function() {handle.setFocus(0);},
152 End: function() {handle.setFocus(handle.length - 1);},
164 End: function() {handle.setFocus(handle.length - 1);},
153 Enter: handle.pick,
165 Enter: handle.pick,
154 Tab: handle.pick,
166 Tab: handle.pick,
155 Esc: handle.close
167 Esc: handle.close
156 };
168 };
157 var custom = completion.options.customKeys;
169 var custom = completion.options.customKeys;
158 var ourMap = custom ? {} : baseMap;
170 var ourMap = custom ? {} : baseMap;
159 function addBinding(key, val) {
171 function addBinding(key, val) {
160 var bound;
172 var bound;
161 if (typeof val != "string")
173 if (typeof val != "string")
162 bound = function(cm) { return val(cm, handle); };
174 bound = function(cm) { return val(cm, handle); };
163 // This mechanism is deprecated
175 // This mechanism is deprecated
164 else if (baseMap.hasOwnProperty(val))
176 else if (baseMap.hasOwnProperty(val))
165 bound = baseMap[val];
177 bound = baseMap[val];
166 else
178 else
167 bound = val;
179 bound = val;
168 ourMap[key] = bound;
180 ourMap[key] = bound;
169 }
181 }
170 if (custom)
182 if (custom)
171 for (var key in custom) if (custom.hasOwnProperty(key))
183 for (var key in custom) if (custom.hasOwnProperty(key))
172 addBinding(key, custom[key]);
184 addBinding(key, custom[key]);
173 var extra = completion.options.extraKeys;
185 var extra = completion.options.extraKeys;
174 if (extra)
186 if (extra)
175 for (var key in extra) if (extra.hasOwnProperty(key))
187 for (var key in extra) if (extra.hasOwnProperty(key))
176 addBinding(key, extra[key]);
188 addBinding(key, extra[key]);
177 return ourMap;
189 return ourMap;
178 }
190 }
179
191
180 function getHintElement(hintsElement, el) {
192 function getHintElement(hintsElement, el) {
181 while (el && el != hintsElement) {
193 while (el && el != hintsElement) {
182 if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
194 if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
183 el = el.parentNode;
195 el = el.parentNode;
184 }
196 }
185 }
197 }
186
198
187 function Widget(completion, data) {
199 function Widget(completion, data) {
188 this.completion = completion;
200 this.completion = completion;
189 this.data = data;
201 this.data = data;
190 this.picked = false;
202 this.picked = false;
191 var widget = this, cm = completion.cm;
203 var widget = this, cm = completion.cm;
192
204
193 var hints = this.hints = document.createElement("ul");
205 var hints = this.hints = document.createElement("ul");
194 hints.className = "CodeMirror-hints";
206 hints.className = "CodeMirror-hints";
195 this.selectedHint = data.selectedHint || 0;
207 this.selectedHint = data.selectedHint || 0;
196
208
197 var completions = data.list;
209 var completions = data.list;
198 for (var i = 0; i < completions.length; ++i) {
210 for (var i = 0; i < completions.length; ++i) {
199 var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
211 var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
200 var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
212 var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
201 if (cur.className != null) className = cur.className + " " + className;
213 if (cur.className != null) className = cur.className + " " + className;
202 elt.className = className;
214 elt.className = className;
203 if (cur.render) cur.render(elt, data, cur);
215 if (cur.render) cur.render(elt, data, cur);
204 else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
216 else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
205 elt.hintId = i;
217 elt.hintId = i;
206 }
218 }
207
219
208 var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
220 var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
209 var left = pos.left, top = pos.bottom, below = true;
221 var left = pos.left, top = pos.bottom, below = true;
210 hints.style.left = left + "px";
222 hints.style.left = left + "px";
211 hints.style.top = top + "px";
223 hints.style.top = top + "px";
212 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
224 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
213 var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
225 var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
214 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
226 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
215 (completion.options.container || document.body).appendChild(hints);
227 (completion.options.container || document.body).appendChild(hints);
216 var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
228 var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
217 if (overlapY > 0) {
229 if (overlapY > 0) {
218 var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
230 var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
219 if (curTop - height > 0) { // Fits above cursor
231 if (curTop - height > 0) { // Fits above cursor
220 hints.style.top = (top = pos.top - height) + "px";
232 hints.style.top = (top = pos.top - height) + "px";
221 below = false;
233 below = false;
222 } else if (height > winH) {
234 } else if (height > winH) {
223 hints.style.height = (winH - 5) + "px";
235 hints.style.height = (winH - 5) + "px";
224 hints.style.top = (top = pos.bottom - box.top) + "px";
236 hints.style.top = (top = pos.bottom - box.top) + "px";
225 var cursor = cm.getCursor();
237 var cursor = cm.getCursor();
226 if (data.from.ch != cursor.ch) {
238 if (data.from.ch != cursor.ch) {
227 pos = cm.cursorCoords(cursor);
239 pos = cm.cursorCoords(cursor);
228 hints.style.left = (left = pos.left) + "px";
240 hints.style.left = (left = pos.left) + "px";
229 box = hints.getBoundingClientRect();
241 box = hints.getBoundingClientRect();
230 }
242 }
231 }
243 }
232 }
244 }
233 var overlapX = box.right - winW;
245 var overlapX = box.right - winW;
234 if (overlapX > 0) {
246 if (overlapX > 0) {
235 if (box.right - box.left > winW) {
247 if (box.right - box.left > winW) {
236 hints.style.width = (winW - 5) + "px";
248 hints.style.width = (winW - 5) + "px";
237 overlapX -= (box.right - box.left) - winW;
249 overlapX -= (box.right - box.left) - winW;
238 }
250 }
239 hints.style.left = (left = pos.left - overlapX) + "px";
251 hints.style.left = (left = pos.left - overlapX) + "px";
240 }
252 }
241
253
242 cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
254 cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
243 moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
255 moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
244 setFocus: function(n) { widget.changeActive(n); },
256 setFocus: function(n) { widget.changeActive(n); },
245 menuSize: function() { return widget.screenAmount(); },
257 menuSize: function() { return widget.screenAmount(); },
246 length: completions.length,
258 length: completions.length,
247 close: function() { completion.close(); },
259 close: function() { completion.close(); },
248 pick: function() { widget.pick(); },
260 pick: function() { widget.pick(); },
249 data: data
261 data: data
250 }));
262 }));
251
263
252 if (completion.options.closeOnUnfocus) {
264 if (completion.options.closeOnUnfocus) {
253 var closingOnBlur;
265 var closingOnBlur;
254 cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
266 cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
255 cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
267 cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
256 }
268 }
257
269
258 var startScroll = cm.getScrollInfo();
270 var startScroll = cm.getScrollInfo();
259 cm.on("scroll", this.onScroll = function() {
271 cm.on("scroll", this.onScroll = function() {
260 var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
272 var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
261 var newTop = top + startScroll.top - curScroll.top;
273 var newTop = top + startScroll.top - curScroll.top;
262 var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
274 var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
263 if (!below) point += hints.offsetHeight;
275 if (!below) point += hints.offsetHeight;
264 if (point <= editor.top || point >= editor.bottom) return completion.close();
276 if (point <= editor.top || point >= editor.bottom) return completion.close();
265 hints.style.top = newTop + "px";
277 hints.style.top = newTop + "px";
266 hints.style.left = (left + startScroll.left - curScroll.left) + "px";
278 hints.style.left = (left + startScroll.left - curScroll.left) + "px";
267 });
279 });
268
280
269 CodeMirror.on(hints, "dblclick", function(e) {
281 CodeMirror.on(hints, "dblclick", function(e) {
270 var t = getHintElement(hints, e.target || e.srcElement);
282 var t = getHintElement(hints, e.target || e.srcElement);
271 if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
283 if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
272 });
284 });
273
285
274 CodeMirror.on(hints, "click", function(e) {
286 CodeMirror.on(hints, "click", function(e) {
275 var t = getHintElement(hints, e.target || e.srcElement);
287 var t = getHintElement(hints, e.target || e.srcElement);
276 if (t && t.hintId != null) {
288 if (t && t.hintId != null) {
277 widget.changeActive(t.hintId);
289 widget.changeActive(t.hintId);
278 if (completion.options.completeOnSingleClick) widget.pick();
290 if (completion.options.completeOnSingleClick) widget.pick();
279 }
291 }
280 });
292 });
281
293
282 CodeMirror.on(hints, "mousedown", function() {
294 CodeMirror.on(hints, "mousedown", function() {
283 setTimeout(function(){cm.focus();}, 20);
295 setTimeout(function(){cm.focus();}, 20);
284 });
296 });
285
297
286 CodeMirror.signal(data, "select", completions[0], hints.firstChild);
298 CodeMirror.signal(data, "select", completions[0], hints.firstChild);
287 return true;
299 return true;
288 }
300 }
289
301
290 Widget.prototype = {
302 Widget.prototype = {
291 close: function() {
303 close: function() {
292 if (this.completion.widget != this) return;
304 if (this.completion.widget != this) return;
293 this.completion.widget = null;
305 this.completion.widget = null;
294 this.hints.parentNode.removeChild(this.hints);
306 this.hints.parentNode.removeChild(this.hints);
295 this.completion.cm.removeKeyMap(this.keyMap);
307 this.completion.cm.removeKeyMap(this.keyMap);
296
308
297 var cm = this.completion.cm;
309 var cm = this.completion.cm;
298 if (this.completion.options.closeOnUnfocus) {
310 if (this.completion.options.closeOnUnfocus) {
299 cm.off("blur", this.onBlur);
311 cm.off("blur", this.onBlur);
300 cm.off("focus", this.onFocus);
312 cm.off("focus", this.onFocus);
301 }
313 }
302 cm.off("scroll", this.onScroll);
314 cm.off("scroll", this.onScroll);
303 },
315 },
304
316
305 disable: function() {
317 disable: function() {
306 this.completion.cm.removeKeyMap(this.keyMap);
318 this.completion.cm.removeKeyMap(this.keyMap);
307 var widget = this;
319 var widget = this;
308 this.keyMap = {Enter: function() { widget.picked = true; }};
320 this.keyMap = {Enter: function() { widget.picked = true; }};
309 this.completion.cm.addKeyMap(this.keyMap);
321 this.completion.cm.addKeyMap(this.keyMap);
310 },
322 },
311
323
312 pick: function() {
324 pick: function() {
313 this.completion.pick(this.data, this.selectedHint);
325 this.completion.pick(this.data, this.selectedHint);
314 },
326 },
315
327
316 changeActive: function(i, avoidWrap) {
328 changeActive: function(i, avoidWrap) {
317 if (i >= this.data.list.length)
329 if (i >= this.data.list.length)
318 i = avoidWrap ? this.data.list.length - 1 : 0;
330 i = avoidWrap ? this.data.list.length - 1 : 0;
319 else if (i < 0)
331 else if (i < 0)
320 i = avoidWrap ? 0 : this.data.list.length - 1;
332 i = avoidWrap ? 0 : this.data.list.length - 1;
321 if (this.selectedHint == i) return;
333 if (this.selectedHint == i) return;
322 var node = this.hints.childNodes[this.selectedHint];
334 var node = this.hints.childNodes[this.selectedHint];
323 node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
335 node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
324 node = this.hints.childNodes[this.selectedHint = i];
336 node = this.hints.childNodes[this.selectedHint = i];
325 node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
337 node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
326 if (node.offsetTop < this.hints.scrollTop)
338 if (node.offsetTop < this.hints.scrollTop)
327 this.hints.scrollTop = node.offsetTop - 3;
339 this.hints.scrollTop = node.offsetTop - 3;
328 else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
340 else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
329 this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
341 this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
330 CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
342 CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
331 },
343 },
332
344
333 screenAmount: function() {
345 screenAmount: function() {
334 return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
346 return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
335 }
347 }
336 };
348 };
337
349
338 CodeMirror.registerHelper("hint", "auto", function(cm, options) {
350 function applicableHelpers(cm, helpers) {
339 var helpers = cm.getHelpers(cm.getCursor(), "hint"), words;
351 if (!cm.somethingSelected()) return helpers
352 var result = []
353 for (var i = 0; i < helpers.length; i++)
354 if (helpers[i].supportsSelection) result.push(helpers[i])
355 return result
356 }
357
358 function resolveAutoHints(cm, pos) {
359 var helpers = cm.getHelpers(pos, "hint"), words
340 if (helpers.length) {
360 if (helpers.length) {
341 for (var i = 0; i < helpers.length; i++) {
361 var async = false, resolved
342 var cur = helpers[i](cm, options);
362 for (var i = 0; i < helpers.length; i++) if (helpers[i].async) async = true
343 if (cur && cur.list.length) return cur;
363 if (async) {
364 resolved = function(cm, callback, options) {
365 var app = applicableHelpers(cm, helpers)
366 function run(i, result) {
367 if (i == app.length) return callback(null)
368 var helper = app[i]
369 if (helper.async) {
370 helper(cm, function(result) {
371 if (result) callback(result)
372 else run(i + 1)
373 }, options)
374 } else {
375 var result = helper(cm, options)
376 if (result) callback(result)
377 else run(i + 1)
378 }
379 }
380 run(0)
381 }
382 resolved.async = true
383 } else {
384 resolved = function(cm, options) {
385 var app = applicableHelpers(cm, helpers)
386 for (var i = 0; i < app.length; i++) {
387 var cur = app[i](cm, options)
388 if (cur && cur.list.length) return cur
389 }
390 }
344 }
391 }
392 resolved.supportsSelection = true
393 return resolved
345 } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
394 } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
346 if (words) return CodeMirror.hint.fromList(cm, {words: words});
395 return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
347 } else if (CodeMirror.hint.anyword) {
396 } else if (CodeMirror.hint.anyword) {
348 return CodeMirror.hint.anyword(cm, options);
397 return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
398 } else {
399 return function() {}
349 }
400 }
401 }
402
403 CodeMirror.registerHelper("hint", "auto", {
404 resolve: resolveAutoHints
350 });
405 });
351
406
352 CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
407 CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
353 var cur = cm.getCursor(), token = cm.getTokenAt(cur);
408 var cur = cm.getCursor(), token = cm.getTokenAt(cur);
409 var to = CodeMirror.Pos(cur.line, token.end);
410 if (token.string && /\w/.test(token.string[token.string.length - 1])) {
411 var term = token.string, from = CodeMirror.Pos(cur.line, token.start);
412 } else {
413 var term = "", from = to;
414 }
354 var found = [];
415 var found = [];
355 for (var i = 0; i < options.words.length; i++) {
416 for (var i = 0; i < options.words.length; i++) {
356 var word = options.words[i];
417 var word = options.words[i];
357 if (word.slice(0, token.string.length) == token.string)
418 if (word.slice(0, term.length) == term)
358 found.push(word);
419 found.push(word);
359 }
420 }
360
421
361 if (found.length) return {
422 if (found.length) return {list: found, from: from, to: to};
362 list: found,
363 from: CodeMirror.Pos(cur.line, token.start),
364 to: CodeMirror.Pos(cur.line, token.end)
365 };
366 });
423 });
367
424
368 CodeMirror.commands.autocomplete = CodeMirror.showHint;
425 CodeMirror.commands.autocomplete = CodeMirror.showHint;
369
426
370 var defaultOptions = {
427 var defaultOptions = {
371 hint: CodeMirror.hint.auto,
428 hint: CodeMirror.hint.auto,
372 completeSingle: true,
429 completeSingle: true,
373 alignWithWord: true,
430 alignWithWord: true,
374 closeCharacters: /[\s()\[\]{};:>,]/,
431 closeCharacters: /[\s()\[\]{};:>,]/,
375 closeOnUnfocus: true,
432 closeOnUnfocus: true,
376 completeOnSingleClick: false,
433 completeOnSingleClick: true,
377 container: null,
434 container: null,
378 customKeys: null,
435 customKeys: null,
379 extraKeys: null
436 extraKeys: null
380 };
437 };
381
438
382 CodeMirror.defineOption("hintOptions", null);
439 CodeMirror.defineOption("hintOptions", null);
383 });
440 });
@@ -1,58 +1,60 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
3
4 (function(mod) {
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
9 else // Plain browser env
10 mod(CodeMirror);
10 mod(CodeMirror);
11 })(function(CodeMirror) {
11 })(function(CodeMirror) {
12 CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
12 CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
13 var prev = old && old != CodeMirror.Init;
13 var prev = old && old != CodeMirror.Init;
14 if (val && !prev) {
14 if (val && !prev) {
15 cm.on("blur", onBlur);
15 cm.on("blur", onBlur);
16 cm.on("change", onChange);
16 cm.on("change", onChange);
17 onChange(cm);
17 onChange(cm);
18 } else if (!val && prev) {
18 } else if (!val && prev) {
19 cm.off("blur", onBlur);
19 cm.off("blur", onBlur);
20 cm.off("change", onChange);
20 cm.off("change", onChange);
21 clearPlaceholder(cm);
21 clearPlaceholder(cm);
22 var wrapper = cm.getWrapperElement();
22 var wrapper = cm.getWrapperElement();
23 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
23 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
24 }
24 }
25
25
26 if (val && !cm.hasFocus()) onBlur(cm);
26 if (val && !cm.hasFocus()) onBlur(cm);
27 });
27 });
28
28
29 function clearPlaceholder(cm) {
29 function clearPlaceholder(cm) {
30 if (cm.state.placeholder) {
30 if (cm.state.placeholder) {
31 cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
31 cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
32 cm.state.placeholder = null;
32 cm.state.placeholder = null;
33 }
33 }
34 }
34 }
35 function setPlaceholder(cm) {
35 function setPlaceholder(cm) {
36 clearPlaceholder(cm);
36 clearPlaceholder(cm);
37 var elt = cm.state.placeholder = document.createElement("pre");
37 var elt = cm.state.placeholder = document.createElement("pre");
38 elt.style.cssText = "height: 0; overflow: visible";
38 elt.style.cssText = "height: 0; overflow: visible";
39 elt.className = "CodeMirror-placeholder";
39 elt.className = "CodeMirror-placeholder";
40 elt.appendChild(document.createTextNode(cm.getOption("placeholder")));
40 var placeHolder = cm.getOption("placeholder")
41 if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
42 elt.appendChild(placeHolder)
41 cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
43 cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
42 }
44 }
43
45
44 function onBlur(cm) {
46 function onBlur(cm) {
45 if (isEmpty(cm)) setPlaceholder(cm);
47 if (isEmpty(cm)) setPlaceholder(cm);
46 }
48 }
47 function onChange(cm) {
49 function onChange(cm) {
48 var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
50 var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
49 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
51 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
50
52
51 if (empty) setPlaceholder(cm);
53 if (empty) setPlaceholder(cm);
52 else clearPlaceholder(cm);
54 else clearPlaceholder(cm);
53 }
55 }
54
56
55 function isEmpty(cm) {
57 function isEmpty(cm) {
56 return (cm.lineCount() === 1) && (cm.getLine(0) === "");
58 return (cm.lineCount() === 1) && (cm.getLine(0) === "");
57 }
59 }
58 });
60 });
@@ -1,17 +0,0 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function() {
5 "use strict";
6
7 var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-gss");
8 function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); }
9
10 MT("atComponent",
11 "[def @component] {",
12 "[tag foo] {",
13 " [property color]: [keyword black];",
14 "}",
15 "}");
16
17 })();
@@ -1,54 +0,0 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function() {
5 "use strict";
6
7 var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-less");
8 function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less"); }
9
10 MT("variable",
11 "[variable-2 @base]: [atom #f04615];",
12 "[qualifier .class] {",
13 " [property width]: [variable percentage]([number 0.5]); [comment // returns `50%`]",
14 " [property color]: [variable saturate]([variable-2 @base], [number 5%]);",
15 "}");
16
17 MT("amp",
18 "[qualifier .child], [qualifier .sibling] {",
19 " [qualifier .parent] [atom &] {",
20 " [property color]: [keyword black];",
21 " }",
22 " [atom &] + [atom &] {",
23 " [property color]: [keyword red];",
24 " }",
25 "}");
26
27 MT("mixin",
28 "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
29 " [property color]: [variable darken]([variable-2 @color], [number 10%]);",
30 "}",
31 "[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
32 " [property color]: [variable lighten]([variable-2 @color], [number 10%]);",
33 "}",
34 "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
35 " [property display]: [atom block];",
36 "}",
37 "[variable-2 @switch]: [variable light];",
38 "[qualifier .class] {",
39 " [qualifier .mixin]([variable-2 @switch]; [atom #888]);",
40 "}");
41
42 MT("nest",
43 "[qualifier .one] {",
44 " [def @media] ([property width]: [number 400px]) {",
45 " [property font-size]: [number 1.2em];",
46 " [def @media] [attribute print] [keyword and] [property color] {",
47 " [property color]: [keyword blue];",
48 " }",
49 " }",
50 "}");
51
52
53 MT("interpolation", ".@{[variable foo]} { [property font-weight]: [atom bold]; }");
54 })();
@@ -1,110 +0,0 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function() {
5 var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
6 function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
7
8 MT('url_with_quotation',
9 "[tag foo] { [property background]:[atom url]([string test.jpg]) }");
10
11 MT('url_with_double_quotes',
12 "[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }");
13
14 MT('url_with_single_quotes',
15 "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }");
16
17 MT('string',
18 "[def @import] [string \"compass/css3\"]");
19
20 MT('important_keyword',
21 "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }");
22
23 MT('variable',
24 "[variable-2 $blue]:[atom #333]");
25
26 MT('variable_as_attribute',
27 "[tag foo] { [property color]:[variable-2 $blue] }");
28
29 MT('numbers',
30 "[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }");
31
32 MT('number_percentage',
33 "[tag foo] { [property width]:[number 80%] }");
34
35 MT('selector',
36 "[builtin #hello][qualifier .world]{}");
37
38 MT('singleline_comment',
39 "[comment // this is a comment]");
40
41 MT('multiline_comment',
42 "[comment /*foobar*/]");
43
44 MT('attribute_with_hyphen',
45 "[tag foo] { [property font-size]:[number 10px] }");
46
47 MT('string_after_attribute',
48 "[tag foo] { [property content]:[string \"::\"] }");
49
50 MT('directives',
51 "[def @include] [qualifier .mixin]");
52
53 MT('basic_structure',
54 "[tag p] { [property background]:[keyword red]; }");
55
56 MT('nested_structure',
57 "[tag p] { [tag a] { [property color]:[keyword red]; } }");
58
59 MT('mixin',
60 "[def @mixin] [tag table-base] {}");
61
62 MT('number_without_semicolon',
63 "[tag p] {[property width]:[number 12]}",
64 "[tag a] {[property color]:[keyword red];}");
65
66 MT('atom_in_nested_block',
67 "[tag p] { [tag a] { [property color]:[atom #000]; } }");
68
69 MT('interpolation_in_property',
70 "[tag foo] { #{[variable-2 $hello]}:[number 2]; }");
71
72 MT('interpolation_in_selector',
73 "[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }");
74
75 MT('interpolation_error',
76 "[tag foo]#{[variable foo]} { [property color]:[atom #000]; }");
77
78 MT("divide_operator",
79 "[tag foo] { [property width]:[number 4] [operator /] [number 2] }");
80
81 MT('nested_structure_with_id_selector',
82 "[tag p] { [builtin #hello] { [property color]:[keyword red]; } }");
83
84 MT('indent_mixin',
85 "[def @mixin] [tag container] (",
86 " [variable-2 $a]: [number 10],",
87 " [variable-2 $b]: [number 10])",
88 "{}");
89
90 MT('indent_nested',
91 "[tag foo] {",
92 " [tag bar] {",
93 " }",
94 "}");
95
96 MT('indent_parentheses',
97 "[tag foo] {",
98 " [property color]: [variable darken]([variable-2 $blue],",
99 " [number 9%]);",
100 "}");
101
102 MT('indent_vardef',
103 "[variable-2 $name]:",
104 " [string 'val'];",
105 "[tag tag] {",
106 " [tag inner] {",
107 " [property margin]: [number 3px];",
108 " }",
109 "}");
110 })();
General Comments 0
You need to be logged in to leave comments. Login now