##// END OF EJS Templates
codemirror: bumped to version 5.49.2
marcink -
r4105:10488616 default
parent child Browse files
Show More
@@ -0,0 +1,173 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 CodeMirror.defineMode("fcl", function(config) {
15 var indentUnit = config.indentUnit;
16
17 var keywords = {
18 "term": true,
19 "method": true, "accu": true,
20 "rule": true, "then": true, "is": true, "and": true, "or": true,
21 "if": true, "default": true
22 };
23
24 var start_blocks = {
25 "var_input": true,
26 "var_output": true,
27 "fuzzify": true,
28 "defuzzify": true,
29 "function_block": true,
30 "ruleblock": true
31 };
32
33 var end_blocks = {
34 "end_ruleblock": true,
35 "end_defuzzify": true,
36 "end_function_block": true,
37 "end_fuzzify": true,
38 "end_var": true
39 };
40
41 var atoms = {
42 "true": true, "false": true, "nan": true,
43 "real": true, "min": true, "max": true, "cog": true, "cogs": true
44 };
45
46 var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
47
48 function tokenBase(stream, state) {
49 var ch = stream.next();
50
51 if (/[\d\.]/.test(ch)) {
52 if (ch == ".") {
53 stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
54 } else if (ch == "0") {
55 stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
56 } else {
57 stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
58 }
59 return "number";
60 }
61
62 if (ch == "/" || ch == "(") {
63 if (stream.eat("*")) {
64 state.tokenize = tokenComment;
65 return tokenComment(stream, state);
66 }
67 if (stream.eat("/")) {
68 stream.skipToEnd();
69 return "comment";
70 }
71 }
72 if (isOperatorChar.test(ch)) {
73 stream.eatWhile(isOperatorChar);
74 return "operator";
75 }
76 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
77
78 var cur = stream.current().toLowerCase();
79 if (keywords.propertyIsEnumerable(cur) ||
80 start_blocks.propertyIsEnumerable(cur) ||
81 end_blocks.propertyIsEnumerable(cur)) {
82 return "keyword";
83 }
84 if (atoms.propertyIsEnumerable(cur)) return "atom";
85 return "variable";
86 }
87
88
89 function tokenComment(stream, state) {
90 var maybeEnd = false, ch;
91 while (ch = stream.next()) {
92 if ((ch == "/" || ch == ")") && maybeEnd) {
93 state.tokenize = tokenBase;
94 break;
95 }
96 maybeEnd = (ch == "*");
97 }
98 return "comment";
99 }
100
101 function Context(indented, column, type, align, prev) {
102 this.indented = indented;
103 this.column = column;
104 this.type = type;
105 this.align = align;
106 this.prev = prev;
107 }
108
109 function pushContext(state, col, type) {
110 return state.context = new Context(state.indented, col, type, null, state.context);
111 }
112
113 function popContext(state) {
114 if (!state.context.prev) return;
115 var t = state.context.type;
116 if (t == "end_block")
117 state.indented = state.context.indented;
118 return state.context = state.context.prev;
119 }
120
121 // Interface
122
123 return {
124 startState: function(basecolumn) {
125 return {
126 tokenize: null,
127 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
128 indented: 0,
129 startOfLine: true
130 };
131 },
132
133 token: function(stream, state) {
134 var ctx = state.context;
135 if (stream.sol()) {
136 if (ctx.align == null) ctx.align = false;
137 state.indented = stream.indentation();
138 state.startOfLine = true;
139 }
140 if (stream.eatSpace()) return null;
141
142 var style = (state.tokenize || tokenBase)(stream, state);
143 if (style == "comment") return style;
144 if (ctx.align == null) ctx.align = true;
145
146 var cur = stream.current().toLowerCase();
147
148 if (start_blocks.propertyIsEnumerable(cur)) pushContext(state, stream.column(), "end_block");
149 else if (end_blocks.propertyIsEnumerable(cur)) popContext(state);
150
151 state.startOfLine = false;
152 return style;
153 },
154
155 indent: function(state, textAfter) {
156 if (state.tokenize != tokenBase && state.tokenize != null) return 0;
157 var ctx = state.context;
158
159 var closing = end_blocks.propertyIsEnumerable(textAfter);
160 if (ctx.align) return ctx.column + (closing ? 0 : 1);
161 else return ctx.indented + (closing ? 0 : indentUnit);
162 },
163
164 electricChars: "ryk",
165 fold: "brace",
166 blockCommentStart: "(*",
167 blockCommentEnd: "*)",
168 lineComment: "//"
169 };
170 });
171
172 CodeMirror.defineMIME("text/x-fcl", "fcl");
173 });
@@ -0,0 +1,129 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 var rfc2822 = [
15 "From", "Sender", "Reply-To", "To", "Cc", "Bcc", "Message-ID",
16 "In-Reply-To", "References", "Resent-From", "Resent-Sender", "Resent-To",
17 "Resent-Cc", "Resent-Bcc", "Resent-Message-ID", "Return-Path", "Received"
18 ];
19 var rfc2822NoEmail = [
20 "Date", "Subject", "Comments", "Keywords", "Resent-Date"
21 ];
22
23 CodeMirror.registerHelper("hintWords", "mbox", rfc2822.concat(rfc2822NoEmail));
24
25 var whitespace = /^[ \t]/;
26 var separator = /^From /; // See RFC 4155
27 var rfc2822Header = new RegExp("^(" + rfc2822.join("|") + "): ");
28 var rfc2822HeaderNoEmail = new RegExp("^(" + rfc2822NoEmail.join("|") + "): ");
29 var header = /^[^:]+:/; // Optional fields defined in RFC 2822
30 var email = /^[^ ]+@[^ ]+/;
31 var untilEmail = /^.*?(?=[^ ]+?@[^ ]+)/;
32 var bracketedEmail = /^<.*?>/;
33 var untilBracketedEmail = /^.*?(?=<.*>)/;
34
35 function styleForHeader(header) {
36 if (header === "Subject") return "header";
37 return "string";
38 }
39
40 function readToken(stream, state) {
41 if (stream.sol()) {
42 // From last line
43 state.inSeparator = false;
44 if (state.inHeader && stream.match(whitespace)) {
45 // Header folding
46 return null;
47 } else {
48 state.inHeader = false;
49 state.header = null;
50 }
51
52 if (stream.match(separator)) {
53 state.inHeaders = true;
54 state.inSeparator = true;
55 return "atom";
56 }
57
58 var match;
59 var emailPermitted = false;
60 if ((match = stream.match(rfc2822HeaderNoEmail)) ||
61 (emailPermitted = true) && (match = stream.match(rfc2822Header))) {
62 state.inHeaders = true;
63 state.inHeader = true;
64 state.emailPermitted = emailPermitted;
65 state.header = match[1];
66 return "atom";
67 }
68
69 // Use vim's heuristics: recognize custom headers only if the line is in a
70 // block of legitimate headers.
71 if (state.inHeaders && (match = stream.match(header))) {
72 state.inHeader = true;
73 state.emailPermitted = true;
74 state.header = match[1];
75 return "atom";
76 }
77
78 state.inHeaders = false;
79 stream.skipToEnd();
80 return null;
81 }
82
83 if (state.inSeparator) {
84 if (stream.match(email)) return "link";
85 if (stream.match(untilEmail)) return "atom";
86 stream.skipToEnd();
87 return "atom";
88 }
89
90 if (state.inHeader) {
91 var style = styleForHeader(state.header);
92
93 if (state.emailPermitted) {
94 if (stream.match(bracketedEmail)) return style + " link";
95 if (stream.match(untilBracketedEmail)) return style;
96 }
97 stream.skipToEnd();
98 return style;
99 }
100
101 stream.skipToEnd();
102 return null;
103 };
104
105 CodeMirror.defineMode("mbox", function() {
106 return {
107 startState: function() {
108 return {
109 // Is in a mbox separator
110 inSeparator: false,
111 // Is in a mail header
112 inHeader: false,
113 // If bracketed email is permitted. Only applicable when inHeader
114 emailPermitted: false,
115 // Name of current header
116 header: null,
117 // Is in a region of mail headers
118 inHeaders: false
119 };
120 },
121 token: readToken,
122 blankLine: function(state) {
123 state.inHeaders = state.inSeparator = state.inHeader = false;
124 }
125 };
126 });
127
128 CodeMirror.defineMIME("application/mbox", "mbox");
129 });
@@ -0,0 +1,398 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 'use strict';
6 if (typeof exports == 'object' && typeof module == 'object') // CommonJS
7 mod(require('../../lib/codemirror'));
8 else if (typeof define == 'function' && define.amd) // AMD
9 define(['../../lib/codemirror'], mod);
10 else // Plain browser env
11 mod(window.CodeMirror);
12 })(function(CodeMirror) {
13 'use strict';
14
15 CodeMirror.defineMode('powershell', function() {
16 function buildRegexp(patterns, options) {
17 options = options || {};
18 var prefix = options.prefix !== undefined ? options.prefix : '^';
19 var suffix = options.suffix !== undefined ? options.suffix : '\\b';
20
21 for (var i = 0; i < patterns.length; i++) {
22 if (patterns[i] instanceof RegExp) {
23 patterns[i] = patterns[i].source;
24 }
25 else {
26 patterns[i] = patterns[i].replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
27 }
28 }
29
30 return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i');
31 }
32
33 var notCharacterOrDash = '(?=[^A-Za-z\\d\\-_]|$)';
34 var varNames = /[\w\-:]/
35 var keywords = buildRegexp([
36 /begin|break|catch|continue|data|default|do|dynamicparam/,
37 /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/,
38 /param|process|return|switch|throw|trap|try|until|where|while/
39 ], { suffix: notCharacterOrDash });
40
41 var punctuation = /[\[\]{},;`\.]|@[({]/;
42 var wordOperators = buildRegexp([
43 'f',
44 /b?not/,
45 /[ic]?split/, 'join',
46 /is(not)?/, 'as',
47 /[ic]?(eq|ne|[gl][te])/,
48 /[ic]?(not)?(like|match|contains)/,
49 /[ic]?replace/,
50 /b?(and|or|xor)/
51 ], { prefix: '-' });
52 var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=!|\/]|<(?!#)|(?!#)>/;
53 var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' });
54
55 var numbers = /^((0x[\da-f]+)|((\d+\.\d+|\d\.|\.\d+|\d+)(e[\+\-]?\d+)?))[ld]?([kmgtp]b)?/i;
56
57 var identifiers = /^[A-Za-z\_][A-Za-z\-\_\d]*\b/;
58
59 var symbolBuiltins = /[A-Z]:|%|\?/i;
60 var namedBuiltins = buildRegexp([
61 /Add-(Computer|Content|History|Member|PSSnapin|Type)/,
62 /Checkpoint-Computer/,
63 /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/,
64 /Compare-Object/,
65 /Complete-Transaction/,
66 /Connect-PSSession/,
67 /ConvertFrom-(Csv|Json|SecureString|StringData)/,
68 /Convert-Path/,
69 /ConvertTo-(Csv|Html|Json|SecureString|Xml)/,
70 /Copy-Item(Property)?/,
71 /Debug-Process/,
72 /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,
73 /Disconnect-PSSession/,
74 /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/,
75 /(Enter|Exit)-PSSession/,
76 /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/,
77 /ForEach-Object/,
78 /Format-(Custom|List|Table|Wide)/,
79 new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential'
80 + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job'
81 + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration'
82 + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'),
83 /Group-Object/,
84 /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/,
85 /ImportSystemModules/,
86 /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/,
87 /Join-Path/,
88 /Limit-EventLog/,
89 /Measure-(Command|Object)/,
90 /Move-Item(Property)?/,
91 new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile'
92 + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'),
93 /Out-(Default|File|GridView|Host|Null|Printer|String)/,
94 /Pause/,
95 /(Pop|Push)-Location/,
96 /Read-Host/,
97 /Receive-(Job|PSSession)/,
98 /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/,
99 /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/,
100 /Rename-(Computer|Item(Property)?)/,
101 /Reset-ComputerMachinePassword/,
102 /Resolve-Path/,
103 /Restart-(Computer|Service)/,
104 /Restore-Computer/,
105 /Resume-(Job|Service)/,
106 /Save-Help/,
107 /Select-(Object|String|Xml)/,
108 /Send-MailMessage/,
109 new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' +
110 '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'),
111 /Show-(Command|ControlPanelItem|EventLog)/,
112 /Sort-Object/,
113 /Split-Path/,
114 /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/,
115 /Stop-(Computer|Job|Process|Service|Transcript)/,
116 /Suspend-(Job|Service)/,
117 /TabExpansion2/,
118 /Tee-Object/,
119 /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/,
120 /Trace-Command/,
121 /Unblock-File/,
122 /Undo-Transaction/,
123 /Unregister-(Event|PSSessionConfiguration)/,
124 /Update-(FormatData|Help|List|TypeData)/,
125 /Use-Transaction/,
126 /Wait-(Event|Job|Process)/,
127 /Where-Object/,
128 /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/,
129 /cd|help|mkdir|more|oss|prompt/,
130 /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/,
131 /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/,
132 /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/,
133 /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/,
134 /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/,
135 /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/
136 ], { prefix: '', suffix: '' });
137 var variableBuiltins = buildRegexp([
138 /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/,
139 /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/,
140 /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/,
141 /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/,
142 /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/,
143 /WarningPreference|WhatIfPreference/,
144
145 /Event|EventArgs|EventSubscriber|Sender/,
146 /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/,
147 /true|false|null/
148 ], { prefix: '\\$', suffix: '' });
149
150 var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash });
151
152 var grammar = {
153 keyword: keywords,
154 number: numbers,
155 operator: operators,
156 builtin: builtins,
157 punctuation: punctuation,
158 identifier: identifiers
159 };
160
161 // tokenizers
162 function tokenBase(stream, state) {
163 // Handle Comments
164 //var ch = stream.peek();
165
166 var parent = state.returnStack[state.returnStack.length - 1];
167 if (parent && parent.shouldReturnFrom(state)) {
168 state.tokenize = parent.tokenize;
169 state.returnStack.pop();
170 return state.tokenize(stream, state);
171 }
172
173 if (stream.eatSpace()) {
174 return null;
175 }
176
177 if (stream.eat('(')) {
178 state.bracketNesting += 1;
179 return 'punctuation';
180 }
181
182 if (stream.eat(')')) {
183 state.bracketNesting -= 1;
184 return 'punctuation';
185 }
186
187 for (var key in grammar) {
188 if (stream.match(grammar[key])) {
189 return key;
190 }
191 }
192
193 var ch = stream.next();
194
195 // single-quote string
196 if (ch === "'") {
197 return tokenSingleQuoteString(stream, state);
198 }
199
200 if (ch === '$') {
201 return tokenVariable(stream, state);
202 }
203
204 // double-quote string
205 if (ch === '"') {
206 return tokenDoubleQuoteString(stream, state);
207 }
208
209 if (ch === '<' && stream.eat('#')) {
210 state.tokenize = tokenComment;
211 return tokenComment(stream, state);
212 }
213
214 if (ch === '#') {
215 stream.skipToEnd();
216 return 'comment';
217 }
218
219 if (ch === '@') {
220 var quoteMatch = stream.eat(/["']/);
221 if (quoteMatch && stream.eol()) {
222 state.tokenize = tokenMultiString;
223 state.startQuote = quoteMatch[0];
224 return tokenMultiString(stream, state);
225 } else if (stream.eol()) {
226 return 'error';
227 } else if (stream.peek().match(/[({]/)) {
228 return 'punctuation';
229 } else if (stream.peek().match(varNames)) {
230 // splatted variable
231 return tokenVariable(stream, state);
232 }
233 }
234 return 'error';
235 }
236
237 function tokenSingleQuoteString(stream, state) {
238 var ch;
239 while ((ch = stream.peek()) != null) {
240 stream.next();
241
242 if (ch === "'" && !stream.eat("'")) {
243 state.tokenize = tokenBase;
244 return 'string';
245 }
246 }
247
248 return 'error';
249 }
250
251 function tokenDoubleQuoteString(stream, state) {
252 var ch;
253 while ((ch = stream.peek()) != null) {
254 if (ch === '$') {
255 state.tokenize = tokenStringInterpolation;
256 return 'string';
257 }
258
259 stream.next();
260 if (ch === '`') {
261 stream.next();
262 continue;
263 }
264
265 if (ch === '"' && !stream.eat('"')) {
266 state.tokenize = tokenBase;
267 return 'string';
268 }
269 }
270
271 return 'error';
272 }
273
274 function tokenStringInterpolation(stream, state) {
275 return tokenInterpolation(stream, state, tokenDoubleQuoteString);
276 }
277
278 function tokenMultiStringReturn(stream, state) {
279 state.tokenize = tokenMultiString;
280 state.startQuote = '"'
281 return tokenMultiString(stream, state);
282 }
283
284 function tokenHereStringInterpolation(stream, state) {
285 return tokenInterpolation(stream, state, tokenMultiStringReturn);
286 }
287
288 function tokenInterpolation(stream, state, parentTokenize) {
289 if (stream.match('$(')) {
290 var savedBracketNesting = state.bracketNesting;
291 state.returnStack.push({
292 /*jshint loopfunc:true */
293 shouldReturnFrom: function(state) {
294 return state.bracketNesting === savedBracketNesting;
295 },
296 tokenize: parentTokenize
297 });
298 state.tokenize = tokenBase;
299 state.bracketNesting += 1;
300 return 'punctuation';
301 } else {
302 stream.next();
303 state.returnStack.push({
304 shouldReturnFrom: function() { return true; },
305 tokenize: parentTokenize
306 });
307 state.tokenize = tokenVariable;
308 return state.tokenize(stream, state);
309 }
310 }
311
312 function tokenComment(stream, state) {
313 var maybeEnd = false, ch;
314 while ((ch = stream.next()) != null) {
315 if (maybeEnd && ch == '>') {
316 state.tokenize = tokenBase;
317 break;
318 }
319 maybeEnd = (ch === '#');
320 }
321 return 'comment';
322 }
323
324 function tokenVariable(stream, state) {
325 var ch = stream.peek();
326 if (stream.eat('{')) {
327 state.tokenize = tokenVariableWithBraces;
328 return tokenVariableWithBraces(stream, state);
329 } else if (ch != undefined && ch.match(varNames)) {
330 stream.eatWhile(varNames);
331 state.tokenize = tokenBase;
332 return 'variable-2';
333 } else {
334 state.tokenize = tokenBase;
335 return 'error';
336 }
337 }
338
339 function tokenVariableWithBraces(stream, state) {
340 var ch;
341 while ((ch = stream.next()) != null) {
342 if (ch === '}') {
343 state.tokenize = tokenBase;
344 break;
345 }
346 }
347 return 'variable-2';
348 }
349
350 function tokenMultiString(stream, state) {
351 var quote = state.startQuote;
352 if (stream.sol() && stream.match(new RegExp(quote + '@'))) {
353 state.tokenize = tokenBase;
354 }
355 else if (quote === '"') {
356 while (!stream.eol()) {
357 var ch = stream.peek();
358 if (ch === '$') {
359 state.tokenize = tokenHereStringInterpolation;
360 return 'string';
361 }
362
363 stream.next();
364 if (ch === '`') {
365 stream.next();
366 }
367 }
368 }
369 else {
370 stream.skipToEnd();
371 }
372
373 return 'string';
374 }
375
376 var external = {
377 startState: function() {
378 return {
379 returnStack: [],
380 bracketNesting: 0,
381 tokenize: tokenBase
382 };
383 },
384
385 token: function(stream, state) {
386 return state.tokenize(stream, state);
387 },
388
389 blockCommentStart: '<#',
390 blockCommentEnd: '#>',
391 lineComment: '#',
392 fold: 'brace'
393 };
394 return external;
395 });
396
397 CodeMirror.defineMIME('application/x-powershell', 'powershell');
398 });
@@ -0,0 +1,69 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 function wordRegexp(words) {
15 return new RegExp("^((" + words.join(")|(") + "))\\b", "i");
16 };
17
18 var keywordArray = [
19 "package", "message", "import", "syntax",
20 "required", "optional", "repeated", "reserved", "default", "extensions", "packed",
21 "bool", "bytes", "double", "enum", "float", "string",
22 "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64",
23 "option", "service", "rpc", "returns"
24 ];
25 var keywords = wordRegexp(keywordArray);
26
27 CodeMirror.registerHelper("hintWords", "protobuf", keywordArray);
28
29 var identifiers = new RegExp("^[_A-Za-z\xa1-\uffff][_A-Za-z0-9\xa1-\uffff]*");
30
31 function tokenBase(stream) {
32 // whitespaces
33 if (stream.eatSpace()) return null;
34
35 // Handle one line Comments
36 if (stream.match("//")) {
37 stream.skipToEnd();
38 return "comment";
39 }
40
41 // Handle Number Literals
42 if (stream.match(/^[0-9\.+-]/, false)) {
43 if (stream.match(/^[+-]?0x[0-9a-fA-F]+/))
44 return "number";
45 if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?/))
46 return "number";
47 if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?/))
48 return "number";
49 }
50
51 // Handle Strings
52 if (stream.match(/^"([^"]|(""))*"/)) { return "string"; }
53 if (stream.match(/^'([^']|(''))*'/)) { return "string"; }
54
55 // Handle words
56 if (stream.match(keywords)) { return "keyword"; }
57 if (stream.match(identifiers)) { return "variable"; } ;
58
59 // Handle non-detected items
60 stream.next();
61 return null;
62 };
63
64 CodeMirror.defineMode("protobuf", function() {
65 return {token: tokenBase};
66 });
67
68 CodeMirror.defineMIME("text/x-protobuf", "protobuf");
69 });
This diff has been collapsed as it changes many lines, (591 lines changed) Show them Hide them
@@ -0,0 +1,591 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 CodeMirror.defineMode("pug", function (config) {
15 // token types
16 var KEYWORD = 'keyword';
17 var DOCTYPE = 'meta';
18 var ID = 'builtin';
19 var CLASS = 'qualifier';
20
21 var ATTRS_NEST = {
22 '{': '}',
23 '(': ')',
24 '[': ']'
25 };
26
27 var jsMode = CodeMirror.getMode(config, 'javascript');
28
29 function State() {
30 this.javaScriptLine = false;
31 this.javaScriptLineExcludesColon = false;
32
33 this.javaScriptArguments = false;
34 this.javaScriptArgumentsDepth = 0;
35
36 this.isInterpolating = false;
37 this.interpolationNesting = 0;
38
39 this.jsState = CodeMirror.startState(jsMode);
40
41 this.restOfLine = '';
42
43 this.isIncludeFiltered = false;
44 this.isEach = false;
45
46 this.lastTag = '';
47 this.scriptType = '';
48
49 // Attributes Mode
50 this.isAttrs = false;
51 this.attrsNest = [];
52 this.inAttributeName = true;
53 this.attributeIsType = false;
54 this.attrValue = '';
55
56 // Indented Mode
57 this.indentOf = Infinity;
58 this.indentToken = '';
59
60 this.innerMode = null;
61 this.innerState = null;
62
63 this.innerModeForLine = false;
64 }
65 /**
66 * Safely copy a state
67 *
68 * @return {State}
69 */
70 State.prototype.copy = function () {
71 var res = new State();
72 res.javaScriptLine = this.javaScriptLine;
73 res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;
74 res.javaScriptArguments = this.javaScriptArguments;
75 res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
76 res.isInterpolating = this.isInterpolating;
77 res.interpolationNesting = this.interpolationNesting;
78
79 res.jsState = CodeMirror.copyState(jsMode, this.jsState);
80
81 res.innerMode = this.innerMode;
82 if (this.innerMode && this.innerState) {
83 res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);
84 }
85
86 res.restOfLine = this.restOfLine;
87
88 res.isIncludeFiltered = this.isIncludeFiltered;
89 res.isEach = this.isEach;
90 res.lastTag = this.lastTag;
91 res.scriptType = this.scriptType;
92 res.isAttrs = this.isAttrs;
93 res.attrsNest = this.attrsNest.slice();
94 res.inAttributeName = this.inAttributeName;
95 res.attributeIsType = this.attributeIsType;
96 res.attrValue = this.attrValue;
97 res.indentOf = this.indentOf;
98 res.indentToken = this.indentToken;
99
100 res.innerModeForLine = this.innerModeForLine;
101
102 return res;
103 };
104
105 function javaScript(stream, state) {
106 if (stream.sol()) {
107 // if javaScriptLine was set at end of line, ignore it
108 state.javaScriptLine = false;
109 state.javaScriptLineExcludesColon = false;
110 }
111 if (state.javaScriptLine) {
112 if (state.javaScriptLineExcludesColon && stream.peek() === ':') {
113 state.javaScriptLine = false;
114 state.javaScriptLineExcludesColon = false;
115 return;
116 }
117 var tok = jsMode.token(stream, state.jsState);
118 if (stream.eol()) state.javaScriptLine = false;
119 return tok || true;
120 }
121 }
122 function javaScriptArguments(stream, state) {
123 if (state.javaScriptArguments) {
124 if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {
125 state.javaScriptArguments = false;
126 return;
127 }
128 if (stream.peek() === '(') {
129 state.javaScriptArgumentsDepth++;
130 } else if (stream.peek() === ')') {
131 state.javaScriptArgumentsDepth--;
132 }
133 if (state.javaScriptArgumentsDepth === 0) {
134 state.javaScriptArguments = false;
135 return;
136 }
137
138 var tok = jsMode.token(stream, state.jsState);
139 return tok || true;
140 }
141 }
142
143 function yieldStatement(stream) {
144 if (stream.match(/^yield\b/)) {
145 return 'keyword';
146 }
147 }
148
149 function doctype(stream) {
150 if (stream.match(/^(?:doctype) *([^\n]+)?/)) {
151 return DOCTYPE;
152 }
153 }
154
155 function interpolation(stream, state) {
156 if (stream.match('#{')) {
157 state.isInterpolating = true;
158 state.interpolationNesting = 0;
159 return 'punctuation';
160 }
161 }
162
163 function interpolationContinued(stream, state) {
164 if (state.isInterpolating) {
165 if (stream.peek() === '}') {
166 state.interpolationNesting--;
167 if (state.interpolationNesting < 0) {
168 stream.next();
169 state.isInterpolating = false;
170 return 'punctuation';
171 }
172 } else if (stream.peek() === '{') {
173 state.interpolationNesting++;
174 }
175 return jsMode.token(stream, state.jsState) || true;
176 }
177 }
178
179 function caseStatement(stream, state) {
180 if (stream.match(/^case\b/)) {
181 state.javaScriptLine = true;
182 return KEYWORD;
183 }
184 }
185
186 function when(stream, state) {
187 if (stream.match(/^when\b/)) {
188 state.javaScriptLine = true;
189 state.javaScriptLineExcludesColon = true;
190 return KEYWORD;
191 }
192 }
193
194 function defaultStatement(stream) {
195 if (stream.match(/^default\b/)) {
196 return KEYWORD;
197 }
198 }
199
200 function extendsStatement(stream, state) {
201 if (stream.match(/^extends?\b/)) {
202 state.restOfLine = 'string';
203 return KEYWORD;
204 }
205 }
206
207 function append(stream, state) {
208 if (stream.match(/^append\b/)) {
209 state.restOfLine = 'variable';
210 return KEYWORD;
211 }
212 }
213 function prepend(stream, state) {
214 if (stream.match(/^prepend\b/)) {
215 state.restOfLine = 'variable';
216 return KEYWORD;
217 }
218 }
219 function block(stream, state) {
220 if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) {
221 state.restOfLine = 'variable';
222 return KEYWORD;
223 }
224 }
225
226 function include(stream, state) {
227 if (stream.match(/^include\b/)) {
228 state.restOfLine = 'string';
229 return KEYWORD;
230 }
231 }
232
233 function includeFiltered(stream, state) {
234 if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) {
235 state.isIncludeFiltered = true;
236 return KEYWORD;
237 }
238 }
239
240 function includeFilteredContinued(stream, state) {
241 if (state.isIncludeFiltered) {
242 var tok = filter(stream, state);
243 state.isIncludeFiltered = false;
244 state.restOfLine = 'string';
245 return tok;
246 }
247 }
248
249 function mixin(stream, state) {
250 if (stream.match(/^mixin\b/)) {
251 state.javaScriptLine = true;
252 return KEYWORD;
253 }
254 }
255
256 function call(stream, state) {
257 if (stream.match(/^\+([-\w]+)/)) {
258 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
259 state.javaScriptArguments = true;
260 state.javaScriptArgumentsDepth = 0;
261 }
262 return 'variable';
263 }
264 if (stream.match(/^\+#{/, false)) {
265 stream.next();
266 state.mixinCallAfter = true;
267 return interpolation(stream, state);
268 }
269 }
270 function callArguments(stream, state) {
271 if (state.mixinCallAfter) {
272 state.mixinCallAfter = false;
273 if (!stream.match(/^\( *[-\w]+ *=/, false)) {
274 state.javaScriptArguments = true;
275 state.javaScriptArgumentsDepth = 0;
276 }
277 return true;
278 }
279 }
280
281 function conditional(stream, state) {
282 if (stream.match(/^(if|unless|else if|else)\b/)) {
283 state.javaScriptLine = true;
284 return KEYWORD;
285 }
286 }
287
288 function each(stream, state) {
289 if (stream.match(/^(- *)?(each|for)\b/)) {
290 state.isEach = true;
291 return KEYWORD;
292 }
293 }
294 function eachContinued(stream, state) {
295 if (state.isEach) {
296 if (stream.match(/^ in\b/)) {
297 state.javaScriptLine = true;
298 state.isEach = false;
299 return KEYWORD;
300 } else if (stream.sol() || stream.eol()) {
301 state.isEach = false;
302 } else if (stream.next()) {
303 while (!stream.match(/^ in\b/, false) && stream.next());
304 return 'variable';
305 }
306 }
307 }
308
309 function whileStatement(stream, state) {
310 if (stream.match(/^while\b/)) {
311 state.javaScriptLine = true;
312 return KEYWORD;
313 }
314 }
315
316 function tag(stream, state) {
317 var captures;
318 if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) {
319 state.lastTag = captures[1].toLowerCase();
320 if (state.lastTag === 'script') {
321 state.scriptType = 'application/javascript';
322 }
323 return 'tag';
324 }
325 }
326
327 function filter(stream, state) {
328 if (stream.match(/^:([\w\-]+)/)) {
329 var innerMode;
330 if (config && config.innerModes) {
331 innerMode = config.innerModes(stream.current().substring(1));
332 }
333 if (!innerMode) {
334 innerMode = stream.current().substring(1);
335 }
336 if (typeof innerMode === 'string') {
337 innerMode = CodeMirror.getMode(config, innerMode);
338 }
339 setInnerMode(stream, state, innerMode);
340 return 'atom';
341 }
342 }
343
344 function code(stream, state) {
345 if (stream.match(/^(!?=|-)/)) {
346 state.javaScriptLine = true;
347 return 'punctuation';
348 }
349 }
350
351 function id(stream) {
352 if (stream.match(/^#([\w-]+)/)) {
353 return ID;
354 }
355 }
356
357 function className(stream) {
358 if (stream.match(/^\.([\w-]+)/)) {
359 return CLASS;
360 }
361 }
362
363 function attrs(stream, state) {
364 if (stream.peek() == '(') {
365 stream.next();
366 state.isAttrs = true;
367 state.attrsNest = [];
368 state.inAttributeName = true;
369 state.attrValue = '';
370 state.attributeIsType = false;
371 return 'punctuation';
372 }
373 }
374
375 function attrsContinued(stream, state) {
376 if (state.isAttrs) {
377 if (ATTRS_NEST[stream.peek()]) {
378 state.attrsNest.push(ATTRS_NEST[stream.peek()]);
379 }
380 if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {
381 state.attrsNest.pop();
382 } else if (stream.eat(')')) {
383 state.isAttrs = false;
384 return 'punctuation';
385 }
386 if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) {
387 if (stream.peek() === '=' || stream.peek() === '!') {
388 state.inAttributeName = false;
389 state.jsState = CodeMirror.startState(jsMode);
390 if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {
391 state.attributeIsType = true;
392 } else {
393 state.attributeIsType = false;
394 }
395 }
396 return 'attribute';
397 }
398
399 var tok = jsMode.token(stream, state.jsState);
400 if (state.attributeIsType && tok === 'string') {
401 state.scriptType = stream.current().toString();
402 }
403 if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {
404 try {
405 Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, ''));
406 state.inAttributeName = true;
407 state.attrValue = '';
408 stream.backUp(stream.current().length);
409 return attrsContinued(stream, state);
410 } catch (ex) {
411 //not the end of an attribute
412 }
413 }
414 state.attrValue += stream.current();
415 return tok || true;
416 }
417 }
418
419 function attributesBlock(stream, state) {
420 if (stream.match(/^&attributes\b/)) {
421 state.javaScriptArguments = true;
422 state.javaScriptArgumentsDepth = 0;
423 return 'keyword';
424 }
425 }
426
427 function indent(stream) {
428 if (stream.sol() && stream.eatSpace()) {
429 return 'indent';
430 }
431 }
432
433 function comment(stream, state) {
434 if (stream.match(/^ *\/\/(-)?([^\n]*)/)) {
435 state.indentOf = stream.indentation();
436 state.indentToken = 'comment';
437 return 'comment';
438 }
439 }
440
441 function colon(stream) {
442 if (stream.match(/^: */)) {
443 return 'colon';
444 }
445 }
446
447 function text(stream, state) {
448 if (stream.match(/^(?:\| ?| )([^\n]+)/)) {
449 return 'string';
450 }
451 if (stream.match(/^(<[^\n]*)/, false)) {
452 // html string
453 setInnerMode(stream, state, 'htmlmixed');
454 state.innerModeForLine = true;
455 return innerMode(stream, state, true);
456 }
457 }
458
459 function dot(stream, state) {
460 if (stream.eat('.')) {
461 var innerMode = null;
462 if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {
463 innerMode = state.scriptType.toLowerCase().replace(/"|'/g, '');
464 } else if (state.lastTag === 'style') {
465 innerMode = 'css';
466 }
467 setInnerMode(stream, state, innerMode);
468 return 'dot';
469 }
470 }
471
472 function fail(stream) {
473 stream.next();
474 return null;
475 }
476
477
478 function setInnerMode(stream, state, mode) {
479 mode = CodeMirror.mimeModes[mode] || mode;
480 mode = config.innerModes ? config.innerModes(mode) || mode : mode;
481 mode = CodeMirror.mimeModes[mode] || mode;
482 mode = CodeMirror.getMode(config, mode);
483 state.indentOf = stream.indentation();
484
485 if (mode && mode.name !== 'null') {
486 state.innerMode = mode;
487 } else {
488 state.indentToken = 'string';
489 }
490 }
491 function innerMode(stream, state, force) {
492 if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {
493 if (state.innerMode) {
494 if (!state.innerState) {
495 state.innerState = state.innerMode.startState ? CodeMirror.startState(state.innerMode, stream.indentation()) : {};
496 }
497 return stream.hideFirstChars(state.indentOf + 2, function () {
498 return state.innerMode.token(stream, state.innerState) || true;
499 });
500 } else {
501 stream.skipToEnd();
502 return state.indentToken;
503 }
504 } else if (stream.sol()) {
505 state.indentOf = Infinity;
506 state.indentToken = null;
507 state.innerMode = null;
508 state.innerState = null;
509 }
510 }
511 function restOfLine(stream, state) {
512 if (stream.sol()) {
513 // if restOfLine was set at end of line, ignore it
514 state.restOfLine = '';
515 }
516 if (state.restOfLine) {
517 stream.skipToEnd();
518 var tok = state.restOfLine;
519 state.restOfLine = '';
520 return tok;
521 }
522 }
523
524
525 function startState() {
526 return new State();
527 }
528 function copyState(state) {
529 return state.copy();
530 }
531 /**
532 * Get the next token in the stream
533 *
534 * @param {Stream} stream
535 * @param {State} state
536 */
537 function nextToken(stream, state) {
538 var tok = innerMode(stream, state)
539 || restOfLine(stream, state)
540 || interpolationContinued(stream, state)
541 || includeFilteredContinued(stream, state)
542 || eachContinued(stream, state)
543 || attrsContinued(stream, state)
544 || javaScript(stream, state)
545 || javaScriptArguments(stream, state)
546 || callArguments(stream, state)
547
548 || yieldStatement(stream)
549 || doctype(stream)
550 || interpolation(stream, state)
551 || caseStatement(stream, state)
552 || when(stream, state)
553 || defaultStatement(stream)
554 || extendsStatement(stream, state)
555 || append(stream, state)
556 || prepend(stream, state)
557 || block(stream, state)
558 || include(stream, state)
559 || includeFiltered(stream, state)
560 || mixin(stream, state)
561 || call(stream, state)
562 || conditional(stream, state)
563 || each(stream, state)
564 || whileStatement(stream, state)
565 || tag(stream, state)
566 || filter(stream, state)
567 || code(stream, state)
568 || id(stream)
569 || className(stream)
570 || attrs(stream, state)
571 || attributesBlock(stream, state)
572 || indent(stream)
573 || text(stream, state)
574 || comment(stream, state)
575 || colon(stream)
576 || dot(stream, state)
577 || fail(stream);
578
579 return tok === true ? null : tok;
580 }
581 return {
582 startState: startState,
583 copyState: copyState,
584 token: nextToken
585 };
586 }, 'javascript', 'css', 'htmlmixed');
587
588 CodeMirror.defineMIME('text/x-pug', 'pug');
589 CodeMirror.defineMIME('text/x-jade', 'pug');
590
591 });
@@ -0,0 +1,303 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4
5 // SAS mode copyright (c) 2016 Jared Dean, SAS Institute
6 // Created by Jared Dean
7
8 // TODO
9 // indent and de-indent
10 // identify macro variables
11
12
13 //Definitions
14 // comment -- text within * ; or /* */
15 // keyword -- SAS language variable
16 // variable -- macro variables starts with '&' or variable formats
17 // variable-2 -- DATA Step, proc, or macro names
18 // string -- text within ' ' or " "
19 // operator -- numeric operator + / - * ** le eq ge ... and so on
20 // builtin -- proc %macro data run mend
21 // atom
22 // def
23
24 (function(mod) {
25 if (typeof exports == "object" && typeof module == "object") // CommonJS
26 mod(require("../../lib/codemirror"));
27 else if (typeof define == "function" && define.amd) // AMD
28 define(["../../lib/codemirror"], mod);
29 else // Plain browser env
30 mod(CodeMirror);
31 })(function(CodeMirror) {
32 "use strict";
33
34 CodeMirror.defineMode("sas", function () {
35 var words = {};
36 var isDoubleOperatorSym = {
37 eq: 'operator',
38 lt: 'operator',
39 le: 'operator',
40 gt: 'operator',
41 ge: 'operator',
42 "in": 'operator',
43 ne: 'operator',
44 or: 'operator'
45 };
46 var isDoubleOperatorChar = /(<=|>=|!=|<>)/;
47 var isSingleOperatorChar = /[=\(:\),{}.*<>+\-\/^\[\]]/;
48
49 // Takes a string of words separated by spaces and adds them as
50 // keys with the value of the first argument 'style'
51 function define(style, string, context) {
52 if (context) {
53 var split = string.split(' ');
54 for (var i = 0; i < split.length; i++) {
55 words[split[i]] = {style: style, state: context};
56 }
57 }
58 }
59 //datastep
60 define('def', 'stack pgm view source debug nesting nolist', ['inDataStep']);
61 define('def', 'if while until for do do; end end; then else cancel', ['inDataStep']);
62 define('def', 'label format _n_ _error_', ['inDataStep']);
63 define('def', 'ALTER BUFNO BUFSIZE CNTLLEV COMPRESS DLDMGACTION ENCRYPT ENCRYPTKEY EXTENDOBSCOUNTER GENMAX GENNUM INDEX LABEL OBSBUF OUTREP PW PWREQ READ REPEMPTY REPLACE REUSE ROLE SORTEDBY SPILL TOBSNO TYPE WRITE FILECLOSE FIRSTOBS IN OBS POINTOBS WHERE WHEREUP IDXNAME IDXWHERE DROP KEEP RENAME', ['inDataStep']);
64 define('def', 'filevar finfo finv fipname fipnamel fipstate first firstobs floor', ['inDataStep']);
65 define('def', 'varfmt varinfmt varlabel varlen varname varnum varray varrayx vartype verify vformat vformatd vformatdx vformatn vformatnx vformatw vformatwx vformatx vinarray vinarrayx vinformat vinformatd vinformatdx vinformatn vinformatnx vinformatw vinformatwx vinformatx vlabel vlabelx vlength vlengthx vname vnamex vnferr vtype vtypex weekday', ['inDataStep']);
66 define('def', 'zipfips zipname zipnamel zipstate', ['inDataStep']);
67 define('def', 'put putc putn', ['inDataStep']);
68 define('builtin', 'data run', ['inDataStep']);
69
70
71 //proc
72 define('def', 'data', ['inProc']);
73
74 // flow control for macros
75 define('def', '%if %end %end; %else %else; %do %do; %then', ['inMacro']);
76
77 //everywhere
78 define('builtin', 'proc run; quit; libname filename %macro %mend option options', ['ALL']);
79
80 define('def', 'footnote title libname ods', ['ALL']);
81 define('def', '%let %put %global %sysfunc %eval ', ['ALL']);
82 // automatic macro variables http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a003167023.htm
83 define('variable', '&sysbuffr &syscc &syscharwidth &syscmd &sysdate &sysdate9 &sysday &sysdevic &sysdmg &sysdsn &sysencoding &sysenv &syserr &syserrortext &sysfilrc &syshostname &sysindex &sysinfo &sysjobid &syslast &syslckrc &syslibrc &syslogapplname &sysmacroname &sysmenv &sysmsg &sysncpu &sysodspath &sysparm &syspbuff &sysprocessid &sysprocessname &sysprocname &sysrc &sysscp &sysscpl &sysscpl &syssite &sysstartid &sysstartname &systcpiphostname &systime &sysuserid &sysver &sysvlong &sysvlong4 &syswarningtext', ['ALL']);
84
85 //footnote[1-9]? title[1-9]?
86
87 //options statement
88 define('def', 'source2 nosource2 page pageno pagesize', ['ALL']);
89
90 //proc and datastep
91 define('def', '_all_ _character_ _cmd_ _freq_ _i_ _infile_ _last_ _msg_ _null_ _numeric_ _temporary_ _type_ abort abs addr adjrsq airy alpha alter altlog altprint and arcos array arsin as atan attrc attrib attrn authserver autoexec awscontrol awsdef awsmenu awsmenumerge awstitle backward band base betainv between blocksize blshift bnot bor brshift bufno bufsize bxor by byerr byline byte calculated call cards cards4 catcache cbufno cdf ceil center cexist change chisq cinv class cleanup close cnonct cntllev coalesce codegen col collate collin column comamid comaux1 comaux2 comdef compbl compound compress config continue convert cos cosh cpuid create cross crosstab css curobs cv daccdb daccdbsl daccsl daccsyd dacctab dairy datalines datalines4 datejul datepart datetime day dbcslang dbcstype dclose ddm delete delimiter depdb depdbsl depsl depsyd deptab dequote descending descript design= device dflang dhms dif digamma dim dinfo display distinct dkricond dkrocond dlm dnum do dopen doptname doptnum dread drop dropnote dsname dsnferr echo else emaildlg emailid emailpw emailserver emailsys encrypt end endsas engine eof eov erf erfc error errorcheck errors exist exp fappend fclose fcol fdelete feedback fetch fetchobs fexist fget file fileclose fileexist filefmt filename fileref fmterr fmtsearch fnonct fnote font fontalias fopen foptname foptnum force formatted formchar formdelim formdlim forward fpoint fpos fput fread frewind frlen from fsep fuzz fwrite gaminv gamma getoption getvarc getvarn go goto group gwindow hbar hbound helpenv helploc hms honorappearance hosthelp hostprint hour hpct html hvar ibessel ibr id if index indexc indexw initcmd initstmt inner input inputc inputn inr insert int intck intnx into intrr invaliddata irr is jbessel join juldate keep kentb kurtosis label lag last lbound leave left length levels lgamma lib library libref line linesize link list log log10 log2 logpdf logpmf logsdf lostcard lowcase lrecl ls macro macrogen maps mautosource max maxdec maxr mdy mean measures median memtype merge merror min minute missing missover mlogic mod mode model modify month mopen mort mprint mrecall msglevel msymtabmax mvarsize myy n nest netpv new news nmiss no nobatch nobs nocaps nocardimage nocenter nocharcode nocmdmac nocol nocum nodate nodbcs nodetails nodmr nodms nodmsbatch nodup nodupkey noduplicates noechoauto noequals noerrorabend noexitwindows nofullstimer noicon noimplmac noint nolist noloadlist nomiss nomlogic nomprint nomrecall nomsgcase nomstored nomultenvappl nonotes nonumber noobs noovp nopad nopercent noprint noprintinit normal norow norsasuser nosetinit nosplash nosymbolgen note notes notitle notitles notsorted noverbose noxsync noxwait npv null number numkeys nummousekeys nway obs on open order ordinal otherwise out outer outp= output over ovp p(1 5 10 25 50 75 90 95 99) pad pad2 paired parm parmcards path pathdll pathname pdf peek peekc pfkey pmf point poisson poke position printer probbeta probbnml probchi probf probgam probhypr probit probnegb probnorm probsig probt procleave prt ps pw pwreq qtr quote r ranbin rancau ranexp rangam range ranks rannor ranpoi rantbl rantri ranuni read recfm register regr remote remove rename repeat replace resolve retain return reuse reverse rewind right round rsquare rtf rtrace rtraceloc s s2 samploc sasautos sascontrol sasfrscr sasmsg sasmstore sasscript sasuser saving scan sdf second select selection separated seq serror set setcomm setot sign simple sin sinh siteinfo skewness skip sle sls sortedby sortpgm sortseq sortsize soundex spedis splashlocation split spool sqrt start std stderr stdin stfips stimer stname stnamel stop stopover subgroup subpopn substr sum sumwgt symbol symbolgen symget symput sysget sysin sysleave sysmsg sysparm sysprint sysprintfont sysprod sysrc system t table tables tan tanh tapeclose tbufsize terminal test then timepart tinv tnonct to today tol tooldef totper transformout translate trantab tranwrd trigamma trim trimn trunc truncover type unformatted uniform union until upcase update user usericon uss validate value var weight when where while wincharset window work workinit workterm write wsum xsync xwait yearcutoff yes yyq min max', ['inDataStep', 'inProc']);
92 define('operator', 'and not ', ['inDataStep', 'inProc']);
93
94 // Main function
95 function tokenize(stream, state) {
96 // Finally advance the stream
97 var ch = stream.next();
98
99 // BLOCKCOMMENT
100 if (ch === '/' && stream.eat('*')) {
101 state.continueComment = true;
102 return "comment";
103 } else if (state.continueComment === true) { // in comment block
104 //comment ends at the beginning of the line
105 if (ch === '*' && stream.peek() === '/') {
106 stream.next();
107 state.continueComment = false;
108 } else if (stream.skipTo('*')) { //comment is potentially later in line
109 stream.skipTo('*');
110 stream.next();
111 if (stream.eat('/'))
112 state.continueComment = false;
113 } else {
114 stream.skipToEnd();
115 }
116 return "comment";
117 }
118
119 if (ch == "*" && stream.column() == stream.indentation()) {
120 stream.skipToEnd()
121 return "comment"
122 }
123
124 // DoubleOperator match
125 var doubleOperator = ch + stream.peek();
126
127 if ((ch === '"' || ch === "'") && !state.continueString) {
128 state.continueString = ch
129 return "string"
130 } else if (state.continueString) {
131 if (state.continueString == ch) {
132 state.continueString = null;
133 } else if (stream.skipTo(state.continueString)) {
134 // quote found on this line
135 stream.next();
136 state.continueString = null;
137 } else {
138 stream.skipToEnd();
139 }
140 return "string";
141 } else if (state.continueString !== null && stream.eol()) {
142 stream.skipTo(state.continueString) || stream.skipToEnd();
143 return "string";
144 } else if (/[\d\.]/.test(ch)) { //find numbers
145 if (ch === ".")
146 stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
147 else if (ch === "0")
148 stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
149 else
150 stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
151 return "number";
152 } else if (isDoubleOperatorChar.test(ch + stream.peek())) { // TWO SYMBOL TOKENS
153 stream.next();
154 return "operator";
155 } else if (isDoubleOperatorSym.hasOwnProperty(doubleOperator)) {
156 stream.next();
157 if (stream.peek() === ' ')
158 return isDoubleOperatorSym[doubleOperator.toLowerCase()];
159 } else if (isSingleOperatorChar.test(ch)) { // SINGLE SYMBOL TOKENS
160 return "operator";
161 }
162
163 // Matches one whole word -- even if the word is a character
164 var word;
165 if (stream.match(/[%&;\w]+/, false) != null) {
166 word = ch + stream.match(/[%&;\w]+/, true);
167 if (/&/.test(word)) return 'variable'
168 } else {
169 word = ch;
170 }
171 // the word after DATA PROC or MACRO
172 if (state.nextword) {
173 stream.match(/[\w]+/);
174 // match memname.libname
175 if (stream.peek() === '.') stream.skipTo(' ');
176 state.nextword = false;
177 return 'variable-2';
178 }
179
180 word = word.toLowerCase()
181 // Are we in a DATA Step?
182 if (state.inDataStep) {
183 if (word === 'run;' || stream.match(/run\s;/)) {
184 state.inDataStep = false;
185 return 'builtin';
186 }
187 // variable formats
188 if ((word) && stream.next() === '.') {
189 //either a format or libname.memname
190 if (/\w/.test(stream.peek())) return 'variable-2';
191 else return 'variable';
192 }
193 // do we have a DATA Step keyword
194 if (word && words.hasOwnProperty(word) &&
195 (words[word].state.indexOf("inDataStep") !== -1 ||
196 words[word].state.indexOf("ALL") !== -1)) {
197 //backup to the start of the word
198 if (stream.start < stream.pos)
199 stream.backUp(stream.pos - stream.start);
200 //advance the length of the word and return
201 for (var i = 0; i < word.length; ++i) stream.next();
202 return words[word].style;
203 }
204 }
205 // Are we in an Proc statement?
206 if (state.inProc) {
207 if (word === 'run;' || word === 'quit;') {
208 state.inProc = false;
209 return 'builtin';
210 }
211 // do we have a proc keyword
212 if (word && words.hasOwnProperty(word) &&
213 (words[word].state.indexOf("inProc") !== -1 ||
214 words[word].state.indexOf("ALL") !== -1)) {
215 stream.match(/[\w]+/);
216 return words[word].style;
217 }
218 }
219 // Are we in a Macro statement?
220 if (state.inMacro) {
221 if (word === '%mend') {
222 if (stream.peek() === ';') stream.next();
223 state.inMacro = false;
224 return 'builtin';
225 }
226 if (word && words.hasOwnProperty(word) &&
227 (words[word].state.indexOf("inMacro") !== -1 ||
228 words[word].state.indexOf("ALL") !== -1)) {
229 stream.match(/[\w]+/);
230 return words[word].style;
231 }
232
233 return 'atom';
234 }
235 // Do we have Keywords specific words?
236 if (word && words.hasOwnProperty(word)) {
237 // Negates the initial next()
238 stream.backUp(1);
239 // Actually move the stream
240 stream.match(/[\w]+/);
241 if (word === 'data' && /=/.test(stream.peek()) === false) {
242 state.inDataStep = true;
243 state.nextword = true;
244 return 'builtin';
245 }
246 if (word === 'proc') {
247 state.inProc = true;
248 state.nextword = true;
249 return 'builtin';
250 }
251 if (word === '%macro') {
252 state.inMacro = true;
253 state.nextword = true;
254 return 'builtin';
255 }
256 if (/title[1-9]/.test(word)) return 'def';
257
258 if (word === 'footnote') {
259 stream.eat(/[1-9]/);
260 return 'def';
261 }
262
263 // Returns their value as state in the prior define methods
264 if (state.inDataStep === true && words[word].state.indexOf("inDataStep") !== -1)
265 return words[word].style;
266 if (state.inProc === true && words[word].state.indexOf("inProc") !== -1)
267 return words[word].style;
268 if (state.inMacro === true && words[word].state.indexOf("inMacro") !== -1)
269 return words[word].style;
270 if (words[word].state.indexOf("ALL") !== -1)
271 return words[word].style;
272 return null;
273 }
274 // Unrecognized syntax
275 return null;
276 }
277
278 return {
279 startState: function () {
280 return {
281 inDataStep: false,
282 inProc: false,
283 inMacro: false,
284 nextword: false,
285 continueString: null,
286 continueComment: false
287 };
288 },
289 token: function (stream, state) {
290 // Strip the spaces, but regex will account for them either way
291 if (stream.eatSpace()) return null;
292 // Go through the main process
293 return tokenize(stream, state);
294 },
295
296 blockCommentStart: "/*",
297 blockCommentEnd: "*/"
298 };
299
300 });
301
302 CodeMirror.defineMIME("text/x-sas", "sas");
303 });
@@ -0,0 +1,195 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 function wordRegexp(words) {
15 return new RegExp("^((" + words.join(")|(") + "))\\b");
16 };
17
18 var builtinArray = [
19 "Clamp",
20 "Constructor",
21 "EnforceRange",
22 "Exposed",
23 "ImplicitThis",
24 "Global", "PrimaryGlobal",
25 "LegacyArrayClass",
26 "LegacyUnenumerableNamedProperties",
27 "LenientThis",
28 "NamedConstructor",
29 "NewObject",
30 "NoInterfaceObject",
31 "OverrideBuiltins",
32 "PutForwards",
33 "Replaceable",
34 "SameObject",
35 "TreatNonObjectAsNull",
36 "TreatNullAs",
37 "EmptyString",
38 "Unforgeable",
39 "Unscopeable"
40 ];
41 var builtins = wordRegexp(builtinArray);
42
43 var typeArray = [
44 "unsigned", "short", "long", // UnsignedIntegerType
45 "unrestricted", "float", "double", // UnrestrictedFloatType
46 "boolean", "byte", "octet", // Rest of PrimitiveType
47 "Promise", // PromiseType
48 "ArrayBuffer", "DataView", "Int8Array", "Int16Array", "Int32Array",
49 "Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray",
50 "Float32Array", "Float64Array", // BufferRelatedType
51 "ByteString", "DOMString", "USVString", "sequence", "object", "RegExp",
52 "Error", "DOMException", "FrozenArray", // Rest of NonAnyType
53 "any", // Rest of SingleType
54 "void" // Rest of ReturnType
55 ];
56 var types = wordRegexp(typeArray);
57
58 var keywordArray = [
59 "attribute", "callback", "const", "deleter", "dictionary", "enum", "getter",
60 "implements", "inherit", "interface", "iterable", "legacycaller", "maplike",
61 "partial", "required", "serializer", "setlike", "setter", "static",
62 "stringifier", "typedef", // ArgumentNameKeyword except
63 // "unrestricted"
64 "optional", "readonly", "or"
65 ];
66 var keywords = wordRegexp(keywordArray);
67
68 var atomArray = [
69 "true", "false", // BooleanLiteral
70 "Infinity", "NaN", // FloatLiteral
71 "null" // Rest of ConstValue
72 ];
73 var atoms = wordRegexp(atomArray);
74
75 CodeMirror.registerHelper("hintWords", "webidl",
76 builtinArray.concat(typeArray).concat(keywordArray).concat(atomArray));
77
78 var startDefArray = ["callback", "dictionary", "enum", "interface"];
79 var startDefs = wordRegexp(startDefArray);
80
81 var endDefArray = ["typedef"];
82 var endDefs = wordRegexp(endDefArray);
83
84 var singleOperators = /^[:<=>?]/;
85 var integers = /^-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/;
86 var floats = /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/;
87 var identifiers = /^_?[A-Za-z][0-9A-Z_a-z-]*/;
88 var identifiersEnd = /^_?[A-Za-z][0-9A-Z_a-z-]*(?=\s*;)/;
89 var strings = /^"[^"]*"/;
90 var multilineComments = /^\/\*.*?\*\//;
91 var multilineCommentsStart = /^\/\*.*/;
92 var multilineCommentsEnd = /^.*?\*\//;
93
94 function readToken(stream, state) {
95 // whitespace
96 if (stream.eatSpace()) return null;
97
98 // comment
99 if (state.inComment) {
100 if (stream.match(multilineCommentsEnd)) {
101 state.inComment = false;
102 return "comment";
103 }
104 stream.skipToEnd();
105 return "comment";
106 }
107 if (stream.match("//")) {
108 stream.skipToEnd();
109 return "comment";
110 }
111 if (stream.match(multilineComments)) return "comment";
112 if (stream.match(multilineCommentsStart)) {
113 state.inComment = true;
114 return "comment";
115 }
116
117 // integer and float
118 if (stream.match(/^-?[0-9\.]/, false)) {
119 if (stream.match(integers) || stream.match(floats)) return "number";
120 }
121
122 // string
123 if (stream.match(strings)) return "string";
124
125 // identifier
126 if (state.startDef && stream.match(identifiers)) return "def";
127
128 if (state.endDef && stream.match(identifiersEnd)) {
129 state.endDef = false;
130 return "def";
131 }
132
133 if (stream.match(keywords)) return "keyword";
134
135 if (stream.match(types)) {
136 var lastToken = state.lastToken;
137 var nextToken = (stream.match(/^\s*(.+?)\b/, false) || [])[1];
138
139 if (lastToken === ":" || lastToken === "implements" ||
140 nextToken === "implements" || nextToken === "=") {
141 // Used as identifier
142 return "builtin";
143 } else {
144 // Used as type
145 return "variable-3";
146 }
147 }
148
149 if (stream.match(builtins)) return "builtin";
150 if (stream.match(atoms)) return "atom";
151 if (stream.match(identifiers)) return "variable";
152
153 // other
154 if (stream.match(singleOperators)) return "operator";
155
156 // unrecognized
157 stream.next();
158 return null;
159 };
160
161 CodeMirror.defineMode("webidl", function() {
162 return {
163 startState: function() {
164 return {
165 // Is in multiline comment
166 inComment: false,
167 // Last non-whitespace, matched token
168 lastToken: "",
169 // Next token is a definition
170 startDef: false,
171 // Last token of the statement is a definition
172 endDef: false
173 };
174 },
175 token: function(stream, state) {
176 var style = readToken(stream, state);
177
178 if (style) {
179 var cur = stream.current();
180 state.lastToken = cur;
181 if (style === "keyword") {
182 state.startDef = startDefs.test(cur);
183 state.endDef = state.endDef || endDefs.test(cur);
184 } else {
185 state.startDef = false;
186 }
187 }
188
189 return style;
190 }
191 };
192 });
193
194 CodeMirror.defineMIME("text/x-webidl", "webidl");
195 });
@@ -0,0 +1,204 b''
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 // Yacas mode copyright (c) 2015 by Grzegorz Mazur
5 // Loosely based on mathematica mode by Calin Barbat
6
7 (function(mod) {
8 if (typeof exports == "object" && typeof module == "object") // CommonJS
9 mod(require("../../lib/codemirror"));
10 else if (typeof define == "function" && define.amd) // AMD
11 define(["../../lib/codemirror"], mod);
12 else // Plain browser env
13 mod(CodeMirror);
14 })(function(CodeMirror) {
15 "use strict";
16
17 CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
18
19 function words(str) {
20 var obj = {}, words = str.split(" ");
21 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
22 return obj;
23 }
24
25 var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " +
26 "FromString Function Integrate InverseTaylor Limit " +
27 "LocalSymbols Macro MacroRule MacroRulePattern " +
28 "NIntegrate Rule RulePattern Subst TD TExplicitSum " +
29 "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " +
30 "ToStdout ToString TraceRule Until While");
31
32 // patterns
33 var pFloatForm = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)";
34 var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)";
35
36 // regular expressions
37 var reFloatForm = new RegExp(pFloatForm);
38 var reIdentifier = new RegExp(pIdentifier);
39 var rePattern = new RegExp(pIdentifier + "?_" + pIdentifier);
40 var reFunctionLike = new RegExp(pIdentifier + "\\s*\\(");
41
42 function tokenBase(stream, state) {
43 var ch;
44
45 // get next character
46 ch = stream.next();
47
48 // string
49 if (ch === '"') {
50 state.tokenize = tokenString;
51 return state.tokenize(stream, state);
52 }
53
54 // comment
55 if (ch === '/') {
56 if (stream.eat('*')) {
57 state.tokenize = tokenComment;
58 return state.tokenize(stream, state);
59 }
60 if (stream.eat("/")) {
61 stream.skipToEnd();
62 return "comment";
63 }
64 }
65
66 // go back one character
67 stream.backUp(1);
68
69 // update scope info
70 var m = stream.match(/^(\w+)\s*\(/, false);
71 if (m !== null && bodiedOps.hasOwnProperty(m[1]))
72 state.scopes.push('bodied');
73
74 var scope = currentScope(state);
75
76 if (scope === 'bodied' && ch === '[')
77 state.scopes.pop();
78
79 if (ch === '[' || ch === '{' || ch === '(')
80 state.scopes.push(ch);
81
82 scope = currentScope(state);
83
84 if (scope === '[' && ch === ']' ||
85 scope === '{' && ch === '}' ||
86 scope === '(' && ch === ')')
87 state.scopes.pop();
88
89 if (ch === ';') {
90 while (scope === 'bodied') {
91 state.scopes.pop();
92 scope = currentScope(state);
93 }
94 }
95
96 // look for ordered rules
97 if (stream.match(/\d+ *#/, true, false)) {
98 return 'qualifier';
99 }
100
101 // look for numbers
102 if (stream.match(reFloatForm, true, false)) {
103 return 'number';
104 }
105
106 // look for placeholders
107 if (stream.match(rePattern, true, false)) {
108 return 'variable-3';
109 }
110
111 // match all braces separately
112 if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) {
113 return 'bracket';
114 }
115
116 // literals looking like function calls
117 if (stream.match(reFunctionLike, true, false)) {
118 stream.backUp(1);
119 return 'variable';
120 }
121
122 // all other identifiers
123 if (stream.match(reIdentifier, true, false)) {
124 return 'variable-2';
125 }
126
127 // operators; note that operators like @@ or /; are matched separately for each symbol.
128 if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%|#)/, true, false)) {
129 return 'operator';
130 }
131
132 // everything else is an error
133 return 'error';
134 }
135
136 function tokenString(stream, state) {
137 var next, end = false, escaped = false;
138 while ((next = stream.next()) != null) {
139 if (next === '"' && !escaped) {
140 end = true;
141 break;
142 }
143 escaped = !escaped && next === '\\';
144 }
145 if (end && !escaped) {
146 state.tokenize = tokenBase;
147 }
148 return 'string';
149 };
150
151 function tokenComment(stream, state) {
152 var prev, next;
153 while((next = stream.next()) != null) {
154 if (prev === '*' && next === '/') {
155 state.tokenize = tokenBase;
156 break;
157 }
158 prev = next;
159 }
160 return 'comment';
161 }
162
163 function currentScope(state) {
164 var scope = null;
165 if (state.scopes.length > 0)
166 scope = state.scopes[state.scopes.length - 1];
167 return scope;
168 }
169
170 return {
171 startState: function() {
172 return {
173 tokenize: tokenBase,
174 scopes: []
175 };
176 },
177 token: function(stream, state) {
178 if (stream.eatSpace()) return null;
179 return state.tokenize(stream, state);
180 },
181 indent: function(state, textAfter) {
182 if (state.tokenize !== tokenBase && state.tokenize !== null)
183 return CodeMirror.Pass;
184
185 var delta = 0;
186 if (textAfter === ']' || textAfter === '];' ||
187 textAfter === '}' || textAfter === '};' ||
188 textAfter === ');')
189 delta = -1;
190
191 return (state.scopes.length + delta) * _config.indentUnit;
192 },
193 electricChars: "{}[]();",
194 blockCommentStart: "/*",
195 blockCommentEnd: "*/",
196 lineComment: "//"
197 };
198 });
199
200 CodeMirror.defineMIME('text/x-yacas', {
201 name: 'yacas'
202 });
203
204 });
@@ -5,6 +5,7 b''
5 5 font-family: monospace;
6 6 height: 300px;
7 7 color: black;
8 direction: ltr;
8 9 border-radius: @border-radius;
9 10 border: @border-thickness solid @grey6;
10 11 margin: 0 0 @padding;
@@ -15,7 +16,8 b''
15 16 .CodeMirror-lines {
16 17 padding: 4px 0; /* Vertical padding around content */
17 18 }
18 .CodeMirror pre {
19 .CodeMirror pre.CodeMirror-line,
20 .CodeMirror pre.CodeMirror-line-like {
19 21 padding: 0 4px; /* Horizontal padding of content */
20 22 }
21 23
@@ -44,28 +46,36 b''
44 46
45 47 /* CURSOR */
46 48
47 .CodeMirror div.CodeMirror-cursor {
49 .CodeMirror-cursor {
48 50 border-left: 1px solid black;
51 border-right: none;
52 width: 0;
49 53 }
50 54 /* Shown when moving in bi-directional text */
51 55 .CodeMirror div.CodeMirror-secondarycursor {
52 56 border-left: 1px solid silver;
53 57 }
54 .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
58 .cm-fat-cursor .CodeMirror-cursor {
55 59 width: auto;
56 border: 0;
60 border: 0 !important;
57 61 background: @grey6;
58 62 }
59 .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
63 .cm-fat-cursor div.CodeMirror-cursors {
60 64 z-index: 1;
61 65 }
62
66 .cm-fat-cursor-mark {
67 background-color: rgba(20, 255, 20, 0.5);
68 -webkit-animation: blink 1.06s steps(1) infinite;
69 -moz-animation: blink 1.06s steps(1) infinite;
70 animation: blink 1.06s steps(1) infinite;
71 }
63 72 .cm-animate-fat-cursor {
64 73 width: auto;
65 74 border: 0;
66 75 -webkit-animation: blink 1.06s steps(1) infinite;
67 76 -moz-animation: blink 1.06s steps(1) infinite;
68 77 animation: blink 1.06s steps(1) infinite;
78 background-color: #7e7;
69 79 }
70 80 @-moz-keyframes blink {
71 81 0% { background: #7e7; }
@@ -84,12 +94,18 b''
84 94 }
85 95
86 96 /* Can style cursor different in overwrite (non-insert) mode */
87 div.CodeMirror-overwrite div.CodeMirror-cursor {}
97 .CodeMirror-overwrite .CodeMirror-cursor {}
88 98
89 99 .cm-tab { display: inline-block; text-decoration: inherit; }
90 100
101 .CodeMirror-rulers {
102 position: absolute;
103 left: 0; right: 0; top: -50px; bottom: 0;
104 overflow: hidden;
105 }
91 106 .CodeMirror-ruler {
92 107 border-left: 1px solid #ccc;
108 top: 0; bottom: 0;
93 109 position: absolute;
94 110 }
95 111
@@ -113,7 +129,7 b' div.CodeMirror-overwrite div.CodeMirror-'
113 129 .cm-s-default .cm-property,
114 130 .cm-s-default .cm-operator {}
115 131 .cm-s-default .cm-variable-2 {color: #05a;}
116 .cm-s-default .cm-variable-3 {color: #085;}
132 .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
117 133 .cm-s-default .cm-comment {color: #a50;}
118 134 .cm-s-default .cm-string {color: #a11;}
119 135 .cm-s-default .cm-string-2 {color: #f50;}
@@ -133,8 +149,8 b' div.CodeMirror-overwrite div.CodeMirror-'
133 149
134 150 /* Default styles for common addons */
135 151
136 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
137 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
152 div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
153 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
138 154 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
139 155 .CodeMirror-activeline-background {background: #e8f2ff;}
140 156
@@ -191,20 +207,21 b' div.CodeMirror span.CodeMirror-nonmatchi'
191 207
192 208 .CodeMirror-gutters {
193 209 position: absolute; left: 0; top: 0;
210 min-height: 100%;
194 211 z-index: 3;
195 212 }
196 213 .CodeMirror-gutter {
197 214 white-space: normal;
198 215 height: 100%;
199 216 display: inline-block;
217 vertical-align: top;
200 218 margin-bottom: -30px;
201 /* Hack to make IE7 behave */
202 *zoom:1;
203 *display:inline;
204 219 }
205 220 .CodeMirror-gutter-wrapper {
206 221 position: absolute;
207 222 z-index: 4;
223 background: none !important;
224 border: none !important;
208 225 height: 100%;
209 226 }
210 227 .CodeMirror-gutter-background {
@@ -227,7 +244,8 b' div.CodeMirror span.CodeMirror-nonmatchi'
227 244 cursor: text;
228 245 min-height: 1px; /* prevents collapsing before first draw */
229 246 }
230 .CodeMirror pre {
247 .CodeMirror pre.CodeMirror-line,
248 .CodeMirror pre.CodeMirror-line-like {
231 249 /* Reset some styles that the rest of the page might have set */
232 250 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
233 251 border-width: 0;
@@ -243,8 +261,11 b' div.CodeMirror span.CodeMirror-nonmatchi'
243 261 position: relative;
244 262 overflow: visible;
245 263 -webkit-tap-highlight-color: transparent;
264 -webkit-font-variant-ligatures: contextual;
265 font-variant-ligatures: contextual;
246 266 }
247 .CodeMirror-wrap pre {
267 .CodeMirror-wrap pre.CodeMirror-line,
268 .CodeMirror-wrap pre.CodeMirror-line-like {
248 269 word-wrap: break-word;
249 270 white-space: pre-wrap;
250 271 word-break: normal;
@@ -259,11 +280,14 b' div.CodeMirror span.CodeMirror-nonmatchi'
259 280 .CodeMirror-linewidget {
260 281 position: relative;
261 282 z-index: 2;
283 padding: 0.1px; /* Force widget margins to stay inside of the container */
262 284 overflow: auto;
263 285 }
264 286
265 287 .CodeMirror-widget {}
266 288
289 .CodeMirror-rtl pre { direction: rtl; }
290
267 291 .CodeMirror-code {
268 292 outline: none;
269 293 }
@@ -286,13 +310,12 b' div.CodeMirror span.CodeMirror-nonmatchi'
286 310 visibility: hidden;
287 311 }
288 312
289
290 .CodeMirror div.CodeMirror-cursor {
313 .CodeMirror-cursor {
291 314 position: absolute;
315 pointer-events: none;
292 316 border-right: none;
293 317 width: 0;
294 318 }
295
296 319 .CodeMirror-measure pre { position: static; }
297 320
298 321 div.CodeMirror-cursors {
@@ -315,13 +338,10 b' div.CodeMirror-dragcursors {'
315 338 .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
316 339
317 340 .cm-searching {
318 background: #ffa;
319 background: rgba(255, 255, 0, .4);
341 background-color: #ffa;
342 background-color: rgba(255, 255, 0, .4);
320 343 }
321 344
322 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
323 .CodeMirror span { *vertical-align: text-bottom; }
324
325 345 /* Used to force a border model for a node */
326 346 .cm-force-border { padding-right: .1px; }
327 347
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -68,6 +68,7 b''
68 68 });
69 69
70 70 CodeMirror.defineMIME("application/pgp", "asciiarmor");
71 CodeMirror.defineMIME("application/pgp-encrypted", "asciiarmor");
71 72 CodeMirror.defineMIME("application/pgp-keys", "asciiarmor");
72 73 CodeMirror.defineMIME("application/pgp-signature", "asciiarmor");
73 74 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 * =====================================================================================
@@ -9,7 +9,7 b''
9 9 * Description: CodeMirror mode for Asterisk dialplan
10 10 *
11 11 * Created: 05/17/2012 09:20:25 PM
12 * Revision: none
12 * Revision: 08/05/2019 AstLinux Project: Support block-comments
13 13 *
14 14 * Author: Stas Kobzar (stas@modulis.ca),
15 15 * Company: Modulis.ca Inc.
@@ -67,7 +67,26 b' CodeMirror.defineMode("asterisk", functi'
67 67 var cur = '';
68 68 var ch = stream.next();
69 69 // comment
70 if (state.blockComment) {
71 if (ch == "-" && stream.match("-;", true)) {
72 state.blockComment = false;
73 } else if (stream.skipTo("--;")) {
74 stream.next();
75 stream.next();
76 stream.next();
77 state.blockComment = false;
78 } else {
79 stream.skipToEnd();
80 }
81 return "comment";
82 }
70 83 if(ch == ";") {
84 if (stream.match("--", true)) {
85 if (!stream.match("-", false)) { // Except ;--- is not a block comment
86 state.blockComment = true;
87 return "comment";
88 }
89 }
71 90 stream.skipToEnd();
72 91 return "comment";
73 92 }
@@ -124,6 +143,7 b' CodeMirror.defineMode("asterisk", functi'
124 143 return {
125 144 startState: function() {
126 145 return {
146 blockComment: false,
127 147 extenStart: false,
128 148 extenSame: false,
129 149 extenInclude: false,
@@ -187,7 +207,11 b' CodeMirror.defineMode("asterisk", functi'
187 207 }
188 208
189 209 return null;
190 }
210 },
211
212 blockCommentStart: ";--",
213 blockCommentEnd: "--;",
214 lineComment: ";"
191 215 };
192 216 });
193 217
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Brainfuck mode created by Michael Kaminsky https://github.com/mkaminsky11
5 5
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,6 +11,41 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 function Context(indented, column, type, info, align, prev) {
15 this.indented = indented;
16 this.column = column;
17 this.type = type;
18 this.info = info;
19 this.align = align;
20 this.prev = prev;
21 }
22 function pushContext(state, col, type, info) {
23 var indent = state.indented;
24 if (state.context && state.context.type == "statement" && type != "statement")
25 indent = state.context.indented;
26 return state.context = new Context(indent, col, type, info, null, state.context);
27 }
28 function popContext(state) {
29 var t = state.context.type;
30 if (t == ")" || t == "]" || t == "}")
31 state.indented = state.context.indented;
32 return state.context = state.context.prev;
33 }
34
35 function typeBefore(stream, state, pos) {
36 if (state.prevToken == "variable" || state.prevToken == "type") return true;
37 if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
38 if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
39 }
40
41 function isTopScope(context) {
42 for (;;) {
43 if (!context || context.type == "top") return true;
44 if (context.type == "}" && context.prev.info != "namespace") return false;
45 context = context.prev;
46 }
47 }
48
14 49 CodeMirror.defineMode("clike", function(config, parserConfig) {
15 50 var indentUnit = config.indentUnit,
16 51 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
@@ -30,7 +65,10 b' CodeMirror.defineMode("clike", function('
30 65 numberStart = parserConfig.numberStart || /[\d\.]/,
31 66 number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
32 67 isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
33 endStatement = parserConfig.endStatement || /^[;:,]$/;
68 isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
69 // An optional function that takes a {string} token and returns true if it
70 // should be treated as a builtin.
71 isReservedIdentifier = parserConfig.isReservedIdentifier || false;
34 72
35 73 var curPunc, isDefKeyword;
36 74
@@ -64,12 +102,12 b' CodeMirror.defineMode("clike", function('
64 102 }
65 103 }
66 104 if (isOperatorChar.test(ch)) {
67 stream.eatWhile(isOperatorChar);
105 while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
68 106 return "operator";
69 107 }
70 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
108 stream.eatWhile(isIdentifierChar);
71 109 if (namespaceSeparator) while (stream.match(namespaceSeparator))
72 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
110 stream.eatWhile(isIdentifierChar);
73 111
74 112 var cur = stream.current();
75 113 if (contains(keywords, cur)) {
@@ -77,8 +115,9 b' CodeMirror.defineMode("clike", function('
77 115 if (contains(defKeywords, cur)) isDefKeyword = true;
78 116 return "keyword";
79 117 }
80 if (contains(types, cur)) return "variable-3";
81 if (contains(builtin, cur)) {
118 if (contains(types, cur)) return "type";
119 if (contains(builtin, cur)
120 || (isReservedIdentifier && isReservedIdentifier(cur))) {
82 121 if (contains(blockKeywords, cur)) curPunc = "newstatement";
83 122 return "builtin";
84 123 }
@@ -111,40 +150,9 b' CodeMirror.defineMode("clike", function('
111 150 return "comment";
112 151 }
113 152
114 function Context(indented, column, type, align, prev) {
115 this.indented = indented;
116 this.column = column;
117 this.type = type;
118 this.align = align;
119 this.prev = prev;
120 }
121 function isStatement(type) {
122 return type == "statement" || type == "switchstatement" || type == "namespace";
123 }
124 function pushContext(state, col, type) {
125 var indent = state.indented;
126 if (state.context && isStatement(state.context.type) && !isStatement(type))
127 indent = state.context.indented;
128 return state.context = new Context(indent, col, type, null, state.context);
129 }
130 function popContext(state) {
131 var t = state.context.type;
132 if (t == ")" || t == "]" || t == "}")
133 state.indented = state.context.indented;
134 return state.context = state.context.prev;
135 }
136
137 function typeBefore(stream, state) {
138 if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
139 if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
140 }
141
142 function isTopScope(context) {
143 for (;;) {
144 if (!context || context.type == "top") return true;
145 if (context.type == "}" && context.prev.type != "namespace") return false;
146 context = context.prev;
147 }
153 function maybeEOL(stream, state) {
154 if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
155 state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
148 156 }
149 157
150 158 // Interface
@@ -153,7 +161,7 b' CodeMirror.defineMode("clike", function('
153 161 startState: function(basecolumn) {
154 162 return {
155 163 tokenize: null,
156 context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
164 context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
157 165 indented: 0,
158 166 startOfLine: true,
159 167 prevToken: null
@@ -167,36 +175,32 b' CodeMirror.defineMode("clike", function('
167 175 state.indented = stream.indentation();
168 176 state.startOfLine = true;
169 177 }
170 if (stream.eatSpace()) return null;
178 if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
171 179 curPunc = isDefKeyword = null;
172 180 var style = (state.tokenize || tokenBase)(stream, state);
173 181 if (style == "comment" || style == "meta") return style;
174 182 if (ctx.align == null) ctx.align = true;
175 183
176 if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state);
184 if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
185 while (state.context.type == "statement") popContext(state);
177 186 else if (curPunc == "{") pushContext(state, stream.column(), "}");
178 187 else if (curPunc == "[") pushContext(state, stream.column(), "]");
179 188 else if (curPunc == "(") pushContext(state, stream.column(), ")");
180 189 else if (curPunc == "}") {
181 while (isStatement(ctx.type)) ctx = popContext(state);
190 while (ctx.type == "statement") ctx = popContext(state);
182 191 if (ctx.type == "}") ctx = popContext(state);
183 while (isStatement(ctx.type)) ctx = popContext(state);
192 while (ctx.type == "statement") ctx = popContext(state);
184 193 }
185 194 else if (curPunc == ctx.type) popContext(state);
186 195 else if (indentStatements &&
187 196 (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
188 (isStatement(ctx.type) && curPunc == "newstatement"))) {
189 var type = "statement";
190 if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
191 type = "switchstatement";
192 else if (style == "keyword" && stream.current() == "namespace")
193 type = "namespace";
194 pushContext(state, stream.column(), type);
197 (ctx.type == "statement" && curPunc == "newstatement"))) {
198 pushContext(state, stream.column(), "statement", stream.current());
195 199 }
196 200
197 201 if (style == "variable" &&
198 202 ((state.prevToken == "def" ||
199 (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
203 (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
200 204 isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
201 205 style = "def";
202 206
@@ -209,24 +213,28 b' CodeMirror.defineMode("clike", function('
209 213
210 214 state.startOfLine = false;
211 215 state.prevToken = isDefKeyword ? "def" : style || curPunc;
216 maybeEOL(stream, state);
212 217 return style;
213 218 },
214 219
215 220 indent: function(state, textAfter) {
216 if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
221 if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
217 222 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
218 if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
223 var closing = firstChar == ctx.type;
224 if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
225 if (parserConfig.dontIndentStatements)
226 while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
227 ctx = ctx.prev
219 228 if (hooks.indent) {
220 var hook = hooks.indent(state, ctx, textAfter);
229 var hook = hooks.indent(state, ctx, textAfter, indentUnit);
221 230 if (typeof hook == "number") return hook
222 231 }
223 var closing = firstChar == ctx.type;
224 var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
232 var switchBlock = ctx.prev && ctx.prev.info == "switch";
225 233 if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
226 234 while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
227 235 return ctx.indented
228 236 }
229 if (isStatement(ctx.type))
237 if (ctx.type == "statement")
230 238 return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
231 239 if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
232 240 return ctx.column + (closing ? 0 : 1);
@@ -240,6 +248,7 b' CodeMirror.defineMode("clike", function('
240 248 electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
241 249 blockCommentStart: "/*",
242 250 blockCommentEnd: "*/",
251 blockCommentContinue: " * ",
243 252 lineComment: "//",
244 253 fold: "brace"
245 254 };
@@ -258,8 +267,52 b' CodeMirror.defineMode("clike", function('
258 267 }
259 268 }
260 269 var cKeywords = "auto if break case register continue return default do sizeof " +
261 "static else struct switch extern typedef union for goto while enum const volatile";
262 var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
270 "static else struct switch extern typedef union for goto while enum const " +
271 "volatile inline restrict asm fortran";
272
273 // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
274 var cppKeywords = "alignas alignof and and_eq audit axiom bitand bitor catch " +
275 "class compl concept constexpr const_cast decltype delete dynamic_cast " +
276 "explicit export final friend import module mutable namespace new noexcept " +
277 "not not_eq operator or or_eq override private protected public " +
278 "reinterpret_cast requires static_assert static_cast template this " +
279 "thread_local throw try typeid typename using virtual xor xor_eq";
280
281 var objCKeywords = "bycopy byref in inout oneway out self super atomic nonatomic retain copy " +
282 "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " +
283 "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " +
284 "@public @package @private @protected @required @optional @try @catch @finally @import " +
285 "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available";
286
287 var objCBuiltins = "FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION " +
288 " NS_RETURNS_RETAINEDNS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER " +
289 "NS_DESIGNATED_INITIALIZER NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION " +
290 "NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"
291
292 // Do not use this. Use the cTypes function below. This is global just to avoid
293 // excessive calls when cTypes is being called multiple times during a parse.
294 var basicCTypes = words("int long char short double float unsigned signed " +
295 "void bool");
296
297 // Do not use this. Use the objCTypes function below. This is global just to avoid
298 // excessive calls when objCTypes is being called multiple times during a parse.
299 var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL");
300
301 // Returns true if identifier is a "C" type.
302 // C type is defined as those that are reserved by the compiler (basicTypes),
303 // and those that end in _t (Reserved by POSIX for types)
304 // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
305 function cTypes(identifier) {
306 return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);
307 }
308
309 // Returns true if identifier is a "Objective C" type.
310 function objCTypes(identifier) {
311 return cTypes(identifier) || contains(basicObjCTypes, identifier);
312 }
313
314 var cBlockKeywords = "case do else for if switch while struct enum union";
315 var cDefKeywords = "struct enum union";
263 316
264 317 function cppHook(stream, state) {
265 318 if (!state.startOfLine) return false
@@ -277,10 +330,18 b' CodeMirror.defineMode("clike", function('
277 330 }
278 331
279 332 function pointerHook(_stream, state) {
280 if (state.prevToken == "variable-3") return "variable-3";
333 if (state.prevToken == "type") return "type";
281 334 return false;
282 335 }
283 336
337 // For C and C++ (and ObjC): identifiers starting with __
338 // or _ followed by a capital letter are reserved for the compiler.
339 function cIsReservedIdentifier(token) {
340 if (!token || token.length < 2) return false;
341 if (token[0] != '_') return false;
342 return (token[1] == '_') || (token[1] !== token[1].toLowerCase());
343 }
344
284 345 function cpp14Literal(stream) {
285 346 stream.eatWhile(/[\w\.']/);
286 347 return "number";
@@ -311,7 +372,7 b' CodeMirror.defineMode("clike", function('
311 372 }
312 373
313 374 function cppLooksLikeConstructor(word) {
314 var lastTwo = /(\w+)::(\w+)$/.exec(word);
375 var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
315 376 return lastTwo && lastTwo[1] == lastTwo[2];
316 377 }
317 378
@@ -363,29 +424,30 b' CodeMirror.defineMode("clike", function('
363 424 def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
364 425 name: "clike",
365 426 keywords: words(cKeywords),
366 types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
367 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
368 "uint32_t uint64_t"),
369 blockKeywords: words("case do else for if switch while struct"),
370 defKeywords: words("struct"),
427 types: cTypes,
428 blockKeywords: words(cBlockKeywords),
429 defKeywords: words(cDefKeywords),
371 430 typeFirstDefinitions: true,
372 atoms: words("null true false"),
373 hooks: {"#": cppHook, "*": pointerHook},
431 atoms: words("NULL true false"),
432 isReservedIdentifier: cIsReservedIdentifier,
433 hooks: {
434 "#": cppHook,
435 "*": pointerHook,
436 },
374 437 modeProps: {fold: ["brace", "include"]}
375 438 });
376 439
377 440 def(["text/x-c++src", "text/x-c++hdr"], {
378 441 name: "clike",
379 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
380 "static_cast typeid catch operator template typename class friend private " +
381 "this using const_cast inline public throw virtual delete mutable protected " +
382 "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
383 "static_assert override"),
384 types: words(cTypes + " bool wchar_t"),
385 blockKeywords: words("catch class do else finally for if struct switch try while"),
386 defKeywords: words("class namespace struct enum union"),
442 keywords: words(cKeywords + " " + cppKeywords),
443 types: cTypes,
444 blockKeywords: words(cBlockKeywords + " class try catch"),
445 defKeywords: words(cDefKeywords + " class namespace"),
387 446 typeFirstDefinitions: true,
388 atoms: words("true false null"),
447 atoms: words("true false NULL nullptr"),
448 dontIndentStatements: /^template$/,
449 isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
450 isReservedIdentifier: cIsReservedIdentifier,
389 451 hooks: {
390 452 "#": cppHook,
391 453 "*": pointerHook,
@@ -418,19 +480,22 b' CodeMirror.defineMode("clike", function('
418 480 def("text/x-java", {
419 481 name: "clike",
420 482 keywords: words("abstract assert break case catch class const continue default " +
421 "do else enum extends final finally float for goto if implements import " +
483 "do else enum extends final finally for goto if implements import " +
422 484 "instanceof interface native new package private protected public " +
423 485 "return static strictfp super switch synchronized this throw throws transient " +
424 "try volatile while"),
486 "try volatile while @interface"),
425 487 types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
426 488 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
427 489 blockKeywords: words("catch class do else finally for if switch try while"),
428 defKeywords: words("class interface package enum"),
490 defKeywords: words("class interface enum @interface"),
429 491 typeFirstDefinitions: true,
430 492 atoms: words("true false null"),
431 endStatement: /^[;:]$/,
493 number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
432 494 hooks: {
433 495 "@": function(stream) {
496 // Don't match the @interface keyword.
497 if (stream.match('interface', false)) return false;
498
434 499 stream.eatWhile(/[\w\$_]/);
435 500 return "meta";
436 501 }
@@ -479,25 +544,42 b' CodeMirror.defineMode("clike", function('
479 544 return "string";
480 545 }
481 546
547 function tokenNestedComment(depth) {
548 return function (stream, state) {
549 var ch
550 while (ch = stream.next()) {
551 if (ch == "*" && stream.eat("/")) {
552 if (depth == 1) {
553 state.tokenize = null
554 break
555 } else {
556 state.tokenize = tokenNestedComment(depth - 1)
557 return state.tokenize(stream, state)
558 }
559 } else if (ch == "/" && stream.eat("*")) {
560 state.tokenize = tokenNestedComment(depth + 1)
561 return state.tokenize(stream, state)
562 }
563 }
564 return "comment"
565 }
566 }
567
482 568 def("text/x-scala", {
483 569 name: "clike",
484 570 keywords: words(
485
486 571 /* scala */
487 572 "abstract case catch class def do else extends final finally for forSome if " +
488 573 "implicit import lazy match new null object override package private protected return " +
489 "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
490 "<% >: # @ " +
574 "sealed super this throw trait try type val var while with yield _ " +
491 575
492 576 /* package scala */
493 577 "assert assume require print println printf readLine readBoolean readByte readShort " +
494 "readChar readInt readLong readFloat readDouble " +
495
496 ":: #:: "
578 "readChar readInt readLong readFloat readDouble"
497 579 ),
498 580 types: words(
499 581 "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
500 "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
582 "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
501 583 "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
502 584 "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
503 585 "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
@@ -509,11 +591,12 b' CodeMirror.defineMode("clike", function('
509 591 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
510 592 ),
511 593 multiLineStrings: true,
512 blockKeywords: words("catch class do else finally for forSome if match switch try while"),
513 defKeywords: words("class def object package trait type val var"),
594 blockKeywords: words("catch class enum do else finally for forSome if match switch try while"),
595 defKeywords: words("class enum def object package trait type val var"),
514 596 atoms: words("true false null"),
515 597 indentStatements: false,
516 598 indentSwitch: false,
599 isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
517 600 hooks: {
518 601 "@": function(stream) {
519 602 stream.eatWhile(/[\w\$_]/);
@@ -527,9 +610,24 b' CodeMirror.defineMode("clike", function('
527 610 "'": function(stream) {
528 611 stream.eatWhile(/[\w\$_\xa1-\uffff]/);
529 612 return "atom";
613 },
614 "=": function(stream, state) {
615 var cx = state.context
616 if (cx.type == "}" && cx.align && stream.eat(">")) {
617 state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
618 return "operator"
619 } else {
620 return false
621 }
622 },
623
624 "/": function(stream, state) {
625 if (!stream.eat("*")) return false
626 state.tokenize = tokenNestedComment(1)
627 return state.tokenize(stream, state)
530 628 }
531 629 },
532 modeProps: {closeBrackets: {triples: '"'}}
630 modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}}
533 631 });
534 632
535 633 function tokenKotlinString(tripleString){
@@ -553,33 +651,59 b' CodeMirror.defineMode("clike", function('
553 651 name: "clike",
554 652 keywords: words(
555 653 /*keywords*/
556 "package as typealias class interface this super val " +
557 "var fun for is in This throw return " +
654 "package as typealias class interface this super val operator " +
655 "var fun for is in This throw return annotation " +
558 656 "break continue object if else while do try when !in !is as? " +
559 657
560 658 /*soft keywords*/
561 659 "file import where by get set abstract enum open inner override private public internal " +
562 660 "protected catch finally out final vararg reified dynamic companion constructor init " +
563 661 "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
564 "external annotation crossinline const operator infix"
662 "external annotation crossinline const operator infix suspend actual expect setparam"
565 663 ),
566 664 types: words(
567 665 /* package java.lang */
568 666 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
569 667 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
570 668 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
571 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
669 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
670 "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
671 "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
572 672 ),
573 673 intendSwitch: false,
574 674 indentStatements: false,
575 675 multiLineStrings: true,
676 number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
576 677 blockKeywords: words("catch class do else finally for if where try while enum"),
577 defKeywords: words("class val var object package interface fun"),
678 defKeywords: words("class val var object interface fun"),
578 679 atoms: words("true false null this"),
579 680 hooks: {
681 "@": function(stream) {
682 stream.eatWhile(/[\w\$_]/);
683 return "meta";
684 },
685 '*': function(_stream, state) {
686 return state.prevToken == '.' ? 'variable' : 'operator';
687 },
580 688 '"': function(stream, state) {
581 689 state.tokenize = tokenKotlinString(stream.match('""'));
582 690 return state.tokenize(stream, state);
691 },
692 "/": function(stream, state) {
693 if (!stream.eat("*")) return false;
694 state.tokenize = tokenNestedComment(1);
695 return state.tokenize(stream, state)
696 },
697 indent: function(state, ctx, textAfter, indentUnit) {
698 var firstChar = textAfter && textAfter.charAt(0);
699 if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
700 return state.indented;
701 if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") ||
702 state.prevToken == "variable" && firstChar == "." ||
703 (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
704 return indentUnit * 2 + ctx.indented;
705 if (ctx.align && ctx.type == "}")
706 return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
583 707 }
584 708 },
585 709 modeProps: {closeBrackets: {triples: '"'}}
@@ -646,11 +770,11 b' CodeMirror.defineMode("clike", function('
646 770
647 771 def("text/x-nesc", {
648 772 name: "clike",
649 keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
773 keywords: words(cKeywords + " as atomic async call command component components configuration event generic " +
650 774 "implementation includes interface module new norace nx_struct nx_union post provides " +
651 775 "signal task uses abstract extends"),
652 types: words(cTypes),
653 blockKeywords: words("case do else for if switch while struct"),
776 types: cTypes,
777 blockKeywords: words(cBlockKeywords),
654 778 atoms: words("null true false"),
655 779 hooks: {"#": cppHook},
656 780 modeProps: {fold: ["brace", "include"]}
@@ -658,28 +782,67 b' CodeMirror.defineMode("clike", function('
658 782
659 783 def("text/x-objectivec", {
660 784 name: "clike",
661 keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
662 "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
663 types: words(cTypes),
664 atoms: words("YES NO NULL NILL ON OFF true false"),
785 keywords: words(cKeywords + " " + objCKeywords),
786 types: objCTypes,
787 builtin: words(objCBuiltins),
788 blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"),
789 defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"),
790 dontIndentStatements: /^@.*$/,
791 typeFirstDefinitions: true,
792 atoms: words("YES NO NULL Nil nil true false nullptr"),
793 isReservedIdentifier: cIsReservedIdentifier,
665 794 hooks: {
666 "@": function(stream) {
667 stream.eatWhile(/[\w\$]/);
668 return "keyword";
669 },
795 "#": cppHook,
796 "*": pointerHook,
797 },
798 modeProps: {fold: ["brace", "include"]}
799 });
800
801 def("text/x-objectivec++", {
802 name: "clike",
803 keywords: words(cKeywords + " " + objCKeywords + " " + cppKeywords),
804 types: objCTypes,
805 builtin: words(objCBuiltins),
806 blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized class try catch"),
807 defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class class namespace"),
808 dontIndentStatements: /^@.*$|^template$/,
809 typeFirstDefinitions: true,
810 atoms: words("YES NO NULL Nil nil true false nullptr"),
811 isReservedIdentifier: cIsReservedIdentifier,
812 hooks: {
670 813 "#": cppHook,
671 indent: function(_state, ctx, textAfter) {
672 if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
814 "*": pointerHook,
815 "u": cpp11StringHook,
816 "U": cpp11StringHook,
817 "L": cpp11StringHook,
818 "R": cpp11StringHook,
819 "0": cpp14Literal,
820 "1": cpp14Literal,
821 "2": cpp14Literal,
822 "3": cpp14Literal,
823 "4": cpp14Literal,
824 "5": cpp14Literal,
825 "6": cpp14Literal,
826 "7": cpp14Literal,
827 "8": cpp14Literal,
828 "9": cpp14Literal,
829 token: function(stream, state, style) {
830 if (style == "variable" && stream.peek() == "(" &&
831 (state.prevToken == ";" || state.prevToken == null ||
832 state.prevToken == "}") &&
833 cppLooksLikeConstructor(stream.current()))
834 return "def";
673 835 }
674 836 },
675 modeProps: {fold: "brace"}
837 namespaceSeparator: "::",
838 modeProps: {fold: ["brace", "include"]}
676 839 });
677 840
678 841 def("text/x-squirrel", {
679 842 name: "clike",
680 843 keywords: words("base break clone continue const default delete enum extends function in class" +
681 844 " foreach local resume return this throw typeof yield constructor instanceof static"),
682 types: words(cTypes),
845 types: cTypes,
683 846 blockKeywords: words("case catch class else for foreach if switch try while"),
684 847 defKeywords: words("function local class"),
685 848 typeFirstDefinitions: true,
@@ -757,7 +920,7 b' CodeMirror.defineMode("clike", function('
757 920 return "atom";
758 921 },
759 922 token: function(_stream, state, style) {
760 if ((style == "variable" || style == "variable-3") &&
923 if ((style == "variable" || style == "type") &&
761 924 state.prevToken == ".") {
762 925 return "variable-2";
763 926 }
@@ -1,15 +1,10 b''
1 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 /**
5 * Author: Hans Engel
6 * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
7 */
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
8 3
9 4 (function(mod) {
10 if (typeof exports == "object" && typeof module == "object") // CommonJS
5 if (typeof exports === "object" && typeof module === "object") // CommonJS
11 6 mod(require("../../lib/codemirror"));
12 else if (typeof define == "function" && define.amd) // AMD
7 else if (typeof define === "function" && define.amd) // AMD
13 8 define(["../../lib/codemirror"], mod);
14 9 else // Plain browser env
15 10 mod(CodeMirror);
@@ -17,233 +12,281 b''
17 12 "use strict";
18 13
19 14 CodeMirror.defineMode("clojure", function (options) {
20 var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
21 ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
22 var INDENT_WORD_SKIP = options.indentUnit || 2;
23 var NORMAL_INDENT_UNIT = options.indentUnit || 2;
24
25 function makeKeywords(str) {
26 var obj = {}, words = str.split(" ");
27 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
28 return obj;
29 }
30
31 var atoms = makeKeywords("true false nil");
32
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");
35
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->>");
38
39 var indentKeys = makeKeywords(
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 " +
42
43 // Binding forms
44 "let letfn binding loop for doseq dotimes when-let if-let " +
45
46 // Data structures
47 "defstruct struct-map assoc " +
48
49 // clojure.test
50 "testing deftest " +
51
52 // contrib
53 "handler-case handle dotrace deftrace");
54
55 var tests = {
56 digit: /\d/,
57 digit_or_colon: /[\d:]/,
58 hex: /[0-9a-f]/i,
59 sign: /[+-]/,
60 exponent: /e/i,
61 keyword_char: /[^\s\(\[\;\)\]]/,
62 symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/,
63 block_indent: /^(?:def|with)[^\/]+$|\/(?:def|with)/
64 };
65
66 function stateStack(indent, type, prev) { // represents a state stack object
67 this.indent = indent;
68 this.type = type;
69 this.prev = prev;
70 }
15 var atoms = ["false", "nil", "true"];
16 var specialForms = [".", "catch", "def", "do", "if", "monitor-enter",
17 "monitor-exit", "new", "quote", "recur", "set!", "throw", "try", "var"];
18 var coreSymbols = ["*", "*'", "*1", "*2", "*3", "*agent*",
19 "*allow-unresolved-vars*", "*assert*", "*clojure-version*",
20 "*command-line-args*", "*compile-files*", "*compile-path*",
21 "*compiler-options*", "*data-readers*", "*default-data-reader-fn*", "*e",
22 "*err*", "*file*", "*flush-on-newline*", "*fn-loader*", "*in*",
23 "*math-context*", "*ns*", "*out*", "*print-dup*", "*print-length*",
24 "*print-level*", "*print-meta*", "*print-namespace-maps*",
25 "*print-readably*", "*read-eval*", "*reader-resolver*", "*source-path*",
26 "*suppress-read*", "*unchecked-math*", "*use-context-classloader*",
27 "*verbose-defrecords*", "*warn-on-reflection*", "+", "+'", "-", "-'",
28 "->", "->>", "->ArrayChunk", "->Eduction", "->Vec", "->VecNode",
29 "->VecSeq", "-cache-protocol-fn", "-reset-methods", "..", "/", "<", "<=",
30 "=", "==", ">", ">=", "EMPTY-NODE", "Inst", "StackTraceElement->vec",
31 "Throwable->map", "accessor", "aclone", "add-classpath", "add-watch",
32 "agent", "agent-error", "agent-errors", "aget", "alength", "alias",
33 "all-ns", "alter", "alter-meta!", "alter-var-root", "amap", "ancestors",
34 "and", "any?", "apply", "areduce", "array-map", "as->", "aset",
35 "aset-boolean", "aset-byte", "aset-char", "aset-double", "aset-float",
36 "aset-int", "aset-long", "aset-short", "assert", "assoc", "assoc!",
37 "assoc-in", "associative?", "atom", "await", "await-for", "await1",
38 "bases", "bean", "bigdec", "bigint", "biginteger", "binding", "bit-and",
39 "bit-and-not", "bit-clear", "bit-flip", "bit-not", "bit-or", "bit-set",
40 "bit-shift-left", "bit-shift-right", "bit-test", "bit-xor", "boolean",
41 "boolean-array", "boolean?", "booleans", "bound-fn", "bound-fn*",
42 "bound?", "bounded-count", "butlast", "byte", "byte-array", "bytes",
43 "bytes?", "case", "cast", "cat", "char", "char-array",
44 "char-escape-string", "char-name-string", "char?", "chars", "chunk",
45 "chunk-append", "chunk-buffer", "chunk-cons", "chunk-first", "chunk-next",
46 "chunk-rest", "chunked-seq?", "class", "class?", "clear-agent-errors",
47 "clojure-version", "coll?", "comment", "commute", "comp", "comparator",
48 "compare", "compare-and-set!", "compile", "complement", "completing",
49 "concat", "cond", "cond->", "cond->>", "condp", "conj", "conj!", "cons",
50 "constantly", "construct-proxy", "contains?", "count", "counted?",
51 "create-ns", "create-struct", "cycle", "dec", "dec'", "decimal?",
52 "declare", "dedupe", "default-data-readers", "definline", "definterface",
53 "defmacro", "defmethod", "defmulti", "defn", "defn-", "defonce",
54 "defprotocol", "defrecord", "defstruct", "deftype", "delay", "delay?",
55 "deliver", "denominator", "deref", "derive", "descendants", "destructure",
56 "disj", "disj!", "dissoc", "dissoc!", "distinct", "distinct?", "doall",
57 "dorun", "doseq", "dosync", "dotimes", "doto", "double", "double-array",
58 "double?", "doubles", "drop", "drop-last", "drop-while", "eduction",
59 "empty", "empty?", "ensure", "ensure-reduced", "enumeration-seq",
60 "error-handler", "error-mode", "eval", "even?", "every-pred", "every?",
61 "ex-data", "ex-info", "extend", "extend-protocol", "extend-type",
62 "extenders", "extends?", "false?", "ffirst", "file-seq", "filter",
63 "filterv", "find", "find-keyword", "find-ns", "find-protocol-impl",
64 "find-protocol-method", "find-var", "first", "flatten", "float",
65 "float-array", "float?", "floats", "flush", "fn", "fn?", "fnext", "fnil",
66 "for", "force", "format", "frequencies", "future", "future-call",
67 "future-cancel", "future-cancelled?", "future-done?", "future?",
68 "gen-class", "gen-interface", "gensym", "get", "get-in", "get-method",
69 "get-proxy-class", "get-thread-bindings", "get-validator", "group-by",
70 "halt-when", "hash", "hash-combine", "hash-map", "hash-ordered-coll",
71 "hash-set", "hash-unordered-coll", "ident?", "identical?", "identity",
72 "if-let", "if-not", "if-some", "ifn?", "import", "in-ns", "inc", "inc'",
73 "indexed?", "init-proxy", "inst-ms", "inst-ms*", "inst?", "instance?",
74 "int", "int-array", "int?", "integer?", "interleave", "intern",
75 "interpose", "into", "into-array", "ints", "io!", "isa?", "iterate",
76 "iterator-seq", "juxt", "keep", "keep-indexed", "key", "keys", "keyword",
77 "keyword?", "last", "lazy-cat", "lazy-seq", "let", "letfn", "line-seq",
78 "list", "list*", "list?", "load", "load-file", "load-reader",
79 "load-string", "loaded-libs", "locking", "long", "long-array", "longs",
80 "loop", "macroexpand", "macroexpand-1", "make-array", "make-hierarchy",
81 "map", "map-entry?", "map-indexed", "map?", "mapcat", "mapv", "max",
82 "max-key", "memfn", "memoize", "merge", "merge-with", "meta",
83 "method-sig", "methods", "min", "min-key", "mix-collection-hash", "mod",
84 "munge", "name", "namespace", "namespace-munge", "nat-int?", "neg-int?",
85 "neg?", "newline", "next", "nfirst", "nil?", "nnext", "not", "not-any?",
86 "not-empty", "not-every?", "not=", "ns", "ns-aliases", "ns-imports",
87 "ns-interns", "ns-map", "ns-name", "ns-publics", "ns-refers",
88 "ns-resolve", "ns-unalias", "ns-unmap", "nth", "nthnext", "nthrest",
89 "num", "number?", "numerator", "object-array", "odd?", "or", "parents",
90 "partial", "partition", "partition-all", "partition-by", "pcalls", "peek",
91 "persistent!", "pmap", "pop", "pop!", "pop-thread-bindings", "pos-int?",
92 "pos?", "pr", "pr-str", "prefer-method", "prefers",
93 "primitives-classnames", "print", "print-ctor", "print-dup",
94 "print-method", "print-simple", "print-str", "printf", "println",
95 "println-str", "prn", "prn-str", "promise", "proxy",
96 "proxy-call-with-super", "proxy-mappings", "proxy-name", "proxy-super",
97 "push-thread-bindings", "pvalues", "qualified-ident?",
98 "qualified-keyword?", "qualified-symbol?", "quot", "rand", "rand-int",
99 "rand-nth", "random-sample", "range", "ratio?", "rational?",
100 "rationalize", "re-find", "re-groups", "re-matcher", "re-matches",
101 "re-pattern", "re-seq", "read", "read-line", "read-string",
102 "reader-conditional", "reader-conditional?", "realized?", "record?",
103 "reduce", "reduce-kv", "reduced", "reduced?", "reductions", "ref",
104 "ref-history-count", "ref-max-history", "ref-min-history", "ref-set",
105 "refer", "refer-clojure", "reify", "release-pending-sends", "rem",
106 "remove", "remove-all-methods", "remove-method", "remove-ns",
107 "remove-watch", "repeat", "repeatedly", "replace", "replicate", "require",
108 "reset!", "reset-meta!", "reset-vals!", "resolve", "rest",
109 "restart-agent", "resultset-seq", "reverse", "reversible?", "rseq",
110 "rsubseq", "run!", "satisfies?", "second", "select-keys", "send",
111 "send-off", "send-via", "seq", "seq?", "seqable?", "seque", "sequence",
112 "sequential?", "set", "set-agent-send-executor!",
113 "set-agent-send-off-executor!", "set-error-handler!", "set-error-mode!",
114 "set-validator!", "set?", "short", "short-array", "shorts", "shuffle",
115 "shutdown-agents", "simple-ident?", "simple-keyword?", "simple-symbol?",
116 "slurp", "some", "some->", "some->>", "some-fn", "some?", "sort",
117 "sort-by", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by",
118 "sorted?", "special-symbol?", "spit", "split-at", "split-with", "str",
119 "string?", "struct", "struct-map", "subs", "subseq", "subvec", "supers",
120 "swap!", "swap-vals!", "symbol", "symbol?", "sync", "tagged-literal",
121 "tagged-literal?", "take", "take-last", "take-nth", "take-while", "test",
122 "the-ns", "thread-bound?", "time", "to-array", "to-array-2d",
123 "trampoline", "transduce", "transient", "tree-seq", "true?", "type",
124 "unchecked-add", "unchecked-add-int", "unchecked-byte", "unchecked-char",
125 "unchecked-dec", "unchecked-dec-int", "unchecked-divide-int",
126 "unchecked-double", "unchecked-float", "unchecked-inc",
127 "unchecked-inc-int", "unchecked-int", "unchecked-long",
128 "unchecked-multiply", "unchecked-multiply-int", "unchecked-negate",
129 "unchecked-negate-int", "unchecked-remainder-int", "unchecked-short",
130 "unchecked-subtract", "unchecked-subtract-int", "underive", "unquote",
131 "unquote-splicing", "unreduced", "unsigned-bit-shift-right", "update",
132 "update-in", "update-proxy", "uri?", "use", "uuid?", "val", "vals",
133 "var-get", "var-set", "var?", "vary-meta", "vec", "vector", "vector-of",
134 "vector?", "volatile!", "volatile?", "vreset!", "vswap!", "when",
135 "when-first", "when-let", "when-not", "when-some", "while",
136 "with-bindings", "with-bindings*", "with-in-str", "with-loading-context",
137 "with-local-vars", "with-meta", "with-open", "with-out-str",
138 "with-precision", "with-redefs", "with-redefs-fn", "xml-seq", "zero?",
139 "zipmap"];
140 var haveBodyParameter = [
141 "->", "->>", "as->", "binding", "bound-fn", "case", "catch", "comment",
142 "cond", "cond->", "cond->>", "condp", "def", "definterface", "defmethod",
143 "defn", "defmacro", "defprotocol", "defrecord", "defstruct", "deftype",
144 "do", "doseq", "dotimes", "doto", "extend", "extend-protocol",
145 "extend-type", "fn", "for", "future", "if", "if-let", "if-not", "if-some",
146 "let", "letfn", "locking", "loop", "ns", "proxy", "reify", "struct-map",
147 "some->", "some->>", "try", "when", "when-first", "when-let", "when-not",
148 "when-some", "while", "with-bindings", "with-bindings*", "with-in-str",
149 "with-loading-context", "with-local-vars", "with-meta", "with-open",
150 "with-out-str", "with-precision", "with-redefs", "with-redefs-fn"];
71 151
72 function pushStack(state, indent, type) {
73 state.indentStack = new stateStack(indent, type, state.indentStack);
74 }
75
76 function popStack(state) {
77 state.indentStack = state.indentStack.prev;
78 }
152 CodeMirror.registerHelper("hintWords", "clojure",
153 [].concat(atoms, specialForms, coreSymbols));
79 154
80 function isNumber(ch, stream){
81 // hex
82 if ( ch === '0' && stream.eat(/x/i) ) {
83 stream.eatWhile(tests.hex);
84 return true;
85 }
155 var atom = createLookupMap(atoms);
156 var specialForm = createLookupMap(specialForms);
157 var coreSymbol = createLookupMap(coreSymbols);
158 var hasBodyParameter = createLookupMap(haveBodyParameter);
159 var delimiter = /^(?:[\\\[\]\s"(),;@^`{}~]|$)/;
160 var numberLiteral = /^(?:[+\-]?\d+(?:(?:N|(?:[eE][+\-]?\d+))|(?:\.?\d*(?:M|(?:[eE][+\-]?\d+))?)|\/\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\[\]\s"#'(),;@^`{}~]|$))/;
161 var characterLiteral = /^(?:\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\[\]\s"(),;@^`{}~]|$))/;
86 162
87 // leading sign
88 if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {
89 stream.eat(tests.sign);
90 ch = stream.next();
91 }
92
93 if ( tests.digit.test(ch) ) {
94 stream.eat(ch);
95 stream.eatWhile(tests.digit);
163 // simple-namespace := /^[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*/
164 // simple-symbol := /^(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)/
165 // qualified-symbol := (<simple-namespace>(<.><simple-namespace>)*</>)?<simple-symbol>
166 var qualifiedSymbol = /^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/;
96 167
97 if ( '.' == stream.peek() ) {
98 stream.eat('.');
99 stream.eatWhile(tests.digit);
100 } else if ('/' == stream.peek() ) {
101 stream.eat('/');
102 stream.eatWhile(tests.digit);
103 }
104
105 if ( stream.eat(tests.exponent) ) {
106 stream.eat(tests.sign);
107 stream.eatWhile(tests.digit);
108 }
109
110 return true;
111 }
168 function base(stream, state) {
169 if (stream.eatSpace() || stream.eat(",")) return ["space", null];
170 if (stream.match(numberLiteral)) return [null, "number"];
171 if (stream.match(characterLiteral)) return [null, "string-2"];
172 if (stream.eat(/^"/)) return (state.tokenize = inString)(stream, state);
173 if (stream.eat(/^[(\[{]/)) return ["open", "bracket"];
174 if (stream.eat(/^[)\]}]/)) return ["close", "bracket"];
175 if (stream.eat(/^;/)) {stream.skipToEnd(); return ["space", "comment"];}
176 if (stream.eat(/^[#'@^`~]/)) return [null, "meta"];
112 177
113 return false;
114 }
178 var matches = stream.match(qualifiedSymbol);
179 var symbol = matches && matches[0];
115 180
116 // Eat character that starts after backslash \
117 function eatCharacter(stream) {
118 var first = stream.next();
119 // Read special literals: backspace, newline, space, return.
120 // Just read all lowercase letters.
121 if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
122 return;
123 }
124 // Read unicode character: \u1000 \uA0a1
125 if (first === "u") {
126 stream.match(/[0-9a-z]{4}/i, true);
127 }
181 if (!symbol) {
182 // advance stream by at least one character so we don't get stuck.
183 stream.next();
184 stream.eatWhile(function (c) {return !is(c, delimiter);});
185 return [null, "error"];
128 186 }
129 187
130 return {
131 startState: function () {
132 return {
133 indentStack: null,
134 indentation: 0,
135 mode: false
136 };
137 },
188 if (symbol === "comment" && state.lastToken === "(")
189 return (state.tokenize = inComment)(stream, state);
190 if (is(symbol, atom) || symbol.charAt(0) === ":") return ["symbol", "atom"];
191 if (is(symbol, specialForm) || is(symbol, coreSymbol)) return ["symbol", "keyword"];
192 if (state.lastToken === "(") return ["symbol", "builtin"]; // other operator
193
194 return ["symbol", "variable"];
195 }
138 196
139 token: function (stream, state) {
140 if (state.indentStack == null && stream.sol()) {
141 // update indentation, but only if indentStack is empty
142 state.indentation = stream.indentation();
143 }
197 function inString(stream, state) {
198 var escaped = false, next;
199
200 while (next = stream.next()) {
201 if (next === "\"" && !escaped) {state.tokenize = base; break;}
202 escaped = !escaped && next === "\\";
203 }
204
205 return [null, "string"];
206 }
144 207
145 // skip spaces
146 if (state.mode != "string" && stream.eatSpace()) {
147 return null;
148 }
149 var returnType = null;
208 function inComment(stream, state) {
209 var parenthesisCount = 1;
210 var next;
150 211
151 switch(state.mode){
152 case "string": // multi-line string parsing mode
153 var next, escaped = false;
154 while ((next = stream.next()) != null) {
155 if (next == "\"" && !escaped) {
212 while (next = stream.next()) {
213 if (next === ")") parenthesisCount--;
214 if (next === "(") parenthesisCount++;
215 if (parenthesisCount === 0) {
216 stream.backUp(1);
217 state.tokenize = base;
218 break;
219 }
220 }
156 221
157 state.mode = false;
158 break;
159 }
160 escaped = !escaped && next == "\\";
161 }
162 returnType = STRING; // continue on in string mode
163 break;
164 default: // default parsing mode
165 var ch = stream.next();
222 return ["space", "comment"];
223 }
224
225 function createLookupMap(words) {
226 var obj = {};
227
228 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
229
230 return obj;
231 }
166 232
167 if (ch == "\"") {
168 state.mode = "string";
169 returnType = STRING;
170 } else if (ch == "\\") {
171 eatCharacter(stream);
172 returnType = CHARACTER;
173 } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
174 returnType = ATOM;
175 } else if (ch == ";") { // comment
176 stream.skipToEnd(); // rest of the line is a comment
177 returnType = COMMENT;
178 } else if (isNumber(ch,stream)){
179 returnType = NUMBER;
180 } else if (ch == "(" || ch == "[" || ch == "{" ) {
181 var keyWord = '', indentTemp = stream.column(), letter;
182 /**
183 Either
184 (indent-word ..
185 (non-indent-word ..
186 (;something else, bracket, etc.
187 */
233 function is(value, test) {
234 if (test instanceof RegExp) return test.test(value);
235 if (test instanceof Object) return test.propertyIsEnumerable(value);
236 }
188 237
189 if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) {
190 keyWord += letter;
191 }
238 return {
239 startState: function () {
240 return {
241 ctx: {prev: null, start: 0, indentTo: 0},
242 lastToken: null,
243 tokenize: base
244 };
245 },
246
247 token: function (stream, state) {
248 if (stream.sol() && (typeof state.ctx.indentTo !== "number"))
249 state.ctx.indentTo = state.ctx.start + 1;
250
251 var typeStylePair = state.tokenize(stream, state);
252 var type = typeStylePair[0];
253 var style = typeStylePair[1];
254 var current = stream.current();
192 255
193 if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) ||
194 tests.block_indent.test(keyWord))) { // indent-word
195 pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
196 } else { // non-indent word
197 // we continue eating the spaces
198 stream.eatSpace();
199 if (stream.eol() || stream.peek() == ";") {
200 // nothing significant after
201 // we restart indentation the user defined spaces after
202 pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
203 } else {
204 pushStack(state, indentTemp + stream.current().length, ch); // else we match
205 }
206 }
207 stream.backUp(stream.current().length - 1); // undo all the eating
256 if (type !== "space") {
257 if (state.lastToken === "(" && state.ctx.indentTo === null) {
258 if (type === "symbol" && is(current, hasBodyParameter))
259 state.ctx.indentTo = state.ctx.start + options.indentUnit;
260 else state.ctx.indentTo = "next";
261 } else if (state.ctx.indentTo === "next") {
262 state.ctx.indentTo = stream.column();
263 }
264
265 state.lastToken = current;
266 }
208 267
209 returnType = BRACKET;
210 } else if (ch == ")" || ch == "]" || ch == "}") {
211 returnType = BRACKET;
212 if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) {
213 popStack(state);
214 }
215 } else if ( ch == ":" ) {
216 stream.eatWhile(tests.symbol);
217 return ATOM;
218 } else {
219 stream.eatWhile(tests.symbol);
268 if (type === "open")
269 state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null};
270 else if (type === "close") state.ctx = state.ctx.prev || state.ctx;
271
272 return style;
273 },
220 274
221 if (keywords && keywords.propertyIsEnumerable(stream.current())) {
222 returnType = KEYWORD;
223 } else if (builtins && builtins.propertyIsEnumerable(stream.current())) {
224 returnType = BUILTIN;
225 } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
226 returnType = ATOM;
227 } else {
228 returnType = VAR;
229 }
230 }
231 }
275 indent: function (state) {
276 var i = state.ctx.indentTo;
232 277
233 return returnType;
234 },
278 return (typeof i === "number") ?
279 i :
280 state.ctx.start + 1;
281 },
235 282
236 indent: function (state) {
237 if (state.indentStack == null) return state.indentation;
238 return state.indentStack.indent;
239 },
240
241 closeBrackets: {pairs: "()[]{}\"\""},
242 lineComment: ";;"
243 };
283 closeBrackets: {pairs: "()[]{}\"\""},
284 lineComment: ";;"
285 };
244 286 });
245 287
246 288 CodeMirror.defineMIME("text/x-clojure", "clojure");
247 289 CodeMirror.defineMIME("text/x-clojurescript", "clojure");
290 CodeMirror.defineMIME("application/edn", "clojure");
248 291
249 292 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object")
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**
5 5 * Author: Gautam Mehta
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**
5 5 * Link to the project's GitHub page:
@@ -349,6 +349,10 b' CodeMirror.defineMode("coffeescript", fu'
349 349 return external;
350 350 });
351 351
352 // IANA registered media type
353 // https://www.iana.org/assignments/media-types/
354 CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
355
352 356 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
353 357 CodeMirror.defineMIME("text/coffeescript", "coffeescript");
354 358
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -43,11 +43,12 b' CodeMirror.defineMode("commonlisp", func'
43 43 else { stream.skipToEnd(); return "error"; }
44 44 } else if (ch == "#") {
45 45 var ch = stream.next();
46 if (ch == "[") { type = "open"; return "bracket"; }
46 if (ch == "(") { type = "open"; return "bracket"; }
47 47 else if (/[+\-=\.']/.test(ch)) return null;
48 48 else if (/\d/.test(ch) && stream.match(/^\d*#/)) return null;
49 49 else if (ch == "|") return (state.tokenize = inComment)(stream, state);
50 50 else if (ch == ":") { readSym(stream); return "meta"; }
51 else if (ch == "\\") { stream.next(); readSym(stream); return "string-2" }
51 52 else return "error";
52 53 } else {
53 54 var name = readSym(stream);
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -29,26 +29,22 b''
29 29 var types = /^[A-Z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/;
30 30 var keywords = wordRegExp([
31 31 "abstract", "alias", "as", "asm", "begin", "break", "case", "class", "def", "do",
32 "else", "elsif", "end", "ensure", "enum", "extend", "for", "fun", "if", "ifdef",
32 "else", "elsif", "end", "ensure", "enum", "extend", "for", "fun", "if",
33 33 "include", "instance_sizeof", "lib", "macro", "module", "next", "of", "out", "pointerof",
34 "private", "protected", "rescue", "return", "require", "sizeof", "struct",
35 "super", "then", "type", "typeof", "union", "unless", "until", "when", "while", "with",
36 "yield", "__DIR__", "__FILE__", "__LINE__"
34 "private", "protected", "rescue", "return", "require", "select", "sizeof", "struct",
35 "super", "then", "type", "typeof", "uninitialized", "union", "unless", "until", "when", "while", "with",
36 "yield", "__DIR__", "__END_LINE__", "__FILE__", "__LINE__"
37 37 ]);
38 38 var atomWords = wordRegExp(["true", "false", "nil", "self"]);
39 39 var indentKeywordsArray = [
40 40 "def", "fun", "macro",
41 41 "class", "module", "struct", "lib", "enum", "union",
42 "if", "unless", "case", "while", "until", "begin", "then",
43 "do",
44 "for", "ifdef"
42 "do", "for"
45 43 ];
46 44 var indentKeywords = wordRegExp(indentKeywordsArray);
47 var dedentKeywordsArray = [
48 "end",
49 "else", "elsif",
50 "rescue", "ensure"
51 ];
45 var indentExpressionKeywordsArray = ["if", "unless", "case", "while", "until", "begin", "then"];
46 var indentExpressionKeywords = wordRegExp(indentExpressionKeywordsArray);
47 var dedentKeywordsArray = ["end", "else", "elsif", "rescue", "ensure"];
52 48 var dedentKeywords = wordRegExp(dedentKeywordsArray);
53 49 var dedentPunctualsArray = ["\\)", "\\}", "\\]"];
54 50 var dedentPunctuals = new RegExp("^(?:" + dedentPunctualsArray.join("|") + ")$");
@@ -90,12 +86,15 b''
90 86 } else if (state.lastToken == ".") {
91 87 return "property";
92 88 } else if (keywords.test(matched)) {
93 if (state.lastToken != "abstract" && indentKeywords.test(matched)) {
94 if (!(matched == "fun" && state.blocks.indexOf("lib") >= 0)) {
89 if (indentKeywords.test(matched)) {
90 if (!(matched == "fun" && state.blocks.indexOf("lib") >= 0) && !(matched == "def" && state.lastToken == "abstract")) {
95 91 state.blocks.push(matched);
96 92 state.currentIndent += 1;
97 93 }
98 } else if (dedentKeywords.test(matched)) {
94 } else if ((state.lastStyle == "operator" || !state.lastStyle) && indentExpressionKeywords.test(matched)) {
95 state.blocks.push(matched);
96 state.currentIndent += 1;
97 } else if (matched == "end") {
99 98 state.blocks.pop();
100 99 state.currentIndent -= 1;
101 100 }
@@ -124,12 +123,6 b''
124 123 return "variable-2";
125 124 }
126 125
127 // Global variables
128 if (stream.eat("$")) {
129 stream.eat(/[0-9]+|\?/) || stream.match(idents) || stream.match(types);
130 return "variable-3";
131 }
132
133 126 // Constants and types
134 127 if (stream.match(types)) {
135 128 return "tag";
@@ -165,6 +158,9 b''
165 158 } else if (stream.match("%w")) {
166 159 embed = false;
167 160 delim = stream.next();
161 } else if (stream.match("%q")) {
162 embed = false;
163 delim = stream.next();
168 164 } else {
169 165 if(delim = stream.match(/^%([^\w\s=])/)) {
170 166 delim = delim[1];
@@ -183,6 +179,11 b''
183 179 return chain(tokenQuote(delim, style, embed), stream, state);
184 180 }
185 181
182 // Here Docs
183 if (matched = stream.match(/^<<-('?)([A-Z]\w*)\1/)) {
184 return chain(tokenHereDoc(matched[2], !matched[1]), stream, state)
185 }
186
186 187 // Characters
187 188 if (stream.eat("'")) {
188 189 stream.match(/^(?:[^']|\\(?:[befnrtv0'"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]{1,6}\})))/);
@@ -202,14 +203,14 b''
202 203 return "number";
203 204 }
204 205
205 if (stream.eat(/\d/)) {
206 if (stream.eat(/^\d/)) {
206 207 stream.match(/^\d*(?:\.\d+)?(?:[eE][+-]?\d+)?/);
207 208 return "number";
208 209 }
209 210
210 211 // Operators
211 212 if (stream.match(operators)) {
212 stream.eat("="); // Operators can follow assigin symbol.
213 stream.eat("="); // Operators can follow assign symbol.
213 214 return "operator";
214 215 }
215 216
@@ -339,7 +340,7 b''
339 340 return style;
340 341 }
341 342
342 escaped = ch == "\\";
343 escaped = embed && ch == "\\";
343 344 } else {
344 345 stream.next();
345 346 escaped = false;
@@ -350,12 +351,52 b''
350 351 };
351 352 }
352 353
354 function tokenHereDoc(phrase, embed) {
355 return function (stream, state) {
356 if (stream.sol()) {
357 stream.eatSpace()
358 if (stream.match(phrase)) {
359 state.tokenize.pop();
360 return "string";
361 }
362 }
363
364 var escaped = false;
365 while (stream.peek()) {
366 if (!escaped) {
367 if (stream.match("{%", false)) {
368 state.tokenize.push(tokenMacro("%", "%"));
369 return "string";
370 }
371
372 if (stream.match("{{", false)) {
373 state.tokenize.push(tokenMacro("{", "}"));
374 return "string";
375 }
376
377 if (embed && stream.match("#{", false)) {
378 state.tokenize.push(tokenNest("#{", "}", "meta"));
379 return "string";
380 }
381
382 escaped = embed && stream.next() == "\\";
383 } else {
384 stream.next();
385 escaped = false;
386 }
387 }
388
389 return "string";
390 }
391 }
392
353 393 return {
354 394 startState: function () {
355 395 return {
356 396 tokenize: [tokenBase],
357 397 currentIndent: 0,
358 398 lastToken: null,
399 lastStyle: null,
359 400 blocks: []
360 401 };
361 402 },
@@ -366,6 +407,7 b''
366 407
367 408 if (style && style != "comment") {
368 409 state.lastToken = token;
410 state.lastStyle = style;
369 411 }
370 412
371 413 return style;
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -28,6 +28,7 b' CodeMirror.defineMode("css", function(co'
28 28 colorKeywords = parserConfig.colorKeywords || {},
29 29 valueKeywords = parserConfig.valueKeywords || {},
30 30 allowNested = parserConfig.allowNested,
31 lineComment = parserConfig.lineComment,
31 32 supportsAtComponent = parserConfig.supportsAtComponent === true;
32 33
33 34 var type, override;
@@ -62,7 +63,7 b' CodeMirror.defineMode("css", function(co'
62 63 if (/[\d.]/.test(stream.peek())) {
63 64 stream.eatWhile(/[\w.%]/);
64 65 return ret("number", "unit");
65 } else if (stream.match(/^-[\w\\\-]+/)) {
66 } else if (stream.match(/^-[\w\\\-]*/)) {
66 67 stream.eatWhile(/[\w\\\-]/);
67 68 if (stream.match(/^\s*:/, false))
68 69 return ret("variable-2", "variable-definition");
@@ -76,12 +77,11 b' CodeMirror.defineMode("css", function(co'
76 77 return ret("qualifier", "qualifier");
77 78 } else if (/[:;{}\[\]\(\)]/.test(ch)) {
78 79 return ret(null, ch);
79 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
80 (ch == "d" && stream.match("omain(")) ||
81 (ch == "r" && stream.match("egexp("))) {
82 stream.backUp(1);
83 state.tokenize = tokenParenthesized;
84 return ret("property", "word");
80 } else if (stream.match(/[\w-.]+(?=\()/)) {
81 if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
82 state.tokenize = tokenParenthesized;
83 }
84 return ret("variable callee", "variable");
85 85 } else if (/[\w\\\-]/.test(ch)) {
86 86 stream.eatWhile(/[\w\\\-]/);
87 87 return ret("property", "word");
@@ -161,16 +161,16 b' CodeMirror.defineMode("css", function(co'
161 161 return pushContext(state, stream, "block");
162 162 } else if (type == "}" && state.context.prev) {
163 163 return popContext(state);
164 } else if (supportsAtComponent && /@component/.test(type)) {
164 } else if (supportsAtComponent && /@component/i.test(type)) {
165 165 return pushContext(state, stream, "atComponentBlock");
166 } else if (/^@(-moz-)?document$/.test(type)) {
166 } else if (/^@(-moz-)?document$/i.test(type)) {
167 167 return pushContext(state, stream, "documentTypes");
168 } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
168 } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
169 169 return pushContext(state, stream, "atBlock");
170 } else if (/^@(font-face|counter-style)/.test(type)) {
170 } else if (/^@(font-face|counter-style)/i.test(type)) {
171 171 state.stateArg = type;
172 172 return "restricted_atBlock_before";
173 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
173 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
174 174 return "keyframes";
175 175 } else if (type && type.charAt(0) == "@") {
176 176 return pushContext(state, stream, "at");
@@ -253,6 +253,8 b' CodeMirror.defineMode("css", function(co'
253 253 };
254 254
255 255 states.pseudo = function(type, stream, state) {
256 if (type == "meta") return "pseudo";
257
256 258 if (type == "word") {
257 259 override = "variable-3";
258 260 return state.context.type;
@@ -380,7 +382,8 b' CodeMirror.defineMode("css", function(co'
380 382 style = style[0];
381 383 }
382 384 override = style;
383 state.state = states[state.state](type, stream, state);
385 if (type != "comment")
386 state.state = states[state.state](type, stream, state);
384 387 return override;
385 388 },
386 389
@@ -398,7 +401,6 b' CodeMirror.defineMode("css", function(co'
398 401 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
399 402 // Dedent relative to current context.
400 403 indent = Math.max(0, cx.indent - indentUnit);
401 cx = cx.prev;
402 404 }
403 405 }
404 406 return indent;
@@ -407,6 +409,8 b' CodeMirror.defineMode("css", function(co'
407 409 electricChars: "}",
408 410 blockCommentStart: "/*",
409 411 blockCommentEnd: "*/",
412 blockCommentContinue: " * ",
413 lineComment: lineComment,
410 414 fold: "brace"
411 415 };
412 416 });
@@ -414,7 +418,7 b' CodeMirror.defineMode("css", function(co'
414 418 function keySet(array) {
415 419 var keys = {};
416 420 for (var i = 0; i < array.length; ++i) {
417 keys[array[i]] = true;
421 keys[array[i].toLowerCase()] = true;
418 422 }
419 423 return keys;
420 424 }
@@ -468,7 +472,7 b' CodeMirror.defineMode("css", function(co'
468 472 "border-top-left-radius", "border-top-right-radius", "border-top-style",
469 473 "border-top-width", "border-width", "bottom", "box-decoration-break",
470 474 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
471 "caption-side", "clear", "clip", "color", "color-profile", "column-count",
475 "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
472 476 "column-fill", "column-gap", "column-rule", "column-rule-color",
473 477 "column-rule-style", "column-rule-width", "column-span", "column-width",
474 478 "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
@@ -484,19 +488,19 b' CodeMirror.defineMode("css", function(co'
484 488 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
485 489 "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
486 490 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
487 "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
488 "grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
489 "grid-template", "grid-template-areas", "grid-template-columns",
491 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
492 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
493 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
490 494 "grid-template-rows", "hanging-punctuation", "height", "hyphens",
491 495 "icon", "image-orientation", "image-rendering", "image-resolution",
492 "inline-box-align", "justify-content", "left", "letter-spacing",
496 "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
493 497 "line-break", "line-height", "line-stacking", "line-stacking-ruby",
494 498 "line-stacking-shift", "line-stacking-strategy", "list-style",
495 499 "list-style-image", "list-style-position", "list-style-type", "margin",
496 500 "margin-bottom", "margin-left", "margin-right", "margin-top",
497 "marker-offset", "marks", "marquee-direction", "marquee-loop",
501 "marks", "marquee-direction", "marquee-loop",
498 502 "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
499 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
503 "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
500 504 "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
501 505 "opacity", "order", "orphans", "outline",
502 506 "outline-color", "outline-offset", "outline-style", "outline-width",
@@ -504,7 +508,7 b' CodeMirror.defineMode("css", function(co'
504 508 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
505 509 "page", "page-break-after", "page-break-before", "page-break-inside",
506 510 "page-policy", "pause", "pause-after", "pause-before", "perspective",
507 "perspective-origin", "pitch", "pitch-range", "play-during", "position",
511 "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
508 512 "presentation-level", "punctuation-trim", "quotes", "region-break-after",
509 513 "region-break-before", "region-break-inside", "region-fragment",
510 514 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
@@ -522,9 +526,9 b' CodeMirror.defineMode("css", function(co'
522 526 "text-wrap", "top", "transform", "transform-origin", "transform-style",
523 527 "transition", "transition-delay", "transition-duration",
524 528 "transition-property", "transition-timing-function", "unicode-bidi",
525 "vertical-align", "visibility", "voice-balance", "voice-duration",
529 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
526 530 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
527 "voice-volume", "volume", "white-space", "widows", "width", "word-break",
531 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
528 532 "word-spacing", "word-wrap", "z-index",
529 533 // SVG-specific
530 534 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
@@ -589,7 +593,7 b' CodeMirror.defineMode("css", function(co'
589 593 "above", "absolute", "activeborder", "additive", "activecaption", "afar",
590 594 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
591 595 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
592 "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
596 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
593 597 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
594 598 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
595 599 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
@@ -598,10 +602,10 b' CodeMirror.defineMode("css", function(co'
598 602 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
599 603 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
600 604 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
601 "compact", "condensed", "contain", "content",
605 "compact", "condensed", "contain", "content", "contents",
602 606 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
603 607 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
604 "decimal-leading-zero", "default", "default-button", "destination-atop",
608 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
605 609 "destination-in", "destination-out", "destination-over", "devanagari", "difference",
606 610 "disc", "discard", "disclosure-closed", "disclosure-open", "document",
607 611 "dot-dash", "dot-dot-dash",
@@ -615,13 +619,13 b' CodeMirror.defineMode("css", function(co'
615 619 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
616 620 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
617 621 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
618 "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
622 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
619 623 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
620 624 "help", "hidden", "hide", "higher", "highlight", "highlighttext",
621 625 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
622 626 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
623 627 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
624 "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
628 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
625 629 "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
626 630 "katakana", "katakana-iroha", "keep-all", "khmer",
627 631 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
@@ -641,7 +645,7 b' CodeMirror.defineMode("css", function(co'
641 645 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
642 646 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
643 647 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
644 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
648 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
645 649 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
646 650 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
647 651 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
@@ -653,17 +657,17 b' CodeMirror.defineMode("css", function(co'
653 657 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
654 658 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
655 659 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
656 "scroll", "scrollbar", "se-resize", "searchfield",
660 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
657 661 "searchfield-cancel-button", "searchfield-decoration",
658 "searchfield-results-button", "searchfield-results-decoration",
662 "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
659 663 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
660 664 "simp-chinese-formal", "simp-chinese-informal", "single",
661 665 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
662 666 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
663 667 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
664 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
668 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
665 669 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
666 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
670 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
667 671 "table-caption", "table-cell", "table-column", "table-column-group",
668 672 "table-footer-group", "table-header-group", "table-row", "table-row-group",
669 673 "tamil",
@@ -671,9 +675,9 b' CodeMirror.defineMode("css", function(co'
671 675 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
672 676 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
673 677 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
674 "trad-chinese-formal", "trad-chinese-informal",
678 "trad-chinese-formal", "trad-chinese-informal", "transform",
675 679 "translate", "translate3d", "translateX", "translateY", "translateZ",
676 "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
680 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
677 681 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
678 682 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
679 683 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
@@ -730,6 +734,7 b' CodeMirror.defineMode("css", function(co'
730 734 valueKeywords: valueKeywords,
731 735 fontProperties: fontProperties,
732 736 allowNested: true,
737 lineComment: "//",
733 738 tokenHooks: {
734 739 "/": function(stream, state) {
735 740 if (stream.eat("/")) {
@@ -743,8 +748,8 b' CodeMirror.defineMode("css", function(co'
743 748 }
744 749 },
745 750 ":": function(stream) {
746 if (stream.match(/\s*\{/))
747 return [null, "{"];
751 if (stream.match(/\s*\{/, false))
752 return [null, null]
748 753 return false;
749 754 },
750 755 "$": function(stream) {
@@ -772,6 +777,7 b' CodeMirror.defineMode("css", function(co'
772 777 valueKeywords: valueKeywords,
773 778 fontProperties: fontProperties,
774 779 allowNested: true,
780 lineComment: "//",
775 781 tokenHooks: {
776 782 "/": function(stream, state) {
777 783 if (stream.eat("/")) {
@@ -786,7 +792,7 b' CodeMirror.defineMode("css", function(co'
786 792 },
787 793 "@": function(stream) {
788 794 if (stream.eat("{")) return [null, "interpolation"];
789 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
795 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
790 796 stream.eatWhile(/[\w\\\-]/);
791 797 if (stream.match(/^\s*:/, false))
792 798 return ["variable-2", "variable-definition"];
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // By the Neo4j Team and contributors.
5 5 // https://github.com/neo4j-contrib/CodeMirror
@@ -20,8 +20,12 b''
20 20 CodeMirror.defineMode("cypher", function(config) {
21 21 var tokenBase = function(stream/*, state*/) {
22 22 var ch = stream.next();
23 if (ch === "\"" || ch === "'") {
24 stream.match(/.+?["']/);
23 if (ch ==='"') {
24 stream.match(/.*?"/);
25 return "string";
26 }
27 if (ch === "'") {
28 stream.match(/.*?'/);
25 29 return "string";
26 30 }
27 31 if (/[{}\(\),\.;\[\]]/.test(ch)) {
@@ -62,7 +66,7 b''
62 66 var curPunc;
63 67 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 68 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", "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"]);
69 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", "call", "yield"]);
66 70 var operatorChars = /[*+\-<>=&|~%^]/;
67 71
68 72 return {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -44,7 +44,7 b' CodeMirror.defineMode("d", function(conf'
44 44 }
45 45 if (ch == "/") {
46 46 if (stream.eat("+")) {
47 state.tokenize = tokenComment;
47 state.tokenize = tokenNestedComment;
48 48 return tokenNestedComment(stream, state);
49 49 }
50 50 if (stream.eat("*")) {
@@ -182,7 +182,12 b' CodeMirror.defineMode("d", function(conf'
182 182 else return ctx.indented + (closing ? 0 : indentUnit);
183 183 },
184 184
185 electricChars: "{}"
185 electricChars: "{}",
186 blockCommentStart: "/*",
187 blockCommentEnd: "*/",
188 blockCommentContinue: " * ",
189 lineComment: "//",
190 fold: "brace"
186 191 };
187 192 });
188 193
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -12,10 +12,10 b''
12 12 "use strict";
13 13
14 14 var keywords = ("this super static final const abstract class extends external factory " +
15 "implements get native operator set typedef with enum throw rethrow " +
16 "assert break case continue default in return new deferred async await " +
15 "implements mixin get native set typedef with enum throw rethrow " +
16 "assert break case continue default in return new deferred async await covariant " +
17 17 "try catch finally do else for if switch while import library export " +
18 "part of show hide is as").split(" ");
18 "part of show hide is as extension on").split(" ");
19 19 var blockKeywords = "try catch finally do else for if switch while".split(" ");
20 20 var atoms = "true false null".split(" ");
21 21 var builtins = "void bool num int double dynamic var String".split(" ");
@@ -72,6 +72,21 b''
72 72 return null;
73 73 }
74 74 return false;
75 },
76
77 "/": function(stream, state) {
78 if (!stream.eat("*")) return false
79 state.tokenize = tokenNestedComment(1)
80 return state.tokenize(stream, state)
81 },
82 token: function(stream, _, style) {
83 if (style == "variable") {
84 // Assume uppercase symbols are classes using variable-2
85 var isUpper = RegExp('^[_$]*[A-Z][a-zA-Z0-9_$]*$','g');
86 if (isUpper.test(stream.current())) {
87 return 'variable-2';
88 }
89 }
75 90 }
76 91 }
77 92 });
@@ -121,6 +136,27 b''
121 136 return "variable";
122 137 }
123 138
139 function tokenNestedComment(depth) {
140 return function (stream, state) {
141 var ch
142 while (ch = stream.next()) {
143 if (ch == "*" && stream.eat("/")) {
144 if (depth == 1) {
145 state.tokenize = null
146 break
147 } else {
148 state.tokenize = tokenNestedComment(depth - 1)
149 return state.tokenize(stream, state)
150 }
151 } else if (ch == "/" && stream.eat("*")) {
152 state.tokenize = tokenNestedComment(depth + 1)
153 return state.tokenize(stream, state)
154 }
155 }
156 return "comment"
157 }
158 }
159
124 160 CodeMirror.registerHelper("hintWords", "application/dart", keywords.concat(atoms).concat(builtins));
125 161
126 162 // This is needed to make loading through meta.js work.
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -66,11 +66,11 b''
66 66 }
67 67
68 68 // A string can be included in either single or double quotes (this is
69 // the delimeter). Mark everything as a string until the start delimeter
69 // the delimiter). Mark everything as a string until the start delimiter
70 70 // occurs again.
71 function inString (delimeter, previousTokenizer) {
71 function inString (delimiter, previousTokenizer) {
72 72 return function (stream, state) {
73 if (!state.escapeNext && stream.eat(delimeter)) {
73 if (!state.escapeNext && stream.eat(delimiter)) {
74 74 state.tokenize = previousTokenizer;
75 75 } else {
76 76 if (state.escapeNext) {
@@ -80,7 +80,7 b''
80 80 var ch = stream.next();
81 81
82 82 // Take into account the backslash for escaping characters, such as
83 // the string delimeter.
83 // the string delimiter.
84 84 if (ch == "\\") {
85 85 state.escapeNext = true;
86 86 }
@@ -100,7 +100,7 b''
100 100 return "null";
101 101 }
102 102
103 // Dot folowed by a non-word character should be considered an error.
103 // Dot followed by a non-word character should be considered an error.
104 104 if (stream.match(/\.\W+/)) {
105 105 return "error";
106 106 } else if (stream.eat(".")) {
@@ -119,7 +119,7 b''
119 119 return "null";
120 120 }
121 121
122 // Pipe folowed by a non-word character should be considered an error.
122 // Pipe followed by a non-word character should be considered an error.
123 123 if (stream.match(/\.\W+/)) {
124 124 return "error";
125 125 } else if (stream.eat("|")) {
@@ -199,7 +199,7 b''
199 199 return "null";
200 200 }
201 201
202 // Dot folowed by a non-word character should be considered an error.
202 // Dot followed by a non-word character should be considered an error.
203 203 if (stream.match(/\.\W+/)) {
204 204 return "error";
205 205 } else if (stream.eat(".")) {
@@ -218,7 +218,7 b''
218 218 return "null";
219 219 }
220 220
221 // Pipe folowed by a non-word character should be considered an error.
221 // Pipe followed by a non-word character should be considered an error.
222 222 if (stream.match(/\.\W+/)) {
223 223 return "error";
224 224 } else if (stream.eat("|")) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,30 +11,64 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 var from = "from";
15 var fromRegex = new RegExp("^(\\s*)\\b(" + from + ")\\b", "i");
16
17 var shells = ["run", "cmd", "entrypoint", "shell"];
18 var shellsAsArrayRegex = new RegExp("^(\\s*)(" + shells.join('|') + ")(\\s+\\[)", "i");
19
20 var expose = "expose";
21 var exposeRegex = new RegExp("^(\\s*)(" + expose + ")(\\s+)", "i");
22
23 var others = [
24 "arg", "from", "maintainer", "label", "env",
25 "add", "copy", "volume", "user",
26 "workdir", "onbuild", "stopsignal", "healthcheck", "shell"
27 ];
28
14 29 // Collect all Dockerfile directives
15 var instructions = ["from", "maintainer", "run", "cmd", "expose", "env",
16 "add", "copy", "entrypoint", "volume", "user",
17 "workdir", "onbuild"],
30 var instructions = [from, expose].concat(shells).concat(others),
18 31 instructionRegex = "(" + instructions.join('|') + ")",
19 instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
20 instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
32 instructionOnlyLine = new RegExp("^(\\s*)" + instructionRegex + "(\\s*)(#.*)?$", "i"),
33 instructionWithArguments = new RegExp("^(\\s*)" + instructionRegex + "(\\s+)", "i");
21 34
22 35 CodeMirror.defineSimpleMode("dockerfile", {
23 36 start: [
24 37 // Block comment: This is a line starting with a comment
25 38 {
26 regex: /#.*$/,
39 regex: /^\s*#.*$/,
40 sol: true,
27 41 token: "comment"
28 42 },
43 {
44 regex: fromRegex,
45 token: [null, "keyword"],
46 sol: true,
47 next: "from"
48 },
29 49 // Highlight an instruction without any arguments (for convenience)
30 50 {
31 51 regex: instructionOnlyLine,
32 token: "variable-2"
52 token: [null, "keyword", null, "error"],
53 sol: true
54 },
55 {
56 regex: shellsAsArrayRegex,
57 token: [null, "keyword", null],
58 sol: true,
59 next: "array"
60 },
61 {
62 regex: exposeRegex,
63 token: [null, "keyword", null],
64 sol: true,
65 next: "expose"
33 66 },
34 67 // Highlight an instruction followed by arguments
35 68 {
36 69 regex: instructionWithArguments,
37 token: ["variable-2", null],
70 token: [null, "keyword", null],
71 sol: true,
38 72 next: "arguments"
39 73 },
40 74 {
@@ -42,26 +76,21 b''
42 76 token: null
43 77 }
44 78 ],
45 arguments: [
79 from: [
46 80 {
47 // Line comment without instruction arguments is an error
48 regex: /#.*$/,
49 token: "error",
50 next: "start"
51 },
52 {
53 regex: /[^#]+\\$/,
54 token: null
55 },
56 {
57 // Match everything except for the inline comment
58 regex: /[^#]+/,
81 regex: /\s*$/,
59 82 token: null,
60 83 next: "start"
61 84 },
62 85 {
63 regex: /$/,
64 token: null,
86 // Line comment without instruction arguments is an error
87 regex: /(\s*)(#.*)$/,
88 token: [null, "error"],
89 next: "start"
90 },
91 {
92 regex: /(\s*\S+\s+)(as)/i,
93 token: [null, "keyword"],
65 94 next: "start"
66 95 },
67 96 // Fail safe return to start
@@ -70,9 +99,112 b''
70 99 next: "start"
71 100 }
72 101 ],
73 meta: {
74 lineComment: "#"
102 single: [
103 {
104 regex: /(?:[^\\']|\\.)/,
105 token: "string"
106 },
107 {
108 regex: /'/,
109 token: "string",
110 pop: true
111 }
112 ],
113 double: [
114 {
115 regex: /(?:[^\\"]|\\.)/,
116 token: "string"
117 },
118 {
119 regex: /"/,
120 token: "string",
121 pop: true
122 }
123 ],
124 array: [
125 {
126 regex: /\]/,
127 token: null,
128 next: "start"
129 },
130 {
131 regex: /"(?:[^\\"]|\\.)*"?/,
132 token: "string"
75 133 }
134 ],
135 expose: [
136 {
137 regex: /\d+$/,
138 token: "number",
139 next: "start"
140 },
141 {
142 regex: /[^\d]+$/,
143 token: null,
144 next: "start"
145 },
146 {
147 regex: /\d+/,
148 token: "number"
149 },
150 {
151 regex: /[^\d]+/,
152 token: null
153 },
154 // Fail safe return to start
155 {
156 token: null,
157 next: "start"
158 }
159 ],
160 arguments: [
161 {
162 regex: /^\s*#.*$/,
163 sol: true,
164 token: "comment"
165 },
166 {
167 regex: /"(?:[^\\"]|\\.)*"?$/,
168 token: "string",
169 next: "start"
170 },
171 {
172 regex: /"/,
173 token: "string",
174 push: "double"
175 },
176 {
177 regex: /'(?:[^\\']|\\.)*'?$/,
178 token: "string",
179 next: "start"
180 },
181 {
182 regex: /'/,
183 token: "string",
184 push: "single"
185 },
186 {
187 regex: /[^#"']+[\\`]$/,
188 token: null
189 },
190 {
191 regex: /[^#"']+$/,
192 token: null,
193 next: "start"
194 },
195 {
196 regex: /[^#"']+/,
197 token: null
198 },
199 // Fail safe return to start
200 {
201 token: null,
202 next: "start"
203 }
204 ],
205 meta: {
206 lineComment: "#"
207 }
76 208 });
77 209
78 210 CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 DTD mode
@@ -114,17 +114,17 b' CodeMirror.defineMode("dtd", function(co'
114 114
115 115 if( textAfter.match(/\]\s+|\]/) )n=n-1;
116 116 else if(textAfter.substr(textAfter.length-1, textAfter.length) === ">"){
117 if(textAfter.substr(0,1) === "<")n;
118 else if( type == "doindent" && textAfter.length > 1 )n;
117 if(textAfter.substr(0,1) === "<") {}
118 else if( type == "doindent" && textAfter.length > 1 ) {}
119 119 else if( type == "doindent")n--;
120 else if( type == ">" && textAfter.length > 1)n;
121 else if( type == "tag" && textAfter !== ">")n;
120 else if( type == ">" && textAfter.length > 1) {}
121 else if( type == "tag" && textAfter !== ">") {}
122 122 else if( type == "tag" && state.stack[state.stack.length-1] == "rule")n--;
123 123 else if( type == "tag")n++;
124 124 else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule" && type === ">")n--;
125 else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule")n;
125 else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule") {}
126 126 else if( textAfter.substr(0,1) !== "<" && textAfter.substr(0,1) === ">" )n=n-1;
127 else if( textAfter === ">")n;
127 else if( textAfter === ">") {}
128 128 else n=n-1;
129 129 //over rule them all
130 130 if(type == null || type == "]")n--;
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,6 +11,14 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 function forEach(arr, f) {
15 for (var i = 0; i < arr.length; i++) f(arr[i], i)
16 }
17 function some(arr, f) {
18 for (var i = 0; i < arr.length; i++) if (f(arr[i], i)) return true
19 return false
20 }
21
14 22 CodeMirror.defineMode("dylan", function(_config) {
15 23 // Words
16 24 var words = {
@@ -136,13 +144,13 b' CodeMirror.defineMode("dylan", function('
136 144 var wordLookup = {};
137 145 var styleLookup = {};
138 146
139 [
147 forEach([
140 148 "keyword",
141 149 "definition",
142 150 "simpleDefinition",
143 151 "signalingCalls"
144 ].forEach(function(type) {
145 words[type].forEach(function(word) {
152 ], function(type) {
153 forEach(words[type], function(word) {
146 154 wordLookup[word] = type;
147 155 styleLookup[word] = styles[type];
148 156 });
@@ -169,15 +177,16 b' CodeMirror.defineMode("dylan", function('
169 177 } else if (stream.eat("/")) {
170 178 stream.skipToEnd();
171 179 return "comment";
172 } else {
173 stream.skipTo(" ");
174 return "operator";
175 180 }
181 stream.backUp(1);
176 182 }
177 183 // Decimal
178 else if (/\d/.test(ch)) {
179 stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
180 return "number";
184 else if (/[+\-\d\.]/.test(ch)) {
185 if (stream.match(/^[+-]?[0-9]*\.[0-9]*([esdx][+-]?[0-9]+)?/i) ||
186 stream.match(/^[+-]?[0-9]+([esdx][+-]?[0-9]+)/i) ||
187 stream.match(/^[+-]?\d+/)) {
188 return "number";
189 }
181 190 }
182 191 // Hash
183 192 else if (ch == "#") {
@@ -186,7 +195,7 b' CodeMirror.defineMode("dylan", function('
186 195 ch = stream.peek();
187 196 if (ch == '"') {
188 197 stream.next();
189 return chain(stream, state, tokenString('"', "string-2"));
198 return chain(stream, state, tokenString('"', "string"));
190 199 }
191 200 // Binary number
192 201 else if (ch == "b") {
@@ -206,29 +215,73 b' CodeMirror.defineMode("dylan", function('
206 215 stream.eatWhile(/[0-7]/);
207 216 return "number";
208 217 }
218 // Token concatenation in macros
219 else if (ch == '#') {
220 stream.next();
221 return "punctuation";
222 }
223 // Sequence literals
224 else if ((ch == '[') || (ch == '(')) {
225 stream.next();
226 return "bracket";
209 227 // Hash symbol
210 else {
228 } else if (stream.match(/f|t|all-keys|include|key|next|rest/i)) {
229 return "atom";
230 } else {
211 231 stream.eatWhile(/[-a-zA-Z]/);
212 return "keyword";
232 return "error";
233 }
234 } else if (ch == "~") {
235 stream.next();
236 ch = stream.peek();
237 if (ch == "=") {
238 stream.next();
239 ch = stream.peek();
240 if (ch == "=") {
241 stream.next();
242 return "operator";
243 }
244 return "operator";
213 245 }
246 return "operator";
247 } else if (ch == ":") {
248 stream.next();
249 ch = stream.peek();
250 if (ch == "=") {
251 stream.next();
252 return "operator";
253 } else if (ch == ":") {
254 stream.next();
255 return "punctuation";
256 }
257 } else if ("[](){}".indexOf(ch) != -1) {
258 stream.next();
259 return "bracket";
260 } else if (".,".indexOf(ch) != -1) {
261 stream.next();
262 return "punctuation";
214 263 } else if (stream.match("end")) {
215 264 return "keyword";
216 265 }
217 266 for (var name in patterns) {
218 267 if (patterns.hasOwnProperty(name)) {
219 268 var pattern = patterns[name];
220 if ((pattern instanceof Array && pattern.some(function(p) {
269 if ((pattern instanceof Array && some(pattern, function(p) {
221 270 return stream.match(p);
222 271 })) || stream.match(pattern))
223 272 return patternStyles[name];
224 273 }
225 274 }
275 if (/[+\-*\/^=<>&|]/.test(ch)) {
276 stream.next();
277 return "operator";
278 }
226 279 if (stream.match("define")) {
227 280 return "def";
228 281 } else {
229 282 stream.eatWhile(/[\w\-]/);
230 283 // Keyword
231 if (wordLookup[stream.current()]) {
284 if (wordLookup.hasOwnProperty(stream.current())) {
232 285 return styleLookup[stream.current()];
233 286 } else if (stream.current().match(symbol)) {
234 287 return "variable";
@@ -240,29 +293,37 b' CodeMirror.defineMode("dylan", function('
240 293 }
241 294
242 295 function tokenComment(stream, state) {
243 var maybeEnd = false,
244 ch;
296 var maybeEnd = false, maybeNested = false, nestedCount = 0, ch;
245 297 while ((ch = stream.next())) {
246 298 if (ch == "/" && maybeEnd) {
247 state.tokenize = tokenBase;
248 break;
299 if (nestedCount > 0) {
300 nestedCount--;
301 } else {
302 state.tokenize = tokenBase;
303 break;
304 }
305 } else if (ch == "*" && maybeNested) {
306 nestedCount++;
249 307 }
250 308 maybeEnd = (ch == "*");
309 maybeNested = (ch == "/");
251 310 }
252 311 return "comment";
253 312 }
254 313
255 314 function tokenString(quote, style) {
256 315 return function(stream, state) {
257 var next, end = false;
316 var escaped = false, next, end = false;
258 317 while ((next = stream.next()) != null) {
259 if (next == quote) {
318 if (next == quote && !escaped) {
260 319 end = true;
261 320 break;
262 321 }
322 escaped = !escaped && next == "\\";
263 323 }
264 if (end)
324 if (end || !escaped) {
265 325 state.tokenize = tokenBase;
326 }
266 327 return style;
267 328 };
268 329 }
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -94,7 +94,7 b''
94 94
95 95 if (bracesMode !== null && (state.braced || peek === "{")) {
96 96 if (state.localState === null)
97 state.localState = bracesMode.startState();
97 state.localState = CodeMirror.startState(bracesMode);
98 98
99 99 var token = bracesMode.token(stream, state.localState),
100 100 text = stream.current();
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -70,7 +70,7 b''
70 70 if (smallRE.test(ch)) {
71 71 var isDef = source.pos === 1;
72 72 source.eatWhile(idRE);
73 return isDef ? "variable-3" : "variable";
73 return isDef ? "type" : "variable";
74 74 }
75 75
76 76 if (digitRE.test(ch)) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*jshint unused:true, eqnull:true, curly:true, bitwise:true */
5 5 /*jshint undef:true, latedef:true, trailing:true */
@@ -433,15 +433,16 b' CodeMirror.defineMode("erlang", function'
433 433 }
434 434
435 435 function maybe_drop_post(s) {
436 if (!s.length) return s
436 437 var last = s.length-1;
437 438
438 439 if (s[last].type === "dot") {
439 440 return [];
440 441 }
441 if (s[last].type === "fun" && s[last-1].token === "fun") {
442 if (last > 1 && s[last].type === "fun" && s[last-1].token === "fun") {
442 443 return s.slice(0,last-1);
443 444 }
444 switch (s[s.length-1].token) {
445 switch (s[last].token) {
445 446 case "}": return d(s,{g:["{"]});
446 447 case "]": return d(s,{i:["["]});
447 448 case ")": return d(s,{i:["("]});
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Factor syntax highlight - simple mode
5 5 //
@@ -22,52 +22,54 b''
22 22 {regex: /#?!.*/, token: "comment"},
23 23 // strings """, multiline --> state
24 24 {regex: /"""/, token: "string", next: "string3"},
25 {regex: /"/, token: "string", next: "string"},
25 {regex: /(STRING:)(\s)/, token: ["keyword", null], next: "string2"},
26 {regex: /\S*?"/, token: "string", next: "string"},
26 27 // numbers: dec, hex, unicode, bin, fractional, complex
27 {regex: /(?:[+-]?)(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\d+.?\d*)/, token: "number"},
28 {regex: /(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\-?\d+.?\d*)(?=\s)/, token: "number"},
28 29 //{regex: /[+-]?/} //fractional
29 30 // definition: defining word, defined word, etc
30 {regex: /(\:)(\s+)(\S+)(\s+)(\()/, token: ["keyword", null, "def", null, "keyword"], next: "stack"},
31 {regex: /((?:GENERIC)|\:?\:)(\s+)(\S+)(\s+)(\()/, token: ["keyword", null, "def", null, "bracket"], next: "stack"},
32 // method definition: defining word, type, defined word, etc
33 {regex: /(M\:)(\s+)(\S+)(\s+)(\S+)/, token: ["keyword", null, "def", null, "tag"]},
31 34 // vocabulary using --> state
32 35 {regex: /USING\:/, token: "keyword", next: "vocabulary"},
33 36 // vocabulary definition/use
34 {regex: /(USE\:|IN\:)(\s+)(\S+)/, token: ["keyword", null, "variable-2"]},
35 // <constructors>
36 {regex: /<\S+>/, token: "builtin"},
37 {regex: /(USE\:|IN\:)(\s+)(\S+)(?=\s|$)/, token: ["keyword", null, "tag"]},
38 // definition: a defining word, defined word
39 {regex: /(\S+\:)(\s+)(\S+)(?=\s|$)/, token: ["keyword", null, "def"]},
37 40 // "keywords", incl. ; t f . [ ] { } defining words
38 {regex: /;|t|f|if|\.|\[|\]|\{|\}|MAIN:/, token: "keyword"},
41 {regex: /(?:;|\\|t|f|if|loop|while|until|do|PRIVATE>|<PRIVATE|\.|\S*\[|\]|\S*\{|\})(?=\s|$)/, token: "keyword"},
42 // <constructors> and the like
43 {regex: /\S+[\)>\.\*\?]+(?=\s|$)/, token: "builtin"},
44 {regex: /[\)><]+\S+(?=\s|$)/, token: "builtin"},
45 // operators
46 {regex: /(?:[\+\-\=\/\*<>])(?=\s|$)/, token: "keyword"},
39 47 // any id (?)
40 48 {regex: /\S+/, token: "variable"},
41
42 {
43 regex: /./,
44 token: null
45 }
49 {regex: /\s+|./, token: null}
46 50 ],
47 51 vocabulary: [
48 52 {regex: /;/, token: "keyword", next: "start"},
49 {regex: /\S+/, token: "variable-2"},
50 {
51 regex: /./,
52 token: null
53 }
53 {regex: /\S+/, token: "tag"},
54 {regex: /\s+|./, token: null}
54 55 ],
55 56 string: [
56 57 {regex: /(?:[^\\]|\\.)*?"/, token: "string", next: "start"},
57 58 {regex: /.*/, token: "string"}
58 59 ],
60 string2: [
61 {regex: /^;/, token: "keyword", next: "start"},
62 {regex: /.*/, token: "string"}
63 ],
59 64 string3: [
60 65 {regex: /(?:[^\\]|\\.)*?"""/, token: "string", next: "start"},
61 66 {regex: /.*/, token: "string"}
62 67 ],
63 68 stack: [
64 {regex: /\)/, token: "meta", next: "start"},
65 {regex: /--/, token: "meta"},
66 {regex: /\S+/, token: "variable-3"},
67 {
68 regex: /./,
69 token: null
70 }
69 {regex: /\)/, token: "bracket", next: "start"},
70 {regex: /--/, token: "bracket"},
71 {regex: /\S+/, token: "meta"},
72 {regex: /\s+|./, token: null}
71 73 ],
72 74 // The meta property contains global information about the mode. It
73 75 // can contain properties like lineComment, which are supported by
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Author: Aliaksei Chapyzhenka
5 5
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -81,7 +81,7 b' CodeMirror.defineMode("gfm", function(co'
81 81 if (stream.sol() || state.ateSpace) {
82 82 state.ateSpace = false;
83 83 if (modeConfig.gitHubSpice !== false) {
84 if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
84 if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/)) {
85 85 // User/Project@SHA
86 86 // User@SHA
87 87 // SHA
@@ -113,10 +113,9 b' CodeMirror.defineMode("gfm", function(co'
113 113 };
114 114
115 115 var markdownConfig = {
116 underscoresBreakWords: false,
117 116 taskLists: true,
118 fencedCodeBlocks: '```',
119 strikethrough: true
117 strikethrough: true,
118 emoji: true
120 119 };
121 120 for (var attr in modeConfig) {
122 121 markdownConfig[attr] = modeConfig[attr];
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 Gherkin mode - http://www.cukes.info/
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -23,12 +23,13 b' CodeMirror.defineMode("go", function(con'
23 23 "bool":true, "byte":true, "complex64":true, "complex128":true,
24 24 "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
25 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, "error": true,
27 "rune":true
27 28 };
28 29
29 30 var atoms = {
30 31 "true":true, "false":true, "iota":true, "nil":true, "append":true,
31 "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
32 "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
32 33 "len":true, "make":true, "new":true, "panic":true, "print":true,
33 34 "println":true, "real":true, "recover":true
34 35 };
@@ -154,14 +155,14 b' CodeMirror.defineMode("go", function(con'
154 155 else if (curPunc == "[") pushContext(state, stream.column(), "]");
155 156 else if (curPunc == "(") pushContext(state, stream.column(), ")");
156 157 else if (curPunc == "case") ctx.type = "case";
157 else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
158 else if (curPunc == "}" && ctx.type == "}") popContext(state);
158 159 else if (curPunc == ctx.type) popContext(state);
159 160 state.startOfLine = false;
160 161 return style;
161 162 },
162 163
163 164 indent: function(state, textAfter) {
164 if (state.tokenize != tokenBase && state.tokenize != null) return 0;
165 if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
165 166 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
166 167 if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
167 168 state.context.type = "}";
@@ -173,6 +174,7 b' CodeMirror.defineMode("go", function(con'
173 174 },
174 175
175 176 electricChars: "{}):",
177 closeBrackets: "()[]{}''\"\"``",
176 178 fold: "brace",
177 179 blockCommentStart: "/*",
178 180 blockCommentEnd: "*/",
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -21,9 +21,9 b' CodeMirror.defineMode("groovy", function'
21 21 "abstract as assert boolean break byte case catch char class const continue def default " +
22 22 "do double else enum extends final finally float for goto if implements import in " +
23 23 "instanceof int interface long native new package private protected public return " +
24 "short static strictfp super switch synchronized threadsafe throw throws transient " +
24 "short static strictfp super switch synchronized threadsafe throw throws trait transient " +
25 25 "try void volatile while");
26 var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
26 var blockKeywords = words("catch class def do else enum finally for if interface switch trait try while");
27 27 var standaloneKeywords = words("return break continue");
28 28 var atoms = words("null true false this");
29 29
@@ -210,7 +210,7 b' CodeMirror.defineMode("groovy", function'
210 210 },
211 211
212 212 indent: function(state, textAfter) {
213 if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
213 if (!state.tokenize[state.tokenize.length-1].isBase) return CodeMirror.Pass;
214 214 var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
215 215 if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;
216 216 var closing = firstChar == ctx.type;
@@ -221,7 +221,10 b' CodeMirror.defineMode("groovy", function'
221 221
222 222 electricChars: "{}",
223 223 closeBrackets: {triples: "'\""},
224 fold: "brace"
224 fold: "brace",
225 blockCommentStart: "/*",
226 blockCommentEnd: "*/",
227 lineComment: "//"
225 228 };
226 229 });
227 230
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,7 +11,7 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 // full haml mode. This handled embeded ruby and html fragments too
14 // full haml mode. This handled embedded ruby and html fragments too
15 15 CodeMirror.defineMode("haml", function(config) {
16 16 var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
17 17 var rubyMode = CodeMirror.getMode(config, "ruby");
@@ -98,8 +98,8 b''
98 98 return {
99 99 // default to html mode
100 100 startState: function() {
101 var htmlState = htmlMode.startState();
102 var rubyState = rubyMode.startState();
101 var htmlState = CodeMirror.startState(htmlMode);
102 var rubyState = CodeMirror.startState(rubyMode);
103 103 return {
104 104 htmlState: htmlState,
105 105 rubyState: rubyState,
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,10 +13,14 b''
13 13
14 14 CodeMirror.defineSimpleMode("handlebars-tags", {
15 15 start: [
16 { regex: /\{\{\{/, push: "handlebars_raw", token: "tag" },
16 17 { regex: /\{\{!--/, push: "dash_comment", token: "comment" },
17 18 { regex: /\{\{!/, push: "comment", token: "comment" },
18 19 { regex: /\{\{/, push: "handlebars", token: "tag" }
19 20 ],
21 handlebars_raw: [
22 { regex: /\}\}\}/, pop: true, token: "tag" },
23 ],
20 24 handlebars: [
21 25 { regex: /\}\}/, pop: true, token: "tag" },
22 26
@@ -46,7 +50,11 b''
46 50 comment: [
47 51 { regex: /\}\}/, pop: true, token: "comment" },
48 52 { regex: /./, token: "comment" }
49 ]
53 ],
54 meta: {
55 blockCommentStart: "{{--",
56 blockCommentEnd: "--}}"
57 }
50 58 });
51 59
52 60 CodeMirror.defineMode("handlebars", function(config, parserConfig) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function (mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -40,4 +40,4 b''
40 40 }, "haskell")
41 41
42 42 CodeMirror.defineMIME("text/x-literate-haskell", "haskell-literate")
43 })
43 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -56,7 +56,7 b' CodeMirror.defineMode("haskell", functio'
56 56 if (source.eat('\'')) {
57 57 return "string";
58 58 }
59 return "error";
59 return "string error";
60 60 }
61 61
62 62 if (ch == '"') {
@@ -166,7 +166,7 b' CodeMirror.defineMode("haskell", functio'
166 166 }
167 167 }
168 168 setState(normal);
169 return "error";
169 return "string error";
170 170 }
171 171
172 172 function stringGap(source, setState) {
@@ -194,16 +194,17 b' CodeMirror.defineMode("haskell", functio'
194 194 "module", "newtype", "of", "then", "type", "where", "_");
195 195
196 196 setType("keyword")(
197 "\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>");
197 "\.\.", ":", "::", "=", "\\", "<-", "->", "@", "~", "=>");
198 198
199 199 setType("builtin")(
200 "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<",
201 "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**");
200 "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<*", "<=",
201 "<$>", "<*>", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*",
202 "*>", "**");
202 203
203 204 setType("builtin")(
204 "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq",
205 "False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT",
206 "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
205 "Applicative", "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum",
206 "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor",
207 "GT", "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
207 208 "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
208 209 "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
209 210 "String", "True");
@@ -223,7 +224,7 b' CodeMirror.defineMode("haskell", functio'
223 224 "lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
224 225 "mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
225 226 "minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
226 "otherwise", "pi", "pred", "print", "product", "properFraction",
227 "otherwise", "pi", "pred", "print", "product", "properFraction", "pure",
227 228 "putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
228 229 "readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
229 230 "realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -485,7 +485,7 b' CodeMirror.defineMode("hxml", function ('
485 485
486 486 if (state.inString == false && ch == "'") {
487 487 state.inString = true;
488 ch = stream.next();
488 stream.next();
489 489 }
490 490
491 491 if (state.inString == true) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,7 +14,16 b''
14 14 "use strict";
15 15
16 16 CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
17 var closeComment = parserConfig.closeComment || "--%>"
17 18 return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
19 open: parserConfig.openComment || "<%--",
20 close: closeComment,
21 delimStyle: "comment",
22 mode: {token: function(stream) {
23 stream.skipTo(closeComment) || stream.skipToEnd()
24 return "comment"
25 }}
26 }, {
18 27 open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
19 28 close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
20 29 mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,7 +14,7 b''
14 14 var defaultTags = {
15 15 script: [
16 16 ["lang", /(javascript|babel)/i, "javascript"],
17 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
17 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
18 18 ["type", /./, "text/plain"],
19 19 [null, null, "javascript"]
20 20 ],
@@ -44,13 +44,9 b''
44 44 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
45 45 }
46 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 "";
47 function getAttrValue(text, attr) {
48 var match = text.match(getAttrRegexp(attr))
49 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
54 50 }
55 51
56 52 function getTagRegexp(tagName, anchored) {
@@ -66,10 +62,10 b''
66 62 }
67 63 }
68 64
69 function findMatchingMode(tagInfo, stream) {
65 function findMatchingMode(tagInfo, tagText) {
70 66 for (var i = 0; i < tagInfo.length; i++) {
71 67 var spec = tagInfo[i];
72 if (!spec[0] || spec[1].test(getAttrValue(stream, spec[0]))) return spec[2];
68 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
73 69 }
74 70 }
75 71
@@ -89,15 +85,17 b''
89 85 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
90 86
91 87 function html(stream, state) {
92 var tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase();
93 var tagInfo = tagName && tags.hasOwnProperty(tagName) && tags[tagName];
94
95 var style = htmlMode.token(stream, state.htmlState), modeSpec;
96
97 if (tagInfo && /\btag\b/.test(style) && stream.current() === ">" &&
98 (modeSpec = findMatchingMode(tagInfo, stream))) {
99 var mode = CodeMirror.getMode(config, modeSpec);
100 var endTagA = getTagRegexp(tagName, true), endTag = getTagRegexp(tagName, false);
88 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
89 if (tag && !/[<>\s\/]/.test(stream.current()) &&
90 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
91 tags.hasOwnProperty(tagName)) {
92 state.inTag = tagName + " "
93 } else if (state.inTag && tag && />$/.test(stream.current())) {
94 var inTag = /^([\S]+) (.*)/.exec(state.inTag)
95 state.inTag = null
96 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
97 var mode = CodeMirror.getMode(config, modeSpec)
98 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
101 99 state.token = function (stream, state) {
102 100 if (stream.match(endTagA, false)) {
103 101 state.token = html;
@@ -107,15 +105,18 b''
107 105 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
108 106 };
109 107 state.localMode = mode;
110 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
108 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
109 } else if (state.inTag) {
110 state.inTag += stream.current()
111 if (stream.eol()) state.inTag += " "
111 112 }
112 113 return style;
113 114 };
114 115
115 116 return {
116 117 startState: function () {
117 var state = htmlMode.startState();
118 return {token: html, localMode: null, localState: null, htmlState: state};
118 var state = CodeMirror.startState(htmlMode);
119 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
119 120 },
120 121
121 122 copyState: function (state) {
@@ -123,7 +124,8 b''
123 124 if (state.localState) {
124 125 local = CodeMirror.copyState(state.localMode, state.localState);
125 126 }
126 return {token: state.token, localMode: state.localMode, localState: local,
127 return {token: state.token, inTag: state.inTag,
128 localMode: state.localMode, localState: local,
127 129 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
128 130 },
129 131
@@ -131,11 +133,11 b''
131 133 return state.token(stream, state);
132 134 },
133 135
134 indent: function (state, textAfter) {
136 indent: function (state, textAfter, line) {
135 137 if (!state.localMode || /^\s*<\//.test(textAfter))
136 return htmlMode.indent(state.htmlState, textAfter);
138 return htmlMode.indent(state.htmlState, textAfter, line);
137 139 else if (state.localMode.indent)
138 return state.localMode.indent(state.localState, textAfter);
140 return state.localMode.indent(state.localState, textAfter, line);
139 141 else
140 142 return CodeMirror.Pass;
141 143 },
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
This diff has been collapsed as it changes many lines, (526 lines changed) Show them Hide them
@@ -1,7 +1,5 b''
1 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 // TODO actually recognize syntax of TypeScript constructs
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
5 3
6 4 (function(mod) {
7 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,11 +11,6 b''
13 11 })(function(CodeMirror) {
14 12 "use strict";
15 13
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
21 14 CodeMirror.defineMode("javascript", function(config, parserConfig) {
22 15 var indentUnit = config.indentUnit;
23 16 var statementIndent = parserConfig.statementIndent;
@@ -30,54 +23,24 b' CodeMirror.defineMode("javascript", func'
30 23
31 24 var keywords = function(){
32 25 function kw(type) {return {type: type, style: "keyword"};}
33 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
26 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
34 27 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
35 28
36 var jsKeywords = {
29 return {
37 30 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
38 "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
39 "var": kw("var"), "const": kw("var"), "let": kw("var"),
31 "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
32 "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
40 33 "function": kw("function"), "catch": kw("catch"),
41 34 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
42 35 "in": operator, "typeof": operator, "instanceof": operator,
43 36 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
44 37 "this": kw("this"), "class": kw("class"), "super": kw("atom"),
45 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
38 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
39 "await": C
46 40 };
47
48 // Extend the 'normal' keywords with the TypeScript language extensions
49 if (isTS) {
50 var type = {type: "variable", style: "variable-3"};
51 var tsKeywords = {
52 // object-like things
53 "interface": kw("class"),
54 "implements": C,
55 "namespace": C,
56 "module": kw("module"),
57 "enum": kw("module"),
58
59 // scope modifiers
60 "public": kw("modifier"),
61 "private": kw("modifier"),
62 "protected": kw("modifier"),
63 "abstract": kw("modifier"),
64
65 // operators
66 "as": operator,
67
68 // types
69 "string": type, "number": type, "boolean": type, "any": type
70 };
71
72 for (var attr in tsKeywords) {
73 jsKeywords[attr] = tsKeywords[attr];
74 }
75 }
76
77 return jsKeywords;
78 41 }();
79 42
80 var isOperatorChar = /[+\-*&%=<>!?|~^]/;
43 var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
81 44 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
82 45
83 46 function readRegexp(stream) {
@@ -104,7 +67,7 b' CodeMirror.defineMode("javascript", func'
104 67 if (ch == '"' || ch == "'") {
105 68 state.tokenize = tokenString(ch);
106 69 return state.tokenize(stream, state);
107 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
70 } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
108 71 return ret("number", "number");
109 72 } else if (ch == "." && stream.match("..")) {
110 73 return ret("spread", "meta");
@@ -112,17 +75,10 b' CodeMirror.defineMode("javascript", func'
112 75 return ret(ch);
113 76 } else if (ch == "=" && stream.eat(">")) {
114 77 return ret("=>", "operator");
115 } else if (ch == "0" && stream.eat(/x/i)) {
116 stream.eatWhile(/[\da-f]/i);
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);
78 } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
123 79 return ret("number", "number");
124 80 } else if (/\d/.test(ch)) {
125 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
81 stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
126 82 return ret("number", "number");
127 83 } else if (ch == "/") {
128 84 if (stream.eat("*")) {
@@ -133,10 +89,10 b' CodeMirror.defineMode("javascript", func'
133 89 return ret("comment", "comment");
134 90 } else if (expressionAllowed(stream, state, 1)) {
135 91 readRegexp(stream);
136 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
92 stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
137 93 return ret("regexp", "string-2");
138 94 } else {
139 stream.eatWhile(isOperatorChar);
95 stream.eat("=");
140 96 return ret("operator", "operator", stream.current());
141 97 }
142 98 } else if (ch == "`") {
@@ -145,14 +101,31 b' CodeMirror.defineMode("javascript", func'
145 101 } else if (ch == "#") {
146 102 stream.skipToEnd();
147 103 return ret("error", "error");
104 } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) {
105 stream.skipToEnd()
106 return ret("comment", "comment")
148 107 } else if (isOperatorChar.test(ch)) {
149 stream.eatWhile(isOperatorChar);
108 if (ch != ">" || !state.lexical || state.lexical.type != ">") {
109 if (stream.eat("=")) {
110 if (ch == "!" || ch == "=") stream.eat("=")
111 } else if (/[<>*+\-]/.test(ch)) {
112 stream.eat(ch)
113 if (ch == ">") stream.eat(ch)
114 }
115 }
150 116 return ret("operator", "operator", stream.current());
151 117 } else if (wordRE.test(ch)) {
152 118 stream.eatWhile(wordRE);
153 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
154 return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
155 ret("variable", "variable", word);
119 var word = stream.current()
120 if (state.lastType != ".") {
121 if (keywords.propertyIsEnumerable(word)) {
122 var kw = keywords[word]
123 return ret(kw.type, kw.style, word)
124 }
125 if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
126 return ret("async", "keyword", word)
127 }
128 return ret("variable", "variable", word)
156 129 }
157 130 }
158 131
@@ -209,19 +182,28 b' CodeMirror.defineMode("javascript", func'
209 182 var arrow = stream.string.indexOf("=>", stream.start);
210 183 if (arrow < 0) return;
211 184
185 if (isTS) { // Try to skip TypeScript return type declarations after the arguments
186 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
187 if (m) arrow = m.index
188 }
189
212 190 var depth = 0, sawSomething = false;
213 191 for (var pos = arrow - 1; pos >= 0; --pos) {
214 192 var ch = stream.string.charAt(pos);
215 193 var bracket = brackets.indexOf(ch);
216 194 if (bracket >= 0 && bracket < 3) {
217 195 if (!depth) { ++pos; break; }
218 if (--depth == 0) break;
196 if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
219 197 } else if (bracket >= 3 && bracket < 6) {
220 198 ++depth;
221 199 } else if (wordRE.test(ch)) {
222 200 sawSomething = true;
223 } else if (/["'\/]/.test(ch)) {
224 return;
201 } else if (/["'\/`]/.test(ch)) {
202 for (;; --pos) {
203 if (pos == 0) return
204 var next = stream.string.charAt(pos - 1)
205 if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
206 }
225 207 } else if (sawSomething && !depth) {
226 208 ++pos;
227 209 break;
@@ -283,35 +265,68 b' CodeMirror.defineMode("javascript", func'
283 265 pass.apply(null, arguments);
284 266 return true;
285 267 }
268 function inList(name, list) {
269 for (var v = list; v; v = v.next) if (v.name == name) return true
270 return false;
271 }
286 272 function register(varname) {
287 function inList(list) {
288 for (var v = list; v; v = v.next)
289 if (v.name == varname) return true;
290 return false;
291 }
292 273 var state = cx.state;
293 274 cx.marked = "def";
294 275 if (state.context) {
295 if (inList(state.localVars)) return;
296 state.localVars = {name: varname, next: state.localVars};
276 if (state.lexical.info == "var" && state.context && state.context.block) {
277 // FIXME function decls are also not block scoped
278 var newContext = registerVarScoped(varname, state.context)
279 if (newContext != null) {
280 state.context = newContext
281 return
282 }
283 } else if (!inList(varname, state.localVars)) {
284 state.localVars = new Var(varname, state.localVars)
285 return
286 }
287 }
288 // Fall through means this is global
289 if (parserConfig.globalVars && !inList(varname, state.globalVars))
290 state.globalVars = new Var(varname, state.globalVars)
291 }
292 function registerVarScoped(varname, context) {
293 if (!context) {
294 return null
295 } else if (context.block) {
296 var inner = registerVarScoped(varname, context.prev)
297 if (!inner) return null
298 if (inner == context.prev) return context
299 return new Context(inner, context.vars, true)
300 } else if (inList(varname, context.vars)) {
301 return context
297 302 } else {
298 if (inList(state.globalVars)) return;
299 if (parserConfig.globalVars)
300 state.globalVars = {name: varname, next: state.globalVars};
303 return new Context(context.prev, new Var(varname, context.vars), false)
301 304 }
302 305 }
303 306
307 function isModifier(name) {
308 return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
309 }
310
304 311 // Combinators
305 312
306 var defaultVars = {name: "this", next: {name: "arguments"}};
313 function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
314 function Var(name, next) { this.name = name; this.next = next }
315
316 var defaultVars = new Var("this", new Var("arguments", null))
307 317 function pushcontext() {
308 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
309 cx.state.localVars = defaultVars;
318 cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
319 cx.state.localVars = defaultVars
320 }
321 function pushblockcontext() {
322 cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
323 cx.state.localVars = null
310 324 }
311 325 function popcontext() {
312 cx.state.localVars = cx.state.context.vars;
313 cx.state.context = cx.state.context.prev;
326 cx.state.localVars = cx.state.context.vars
327 cx.state.context = cx.state.context.prev
314 328 }
329 popcontext.lex = true
315 330 function pushlex(type, info) {
316 331 var result = function() {
317 332 var state = cx.state, indent = state.indented;
@@ -336,71 +351,99 b' CodeMirror.defineMode("javascript", func'
336 351 function expect(wanted) {
337 352 function exp(type) {
338 353 if (type == wanted) return cont();
339 else if (wanted == ";") return pass();
354 else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
340 355 else return cont(exp);
341 356 };
342 357 return exp;
343 358 }
344 359
345 360 function statement(type, value) {
346 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
347 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
361 if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
362 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
348 363 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
349 if (type == "{") return cont(pushlex("}"), block, poplex);
364 if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
365 if (type == "debugger") return cont(expect(";"));
366 if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
350 367 if (type == ";") return cont();
351 368 if (type == "if") {
352 369 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
353 370 cx.state.cc.pop()();
354 return cont(pushlex("form"), expression, statement, poplex, maybeelse);
371 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
355 372 }
356 373 if (type == "function") return cont(functiondef);
357 374 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
358 if (type == "variable") return cont(pushlex("stat"), maybelabel);
359 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
360 block, poplex, poplex);
375 if (type == "class" || (isTS && value == "interface")) {
376 cx.marked = "keyword"
377 return cont(pushlex("form", type == "class" ? type : value), className, poplex)
378 }
379 if (type == "variable") {
380 if (isTS && value == "declare") {
381 cx.marked = "keyword"
382 return cont(statement)
383 } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
384 cx.marked = "keyword"
385 if (value == "enum") return cont(enumdef);
386 else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
387 else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
388 } else if (isTS && value == "namespace") {
389 cx.marked = "keyword"
390 return cont(pushlex("form"), expression, statement, poplex)
391 } else if (isTS && value == "abstract") {
392 cx.marked = "keyword"
393 return cont(statement)
394 } else {
395 return cont(pushlex("stat"), maybelabel);
396 }
397 }
398 if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
399 block, poplex, poplex, popcontext);
361 400 if (type == "case") return cont(expression, expect(":"));
362 401 if (type == "default") return cont(expect(":"));
363 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
364 statement, poplex, popcontext);
365 if (type == "class") return cont(pushlex("form"), className, poplex);
402 if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
366 403 if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
367 404 if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
368 if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
405 if (type == "async") return cont(statement)
406 if (value == "@") return cont(expression, statement)
369 407 return pass(pushlex("stat"), expression, expect(";"), poplex);
370 408 }
371 function expression(type) {
372 return expressionInner(type, false);
409 function maybeCatchBinding(type) {
410 if (type == "(") return cont(funarg, expect(")"))
411 }
412 function expression(type, value) {
413 return expressionInner(type, value, false);
373 414 }
374 function expressionNoComma(type) {
375 return expressionInner(type, true);
415 function expressionNoComma(type, value) {
416 return expressionInner(type, value, true);
376 417 }
377 function expressionInner(type, noComma) {
418 function parenExpr(type) {
419 if (type != "(") return pass()
420 return cont(pushlex(")"), expression, expect(")"), poplex)
421 }
422 function expressionInner(type, value, noComma) {
378 423 if (cx.state.fatArrowAt == cx.stream.start) {
379 424 var body = noComma ? arrowBodyNoComma : arrowBody;
380 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
425 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
381 426 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
382 427 }
383 428
384 429 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
385 430 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
386 431 if (type == "function") return cont(functiondef, maybeop);
387 if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
388 if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
432 if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
433 if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
434 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
389 435 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
390 436 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
391 437 if (type == "{") return contCommasep(objprop, "}", null, maybeop);
392 438 if (type == "quasi") return pass(quasi, maybeop);
393 439 if (type == "new") return cont(maybeTarget(noComma));
440 if (type == "import") return cont(expression);
394 441 return cont();
395 442 }
396 443 function maybeexpression(type) {
397 444 if (type.match(/[;\}\)\],]/)) return pass();
398 445 return pass(expression);
399 446 }
400 function maybeexpressionNoComma(type) {
401 if (type.match(/[;\}\)\],]/)) return pass();
402 return pass(expressionNoComma);
403 }
404 447
405 448 function maybeoperatorComma(type, value) {
406 449 if (type == ",") return cont(expression);
@@ -411,7 +454,9 b' CodeMirror.defineMode("javascript", func'
411 454 var expr = noComma == false ? expression : expressionNoComma;
412 455 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
413 456 if (type == "operator") {
414 if (/\+\+|--/.test(value)) return cont(me);
457 if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
458 if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
459 return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
415 460 if (value == "?") return cont(expression, expect(":"), expr);
416 461 return cont(expr);
417 462 }
@@ -420,6 +465,12 b' CodeMirror.defineMode("javascript", func'
420 465 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
421 466 if (type == ".") return cont(property, me);
422 467 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
468 if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
469 if (type == "regexp") {
470 cx.state.lastType = cx.marked = "operator"
471 cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
472 return cont(expr)
473 }
423 474 }
424 475 function quasi(type, value) {
425 476 if (type != "quasi") return pass();
@@ -444,6 +495,7 b' CodeMirror.defineMode("javascript", func'
444 495 function maybeTarget(noComma) {
445 496 return function(type) {
446 497 if (type == ".") return cont(noComma ? targetNoComma : target);
498 else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
447 499 else return pass(noComma ? expressionNoComma : expression);
448 500 };
449 501 }
@@ -461,21 +513,33 b' CodeMirror.defineMode("javascript", func'
461 513 if (type == "variable") {cx.marked = "property"; return cont();}
462 514 }
463 515 function objprop(type, value) {
464 if (type == "variable" || cx.style == "keyword") {
516 if (type == "async") {
517 cx.marked = "property";
518 return cont(objprop);
519 } else if (type == "variable" || cx.style == "keyword") {
465 520 cx.marked = "property";
466 521 if (value == "get" || value == "set") return cont(getterSetter);
522 var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
523 if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
524 cx.state.fatArrowAt = cx.stream.pos + m[0].length
467 525 return cont(afterprop);
468 526 } else if (type == "number" || type == "string") {
469 527 cx.marked = jsonldMode ? "property" : (cx.style + " property");
470 528 return cont(afterprop);
471 529 } else if (type == "jsonld-keyword") {
472 530 return cont(afterprop);
473 } else if (type == "modifier") {
531 } else if (isTS && isModifier(value)) {
532 cx.marked = "keyword"
474 533 return cont(objprop)
475 534 } else if (type == "[") {
476 return cont(expression, expect("]"), afterprop);
535 return cont(expression, maybetype, expect("]"), afterprop);
477 536 } else if (type == "spread") {
478 return cont(expression);
537 return cont(expressionNoComma, afterprop);
538 } else if (value == "*") {
539 cx.marked = "keyword";
540 return cont(objprop);
541 } else if (type == ":") {
542 return pass(afterprop)
479 543 }
480 544 }
481 545 function getterSetter(type) {
@@ -487,18 +551,22 b' CodeMirror.defineMode("javascript", func'
487 551 if (type == ":") return cont(expressionNoComma);
488 552 if (type == "(") return pass(functiondef);
489 553 }
490 function commasep(what, end) {
491 function proceed(type) {
492 if (type == ",") {
554 function commasep(what, end, sep) {
555 function proceed(type, value) {
556 if (sep ? sep.indexOf(type) > -1 : type == ",") {
493 557 var lex = cx.state.lexical;
494 558 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
495 return cont(what, proceed);
559 return cont(function(type, value) {
560 if (type == end || value == end) return pass()
561 return pass(what)
562 }, proceed);
496 563 }
497 if (type == end) return cont();
564 if (type == end || value == end) return cont();
565 if (sep && sep.indexOf(";") > -1) return pass(what)
498 566 return cont(expect(end));
499 567 }
500 return function(type) {
501 if (type == end) return cont();
568 return function(type, value) {
569 if (type == end || value == end) return cont();
502 570 return pass(what, proceed);
503 571 };
504 572 }
@@ -511,23 +579,91 b' CodeMirror.defineMode("javascript", func'
511 579 if (type == "}") return cont();
512 580 return pass(statement, block);
513 581 }
514 function maybetype(type) {
515 if (isTS && type == ":") return cont(typedef);
582 function maybetype(type, value) {
583 if (isTS) {
584 if (type == ":") return cont(typeexpr);
585 if (value == "?") return cont(maybetype);
586 }
587 }
588 function maybetypeOrIn(type, value) {
589 if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
590 }
591 function mayberettype(type) {
592 if (isTS && type == ":") {
593 if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
594 else return cont(typeexpr)
595 }
596 }
597 function isKW(_, value) {
598 if (value == "is") {
599 cx.marked = "keyword"
600 return cont()
601 }
602 }
603 function typeexpr(type, value) {
604 if (value == "keyof" || value == "typeof" || value == "infer") {
605 cx.marked = "keyword"
606 return cont(value == "typeof" ? expressionNoComma : typeexpr)
607 }
608 if (type == "variable" || value == "void") {
609 cx.marked = "type"
610 return cont(afterType)
611 }
612 if (value == "|" || value == "&") return cont(typeexpr)
613 if (type == "string" || type == "number" || type == "atom") return cont(afterType);
614 if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
615 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
616 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
617 if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
516 618 }
517 function maybedefault(_, value) {
518 if (value == "=") return cont(expressionNoComma);
619 function maybeReturnType(type) {
620 if (type == "=>") return cont(typeexpr)
621 }
622 function typeprop(type, value) {
623 if (type == "variable" || cx.style == "keyword") {
624 cx.marked = "property"
625 return cont(typeprop)
626 } else if (value == "?" || type == "number" || type == "string") {
627 return cont(typeprop)
628 } else if (type == ":") {
629 return cont(typeexpr)
630 } else if (type == "[") {
631 return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
632 } else if (type == "(") {
633 return pass(functiondecl, typeprop)
634 }
519 635 }
520 function typedef(type) {
521 if (type == "variable") {cx.marked = "variable-3"; return cont();}
636 function typearg(type, value) {
637 if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
638 if (type == ":") return cont(typeexpr)
639 if (type == "spread") return cont(typearg)
640 return pass(typeexpr)
522 641 }
523 function vardef() {
642 function afterType(type, value) {
643 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
644 if (value == "|" || type == "." || value == "&") return cont(typeexpr)
645 if (type == "[") return cont(typeexpr, expect("]"), afterType)
646 if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
647 if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
648 }
649 function maybeTypeArgs(_, value) {
650 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
651 }
652 function typeparam() {
653 return pass(typeexpr, maybeTypeDefault)
654 }
655 function maybeTypeDefault(_, value) {
656 if (value == "=") return cont(typeexpr)
657 }
658 function vardef(_, value) {
659 if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
524 660 return pass(pattern, maybetype, maybeAssign, vardefCont);
525 661 }
526 662 function pattern(type, value) {
527 if (type == "modifier") return cont(pattern)
663 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
528 664 if (type == "variable") { register(value); return cont(); }
529 665 if (type == "spread") return cont(pattern);
530 if (type == "[") return contCommasep(pattern, "]");
666 if (type == "[") return contCommasep(eltpattern, "]");
531 667 if (type == "{") return contCommasep(proppattern, "}");
532 668 }
533 669 function proppattern(type, value) {
@@ -538,8 +674,12 b' CodeMirror.defineMode("javascript", func'
538 674 if (type == "variable") cx.marked = "property";
539 675 if (type == "spread") return cont(pattern);
540 676 if (type == "}") return pass();
677 if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
541 678 return cont(expect(":"), pattern, maybeAssign);
542 679 }
680 function eltpattern() {
681 return pass(pattern, maybeAssign)
682 }
543 683 function maybeAssign(_type, value) {
544 684 if (value == "=") return cont(expressionNoComma);
545 685 }
@@ -549,73 +689,109 b' CodeMirror.defineMode("javascript", func'
549 689 function maybeelse(type, value) {
550 690 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
551 691 }
552 function forspec(type) {
553 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
692 function forspec(type, value) {
693 if (value == "await") return cont(forspec);
694 if (type == "(") return cont(pushlex(")"), forspec1, poplex);
554 695 }
555 696 function forspec1(type) {
556 if (type == "var") return cont(vardef, expect(";"), forspec2);
557 if (type == ";") return cont(forspec2);
558 if (type == "variable") return cont(formaybeinof);
559 return pass(expression, expect(";"), forspec2);
560 }
561 function formaybeinof(_type, value) {
562 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
563 return cont(maybeoperatorComma, forspec2);
697 if (type == "var") return cont(vardef, forspec2);
698 if (type == "variable") return cont(forspec2);
699 return pass(forspec2)
564 700 }
565 701 function forspec2(type, value) {
566 if (type == ";") return cont(forspec3);
567 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
568 return pass(expression, expect(";"), forspec3);
569 }
570 function forspec3(type) {
571 if (type != ")") cont(expression);
702 if (type == ")") return cont()
703 if (type == ";") return cont(forspec2)
704 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
705 return pass(expression, forspec2)
572 706 }
573 707 function functiondef(type, value) {
574 708 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
575 709 if (type == "variable") {register(value); return cont(functiondef);}
576 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
710 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
711 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
712 }
713 function functiondecl(type, value) {
714 if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
715 if (type == "variable") {register(value); return cont(functiondecl);}
716 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
717 if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
577 718 }
578 function funarg(type) {
719 function typename(type, value) {
720 if (type == "keyword" || type == "variable") {
721 cx.marked = "type"
722 return cont(typename)
723 } else if (value == "<") {
724 return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
725 }
726 }
727 function funarg(type, value) {
728 if (value == "@") cont(expression, funarg)
579 729 if (type == "spread") return cont(funarg);
580 return pass(pattern, maybetype, maybedefault);
730 if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
731 if (isTS && type == "this") return cont(maybetype, maybeAssign)
732 return pass(pattern, maybetype, maybeAssign);
733 }
734 function classExpression(type, value) {
735 // Class expressions may have an optional name.
736 if (type == "variable") return className(type, value);
737 return classNameAfter(type, value);
581 738 }
582 739 function className(type, value) {
583 740 if (type == "variable") {register(value); return cont(classNameAfter);}
584 741 }
585 742 function classNameAfter(type, value) {
586 if (value == "extends") return cont(expression, classNameAfter);
743 if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
744 if (value == "extends" || value == "implements" || (isTS && type == ",")) {
745 if (value == "implements") cx.marked = "keyword";
746 return cont(isTS ? typeexpr : expression, classNameAfter);
747 }
587 748 if (type == "{") return cont(pushlex("}"), classBody, poplex);
588 749 }
589 750 function classBody(type, value) {
751 if (type == "async" ||
752 (type == "variable" &&
753 (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
754 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
755 cx.marked = "keyword";
756 return cont(classBody);
757 }
590 758 if (type == "variable" || cx.style == "keyword") {
591 if (value == "static") {
592 cx.marked = "keyword";
593 return cont(classBody);
594 }
595 759 cx.marked = "property";
596 if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
597 return cont(functiondef, classBody);
760 return cont(isTS ? classfield : functiondef, classBody);
598 761 }
762 if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
763 if (type == "[")
764 return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
599 765 if (value == "*") {
600 766 cx.marked = "keyword";
601 767 return cont(classBody);
602 768 }
603 if (type == ";") return cont(classBody);
769 if (isTS && type == "(") return pass(functiondecl, classBody)
770 if (type == ";" || type == ",") return cont(classBody);
604 771 if (type == "}") return cont();
772 if (value == "@") return cont(expression, classBody)
605 773 }
606 function classGetterSetter(type) {
607 if (type != "variable") return pass();
608 cx.marked = "property";
609 return cont();
774 function classfield(type, value) {
775 if (value == "?") return cont(classfield)
776 if (type == ":") return cont(typeexpr, maybeAssign)
777 if (value == "=") return cont(expressionNoComma)
778 var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
779 return pass(isInterface ? functiondecl : functiondef)
610 780 }
611 function afterExport(_type, value) {
781 function afterExport(type, value) {
612 782 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
613 783 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
784 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
614 785 return pass(statement);
615 786 }
787 function exportField(type, value) {
788 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
789 if (type == "variable") return pass(expressionNoComma, exportField);
790 }
616 791 function afterImport(type) {
617 792 if (type == "string") return cont();
618 return pass(importSpec, maybeFrom);
793 if (type == "(") return pass(expression);
794 return pass(importSpec, maybeMoreImports, maybeFrom);
619 795 }
620 796 function importSpec(type, value) {
621 797 if (type == "{") return contCommasep(importSpec, "}");
@@ -623,6 +799,9 b' CodeMirror.defineMode("javascript", func'
623 799 if (value == "*") cx.marked = "keyword";
624 800 return cont(maybeAs);
625 801 }
802 function maybeMoreImports(type) {
803 if (type == ",") return cont(importSpec, maybeMoreImports)
804 }
626 805 function maybeAs(_type, value) {
627 806 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
628 807 }
@@ -631,16 +810,13 b' CodeMirror.defineMode("javascript", func'
631 810 }
632 811 function arrayLiteral(type) {
633 812 if (type == "]") return cont();
634 return pass(expressionNoComma, maybeArrayComprehension);
635 }
636 function maybeArrayComprehension(type) {
637 if (type == "for") return pass(comprehension, expect("]"));
638 if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
639 813 return pass(commasep(expressionNoComma, "]"));
640 814 }
641 function comprehension(type) {
642 if (type == "for") return cont(forspec, comprehension);
643 if (type == "if") return cont(expression, comprehension);
815 function enumdef() {
816 return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
817 }
818 function enummember() {
819 return pass(pattern, maybeAssign);
644 820 }
645 821
646 822 function isContinuedStatement(state, textAfter) {
@@ -649,6 +825,12 b' CodeMirror.defineMode("javascript", func'
649 825 /[,.]/.test(textAfter.charAt(0));
650 826 }
651 827
828 function expressionAllowed(stream, state, backUp) {
829 return state.tokenize == tokenBase &&
830 /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
831 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
832 }
833
652 834 // Interface
653 835
654 836 return {
@@ -659,7 +841,7 b' CodeMirror.defineMode("javascript", func'
659 841 cc: [],
660 842 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
661 843 localVars: parserConfig.localVars,
662 context: parserConfig.localVars && {vars: parserConfig.localVars},
844 context: parserConfig.localVars && new Context(null, null, false),
663 845 indented: basecolumn || 0
664 846 };
665 847 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
@@ -684,19 +866,23 b' CodeMirror.defineMode("javascript", func'
684 866 indent: function(state, textAfter) {
685 867 if (state.tokenize == tokenComment) return CodeMirror.Pass;
686 868 if (state.tokenize != tokenBase) return 0;
687 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
869 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
688 870 // Kludge to prevent 'maybelse' from blocking lexical scope pops
689 871 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
690 872 var c = state.cc[i];
691 873 if (c == poplex) lexical = lexical.prev;
692 874 else if (c != maybeelse) break;
693 875 }
694 if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
876 while ((lexical.type == "stat" || lexical.type == "form") &&
877 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
878 (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
879 !/^[,\.=+\-*:?[\(]/.test(textAfter))))
880 lexical = lexical.prev;
695 881 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
696 882 lexical = lexical.prev;
697 883 var type = lexical.type, closing = firstChar == type;
698 884
699 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
885 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
700 886 else if (type == "form" && firstChar == "{") return lexical.indented;
701 887 else if (type == "form") return lexical.indented + indentUnit;
702 888 else if (type == "stat")
@@ -710,6 +896,7 b' CodeMirror.defineMode("javascript", func'
710 896 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
711 897 blockCommentStart: jsonMode ? null : "/*",
712 898 blockCommentEnd: jsonMode ? null : "*/",
899 blockCommentContinue: jsonMode ? null : " * ",
713 900 lineComment: jsonMode ? null : "//",
714 901 fold: "brace",
715 902 closeBrackets: "()[]{}''\"\"``",
@@ -719,6 +906,7 b' CodeMirror.defineMode("javascript", func'
719 906 jsonMode: jsonMode,
720 907
721 908 expressionAllowed: expressionAllowed,
909
722 910 skipExpression: function(state) {
723 911 var top = state.cc[state.cc.length - 1]
724 912 if (top == expression || top == expressionNoComma) state.cc.pop()
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -107,7 +107,7 b''
107 107 }
108 108 return "variable";
109 109 } else if (stream.eat("{")) {
110 if (ch = stream.eat("#")) {
110 if (stream.eat("#")) {
111 111 state.incomment = true;
112 112 if(!stream.skipTo("#}")) {
113 113 stream.skipToEnd();
@@ -136,7 +136,11 b''
136 136 },
137 137 token: function (stream, state) {
138 138 return state.tokenize(stream, state);
139 }
139 },
140 blockCommentStart: "{#",
141 blockCommentEnd: "#}"
140 142 };
141 143 });
144
145 CodeMirror.defineMIME("text/jinja2", "jinja2");
142 146 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,14 +25,14 b''
25 25 context.prev && copyContext(context.prev))
26 26 }
27 27
28 CodeMirror.defineMode("jsx", function(config) {
29 var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
30 var jsMode = CodeMirror.getMode(config, "javascript")
28 CodeMirror.defineMode("jsx", function(config, modeConfig) {
29 var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
30 var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
31 31
32 32 function flatXMLIndent(state) {
33 33 var tagName = state.tagName
34 34 state.tagName = null
35 var result = xmlMode.indent(state, "")
35 var result = xmlMode.indent(state, "", "")
36 36 state.tagName = tagName
37 37 return result
38 38 }
@@ -105,7 +105,7 b''
105 105 function jsToken(stream, state, cx) {
106 106 if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
107 107 jsMode.skipExpression(cx.state)
108 state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
108 state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "", "")),
109 109 xmlMode, 0, state.context)
110 110 return null
111 111 }
@@ -144,4 +144,5 b''
144 144 }, "xml", "javascript")
145 145
146 146 CodeMirror.defineMIME("text/jsx", "jsx")
147 })
147 CodeMirror.defineMIME("text/typescript-jsx", {name: "jsx", base: {name: "javascript", typescript: true}})
148 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,61 +11,78 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 CodeMirror.defineMode("julia", function(_conf, parserConf) {
15 var ERRORCLASS = 'error';
16
17 function wordRegexp(words) {
18 return new RegExp("^((" + words.join(")|(") + "))\\b");
14 CodeMirror.defineMode("julia", function(config, parserConf) {
15 function wordRegexp(words, end) {
16 if (typeof end === "undefined") { end = "\\b"; }
17 return new RegExp("^((" + words.join(")|(") + "))" + end);
19 18 }
20 19
21 var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/;
20 var octChar = "\\\\[0-7]{1,3}";
21 var hexChar = "\\\\x[A-Fa-f0-9]{1,2}";
22 var sChar = "\\\\[abefnrtv0%?'\"\\\\]";
23 var uChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])";
24
25 var operators = parserConf.operators || wordRegexp([
26 "[<>]:", "[<>=]=", "<<=?", ">>>?=?", "=>", "->", "\\/\\/",
27 "[\\\\%*+\\-<>!=\\/^|&\\u00F7\\u22BB]=?", "\\?", "\\$", "~", ":",
28 "\\u00D7", "\\u2208", "\\u2209", "\\u220B", "\\u220C", "\\u2218",
29 "\\u221A", "\\u221B", "\\u2229", "\\u222A", "\\u2260", "\\u2264",
30 "\\u2265", "\\u2286", "\\u2288", "\\u228A", "\\u22C5",
31 "\\b(in|isa)\\b(?!\.?\\()"], "");
22 32 var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
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"];
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'];
27 var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf'];
33 var identifiers = parserConf.identifiers ||
34 /^[_A-Za-z\u00A1-\u2217\u2219-\uFFFF][\w\u00A1-\u2217\u2219-\uFFFF]*!*/;
35
36 var chars = wordRegexp([octChar, hexChar, sChar, uChar], "'");
37
38 var openersList = ["begin", "function", "type", "struct", "immutable", "let",
39 "macro", "for", "while", "quote", "if", "else", "elseif", "try",
40 "finally", "catch", "do"];
28 41
29 //var stringPrefixes = new RegExp("^[br]?('|\")")
30 var stringPrefixes = /^(`|'|"{3}|([brv]?"))/;
31 var keywords = wordRegexp(keywordList);
32 var builtins = wordRegexp(builtinList);
33 var openers = wordRegexp(blockOpeners);
34 var closers = wordRegexp(blockClosers);
35 var macro = /^@[_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]+({[^}]*}+)*/;
42 var closersList = ["end", "else", "elseif", "catch", "finally"];
43
44 var keywordsList = ["if", "else", "elseif", "while", "for", "begin", "let",
45 "end", "do", "try", "catch", "finally", "return", "break", "continue",
46 "global", "local", "const", "export", "import", "importall", "using",
47 "function", "where", "macro", "module", "baremodule", "struct", "type",
48 "mutable", "immutable", "quote", "typealias", "abstract", "primitive",
49 "bitstype"];
50
51 var builtinsList = ["true", "false", "nothing", "NaN", "Inf"];
52
53 CodeMirror.registerHelper("hintWords", "julia", keywordsList.concat(builtinsList));
54
55 var openers = wordRegexp(openersList);
56 var closers = wordRegexp(closersList);
57 var keywords = wordRegexp(keywordsList);
58 var builtins = wordRegexp(builtinsList);
59
60 var macro = /^@[_A-Za-z][\w]*/;
61 var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/;
62 var stringPrefixes = /^(`|([_A-Za-z\u00A1-\uFFFF]*"("")?))/;
38 63
39 64 function inArray(state) {
40 var ch = currentScope(state);
41 if (ch == '[') {
42 return true;
43 }
44 return false;
65 return (state.nestedArrays > 0);
66 }
67
68 function inGenerator(state) {
69 return (state.nestedGenerators > 0);
45 70 }
46 71
47 function currentScope(state) {
48 if (state.scopes.length == 0) {
72 function currentScope(state, n) {
73 if (typeof(n) === "undefined") { n = 0; }
74 if (state.scopes.length <= n) {
49 75 return null;
50 76 }
51 return state.scopes[state.scopes.length - 1];
77 return state.scopes[state.scopes.length - (n + 1)];
52 78 }
53 79
54 80 // tokenizers
55 81 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';
82 // Handle multiline comments
83 if (stream.match(/^#=/, false)) {
84 state.tokenize = tokenComment;
85 return state.tokenize(stream, state);
69 86 }
70 87
71 88 // Handle scope changes
@@ -74,14 +91,17 b' CodeMirror.defineMode("julia", function('
74 91 leavingExpr = false;
75 92 }
76 93 state.leavingExpr = false;
94
77 95 if (leavingExpr) {
78 96 if (stream.match(/^'+/)) {
79 return 'operator';
97 return "operator";
80 98 }
81 99 }
82 100
83 if (stream.match(/^\.{2,3}/)) {
84 return 'operator';
101 if (stream.match(/\.{4,}/)) {
102 return "error";
103 } else if (stream.match(/\.{1,3}/)) {
104 return "operator";
85 105 }
86 106
87 107 if (stream.eatSpace()) {
@@ -93,105 +113,101 b' CodeMirror.defineMode("julia", function('
93 113 // Handle single line comments
94 114 if (ch === '#') {
95 115 stream.skipToEnd();
96 return 'comment';
116 return "comment";
97 117 }
98 118
99 119 if (ch === '[') {
100 120 state.scopes.push('[');
121 state.nestedArrays++;
101 122 }
102 123
103 var scope = currentScope(state);
124 if (ch === '(') {
125 state.scopes.push('(');
126 state.nestedGenerators++;
127 }
104 128
105 if (scope == '[' && ch === ']') {
129 if (inArray(state) && ch === ']') {
130 if (currentScope(state) === "if") { state.scopes.pop(); }
131 while (currentScope(state) === "for") { state.scopes.pop(); }
106 132 state.scopes.pop();
133 state.nestedArrays--;
107 134 state.leavingExpr = true;
108 135 }
109 136
110 if (scope == '(' && ch === ')') {
137 if (inGenerator(state) && ch === ')') {
138 if (currentScope(state) === "if") { state.scopes.pop(); }
139 while (currentScope(state) === "for") { state.scopes.pop(); }
111 140 state.scopes.pop();
141 state.nestedGenerators--;
112 142 state.leavingExpr = true;
113 143 }
114 144
145 if (inArray(state)) {
146 if (state.lastToken == "end" && stream.match(/^:/)) {
147 return "operator";
148 }
149 if (stream.match(/^end/)) {
150 return "number";
151 }
152 }
153
115 154 var match;
116 if (!inArray(state) && (match=stream.match(openers, false))) {
117 state.scopes.push(match);
155 if (match = stream.match(openers, false)) {
156 state.scopes.push(match[0]);
118 157 }
119 158
120 if (!inArray(state) && stream.match(closers, false)) {
159 if (stream.match(closers, false)) {
121 160 state.scopes.pop();
122 161 }
123 162
124 if (inArray(state)) {
125 if (state.lastToken == 'end' && stream.match(/^:/)) {
126 return 'operator';
127 }
128 if (stream.match(/^end/)) {
129 return 'number';
130 }
163 // Handle type annotations
164 if (stream.match(/^::(?![:\$])/)) {
165 state.tokenize = tokenAnnotation;
166 return state.tokenize(stream, state);
131 167 }
132 168
133 if (stream.match(/^=>/)) {
134 return 'operator';
169 // Handle symbols
170 if (!leavingExpr && stream.match(symbol) ||
171 stream.match(/:([<>]:|<<=?|>>>?=?|->|\/\/|\.{2,3}|[\.\\%*+\-<>!\/^|&]=?|[~\?\$])/)) {
172 return "builtin";
173 }
174
175 // Handle parametric types
176 //if (stream.match(/^{[^}]*}(?=\()/)) {
177 // return "builtin";
178 //}
179
180 // Handle operators and Delimiters
181 if (stream.match(operators)) {
182 return "operator";
135 183 }
136 184
137 185 // Handle Number Literals
138 if (stream.match(/^[0-9\.]/, false)) {
186 if (stream.match(/^\.?\d/, false)) {
139 187 var imMatcher = RegExp(/^im\b/);
140 var floatLiteral = false;
188 var numberLiteral = false;
141 189 // Floats
142 if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
143 if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
144 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
145 if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { floatLiteral = true; }
146 if (floatLiteral) {
147 // Float literals may be "imaginary"
148 stream.match(imMatcher);
149 state.leavingExpr = true;
150 return 'number';
151 }
190 if (stream.match(/^(?:(?:\d[_\d]*)?\.(?!\.)(?:\d[_\d]*)?|\d[_\d]*\.(?!\.)(?:\d[_\d]*))?([Eef][\+\-]?[_\d]+)?/i)) { numberLiteral = true; }
191 if (stream.match(/^0x\.[0-9a-f_]+p[\+\-]?[_\d]+/i)) { numberLiteral = true; }
152 192 // Integers
153 var intLiteral = false;
154 // Hex
155 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
156 // Binary
157 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
158 // Octal
159 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
160 // Decimal
161 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
162 intLiteral = true;
163 }
193 if (stream.match(/^0x[0-9a-f_]+/i)) { numberLiteral = true; } // Hex
194 if (stream.match(/^0b[01_]+/i)) { numberLiteral = true; } // Binary
195 if (stream.match(/^0o[0-7_]+/i)) { numberLiteral = true; } // Octal
196 if (stream.match(/^[1-9][_\d]*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal
164 197 // Zero by itself with no other piece of number.
165 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
166 if (intLiteral) {
198 if (stream.match(/^0(?![\dx])/i)) { numberLiteral = true; }
199 if (numberLiteral) {
167 200 // Integer literals may be "long"
168 201 stream.match(imMatcher);
169 202 state.leavingExpr = true;
170 return 'number';
203 return "number";
171 204 }
172 205 }
173 206
174 if (stream.match(/^<:/)) {
175 return 'operator';
176 }
177
178 if (stream.match(typeAnnotation)) {
179 return 'builtin';
180 }
181
182 // Handle symbols
183 if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) {
184 return 'builtin';
185 }
186
187 // Handle parametric types
188 if (stream.match(/^{[^}]*}(?=\()/)) {
189 return 'builtin';
190 }
191
192 // Handle operators and Delimiters
193 if (stream.match(operators)) {
194 return 'operator';
207 // Handle Chars
208 if (stream.match(/^'/)) {
209 state.tokenize = tokenChar;
210 return state.tokenize(stream, state);
195 211 }
196 212
197 213 // Handle Strings
@@ -201,7 +217,7 b' CodeMirror.defineMode("julia", function('
201 217 }
202 218
203 219 if (stream.match(macro)) {
204 return 'meta';
220 return "meta";
205 221 }
206 222
207 223 if (stream.match(delimiters)) {
@@ -209,41 +225,40 b' CodeMirror.defineMode("julia", function('
209 225 }
210 226
211 227 if (stream.match(keywords)) {
212 return 'keyword';
228 return "keyword";
213 229 }
214 230
215 231 if (stream.match(builtins)) {
216 return 'builtin';
232 return "builtin";
217 233 }
218 234
219 var isDefinition = state.isDefinition ||
220 state.lastToken == 'function' ||
221 state.lastToken == 'macro' ||
222 state.lastToken == 'type' ||
223 state.lastToken == 'immutable';
235 var isDefinition = state.isDefinition || state.lastToken == "function" ||
236 state.lastToken == "macro" || state.lastToken == "type" ||
237 state.lastToken == "struct" || state.lastToken == "immutable";
224 238
225 239 if (stream.match(identifiers)) {
226 240 if (isDefinition) {
227 241 if (stream.peek() === '.') {
228 242 state.isDefinition = true;
229 return 'variable';
243 return "variable";
230 244 }
231 245 state.isDefinition = false;
232 return 'def';
246 return "def";
233 247 }
234 248 if (stream.match(/^({[^}]*})*\(/, false)) {
235 return callOrDef(stream, state);
249 state.tokenize = tokenCallOrDef;
250 return state.tokenize(stream, state);
236 251 }
237 252 state.leavingExpr = true;
238 return 'variable';
253 return "variable";
239 254 }
240 255
241 256 // Handle non-detected items
242 257 stream.next();
243 return ERRORCLASS;
258 return "error";
244 259 }
245 260
246 function callOrDef(stream, state) {
261 function tokenCallOrDef(stream, state) {
247 262 var match = stream.match(/^(\(\s*)/);
248 263 if (match) {
249 264 if (state.firstParenPos < 0)
@@ -255,13 +270,14 b' CodeMirror.defineMode("julia", function('
255 270 state.scopes.pop();
256 271 state.charsAdvanced += 1;
257 272 if (state.scopes.length <= state.firstParenPos) {
258 var isDefinition = stream.match(/^\s*?=(?!=)/, false);
273 var isDefinition = stream.match(/^(\s*where\s+[^\s=]+)*\s*?=(?!=)/, false);
259 274 stream.backUp(state.charsAdvanced);
260 275 state.firstParenPos = -1;
261 276 state.charsAdvanced = 0;
277 state.tokenize = tokenBase;
262 278 if (isDefinition)
263 return 'def';
264 return 'builtin';
279 return "def";
280 return "builtin";
265 281 }
266 282 }
267 283 // Unfortunately javascript does not support multiline strings, so we have
@@ -269,48 +285,93 b' CodeMirror.defineMode("julia", function('
269 285 // over two or more lines.
270 286 if (stream.match(/^$/g, false)) {
271 287 stream.backUp(state.charsAdvanced);
272 while (state.scopes.length > state.firstParenPos + 1)
288 while (state.scopes.length > state.firstParenPos)
273 289 state.scopes.pop();
274 290 state.firstParenPos = -1;
275 291 state.charsAdvanced = 0;
276 return 'builtin';
292 state.tokenize = tokenBase;
293 return "builtin";
277 294 }
278 295 state.charsAdvanced += stream.match(/^([^()]*)/)[1].length;
279 return callOrDef(stream, state);
296 return state.tokenize(stream, state);
297 }
298
299 function tokenAnnotation(stream, state) {
300 stream.match(/.*?(?=,|;|{|}|\(|\)|=|$|\s)/);
301 if (stream.match(/^{/)) {
302 state.nestedParameters++;
303 } else if (stream.match(/^}/) && state.nestedParameters > 0) {
304 state.nestedParameters--;
305 }
306 if (state.nestedParameters > 0) {
307 stream.match(/.*?(?={|})/) || stream.next();
308 } else if (state.nestedParameters == 0) {
309 state.tokenize = tokenBase;
310 }
311 return "builtin";
312 }
313
314 function tokenComment(stream, state) {
315 if (stream.match(/^#=/)) {
316 state.nestedComments++;
317 }
318 if (!stream.match(/.*?(?=(#=|=#))/)) {
319 stream.skipToEnd();
320 }
321 if (stream.match(/^=#/)) {
322 state.nestedComments--;
323 if (state.nestedComments == 0)
324 state.tokenize = tokenBase;
325 }
326 return "comment";
327 }
328
329 function tokenChar(stream, state) {
330 var isChar = false, match;
331 if (stream.match(chars)) {
332 isChar = true;
333 } else if (match = stream.match(/\\u([a-f0-9]{1,4})(?=')/i)) {
334 var value = parseInt(match[1], 16);
335 if (value <= 55295 || value >= 57344) { // (U+0,U+D7FF), (U+E000,U+FFFF)
336 isChar = true;
337 stream.next();
338 }
339 } else if (match = stream.match(/\\U([A-Fa-f0-9]{5,8})(?=')/)) {
340 var value = parseInt(match[1], 16);
341 if (value <= 1114111) { // U+10FFFF
342 isChar = true;
343 stream.next();
344 }
345 }
346 if (isChar) {
347 state.leavingExpr = true;
348 state.tokenize = tokenBase;
349 return "string";
350 }
351 if (!stream.match(/^[^']+(?=')/)) { stream.skipToEnd(); }
352 if (stream.match(/^'/)) { state.tokenize = tokenBase; }
353 return "error";
280 354 }
281 355
282 356 function tokenStringFactory(delimiter) {
283 while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
284 delimiter = delimiter.substr(1);
357 if (delimiter.substr(-3) === '"""') {
358 delimiter = '"""';
359 } else if (delimiter.substr(-1) === '"') {
360 delimiter = '"';
285 361 }
286 var singleline = delimiter == "'";
287 var OUTCLASS = 'string';
288
289 362 function tokenString(stream, state) {
290 while (!stream.eol()) {
291 stream.eatWhile(/[^'"\\]/);
292 if (stream.eat('\\')) {
293 stream.next();
294 if (singleline && stream.eol()) {
295 return OUTCLASS;
296 }
297 } else if (stream.match(delimiter)) {
298 state.tokenize = tokenBase;
299 return OUTCLASS;
300 } else {
301 stream.eat(/['"]/);
302 }
363 if (stream.eat('\\')) {
364 stream.next();
365 } else if (stream.match(delimiter)) {
366 state.tokenize = tokenBase;
367 state.leavingExpr = true;
368 return "string";
369 } else {
370 stream.eat(/[`"]/);
303 371 }
304 if (singleline) {
305 if (parserConf.singleLineStringErrors) {
306 return ERRORCLASS;
307 } else {
308 state.tokenize = tokenBase;
309 }
310 }
311 return OUTCLASS;
372 stream.eatWhile(/[^\\`"]/);
373 return "string";
312 374 }
313 tokenString.isString = true;
314 375 return tokenString;
315 376 }
316 377
@@ -322,6 +383,10 b' CodeMirror.defineMode("julia", function('
322 383 lastToken: null,
323 384 leavingExpr: false,
324 385 isDefinition: false,
386 nestedArrays: 0,
387 nestedComments: 0,
388 nestedGenerators: 0,
389 nestedParameters: 0,
325 390 charsAdvanced: 0,
326 391 firstParenPos: -1
327 392 };
@@ -335,25 +400,25 b' CodeMirror.defineMode("julia", function('
335 400 state.lastToken = current;
336 401 }
337 402
338 // Handle '.' connected identifiers
339 if (current === '.') {
340 style = stream.match(identifiers, false) || stream.match(macro, false) ||
341 stream.match(/\(/, false) ? 'operator' : ERRORCLASS;
342 }
343 403 return style;
344 404 },
345 405
346 406 indent: function(state, textAfter) {
347 407 var delta = 0;
348 if (textAfter == "end" || textAfter == "]" || textAfter == "}" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") {
408 if ( textAfter === ']' || textAfter === ')' || textAfter === "end" ||
409 textAfter === "else" || textAfter === "catch" || textAfter === "elseif" ||
410 textAfter === "finally" ) {
349 411 delta = -1;
350 412 }
351 return (state.scopes.length + delta) * _conf.indentUnit;
413 return (state.scopes.length + delta) * config.indentUnit;
352 414 },
353 415
416 electricInput: /\b(end|else|catch|finally)\b/,
417 blockCommentStart: "#=",
418 blockCommentEnd: "=#",
354 419 lineComment: "#",
355 fold: "indent",
356 electricChars: "edlsifyh]}"
420 closeBrackets: "()[]{}\"\"",
421 fold: "indent"
357 422 };
358 423 return external;
359 424 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**
5 5 * Link to the project's GitHub page:
@@ -50,7 +50,7 b''
50 50 startState: function(){
51 51 return {
52 52 next: 'start',
53 lastToken: null
53 lastToken: {style: null, indent: 0, content: ""}
54 54 };
55 55 },
56 56 token: function(stream, state){
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's
5 5 // CodeMirror 1 mode.
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,8 +13,8 b''
13 13
14 14 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
15 15
16 var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
17 var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
16 var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
17 var htmlModeMissing = htmlMode.name == "null"
18 18
19 19 function getMode(name) {
20 20 if (CodeMirror.findModeByName) {
@@ -35,15 +35,6 b' CodeMirror.defineMode("markdown", functi'
35 35 if (modeCfg.maxBlockquoteDepth === undefined)
36 36 modeCfg.maxBlockquoteDepth = 0;
37 37
38 // Should underscores in words open/close em/strong?
39 if (modeCfg.underscoresBreakWords === undefined)
40 modeCfg.underscoresBreakWords = true;
41
42 // Use `fencedCodeBlocks` to configure fenced code blocks. false to
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).
46
47 38 // Turn on task lists? ("- [ ] " and "- [x] ")
48 39 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
49 40
@@ -51,12 +42,19 b' CodeMirror.defineMode("markdown", functi'
51 42 if (modeCfg.strikethrough === undefined)
52 43 modeCfg.strikethrough = false;
53 44
45 if (modeCfg.emoji === undefined)
46 modeCfg.emoji = false;
47
48 if (modeCfg.fencedCodeBlockHighlighting === undefined)
49 modeCfg.fencedCodeBlockHighlighting = true;
50
51 if (modeCfg.xml === undefined)
52 modeCfg.xml = true;
53
54 54 // Allow token types to be overridden by user-provided token types.
55 55 if (modeCfg.tokenTypeOverrides === undefined)
56 56 modeCfg.tokenTypeOverrides = {};
57 57
58 var codeDepth = 0;
59
60 58 var tokenTypes = {
61 59 header: "header",
62 60 code: "comment",
@@ -65,7 +63,9 b' CodeMirror.defineMode("markdown", functi'
65 63 list2: "variable-3",
66 64 list3: "keyword",
67 65 hr: "hr",
68 image: "tag",
66 image: "image",
67 imageAltText: "image-alt-text",
68 imageMarker: "image-marker",
69 69 formatting: "formatting",
70 70 linkInline: "link",
71 71 linkEmail: "link",
@@ -73,7 +73,8 b' CodeMirror.defineMode("markdown", functi'
73 73 linkHref: "string",
74 74 em: "em",
75 75 strong: "strong",
76 strikethrough: "strikethrough"
76 strikethrough: "strikethrough",
77 emoji: "builtin"
77 78 };
78 79
79 80 for (var tokenType in tokenTypes) {
@@ -83,14 +84,15 b' CodeMirror.defineMode("markdown", functi'
83 84 }
84 85
85 86 var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
86 , ulRE = /^[*\-+]\s+/
87 , olRE = /^[0-9]+([.)])\s+/
88 , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
87 , listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
88 , taskListRE = /^\[(x| )\](?=\s)/i // Must follow listRE
89 89 , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
90 90 , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
91 , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
92 , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
93 ")[ \\t]*([\\w+#]*)");
91 , textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
92 , fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)[^\n`]*$/
93 , linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
94 , punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/
95 , expandedTab = " " // CommonMark specifies tab as 4 spaces
94 96
95 97 function switchInline(stream, state, f) {
96 98 state.f = state.inline = f;
@@ -111,6 +113,8 b' CodeMirror.defineMode("markdown", functi'
111 113 function blankLine(state) {
112 114 // Reset linkTitle state
113 115 state.linkTitle = false;
116 state.linkHref = false;
117 state.linkText = false;
114 118 // Reset EM state
115 119 state.em = false;
116 120 // Reset STRONG state
@@ -121,102 +125,154 b' CodeMirror.defineMode("markdown", functi'
121 125 state.quote = 0;
122 126 // Reset state.indentedCode
123 127 state.indentedCode = false;
124 if (!htmlFound && state.f == htmlBlock) {
125 state.f = inlineNormal;
126 state.block = blockNormal;
128 if (state.f == htmlBlock) {
129 var exit = htmlModeMissing
130 if (!exit) {
131 var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
132 exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
133 (!inner.state.context && inner.state.tokenize.isInText)
134 }
135 if (exit) {
136 state.f = inlineNormal;
137 state.block = blockNormal;
138 state.htmlState = null;
139 }
127 140 }
128 141 // Reset state.trailingSpace
129 142 state.trailingSpace = 0;
130 143 state.trailingSpaceNewLine = false;
131 144 // Mark this line as blank
132 145 state.prevLine = state.thisLine
133 state.thisLine = null
146 state.thisLine = {stream: null}
134 147 return null;
135 148 }
136 149
137 150 function blockNormal(stream, state) {
138
139 var sol = stream.sol();
140
141 var prevLineIsList = state.list !== false,
142 prevLineIsIndentedCode = state.indentedCode;
151 var firstTokenOnLine = stream.column() === state.indentation;
152 var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);
153 var prevLineIsIndentedCode = state.indentedCode;
154 var prevLineIsHr = state.prevLine.hr;
155 var prevLineIsList = state.list !== false;
156 var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;
143 157
144 158 state.indentedCode = false;
145 159
146 if (prevLineIsList) {
147 if (state.indentationDiff >= 0) { // Continued list
148 if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
149 state.indentation -= state.indentationDiff;
150 }
160 var lineIndentation = state.indentation;
161 // compute once per line (on first token)
162 if (state.indentationDiff === null) {
163 state.indentationDiff = state.indentation;
164 if (prevLineIsList) {
151 165 state.list = null;
152 } else if (state.indentation > 0) {
153 state.list = null;
154 state.listDepth = Math.floor(state.indentation / 4);
155 } else { // No longer a list
156 state.list = false;
157 state.listDepth = 0;
166 // While this list item's marker's indentation is less than the deepest
167 // list item's content's indentation,pop the deepest list item
168 // indentation off the stack, and update block indentation state
169 while (lineIndentation < state.listStack[state.listStack.length - 1]) {
170 state.listStack.pop();
171 if (state.listStack.length) {
172 state.indentation = state.listStack[state.listStack.length - 1];
173 // less than the first list's indent -> the line is no longer a list
174 } else {
175 state.list = false;
176 }
177 }
178 if (state.list !== false) {
179 state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
180 }
158 181 }
159 182 }
160 183
184 // not comprehensive (currently only for setext detection purposes)
185 var allowsInlineContinuation = (
186 !prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&
187 (!prevLineIsList || !prevLineIsIndentedCode) &&
188 !state.prevLine.fencedCodeEnd
189 );
190
191 var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&
192 state.indentation <= maxNonCodeIndentation && stream.match(hrRE);
193
161 194 var match = null;
162 if (state.indentationDiff >= 4) {
195 if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||
196 state.prevLine.header || prevLineLineIsEmpty)) {
163 197 stream.skipToEnd();
164 if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
165 state.indentation -= 4;
166 state.indentedCode = true;
167 return tokenTypes.code;
168 } else {
169 return null;
170 }
198 state.indentedCode = true;
199 return tokenTypes.code;
171 200 } else if (stream.eatSpace()) {
172 201 return null;
173 } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
202 } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
203 state.quote = 0;
174 204 state.header = match[1].length;
205 state.thisLine.header = true;
175 206 if (modeCfg.highlightFormatting) state.formatting = "header";
176 207 state.f = state.inline;
177 208 return getType(state);
178 } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
179 !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
180 state.header = match[0].charAt(0) == '=' ? 1 : 2;
181 if (modeCfg.highlightFormatting) state.formatting = "header";
182 state.f = state.inline;
183 return getType(state);
184 } else if (stream.eat('>')) {
185 state.quote = sol ? 1 : state.quote + 1;
209 } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {
210 state.quote = firstTokenOnLine ? 1 : state.quote + 1;
186 211 if (modeCfg.highlightFormatting) state.formatting = "quote";
187 212 stream.eatSpace();
188 213 return getType(state);
189 } else if (stream.peek() === '[') {
190 return switchInline(stream, state, footnoteLink);
191 } else if (stream.match(hrRE, true)) {
192 state.hr = true;
193 return tokenTypes.hr;
194 } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
195 var listType = null;
196 if (stream.match(ulRE, true)) {
197 listType = 'ul';
198 } else {
199 stream.match(olRE, true);
200 listType = 'ol';
201 }
202 state.indentation = stream.column() + stream.current().length;
214 } else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {
215 var listType = match[1] ? "ol" : "ul";
216
217 state.indentation = lineIndentation + stream.current().length;
203 218 state.list = true;
204 state.listDepth++;
219 state.quote = 0;
220
221 // Add this list item's content's indentation to the stack
222 state.listStack.push(state.indentation);
223 // Reset inline styles which shouldn't propagate aross list items
224 state.em = false;
225 state.strong = false;
226 state.code = false;
227 state.strikethrough = false;
228
205 229 if (modeCfg.taskLists && stream.match(taskListRE, false)) {
206 230 state.taskList = true;
207 231 }
208 232 state.f = state.inline;
209 233 if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
210 234 return getType(state);
211 } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
212 state.fencedChars = match[1]
235 } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {
236 state.quote = 0;
237 state.fencedEndRE = new RegExp(match[1] + "+ *$");
213 238 // try switching mode
214 state.localMode = getMode(match[2]);
215 if (state.localMode) state.localState = state.localMode.startState();
239 state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2]);
240 if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
216 241 state.f = state.block = local;
217 242 if (modeCfg.highlightFormatting) state.formatting = "code-block";
218 state.code = true;
243 state.code = -1
219 244 return getType(state);
245 // SETEXT has lowest block-scope precedence after HR, so check it after
246 // the others (code, blockquote, list...)
247 } else if (
248 // if setext set, indicates line after ---/===
249 state.setext || (
250 // line before ---/===
251 (!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&
252 !state.code && !isHr && !linkDefRE.test(stream.string) &&
253 (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))
254 )
255 ) {
256 if ( !state.setext ) {
257 state.header = match[0].charAt(0) == '=' ? 1 : 2;
258 state.setext = state.header;
259 } else {
260 state.header = state.setext;
261 // has no effect on type so we can reset it now
262 state.setext = 0;
263 stream.skipToEnd();
264 if (modeCfg.highlightFormatting) state.formatting = "header";
265 }
266 state.thisLine.header = true;
267 state.f = state.inline;
268 return getType(state);
269 } else if (isHr) {
270 stream.skipToEnd();
271 state.hr = true;
272 state.thisLine.hr = true;
273 return tokenTypes.hr;
274 } else if (stream.peek() === '[') {
275 return switchInline(stream, state, footnoteLink);
220 276 }
221 277
222 278 return switchInline(stream, state, state.inline);
@@ -224,21 +280,35 b' CodeMirror.defineMode("markdown", functi'
224 280
225 281 function htmlBlock(stream, state) {
226 282 var style = htmlMode.token(stream, state.htmlState);
227 if ((htmlFound && state.htmlState.tagStart === null &&
228 (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
229 (state.md_inside && stream.current().indexOf(">") > -1)) {
230 state.f = inlineNormal;
231 state.block = blockNormal;
232 state.htmlState = null;
283 if (!htmlModeMissing) {
284 var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
285 if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
286 (!inner.state.context && inner.state.tokenize.isInText)) ||
287 (state.md_inside && stream.current().indexOf(">") > -1)) {
288 state.f = inlineNormal;
289 state.block = blockNormal;
290 state.htmlState = null;
291 }
233 292 }
234 293 return style;
235 294 }
236 295
237 296 function local(stream, state) {
238 if (state.fencedChars && stream.match(state.fencedChars, false)) {
297 var currListInd = state.listStack[state.listStack.length - 1] || 0;
298 var hasExitedList = state.indentation < currListInd;
299 var maxFencedEndInd = currListInd + 3;
300 if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {
301 if (modeCfg.highlightFormatting) state.formatting = "code-block";
302 var returnType;
303 if (!hasExitedList) returnType = getType(state)
239 304 state.localMode = state.localState = null;
240 state.f = state.block = leavingLocal;
241 return null;
305 state.block = blockNormal;
306 state.f = inlineNormal;
307 state.fencedEndRE = null;
308 state.code = 0
309 state.thisLine.fencedCodeEnd = true;
310 if (hasExitedList) return switchBlock(stream, state, state.block);
311 return returnType;
242 312 } else if (state.localMode) {
243 313 return state.localMode.token(stream, state.localState);
244 314 } else {
@@ -247,18 +317,6 b' CodeMirror.defineMode("markdown", functi'
247 317 }
248 318 }
249 319
250 function leavingLocal(stream, state) {
251 stream.match(state.fencedChars);
252 state.block = blockNormal;
253 state.f = inlineNormal;
254 state.fencedChars = null;
255 if (modeCfg.highlightFormatting) state.formatting = "code-block";
256 state.code = true;
257 var returnType = getType(state);
258 state.code = false;
259 return returnType;
260 }
261
262 320 // Inline
263 321 function getType(state) {
264 322 var styles = [];
@@ -302,8 +360,12 b' CodeMirror.defineMode("markdown", functi'
302 360 if (state.strong) { styles.push(tokenTypes.strong); }
303 361 if (state.em) { styles.push(tokenTypes.em); }
304 362 if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
363 if (state.emoji) { styles.push(tokenTypes.emoji); }
305 364 if (state.linkText) { styles.push(tokenTypes.linkText); }
306 365 if (state.code) { styles.push(tokenTypes.code); }
366 if (state.image) { styles.push(tokenTypes.image); }
367 if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); }
368 if (state.imageMarker) { styles.push(tokenTypes.imageMarker); }
307 369 }
308 370
309 371 if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
@@ -320,7 +382,7 b' CodeMirror.defineMode("markdown", functi'
320 382 }
321 383
322 384 if (state.list !== false) {
323 var listMod = (state.listDepth - 1) % 3;
385 var listMod = (state.listStack.length - 1) % 3;
324 386 if (!listMod) {
325 387 styles.push(tokenTypes.list1);
326 388 } else if (listMod === 1) {
@@ -357,7 +419,7 b' CodeMirror.defineMode("markdown", functi'
357 419 }
358 420
359 421 if (state.taskList) {
360 var taskOpen = stream.match(taskListRE, true)[1] !== "x";
422 var taskOpen = stream.match(taskListRE, true)[1] === " ";
361 423 if (taskOpen) state.taskOpen = true;
362 424 else state.taskClosed = true;
363 425 if (modeCfg.highlightFormatting) state.formatting = "task";
@@ -373,20 +435,8 b' CodeMirror.defineMode("markdown", functi'
373 435 return getType(state);
374 436 }
375 437
376 // Get sol() value now, before character is consumed
377 var sol = stream.sol();
378
379 438 var ch = stream.next();
380 439
381 if (ch === '\\') {
382 stream.next();
383 if (modeCfg.highlightFormatting) {
384 var type = getType(state);
385 var formattingEscape = tokenTypes.formatting + "-escape";
386 return type ? type + " " + formattingEscape : formattingEscape;
387 }
388 }
389
390 440 // Matches link titles present on next line
391 441 if (state.linkTitle) {
392 442 state.linkTitle = false;
@@ -394,7 +444,7 b' CodeMirror.defineMode("markdown", functi'
394 444 if (ch === '(') {
395 445 matchCh = ')';
396 446 }
397 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
447 matchCh = (matchCh+'').replace(/([.?*+^\[\]\\(){}|-])/g, "\\$1");
398 448 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
399 449 if (stream.match(new RegExp(regex), true)) {
400 450 return tokenTypes.linkHref;
@@ -405,43 +455,67 b' CodeMirror.defineMode("markdown", functi'
405 455 if (ch === '`') {
406 456 var previousFormatting = state.formatting;
407 457 if (modeCfg.highlightFormatting) state.formatting = "code";
408 var t = getType(state);
409 var before = stream.pos;
410 458 stream.eatWhile('`');
411 var difference = 1 + stream.pos - before;
412 if (!state.code) {
413 codeDepth = difference;
414 state.code = true;
415 return getType(state);
459 var count = stream.current().length
460 if (state.code == 0 && (!state.quote || count == 1)) {
461 state.code = count
462 return getType(state)
463 } else if (count == state.code) { // Must be exact
464 var t = getType(state)
465 state.code = 0
466 return t
416 467 } else {
417 if (difference === codeDepth) { // Must be exact
418 state.code = false;
419 return t;
420 }
421 state.formatting = previousFormatting;
422 return getType(state);
468 state.formatting = previousFormatting
469 return getType(state)
423 470 }
424 471 } else if (state.code) {
425 472 return getType(state);
426 473 }
427 474
475 if (ch === '\\') {
476 stream.next();
477 if (modeCfg.highlightFormatting) {
478 var type = getType(state);
479 var formattingEscape = tokenTypes.formatting + "-escape";
480 return type ? type + " " + formattingEscape : formattingEscape;
481 }
482 }
483
428 484 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
429 stream.match(/\[[^\]]*\]/);
430 state.inline = state.f = linkHref;
431 return tokenTypes.image;
485 state.imageMarker = true;
486 state.image = true;
487 if (modeCfg.highlightFormatting) state.formatting = "image";
488 return getType(state);
432 489 }
433 490
434 if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
491 if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
492 state.imageMarker = false;
493 state.imageAltText = true
494 if (modeCfg.highlightFormatting) state.formatting = "image";
495 return getType(state);
496 }
497
498 if (ch === ']' && state.imageAltText) {
499 if (modeCfg.highlightFormatting) state.formatting = "image";
500 var type = getType(state);
501 state.imageAltText = false;
502 state.image = false;
503 state.inline = state.f = linkHref;
504 return type;
505 }
506
507 if (ch === '[' && !state.image) {
508 if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
435 509 state.linkText = true;
436 510 if (modeCfg.highlightFormatting) state.formatting = "link";
437 511 return getType(state);
438 512 }
439 513
440 if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
514 if (ch === ']' && state.linkText) {
441 515 if (modeCfg.highlightFormatting) state.formatting = "link";
442 516 var type = getType(state);
443 517 state.linkText = false;
444 state.inline = state.f = linkHref;
518 state.inline = state.f = stream.match(/\(.*?\)| ?\[.*?\]/, false) ? linkHref : inlineNormal
445 519 return type;
446 520 }
447 521
@@ -469,7 +543,7 b' CodeMirror.defineMode("markdown", functi'
469 543 return type + tokenTypes.linkEmail;
470 544 }
471 545
472 if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
546 if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
473 547 var end = stream.string.indexOf(">", stream.pos);
474 548 if (end != -1) {
475 549 var atts = stream.string.substring(stream.start, end);
@@ -480,44 +554,37 b' CodeMirror.defineMode("markdown", functi'
480 554 return switchBlock(stream, state, htmlBlock);
481 555 }
482 556
483 if (ch === '<' && stream.match(/^\/\w*?>/)) {
557 if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) {
484 558 state.md_inside = false;
485 559 return "tag";
486 }
487
488 var ignoreUnderscore = false;
489 if (!modeCfg.underscoresBreakWords) {
490 if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
491 var prevPos = stream.pos - 2;
492 if (prevPos >= 0) {
493 var prevCh = stream.string.charAt(prevPos);
494 if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
495 ignoreUnderscore = true;
496 }
497 }
560 } else if (ch === "*" || ch === "_") {
561 var len = 1, before = stream.pos == 1 ? " " : stream.string.charAt(stream.pos - 2)
562 while (len < 3 && stream.eat(ch)) len++
563 var after = stream.peek() || " "
564 // See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis
565 var leftFlanking = !/\s/.test(after) && (!punctuation.test(after) || /\s/.test(before) || punctuation.test(before))
566 var rightFlanking = !/\s/.test(before) && (!punctuation.test(before) || /\s/.test(after) || punctuation.test(after))
567 var setEm = null, setStrong = null
568 if (len % 2) { // Em
569 if (!state.em && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
570 setEm = true
571 else if (state.em == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
572 setEm = false
498 573 }
499 }
500 if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
501 if (sol && stream.peek() === ' ') {
502 // Do nothing, surrounded by newline and space
503 } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
504 if (modeCfg.highlightFormatting) state.formatting = "strong";
505 var t = getType(state);
506 state.strong = false;
507 return t;
508 } else if (!state.strong && stream.eat(ch)) { // Add STRONG
509 state.strong = ch;
510 if (modeCfg.highlightFormatting) state.formatting = "strong";
511 return getType(state);
512 } else if (state.em === ch) { // Remove EM
513 if (modeCfg.highlightFormatting) state.formatting = "em";
514 var t = getType(state);
515 state.em = false;
516 return t;
517 } else if (!state.em) { // Add EM
518 state.em = ch;
519 if (modeCfg.highlightFormatting) state.formatting = "em";
520 return getType(state);
574 if (len > 1) { // Strong
575 if (!state.strong && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
576 setStrong = true
577 else if (state.strong == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
578 setStrong = false
579 }
580 if (setStrong != null || setEm != null) {
581 if (modeCfg.highlightFormatting) state.formatting = setEm == null ? "strong" : setStrong == null ? "em" : "strong em"
582 if (setEm === true) state.em = ch
583 if (setStrong === true) state.strong = ch
584 var t = getType(state)
585 if (setEm === false) state.em = false
586 if (setStrong === false) state.strong = false
587 return t
521 588 }
522 589 } else if (ch === ' ') {
523 590 if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
@@ -552,8 +619,16 b' CodeMirror.defineMode("markdown", functi'
552 619 }
553 620 }
554 621
622 if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) {
623 state.emoji = true;
624 if (modeCfg.highlightFormatting) state.formatting = "emoji";
625 var retType = getType(state);
626 state.emoji = false;
627 return retType;
628 }
629
555 630 if (ch === ' ') {
556 if (stream.match(/ +$/, false)) {
631 if (stream.match(/^ +$/, false)) {
557 632 state.trailingSpace++;
558 633 } else if (state.trailingSpace) {
559 634 state.trailingSpaceNewLine = true;
@@ -598,6 +673,11 b' CodeMirror.defineMode("markdown", functi'
598 673 return 'error';
599 674 }
600 675
676 var linkRE = {
677 ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
678 "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/
679 }
680
601 681 function getLinkHrefInside(endChar) {
602 682 return function(stream, state) {
603 683 var ch = stream.next();
@@ -610,10 +690,7 b' CodeMirror.defineMode("markdown", functi'
610 690 return returnState;
611 691 }
612 692
613 if (stream.match(inlineRE(endChar), true)) {
614 stream.backUp(1);
615 }
616
693 stream.match(linkRE[endChar])
617 694 state.linkHref = true;
618 695 return getType(state);
619 696 };
@@ -661,25 +738,13 b' CodeMirror.defineMode("markdown", functi'
661 738 return tokenTypes.linkHref + " url";
662 739 }
663 740
664 var savedInlineRE = [];
665 function inlineRE(endChar) {
666 if (!savedInlineRE[endChar]) {
667 // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
668 endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
669 // Match any non-endChar, escaped character, as well as the closing
670 // endChar.
671 savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
672 }
673 return savedInlineRE[endChar];
674 }
675
676 741 var mode = {
677 742 startState: function() {
678 743 return {
679 744 f: blockNormal,
680 745
681 prevLine: null,
682 thisLine: null,
746 prevLine: {stream: null},
747 thisLine: {stream: null},
683 748
684 749 block: blockNormal,
685 750 htmlState: null,
@@ -692,18 +757,21 b' CodeMirror.defineMode("markdown", functi'
692 757 linkText: false,
693 758 linkHref: false,
694 759 linkTitle: false,
760 code: 0,
695 761 em: false,
696 762 strong: false,
697 763 header: 0,
764 setext: 0,
698 765 hr: false,
699 766 taskList: false,
700 767 list: false,
701 listDepth: 0,
768 listStack: [],
702 769 quote: 0,
703 770 trailingSpace: 0,
704 771 trailingSpaceNewLine: false,
705 772 strikethrough: false,
706 fencedChars: null
773 emoji: false,
774 fencedEndRE: null
707 775 };
708 776 },
709 777
@@ -724,22 +792,26 b' CodeMirror.defineMode("markdown", functi'
724 792 inline: s.inline,
725 793 text: s.text,
726 794 formatting: false,
795 linkText: s.linkText,
727 796 linkTitle: s.linkTitle,
797 linkHref: s.linkHref,
728 798 code: s.code,
729 799 em: s.em,
730 800 strong: s.strong,
731 801 strikethrough: s.strikethrough,
802 emoji: s.emoji,
732 803 header: s.header,
804 setext: s.setext,
733 805 hr: s.hr,
734 806 taskList: s.taskList,
735 807 list: s.list,
736 listDepth: s.listDepth,
808 listStack: s.listStack.slice(0),
737 809 quote: s.quote,
738 810 indentedCode: s.indentedCode,
739 811 trailingSpace: s.trailingSpace,
740 812 trailingSpaceNewLine: s.trailingSpaceNewLine,
741 813 md_inside: s.md_inside,
742 fencedChars: s.fencedChars
814 fencedEndRE: s.fencedEndRE
743 815 };
744 816 },
745 817
@@ -748,21 +820,17 b' CodeMirror.defineMode("markdown", functi'
748 820 // Reset state.formatting
749 821 state.formatting = false;
750 822
751 if (stream != state.thisLine) {
752 var forceBlankLine = state.header || state.hr;
753
754 // Reset state.header and state.hr
823 if (stream != state.thisLine.stream) {
755 824 state.header = 0;
756 825 state.hr = false;
757 826
758 if (stream.match(/^\s*$/, true) || forceBlankLine) {
827 if (stream.match(/^\s*$/, true)) {
759 828 blankLine(state);
760 if (!forceBlankLine) return null
761 state.prevLine = null
829 return null;
762 830 }
763 831
764 832 state.prevLine = state.thisLine
765 state.thisLine = stream
833 state.thisLine = {stream: stream}
766 834
767 835 // Reset state.taskList
768 836 state.taskList = false;
@@ -771,14 +839,15 b' CodeMirror.defineMode("markdown", functi'
771 839 state.trailingSpace = 0;
772 840 state.trailingSpaceNewLine = false;
773 841
774 state.f = state.block;
775 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
776 var difference = Math.floor((indentation - state.indentation) / 4) * 4;
777 if (difference > 4) difference = 4;
778 var adjustedIndentation = state.indentation + difference;
779 state.indentationDiff = adjustedIndentation - state.indentation;
780 state.indentation = adjustedIndentation;
781 if (indentation > 0) return null;
842 if (!state.localState) {
843 state.f = state.block;
844 if (state.f != htmlBlock) {
845 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
846 state.indentation = indentation;
847 state.indentationDiff = null;
848 if (indentation > 0) return null;
849 }
850 }
782 851 }
783 852 return state.f(stream, state);
784 853 },
@@ -789,15 +858,26 b' CodeMirror.defineMode("markdown", functi'
789 858 return {state: state, mode: mode};
790 859 },
791 860
861 indent: function(state, textAfter, line) {
862 if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)
863 if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)
864 return CodeMirror.Pass
865 },
866
792 867 blankLine: blankLine,
793 868
794 869 getType: getType,
795 870
871 blockCommentStart: "<!--",
872 blockCommentEnd: "-->",
873 closeBrackets: "()[]{}''\"\"``",
796 874 fold: "markdown"
797 875 };
798 876 return mode;
799 877 }, "xml");
800 878
879 CodeMirror.defineMIME("text/markdown", "markdown");
880
801 881 CodeMirror.defineMIME("text/x-markdown", "markdown");
802 882
803 883 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Mathematica mode copyright (c) 2015 by Calin Barbat
5 5 // Based on code by Patrick Scheibe (halirutan)
@@ -71,12 +71,12 b" CodeMirror.defineMode('mathematica', fun"
71 71 }
72 72
73 73 // usage
74 if (stream.match(/([a-zA-Z\$]+(?:`?[a-zA-Z0-9\$])*::usage)/, true, false)) {
74 if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/, true, false)) {
75 75 return 'meta';
76 76 }
77 77
78 78 // message
79 if (stream.match(/([a-zA-Z\$]+(?:`?[a-zA-Z0-9\$])*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
79 if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
80 80 return 'string-2';
81 81 }
82 82
@@ -126,6 +126,7 b" CodeMirror.defineMode('mathematica', fun"
126 126 }
127 127
128 128 // everything else is an error
129 stream.next(); // advance the stream.
129 130 return 'error';
130 131 }
131 132
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,19 +13,19 b''
13 13
14 14 CodeMirror.modeInfo = [
15 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-encrypted", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["asc", "pgp", "sig"]},
17 17 {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
18 18 {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
19 19 {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
20 {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
20 {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h", "ino"]},
21 21 {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
22 22 {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
23 {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
24 {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
23 {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp", "cs"]},
24 {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]},
25 25 {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
26 26 {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
27 27 {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
28 {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
28 {name: "CoffeeScript", mimes: ["application/vnd.coffeescript", "text/coffeescript", "text/x-coffeescript"], mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
29 29 {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
30 30 {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
31 31 {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
@@ -41,30 +41,33 b''
41 41 {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
42 42 {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
43 43 {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
44 {name: "edn", mime: "application/edn", mode: "clojure", ext: ["edn"]},
44 45 {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
45 46 {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
46 47 {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
47 48 {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
48 49 {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
50 {name: "Esper", mime: "text/x-esper", mode: "sql"},
49 51 {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
52 {name: "FCL", mime: "text/x-fcl", mode: "fcl"},
50 53 {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
51 {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
54 {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90", "f95"]},
52 55 {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
53 56 {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
54 57 {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
55 58 {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
56 59 {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
57 {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
60 {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"], file: /^Jenkinsfile$/},
58 61 {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
59 62 {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
60 63 {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
61 64 {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
62 65 {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
63 66 {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
64 {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
67 {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm", "handlebars", "hbs"], alias: ["xhtml"]},
65 68 {name: "HTTP", mime: "message/http", mode: "http"},
66 69 {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
67 {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
70 {name: "Pug", mime: "text/x-pug", mode: "pug", ext: ["jade", "pug"], alias: ["jade"]},
68 71 {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
69 72 {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
70 73 {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
@@ -72,7 +75,7 b''
72 75 {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
73 76 {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
74 77 {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
75 {name: "Jinja2", mime: "null", mode: "jinja2"},
78 {name: "Jinja2", mime: "text/jinja2", mode: "jinja2", ext: ["j2", "jinja", "jinja2"]},
76 79 {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
77 80 {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
78 81 {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
@@ -81,75 +84,88 b''
81 84 {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
82 85 {name: "mIRC", mime: "text/mirc", mode: "mirc"},
83 86 {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
84 {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
87 {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb", "wl", "wls"]},
85 88 {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
86 {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
89 {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]},
87 90 {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
91 {name: "mbox", mime: "application/mbox", mode: "mbox", ext: ["mbox"]},
88 92 {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
89 93 {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
90 94 {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
91 {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
92 {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
95 {name: "NTriples", mimes: ["application/n-triples", "application/n-quads", "text/n-triples"],
96 mode: "ntriples", ext: ["nt", "nq"]},
97 {name: "Objective-C", mime: "text/x-objectivec", mode: "clike", ext: ["m"], alias: ["objective-c", "objc"]},
98 {name: "Objective-C++", mime: "text/x-objectivec++", mode: "clike", ext: ["mm"], alias: ["objective-c++", "objc++"]},
93 99 {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
94 100 {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
95 101 {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
96 102 {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
97 103 {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
98 104 {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
99 {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
105 {name: "PHP", mimes: ["text/x-php", "application/x-httpd-php", "application/x-httpd-php-open"], mode: "php", ext: ["php", "php3", "php4", "php5", "php7", "phtml"]},
100 106 {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
101 107 {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
102 108 {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
109 {name: "PostgreSQL", mime: "text/x-pgsql", mode: "sql"},
110 {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]},
103 111 {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
104 {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
112 {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]},
113 {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/},
105 114 {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
106 115 {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
107 {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
116 {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r", "R"], alias: ["rscript"]},
108 117 {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
109 118 {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
110 119 {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
111 120 {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
112 121 {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
122 {name: "SAS", mime: "text/x-sas", mode: "sas", ext: ["sas"]},
113 123 {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
114 124 {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
115 125 {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
116 126 {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
117 {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
127 {name: "Shell", mimes: ["text/x-sh", "application/x-sh"], mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
118 128 {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
119 129 {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
120 130 {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
121 131 {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
122 132 {name: "Solr", mime: "text/x-solr", mode: "solr"},
133 {name: "SML", mime: "text/x-sml", mode: "mllike", ext: ["sml", "sig", "fun", "smackspec"]},
123 134 {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
124 135 {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
125 136 {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
126 137 {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
138 {name: "SQLite", mime: "text/x-sqlite", mode: "sql"},
127 139 {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
140 {name: "Stylus", mime: "text/x-styl", mode: "stylus", ext: ["styl"]},
128 141 {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
129 {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
130 142 {name: "sTeX", mime: "text/x-stex", mode: "stex"},
131 {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
132 {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
143 {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx", "tex"], alias: ["tex"]},
144 {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v", "sv", "svh"]},
133 145 {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
134 146 {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
135 147 {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
136 148 {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
137 149 {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
138 150 {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
139 {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
151 {name: "troff", mime: "text/troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
140 152 {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
141 153 {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
142 154 {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
143 155 {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
156 {name: "TypeScript-JSX", mime: "text/typescript-jsx", mode: "jsx", ext: ["tsx"], alias: ["tsx"]},
144 157 {name: "Twig", mime: "text/x-twig", mode: "twig"},
158 {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]},
145 159 {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
146 160 {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
147 161 {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
148 162 {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
149 163 {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
150 {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
164 {name: "Vue.js Component", mimes: ["script/x-vue", "text/x-vue"], mode: "vue", ext: ["vue"]},
165 {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd", "svg"], alias: ["rss", "wsdl", "xsd"]},
151 166 {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
152 {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
167 {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]},
168 {name: "YAML", mimes: ["text/x-yaml", "text/yaml"], mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
153 169 {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
154 170 {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
155 171 {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
@@ -169,6 +185,8 b''
169 185 if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
170 186 if (info.mimes[j] == mime) return info;
171 187 }
188 if (/\+xml$/.test(mime)) return CodeMirror.findModeByMIME("application/xml")
189 if (/\+json$/.test(mime)) return CodeMirror.findModeByMIME("application/json")
172 190 };
173 191
174 192 CodeMirror.findModeByExtension = function(ext) {
@@ -36,7 +36,7 b' MIME_TO_EXT = {'
36 36 "application/x-ssp": {"exts": ["*.ssp"], "mode": ""},
37 37 "application/x-troff": {"exts": ["*.[1234567]","*.man"], "mode": ""},
38 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","*.svg"], "mode": "xml"},
40 40 "application/xml+evoque": {"exts": ["*.xml"], "mode": ""},
41 41 "application/xml-dtd": {"exts": ["*.dtd"], "mode": "dtd"},
42 42 "application/xquery": {"exts": ["*.xqy","*.xquery","*.xq","*.xql","*.xqm","*.xy"], "mode": "xquery"},
@@ -48,7 +48,7 b' MIME_TO_EXT = {'
48 48 "text/coffeescript": {"exts": ["*.coffee"], "mode": ""},
49 49 "text/css": {"exts": ["*.css"], "mode": "css"},
50 50 "text/haxe": {"exts": ["*.hx"], "mode": ""},
51 "text/html": {"exts": ["*.html","*.htm","*.xhtml","*.xslt"], "mode": "htmlmixed"},
51 "text/html": {"exts": ["*.html","*.htm","*.xhtml","*.xslt","*.handlebars","*.hbs"], "mode": "htmlmixed"},
52 52 "text/html+evoque": {"exts": ["*.html"], "mode": ""},
53 53 "text/html+ruby": {"exts": ["*.rhtml"], "mode": ""},
54 54 "text/idl": {"exts": ["*.pro"], "mode": ""},
@@ -80,7 +80,7 b' MIME_TO_EXT = {'
80 80 "text/x-c-objdump": {"exts": ["*.c-objdump"], "mode": ""},
81 81 "text/x-ceylon": {"exts": ["*.ceylon"], "mode": ""},
82 82 "text/x-chdr": {"exts": ["*.c","*.h","*.idc"], "mode": "clike"},
83 "text/x-clojure": {"exts": ["*.clj"], "mode": "clojure"},
83 "text/x-clojure": {"exts": ["*.clj","*.cljc","*.cljx"], "mode": "clojure"},
84 84 "text/x-cmake": {"exts": ["*.cmake","CMakeLists.txt","*.cmake.in"], "mode": "cmake"},
85 85 "text/x-cobol": {"exts": ["*.cob","*.COB","*.cpy","*.CPY"], "mode": "cobol"},
86 86 "text/x-coffeescript": {"exts": ["*.coffee"], "mode": "coffeescript"},
@@ -89,7 +89,7 b' MIME_TO_EXT = {'
89 89 "text/x-cpp-objdump": {"exts": ["*.cpp-objdump","*.c++-objdump","*.cxx-objdump"], "mode": ""},
90 90 "text/x-crocsrc": {"exts": ["*.croc"], "mode": ""},
91 91 "text/x-csharp": {"exts": ["*.cs"], "mode": "clike"},
92 "text/x-csrc": {"exts": ["*.c","*.h"], "mode": "clike"},
92 "text/x-csrc": {"exts": ["*.c","*.h","*.ino"], "mode": "clike"},
93 93 "text/x-cuda": {"exts": ["*.cu","*.cuh"], "mode": ""},
94 94 "text/x-cython": {"exts": ["*.pyx","*.pxd","*.pxi"], "mode": "python"},
95 95 "text/x-d": {"exts": ["*.d"], "mode": "d"},
@@ -110,7 +110,7 b' MIME_TO_EXT = {'
110 110 "text/x-factor": {"exts": ["*.factor"], "mode": "factor"},
111 111 "text/x-fancysrc": {"exts": ["*.fy","*.fancypack"], "mode": ""},
112 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","*.f95"], "mode": "fortran"},
114 114 "text/x-fsharp": {"exts": ["*.fs","*.fsi"], "mode": "mllike"},
115 115 "text/x-gas": {"exts": ["*.s","*.S"], "mode": "gas"},
116 116 "text/x-gfm": {"exts": ["*.md","*.MD"], "mode": "gfm"},
@@ -123,7 +123,7 b' MIME_TO_EXT = {'
123 123 "text/x-gosrc": {"exts": ["*.go"], "mode": ""},
124 124 "text/x-gosu": {"exts": ["*.gs","*.gsx","*.gsp","*.vark"], "mode": ""},
125 125 "text/x-gosu-template": {"exts": ["*.gst"], "mode": ""},
126 "text/x-groovy": {"exts": ["*.groovy"], "mode": "groovy"},
126 "text/x-groovy": {"exts": ["*.groovy","*.gradle"], "mode": "groovy"},
127 127 "text/x-haml": {"exts": ["*.haml"], "mode": "haml"},
128 128 "text/x-haskell": {"exts": ["*.hs"], "mode": "haskell"},
129 129 "text/x-haxe": {"exts": ["*.hx"], "mode": "haxe"},
@@ -139,7 +139,7 b' MIME_TO_EXT = {'
139 139 "text/x-koka": {"exts": ["*.kk","*.kki"], "mode": ""},
140 140 "text/x-kotlin": {"exts": ["*.kt"], "mode": "clike"},
141 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","*.tex"], "mode": "stex"},
143 143 "text/x-less": {"exts": ["*.less"], "mode": "css"},
144 144 "text/x-literate-haskell": {"exts": ["*.lhs"], "mode": "haskell-literate"},
145 145 "text/x-livescript": {"exts": ["*.ls"], "mode": "livescript"},
@@ -173,13 +173,13 b' MIME_TO_EXT = {'
173 173 "text/x-openedge": {"exts": ["*.p","*.cls"], "mode": ""},
174 174 "text/x-pascal": {"exts": ["*.pas","*.p"], "mode": "pascal"},
175 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","*.php3","*.php4","*.php5","*.php7","*.phtml"], "mode": "php"},
177 177 "text/x-pig": {"exts": ["*.pig"], "mode": "pig"},
178 178 "text/x-povray": {"exts": ["*.pov","*.inc"], "mode": ""},
179 179 "text/x-powershell": {"exts": ["*.ps1"], "mode": ""},
180 180 "text/x-prolog": {"exts": ["*.prolog","*.pro","*.pl"], "mode": ""},
181 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","*.BUILD","*.bzl"], "mode": "python"},
183 183 "text/x-python-traceback": {"exts": ["*.pytb"], "mode": ""},
184 184 "text/x-python3-traceback": {"exts": ["*.py3tb"], "mode": ""},
185 185 "text/x-r-doc": {"exts": ["*.Rd"], "mode": ""},
@@ -187,7 +187,7 b' MIME_TO_EXT = {'
187 187 "text/x-rebol": {"exts": ["*.r","*.r3"], "mode": ""},
188 188 "text/x-robotframework": {"exts": ["*.txt","*.robot"], "mode": ""},
189 189 "text/x-rpm-spec": {"exts": ["*.spec"], "mode": "rpm"},
190 "text/x-rsrc": {"exts": ["*.r"], "mode": "r"},
190 "text/x-rsrc": {"exts": ["*.r","*.R"], "mode": "r"},
191 191 "text/x-rst": {"exts": ["*.rst","*.rest"], "mode": "rst"},
192 192 "text/x-ruby": {"exts": ["*.rb","*.rbw","Rakefile","*.rake","*.gemspec","*.rbx","*.duby"], "mode": "ruby"},
193 193 "text/x-rustsrc": {"exts": ["*.rs","*.rc"], "mode": "rust"},
@@ -224,7 +224,7 b' MIME_TO_EXT = {'
224 224 "text/x-yaml": {"exts": ["*.yaml","*.yml"], "mode": "yaml"},
225 225 "text/x-z80": {"exts": ["*.z80"], "mode": "z80"},
226 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 230 /* Special case for overriding mode by file extensions
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 //mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara
5 5
@@ -130,7 +130,7 b' CodeMirror.defineMode("mirc", function()'
130 130 }
131 131 }
132 132 else if (ch == "%") {
133 stream.eatWhile(/[^,^\s^\(^\)]/);
133 stream.eatWhile(/[^,\s()]/);
134 134 state.beforeParams = true;
135 135 return "string";
136 136 }
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,31 +13,26 b''
13 13
14 14 CodeMirror.defineMode('mllike', function(_config, parserConfig) {
15 15 var words = {
16 'let': 'keyword',
17 'rec': 'keyword',
18 'in': 'keyword',
19 'of': 'keyword',
20 'and': 'keyword',
21 'if': 'keyword',
22 'then': 'keyword',
23 'else': 'keyword',
24 'for': 'keyword',
25 'to': 'keyword',
26 'while': 'keyword',
16 'as': 'keyword',
27 17 'do': 'keyword',
28 'done': 'keyword',
18 'else': 'keyword',
19 'end': 'keyword',
20 'exception': 'keyword',
29 21 'fun': 'keyword',
30 'function': 'keyword',
31 'val': 'keyword',
22 'functor': 'keyword',
23 'if': 'keyword',
24 'in': 'keyword',
25 'include': 'keyword',
26 'let': 'keyword',
27 'of': 'keyword',
28 'open': 'keyword',
29 'rec': 'keyword',
30 'struct': 'keyword',
31 'then': 'keyword',
32 32 'type': 'keyword',
33 'mutable': 'keyword',
34 'match': 'keyword',
35 'with': 'keyword',
36 'try': 'keyword',
37 'open': 'builtin',
38 'ignore': 'builtin',
39 'begin': 'keyword',
40 'end': 'keyword'
33 'val': 'keyword',
34 'while': 'keyword',
35 'with': 'keyword'
41 36 };
42 37
43 38 var extraWords = parserConfig.extraWords || {};
@@ -46,6 +41,9 b" CodeMirror.defineMode('mllike', function"
46 41 words[prop] = parserConfig.extraWords[prop];
47 42 }
48 43 }
44 var hintWords = [];
45 for (var k in words) { hintWords.push(k); }
46 CodeMirror.registerHelper("hintWords", "mllike", hintWords);
49 47
50 48 function tokenBase(stream, state) {
51 49 var ch = stream.next();
@@ -54,6 +52,13 b" CodeMirror.defineMode('mllike', function"
54 52 state.tokenize = tokenString;
55 53 return state.tokenize(stream, state);
56 54 }
55 if (ch === '{') {
56 if (stream.eat('|')) {
57 state.longString = true;
58 state.tokenize = tokenLongString;
59 return state.tokenize(stream, state);
60 }
61 }
57 62 if (ch === '(') {
58 63 if (stream.eat('*')) {
59 64 state.commentLevel++;
@@ -61,7 +66,7 b" CodeMirror.defineMode('mllike', function"
61 66 return state.tokenize(stream, state);
62 67 }
63 68 }
64 if (ch === '~') {
69 if (ch === '~' || ch === '?') {
65 70 stream.eatWhile(/\w/);
66 71 return 'variable-2';
67 72 }
@@ -74,18 +79,32 b" CodeMirror.defineMode('mllike', function"
74 79 return 'comment';
75 80 }
76 81 if (/\d/.test(ch)) {
77 stream.eatWhile(/[\d]/);
78 if (stream.eat('.')) {
79 stream.eatWhile(/[\d]/);
82 if (ch === '0' && stream.eat(/[bB]/)) {
83 stream.eatWhile(/[01]/);
84 } if (ch === '0' && stream.eat(/[xX]/)) {
85 stream.eatWhile(/[0-9a-fA-F]/)
86 } if (ch === '0' && stream.eat(/[oO]/)) {
87 stream.eatWhile(/[0-7]/);
88 } else {
89 stream.eatWhile(/[\d_]/);
90 if (stream.eat('.')) {
91 stream.eatWhile(/[\d]/);
92 }
93 if (stream.eat(/[eE]/)) {
94 stream.eatWhile(/[\d\-+]/);
95 }
80 96 }
81 97 return 'number';
82 98 }
83 if ( /[+\-*&%=<>!?|]/.test(ch)) {
99 if ( /[+\-*&%=<>!?|@\.~:]/.test(ch)) {
84 100 return 'operator';
85 101 }
86 stream.eatWhile(/\w/);
87 var cur = stream.current();
88 return words.hasOwnProperty(cur) ? words[cur] : 'variable';
102 if (/[\w\xa1-\uffff]/.test(ch)) {
103 stream.eatWhile(/[\w\xa1-\uffff]/);
104 var cur = stream.current();
105 return words.hasOwnProperty(cur) ? words[cur] : 'variable';
106 }
107 return null
89 108 }
90 109
91 110 function tokenString(stream, state) {
@@ -116,8 +135,20 b" CodeMirror.defineMode('mllike', function"
116 135 return 'comment';
117 136 }
118 137
138 function tokenLongString(stream, state) {
139 var prev, next;
140 while (state.longString && (next = stream.next()) != null) {
141 if (prev === '|' && next === '}') state.longString = false;
142 prev = next;
143 }
144 if (!state.longString) {
145 state.tokenize = tokenBase;
146 }
147 return 'string';
148 }
149
119 150 return {
120 startState: function() {return {tokenize: tokenBase, commentLevel: 0};},
151 startState: function() {return {tokenize: tokenBase, commentLevel: 0, longString: false};},
121 152 token: function(stream, state) {
122 153 if (stream.eatSpace()) return null;
123 154 return state.tokenize(stream, state);
@@ -132,14 +163,64 b" CodeMirror.defineMode('mllike', function"
132 163 CodeMirror.defineMIME('text/x-ocaml', {
133 164 name: 'mllike',
134 165 extraWords: {
135 'succ': 'keyword',
166 'and': 'keyword',
167 'assert': 'keyword',
168 'begin': 'keyword',
169 'class': 'keyword',
170 'constraint': 'keyword',
171 'done': 'keyword',
172 'downto': 'keyword',
173 'external': 'keyword',
174 'function': 'keyword',
175 'initializer': 'keyword',
176 'lazy': 'keyword',
177 'match': 'keyword',
178 'method': 'keyword',
179 'module': 'keyword',
180 'mutable': 'keyword',
181 'new': 'keyword',
182 'nonrec': 'keyword',
183 'object': 'keyword',
184 'private': 'keyword',
185 'sig': 'keyword',
186 'to': 'keyword',
187 'try': 'keyword',
188 'value': 'keyword',
189 'virtual': 'keyword',
190 'when': 'keyword',
191
192 // builtins
193 'raise': 'builtin',
194 'failwith': 'builtin',
195 'true': 'builtin',
196 'false': 'builtin',
197
198 // Pervasives builtins
199 'asr': 'builtin',
200 'land': 'builtin',
201 'lor': 'builtin',
202 'lsl': 'builtin',
203 'lsr': 'builtin',
204 'lxor': 'builtin',
205 'mod': 'builtin',
206 'or': 'builtin',
207
208 // More Pervasives
209 'raise_notrace': 'builtin',
136 210 'trace': 'builtin',
137 211 'exit': 'builtin',
138 212 'print_string': 'builtin',
139 213 'print_endline': 'builtin',
140 'true': 'atom',
141 'false': 'atom',
142 'raise': 'keyword'
214
215 'int': 'type',
216 'float': 'type',
217 'bool': 'type',
218 'char': 'type',
219 'string': 'type',
220 'unit': 'type',
221
222 // Modules
223 'List': 'builtin'
143 224 }
144 225 });
145 226
@@ -147,18 +228,21 b" CodeMirror.defineMIME('text/x-fsharp', {"
147 228 name: 'mllike',
148 229 extraWords: {
149 230 'abstract': 'keyword',
150 'as': 'keyword',
151 231 'assert': 'keyword',
152 232 'base': 'keyword',
233 'begin': 'keyword',
153 234 'class': 'keyword',
154 235 'default': 'keyword',
155 236 'delegate': 'keyword',
237 'do!': 'keyword',
238 'done': 'keyword',
156 239 'downcast': 'keyword',
157 240 'downto': 'keyword',
158 241 'elif': 'keyword',
159 'exception': 'keyword',
160 242 'extern': 'keyword',
161 243 'finally': 'keyword',
244 'for': 'keyword',
245 'function': 'keyword',
162 246 'global': 'keyword',
163 247 'inherit': 'keyword',
164 248 'inline': 'keyword',
@@ -166,38 +250,108 b" CodeMirror.defineMIME('text/x-fsharp', {"
166 250 'internal': 'keyword',
167 251 'lazy': 'keyword',
168 252 'let!': 'keyword',
169 'member' : 'keyword',
253 'match': 'keyword',
254 'member': 'keyword',
170 255 'module': 'keyword',
256 'mutable': 'keyword',
171 257 'namespace': 'keyword',
172 258 'new': 'keyword',
173 259 'null': 'keyword',
174 260 'override': 'keyword',
175 261 'private': 'keyword',
176 262 'public': 'keyword',
263 'return!': 'keyword',
177 264 'return': 'keyword',
178 'return!': 'keyword',
179 265 'select': 'keyword',
180 266 'static': 'keyword',
181 'struct': 'keyword',
267 'to': 'keyword',
268 'try': 'keyword',
182 269 'upcast': 'keyword',
183 'use': 'keyword',
184 270 'use!': 'keyword',
185 'val': 'keyword',
271 'use': 'keyword',
272 'void': 'keyword',
186 273 'when': 'keyword',
274 'yield!': 'keyword',
187 275 'yield': 'keyword',
188 'yield!': 'keyword',
189 276
277 // Reserved words
278 'atomic': 'keyword',
279 'break': 'keyword',
280 'checked': 'keyword',
281 'component': 'keyword',
282 'const': 'keyword',
283 'constraint': 'keyword',
284 'constructor': 'keyword',
285 'continue': 'keyword',
286 'eager': 'keyword',
287 'event': 'keyword',
288 'external': 'keyword',
289 'fixed': 'keyword',
290 'method': 'keyword',
291 'mixin': 'keyword',
292 'object': 'keyword',
293 'parallel': 'keyword',
294 'process': 'keyword',
295 'protected': 'keyword',
296 'pure': 'keyword',
297 'sealed': 'keyword',
298 'tailcall': 'keyword',
299 'trait': 'keyword',
300 'virtual': 'keyword',
301 'volatile': 'keyword',
302
303 // builtins
190 304 'List': 'builtin',
191 305 'Seq': 'builtin',
192 306 'Map': 'builtin',
193 307 'Set': 'builtin',
308 'Option': 'builtin',
194 309 'int': 'builtin',
195 310 'string': 'builtin',
196 'raise': 'builtin',
197 'failwith': 'builtin',
198 311 'not': 'builtin',
199 312 'true': 'builtin',
200 'false': 'builtin'
313 'false': 'builtin',
314
315 'raise': 'builtin',
316 'failwith': 'builtin'
317 },
318 slashComments: true
319 });
320
321
322 CodeMirror.defineMIME('text/x-sml', {
323 name: 'mllike',
324 extraWords: {
325 'abstype': 'keyword',
326 'and': 'keyword',
327 'andalso': 'keyword',
328 'case': 'keyword',
329 'datatype': 'keyword',
330 'fn': 'keyword',
331 'handle': 'keyword',
332 'infix': 'keyword',
333 'infixr': 'keyword',
334 'local': 'keyword',
335 'nonfix': 'keyword',
336 'op': 'keyword',
337 'orelse': 'keyword',
338 'raise': 'keyword',
339 'withtype': 'keyword',
340 'eqtype': 'keyword',
341 'sharing': 'keyword',
342 'sig': 'keyword',
343 'signature': 'keyword',
344 'structure': 'keyword',
345 'where': 'keyword',
346 'true': 'keyword',
347 'false': 'keyword',
348
349 // types
350 'int': 'builtin',
351 'real': 'builtin',
352 'string': 'builtin',
353 'char': 'builtin',
354 'bool': 'builtin'
201 355 },
202 356 slashComments: true
203 357 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Modelica support for CodeMirror, copyright (c) by Lennart Ochel
5 5
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // mode(s) for the sequence chart dsl's mscgen, xΓΉ and msgenny
5 5 // For more information on mscgen, see the site of the original author:
@@ -23,6 +23,7 b''
23 23 mscgen: {
24 24 "keywords" : ["msc"],
25 25 "options" : ["hscale", "width", "arcgradient", "wordwraparcs"],
26 "constants" : ["true", "false", "on", "off"],
26 27 "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
27 28 "brackets" : ["\\{", "\\}"], // [ and ] are brackets too, but these get handled in with lists
28 29 "arcsWords" : ["note", "abox", "rbox", "box"],
@@ -31,9 +32,10 b''
31 32 "operators" : ["="]
32 33 },
33 34 xu: {
34 "keywords" : ["msc"],
35 "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
36 "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
35 "keywords" : ["msc", "xu"],
36 "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "wordwrapentities", "watermark"],
37 "constants" : ["true", "false", "on", "off", "auto"],
38 "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip", "title", "deactivate", "activate", "activation"],
37 39 "brackets" : ["\\{", "\\}"], // [ and ] are brackets too, but these get handled in with lists
38 40 "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
39 41 "arcsOthers" : ["\\|\\|\\|", "\\.\\.\\.", "---", "--", "<->", "==", "<<=>>", "<=>", "\\.\\.", "<<>>", "::", "<:>", "->", "=>>", "=>", ">>", ":>", "<-", "<<=", "<=", "<<", "<:", "x-", "-x"],
@@ -42,7 +44,8 b''
42 44 },
43 45 msgenny: {
44 46 "keywords" : null,
45 "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
47 "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "wordwrapentities", "watermark"],
48 "constants" : ["true", "false", "on", "off", "auto"],
46 49 "attributes" : null,
47 50 "brackets" : ["\\{", "\\}"],
48 51 "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
@@ -146,6 +149,9 b''
146 149 if (!!pConfig.operators && pStream.match(wordRegexp(pConfig.operators), true, true))
147 150 return "operator";
148 151
152 if (!!pConfig.constants && pStream.match(wordRegexp(pConfig.constants), true, true))
153 return "variable";
154
149 155 /* attribute lists */
150 156 if (!pConfig.inAttributeList && !!pConfig.attributes && pStream.match(/\[/, true, true)) {
151 157 pConfig.inAttributeList = true;
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function() {
5 5 var mode = CodeMirror.getMode({indentUnit: 2}, "mscgen");
@@ -26,9 +26,18 b''
26 26
27 27 MT("xΓΉ/ msgenny keywords classify as 'base'",
28 28 "[base watermark]",
29 "[base wordwrapentities]",
29 30 "[base alt loop opt ref else break par seq assert]"
30 31 );
31 32
33 MT("xΓΉ/ msgenny constants classify as 'base'",
34 "[base auto]"
35 );
36
37 MT("mscgen constants classify as 'variable'",
38 "[variable true]", "[variable false]", "[variable on]", "[variable off]"
39 );
40
32 41 MT("mscgen options classify as keyword",
33 42 "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
34 43 );
@@ -63,7 +72,7 b''
63 72 MT("a typical program",
64 73 "[comment # typical mscgen program]",
65 74 "[keyword msc][base ][bracket {]",
66 "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]",
75 "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
67 76 "[base a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]",
68 77 "[base b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]",
69 78 "[base c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]",
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function() {
5 5 var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-msgenny");
@@ -20,9 +20,15 b''
20 20
21 21 MT("xΓΉ/ msgenny keywords classify as 'keyword'",
22 22 "[keyword watermark]",
23 "[keyword wordwrapentities]",
23 24 "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]"
24 25 );
25 26
27 MT("xΓΉ/ msgenny constants classify as 'variable'",
28 "[variable auto]",
29 "[variable true]", "[variable false]", "[variable on]", "[variable off]"
30 );
31
26 32 MT("mscgen options classify as keyword",
27 33 "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
28 34 );
@@ -56,7 +62,7 b''
56 62
57 63 MT("a typical program",
58 64 "[comment # typical msgenny program]",
59 "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
65 "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
60 66 "[base a : ][string \"Entity A\"][base ,]",
61 67 "[base b : Entity B,]",
62 68 "[base c : Entity C;]",
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function() {
5 5 var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-xu");
@@ -9,7 +9,13 b''
9 9 "[keyword msc][bracket {]",
10 10 "[base ]",
11 11 "[bracket }]"
12 );
12 );
13
14 MT("empty chart",
15 "[keyword xu][bracket {]",
16 "[base ]",
17 "[bracket }]"
18 );
13 19
14 20 MT("comments",
15 21 "[comment // a single line comment]",
@@ -29,6 +35,11 b''
29 35 "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]"
30 36 );
31 37
38 MT("xΓΉ/ msgenny constants classify as 'variable'",
39 "[variable auto]",
40 "[variable true]", "[variable false]", "[variable on]", "[variable off]"
41 );
42
32 43 MT("mscgen options classify as keyword",
33 44 "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
34 45 );
@@ -49,7 +60,8 b''
49 60 "[attribute id]","[attribute url]","[attribute idurl]",
50 61 "[attribute linecolor]","[attribute linecolour]","[attribute textcolor]","[attribute textcolour]","[attribute textbgcolor]","[attribute textbgcolour]",
51 62 "[attribute arclinecolor]","[attribute arclinecolour]","[attribute arctextcolor]","[attribute arctextcolour]","[attribute arctextbgcolor]","[attribute arctextbgcolour]",
52 "[attribute arcskip][bracket ]]]"
63 "[attribute arcskip]","[attribute title]",
64 "[attribute activate]","[attribute deactivate]","[attribute activation][bracket ]]]"
53 65 );
54 66
55 67 MT("outside an attribute list, attributes classify as base",
@@ -57,18 +69,18 b''
57 69 "[base id]","[base url]","[base idurl]",
58 70 "[base linecolor]","[base linecolour]","[base textcolor]","[base textcolour]","[base textbgcolor]","[base textbgcolour]",
59 71 "[base arclinecolor]","[base arclinecolour]","[base arctextcolor]","[base arctextcolour]","[base arctextbgcolor]","[base arctextbgcolour]",
60 "[base arcskip]"
72 "[base arcskip]", "[base title]"
61 73 );
62 74
63 75 MT("a typical program",
64 "[comment # typical mscgen program]",
65 "[keyword msc][base ][bracket {]",
66 "[keyword wordwraparcs][operator =][string \"true\"][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]",
76 "[comment # typical xu program]",
77 "[keyword xu][base ][bracket {]",
78 "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30, ][keyword width][operator =][variable auto][base ;]",
67 79 "[base a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]",
68 80 "[base b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]",
69 81 "[base c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]",
70 82 "[base a ][keyword =>>][base b][bracket [[][attribute label][operator =][string \"Hello entity B\"][bracket ]]][base ;]",
71 "[base a ][keyword <<][base b][bracket [[][attribute label][operator =][string \"Here's an answer dude!\"][bracket ]]][base ;]",
83 "[base a ][keyword <<][base b][bracket [[][attribute label][operator =][string \"Here's an answer dude!\"][base , ][attribute title][operator =][string \"This is a title for this message\"][bracket ]]][base ;]",
72 84 "[base c ][keyword :>][base *][bracket [[][attribute label][operator =][string \"What about me?\"][base , ][attribute textcolor][operator =][base red][bracket ]]][base ;]",
73 85 "[bracket }]"
74 86 );
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 This MUMPS Language script was constructed using vbscript.js as a template.
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Author: Jan T. Sott (http://github.com/idleberg)
5 5
@@ -24,20 +24,20 b' CodeMirror.defineSimpleMode("nsis",{'
24 24 { regex: /`(?:[^\\`]|\\.)*`?/, token: "string" },
25 25
26 26 // Compile Time Commands
27 {regex: /(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|makensis|searchparse|searchreplace))\b/, token: "keyword"},
27 {regex: /^\s*(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|pragma|finalize|getdllversion|gettlbversion|system|tempfile|warning|verbose|define|undef|insertmacro|macro|macroend|makensis|searchparse|searchreplace))\b/, token: "keyword"},
28 28
29 29 // Conditional Compilation
30 {regex: /(?:\!(if(?:n?def)?|ifmacron?def|macro))\b/, token: "keyword", indent: true},
31 {regex: /(?:\!(else|endif|macroend))\b/, token: "keyword", dedent: true},
30 {regex: /^\s*(?:\!(if(?:n?def)?|ifmacron?def|macro))\b/, token: "keyword", indent: true},
31 {regex: /^\s*(?:\!(else|endif|macroend))\b/, token: "keyword", dedent: true},
32 32
33 33 // Runtime Commands
34 {regex: /\b(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|IntCmp|IntCmpU|IntFmt|IntOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetPluginUnload|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"},
35 {regex: /\b(?:Function|PageEx|Section(?:Group)?)\b/, token: "keyword", indent: true},
36 {regex: /\b(?:(Function|PageEx|Section(?:Group)?)End)\b/, token: "keyword", dedent: true},
34 {regex: /^\s*(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecShellWait|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|Int64Cmp|Int64CmpU|Int64Fmt|IntCmp|IntCmpU|IntFmt|IntOp|IntPtrCmp|IntPtrCmpU|IntPtrOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|PEDllCharacteristics|PESubsysVer|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegMultiStr|WriteRegNone|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"},
35 {regex: /^\s*(?:Function|PageEx|Section(?:Group)?)\b/, token: "keyword", indent: true},
36 {regex: /^\s*(?:(Function|PageEx|Section(?:Group)?)End)\b/, token: "keyword", dedent: true},
37 37
38 38 // Command Options
39 {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"},
40 {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|right|show|silent|silentlog|textonly|top|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"},
39 {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR(32|64)?|HKCU(32|64)?|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM(32|64)?|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"},
40 {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|false|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|off|on|right|show|silent|silentlog|textonly|top|true|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"},
41 41
42 42 // LogicLib.nsh
43 43 {regex: /\$\{(?:And(?:If(?:Not)?|Unless)|Break|Case(?:Else)?|Continue|Default|Do(?:Until|While)?|Else(?:If(?:Not)?|Unless)?|End(?:If|Select|Switch)|Exit(?:Do|For|While)|For(?:Each)?|If(?:Cmd|Not(?:Then)?|Then)?|Loop(?:Until|While)?|Or(?:If(?:Not)?|Unless)|Select|Switch|Unless|While)\}/, token: "variable-2", indent: true},
@@ -71,13 +71,13 b' CodeMirror.defineSimpleMode("nsis",{'
71 71 {regex: /[-+\/*=<>!]+/, token: "operator"},
72 72
73 73 // Variable
74 {regex: /\$[\w]+/, token: "variable"},
74 {regex: /\$\w+/, token: "variable"},
75 75
76 76 // Constant
77 {regex: /\${[\w]+}/,token: "variable-2"},
77 {regex: /\${[\w\.:-]+}/, token: "variable-2"},
78 78
79 79 // Language String
80 {regex: /\$\([\w]+\)/,token: "variable-3"}
80 {regex: /\$\([\w\.:-]+\)/, token: "variable-3"}
81 81 ],
82 82 comment: [
83 83 {regex: /.*?\*\//, token: "comment", next: "start"},
@@ -1,11 +1,11 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**********************************************************
5 5 * This script provides syntax highlighting support for
6 * the Ntriples format.
7 * Ntriples format specification:
8 * http://www.w3.org/TR/rdf-testcases/#ntriples
6 * the N-Triples format.
7 * N-Triples format specification:
8 * https://www.w3.org/TR/n-triples/
9 9 ***********************************************************/
10 10
11 11 /*
@@ -181,6 +181,15 b' CodeMirror.defineMode("ntriples", functi'
181 181 };
182 182 });
183 183
184 // define the registered Media Type for n-triples:
185 // https://www.w3.org/TR/n-triples/#n-triples-mediatype
186 CodeMirror.defineMIME("application/n-triples", "ntriples");
187
188 // N-Quads is based on the N-Triples format (so same highlighting works)
189 // https://www.w3.org/TR/n-quads/
190 CodeMirror.defineMIME("application/n-quads", "ntriples");
191
192 // previously used, though technically incorrect media type for n-triples
184 193 CodeMirror.defineMIME("text/n-triples", "ntriples");
185 194
186 195 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -17,7 +17,7 b' CodeMirror.defineMode("octave", function'
17 17 }
18 18
19 19 var singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!@'\\\\]");
20 var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;]');
20 var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;\\.]');
21 21 var doubleOperators = new RegExp("^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^\\\\]))");
22 22 var doubleDelimiters = new RegExp("^((!=)|(\\+=)|(\\-=)|(\\*=)|(/=)|(&=)|(\\|=)|(\\^=))");
23 23 var tripleDelimiters = new RegExp("^((>>=)|(<<=))");
@@ -90,8 +90,8 b' CodeMirror.defineMode("octave", function'
90 90 if (stream.match(wordRegexp(['nan','NaN','inf','Inf']))) { return 'number'; };
91 91
92 92 // Handle Strings
93 if (stream.match(/^"([^"]|(""))*"/)) { return 'string'; } ;
94 if (stream.match(/^'([^']|(''))*'/)) { return 'string'; } ;
93 var m = stream.match(/^"(?:[^"]|"")*("|$)/) || stream.match(/^'(?:[^']|'')*('|$)/)
94 if (m) { return m[1] ? 'string' : "string error"; }
95 95
96 96 // Handle words
97 97 if (stream.match(keywords)) { return 'keyword'; } ;
@@ -126,7 +126,11 b' CodeMirror.defineMode("octave", function'
126 126 state.tokenize = tokenTranspose;
127 127 }
128 128 return style;
129 }
129 },
130
131 lineComment: '%',
132
133 fold: 'indent'
130 134 };
131 135 });
132 136
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -27,7 +27,7 b' CodeMirror.defineMode("oz", function (co'
27 27
28 28 var atoms = wordRegexp(["true", "false", "nil", "unit"]);
29 29 var commonKeywords = wordRegexp(["andthen", "at", "attr", "declare", "feat", "from", "lex",
30 "mod", "mode", "orelse", "parser", "prod", "prop", "scanner", "self", "syn", "token"]);
30 "mod", "div", "mode", "orelse", "parser", "prod", "prop", "scanner", "self", "syn", "token"]);
31 31 var openingKeywords = wordRegexp(["local", "proc", "fun", "case", "class", "if", "cond", "or", "dis",
32 32 "choice", "not", "thread", "try", "raise", "lock", "for", "suchthat", "meth", "functor"]);
33 33 var middleKeywords = wordRegexp(middle);
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -17,9 +17,21 b' CodeMirror.defineMode("pascal", function'
17 17 for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
18 18 return obj;
19 19 }
20 var keywords = words("and array begin case const div do downto else end file for forward integer " +
21 "boolean char function goto if in label mod nil not of or packed procedure " +
22 "program record repeat set string then to type until var while with");
20 var keywords = words(
21 "absolute and array asm begin case const constructor destructor div do " +
22 "downto else end file for function goto if implementation in inherited " +
23 "inline interface label mod nil not object of operator or packed procedure " +
24 "program record reintroduce repeat self set shl shr string then to type " +
25 "unit until uses var while with xor as class dispinterface except exports " +
26 "finalization finally initialization inline is library on out packed " +
27 "property raise resourcestring threadvar try absolute abstract alias " +
28 "assembler bitpacked break cdecl continue cppdecl cvar default deprecated " +
29 "dynamic enumerator experimental export external far far16 forward generic " +
30 "helper implements index interrupt iocheck local message name near " +
31 "nodefault noreturn nostackframe oldfpccall otherwise overload override " +
32 "pascal platform private protected public published read register " +
33 "reintroduce result safecall saveregisters softfloat specialize static " +
34 "stdcall stored strict unaligned unimplemented varargs virtual write");
23 35 var atoms = {"null": true};
24 36
25 37 var isOperatorChar = /[+\-*&%=<>!?|\/]/;
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -24,7 +24,7 b' CodeMirror.defineMode("pegjs", function '
24 24 inString: false,
25 25 stringType: null,
26 26 inComment: false,
27 inChracterClass: false,
27 inCharacterClass: false,
28 28 braced: 0,
29 29 lhs: true,
30 30 localState: null
@@ -66,22 +66,22 b' CodeMirror.defineMode("pegjs", function '
66 66 }
67 67 }
68 68 return "comment";
69 } else if (state.inChracterClass) {
70 while (state.inChracterClass && !stream.eol()) {
69 } else if (state.inCharacterClass) {
70 while (state.inCharacterClass && !stream.eol()) {
71 71 if (!(stream.match(/^[^\]\\]+/) || stream.match(/^\\./))) {
72 state.inChracterClass = false;
72 state.inCharacterClass = false;
73 73 }
74 74 }
75 75 } else if (stream.peek() === '[') {
76 76 stream.next();
77 state.inChracterClass = true;
77 state.inCharacterClass = true;
78 78 return 'bracket';
79 79 } else if (stream.match(/^\/\//)) {
80 80 stream.skipToEnd();
81 81 return "comment";
82 82 } else if (state.braced || stream.peek() === '{') {
83 83 if (state.localState === null) {
84 state.localState = jsMode.startState();
84 state.localState = CodeMirror.startState(jsMode);
85 85 }
86 86 var token = jsMode.token(stream, state.localState);
87 87 var text = stream.current();
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08)
5 5 // This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com)
@@ -268,7 +268,7 b' CodeMirror.defineMode("perl",function(){'
268 268 chmod :1, // - changes the permissions on a list of files
269 269 chomp :1, // - remove a trailing record separator from a string
270 270 chop :1, // - remove the last character from a string
271 chown :1, // - change the owership on a list of files
271 chown :1, // - change the ownership on a list of files
272 272 chr :1, // - get character this number represents
273 273 chroot :1, // - make directory new root for path lookups
274 274 close :1, // - close file (or pipe or socket) handle
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -86,7 +86,7 b''
86 86 "die echo empty exit eval include include_once isset list require require_once return " +
87 87 "print unset __halt_compiler self static parent yield insteadof finally";
88 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 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";
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 array_intersect_key array_combine array_column 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 90 CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
91 91 CodeMirror.registerHelper("wordChars", "php", /[\w$]/);
92 92
@@ -151,7 +151,7 b''
151 151 };
152 152
153 153 CodeMirror.defineMode("php", function(config, parserConfig) {
154 var htmlMode = CodeMirror.getMode(config, "text/html");
154 var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || "text/html");
155 155 var phpMode = CodeMirror.getMode(config, phpConfig);
156 156
157 157 function dispatch(stream, state) {
@@ -160,7 +160,7 b''
160 160 if (!isPHP) {
161 161 if (stream.match(/^<\?\w*/)) {
162 162 state.curMode = phpMode;
163 if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, ""))
163 if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "", ""))
164 164 state.curState = state.php;
165 165 return "meta";
166 166 }
@@ -213,11 +213,11 b''
213 213
214 214 token: dispatch,
215 215
216 indent: function(state, textAfter) {
216 indent: function(state, textAfter, line) {
217 217 if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
218 218 (state.curMode == phpMode && /^\?>/.test(textAfter)))
219 return htmlMode.indent(state.html, textAfter);
220 return state.curMode.indent(state.curState, textAfter);
219 return htmlMode.indent(state.html, textAfter, line);
220 return state.curMode.indent(state.curState, textAfter, line);
221 221 },
222 222
223 223 blockCommentStart: "/*",
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 * Pig Latin Mode for CodeMirror 2
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -34,7 +34,7 b' CodeMirror.defineMode("properties", func'
34 34 }
35 35
36 36 if (sol) {
37 while(stream.eatSpace());
37 while(stream.eatSpace()) {}
38 38 }
39 39
40 40 var ch = stream.next();
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -126,7 +126,7 b' CodeMirror.defineMode("puppet", function'
126 126 if (word && words.hasOwnProperty(word)) {
127 127 // Negates the initial next()
128 128 stream.backUp(1);
129 // Acutally move the stream
129 // rs move the stream
130 130 stream.match(/[\w]+/);
131 131 // We want to process these words differently
132 132 // do to the importance they have in Puppet
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -32,13 +32,6 b''
32 32 "sorted", "staticmethod", "str", "sum", "super", "tuple",
33 33 "type", "vars", "zip", "__import__", "NotImplemented",
34 34 "Ellipsis", "__debug__"];
35 var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
36 "file", "intern", "long", "raw_input", "reduce", "reload",
37 "unichr", "unicode", "xrange", "False", "True", "None"],
38 keywords: ["exec", "print"]};
39 var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
40 keywords: ["nonlocal", "False", "True", "None", "async", "await"]};
41
42 35 CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));
43 36
44 37 function top(state) {
@@ -48,51 +41,51 b''
48 41 CodeMirror.defineMode("python", function(conf, parserConf) {
49 42 var ERRORCLASS = "error";
50 43
51 var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/;
52 var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/;
53 var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/;
54 var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/;
55
56 if (parserConf.version && parseInt(parserConf.version, 10) == 3){
57 // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
58 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/;
59 var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
60 } else {
61 var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/;
62 var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
63 }
44 var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
45 // (Backwards-compatiblity with old, cumbersome config system)
46 var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
47 parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
48 for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
64 49
65 50 var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
66 51
67 52 var myKeywords = commonKeywords, myBuiltins = commonBuiltins;
68 if(parserConf.extra_keywords != undefined){
53 if (parserConf.extra_keywords != undefined)
69 54 myKeywords = myKeywords.concat(parserConf.extra_keywords);
70 }
71 if(parserConf.extra_builtins != undefined){
55
56 if (parserConf.extra_builtins != undefined)
72 57 myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
73 }
74 if (parserConf.version && parseInt(parserConf.version, 10) == 3) {
75 myKeywords = myKeywords.concat(py3.keywords);
76 myBuiltins = myBuiltins.concat(py3.builtins);
77 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
58
59 var py3 = !(parserConf.version && Number(parserConf.version) < 3)
60 if (py3) {
61 // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
62 var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
63 myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
64 myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
65 var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
78 66 } else {
79 myKeywords = myKeywords.concat(py2.keywords);
80 myBuiltins = myBuiltins.concat(py2.builtins);
81 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
67 var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
68 myKeywords = myKeywords.concat(["exec", "print"]);
69 myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
70 "file", "intern", "long", "raw_input", "reduce", "reload",
71 "unichr", "unicode", "xrange", "False", "True", "None"]);
72 var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
82 73 }
83 74 var keywords = wordRegexp(myKeywords);
84 75 var builtins = wordRegexp(myBuiltins);
85 76
86 77 // tokenizers
87 78 function tokenBase(stream, state) {
79 var sol = stream.sol() && state.lastToken != "\\"
80 if (sol) state.indent = stream.indentation()
88 81 // Handle scope changes
89 if (stream.sol() && top(state).type == "py") {
82 if (sol && top(state).type == "py") {
90 83 var scopeOffset = top(state).offset;
91 84 if (stream.eatSpace()) {
92 85 var lineOffset = stream.indentation();
93 86 if (lineOffset > scopeOffset)
94 pushScope(stream, state, "py");
95 else if (lineOffset < scopeOffset && dedent(stream, state))
87 pushPyScope(state);
88 else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
96 89 state.errorToken = true;
97 90 return null;
98 91 } else {
@@ -108,20 +101,15 b''
108 101 function tokenBaseInner(stream, state) {
109 102 if (stream.eatSpace()) return null;
110 103
111 var ch = stream.peek();
112
113 104 // Handle Comments
114 if (ch == "#") {
115 stream.skipToEnd();
116 return "comment";
117 }
105 if (stream.match(/^#.*/)) return "comment";
118 106
119 107 // Handle Number Literals
120 108 if (stream.match(/^[0-9\.]/, false)) {
121 109 var floatLiteral = false;
122 110 // Floats
123 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
124 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
111 if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
112 if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
125 113 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
126 114 if (floatLiteral) {
127 115 // Float literals may be "imaginary"
@@ -131,13 +119,13 b''
131 119 // Integers
132 120 var intLiteral = false;
133 121 // Hex
134 if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
122 if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
135 123 // Binary
136 if (stream.match(/^0b[01]+/i)) intLiteral = true;
124 if (stream.match(/^0b[01_]+/i)) intLiteral = true;
137 125 // Octal
138 if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
126 if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
139 127 // Decimal
140 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
128 if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
141 129 // Decimal literals may be "imaginary"
142 130 stream.eat(/J/i);
143 131 // TODO - Can you have imaginary longs?
@@ -154,19 +142,20 b''
154 142
155 143 // Handle Strings
156 144 if (stream.match(stringPrefixes)) {
157 state.tokenize = tokenStringFactory(stream.current());
158 return state.tokenize(stream, state);
145 var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
146 if (!isFmtString) {
147 state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
148 return state.tokenize(stream, state);
149 } else {
150 state.tokenize = formatStringFactory(stream.current(), state.tokenize);
151 return state.tokenize(stream, state);
152 }
159 153 }
160 154
161 // Handle operators and Delimiters
162 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
163 return "punctuation";
155 for (var i = 0; i < operators.length; i++)
156 if (stream.match(operators[i])) return "operator"
164 157
165 if (stream.match(doubleOperators) || stream.match(singleOperators))
166 return "operator";
167
168 if (stream.match(singleDelimiters))
169 return "punctuation";
158 if (stream.match(delimiters)) return "punctuation";
170 159
171 160 if (state.lastToken == "." && stream.match(identifiers))
172 161 return "property";
@@ -191,8 +180,69 b''
191 180 return ERRORCLASS;
192 181 }
193 182
194 function tokenStringFactory(delimiter) {
195 while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
183 function formatStringFactory(delimiter, tokenOuter) {
184 while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
185 delimiter = delimiter.substr(1);
186
187 var singleline = delimiter.length == 1;
188 var OUTCLASS = "string";
189
190 function tokenNestedExpr(depth) {
191 return function(stream, state) {
192 var inner = tokenBaseInner(stream, state)
193 if (inner == "punctuation") {
194 if (stream.current() == "{") {
195 state.tokenize = tokenNestedExpr(depth + 1)
196 } else if (stream.current() == "}") {
197 if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
198 else state.tokenize = tokenString
199 }
200 }
201 return inner
202 }
203 }
204
205 function tokenString(stream, state) {
206 while (!stream.eol()) {
207 stream.eatWhile(/[^'"\{\}\\]/);
208 if (stream.eat("\\")) {
209 stream.next();
210 if (singleline && stream.eol())
211 return OUTCLASS;
212 } else if (stream.match(delimiter)) {
213 state.tokenize = tokenOuter;
214 return OUTCLASS;
215 } else if (stream.match('{{')) {
216 // ignore {{ in f-str
217 return OUTCLASS;
218 } else if (stream.match('{', false)) {
219 // switch to nested mode
220 state.tokenize = tokenNestedExpr(0)
221 if (stream.current()) return OUTCLASS;
222 else return state.tokenize(stream, state)
223 } else if (stream.match('}}')) {
224 return OUTCLASS;
225 } else if (stream.match('}')) {
226 // single } in f-string is an error
227 return ERRORCLASS;
228 } else {
229 stream.eat(/['"]/);
230 }
231 }
232 if (singleline) {
233 if (parserConf.singleLineStringErrors)
234 return ERRORCLASS;
235 else
236 state.tokenize = tokenOuter;
237 }
238 return OUTCLASS;
239 }
240 tokenString.isString = true;
241 return tokenString;
242 }
243
244 function tokenStringFactory(delimiter, tokenOuter) {
245 while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
196 246 delimiter = delimiter.substr(1);
197 247
198 248 var singleline = delimiter.length == 1;
@@ -206,7 +256,7 b''
206 256 if (singleline && stream.eol())
207 257 return OUTCLASS;
208 258 } else if (stream.match(delimiter)) {
209 state.tokenize = tokenBase;
259 state.tokenize = tokenOuter;
210 260 return OUTCLASS;
211 261 } else {
212 262 stream.eat(/['"]/);
@@ -216,7 +266,7 b''
216 266 if (parserConf.singleLineStringErrors)
217 267 return ERRORCLASS;
218 268 else
219 state.tokenize = tokenBase;
269 state.tokenize = tokenOuter;
220 270 }
221 271 return OUTCLASS;
222 272 }
@@ -224,21 +274,23 b''
224 274 return tokenString;
225 275 }
226 276
227 function pushScope(stream, state, type) {
228 var offset = 0, align = null;
229 if (type == "py") {
230 while (top(state).type != "py")
231 state.scopes.pop();
232 }
233 offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent);
234 if (type != "py" && !stream.match(/^(\s|#.*)*$/, false))
235 align = stream.column() + 1;
236 state.scopes.push({offset: offset, type: type, align: align});
277 function pushPyScope(state) {
278 while (top(state).type != "py") state.scopes.pop()
279 state.scopes.push({offset: top(state).offset + conf.indentUnit,
280 type: "py",
281 align: null})
282 }
283
284 function pushBracketScope(stream, state, type) {
285 var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1
286 state.scopes.push({offset: state.indent + hangingIndent,
287 type: type,
288 align: align})
237 289 }
238 290
239 291 function dedent(stream, state) {
240 292 var indented = stream.indentation();
241 while (top(state).offset > indented) {
293 while (state.scopes.length > 1 && top(state).offset > indented) {
242 294 if (top(state).type != "py") return true;
243 295 state.scopes.pop();
244 296 }
@@ -246,17 +298,16 b''
246 298 }
247 299
248 300 function tokenLexer(stream, state) {
301 if (stream.sol()) state.beginningOfLine = true;
302
249 303 var style = state.tokenize(stream, state);
250 304 var current = stream.current();
251 305
252 306 // Handle decorators
253 if (current == "@"){
254 if(parserConf.version && parseInt(parserConf.version, 10) == 3){
255 return stream.match(identifiers, false) ? "meta" : "operator";
256 } else {
257 return stream.match(identifiers, false) ? "meta" : ERRORCLASS;
258 }
259 }
307 if (state.beginningOfLine && current == "@")
308 return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS;
309
310 if (/\S/.test(current)) state.beginningOfLine = false;
260 311
261 312 if ((style == "variable" || style == "builtin")
262 313 && state.lastToken == "meta")
@@ -268,16 +319,18 b''
268 319
269 320 if (current == "lambda") state.lambda = true;
270 321 if (current == ":" && !state.lambda && top(state).type == "py")
271 pushScope(stream, state, "py");
322 pushPyScope(state);
272 323
273 var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
274 if (delimiter_index != -1)
275 pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
324 if (current.length == 1 && !/string|comment/.test(style)) {
325 var delimiter_index = "[({".indexOf(current);
326 if (delimiter_index != -1)
327 pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
276 328
277 delimiter_index = "])}".indexOf(current);
278 if (delimiter_index != -1) {
279 if (top(state).type == current) state.scopes.pop();
280 else return ERRORCLASS;
329 delimiter_index = "])}".indexOf(current);
330 if (delimiter_index != -1) {
331 if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
332 else return ERRORCLASS;
333 }
281 334 }
282 335 if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
283 336 if (state.scopes.length > 1) state.scopes.pop();
@@ -292,6 +345,7 b''
292 345 return {
293 346 tokenize: tokenBase,
294 347 scopes: [{offset: basecolumn || 0, type: "py", align: null}],
348 indent: basecolumn || 0,
295 349 lastToken: null,
296 350 lambda: false,
297 351 dedent: 0
@@ -316,16 +370,14 b''
316 370 if (state.tokenize != tokenBase)
317 371 return state.tokenize.isString ? CodeMirror.Pass : 0;
318 372
319 var scope = top(state);
320 var closing = textAfter && textAfter.charAt(0) == scope.type;
373 var scope = top(state), closing = scope.type == textAfter.charAt(0)
321 374 if (scope.align != null)
322 return scope.align - (closing ? 1 : 0);
323 else if (closing && state.scopes.length > 1)
324 return state.scopes[state.scopes.length - 2].offset;
375 return scope.align - (closing ? 1 : 0)
325 376 else
326 return scope.offset;
377 return scope.offset - (closing ? hangingIndent : 0)
327 378 },
328 379
380 electricInput: /^\s*[\}\]\)]$/,
329 381 closeBrackets: {triples: "'\""},
330 382 lineComment: "#",
331 383 fold: "indent"
@@ -339,8 +391,8 b''
339 391
340 392 CodeMirror.defineMIME("text/x-cython", {
341 393 name: "python",
342 extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
343 "extern gil include nogil property public"+
394 extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
395 "extern gil include nogil property public "+
344 396 "readonly struct union DEF IF ELIF ELSE")
345 397 });
346 398
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,7 +25,7 b' CodeMirror.defineMode("q",function(confi'
25 25 return(state.tokenize=tokenLineComment)(stream,state);
26 26 else if(c=="\\"){
27 27 if(stream.eol()||/\s/.test(stream.peek()))
28 return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream, state):state.tokenize=tokenBase,"comment";
28 return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream):state.tokenize=tokenBase,"comment";
29 29 else
30 30 return state.tokenize=tokenBase,"builtin";
31 31 }
@@ -34,25 +34,25 b' CodeMirror.defineMode("q",function(confi'
34 34 if(c=='"')
35 35 return(state.tokenize=tokenString)(stream,state);
36 36 if(c=='`')
37 return stream.eatWhile(/[A-Z|a-z|\d|_|:|\/|\.]/),"symbol";
37 return stream.eatWhile(/[A-Za-z\d_:\/.]/),"symbol";
38 38 if(("."==c&&/\d/.test(stream.peek()))||/\d/.test(c)){
39 39 var t=null;
40 40 stream.backUp(1);
41 if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([D|T](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
41 if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([DT](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
42 42 || stream.match(/^\d+D(\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)/)
43 43 || stream.match(/^\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?/)
44 44 || stream.match(/^\d+[ptuv]{1}/))
45 45 t="temporal";
46 46 else if(stream.match(/^0[NwW]{1}/)
47 || stream.match(/^0x[\d|a-f|A-F]*/)
48 || stream.match(/^[0|1]+[b]{1}/)
47 || stream.match(/^0x[\da-fA-F]*/)
48 || stream.match(/^[01]+[b]{1}/)
49 49 || stream.match(/^\d+[chijn]{1}/)
50 50 || stream.match(/-?\d*(\.\d*)?(e[+\-]?\d+)?(e|f)?/))
51 51 t="number";
52 52 return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),"error");
53 53 }
54 if(/[A-Z|a-z]|\./.test(c))
55 return stream.eatWhile(/[A-Z|a-z|\.|_|\d]/),keywords.test(stream.current())?"keyword":"variable";
54 if(/[A-Za-z]|\./.test(c))
55 return stream.eatWhile(/[A-Za-z._\d]/),keywords.test(stream.current())?"keyword":"variable";
56 56 if(/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c))
57 57 return null;
58 58 if(/[{}\(\[\]\)]/.test(c))
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,16 +11,25 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 CodeMirror.registerHelper("wordChars", "r", /[\w.]/);
15
14 16 CodeMirror.defineMode("r", function(config) {
15 function wordObj(str) {
16 var words = str.split(" "), res = {};
17 function wordObj(words) {
18 var res = {};
17 19 for (var i = 0; i < words.length; ++i) res[words[i]] = true;
18 20 return res;
19 21 }
20 var atoms = wordObj("NULL NA Inf NaN NA_integer_ NA_real_ NA_complex_ NA_character_");
21 var builtins = wordObj("list quote bquote eval return call parse deparse");
22 var keywords = wordObj("if else repeat while function for in next break");
23 var blockkeywords = wordObj("if else repeat while function for");
22 var commonAtoms = ["NULL", "NA", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_", "TRUE", "FALSE"];
23 var commonBuiltins = ["list", "quote", "bquote", "eval", "return", "call", "parse", "deparse"];
24 var commonKeywords = ["if", "else", "repeat", "while", "function", "for", "in", "next", "break"];
25 var commonBlockKeywords = ["if", "else", "repeat", "while", "function", "for"];
26
27 CodeMirror.registerHelper("hintWords", "r", commonAtoms.concat(commonBuiltins, commonKeywords));
28
29 var atoms = wordObj(commonAtoms);
30 var builtins = wordObj(commonBuiltins);
31 var keywords = wordObj(commonKeywords);
32 var blockkeywords = wordObj(commonBlockKeywords);
24 33 var opChars = /[+\-*\/^<>=!&|~$:]/;
25 34 var curPunc;
26 35
@@ -42,6 +51,9 b' CodeMirror.defineMode("r", function(conf'
42 51 } else if (ch == "'" || ch == '"') {
43 52 state.tokenize = tokenString(ch);
44 53 return "string";
54 } else if (ch == "`") {
55 stream.match(/[^`]+`/);
56 return "variable-3";
45 57 } else if (ch == "." && stream.match(/.[.\d]+/)) {
46 58 return "keyword";
47 59 } else if (/[\w\.]/.test(ch) && ch != "_") {
@@ -60,13 +72,17 b' CodeMirror.defineMode("r", function(conf'
60 72 return "variable";
61 73 } else if (ch == "%") {
62 74 if (stream.skipTo("%")) stream.next();
63 return "variable-2";
64 } else if (ch == "<" && stream.eat("-")) {
65 return "arrow";
75 return "operator variable-2";
76 } else if (
77 (ch == "<" && stream.eat("-")) ||
78 (ch == "<" && stream.match("<-")) ||
79 (ch == "-" && stream.match(/>>?/))
80 ) {
81 return "operator arrow";
66 82 } else if (ch == "=" && state.ctx.argList) {
67 83 return "arg-is";
68 84 } else if (opChars.test(ch)) {
69 if (ch == "$") return "dollar";
85 if (ch == "$") return "operator dollar";
70 86 stream.eatWhile(opChars);
71 87 return "operator";
72 88 } else if (/[\(\){}\[\];]/.test(ch)) {
@@ -99,13 +115,23 b' CodeMirror.defineMode("r", function(conf'
99 115 };
100 116 }
101 117
118 var ALIGN_YES = 1, ALIGN_NO = 2, BRACELESS = 4
119
102 120 function push(state, type, stream) {
103 121 state.ctx = {type: type,
104 122 indent: state.indent,
105 align: null,
123 flags: 0,
106 124 column: stream.column(),
107 125 prev: state.ctx};
108 126 }
127 function setFlag(state, flag) {
128 var ctx = state.ctx
129 state.ctx = {type: ctx.type,
130 indent: ctx.indent,
131 flags: ctx.flags | flag,
132 column: ctx.column,
133 prev: ctx.prev}
134 }
109 135 function pop(state) {
110 136 state.indent = state.ctx.indent;
111 137 state.ctx = state.ctx.prev;
@@ -116,22 +142,22 b' CodeMirror.defineMode("r", function(conf'
116 142 return {tokenize: tokenBase,
117 143 ctx: {type: "top",
118 144 indent: -config.indentUnit,
119 align: false},
145 flags: ALIGN_NO},
120 146 indent: 0,
121 147 afterIdent: false};
122 148 },
123 149
124 150 token: function(stream, state) {
125 151 if (stream.sol()) {
126 if (state.ctx.align == null) state.ctx.align = false;
152 if ((state.ctx.flags & 3) == 0) state.ctx.flags |= ALIGN_NO
153 if (state.ctx.flags & BRACELESS) pop(state)
127 154 state.indent = stream.indentation();
128 155 }
129 156 if (stream.eatSpace()) return null;
130 157 var style = state.tokenize(stream, state);
131 if (style != "comment" && state.ctx.align == null) state.ctx.align = true;
158 if (style != "comment" && (state.ctx.flags & ALIGN_NO) == 0) setFlag(state, ALIGN_YES)
132 159
133 var ctype = state.ctx.type;
134 if ((curPunc == ";" || curPunc == "{" || curPunc == "}") && ctype == "block") pop(state);
160 if ((curPunc == ";" || curPunc == "{" || curPunc == "}") && state.ctx.type == "block") pop(state);
135 161 if (curPunc == "{") push(state, "}", stream);
136 162 else if (curPunc == "(") {
137 163 push(state, ")", stream);
@@ -139,7 +165,8 b' CodeMirror.defineMode("r", function(conf'
139 165 }
140 166 else if (curPunc == "[") push(state, "]", stream);
141 167 else if (curPunc == "block") push(state, "block", stream);
142 else if (curPunc == ctype) pop(state);
168 else if (curPunc == state.ctx.type) pop(state);
169 else if (state.ctx.type == "block" && style != "comment") setFlag(state, BRACELESS)
143 170 state.afterIdent = style == "variable" || style == "keyword";
144 171 return style;
145 172 },
@@ -148,8 +175,9 b' CodeMirror.defineMode("r", function(conf'
148 175 if (state.tokenize != tokenBase) return 0;
149 176 var firstChar = textAfter && textAfter.charAt(0), ctx = state.ctx,
150 177 closing = firstChar == ctx.type;
178 if (ctx.flags & BRACELESS) ctx = ctx.prev
151 179 if (ctx.type == "block") return ctx.indent + (firstChar == "{" ? 0 : config.indentUnit);
152 else if (ctx.align) return ctx.column + (closing ? 0 : 1);
180 else if (ctx.flags & ALIGN_YES) return ctx.column + (closing ? 0 : 1);
153 181 else return ctx.indent + (closing ? 0 : config.indentUnit);
154 182 },
155 183
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -28,7 +28,8 b' CodeMirror.defineMode("ruby", function(c'
28 28 var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then",
29 29 "catch", "loop", "proc", "begin"]);
30 30 var dedentWords = wordObj(["end", "until"]);
31 var matching = {"[": "]", "{": "}", "(": ")"};
31 var opening = {"[": "]", "{": "}", "(": ")"};
32 var closing = {"]": "[", "}": "{", ")": "("};
32 33 var curPunc;
33 34
34 35 function chain(newtok, stream, state) {
@@ -46,22 +47,10 b' CodeMirror.defineMode("ruby", function(c'
46 47 if (ch == "`" || ch == "'" || ch == '"') {
47 48 return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
48 49 } else if (ch == "/") {
49 var currentIndex = stream.current().length;
50 if (stream.skipTo("/")) {
51 var search_till = stream.current().length;
52 stream.backUp(stream.current().length - currentIndex);
53 var balance = 0; // balance brackets
54 while (stream.current().length < search_till) {
55 var chchr = stream.next();
56 if (chchr == "(") balance += 1;
57 else if (chchr == ")") balance -= 1;
58 if (balance < 0) break;
59 }
60 stream.backUp(stream.current().length - currentIndex);
61 if (balance == 0)
62 return chain(readQuoted(ch, "string-2", true), stream, state);
63 }
64 return "operator";
50 if (regexpAhead(stream))
51 return chain(readQuoted(ch, "string-2", true), stream, state);
52 else
53 return "operator";
65 54 } else if (ch == "%") {
66 55 var style = "string", embed = true;
67 56 if (stream.eat("s")) style = "atom";
@@ -70,13 +59,13 b' CodeMirror.defineMode("ruby", function(c'
70 59 else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
71 60 var delim = stream.eat(/[^\w\s=]/);
72 61 if (!delim) return "operator";
73 if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
62 if (opening.propertyIsEnumerable(delim)) delim = opening[delim];
74 63 return chain(readQuoted(delim, style, embed, true), stream, state);
75 64 } else if (ch == "#") {
76 65 stream.skipToEnd();
77 66 return "comment";
78 } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
79 return chain(readHereDoc(m[1]), stream, state);
67 } else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
68 return chain(readHereDoc(m[2], m[1]), stream, state);
80 69 } else if (ch == "0") {
81 70 if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
82 71 else if (stream.eat("b")) stream.eatWhile(/[01]/);
@@ -148,6 +137,28 b' CodeMirror.defineMode("ruby", function(c'
148 137 }
149 138 }
150 139
140 function regexpAhead(stream) {
141 var start = stream.pos, depth = 0, next, found = false, escaped = false
142 while ((next = stream.next()) != null) {
143 if (!escaped) {
144 if ("[{(".indexOf(next) > -1) {
145 depth++
146 } else if ("]})".indexOf(next) > -1) {
147 depth--
148 if (depth < 0) break
149 } else if (next == "/" && depth == 0) {
150 found = true
151 break
152 }
153 escaped = next == "\\"
154 } else {
155 escaped = false
156 }
157 }
158 stream.backUp(stream.pos - start)
159 return found
160 }
161
151 162 function tokenBaseUntilBrace(depth) {
152 163 if (!depth) depth = 1;
153 164 return function(stream, state) {
@@ -206,8 +217,9 b' CodeMirror.defineMode("ruby", function(c'
206 217 return style;
207 218 };
208 219 }
209 function readHereDoc(phrase) {
220 function readHereDoc(phrase, mayIndent) {
210 221 return function(stream, state) {
222 if (mayIndent) stream.eatSpace()
211 223 if (stream.match(phrase)) state.tokenize.pop();
212 224 else stream.skipToEnd();
213 225 return "string";
@@ -266,17 +278,18 b' CodeMirror.defineMode("ruby", function(c'
266 278 },
267 279
268 280 indent: function(state, textAfter) {
269 if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0;
281 if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass;
270 282 var firstChar = textAfter && textAfter.charAt(0);
271 283 var ct = state.context;
272 var closing = ct.type == matching[firstChar] ||
284 var closed = ct.type == closing[firstChar] ||
273 285 ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
274 return ct.indented + (closing ? 0 : config.indentUnit) +
286 return ct.indented + (closed ? 0 : config.indentUnit) +
275 287 (state.continuedLine ? config.indentUnit : 0);
276 288 },
277 289
278 electricInput: /^\s*(?:end|rescue|\})$/,
279 lineComment: "#"
290 electricInput: /^\s*(?:end|rescue|elsif|else|\})$/,
291 lineComment: "#",
292 fold: "indent"
280 293 };
281 294 });
282 295
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -68,4 +68,5 b' CodeMirror.defineSimpleMode("rust",{'
68 68
69 69
70 70 CodeMirror.defineMIME("text/x-rustsrc", "rust");
71 CodeMirror.defineMIME("text/rust", "rust");
71 72 });
@@ -1,17 +1,23 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"), require("../css/css"));
7 7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror", "../css/css"], mod);
9 9 else // Plain browser env
10 10 mod(CodeMirror);
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 14 CodeMirror.defineMode("sass", function(config) {
15 var cssMode = CodeMirror.mimeModes["text/css"];
16 var propertyKeywords = cssMode.propertyKeywords || {},
17 colorKeywords = cssMode.colorKeywords || {},
18 valueKeywords = cssMode.valueKeywords || {},
19 fontProperties = cssMode.fontProperties || {};
20
15 21 function tokenRegexp(words) {
16 22 return new RegExp("^" + words.join("|"));
17 23 }
@@ -25,6 +31,12 b' CodeMirror.defineMode("sass", function(c'
25 31
26 32 var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
27 33
34 var word;
35
36 function isEndLine(stream) {
37 return !stream.peek() || stream.match(/\s+$/, false);
38 }
39
28 40 function urlTokens(stream, state) {
29 41 var ch = stream.peek();
30 42
@@ -76,6 +88,9 b' CodeMirror.defineMode("sass", function(c'
76 88
77 89 if (endingString) {
78 90 if (nextChar !== quote && greedy) { stream.next(); }
91 if (isEndLine(stream)) {
92 state.cursorHalf = 0;
93 }
79 94 state.tokenizer = tokenBase;
80 95 return "string";
81 96 } else if (nextChar === "#" && peekChar === "{") {
@@ -147,14 +162,20 b' CodeMirror.defineMode("sass", function(c'
147 162 // first half i.e. before : for key-value pairs
148 163 // including selectors
149 164
165 if (ch === "-") {
166 if (stream.match(/^-\w+-/)) {
167 return "meta";
168 }
169 }
170
150 171 if (ch === ".") {
151 172 stream.next();
152 173 if (stream.match(/^[\w-]+/)) {
153 174 indent(state);
154 return "atom";
175 return "qualifier";
155 176 } else if (stream.peek() === "#") {
156 177 indent(state);
157 return "atom";
178 return "tag";
158 179 }
159 180 }
160 181
@@ -163,11 +184,11 b' CodeMirror.defineMode("sass", function(c'
163 184 // ID selectors
164 185 if (stream.match(/^[\w-]+/)) {
165 186 indent(state);
166 return "atom";
187 return "builtin";
167 188 }
168 189 if (stream.peek() === "#") {
169 190 indent(state);
170 return "atom";
191 return "tag";
171 192 }
172 193 }
173 194
@@ -220,37 +241,48 b' CodeMirror.defineMode("sass", function(c'
220 241 // Indent Directives
221 242 if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
222 243 indent(state);
223 return "meta";
244 return "def";
224 245 }
225 246
226 247 // Other Directives
227 248 if (ch === "@") {
228 249 stream.next();
229 250 stream.eatWhile(/[\w-]/);
230 return "meta";
251 return "def";
231 252 }
232 253
233 254 if (stream.eatWhile(/[\w-]/)){
234 255 if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
235 return "property";
256 word = stream.current().toLowerCase();
257 var prop = state.prevProp + "-" + word;
258 if (propertyKeywords.hasOwnProperty(prop)) {
259 return "property";
260 } else if (propertyKeywords.hasOwnProperty(word)) {
261 state.prevProp = word;
262 return "property";
263 } else if (fontProperties.hasOwnProperty(word)) {
264 return "property";
265 }
266 return "tag";
236 267 }
237 268 else if(stream.match(/ *:/,false)){
238 269 indent(state);
239 270 state.cursorHalf = 1;
240 return "atom";
271 state.prevProp = stream.current().toLowerCase();
272 return "property";
241 273 }
242 274 else if(stream.match(/ *,/,false)){
243 return "atom";
275 return "tag";
244 276 }
245 277 else{
246 278 indent(state);
247 return "atom";
279 return "tag";
248 280 }
249 281 }
250 282
251 283 if(ch === ":"){
252 284 if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
253 return "keyword";
285 return "variable-3";
254 286 }
255 287 stream.next();
256 288 state.cursorHalf=1;
@@ -264,7 +296,7 b' CodeMirror.defineMode("sass", function(c'
264 296 stream.next();
265 297 // Hex numbers
266 298 if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
267 if(!stream.peek()){
299 if (isEndLine(stream)) {
268 300 state.cursorHalf = 0;
269 301 }
270 302 return "number";
@@ -273,7 +305,7 b' CodeMirror.defineMode("sass", function(c'
273 305
274 306 // Numbers
275 307 if (stream.match(/^-?[0-9\.]+/)){
276 if(!stream.peek()){
308 if (isEndLine(stream)) {
277 309 state.cursorHalf = 0;
278 310 }
279 311 return "number";
@@ -281,14 +313,14 b' CodeMirror.defineMode("sass", function(c'
281 313
282 314 // Units
283 315 if (stream.match(/^(px|em|in)\b/)){
284 if(!stream.peek()){
316 if (isEndLine(stream)) {
285 317 state.cursorHalf = 0;
286 318 }
287 319 return "unit";
288 320 }
289 321
290 322 if (stream.match(keywordsRegexp)){
291 if(!stream.peek()){
323 if (isEndLine(stream)) {
292 324 state.cursorHalf = 0;
293 325 }
294 326 return "keyword";
@@ -296,7 +328,7 b' CodeMirror.defineMode("sass", function(c'
296 328
297 329 if (stream.match(/^url/) && stream.peek() === "(") {
298 330 state.tokenizer = urlTokens;
299 if(!stream.peek()){
331 if (isEndLine(stream)) {
300 332 state.cursorHalf = 0;
301 333 }
302 334 return "atom";
@@ -306,23 +338,21 b' CodeMirror.defineMode("sass", function(c'
306 338 if (ch === "$") {
307 339 stream.next();
308 340 stream.eatWhile(/[\w-]/);
309 if(!stream.peek()){
341 if (isEndLine(stream)) {
310 342 state.cursorHalf = 0;
311 343 }
312 return "variable-3";
344 return "variable-2";
313 345 }
314 346
315 347 // bang character for !important, !default, etc.
316 348 if (ch === "!") {
317 349 stream.next();
318 if(!stream.peek()){
319 state.cursorHalf = 0;
320 }
350 state.cursorHalf = 0;
321 351 return stream.match(/^[\w]+/) ? "keyword": "operator";
322 352 }
323 353
324 354 if (stream.match(opRegexp)){
325 if(!stream.peek()){
355 if (isEndLine(stream)) {
326 356 state.cursorHalf = 0;
327 357 }
328 358 return "operator";
@@ -330,14 +360,24 b' CodeMirror.defineMode("sass", function(c'
330 360
331 361 // attributes
332 362 if (stream.eatWhile(/[\w-]/)) {
333 if(!stream.peek()){
363 if (isEndLine(stream)) {
334 364 state.cursorHalf = 0;
335 365 }
336 return "attribute";
366 word = stream.current().toLowerCase();
367 if (valueKeywords.hasOwnProperty(word)) {
368 return "atom";
369 } else if (colorKeywords.hasOwnProperty(word)) {
370 return "keyword";
371 } else if (propertyKeywords.hasOwnProperty(word)) {
372 state.prevProp = stream.current().toLowerCase();
373 return "property";
374 } else {
375 return "tag";
376 }
337 377 }
338 378
339 379 //stream.eatSpace();
340 if(!stream.peek()){
380 if (isEndLine(stream)) {
341 381 state.cursorHalf = 0;
342 382 return null;
343 383 }
@@ -407,7 +447,7 b' CodeMirror.defineMode("sass", function(c'
407 447 return state.scopes[0].offset;
408 448 }
409 449 };
410 });
450 }, "css");
411 451
412 452 CodeMirror.defineMIME("text/x-sass", "sass");
413 453
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**
5 5 * Author: Koh Zi Han, based on implementation by Koh Zi Chun
@@ -73,7 +73,8 b' CodeMirror.defineMode("scheme", function'
73 73 indentStack: null,
74 74 indentation: 0,
75 75 mode: false,
76 sExprComment: false
76 sExprComment: false,
77 sExprQuote: false
77 78 };
78 79 },
79 80
@@ -121,7 +122,7 b' CodeMirror.defineMode("scheme", function'
121 122 state.sExprComment = 0;
122 123 }else{
123 124 // if not we just comment the entire of the next token
124 stream.eatWhile(/[^/s]/); // eat non spaces
125 stream.eatWhile(/[^\s\(\)\[\]]/); // eat symbol atom
125 126 returnType = COMMENT;
126 127 break;
127 128 }
@@ -133,7 +134,15 b' CodeMirror.defineMode("scheme", function'
133 134 returnType = STRING;
134 135
135 136 } else if (ch == "'") {
136 returnType = ATOM;
137 if (stream.peek() == "(" || stream.peek() == "["){
138 if (typeof state.sExprQuote != "number") {
139 state.sExprQuote = 0;
140 } // else already in a quoted expression
141 returnType = ATOM;
142 } else {
143 stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/);
144 returnType = ATOM;
145 }
137 146 } else if (ch == '#') {
138 147 if (stream.eat("|")) { // Multi-line comment
139 148 state.mode = "comment"; // toggle to comment mode
@@ -209,6 +218,7 b' CodeMirror.defineMode("scheme", function'
209 218 stream.backUp(stream.current().length - 1); // undo all the eating
210 219
211 220 if(typeof state.sExprComment == "number") state.sExprComment++;
221 if(typeof state.sExprQuote == "number") state.sExprQuote++;
212 222
213 223 returnType = BRACKET;
214 224 } else if (ch == ")" || ch == "]") {
@@ -222,16 +232,22 b' CodeMirror.defineMode("scheme", function'
222 232 state.sExprComment = false; // turn off s-expr commenting mode
223 233 }
224 234 }
235 if(typeof state.sExprQuote == "number"){
236 if(--state.sExprQuote == 0){
237 returnType = ATOM; // final closing bracket
238 state.sExprQuote = false; // turn off s-expr quote mode
239 }
240 }
225 241 }
226 242 } else {
227 stream.eatWhile(/[\w\$_\-!$%&*+\.\/:<=>?@\^~]/);
243 stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/);
228 244
229 245 if (keywords && keywords.propertyIsEnumerable(stream.current())) {
230 246 returnType = BUILTIN;
231 247 } else returnType = "variable";
232 248 }
233 249 }
234 return (typeof state.sExprComment == "number") ? COMMENT : returnType;
250 return (typeof state.sExprComment == "number") ? COMMENT : ((typeof state.sExprQuote == "number") ? ATOM : returnType);
235 251 },
236 252
237 253 indent: function (state) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,26 +14,27 b''
14 14 CodeMirror.defineMode('shell', function() {
15 15
16 16 var words = {};
17 function define(style, string) {
18 var split = string.split(' ');
19 for(var i = 0; i < split.length; i++) {
20 words[split[i]] = style;
17 function define(style, dict) {
18 for(var i = 0; i < dict.length; i++) {
19 words[dict[i]] = style;
21 20 }
22 21 };
23 22
24 // Atoms
25 define('atom', 'true false');
26
27 // Keywords
28 define('keyword', 'if then do else elif while until for in esac fi fin ' +
29 'fil done exit set unset export function');
23 var commonAtoms = ["true", "false"];
24 var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi",
25 "fin", "fil", "done", "exit", "set", "unset", "export", "function"];
26 var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear",
27 "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall",
28 "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm",
29 "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop",
30 "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write",
31 "yes", "zsh"];
30 32
31 // Commands
32 define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' +
33 'curl cut diff echo find gawk gcc get git grep kill killall ln ls make ' +
34 'mkdir openssl mv nc node npm ping ps restart rm rmdir sed service sh ' +
35 'shopt shred source sort sleep ssh start stop su sudo tee telnet top ' +
36 'touch vi vim wall wc wget who write yes zsh');
33 CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands));
34
35 define('atom', commonAtoms);
36 define('keyword', commonKeywords);
37 define('builtin', commonCommands);
37 38
38 39 function tokenBase(stream, state) {
39 40 if (stream.eatSpace()) return null;
@@ -46,7 +47,7 b" CodeMirror.defineMode('shell', function("
46 47 return null;
47 48 }
48 49 if (ch === '\'' || ch === '"' || ch === '`') {
49 state.tokens.unshift(tokenString(ch));
50 state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string"));
50 51 return tokenize(stream, state);
51 52 }
52 53 if (ch === '#') {
@@ -81,41 +82,49 b" CodeMirror.defineMode('shell', function("
81 82 return words.hasOwnProperty(cur) ? words[cur] : null;
82 83 }
83 84
84 function tokenString(quote) {
85 function tokenString(quote, style) {
86 var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
85 87 return function(stream, state) {
86 var next, end = false, escaped = false;
88 var next, escaped = false;
87 89 while ((next = stream.next()) != null) {
88 if (next === quote && !escaped) {
89 end = true;
90 if (next === close && !escaped) {
91 state.tokens.shift();
90 92 break;
91 }
92 if (next === '$' && !escaped && quote !== '\'') {
93 } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
93 94 escaped = true;
94 95 stream.backUp(1);
95 96 state.tokens.unshift(tokenDollar);
96 97 break;
98 } else if (!escaped && quote !== close && next === quote) {
99 state.tokens.unshift(tokenString(quote, style))
100 return tokenize(stream, state)
101 } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
102 state.tokens.unshift(tokenStringStart(next, "string"));
103 stream.backUp(1);
104 break;
97 105 }
98 106 escaped = !escaped && next === '\\';
99 107 }
100 if (end || !escaped) {
101 state.tokens.shift();
102 }
103 return (quote === '`' || quote === ')' ? 'quote' : 'string');
108 return style;
104 109 };
105 110 };
106 111
112 function tokenStringStart(quote, style) {
113 return function(stream, state) {
114 state.tokens[0] = tokenString(quote, style)
115 stream.next()
116 return tokenize(stream, state)
117 }
118 }
119
107 120 var tokenDollar = function(stream, state) {
108 121 if (state.tokens.length > 1) stream.eat('$');
109 var ch = stream.next(), hungry = /\w/;
110 if (ch === '{') hungry = /[^}]/;
111 if (ch === '(') {
112 state.tokens[0] = tokenString(')');
122 var ch = stream.next()
123 if (/['"({]/.test(ch)) {
124 state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string");
113 125 return tokenize(stream, state);
114 126 }
115 if (!/\d/.test(ch)) {
116 stream.eatWhile(hungry);
117 stream.eat('}');
118 }
127 if (!/\d/.test(ch)) stream.eatWhile(/\w/);
119 128 state.tokens.shift();
120 129 return 'def';
121 130 };
@@ -129,11 +138,15 b" CodeMirror.defineMode('shell', function("
129 138 token: function(stream, state) {
130 139 return tokenize(stream, state);
131 140 },
141 closeBrackets: "()[]{}''\"\"``",
132 142 lineComment: '#',
133 143 fold: "brace"
134 144 };
135 145 });
136 146
137 147 CodeMirror.defineMIME('text/x-sh', 'shell');
148 // Apache uses a slightly different Media Type for Shell scripts
149 // http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
150 CodeMirror.defineMIME('application/x-sh', 'shell');
138 151
139 152 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -170,7 +170,7 b' CodeMirror.defineMode("sieve", function('
170 170 if (stream.eatSpace())
171 171 return null;
172 172
173 return (state.tokenize || tokenBase)(stream, state);;
173 return (state.tokenize || tokenBase)(stream, state);
174 174 },
175 175
176 176 indent: function(state, _textAfter) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh
5 5
@@ -165,7 +165,7 b''
165 165 };
166 166 return function(stream, state) {
167 167 rubyState = state.rubyState;
168 state.rubyState = rubyMode.startState();
168 state.rubyState = CodeMirror.startState(rubyMode);
169 169 state.tokenize = runSplat;
170 170 return ruby(stream, state);
171 171 };
@@ -317,7 +317,7 b''
317 317
318 318 function startSubMode(mode, state) {
319 319 var subMode = getMode(mode);
320 var subState = subMode.startState && subMode.startState();
320 var subState = CodeMirror.startState(subMode);
321 321
322 322 state.subMode = subMode;
323 323 state.subState = subState;
@@ -507,8 +507,8 b''
507 507 var mode = {
508 508 // default to html mode
509 509 startState: function() {
510 var htmlState = htmlMode.startState();
511 var rubyState = rubyMode.startState();
510 var htmlState = CodeMirror.startState(htmlMode);
511 var rubyState = CodeMirror.startState(rubyMode);
512 512 return {
513 513 htmlState: htmlState,
514 514 rubyState: rubyState,
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /**
5 5 * Smarty 2 and 3 mode.
@@ -210,9 +210,9 b''
210 210 state.last = last;
211 211 return style;
212 212 },
213 indent: function(state, text) {
213 indent: function(state, text, line) {
214 214 if (state.tokenize == tokenTop && baseMode.indent)
215 return baseMode.indent(state.base, text);
215 return baseMode.indent(state.base, text, line);
216 216 else
217 217 return CodeMirror.Pass;
218 218 },
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,12 +14,12 b''
14 14 CodeMirror.defineMode("solr", function() {
15 15 "use strict";
16 16
17 var isStringChar = /[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\^\"\\]/;
17 var isStringChar = /[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\"\\]/;
18 18 var isOperatorChar = /[\|\!\+\-\*\?\~\^\&]/;
19 19 var isOperatorString = /^(OR|AND|NOT|TO)$/i;
20 20
21 21 function isNumber(word) {
22 return parseFloat(word, 10).toString() === word;
22 return parseFloat(word).toString() === word;
23 23 }
24 24
25 25 function tokenString(quote) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,9 +11,45 b''
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 var indentingTags = ["template", "literal", "msg", "fallbackmsg", "let", "if", "elseif",
15 "else", "switch", "case", "default", "foreach", "ifempty", "for",
16 "call", "param", "deltemplate", "delcall", "log"];
14 var paramData = { noEndTag: true, soyState: "param-def" };
15 var tags = {
16 "alias": { noEndTag: true },
17 "delpackage": { noEndTag: true },
18 "namespace": { noEndTag: true, soyState: "namespace-def" },
19 "@param": paramData,
20 "@param?": paramData,
21 "@inject": paramData,
22 "@inject?": paramData,
23 "@state": paramData,
24 "@state?": paramData,
25 "template": { soyState: "templ-def", variableScope: true},
26 "literal": { },
27 "msg": {},
28 "fallbackmsg": { noEndTag: true, reduceIndent: true},
29 "select": {},
30 "plural": {},
31 "let": { soyState: "var-def" },
32 "if": {},
33 "elseif": { noEndTag: true, reduceIndent: true},
34 "else": { noEndTag: true, reduceIndent: true},
35 "switch": {},
36 "case": { noEndTag: true, reduceIndent: true},
37 "default": { noEndTag: true, reduceIndent: true},
38 "foreach": { variableScope: true, soyState: "var-def" },
39 "ifempty": { noEndTag: true, reduceIndent: true},
40 "for": { variableScope: true, soyState: "var-def" },
41 "call": { soyState: "templ-ref" },
42 "param": { soyState: "param-ref"},
43 "print": { noEndTag: true },
44 "deltemplate": { soyState: "templ-def", variableScope: true},
45 "delcall": { soyState: "templ-ref" },
46 "log": {},
47 "element": { variableScope: true },
48 };
49
50 var indentingTags = Object.keys(tags).filter(function(tag) {
51 return !tags[tag].noEndTag || tags[tag].reduceIndent;
52 });
17 53
18 54 CodeMirror.defineMode("soy", function(config) {
19 55 var textMode = CodeMirror.getMode(config, "text/plain");
@@ -22,6 +58,7 b''
22 58 attributes: textMode,
23 59 text: textMode,
24 60 uri: textMode,
61 trusted_resource_uri: textMode,
25 62 css: CodeMirror.getMode(config, "text/css"),
26 63 js: CodeMirror.getMode(config, {name: "text/javascript", statementIndent: 2 * config.indentUnit})
27 64 };
@@ -31,6 +68,12 b''
31 68 }
32 69
33 70 function tokenUntil(stream, state, untilRegExp) {
71 if (stream.sol()) {
72 for (var indent = 0; indent < state.indent; indent++) {
73 if (!stream.eat(/\s/)) break;
74 }
75 if (indent) return null;
76 }
34 77 var oldString = stream.string;
35 78 var match = untilRegExp.exec(oldString.substr(stream.pos));
36 79 if (match) {
@@ -39,33 +82,82 b''
39 82 stream.string = oldString.substr(0, stream.pos + match.index);
40 83 }
41 84 var result = stream.hideFirstChars(state.indent, function() {
42 return state.localMode.token(stream, state.localState);
85 var localState = last(state.localStates);
86 return localState.mode.token(stream, localState.state);
43 87 });
44 88 stream.string = oldString;
45 89 return result;
46 90 }
47 91
92 function contains(list, element) {
93 while (list) {
94 if (list.element === element) return true;
95 list = list.next;
96 }
97 return false;
98 }
99
100 function prepend(list, element) {
101 return {
102 element: element,
103 next: list
104 };
105 }
106
107 function popcontext(state) {
108 if (!state.context) return;
109 if (state.context.scope) {
110 state.variables = state.context.scope;
111 }
112 state.context = state.context.previousContext;
113 }
114
115 // Reference a variable `name` in `list`.
116 // Let `loose` be truthy to ignore missing identifiers.
117 function ref(list, name, loose) {
118 return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error");
119 }
120
121 // Data for an open soy tag.
122 function Context(previousContext, tag, scope) {
123 this.previousContext = previousContext;
124 this.tag = tag;
125 this.kind = null;
126 this.scope = scope;
127 }
128
48 129 return {
49 130 startState: function() {
50 131 return {
51 kind: [],
52 kindTag: [],
53 132 soyState: [],
133 templates: null,
134 variables: prepend(null, 'ij'),
135 scopes: null,
54 136 indent: 0,
55 localMode: modes.html,
56 localState: CodeMirror.startState(modes.html)
137 quoteKind: null,
138 context: null,
139 localStates: [{
140 mode: modes.html,
141 state: CodeMirror.startState(modes.html)
142 }]
57 143 };
58 144 },
59 145
60 146 copyState: function(state) {
61 147 return {
62 148 tag: state.tag, // Last seen Soy tag.
63 kind: state.kind.concat([]), // Values of kind="" attributes.
64 kindTag: state.kindTag.concat([]), // Opened tags with kind="" attributes.
65 149 soyState: state.soyState.concat([]),
150 templates: state.templates,
151 variables: state.variables,
152 context: state.context,
66 153 indent: state.indent, // Indentation of the following line.
67 localMode: state.localMode,
68 localState: CodeMirror.copyState(state.localMode, state.localState)
154 quoteKind: state.quoteKind,
155 localStates: state.localStates.map(function(localState) {
156 return {
157 mode: localState.mode,
158 state: CodeMirror.copyState(localState.mode, localState.state)
159 };
160 })
69 161 };
70 162 },
71 163
@@ -79,36 +171,159 b''
79 171 } else {
80 172 stream.skipToEnd();
81 173 }
174 if (!state.context || !state.context.scope) {
175 var paramRe = /@param\??\s+(\S+)/g;
176 var current = stream.current();
177 for (var match; (match = paramRe.exec(current)); ) {
178 state.variables = prepend(state.variables, match[1]);
179 }
180 }
82 181 return "comment";
83 182
84 case "variable":
85 if (stream.match(/^}/)) {
86 state.indent -= 2 * config.indentUnit;
183 case "string":
184 var match = stream.match(/^.*?(["']|\\[\s\S])/);
185 if (!match) {
186 stream.skipToEnd();
187 } else if (match[1] == state.quoteKind) {
188 state.quoteKind = null;
189 state.soyState.pop();
190 }
191 return "string";
192 }
193
194 if (!state.soyState.length || last(state.soyState) != "literal") {
195 if (stream.match(/^\/\*/)) {
196 state.soyState.push("comment");
197 return "comment";
198 } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
199 return "comment";
200 }
201 }
202
203 switch (last(state.soyState)) {
204 case "templ-def":
205 if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) {
206 state.templates = prepend(state.templates, match[1]);
207 state.soyState.pop();
208 return "def";
209 }
210 stream.next();
211 return null;
212
213 case "templ-ref":
214 if (match = stream.match(/(\.?[a-zA-Z_][a-zA-Z_0-9]+)+/)) {
87 215 state.soyState.pop();
88 return "variable-2";
216 // If the first character is '.', it can only be a local template.
217 if (match[0][0] == '.') {
218 return "variable-2"
219 }
220 // Otherwise
221 return "variable";
222 }
223 stream.next();
224 return null;
225
226 case "namespace-def":
227 if (match = stream.match(/^\.?([\w\.]+)/)) {
228 state.soyState.pop();
229 return "variable";
230 }
231 stream.next();
232 return null;
233
234 case "param-def":
235 if (match = stream.match(/^\w+/)) {
236 state.variables = prepend(state.variables, match[0]);
237 state.soyState.pop();
238 state.soyState.push("param-type");
239 return "def";
240 }
241 stream.next();
242 return null;
243
244 case "param-ref":
245 if (match = stream.match(/^\w+/)) {
246 state.soyState.pop();
247 return "property";
248 }
249 stream.next();
250 return null;
251
252 case "param-type":
253 if (stream.peek() == "}") {
254 state.soyState.pop();
255 return null;
256 }
257 if (stream.eatWhile(/^([\w]+|[?])/)) {
258 return "type";
259 }
260 stream.next();
261 return null;
262
263 case "var-def":
264 if (match = stream.match(/^\$([\w]+)/)) {
265 state.variables = prepend(state.variables, match[1]);
266 state.soyState.pop();
267 return "def";
89 268 }
90 269 stream.next();
91 270 return null;
92 271
93 272 case "tag":
273 var endTag = state.tag[0] == "/";
274 var tagName = endTag ? state.tag.substring(1) : state.tag;
275 var tag = tags[tagName];
94 276 if (stream.match(/^\/?}/)) {
95 if (state.tag == "/template" || state.tag == "/deltemplate") state.indent = 0;
96 else state.indent -= (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1) * config.indentUnit;
277 var selfClosed = stream.current() == "/}";
278 if (selfClosed && !endTag) {
279 popcontext(state);
280 }
281 if (state.tag == "/template" || state.tag == "/deltemplate") {
282 state.variables = prepend(null, 'ij');
283 state.indent = 0;
284 } else {
285 state.indent -= config.indentUnit *
286 (selfClosed || indentingTags.indexOf(state.tag) == -1 ? 2 : 1);
287 }
97 288 state.soyState.pop();
98 289 return "keyword";
99 290 } else if (stream.match(/^([\w?]+)(?==)/)) {
100 if (stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
291 if (state.context && state.context.tag == tagName && stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
101 292 var kind = match[1];
102 state.kind.push(kind);
103 state.kindTag.push(state.tag);
104 state.localMode = modes[kind] || modes.html;
105 state.localState = CodeMirror.startState(state.localMode);
293 state.context.kind = kind;
294 var mode = modes[kind] || modes.html;
295 var localState = last(state.localStates);
296 if (localState.mode.indent) {
297 state.indent += localState.mode.indent(localState.state, "", "");
298 }
299 state.localStates.push({
300 mode: mode,
301 state: CodeMirror.startState(mode)
302 });
106 303 }
107 304 return "attribute";
108 } else if (stream.match(/^"/)) {
305 } else if (match = stream.match(/([\w]+)(?=\()/)) {
306 return "variable callee";
307 } else if (match = stream.match(/^["']/)) {
109 308 state.soyState.push("string");
309 state.quoteKind = match;
110 310 return "string";
111 311 }
312 if (stream.match(/(null|true|false)(?!\w)/) ||
313 stream.match(/0x([0-9a-fA-F]{2,})/) ||
314 stream.match(/-?([0-9]*[.])?[0-9]+(e[0-9]*)?/)) {
315 return "atom";
316 }
317 if (stream.match(/(\||[+\-*\/%]|[=!]=|\?:|[<>]=?)/)) {
318 // Tokenize filter, binary, null propagator, and equality operators.
319 return "operator";
320 }
321 if (match = stream.match(/^\$([\w]+)/)) {
322 return ref(state.variables, match[1]);
323 }
324 if (match = stream.match(/^\w+/)) {
325 return /^(?:as|and|or|not|in)$/.test(match[0]) ? "keyword" : null;
326 }
112 327 stream.next();
113 328 return null;
114 329
@@ -119,40 +334,59 b''
119 334 return this.token(stream, state);
120 335 }
121 336 return tokenUntil(stream, state, /\{\/literal}/);
122
123 case "string":
124 if (stream.match(/^.*?"/)) {
125 state.soyState.pop();
126 } else {
127 stream.skipToEnd();
128 }
129 return "string";
130 337 }
131 338
132 if (stream.match(/^\/\*/)) {
133 state.soyState.push("comment");
134 return "comment";
135 } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
136 return "comment";
137 } else if (stream.match(/^\{\$[\w?]*/)) {
138 state.indent += 2 * config.indentUnit;
139 state.soyState.push("variable");
140 return "variable-2";
141 } else if (stream.match(/^\{literal}/)) {
339 if (stream.match(/^\{literal}/)) {
142 340 state.indent += config.indentUnit;
143 341 state.soyState.push("literal");
342 state.context = new Context(state.context, "literal", state.variables);
144 343 return "keyword";
145 } else if (match = stream.match(/^\{([\/@\\]?[\w?]*)/)) {
146 if (match[1] != "/switch")
147 state.indent += (/^(\/|(else|elseif|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit;
344
345 // A tag-keyword must be followed by whitespace, comment or a closing tag.
346 } else if (match = stream.match(/^\{([/@\\]?\w+\??)(?=$|[\s}]|\/[/*])/)) {
347 var prevTag = state.tag;
148 348 state.tag = match[1];
149 if (state.tag == "/" + last(state.kindTag)) {
150 // We found the tag that opened the current kind="".
151 state.kind.pop();
152 state.kindTag.pop();
153 state.localMode = modes[last(state.kind)] || modes.html;
154 state.localState = CodeMirror.startState(state.localMode);
349 var endTag = state.tag[0] == "/";
350 var indentingTag = !!tags[state.tag];
351 var tagName = endTag ? state.tag.substring(1) : state.tag;
352 var tag = tags[tagName];
353 if (state.tag != "/switch")
354 state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != "switch" ? 1 : 2) * config.indentUnit;
355
356 state.soyState.push("tag");
357 var tagError = false;
358 if (tag) {
359 if (!endTag) {
360 if (tag.soyState) state.soyState.push(tag.soyState);
361 }
362 // If a new tag, open a new context.
363 if (!tag.noEndTag && (indentingTag || !endTag)) {
364 state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null);
365 // Otherwise close the current context.
366 } else if (endTag) {
367 if (!state.context || state.context.tag != tagName) {
368 tagError = true;
369 } else if (state.context) {
370 if (state.context.kind) {
371 state.localStates.pop();
372 var localState = last(state.localStates);
373 if (localState.mode.indent) {
374 state.indent -= localState.mode.indent(localState.state, "", "");
375 }
376 }
377 popcontext(state);
378 }
379 }
380 } else if (endTag) {
381 // Assume all tags with a closing tag are defined in the config.
382 tagError = true;
155 383 }
384 return (tagError ? "error " : "") + "keyword";
385
386 // Not a tag-keyword; it's an implicit print tag.
387 } else if (stream.eat('{')) {
388 state.tag = "print";
389 state.indent += 2 * config.indentUnit;
156 390 state.soyState.push("tag");
157 391 return "keyword";
158 392 }
@@ -160,7 +394,7 b''
160 394 return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/);
161 395 },
162 396
163 indent: function(state, textAfter) {
397 indent: function(state, textAfter, line) {
164 398 var indent = state.indent, top = last(state.soyState);
165 399 if (top == "comment") return CodeMirror.Pass;
166 400
@@ -172,14 +406,16 b''
172 406 if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit;
173 407 if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit;
174 408 }
175 if (indent && state.localMode.indent)
176 indent += state.localMode.indent(state.localState, textAfter);
409 var localState = last(state.localStates);
410 if (indent && localState.mode.indent) {
411 indent += localState.mode.indent(localState.state, textAfter, line);
412 }
177 413 return indent;
178 414 },
179 415
180 416 innerMode: function(state) {
181 417 if (state.soyState.length && last(state.soyState) != "literal") return null;
182 else return {state: state.localState, mode: state.localMode};
418 else return last(state.localStates);
183 419 },
184 420
185 421 electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/,
@@ -187,12 +423,15 b''
187 423 blockCommentStart: "/*",
188 424 blockCommentEnd: "*/",
189 425 blockCommentContinue: " * ",
426 useInnerComments: false,
190 427 fold: "indent"
191 428 };
192 429 }, "htmlmixed");
193 430
194 CodeMirror.registerHelper("hintWords", "soy", indentingTags.concat(
195 ["delpackage", "namespace", "alias", "print", "css", "debugger"]));
431 CodeMirror.registerHelper("wordChars", "soy", /[\w$]/);
432
433 CodeMirror.registerHelper("hintWords", "soy", Object.keys(tags).concat(
434 ["css", "debugger"]));
196 435
197 436 CodeMirror.defineMIME("text/x-soy", "soy");
198 437 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,7 +25,7 b' CodeMirror.defineMode("sparql", function'
25 25 "strbefore", "strafter", "year", "month", "day", "hours", "minutes", "seconds",
26 26 "timezone", "tz", "now", "uuid", "struuid", "md5", "sha1", "sha256", "sha384",
27 27 "sha512", "coalesce", "if", "strlang", "strdt", "isnumeric", "regex", "exists",
28 "isblank", "isliteral", "a"]);
28 "isblank", "isliteral", "a", "bind"]);
29 29 var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
30 30 "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
31 31 "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group",
@@ -41,7 +41,7 b' CodeMirror.defineMode("sparql", function'
41 41 if(ch == "?" && stream.match(/\s/, false)){
42 42 return "operator";
43 43 }
44 stream.match(/^[\w\d]*/);
44 stream.match(/^[A-Za-z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][A-Za-z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*/);
45 45 return "variable-2";
46 46 }
47 47 else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
@@ -135,7 +135,11 b' CodeMirror.defineMode("sparql", function'
135 135 else if (curPunc == "{") pushContext(state, "}", stream.column());
136 136 else if (/[\]\}\)]/.test(curPunc)) {
137 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) {
139 popContext(state);
140 if (curPunc == "}" && state.context && state.context.type == "pattern")
141 popContext(state);
142 }
139 143 }
140 144 else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
141 145 else if (/atom|string|variable/.test(style) && state.context) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -70,7 +70,10 b''
70 70 return "operator";
71 71 case "\\":
72 72 if (stream.match(/\\[a-z]+/)) return "string-2";
73 else return null;
73 else {
74 stream.next();
75 return "atom";
76 }
74 77 case ".":
75 78 case ",":
76 79 case ";":
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -12,16 +12,17 b''
12 12 "use strict";
13 13
14 14 CodeMirror.defineMode("sql", function(config, parserConfig) {
15 "use strict";
16
17 15 var client = parserConfig.client || {},
18 16 atoms = parserConfig.atoms || {"false": true, "true": true, "null": true},
19 builtin = parserConfig.builtin || {},
20 keywords = parserConfig.keywords || {},
21 operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
17 builtin = parserConfig.builtin || set(defaultBuiltin),
18 keywords = parserConfig.keywords || set(sqlKeywords),
19 operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/,
22 20 support = parserConfig.support || {},
23 21 hooks = parserConfig.hooks || {},
24 dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};
22 dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true},
23 backslashStringEscapes = parserConfig.backslashStringEscapes !== false,
24 brackets = parserConfig.brackets || /^[\{}\(\)\[\]]/,
25 punctuation = parserConfig.punctuation || /^[;.,:]/
25 26
26 27 function tokenBase(stream, state) {
27 28 var ch = stream.next();
@@ -32,13 +33,13 b' CodeMirror.defineMode("sql", function(co'
32 33 if (result !== false) return result;
33 34 }
34 35
35 if (support.hexNumber == true &&
36 if (support.hexNumber &&
36 37 ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
37 38 || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
38 39 // hex
39 40 // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
40 41 return "number";
41 } else if (support.binaryNumber == true &&
42 } else if (support.binaryNumber &&
42 43 (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
43 44 || (ch == "0" && stream.match(/^b[01]+/)))) {
44 45 // bitstring
@@ -47,8 +48,8 b' CodeMirror.defineMode("sql", function(co'
47 48 } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
48 49 // numbers
49 50 // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
50 stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
51 support.decimallessFloat == true && stream.eat('.');
51 stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/);
52 support.decimallessFloat && stream.match(/^\.(?!\.)/);
52 53 return "number";
53 54 } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
54 55 // placeholders
@@ -58,15 +59,12 b' CodeMirror.defineMode("sql", function(co'
58 59 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
59 60 state.tokenize = tokenLiteral(ch);
60 61 return state.tokenize(stream, state);
61 } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
62 || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
62 } else if ((((support.nCharCast && (ch == "n" || ch == "N"))
63 || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
63 64 && (stream.peek() == "'" || stream.peek() == '"'))) {
64 65 // charset casting: _utf8'str', N'str', n'str'
65 66 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
66 67 return "keyword";
67 } else if (/^[\(\),\;\[\]]/.test(ch)) {
68 // no highlightning
69 return null;
70 68 } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
71 69 // 1-line comment
72 70 stream.skipToEnd();
@@ -80,22 +78,29 b' CodeMirror.defineMode("sql", function(co'
80 78 } else if (ch == "/" && stream.eat("*")) {
81 79 // multi-line comments
82 80 // ref: https://kb.askmonty.org/en/comment-syntax/
83 state.tokenize = tokenComment;
81 state.tokenize = tokenComment(1);
84 82 return state.tokenize(stream, state);
85 83 } else if (ch == ".") {
86 84 // .1 for 0.1
87 if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
85 if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i))
88 86 return "number";
89 }
87 if (stream.match(/^\.+/))
88 return null
90 89 // .table_name (ODBC)
91 90 // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
92 if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
91 if (support.ODBCdotTable && stream.match(/^[\w\d_]+/))
93 92 return "variable-2";
94 }
95 93 } else if (operatorChars.test(ch)) {
96 94 // operators
97 95 stream.eatWhile(operatorChars);
98 return null;
96 return "operator";
97 } else if (brackets.test(ch)) {
98 // brackets
99 return "bracket";
100 } else if (punctuation.test(ch)) {
101 // punctuation
102 stream.eatWhile(punctuation);
103 return "punctuation";
99 104 } else if (ch == '{' &&
100 105 (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
101 106 // dates (weird ODBC syntax)
@@ -125,25 +130,20 b' CodeMirror.defineMode("sql", function(co'
125 130 state.tokenize = tokenBase;
126 131 break;
127 132 }
128 escaped = !escaped && ch == "\\";
133 escaped = backslashStringEscapes && !escaped && ch == "\\";
129 134 }
130 135 return "string";
131 136 };
132 137 }
133 function tokenComment(stream, state) {
134 while (true) {
135 if (stream.skipTo("*")) {
136 stream.next();
137 if (stream.eat("/")) {
138 state.tokenize = tokenBase;
139 break;
140 }
141 } else {
142 stream.skipToEnd();
143 break;
144 }
138 function tokenComment(depth) {
139 return function(stream, state) {
140 var m = stream.match(/^.*?(\/\*|\*\/)/)
141 if (!m) stream.skipToEnd()
142 else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1)
143 else if (depth > 1) state.tokenize = tokenComment(depth - 1)
144 else state.tokenize = tokenBase
145 return "comment"
145 146 }
146 return "comment";
147 147 }
148 148
149 149 function pushContext(stream, state, type) {
@@ -170,7 +170,7 b' CodeMirror.defineMode("sql", function(co'
170 170 if (state.context && state.context.align == null)
171 171 state.context.align = false;
172 172 }
173 if (stream.eatSpace()) return null;
173 if (state.tokenize == tokenBase && stream.eatSpace()) return null;
174 174
175 175 var style = state.tokenize(stream, state);
176 176 if (style == "comment") return style;
@@ -198,13 +198,11 b' CodeMirror.defineMode("sql", function(co'
198 198
199 199 blockCommentStart: "/*",
200 200 blockCommentEnd: "*/",
201 lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
201 lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--",
202 closeBrackets: "()[]{}''\"\"``"
202 203 };
203 204 });
204 205
205 (function() {
206 "use strict";
207
208 206 // `identifier`
209 207 function hookIdentifier(stream) {
210 208 // MySQL/MariaDB identifiers
@@ -217,6 +215,19 b' CodeMirror.defineMode("sql", function(co'
217 215 return stream.eatWhile(/\w/) ? "variable-2" : null;
218 216 }
219 217
218 // "identifier"
219 function hookIdentifierDoublequote(stream) {
220 // Standard SQL /SQLite identifiers
221 // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
222 // ref: http://sqlite.org/lang_keywords.html
223 var ch;
224 while ((ch = stream.next()) != null) {
225 if (ch == "\"" && !stream.eat("\"")) return "variable-2";
226 }
227 stream.backUp(stream.current().length - 1);
228 return stream.eatWhile(/\w/) ? "variable-2" : null;
229 }
230
220 231 // variable token
221 232 function hookVar(stream) {
222 233 // variables
@@ -257,7 +268,7 b' CodeMirror.defineMode("sql", function(co'
257 268 }
258 269
259 270 // 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 limit";
271 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 272
262 273 // turn a space-separated list into an array
263 274 function set(str) {
@@ -266,24 +277,28 b' CodeMirror.defineMode("sql", function(co'
266 277 return obj;
267 278 }
268 279
280 var defaultBuiltin = "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"
281
269 282 // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
270 283 CodeMirror.defineMIME("text/x-sql", {
271 284 name: "sql",
272 285 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"),
286 builtin: set(defaultBuiltin),
274 287 atoms: set("false true null unknown"),
275 operatorChars: /^[*+\-%<>!=]/,
276 288 dateSQL: set("date time timestamp"),
277 289 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
278 290 });
279 291
280 292 CodeMirror.defineMIME("text/x-mssql", {
281 293 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"),
283 keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare"),
294 client: set("$partition binary_checksum checksum connectionproperty context_info current_request_id error_line error_message error_number error_procedure error_severity error_state formatmessage get_filestream_transaction_context getansinull host_id host_name isnull isnumeric min_active_rowversion newid newsequentialid rowcount_big xact_state object_id"),
295 keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec go if use index holdlock nolock nowait paglock readcommitted readcommittedlock readpast readuncommitted repeatableread rowlock serializable snapshot tablock tablockx updlock with"),
284 296 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"),
286 operatorChars: /^[*+\-%<>!=]/,
297 atoms: set("is not null like and or in left right between inner outer join all any some cross unpivot pivot exists"),
298 operatorChars: /^[*+\-%<>!=^\&|\/]/,
299 brackets: /^[\{}\(\)]/,
300 punctuation: /^[;.,:/]/,
301 backslashStringEscapes: false,
287 302 dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
288 303 hooks: {
289 304 "@": hookVar
@@ -322,6 +337,36 b' CodeMirror.defineMode("sql", function(co'
322 337 }
323 338 });
324 339
340 // provided by the phpLiteAdmin project - phpliteadmin.org
341 CodeMirror.defineMIME("text/x-sqlite", {
342 name: "sql",
343 // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd
344 client: set("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"),
345 // ref: http://sqlite.org/lang_keywords.html
346 keywords: set(sqlKeywords + "abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"),
347 // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
348 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"),
349 // ref: http://sqlite.org/syntax/literal-value.html
350 atoms: set("null current_date current_time current_timestamp"),
351 // ref: http://sqlite.org/lang_expr.html#binaryops
352 operatorChars: /^[*+\-%<>!=&|/~]/,
353 // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
354 dateSQL: set("date time timestamp datetime"),
355 support: set("decimallessFloat zerolessFloat"),
356 identifierQuote: "\"", //ref: http://sqlite.org/lang_keywords.html
357 hooks: {
358 // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam
359 "@": hookVar,
360 ":": hookVar,
361 "?": hookVar,
362 "$": hookVar,
363 // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html
364 "\"": hookIdentifierDoublequote,
365 // there is also support for backtics, ref: http://sqlite.org/lang_keywords.html
366 "`": hookIdentifier
367 }
368 });
369
325 370 // the query language used by Apache Cassandra is called CQL, but this mime type
326 371 // is called Cassandra to avoid confusion with Contextual Query Language
327 372 CodeMirror.defineMIME("text/x-cassandra", {
@@ -341,8 +386,8 b' CodeMirror.defineMode("sql", function(co'
341 386 name: "sql",
342 387 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 388 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"),
345 operatorChars: /^[*+\-%<>!=~]/,
389 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 length lengthb 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"),
390 operatorChars: /^[*\/+\-%<>!=~]/,
346 391 dateSQL: set("date time timestamp"),
347 392 support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
348 393 });
@@ -350,15 +395,73 b' CodeMirror.defineMode("sql", function(co'
350 395 // Created to support specific hive keywords
351 396 CodeMirror.defineMIME("text/x-hive", {
352 397 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"),
354 builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),
398 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 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 unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with admin authorization char compact compactions conf cube current current_date current_timestamp day decimal defined dependency directories elem_type exchange file following for grouping hour ignore inner interval jar less logical macro minute month more none noscan over owner partialscan preceding pretty principals protection reload rewrite role roles rollup rows second server sets skewed transactions truncate unbounded unset uri user values window year"),
399 builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype key_type utctimestamp value_type varchar"),
355 400 atoms: set("false true null unknown"),
356 401 operatorChars: /^[*+\-%<>!=]/,
357 402 dateSQL: set("date timestamp"),
358 403 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
359 404 });
360 }());
405
406 CodeMirror.defineMIME("text/x-pgsql", {
407 name: "sql",
408 client: set("source"),
409 // For PostgreSQL - https://www.postgresql.org/docs/11/sql-keywords-appendix.html
410 // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c
411 keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone"),
412 // https://www.postgresql.org/docs/11/datatype.html
413 builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
414 atoms: set("false true null unknown"),
415 operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/,
416 dateSQL: set("date time timestamp"),
417 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
418 });
419
420 // Google's SQL-like query language, GQL
421 CodeMirror.defineMIME("text/x-gql", {
422 name: "sql",
423 keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"),
424 atoms: set("false true"),
425 builtin: set("blob datetime first key __key__ string integer double boolean null"),
426 operatorChars: /^[*+\-%<>!=]/
427 });
361 428
429 // Greenplum
430 CodeMirror.defineMIME("text/x-gpsql", {
431 name: "sql",
432 client: set("source"),
433 //https://github.com/greenplum-db/gpdb/blob/master/src/include/parser/kwlist.h
434 keywords: set("abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone"),
435 builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
436 atoms: set("false true null unknown"),
437 operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
438 dateSQL: set("date time timestamp"),
439 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
440 });
441
442 // Spark SQL
443 CodeMirror.defineMIME("text/x-sparksql", {
444 name: "sql",
445 keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"),
446 builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"),
447 atoms: set("false true null"),
448 operatorChars: /^[*\/+\-%<>!=~&|^]/,
449 dateSQL: set("date time timestamp"),
450 support: set("ODBCdotTable doubleQuote zerolessFloat")
451 });
452
453 // Esper
454 CodeMirror.defineMIME("text/x-esper", {
455 name: "sql",
456 client: set("source"),
457 // http://www.espertech.com/esper/release-5.5.0/esper-reference/html/appendix_keywords.html
458 keywords: set("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 after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window"),
459 builtin: {},
460 atoms: set("false true null"),
461 operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
462 dateSQL: set("time"),
463 support: set("decimallessFloat zerolessFloat binaryNumber hexNumber")
464 });
362 465 });
363 466
364 467 /*
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
@@ -16,7 +16,7 b''
16 16 })(function(CodeMirror) {
17 17 "use strict";
18 18
19 CodeMirror.defineMode("stex", function() {
19 CodeMirror.defineMode("stex", function(_config, parserConfig) {
20 20 "use strict";
21 21
22 22 function pushCommand(state, command) {
@@ -78,6 +78,14 b''
78 78 plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]);
79 79 plugins["end"] = addPluginPattern("end", "tag", ["atom"]);
80 80
81 plugins["label" ] = addPluginPattern("label" , "tag", ["atom"]);
82 plugins["ref" ] = addPluginPattern("ref" , "tag", ["atom"]);
83 plugins["eqref" ] = addPluginPattern("eqref" , "tag", ["atom"]);
84 plugins["cite" ] = addPluginPattern("cite" , "tag", ["atom"]);
85 plugins["bibitem" ] = addPluginPattern("bibitem" , "tag", ["atom"]);
86 plugins["Bibitem" ] = addPluginPattern("Bibitem" , "tag", ["atom"]);
87 plugins["RBibitem" ] = addPluginPattern("RBibitem" , "tag", ["atom"]);
88
81 89 plugins["DEFAULT"] = function () {
82 90 this.name = "DEFAULT";
83 91 this.style = "tag";
@@ -117,6 +125,10 b''
117 125 setState(state, function(source, state){ return inMathMode(source, state, "\\]"); });
118 126 return "keyword";
119 127 }
128 if (source.match("\\(")) {
129 setState(state, function(source, state){ return inMathMode(source, state, "\\)"); });
130 return "keyword";
131 }
120 132 if (source.match("$$")) {
121 133 setState(state, function(source, state){ return inMathMode(source, state, "$$"); });
122 134 return "keyword";
@@ -161,7 +173,7 b''
161 173 if (source.eatSpace()) {
162 174 return null;
163 175 }
164 if (source.match(endModeSeq)) {
176 if (endModeSeq && source.match(endModeSeq)) {
165 177 setState(state, normal);
166 178 return "keyword";
167 179 }
@@ -223,9 +235,10 b''
223 235
224 236 return {
225 237 startState: function() {
238 var f = parserConfig.inMathMode ? function(source, state){ return inMathMode(source, state); } : normal;
226 239 return {
227 240 cmdState: [],
228 f: normal
241 f: f
229 242 };
230 243 },
231 244 copyState: function(s) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Stylus mode created by Dmitry Kiselyov http://git.io/AaRB
5 5
@@ -15,6 +15,7 b''
15 15
16 16 CodeMirror.defineMode("stylus", function(config) {
17 17 var indentUnit = config.indentUnit,
18 indentUnitString = '',
18 19 tagKeywords = keySet(tagKeywords_),
19 20 tagVariablesRegexp = /^(a|b|i|s|col|em)$/i,
20 21 propertyKeywords = keySet(propertyKeywords_),
@@ -38,6 +39,8 b''
38 39 type,
39 40 override;
40 41
42 while (indentUnitString.length < indentUnit) indentUnitString += ' ';
43
41 44 /**
42 45 * Tokenizers
43 46 */
@@ -73,7 +76,7 b''
73 76 if (ch == "#") {
74 77 stream.next();
75 78 // Hex color
76 if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) {
79 if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) {
77 80 return ["atom", "atom"];
78 81 }
79 82 // ID selector
@@ -313,7 +316,7 b''
313 316 return pushContext(state, stream, "block", 0);
314 317 }
315 318 }
316 if (typeIsBlock(type, stream, state)) {
319 if (typeIsBlock(type, stream)) {
317 320 return pushContext(state, stream, "block");
318 321 }
319 322 if (type == "}" && endOfLine(stream)) {
@@ -513,7 +516,7 b''
513 516 */
514 517 states.atBlock = function(type, stream, state) {
515 518 if (type == "(") return pushContext(state, stream, "atBlock_parens");
516 if (typeIsBlock(type, stream, state)) {
519 if (typeIsBlock(type, stream)) {
517 520 return pushContext(state, stream, "block");
518 521 }
519 522 if (typeIsInterpolation(type, stream)) {
@@ -672,7 +675,7 b''
672 675 ch = textAfter && textAfter.charAt(0),
673 676 indent = cx.indent,
674 677 lineFirstWord = firstWordOfLine(textAfter),
675 lineIndent = line.length - line.replace(/^\s*/, "").length,
678 lineIndent = line.match(/^\s*/)[0].replace(/\t/g, indentUnitString).length,
676 679 prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : "",
677 680 prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent;
678 681
@@ -681,7 +684,6 b''
681 684 ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
682 685 ch == "{" && (cx.type == "at"))) {
683 686 indent = cx.indent - indentUnit;
684 cx = cx.prev;
685 687 } else if (!(/(\})/.test(ch))) {
686 688 if (/@|\$|\d/.test(ch) ||
687 689 /^\{/.test(textAfter) ||
@@ -732,11 +734,11 b''
732 734 var documentTypes_ = ["domain", "regexp", "url", "url-prefix"];
733 735 var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"];
734 736 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"];
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"];
737 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","will-change","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"];
736 738 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"];
737 739 var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"];
738 740 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"];
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"];
741 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","contents","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","scroll-position","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", "unset"];
740 742
741 743 var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"],
742 744 blockKeywords_ = ["for","if","else","unless", "from", "to"],
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Swift mode created by Michael Kaminsky https://github.com/mkaminsky11
5 5
@@ -19,25 +19,28 b''
19 19 return set
20 20 }
21 21
22 var keywords = wordSet(["var","let","class","deinit","enum","extension","func","import","init","protocol",
23 "static","struct","subscript","typealias","as","dynamicType","is","new","super",
24 "self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case",
25 "continue","default","do","else","fallthrough","if","in","for","return","switch",
26 "where","while","associativity","didSet","get","infix","inout","left","mutating",
27 "none","nonmutating","operator","override","postfix","precedence","prefix","right",
28 "set","unowned","weak","willSet"])
29 var definingKeywords = wordSet(["var","let","class","enum","extension","func","import","protocol","struct",
30 "typealias","dynamicType","for"])
31 var atoms = wordSet(["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null",
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)(?:\/\/)?(?:\\.|[^\/])+\//
22 var keywords = wordSet(["_","var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype",
23 "open","public","internal","fileprivate","private","deinit","init","new","override","self","subscript","super",
24 "convenience","dynamic","final","indirect","lazy","required","static","unowned","unowned(safe)","unowned(unsafe)","weak","as","is",
25 "break","case","continue","default","else","fallthrough","for","guard","if","in","repeat","switch","where","while",
26 "defer","return","inout","mutating","nonmutating","catch","do","rethrows","throw","throws","try","didSet","get","set","willSet",
27 "assignment","associativity","infix","left","none","operator","postfix","precedence","precedencegroup","prefix","right",
28 "Any","AnyObject","Type","dynamicType","Self","Protocol","__COLUMN__","__FILE__","__FUNCTION__","__LINE__"])
29 var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype","for"])
30 var atoms = wordSet(["true","false","nil","self","super","_"])
31 var types = wordSet(["Array","Bool","Character","Dictionary","Double","Float","Int","Int8","Int16","Int32","Int64","Never","Optional","Set","String",
32 "UInt8","UInt16","UInt32","UInt64","Void"])
33 var operators = "+-/*%=|&<>~^?!"
34 var punc = ":;,.(){}[]"
35 var binary = /^\-?0b[01][01_]*/
36 var octal = /^\-?0o[0-7][0-7_]*/
37 var hexadecimal = /^\-?0x[\dA-Fa-f][\dA-Fa-f_]*(?:(?:\.[\dA-Fa-f][\dA-Fa-f_]*)?[Pp]\-?\d[\d_]*)?/
38 var decimal = /^\-?\d[\d_]*(?:\.\d[\d_]*)?(?:[Ee]\-?\d[\d_]*)?/
39 var identifier = /^\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1/
40 var property = /^\.(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/
41 var instruction = /^\#[A-Za-z]+/
42 var attribute = /^@(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/
43 //var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\//
41 44
42 45 function tokenBase(stream, state, prev) {
43 46 if (stream.sol()) state.indented = stream.indentation()
@@ -53,8 +56,14 b''
53 56 state.tokenize.push(tokenComment)
54 57 return tokenComment(stream, state)
55 58 }
56 if (stream.match(regexp)) return "string-2"
57 59 }
60 if (stream.match(instruction)) return "builtin"
61 if (stream.match(attribute)) return "attribute"
62 if (stream.match(binary)) return "number"
63 if (stream.match(octal)) return "number"
64 if (stream.match(hexadecimal)) return "number"
65 if (stream.match(decimal)) return "number"
66 if (stream.match(property)) return "property"
58 67 if (operators.indexOf(ch) > -1) {
59 68 stream.next()
60 69 return "operator"
@@ -64,25 +73,22 b''
64 73 stream.match("..")
65 74 return "punctuation"
66 75 }
67 if (ch == '"' || ch == "'") {
68 stream.next()
69 var tokenize = tokenString(ch)
76 var stringMatch
77 if (stringMatch = stream.match(/("""|"|')/)) {
78 var tokenize = tokenString.bind(null, stringMatch[0])
70 79 state.tokenize.push(tokenize)
71 80 return tokenize(stream, state)
72 81 }
73 82
74 if (stream.match(number)) return "number"
75 if (stream.match(property)) return "property"
76
77 83 if (stream.match(identifier)) {
78 84 var ident = stream.current()
85 if (types.hasOwnProperty(ident)) return "variable-2"
86 if (atoms.hasOwnProperty(ident)) return "atom"
79 87 if (keywords.hasOwnProperty(ident)) {
80 88 if (definingKeywords.hasOwnProperty(ident))
81 89 state.prev = "define"
82 90 return "keyword"
83 91 }
84 if (types.hasOwnProperty(ident)) return "variable-2"
85 if (atoms.hasOwnProperty(ident)) return "atom"
86 92 if (prev == "define") return "def"
87 93 return "variable"
88 94 }
@@ -110,30 +116,43 b''
110 116 }
111 117 }
112 118
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 == "\\"
119 function tokenString(openQuote, stream, state) {
120 var singleLine = openQuote.length == 1
121 var ch, escaped = false
122 while (ch = stream.peek()) {
123 if (escaped) {
124 stream.next()
125 if (ch == "(") {
126 state.tokenize.push(tokenUntilClosingParen())
127 return "string"
127 128 }
129 escaped = false
130 } else if (stream.match(openQuote)) {
131 state.tokenize.pop()
132 return "string"
133 } else {
134 stream.next()
135 escaped = ch == "\\"
128 136 }
137 }
138 if (singleLine) {
129 139 state.tokenize.pop()
130 return "string"
131 140 }
141 return "string"
132 142 }
133 143
134 144 function tokenComment(stream, state) {
135 stream.match(/^(?:[^*]|\*(?!\/))*/)
136 if (stream.match("*/")) state.tokenize.pop()
145 var ch
146 while (true) {
147 stream.match(/^[^/*]+/, true)
148 ch = stream.next()
149 if (!ch) break
150 if (ch === "/" && stream.eat("*")) {
151 state.tokenize.push(tokenComment)
152 } else if (ch === "*" && stream.eat("/")) {
153 state.tokenize.pop()
154 }
155 }
137 156 return "comment"
138 157 }
139 158
@@ -194,9 +213,11 b''
194 213
195 214 lineComment: "//",
196 215 blockCommentStart: "/*",
197 blockCommentEnd: "*/"
216 blockCommentEnd: "*/",
217 fold: "brace",
218 closeBrackets: "()[]{}''\"\"``"
198 219 }
199 220 })
200 221
201 222 CodeMirror.defineMIME("text/x-swift","swift")
202 })
223 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 //tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara
5 5
@@ -42,42 +42,34 b' CodeMirror.defineMode("tcl", function() '
42 42 var beforeParams = state.beforeParams;
43 43 state.beforeParams = false;
44 44 var ch = stream.next();
45 if ((ch == '"' || ch == "'") && state.inParams)
45 if ((ch == '"' || ch == "'") && state.inParams) {
46 46 return chain(stream, state, tokenString(ch));
47 else if (/[\[\]{}\(\),;\.]/.test(ch)) {
47 } else if (/[\[\]{}\(\),;\.]/.test(ch)) {
48 48 if (ch == "(" && beforeParams) state.inParams = true;
49 49 else if (ch == ")") state.inParams = false;
50 50 return null;
51 }
52 else if (/\d/.test(ch)) {
51 } else if (/\d/.test(ch)) {
53 52 stream.eatWhile(/[\w\.]/);
54 53 return "number";
55 }
56 else if (ch == "#" && stream.eat("*")) {
57 return chain(stream, state, tokenComment);
58 }
59 else if (ch == "#" && stream.match(/ *\[ *\[/)) {
60 return chain(stream, state, tokenUnparsed);
61 }
62 else if (ch == "#" && stream.eat("#")) {
54 } else if (ch == "#") {
55 if (stream.eat("*"))
56 return chain(stream, state, tokenComment);
57 if (ch == "#" && stream.match(/ *\[ *\[/))
58 return chain(stream, state, tokenUnparsed);
63 59 stream.skipToEnd();
64 60 return "comment";
65 }
66 else if (ch == '"') {
61 } else if (ch == '"') {
67 62 stream.skipTo(/"/);
68 63 return "comment";
69 }
70 else if (ch == "$") {
64 } else if (ch == "$") {
71 65 stream.eatWhile(/[$_a-z0-9A-Z\.{:]/);
72 66 stream.eatWhile(/}/);
73 67 state.beforeParams = true;
74 68 return "builtin";
75 }
76 else if (isOperatorChar.test(ch)) {
69 } else if (isOperatorChar.test(ch)) {
77 70 stream.eatWhile(isOperatorChar);
78 71 return "comment";
79 }
80 else {
72 } else {
81 73 stream.eatWhile(/[\w\$_{}\xa1-\uffff]/);
82 74 var word = stream.current().toLowerCase();
83 75 if (keywords && keywords.propertyIsEnumerable(word))
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") { // CommonJS
@@ -203,7 +203,7 b''
203 203 single: {
204 204 bc: "bc",
205 205 bq: "bq",
206 definitionList: /- [^(?::=)]+:=+/,
206 definitionList: /- .*?:=+/,
207 207 definitionListEnd: /.*=:\s*$/,
208 208 div: "div",
209 209 drawTable: /\|.*\|/,
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /***
5 5 |''Name''|tiddlywiki.js|
@@ -8,7 +8,7 b''
8 8 |''Version''|0.1.7|
9 9 |''Status''|''stable''|
10 10 |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|
11 |''Documentation''|http://codemirror.tiddlyspace.com/|
11 |''Documentation''|https://codemirror.tiddlyspace.com/|
12 12 |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
13 13 |''CoreVersion''|2.5.0|
14 14 |''Requires''|codemirror.js|
@@ -16,7 +16,6 b''
16 16 ! Info
17 17 CoreVersion parameter is needed for TiddlyWiki only!
18 18 ***/
19 //{{{
20 19
21 20 (function(mod) {
22 21 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -32,73 +31,60 b' CodeMirror.defineMode("tiddlywiki", func'
32 31 // Tokenizer
33 32 var textwords = {};
34 33
35 var keywords = function () {
36 function kw(type) {
37 return { type: type, style: "macro"};
38 }
39 return {
40 "allTags": kw('allTags'), "closeAll": kw('closeAll'), "list": kw('list'),
41 "newJournal": kw('newJournal'), "newTiddler": kw('newTiddler'),
42 "permaview": kw('permaview'), "saveChanges": kw('saveChanges'),
43 "search": kw('search'), "slider": kw('slider'), "tabs": kw('tabs'),
44 "tag": kw('tag'), "tagging": kw('tagging'), "tags": kw('tags'),
45 "tiddler": kw('tiddler'), "timeline": kw('timeline'),
46 "today": kw('today'), "version": kw('version'), "option": kw('option'),
47
48 "with": kw('with'),
49 "filter": kw('filter')
50 };
51 }();
34 var keywords = {
35 "allTags": true, "closeAll": true, "list": true,
36 "newJournal": true, "newTiddler": true,
37 "permaview": true, "saveChanges": true,
38 "search": true, "slider": true, "tabs": true,
39 "tag": true, "tagging": true, "tags": true,
40 "tiddler": true, "timeline": true,
41 "today": true, "version": true, "option": true,
42 "with": true, "filter": true
43 };
52 44
53 45 var isSpaceName = /[\w_\-]/i,
54 reHR = /^\-\-\-\-+$/, // <hr>
55 reWikiCommentStart = /^\/\*\*\*$/, // /***
56 reWikiCommentStop = /^\*\*\*\/$/, // ***/
57 reBlockQuote = /^<<<$/,
46 reHR = /^\-\-\-\-+$/, // <hr>
47 reWikiCommentStart = /^\/\*\*\*$/, // /***
48 reWikiCommentStop = /^\*\*\*\/$/, // ***/
49 reBlockQuote = /^<<<$/,
58 50
59 reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start
60 reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop
61 reXmlCodeStart = /^<!--\{\{\{-->$/, // xml block start
62 reXmlCodeStop = /^<!--\}\}\}-->$/, // xml stop
51 reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start
52 reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop
53 reXmlCodeStart = /^<!--\{\{\{-->$/, // xml block start
54 reXmlCodeStop = /^<!--\}\}\}-->$/, // xml stop
63 55
64 reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start
65 reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop
56 reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start
57 reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop
66 58
67 reUntilCodeStop = /.*?\}\}\}/;
59 reUntilCodeStop = /.*?\}\}\}/;
68 60
69 61 function chain(stream, state, f) {
70 62 state.tokenize = f;
71 63 return f(stream, state);
72 64 }
73 65
74 function jsTokenBase(stream, state) {
75 var sol = stream.sol(), ch;
66 function tokenBase(stream, state) {
67 var sol = stream.sol(), ch = stream.peek();
76 68
77 69 state.block = false; // indicates the start of a code block.
78 70
79 ch = stream.peek(); // don't eat, to make matching simpler
80
81 71 // check start of blocks
82 72 if (sol && /[<\/\*{}\-]/.test(ch)) {
83 73 if (stream.match(reCodeBlockStart)) {
84 74 state.block = true;
85 75 return chain(stream, state, twTokenCode);
86 76 }
87 if (stream.match(reBlockQuote)) {
77 if (stream.match(reBlockQuote))
88 78 return 'quote';
89 }
90 if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) {
79 if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop))
91 80 return 'comment';
92 }
93 if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) {
81 if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop))
94 82 return 'comment';
95 }
96 if (stream.match(reHR)) {
83 if (stream.match(reHR))
97 84 return 'hr';
98 }
99 } // sol
100 ch = stream.next();
85 }
101 86
87 stream.next();
102 88 if (sol && /[\/\*!#;:>|]/.test(ch)) {
103 89 if (ch == "!") { // tw header
104 90 stream.skipToEnd();
@@ -124,95 +110,77 b' CodeMirror.defineMode("tiddlywiki", func'
124 110 stream.eatWhile(">");
125 111 return "quote";
126 112 }
127 if (ch == '|') {
113 if (ch == '|')
128 114 return 'header';
129 }
130 115 }
131 116
132 if (ch == '{' && stream.match(/\{\{/)) {
117 if (ch == '{' && stream.match(/\{\{/))
133 118 return chain(stream, state, twTokenCode);
134 }
135 119
136 120 // rudimentary html:// file:// link matching. TW knows much more ...
137 if (/[hf]/i.test(ch)) {
138 if (/[ti]/i.test(stream.peek()) && stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i)) {
139 return "link";
140 }
141 }
121 if (/[hf]/i.test(ch) &&
122 /[ti]/i.test(stream.peek()) &&
123 stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i))
124 return "link";
125
142 126 // just a little string indicator, don't want to have the whole string covered
143 if (ch == '"') {
127 if (ch == '"')
144 128 return 'string';
145 }
146 if (ch == '~') { // _no_ CamelCase indicator should be bold
129
130 if (ch == '~') // _no_ CamelCase indicator should be bold
147 131 return 'brace';
148 }
149 if (/[\[\]]/.test(ch)) { // check for [[..]]
150 if (stream.peek() == ch) {
151 stream.next();
152 return 'brace';
153 }
154 }
132
133 if (/[\[\]]/.test(ch) && stream.match(ch)) // check for [[..]]
134 return 'brace';
135
155 136 if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting
156 137 stream.eatWhile(isSpaceName);
157 138 return "link";
158 139 }
140
159 141 if (/\d/.test(ch)) { // numbers
160 142 stream.eatWhile(/\d/);
161 143 return "number";
162 144 }
145
163 146 if (ch == "/") { // tw invisible comment
164 147 if (stream.eat("%")) {
165 148 return chain(stream, state, twTokenComment);
166 }
167 else if (stream.eat("/")) { //
149 } else if (stream.eat("/")) { //
168 150 return chain(stream, state, twTokenEm);
169 151 }
170 152 }
171 if (ch == "_") { // tw underline
172 if (stream.eat("_")) {
153
154 if (ch == "_" && stream.eat("_")) // tw underline
173 155 return chain(stream, state, twTokenUnderline);
174 }
175 }
156
176 157 // strikethrough and mdash handling
177 if (ch == "-") {
178 if (stream.eat("-")) {
179 // if strikethrough looks ugly, change CSS.
180 if (stream.peek() != ' ')
181 return chain(stream, state, twTokenStrike);
182 // mdash
183 if (stream.peek() == ' ')
184 return 'brace';
185 }
158 if (ch == "-" && stream.eat("-")) {
159 // if strikethrough looks ugly, change CSS.
160 if (stream.peek() != ' ')
161 return chain(stream, state, twTokenStrike);
162 // mdash
163 if (stream.peek() == ' ')
164 return 'brace';
186 165 }
187 if (ch == "'") { // tw bold
188 if (stream.eat("'")) {
189 return chain(stream, state, twTokenStrong);
190 }
191 }
192 if (ch == "<") { // tw macro
193 if (stream.eat("<")) {
194 return chain(stream, state, twTokenMacro);
195 }
196 }
197 else {
198 return null;
199 }
166
167 if (ch == "'" && stream.eat("'")) // tw bold
168 return chain(stream, state, twTokenStrong);
169
170 if (ch == "<" && stream.eat("<")) // tw macro
171 return chain(stream, state, twTokenMacro);
200 172
201 173 // core macro handling
202 174 stream.eatWhile(/[\w\$_]/);
203 var word = stream.current(),
204 known = textwords.propertyIsEnumerable(word) && textwords[word];
205
206 return known ? known.style : null;
207 } // jsTokenBase()
175 return textwords.propertyIsEnumerable(stream.current()) ? "keyword" : null
176 }
208 177
209 178 // tw invisible comment
210 179 function twTokenComment(stream, state) {
211 var maybeEnd = false,
212 ch;
180 var maybeEnd = false, ch;
213 181 while (ch = stream.next()) {
214 182 if (ch == "/" && maybeEnd) {
215 state.tokenize = jsTokenBase;
183 state.tokenize = tokenBase;
216 184 break;
217 185 }
218 186 maybeEnd = (ch == "%");
@@ -226,7 +194,7 b' CodeMirror.defineMode("tiddlywiki", func'
226 194 ch;
227 195 while (ch = stream.next()) {
228 196 if (ch == "'" && maybeEnd) {
229 state.tokenize = jsTokenBase;
197 state.tokenize = tokenBase;
230 198 break;
231 199 }
232 200 maybeEnd = (ch == "'");
@@ -243,12 +211,12 b' CodeMirror.defineMode("tiddlywiki", func'
243 211 }
244 212
245 213 if (!sb && stream.match(reUntilCodeStop)) {
246 state.tokenize = jsTokenBase;
214 state.tokenize = tokenBase;
247 215 return "comment";
248 216 }
249 217
250 218 if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
251 state.tokenize = jsTokenBase;
219 state.tokenize = tokenBase;
252 220 return "comment";
253 221 }
254 222
@@ -262,7 +230,7 b' CodeMirror.defineMode("tiddlywiki", func'
262 230 ch;
263 231 while (ch = stream.next()) {
264 232 if (ch == "/" && maybeEnd) {
265 state.tokenize = jsTokenBase;
233 state.tokenize = tokenBase;
266 234 break;
267 235 }
268 236 maybeEnd = (ch == "/");
@@ -276,7 +244,7 b' CodeMirror.defineMode("tiddlywiki", func'
276 244 ch;
277 245 while (ch = stream.next()) {
278 246 if (ch == "_" && maybeEnd) {
279 state.tokenize = jsTokenBase;
247 state.tokenize = tokenBase;
280 248 break;
281 249 }
282 250 maybeEnd = (ch == "_");
@@ -291,7 +259,7 b' CodeMirror.defineMode("tiddlywiki", func'
291 259
292 260 while (ch = stream.next()) {
293 261 if (ch == "-" && maybeEnd) {
294 state.tokenize = jsTokenBase;
262 state.tokenize = tokenBase;
295 263 break;
296 264 }
297 265 maybeEnd = (ch == "-");
@@ -301,58 +269,40 b' CodeMirror.defineMode("tiddlywiki", func'
301 269
302 270 // macro
303 271 function twTokenMacro(stream, state) {
304 var ch, word, known;
305
306 272 if (stream.current() == '<<') {
307 273 return 'macro';
308 274 }
309 275
310 ch = stream.next();
276 var ch = stream.next();
311 277 if (!ch) {
312 state.tokenize = jsTokenBase;
278 state.tokenize = tokenBase;
313 279 return null;
314 280 }
315 281 if (ch == ">") {
316 282 if (stream.peek() == '>') {
317 283 stream.next();
318 state.tokenize = jsTokenBase;
284 state.tokenize = tokenBase;
319 285 return "macro";
320 286 }
321 287 }
322 288
323 289 stream.eatWhile(/[\w\$_]/);
324 word = stream.current();
325 known = keywords.propertyIsEnumerable(word) && keywords[word];
326
327 if (known) {
328 return known.style, word;
329 }
330 else {
331 return null, word;
332 }
290 return keywords.propertyIsEnumerable(stream.current()) ? "keyword" : null
333 291 }
334 292
335 293 // Interface
336 294 return {
337 295 startState: function () {
338 return {
339 tokenize: jsTokenBase,
340 indented: 0,
341 level: 0
342 };
296 return {tokenize: tokenBase};
343 297 },
344 298
345 299 token: function (stream, state) {
346 300 if (stream.eatSpace()) return null;
347 301 var style = state.tokenize(stream, state);
348 302 return style;
349 },
350
351 electricChars: ""
303 }
352 304 };
353 305 });
354 306
355 307 CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");
356 308 });
357
358 //}}}
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -144,7 +144,7 b" CodeMirror.defineMode('tiki', function(c"
144 144 type = "equals";
145 145
146 146 if (peek == ">") {
147 ch = stream.next();
147 stream.next();
148 148 peek = stream.peek();
149 149 }
150 150
@@ -298,13 +298,13 b' return {'
298 298 if (context && context.noIndent) return 0;
299 299 if (context && /^{\//.test(textAfter))
300 300 context = context.prev;
301 while (context && !context.startOfLine)
302 context = context.prev;
303 if (context) return context.indent + indentUnit;
304 else return 0;
305 },
306 electricChars: "/"
307 };
301 while (context && !context.startOfLine)
302 context = context.prev;
303 if (context) return context.indent + indentUnit;
304 else return 0;
305 },
306 electricChars: "/"
307 };
308 308 });
309 309
310 310 CodeMirror.defineMIME("text/tiki", "tiki");
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object")
@@ -77,6 +77,8 b" CodeMirror.defineMode('troff', function("
77 77 };
78 78 });
79 79
80 CodeMirror.defineMIME('troff', 'troff');
80 CodeMirror.defineMIME('text/troff', 'troff');
81 CodeMirror.defineMIME('text/x-troff', 'troff');
82 CodeMirror.defineMIME('application/x-troff', 'troff');
81 83
82 84 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,17 +1,17 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
6 mod(require("../../lib/codemirror"), require("../../addon/mode/multiplex"));
7 7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
8 define(["../../lib/codemirror", "../../addon/mode/multiplex"], mod);
9 9 else // Plain browser env
10 10 mod(CodeMirror);
11 11 })(function(CodeMirror) {
12 12 "use strict";
13 13
14 CodeMirror.defineMode("twig", function() {
14 CodeMirror.defineMode("twig:inner", function() {
15 15 var keywords = ["and", "as", "autoescape", "endautoescape", "block", "do", "endblock", "else", "elseif", "extends", "for", "endfor", "embed", "endembed", "filter", "endfilter", "flush", "from", "if", "endif", "in", "is", "include", "import", "not", "or", "set", "spaceless", "endspaceless", "with", "endwith", "trans", "endtrans", "blocktrans", "endblocktrans", "macro", "endmacro", "use", "verbatim", "endverbatim"],
16 16 operator = /^[+\-*&%=<>!?|~^]/,
17 17 sign = /^[:\[\(\{]/,
@@ -95,7 +95,7 b''
95 95 }
96 96 return "variable";
97 97 } else if (stream.eat("{")) {
98 if (ch = stream.eat("#")) {
98 if (stream.eat("#")) {
99 99 state.incomment = true;
100 100 if (!stream.skipTo("#}")) {
101 101 stream.skipToEnd();
@@ -128,5 +128,14 b''
128 128 };
129 129 });
130 130
131 CodeMirror.defineMode("twig", function(config, parserConfig) {
132 var twigInner = CodeMirror.getMode(config, "twig:inner");
133 if (!parserConfig || !parserConfig.base) return twigInner;
134 return CodeMirror.multiplexingMode(
135 CodeMirror.getMode(config, parserConfig.base), {
136 open: /\{[{#%]/, close: /[}#%]\}/, mode: twigInner, parseDelimiters: true
137 }
138 );
139 });
131 140 CodeMirror.defineMIME("text/x-twig", "twig");
132 141 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,16 +25,16 b' CodeMirror.defineMode("vb", function(con'
25 25 var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
26 26 var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
27 27
28 var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try'];
29 var middleKeywords = ['else','elseif','case', 'catch'];
28 var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with'];
29 var middleKeywords = ['else','elseif','case', 'catch', 'finally'];
30 30 var endKeywords = ['next','loop'];
31 31
32 var operatorKeywords = ['and', 'or', 'not', 'xor', 'in'];
32 var operatorKeywords = ['and', "andalso", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like'];
33 33 var wordOperators = wordRegexp(operatorKeywords);
34 var commonKeywords = ['as', 'dim', 'break', 'continue','optional', 'then', 'until',
35 'goto', 'byval','byref','new','handles','property', 'return',
36 'const','private', 'protected', 'friend', 'public', 'shared', 'static', 'true','false'];
37 var commontypes = ['integer','string','double','decimal','boolean','short','char', 'float','single'];
34
35 var commonKeywords = ["#const", "#else", "#elseif", "#end", "#if", "#region", "addhandler", "addressof", "alias", "as", "byref", "byval", "cbool", "cbyte", "cchar", "cdate", "cdbl", "cdec", "cint", "clng", "cobj", "compare", "const", "continue", "csbyte", "cshort", "csng", "cstr", "cuint", "culng", "cushort", "declare", "default", "delegate", "dim", "directcast", "each", "erase", "error", "event", "exit", "explicit", "false", "for", "friend", "gettype", "goto", "handles", "implements", "imports", "infer", "inherits", "interface", "isfalse", "istrue", "lib", "me", "mod", "mustinherit", "mustoverride", "my", "mybase", "myclass", "namespace", "narrowing", "new", "nothing", "notinheritable", "notoverridable", "of", "off", "on", "operator", "option", "optional", "out", "overloads", "overridable", "overrides", "paramarray", "partial", "private", "protected", "public", "raiseevent", "readonly", "redim", "removehandler", "resume", "return", "shadows", "shared", "static", "step", "stop", "strict", "then", "throw", "to", "true", "trycast", "typeof", "until", "until", "when", "widening", "withevents", "writeonly"];
36
37 var commontypes = ['object', 'boolean', 'char', 'string', 'byte', 'sbyte', 'short', 'ushort', 'int16', 'uint16', 'integer', 'uinteger', 'int32', 'uint32', 'long', 'ulong', 'int64', 'uint64', 'decimal', 'single', 'double', 'float', 'date', 'datetime', 'intptr', 'uintptr'];
38 38
39 39 var keywords = wordRegexp(commonKeywords);
40 40 var types = wordRegexp(commontypes);
@@ -202,7 +202,6 b' CodeMirror.defineMode("vb", function(con'
202 202 // Handle '.' connected identifiers
203 203 if (current === '.') {
204 204 style = state.tokenize(stream, state);
205 current = stream.current();
206 205 if (style === 'variable') {
207 206 return 'variable';
208 207 } else {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 /*
5 5 For extra ASP classic objects, initialize CodeMirror instance with this option:
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -34,7 +34,7 b' CodeMirror.defineMode("velocity", functi'
34 34 state.beforeParams = false;
35 35 var ch = stream.next();
36 36 // start of unparsed string?
37 if ((ch == "'") && state.inParams) {
37 if ((ch == "'") && !state.inString && state.inParams) {
38 38 state.lastTokenWasBuiltin = false;
39 39 return chain(stream, state, tokenString(ch));
40 40 }
@@ -82,7 +82,7 b' CodeMirror.defineMode("velocity", functi'
82 82 }
83 83 // variable?
84 84 else if (ch == "$") {
85 stream.eatWhile(/[\w\d\$_\.{}]/);
85 stream.eatWhile(/[\w\d\$_\.{}-]/);
86 86 // is it one of the specials?
87 87 if (specials && specials.propertyIsEnumerable(stream.current())) {
88 88 return "keyword";
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -81,7 +81,7 b' CodeMirror.defineMode("verilog", functio'
81 81 // Block openings which are closed by a matching keyword in the form of ("end" + keyword)
82 82 // E.g. "task" => "endtask"
83 83 var blockKeywords = words(
84 "case checker class clocking config function generate interface module package" +
84 "case checker class clocking config function generate interface module package " +
85 85 "primitive program property specify sequence table task"
86 86 );
87 87
@@ -250,7 +250,7 b' CodeMirror.defineMode("verilog", functio'
250 250 if (text == contextClosing) {
251 251 return true;
252 252 } else {
253 // contextClosing may be mulitple keywords separated by ;
253 // contextClosing may be multiple keywords separated by ;
254 254 var closingKeywords = contextClosing.split(";");
255 255 for (var i in closingKeywords) {
256 256 if (text == closingKeywords[i]) {
@@ -302,7 +302,13 b' CodeMirror.defineMode("verilog", functio'
302 302 state.indented = stream.indentation();
303 303 state.startOfLine = true;
304 304 }
305 if (hooks.token) hooks.token(stream, state);
305 if (hooks.token) {
306 // Call hook, with an optional return value of a style to override verilog styling.
307 var style = hooks.token(stream, state);
308 if (style !== undefined) {
309 return style;
310 }
311 }
306 312 if (stream.eatSpace()) return null;
307 313 curPunc = null;
308 314 curKeyword = null;
@@ -375,163 +381,295 b' CodeMirror.defineMode("verilog", functio'
375 381 name: "verilog"
376 382 });
377 383
378 // TLVVerilog mode
384
385
386 // TL-Verilog mode.
387 // See tl-x.org for language spec.
388 // See the mode in action at makerchip.com.
389 // Contact: steve.hoover@redwoodeda.com
379 390
380 var tlvchScopePrefixes = {
381 ">": "property", "->": "property", "-": "hr", "|": "link", "?$": "qualifier", "?*": "qualifier",
382 "@-": "variable-3", "@": "variable-3", "?": "qualifier"
391 // TLV Identifier prefixes.
392 // Note that sign is not treated separately, so "+/-" versions of numeric identifiers
393 // are included.
394 var tlvIdentifierStyle = {
395 "|": "link",
396 ">": "property", // Should condition this off for > TLV 1c.
397 "$": "variable",
398 "$$": "variable",
399 "?$": "qualifier",
400 "?*": "qualifier",
401 "-": "hr",
402 "/": "property",
403 "/-": "property",
404 "@": "variable-3",
405 "@-": "variable-3",
406 "@++": "variable-3",
407 "@+=": "variable-3",
408 "@+=-": "variable-3",
409 "@--": "variable-3",
410 "@-=": "variable-3",
411 "%+": "tag",
412 "%-": "tag",
413 "%": "tag",
414 ">>": "tag",
415 "<<": "tag",
416 "<>": "tag",
417 "#": "tag", // Need to choose a style for this.
418 "^": "attribute",
419 "^^": "attribute",
420 "^!": "attribute",
421 "*": "variable-2",
422 "**": "variable-2",
423 "\\": "keyword",
424 "\"": "comment"
383 425 };
384 426
385 function tlvGenIndent(stream, state) {
386 var tlvindentUnit = 2;
387 var rtnIndent = -1, indentUnitRq = 0, curIndent = stream.indentation();
388 switch (state.tlvCurCtlFlowChar) {
389 case "\\":
390 curIndent = 0;
391 break;
392 case "|":
393 if (state.tlvPrevPrevCtlFlowChar == "@") {
394 indentUnitRq = -2; //-2 new pipe rq after cur pipe
395 break;
396 }
397 if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
398 indentUnitRq = 1; // +1 new scope
399 break;
400 case "M": // m4
401 if (state.tlvPrevPrevCtlFlowChar == "@") {
402 indentUnitRq = -2; //-2 new inst rq after pipe
403 break;
404 }
405 if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
406 indentUnitRq = 1; // +1 new scope
407 break;
408 case "@":
409 if (state.tlvPrevCtlFlowChar == "S")
410 indentUnitRq = -1; // new pipe stage after stmts
411 if (state.tlvPrevCtlFlowChar == "|")
412 indentUnitRq = 1; // 1st pipe stage
413 break;
414 case "S":
415 if (state.tlvPrevCtlFlowChar == "@")
416 indentUnitRq = 1; // flow in pipe stage
417 if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
418 indentUnitRq = 1; // +1 new scope
419 break;
420 }
421 var statementIndentUnit = tlvindentUnit;
422 rtnIndent = curIndent + (indentUnitRq*statementIndentUnit);
423 return rtnIndent >= 0 ? rtnIndent : curIndent;
427 // Lines starting with these characters define scope (result in indentation).
428 var tlvScopePrefixChars = {
429 "/": "beh-hier",
430 ">": "beh-hier",
431 "-": "phys-hier",
432 "|": "pipe",
433 "?": "when",
434 "@": "stage",
435 "\\": "keyword"
436 };
437 var tlvIndentUnit = 3;
438 var tlvTrackStatements = false;
439 var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/; // Matches an identifiere.
440 // Note that ':' is excluded, because of it's use in [:].
441 var tlvFirstLevelIndentMatch = /^[! ] /;
442 var tlvLineIndentationMatch = /^[! ] */;
443 var tlvCommentMatch = /^\/[\/\*]/;
444
445
446 // Returns a style specific to the scope at the given indentation column.
447 // Type is one of: "indent", "scope-ident", "before-scope-ident".
448 function tlvScopeStyle(state, indentation, type) {
449 // Begin scope.
450 var depth = indentation / tlvIndentUnit; // TODO: Pass this in instead.
451 return "tlv-" + state.tlvIndentationStyle[depth] + "-" + type;
452 }
453
454 // Return true if the next thing in the stream is an identifier with a mnemonic.
455 function tlvIdentNext(stream) {
456 var match;
457 return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0;
424 458 }
425 459
426 460 CodeMirror.defineMIME("text/x-tlv", {
427 461 name: "verilog",
462
428 463 hooks: {
429 "\\": function(stream, state) {
430 var vxIndent = 0, style = false;
431 var curPunc = stream.string;
432 if ((stream.sol()) && ((/\\SV/.test(stream.string)) || (/\\TLV/.test(stream.string)))) {
433 curPunc = (/\\TLV_version/.test(stream.string))
434 ? "\\TLV_version" : stream.string;
435 stream.skipToEnd();
436 if (curPunc == "\\SV" && state.vxCodeActive) {state.vxCodeActive = false;};
437 if ((/\\TLV/.test(curPunc) && !state.vxCodeActive)
438 || (curPunc=="\\TLV_version" && state.vxCodeActive)) {state.vxCodeActive = true;};
439 style = "keyword";
440 state.tlvCurCtlFlowChar = state.tlvPrevPrevCtlFlowChar
441 = state.tlvPrevCtlFlowChar = "";
442 if (state.vxCodeActive == true) {
443 state.tlvCurCtlFlowChar = "\\";
444 vxIndent = tlvGenIndent(stream, state);
464
465 electricInput: false,
466
467
468 // Return undefined for verilog tokenizing, or style for TLV token (null not used).
469 // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting
470 // can be enabled with the definition of cm-tlv-* styles, including highlighting for:
471 // - M4 tokens
472 // - TLV scope indentation
473 // - Statement delimitation (enabled by tlvTrackStatements)
474 token: function(stream, state) {
475 var style = undefined;
476 var match; // Return value of pattern matches.
477
478 // Set highlighting mode based on code region (TLV or SV).
479 if (stream.sol() && ! state.tlvInBlockComment) {
480 // Process region.
481 if (stream.peek() == '\\') {
482 style = "def";
483 stream.skipToEnd();
484 if (stream.string.match(/\\SV/)) {
485 state.tlvCodeActive = false;
486 } else if (stream.string.match(/\\TLV/)){
487 state.tlvCodeActive = true;
488 }
489 }
490 // Correct indentation in the face of a line prefix char.
491 if (state.tlvCodeActive && stream.pos == 0 &&
492 (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) {
493 state.indented = match[0].length;
445 494 }
446 state.vxIndentRq = vxIndent;
495
496 // Compute indentation state:
497 // o Auto indentation on next line
498 // o Indentation scope styles
499 var indented = state.indented;
500 var depth = indented / tlvIndentUnit;
501 if (depth <= state.tlvIndentationStyle.length) {
502 // not deeper than current scope
503
504 var blankline = stream.string.length == indented;
505 var chPos = depth * tlvIndentUnit;
506 if (chPos < stream.string.length) {
507 var bodyString = stream.string.slice(chPos);
508 var ch = bodyString[0];
509 if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) &&
510 tlvIdentifierStyle[match[1]])) {
511 // This line begins scope.
512 // Next line gets indented one level.
513 indented += tlvIndentUnit;
514 // Style the next level of indentation (except non-region keyword identifiers,
515 // which are statements themselves)
516 if (!(ch == "\\" && chPos > 0)) {
517 state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch];
518 if (tlvTrackStatements) {state.statementComment = false;}
519 depth++;
520 }
521 }
522 }
523 // Clear out deeper indentation levels unless line is blank.
524 if (!blankline) {
525 while (state.tlvIndentationStyle.length > depth) {
526 state.tlvIndentationStyle.pop();
527 }
528 }
529 }
530 // Set next level of indentation.
531 state.tlvNextIndent = indented;
447 532 }
448 return style;
449 },
450 tokenBase: function(stream, state) {
451 var vxIndent = 0, style = false;
452 var tlvisOperatorChar = /[\[\]=:]/;
453 var tlvkpScopePrefixs = {
454 "**":"variable-2", "*":"variable-2", "$$":"variable", "$":"variable",
455 "^^":"attribute", "^":"attribute"};
456 var ch = stream.peek();
457 var vxCurCtlFlowCharValueAtStart = state.tlvCurCtlFlowChar;
458 if (state.vxCodeActive == true) {
459 if (/[\[\]{}\(\);\:]/.test(ch)) {
460 // bypass nesting and 1 char punc
461 style = "meta";
462 stream.next();
463 } else if (ch == "/") {
464 stream.next();
465 if (stream.eat("/")) {
466 stream.skipToEnd();
467 style = "comment";
468 state.tlvCurCtlFlowChar = "S";
533
534 if (state.tlvCodeActive) {
535 // Highlight as TLV.
536
537 var beginStatement = false;
538 if (tlvTrackStatements) {
539 // This starts a statement if the position is at the scope level
540 // and we're not within a statement leading comment.
541 beginStatement =
542 (stream.peek() != " ") && // not a space
543 (style === undefined) && // not a region identifier
544 !state.tlvInBlockComment && // not in block comment
545 //!stream.match(tlvCommentMatch, false) && // not comment start
546 (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit); // at scope level
547 if (beginStatement) {
548 if (state.statementComment) {
549 // statement already started by comment
550 beginStatement = false;
551 }
552 state.statementComment =
553 stream.match(tlvCommentMatch, false); // comment start
554 }
555 }
556
557 var match;
558 if (style !== undefined) {
559 // Region line.
560 style += " " + tlvScopeStyle(state, 0, "scope-ident")
561 } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) &&
562 (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^ /))) {
563 // Indentation
564 style = // make this style distinct from the previous one to prevent
565 // codemirror from combining spans
566 "tlv-indent-" + (((stream.pos % 2) == 0) ? "even" : "odd") +
567 // and style it
568 " " + tlvScopeStyle(state, stream.pos - tlvIndentUnit, "indent");
569 // Style the line prefix character.
570 if (match[0].charAt(0) == "!") {
571 style += " tlv-alert-line-prefix";
572 }
573 // Place a class before a scope identifier.
574 if (tlvIdentNext(stream)) {
575 style += " " + tlvScopeStyle(state, stream.pos, "before-scope-ident");
576 }
577 } else if (state.tlvInBlockComment) {
578 // In a block comment.
579 if (stream.match(/^.*?\*\//)) {
580 // Exit block comment.
581 state.tlvInBlockComment = false;
582 if (tlvTrackStatements && !stream.eol()) {
583 // Anything after comment is assumed to be real statement content.
584 state.statementComment = false;
585 }
469 586 } else {
470 stream.backUp(1);
587 stream.skipToEnd();
588 }
589 style = "comment";
590 } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) {
591 // Start comment.
592 if (match[0] == "//") {
593 // Line comment.
594 stream.skipToEnd();
595 } else {
596 // Block comment.
597 state.tlvInBlockComment = true;
471 598 }
472 } else if (ch == "@") {
473 // pipeline stage
474 style = tlvchScopePrefixes[ch];
475 state.tlvCurCtlFlowChar = "@";
476 stream.next();
477 stream.eatWhile(/[\w\$_]/);
478 } else if (stream.match(/\b[mM]4+/, true)) { // match: function(pattern, consume, caseInsensitive)
599 style = "comment";
600 } else if (match = stream.match(tlvIdentMatch)) {
601 // looks like an identifier (or identifier prefix)
602 var prefix = match[1];
603 var mnemonic = match[2];
604 if (// is identifier prefix
605 tlvIdentifierStyle.hasOwnProperty(prefix) &&
606 // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet)
607 (mnemonic.length > 0 || stream.eol())) {
608 style = tlvIdentifierStyle[prefix];
609 if (stream.column() == state.indented) {
610 // Begin scope.
611 style += " " + tlvScopeStyle(state, stream.column(), "scope-ident")
612 }
613 } else {
614 // Just swallow one character and try again.
615 // This enables subsequent identifier match with preceding symbol character, which
616 // is legal within a statement. (Eg, !$reset). It also enables detection of
617 // comment start with preceding symbols.
618 stream.backUp(stream.current().length - 1);
619 style = "tlv-default";
620 }
621 } else if (stream.match(/^\t+/)) {
622 // Highlight tabs, which are illegal.
623 style = "tlv-tab";
624 } else if (stream.match(/^[\[\]{}\(\);\:]+/)) {
625 // [:], (), {}, ;.
626 style = "meta";
627 } else if (match = stream.match(/^[mM]4([\+_])?[\w\d_]*/)) {
479 628 // m4 pre proc
480 stream.skipTo("(");
481 style = "def";
482 state.tlvCurCtlFlowChar = "M";
483 } else if (ch == "!" && stream.sol()) {
484 // v stmt in tlv region
485 // state.tlvCurCtlFlowChar = "S";
486 style = "comment";
629 style = (match[1] == "+") ? "tlv-m4-plus" : "tlv-m4";
630 } else if (stream.match(/^ +/)){
631 // Skip over spaces.
632 if (stream.eol()) {
633 // Trailing spaces.
634 style = "error";
635 } else {
636 // Non-trailing spaces.
637 style = "tlv-default";
638 }
639 } else if (stream.match(/^[\w\d_]+/)) {
640 // alpha-numeric token.
641 style = "number";
642 } else {
643 // Eat the next char w/ no formatting.
487 644 stream.next();
488 } else if (tlvisOperatorChar.test(ch)) {
489 // operators
490 stream.eatWhile(tlvisOperatorChar);
491 style = "operator";
492 } else if (ch == "#") {
493 // phy hier
494 state.tlvCurCtlFlowChar = (state.tlvCurCtlFlowChar == "")
495 ? ch : state.tlvCurCtlFlowChar;
496 stream.next();
497 stream.eatWhile(/[+-]\d/);
498 style = "tag";
499 } else if (tlvkpScopePrefixs.propertyIsEnumerable(ch)) {
500 // special TLV operators
501 style = tlvkpScopePrefixs[ch];
502 state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? "S" : state.tlvCurCtlFlowChar; // stmt
503 stream.next();
504 stream.match(/[a-zA-Z_0-9]+/);
505 } else if (style = tlvchScopePrefixes[ch] || false) {
506 // special TLV operators
507 state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? ch : state.tlvCurCtlFlowChar;
508 stream.next();
509 stream.match(/[a-zA-Z_0-9]+/);
645 style = "tlv-default";
646 }
647 if (beginStatement) {
648 style += " tlv-statement";
510 649 }
511 if (state.tlvCurCtlFlowChar != vxCurCtlFlowCharValueAtStart) { // flow change
512 vxIndent = tlvGenIndent(stream, state);
513 state.vxIndentRq = vxIndent;
650 } else {
651 if (stream.match(/^[mM]4([\w\d_]*)/)) {
652 // m4 pre proc
653 style = "tlv-m4";
514 654 }
515 655 }
516 656 return style;
517 657 },
518 token: function(stream, state) {
519 if (state.vxCodeActive == true && stream.sol() && state.tlvCurCtlFlowChar != "") {
520 state.tlvPrevPrevCtlFlowChar = state.tlvPrevCtlFlowChar;
521 state.tlvPrevCtlFlowChar = state.tlvCurCtlFlowChar;
522 state.tlvCurCtlFlowChar = "";
523 }
524 },
658
525 659 indent: function(state) {
526 return (state.vxCodeActive == true) ? state.vxIndentRq : -1;
660 return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1;
527 661 },
662
528 663 startState: function(state) {
529 state.tlvCurCtlFlowChar = "";
530 state.tlvPrevCtlFlowChar = "";
531 state.tlvPrevPrevCtlFlowChar = "";
532 state.vxCodeActive = true;
533 state.vxIndentRq = 0;
664 state.tlvIndentationStyle = []; // Styles to use for each level of indentation.
665 state.tlvCodeActive = true; // True when we're in a TLV region (and at beginning of file).
666 state.tlvNextIndent = -1; // The number of spaces to autoindent the next line if tlvCodeActive.
667 state.tlvInBlockComment = false; // True inside /**/ comment.
668 if (tlvTrackStatements) {
669 state.statementComment = false; // True inside a statement's header comment.
670 }
534 671 }
672
535 673 }
536 674 });
537 675 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Originally written by Alf Nielsen, re-written by Michael Zhou
5 5 (function(mod) {
@@ -36,7 +36,7 b' CodeMirror.defineMode("vhdl", function(c'
36 36 multiLineStrings = parserConfig.multiLineStrings;
37 37
38 38 var keywords = words("abs,access,after,alias,all,and,architecture,array,assert,attribute,begin,block," +
39 "body,buffer,bus,case,component,configuration,constant,disconnent,downto,else,elsif,end,end block,end case," +
39 "body,buffer,bus,case,component,configuration,constant,disconnect,downto,else,elsif,end,end block,end case," +
40 40 "end component,end for,end generate,end if,end loop,end process,end record,end units,entity,exit,file,for," +
41 41 "function,generate,generic,generic map,group,guarded,if,impure,in,inertial,inout,is,label,library,linkage," +
42 42 "literal,loop,map,mod,nand,new,next,nor,null,of,on,open,or,others,out,package,package body,port,port map," +
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function (mod) {
5 5 "use strict";
@@ -12,7 +12,7 b''
12 12 require("../css/css"),
13 13 require("../sass/sass"),
14 14 require("../stylus/stylus"),
15 require("../jade/jade"),
15 require("../pug/pug"),
16 16 require("../handlebars/handlebars"));
17 17 } else if (typeof define === "function" && define.amd) { // AMD
18 18 define(["../../lib/codemirror",
@@ -23,7 +23,7 b''
23 23 "../css/css",
24 24 "../sass/sass",
25 25 "../stylus/stylus",
26 "../jade/jade",
26 "../pug/pug",
27 27 "../handlebars/handlebars"], mod);
28 28 } else { // Plain browser env
29 29 mod(CodeMirror);
@@ -32,19 +32,26 b''
32 32 var tagLanguages = {
33 33 script: [
34 34 ["lang", /coffee(script)?/, "coffeescript"],
35 ["type", /^(?:text|application)\/(?:x-)?coffee(?:script)?$/, "coffeescript"]
35 ["type", /^(?:text|application)\/(?:x-)?coffee(?:script)?$/, "coffeescript"],
36 ["lang", /^babel$/, "javascript"],
37 ["type", /^text\/babel$/, "javascript"],
38 ["type", /^text\/ecmascript-\d+$/, "javascript"]
36 39 ],
37 40 style: [
38 41 ["lang", /^stylus$/i, "stylus"],
39 42 ["lang", /^sass$/i, "sass"],
43 ["lang", /^less$/i, "text/x-less"],
44 ["lang", /^scss$/i, "text/x-scss"],
40 45 ["type", /^(text\/)?(x-)?styl(us)?$/i, "stylus"],
41 ["type", /^text\/sass/i, "sass"]
46 ["type", /^text\/sass/i, "sass"],
47 ["type", /^(text\/)?(x-)?scss$/i, "text/x-scss"],
48 ["type", /^(text\/)?(x-)?less$/i, "text/x-less"]
42 49 ],
43 50 template: [
44 51 ["lang", /^vue-template$/i, "vue"],
45 ["lang", /^jade$/i, "jade"],
52 ["lang", /^pug$/i, "pug"],
46 53 ["lang", /^handlebars$/i, "handlebars"],
47 ["type", /^(text\/)?(x-)?jade$/i, "jade"],
54 ["type", /^(text\/)?(x-)?pug$/i, "pug"],
48 55 ["type", /^text\/x-handlebars-template$/i, "handlebars"],
49 56 [null, null, "vue-template"]
50 57 ]
@@ -63,7 +70,8 b''
63 70
64 71 CodeMirror.defineMode("vue", function (config) {
65 72 return CodeMirror.getMode(config, {name: "htmlmixed", tags: tagLanguages});
66 }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "jade", "handlebars");
73 }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "pug", "handlebars");
67 74
68 75 CodeMirror.defineMIME("script/x-vue", "vue");
76 CodeMirror.defineMIME("text/x-vue", "vue");
69 77 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -52,6 +52,7 b' var xmlConfig = {'
52 52 doNotIndent: {},
53 53 allowUnquoted: false,
54 54 allowMissing: false,
55 allowMissingTagName: false,
55 56 caseFold: false
56 57 }
57 58
@@ -162,8 +163,9 b' CodeMirror.defineMode("xml", function(ed'
162 163 stream.next();
163 164 }
164 165 return style;
165 };
166 }
166 167 }
168
167 169 function doctype(depth) {
168 170 return function(stream, state) {
169 171 var ch;
@@ -226,6 +228,9 b' CodeMirror.defineMode("xml", function(ed'
226 228 state.tagName = stream.current();
227 229 setStyle = "tag";
228 230 return attrState;
231 } else if (config.allowMissingTagName && type == "endTag") {
232 setStyle = "tag bracket";
233 return attrState(type, stream, state);
229 234 } else {
230 235 setStyle = "error";
231 236 return tagNameState;
@@ -237,13 +242,16 b' CodeMirror.defineMode("xml", function(ed'
237 242 if (state.context && state.context.tagName != tagName &&
238 243 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
239 244 popContext(state);
240 if (state.context && state.context.tagName == tagName) {
245 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
241 246 setStyle = "tag";
242 247 return closeState;
243 248 } else {
244 249 setStyle = "tag error";
245 250 return closeStateErr;
246 251 }
252 } else if (config.allowMissingTagName && type == "endTag") {
253 setStyle = "tag bracket";
254 return closeState(type, stream, state);
247 255 } else {
248 256 setStyle = "error";
249 257 return closeStateErr;
@@ -382,6 +390,17 b' CodeMirror.defineMode("xml", function(ed'
382 390 skipAttribute: function(state) {
383 391 if (state.state == attrValueState)
384 392 state.state = attrState
393 },
394
395 xmlCurrentTag: function(state) {
396 return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
397 },
398
399 xmlCurrentContext: function(state) {
400 var context = []
401 for (var cx = state.context; cx; cx = cx.prev)
402 if (cx.tagName) context.push(cx.tagName)
403 return context.reverse()
385 404 }
386 405 };
387 406 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -17,43 +17,54 b' CodeMirror.defineMode("xquery", function'
17 17 // function. Each keyword is a property of the keywords object whose
18 18 // value is {type: atype, style: astyle}
19 19 var keywords = function(){
20 // conveinence functions used to build keywords object
20 // convenience functions used to build keywords object
21 21 function kw(type) {return {type: type, style: "keyword"};}
22 var A = kw("keyword a")
23 , B = kw("keyword b")
24 , C = kw("keyword c")
25 , operator = kw("operator")
22 var operator = kw("operator")
26 23 , atom = {type: "atom", style: "atom"}
27 24 , punctuation = {type: "punctuation", style: null}
28 25 , qualifier = {type: "axis_specifier", style: "qualifier"};
29 26
30 27 // kwObj is what is return from this function at the end
31 28 var kwObj = {
32 'if': A, 'switch': A, 'while': A, 'for': A,
33 'else': B, 'then': B, 'try': B, 'finally': B, 'catch': B,
34 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C,
35 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C,
36 ',': punctuation,
37 'null': atom, 'fn:false()': atom, 'fn:true()': atom
29 ',': punctuation
38 30 };
39 31
40 32 // a list of 'basic' keywords. For each add a property to kwObj with the value of
41 33 // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"}
42 var basic = ['after','ancestor','ancestor-or-self','and','as','ascending','assert','attribute','before',
43 'by','case','cast','child','comment','declare','default','define','descendant','descendant-or-self',
44 'descending','document','document-node','element','else','eq','every','except','external','following',
45 'following-sibling','follows','for','function','if','import','in','instance','intersect','item',
46 'let','module','namespace','node','node','of','only','or','order','parent','precedes','preceding',
47 'preceding-sibling','processing-instruction','ref','return','returns','satisfies','schema','schema-element',
48 'self','some','sortby','stable','text','then','to','treat','typeswitch','union','variable','version','where',
49 'xquery', 'empty-sequence'];
34 var basic = ['after', 'all', 'allowing', 'ancestor', 'ancestor-or-self', 'any', 'array', 'as',
35 'ascending', 'at', 'attribute', 'base-uri', 'before', 'boundary-space', 'by', 'case', 'cast',
36 'castable', 'catch', 'child', 'collation', 'comment', 'construction', 'contains', 'content',
37 'context', 'copy', 'copy-namespaces', 'count', 'decimal-format', 'declare', 'default', 'delete',
38 'descendant', 'descendant-or-self', 'descending', 'diacritics', 'different', 'distance',
39 'document', 'document-node', 'element', 'else', 'empty', 'empty-sequence', 'encoding', 'end',
40 'entire', 'every', 'exactly', 'except', 'external', 'first', 'following', 'following-sibling',
41 'for', 'from', 'ftand', 'ftnot', 'ft-option', 'ftor', 'function', 'fuzzy', 'greatest', 'group',
42 'if', 'import', 'in', 'inherit', 'insensitive', 'insert', 'instance', 'intersect', 'into',
43 'invoke', 'is', 'item', 'language', 'last', 'lax', 'least', 'let', 'levels', 'lowercase', 'map',
44 'modify', 'module', 'most', 'namespace', 'next', 'no', 'node', 'nodes', 'no-inherit',
45 'no-preserve', 'not', 'occurs', 'of', 'only', 'option', 'order', 'ordered', 'ordering',
46 'paragraph', 'paragraphs', 'parent', 'phrase', 'preceding', 'preceding-sibling', 'preserve',
47 'previous', 'processing-instruction', 'relationship', 'rename', 'replace', 'return',
48 'revalidation', 'same', 'satisfies', 'schema', 'schema-attribute', 'schema-element', 'score',
49 'self', 'sensitive', 'sentence', 'sentences', 'sequence', 'skip', 'sliding', 'some', 'stable',
50 'start', 'stemming', 'stop', 'strict', 'strip', 'switch', 'text', 'then', 'thesaurus', 'times',
51 'to', 'transform', 'treat', 'try', 'tumbling', 'type', 'typeswitch', 'union', 'unordered',
52 'update', 'updating', 'uppercase', 'using', 'validate', 'value', 'variable', 'version',
53 'weight', 'when', 'where', 'wildcards', 'window', 'with', 'without', 'word', 'words', 'xquery'];
50 54 for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);};
51 55
52 56 // a list of types. For each add a property to kwObj with the value of
53 57 // {type: "atom", style: "atom"}
54 var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime',
55 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary',
56 'xs:base64Binary', 'xs:anyURI', 'xs:QName', 'xs:byte','xs:boolean','xs:anyURI','xf:yearMonthDuration'];
58 var types = ['xs:anyAtomicType', 'xs:anySimpleType', 'xs:anyType', 'xs:anyURI',
59 'xs:base64Binary', 'xs:boolean', 'xs:byte', 'xs:date', 'xs:dateTime', 'xs:dateTimeStamp',
60 'xs:dayTimeDuration', 'xs:decimal', 'xs:double', 'xs:duration', 'xs:ENTITIES', 'xs:ENTITY',
61 'xs:float', 'xs:gDay', 'xs:gMonth', 'xs:gMonthDay', 'xs:gYear', 'xs:gYearMonth', 'xs:hexBinary',
62 'xs:ID', 'xs:IDREF', 'xs:IDREFS', 'xs:int', 'xs:integer', 'xs:item', 'xs:java', 'xs:language',
63 'xs:long', 'xs:Name', 'xs:NCName', 'xs:negativeInteger', 'xs:NMTOKEN', 'xs:NMTOKENS',
64 'xs:nonNegativeInteger', 'xs:nonPositiveInteger', 'xs:normalizedString', 'xs:NOTATION',
65 'xs:numeric', 'xs:positiveInteger', 'xs:precisionDecimal', 'xs:QName', 'xs:short', 'xs:string',
66 'xs:time', 'xs:token', 'xs:unsignedByte', 'xs:unsignedInt', 'xs:unsignedLong',
67 'xs:unsignedShort', 'xs:untyped', 'xs:untypedAtomic', 'xs:yearMonthDuration'];
57 68 for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;};
58 69
59 70 // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"}
@@ -102,7 +113,7 b' CodeMirror.defineMode("xquery", function'
102 113 }
103 114 // start code block
104 115 else if(ch == "{") {
105 pushStateStack(state,{ type: "codeblock"});
116 pushStateStack(state, { type: "codeblock"});
106 117 return null;
107 118 }
108 119 // end code block
@@ -132,7 +143,7 b' CodeMirror.defineMode("xquery", function'
132 143 return chain(stream, state, tokenComment);
133 144 }
134 145 // quoted string
135 else if ( !isEQName && (ch === '"' || ch === "'"))
146 else if (!isEQName && (ch === '"' || ch === "'"))
136 147 return chain(stream, state, tokenString(ch));
137 148 // variable
138 149 else if(ch === "$") {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function (mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -45,7 +45,7 b''
45 45 return innerMode.token(stream, state.inner)
46 46 }
47 47 } else if (state.state == FRONTMATTER) {
48 var end = stream.sol() && stream.match(/---/, false)
48 var end = stream.sol() && stream.match(/(---|\.\.\.)/, false)
49 49 var style = yamlMode.token(stream, state.inner)
50 50 if (end) {
51 51 state.state = BODY
@@ -65,4 +65,4 b''
65 65 }
66 66 }
67 67 })
68 })
68 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -108,10 +108,13 b' CodeMirror.defineMode("yaml", function()'
108 108 literal: false,
109 109 escaped: false
110 110 };
111 }
111 },
112 lineComment: "#",
113 fold: "indent"
112 114 };
113 115 });
114 116
115 117 CodeMirror.defineMIME("text/x-yaml", "yaml");
118 CodeMirror.defineMIME("text/yaml", "yaml");
116 119
117 120 });
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
This diff has been collapsed as it changes many lines, (17503 lines changed) Show them Hide them
@@ -1,23 +1,17 b''
1 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 // This is CodeMirror (http://codemirror.net), a code editor
2 // Distributed under an MIT license: https://codemirror.net/LICENSE
3
4 // This is CodeMirror (https://codemirror.net), a code editor
5 5 // implemented in JavaScript on top of the browser's DOM.
6 6 //
7 7 // You can find some technical background for some of the code below
8 8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9 9
10 (function(mod) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS
12 module.exports = mod();
13 else if (typeof define == "function" && define.amd) // AMD
14 return define([], mod);
15 else // Plain browser env
16 (this || window).CodeMirror = mod();
17 })(function() {
18 "use strict";
19
20 // BROWSER SNIFFING
10 (function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12 typeof define === 'function' && define.amd ? define(factory) :
13 (global.CodeMirror = factory());
14 }(this, (function () { 'use strict';
21 15
22 16 // Kludges for bugs and behavior differences that can't be feature
23 17 // detected are enabled based on userAgent etc sniffing.
@@ -27,113 +21,4314 b''
27 21 var gecko = /gecko\/\d/i.test(userAgent);
28 22 var ie_upto10 = /MSIE \d/.test(userAgent);
29 23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
30 var ie = ie_upto10 || ie_11up;
31 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
32 var webkit = /WebKit\//.test(userAgent);
24 var edge = /Edge\/(\d+)/.exec(userAgent);
25 var ie = ie_upto10 || ie_11up || edge;
26 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
27 var webkit = !edge && /WebKit\//.test(userAgent);
33 28 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
34 var chrome = /Chrome\//.test(userAgent);
29 var chrome = !edge && /Chrome\//.test(userAgent);
35 30 var presto = /Opera\//.test(userAgent);
36 31 var safari = /Apple Computer/.test(navigator.vendor);
37 32 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
38 33 var phantom = /PhantomJS/.test(userAgent);
39 34
40 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
35 var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
36 var android = /Android/.test(userAgent);
41 37 // This is woefully incomplete. Suggestions for alternative methods welcome.
42 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
38 var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
43 39 var mac = ios || /Mac/.test(platform);
40 var chromeOS = /\bCrOS\b/.test(userAgent);
44 41 var windows = /win/i.test(platform);
45 42
46 43 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
47 if (presto_version) presto_version = Number(presto_version[1]);
44 if (presto_version) { presto_version = Number(presto_version[1]); }
48 45 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
49 46 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
50 47 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
51 48 var captureRightClick = gecko || (ie && ie_version >= 9);
52 49
50 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51
52 var rmClass = function(node, cls) {
53 var current = node.className;
54 var match = classTest(cls).exec(current);
55 if (match) {
56 var after = current.slice(match.index + match[0].length);
57 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
58 }
59 };
60
61 function removeChildren(e) {
62 for (var count = e.childNodes.length; count > 0; --count)
63 { e.removeChild(e.firstChild); }
64 return e
65 }
66
67 function removeChildrenAndAdd(parent, e) {
68 return removeChildren(parent).appendChild(e)
69 }
70
71 function elt(tag, content, className, style) {
72 var e = document.createElement(tag);
73 if (className) { e.className = className; }
74 if (style) { e.style.cssText = style; }
75 if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
76 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
77 return e
78 }
79 // wrapper for elt, which removes the elt from the accessibility tree
80 function eltP(tag, content, className, style) {
81 var e = elt(tag, content, className, style);
82 e.setAttribute("role", "presentation");
83 return e
84 }
85
86 var range;
87 if (document.createRange) { range = function(node, start, end, endNode) {
88 var r = document.createRange();
89 r.setEnd(endNode || node, end);
90 r.setStart(node, start);
91 return r
92 }; }
93 else { range = function(node, start, end) {
94 var r = document.body.createTextRange();
95 try { r.moveToElementText(node.parentNode); }
96 catch(e) { return r }
97 r.collapse(true);
98 r.moveEnd("character", end);
99 r.moveStart("character", start);
100 return r
101 }; }
102
103 function contains(parent, child) {
104 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105 { child = child.parentNode; }
106 if (parent.contains)
107 { return parent.contains(child) }
108 do {
109 if (child.nodeType == 11) { child = child.host; }
110 if (child == parent) { return true }
111 } while (child = child.parentNode)
112 }
113
114 function activeElt() {
115 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
116 // IE < 10 will throw when accessed while the page is loading or in an iframe.
117 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
118 var activeElement;
119 try {
120 activeElement = document.activeElement;
121 } catch(e) {
122 activeElement = document.body || null;
123 }
124 while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
125 { activeElement = activeElement.shadowRoot.activeElement; }
126 return activeElement
127 }
128
129 function addClass(node, cls) {
130 var current = node.className;
131 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
132 }
133 function joinClasses(a, b) {
134 var as = a.split(" ");
135 for (var i = 0; i < as.length; i++)
136 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
137 return b
138 }
139
140 var selectInput = function(node) { node.select(); };
141 if (ios) // Mobile Safari apparently has a bug where select() is broken.
142 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
143 else if (ie) // Suppress mysterious IE10 errors
144 { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
145
146 function bind(f) {
147 var args = Array.prototype.slice.call(arguments, 1);
148 return function(){return f.apply(null, args)}
149 }
150
151 function copyObj(obj, target, overwrite) {
152 if (!target) { target = {}; }
153 for (var prop in obj)
154 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
155 { target[prop] = obj[prop]; } }
156 return target
157 }
158
159 // Counts the column offset in a string, taking tabs into account.
160 // Used mostly to find indentation.
161 function countColumn(string, end, tabSize, startIndex, startValue) {
162 if (end == null) {
163 end = string.search(/[^\s\u00a0]/);
164 if (end == -1) { end = string.length; }
165 }
166 for (var i = startIndex || 0, n = startValue || 0;;) {
167 var nextTab = string.indexOf("\t", i);
168 if (nextTab < 0 || nextTab >= end)
169 { return n + (end - i) }
170 n += nextTab - i;
171 n += tabSize - (n % tabSize);
172 i = nextTab + 1;
173 }
174 }
175
176 var Delayed = function() {
177 this.id = null;
178 this.f = null;
179 this.time = 0;
180 this.handler = bind(this.onTimeout, this);
181 };
182 Delayed.prototype.onTimeout = function (self) {
183 self.id = 0;
184 if (self.time <= +new Date) {
185 self.f();
186 } else {
187 setTimeout(self.handler, self.time - +new Date);
188 }
189 };
190 Delayed.prototype.set = function (ms, f) {
191 this.f = f;
192 var time = +new Date + ms;
193 if (!this.id || time < this.time) {
194 clearTimeout(this.id);
195 this.id = setTimeout(this.handler, ms);
196 this.time = time;
197 }
198 };
199
200 function indexOf(array, elt) {
201 for (var i = 0; i < array.length; ++i)
202 { if (array[i] == elt) { return i } }
203 return -1
204 }
205
206 // Number of pixels added to scroller and sizer to hide scrollbar
207 var scrollerGap = 30;
208
209 // Returned or thrown by various protocols to signal 'I'm not
210 // handling this'.
211 var Pass = {toString: function(){return "CodeMirror.Pass"}};
212
213 // Reused option objects for setSelection & friends
214 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
215
216 // The inverse of countColumn -- find the offset that corresponds to
217 // a particular column.
218 function findColumn(string, goal, tabSize) {
219 for (var pos = 0, col = 0;;) {
220 var nextTab = string.indexOf("\t", pos);
221 if (nextTab == -1) { nextTab = string.length; }
222 var skipped = nextTab - pos;
223 if (nextTab == string.length || col + skipped >= goal)
224 { return pos + Math.min(skipped, goal - col) }
225 col += nextTab - pos;
226 col += tabSize - (col % tabSize);
227 pos = nextTab + 1;
228 if (col >= goal) { return pos }
229 }
230 }
231
232 var spaceStrs = [""];
233 function spaceStr(n) {
234 while (spaceStrs.length <= n)
235 { spaceStrs.push(lst(spaceStrs) + " "); }
236 return spaceStrs[n]
237 }
238
239 function lst(arr) { return arr[arr.length-1] }
240
241 function map(array, f) {
242 var out = [];
243 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
244 return out
245 }
246
247 function insertSorted(array, value, score) {
248 var pos = 0, priority = score(value);
249 while (pos < array.length && score(array[pos]) <= priority) { pos++; }
250 array.splice(pos, 0, value);
251 }
252
253 function nothing() {}
254
255 function createObj(base, props) {
256 var inst;
257 if (Object.create) {
258 inst = Object.create(base);
259 } else {
260 nothing.prototype = base;
261 inst = new nothing();
262 }
263 if (props) { copyObj(props, inst); }
264 return inst
265 }
266
267 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
268 function isWordCharBasic(ch) {
269 return /\w/.test(ch) || ch > "\x80" &&
270 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
271 }
272 function isWordChar(ch, helper) {
273 if (!helper) { return isWordCharBasic(ch) }
274 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
275 return helper.test(ch)
276 }
277
278 function isEmpty(obj) {
279 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
280 return true
281 }
282
283 // Extending unicode characters. A series of a non-extending char +
284 // any number of extending chars is treated as a single unit as far
285 // as editing and measuring is concerned. This is not fully correct,
286 // since some scripts/fonts/browsers also treat other configurations
287 // of code points as a group.
288 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]/;
289 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
290
291 // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
292 function skipExtendingChars(str, pos, dir) {
293 while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
294 return pos
295 }
296
297 // Returns the value from the range [`from`; `to`] that satisfies
298 // `pred` and is closest to `from`. Assumes that at least `to`
299 // satisfies `pred`. Supports `from` being greater than `to`.
300 function findFirst(pred, from, to) {
301 // At any point we are certain `to` satisfies `pred`, don't know
302 // whether `from` does.
303 var dir = from > to ? -1 : 1;
304 for (;;) {
305 if (from == to) { return from }
306 var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
307 if (mid == from) { return pred(mid) ? from : to }
308 if (pred(mid)) { to = mid; }
309 else { from = mid + dir; }
310 }
311 }
312
313 // BIDI HELPERS
314
315 function iterateBidiSections(order, from, to, f) {
316 if (!order) { return f(from, to, "ltr", 0) }
317 var found = false;
318 for (var i = 0; i < order.length; ++i) {
319 var part = order[i];
320 if (part.from < to && part.to > from || from == to && part.to == from) {
321 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
322 found = true;
323 }
324 }
325 if (!found) { f(from, to, "ltr"); }
326 }
327
328 var bidiOther = null;
329 function getBidiPartAt(order, ch, sticky) {
330 var found;
331 bidiOther = null;
332 for (var i = 0; i < order.length; ++i) {
333 var cur = order[i];
334 if (cur.from < ch && cur.to > ch) { return i }
335 if (cur.to == ch) {
336 if (cur.from != cur.to && sticky == "before") { found = i; }
337 else { bidiOther = i; }
338 }
339 if (cur.from == ch) {
340 if (cur.from != cur.to && sticky != "before") { found = i; }
341 else { bidiOther = i; }
342 }
343 }
344 return found != null ? found : bidiOther
345 }
346
347 // Bidirectional ordering algorithm
348 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
349 // that this (partially) implements.
350
351 // One-char codes used for character types:
352 // L (L): Left-to-Right
353 // R (R): Right-to-Left
354 // r (AL): Right-to-Left Arabic
355 // 1 (EN): European Number
356 // + (ES): European Number Separator
357 // % (ET): European Number Terminator
358 // n (AN): Arabic Number
359 // , (CS): Common Number Separator
360 // m (NSM): Non-Spacing Mark
361 // b (BN): Boundary Neutral
362 // s (B): Paragraph Separator
363 // t (S): Segment Separator
364 // w (WS): Whitespace
365 // N (ON): Other Neutrals
366
367 // Returns null if characters are ordered as they appear
368 // (left-to-right), or an array of sections ({from, to, level}
369 // objects) in the order in which they occur visually.
370 var bidiOrdering = (function() {
371 // Character types for codepoints 0 to 0xff
372 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
373 // Character types for codepoints 0x600 to 0x6f9
374 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
375 function charType(code) {
376 if (code <= 0xf7) { return lowTypes.charAt(code) }
377 else if (0x590 <= code && code <= 0x5f4) { return "R" }
378 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
379 else if (0x6ee <= code && code <= 0x8ac) { return "r" }
380 else if (0x2000 <= code && code <= 0x200b) { return "w" }
381 else if (code == 0x200c) { return "b" }
382 else { return "L" }
383 }
384
385 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
386 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
387
388 function BidiSpan(level, from, to) {
389 this.level = level;
390 this.from = from; this.to = to;
391 }
392
393 return function(str, direction) {
394 var outerType = direction == "ltr" ? "L" : "R";
395
396 if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
397 var len = str.length, types = [];
398 for (var i = 0; i < len; ++i)
399 { types.push(charType(str.charCodeAt(i))); }
400
401 // W1. Examine each non-spacing mark (NSM) in the level run, and
402 // change the type of the NSM to the type of the previous
403 // character. If the NSM is at the start of the level run, it will
404 // get the type of sor.
405 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
406 var type = types[i$1];
407 if (type == "m") { types[i$1] = prev; }
408 else { prev = type; }
409 }
410
411 // W2. Search backwards from each instance of a European number
412 // until the first strong type (R, L, AL, or sor) is found. If an
413 // AL is found, change the type of the European number to Arabic
414 // number.
415 // W3. Change all ALs to R.
416 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
417 var type$1 = types[i$2];
418 if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
419 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
420 }
421
422 // W4. A single European separator between two European numbers
423 // changes to a European number. A single common separator between
424 // two numbers of the same type changes to that type.
425 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
426 var type$2 = types[i$3];
427 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
428 else if (type$2 == "," && prev$1 == types[i$3+1] &&
429 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
430 prev$1 = type$2;
431 }
432
433 // W5. A sequence of European terminators adjacent to European
434 // numbers changes to all European numbers.
435 // W6. Otherwise, separators and terminators change to Other
436 // Neutral.
437 for (var i$4 = 0; i$4 < len; ++i$4) {
438 var type$3 = types[i$4];
439 if (type$3 == ",") { types[i$4] = "N"; }
440 else if (type$3 == "%") {
441 var end = (void 0);
442 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
443 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
444 for (var j = i$4; j < end; ++j) { types[j] = replace; }
445 i$4 = end - 1;
446 }
447 }
448
449 // W7. Search backwards from each instance of a European number
450 // until the first strong type (R, L, or sor) is found. If an L is
451 // found, then change the type of the European number to L.
452 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
453 var type$4 = types[i$5];
454 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
455 else if (isStrong.test(type$4)) { cur$1 = type$4; }
456 }
457
458 // N1. A sequence of neutrals takes the direction of the
459 // surrounding strong text if the text on both sides has the same
460 // direction. European and Arabic numbers act as if they were R in
461 // terms of their influence on neutrals. Start-of-level-run (sor)
462 // and end-of-level-run (eor) are used at level run boundaries.
463 // N2. Any remaining neutrals take the embedding direction.
464 for (var i$6 = 0; i$6 < len; ++i$6) {
465 if (isNeutral.test(types[i$6])) {
466 var end$1 = (void 0);
467 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
468 var before = (i$6 ? types[i$6-1] : outerType) == "L";
469 var after = (end$1 < len ? types[end$1] : outerType) == "L";
470 var replace$1 = before == after ? (before ? "L" : "R") : outerType;
471 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
472 i$6 = end$1 - 1;
473 }
474 }
475
476 // Here we depart from the documented algorithm, in order to avoid
477 // building up an actual levels array. Since there are only three
478 // levels (0, 1, 2) in an implementation that doesn't take
479 // explicit embedding into account, we can build up the order on
480 // the fly, without following the level-based algorithm.
481 var order = [], m;
482 for (var i$7 = 0; i$7 < len;) {
483 if (countsAsLeft.test(types[i$7])) {
484 var start = i$7;
485 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
486 order.push(new BidiSpan(0, start, i$7));
487 } else {
488 var pos = i$7, at = order.length;
489 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
490 for (var j$2 = pos; j$2 < i$7;) {
491 if (countsAsNum.test(types[j$2])) {
492 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
493 var nstart = j$2;
494 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
495 order.splice(at, 0, new BidiSpan(2, nstart, j$2));
496 pos = j$2;
497 } else { ++j$2; }
498 }
499 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
500 }
501 }
502 if (direction == "ltr") {
503 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
504 order[0].from = m[0].length;
505 order.unshift(new BidiSpan(0, 0, m[0].length));
506 }
507 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
508 lst(order).to -= m[0].length;
509 order.push(new BidiSpan(0, len - m[0].length, len));
510 }
511 }
512
513 return direction == "rtl" ? order.reverse() : order
514 }
515 })();
516
517 // Get the bidi ordering for the given line (and cache it). Returns
518 // false for lines that are fully left-to-right, and an array of
519 // BidiSpan objects otherwise.
520 function getOrder(line, direction) {
521 var order = line.order;
522 if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
523 return order
524 }
525
526 // EVENT HANDLING
527
528 // Lightweight event framework. on/off also work on DOM nodes,
529 // registering native DOM handlers.
530
531 var noHandlers = [];
532
533 var on = function(emitter, type, f) {
534 if (emitter.addEventListener) {
535 emitter.addEventListener(type, f, false);
536 } else if (emitter.attachEvent) {
537 emitter.attachEvent("on" + type, f);
538 } else {
539 var map$$1 = emitter._handlers || (emitter._handlers = {});
540 map$$1[type] = (map$$1[type] || noHandlers).concat(f);
541 }
542 };
543
544 function getHandlers(emitter, type) {
545 return emitter._handlers && emitter._handlers[type] || noHandlers
546 }
547
548 function off(emitter, type, f) {
549 if (emitter.removeEventListener) {
550 emitter.removeEventListener(type, f, false);
551 } else if (emitter.detachEvent) {
552 emitter.detachEvent("on" + type, f);
553 } else {
554 var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
555 if (arr) {
556 var index = indexOf(arr, f);
557 if (index > -1)
558 { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
559 }
560 }
561 }
562
563 function signal(emitter, type /*, values...*/) {
564 var handlers = getHandlers(emitter, type);
565 if (!handlers.length) { return }
566 var args = Array.prototype.slice.call(arguments, 2);
567 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
568 }
569
570 // The DOM events that CodeMirror handles can be overridden by
571 // registering a (non-DOM) handler on the editor for the event name,
572 // and preventDefault-ing the event in that handler.
573 function signalDOMEvent(cm, e, override) {
574 if (typeof e == "string")
575 { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
576 signal(cm, override || e.type, cm, e);
577 return e_defaultPrevented(e) || e.codemirrorIgnore
578 }
579
580 function signalCursorActivity(cm) {
581 var arr = cm._handlers && cm._handlers.cursorActivity;
582 if (!arr) { return }
583 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
584 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
585 { set.push(arr[i]); } }
586 }
587
588 function hasHandler(emitter, type) {
589 return getHandlers(emitter, type).length > 0
590 }
591
592 // Add on and off methods to a constructor's prototype, to make
593 // registering events on such objects more convenient.
594 function eventMixin(ctor) {
595 ctor.prototype.on = function(type, f) {on(this, type, f);};
596 ctor.prototype.off = function(type, f) {off(this, type, f);};
597 }
598
599 // Due to the fact that we still support jurassic IE versions, some
600 // compatibility wrappers are needed.
601
602 function e_preventDefault(e) {
603 if (e.preventDefault) { e.preventDefault(); }
604 else { e.returnValue = false; }
605 }
606 function e_stopPropagation(e) {
607 if (e.stopPropagation) { e.stopPropagation(); }
608 else { e.cancelBubble = true; }
609 }
610 function e_defaultPrevented(e) {
611 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
612 }
613 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
614
615 function e_target(e) {return e.target || e.srcElement}
616 function e_button(e) {
617 var b = e.which;
618 if (b == null) {
619 if (e.button & 1) { b = 1; }
620 else if (e.button & 2) { b = 3; }
621 else if (e.button & 4) { b = 2; }
622 }
623 if (mac && e.ctrlKey && b == 1) { b = 3; }
624 return b
625 }
626
627 // Detect drag-and-drop
628 var dragAndDrop = function() {
629 // There is *some* kind of drag-and-drop support in IE6-8, but I
630 // couldn't get it to work yet.
631 if (ie && ie_version < 9) { return false }
632 var div = elt('div');
633 return "draggable" in div || "dragDrop" in div
634 }();
635
636 var zwspSupported;
637 function zeroWidthElement(measure) {
638 if (zwspSupported == null) {
639 var test = elt("span", "\u200b");
640 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
641 if (measure.firstChild.offsetHeight != 0)
642 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
643 }
644 var node = zwspSupported ? elt("span", "\u200b") :
645 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
646 node.setAttribute("cm-text", "");
647 return node
648 }
649
650 // Feature-detect IE's crummy client rect reporting for bidi text
651 var badBidiRects;
652 function hasBadBidiRects(measure) {
653 if (badBidiRects != null) { return badBidiRects }
654 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
655 var r0 = range(txt, 0, 1).getBoundingClientRect();
656 var r1 = range(txt, 1, 2).getBoundingClientRect();
657 removeChildren(measure);
658 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
659 return badBidiRects = (r1.right - r0.right < 3)
660 }
661
662 // See if "".split is the broken IE version, if so, provide an
663 // alternative way to split lines.
664 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
665 var pos = 0, result = [], l = string.length;
666 while (pos <= l) {
667 var nl = string.indexOf("\n", pos);
668 if (nl == -1) { nl = string.length; }
669 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
670 var rt = line.indexOf("\r");
671 if (rt != -1) {
672 result.push(line.slice(0, rt));
673 pos += rt + 1;
674 } else {
675 result.push(line);
676 pos = nl + 1;
677 }
678 }
679 return result
680 } : function (string) { return string.split(/\r\n?|\n/); };
681
682 var hasSelection = window.getSelection ? function (te) {
683 try { return te.selectionStart != te.selectionEnd }
684 catch(e) { return false }
685 } : function (te) {
686 var range$$1;
687 try {range$$1 = te.ownerDocument.selection.createRange();}
688 catch(e) {}
689 if (!range$$1 || range$$1.parentElement() != te) { return false }
690 return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
691 };
692
693 var hasCopyEvent = (function () {
694 var e = elt("div");
695 if ("oncopy" in e) { return true }
696 e.setAttribute("oncopy", "return;");
697 return typeof e.oncopy == "function"
698 })();
699
700 var badZoomedRects = null;
701 function hasBadZoomedRects(measure) {
702 if (badZoomedRects != null) { return badZoomedRects }
703 var node = removeChildrenAndAdd(measure, elt("span", "x"));
704 var normal = node.getBoundingClientRect();
705 var fromRange = range(node, 0, 1).getBoundingClientRect();
706 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
707 }
708
709 // Known modes, by name and by MIME
710 var modes = {}, mimeModes = {};
711
712 // Extra arguments are stored as the mode's dependencies, which is
713 // used by (legacy) mechanisms like loadmode.js to automatically
714 // load a mode. (Preferred mechanism is the require/define calls.)
715 function defineMode(name, mode) {
716 if (arguments.length > 2)
717 { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
718 modes[name] = mode;
719 }
720
721 function defineMIME(mime, spec) {
722 mimeModes[mime] = spec;
723 }
724
725 // Given a MIME type, a {name, ...options} config object, or a name
726 // string, return a mode config object.
727 function resolveMode(spec) {
728 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
729 spec = mimeModes[spec];
730 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
731 var found = mimeModes[spec.name];
732 if (typeof found == "string") { found = {name: found}; }
733 spec = createObj(found, spec);
734 spec.name = found.name;
735 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
736 return resolveMode("application/xml")
737 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
738 return resolveMode("application/json")
739 }
740 if (typeof spec == "string") { return {name: spec} }
741 else { return spec || {name: "null"} }
742 }
743
744 // Given a mode spec (anything that resolveMode accepts), find and
745 // initialize an actual mode object.
746 function getMode(options, spec) {
747 spec = resolveMode(spec);
748 var mfactory = modes[spec.name];
749 if (!mfactory) { return getMode(options, "text/plain") }
750 var modeObj = mfactory(options, spec);
751 if (modeExtensions.hasOwnProperty(spec.name)) {
752 var exts = modeExtensions[spec.name];
753 for (var prop in exts) {
754 if (!exts.hasOwnProperty(prop)) { continue }
755 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
756 modeObj[prop] = exts[prop];
757 }
758 }
759 modeObj.name = spec.name;
760 if (spec.helperType) { modeObj.helperType = spec.helperType; }
761 if (spec.modeProps) { for (var prop$1 in spec.modeProps)
762 { modeObj[prop$1] = spec.modeProps[prop$1]; } }
763
764 return modeObj
765 }
766
767 // This can be used to attach properties to mode objects from
768 // outside the actual mode definition.
769 var modeExtensions = {};
770 function extendMode(mode, properties) {
771 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
772 copyObj(properties, exts);
773 }
774
775 function copyState(mode, state) {
776 if (state === true) { return state }
777 if (mode.copyState) { return mode.copyState(state) }
778 var nstate = {};
779 for (var n in state) {
780 var val = state[n];
781 if (val instanceof Array) { val = val.concat([]); }
782 nstate[n] = val;
783 }
784 return nstate
785 }
786
787 // Given a mode and a state (for that mode), find the inner mode and
788 // state at the position that the state refers to.
789 function innerMode(mode, state) {
790 var info;
791 while (mode.innerMode) {
792 info = mode.innerMode(state);
793 if (!info || info.mode == mode) { break }
794 state = info.state;
795 mode = info.mode;
796 }
797 return info || {mode: mode, state: state}
798 }
799
800 function startState(mode, a1, a2) {
801 return mode.startState ? mode.startState(a1, a2) : true
802 }
803
804 // STRING STREAM
805
806 // Fed to the mode parsers, provides helper functions to make
807 // parsers more succinct.
808
809 var StringStream = function(string, tabSize, lineOracle) {
810 this.pos = this.start = 0;
811 this.string = string;
812 this.tabSize = tabSize || 8;
813 this.lastColumnPos = this.lastColumnValue = 0;
814 this.lineStart = 0;
815 this.lineOracle = lineOracle;
816 };
817
818 StringStream.prototype.eol = function () {return this.pos >= this.string.length};
819 StringStream.prototype.sol = function () {return this.pos == this.lineStart};
820 StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
821 StringStream.prototype.next = function () {
822 if (this.pos < this.string.length)
823 { return this.string.charAt(this.pos++) }
824 };
825 StringStream.prototype.eat = function (match) {
826 var ch = this.string.charAt(this.pos);
827 var ok;
828 if (typeof match == "string") { ok = ch == match; }
829 else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
830 if (ok) {++this.pos; return ch}
831 };
832 StringStream.prototype.eatWhile = function (match) {
833 var start = this.pos;
834 while (this.eat(match)){}
835 return this.pos > start
836 };
837 StringStream.prototype.eatSpace = function () {
838 var this$1 = this;
839
840 var start = this.pos;
841 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
842 return this.pos > start
843 };
844 StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
845 StringStream.prototype.skipTo = function (ch) {
846 var found = this.string.indexOf(ch, this.pos);
847 if (found > -1) {this.pos = found; return true}
848 };
849 StringStream.prototype.backUp = function (n) {this.pos -= n;};
850 StringStream.prototype.column = function () {
851 if (this.lastColumnPos < this.start) {
852 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
853 this.lastColumnPos = this.start;
854 }
855 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
856 };
857 StringStream.prototype.indentation = function () {
858 return countColumn(this.string, null, this.tabSize) -
859 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
860 };
861 StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
862 if (typeof pattern == "string") {
863 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
864 var substr = this.string.substr(this.pos, pattern.length);
865 if (cased(substr) == cased(pattern)) {
866 if (consume !== false) { this.pos += pattern.length; }
867 return true
868 }
869 } else {
870 var match = this.string.slice(this.pos).match(pattern);
871 if (match && match.index > 0) { return null }
872 if (match && consume !== false) { this.pos += match[0].length; }
873 return match
874 }
875 };
876 StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
877 StringStream.prototype.hideFirstChars = function (n, inner) {
878 this.lineStart += n;
879 try { return inner() }
880 finally { this.lineStart -= n; }
881 };
882 StringStream.prototype.lookAhead = function (n) {
883 var oracle = this.lineOracle;
884 return oracle && oracle.lookAhead(n)
885 };
886 StringStream.prototype.baseToken = function () {
887 var oracle = this.lineOracle;
888 return oracle && oracle.baseToken(this.pos)
889 };
890
891 // Find the line object corresponding to the given line number.
892 function getLine(doc, n) {
893 n -= doc.first;
894 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
895 var chunk = doc;
896 while (!chunk.lines) {
897 for (var i = 0;; ++i) {
898 var child = chunk.children[i], sz = child.chunkSize();
899 if (n < sz) { chunk = child; break }
900 n -= sz;
901 }
902 }
903 return chunk.lines[n]
904 }
905
906 // Get the part of a document between two positions, as an array of
907 // strings.
908 function getBetween(doc, start, end) {
909 var out = [], n = start.line;
910 doc.iter(start.line, end.line + 1, function (line) {
911 var text = line.text;
912 if (n == end.line) { text = text.slice(0, end.ch); }
913 if (n == start.line) { text = text.slice(start.ch); }
914 out.push(text);
915 ++n;
916 });
917 return out
918 }
919 // Get the lines between from and to, as array of strings.
920 function getLines(doc, from, to) {
921 var out = [];
922 doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
923 return out
924 }
925
926 // Update the height of a line, propagating the height change
927 // upwards to parent nodes.
928 function updateLineHeight(line, height) {
929 var diff = height - line.height;
930 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
931 }
932
933 // Given a line object, find its line number by walking up through
934 // its parent links.
935 function lineNo(line) {
936 if (line.parent == null) { return null }
937 var cur = line.parent, no = indexOf(cur.lines, line);
938 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
939 for (var i = 0;; ++i) {
940 if (chunk.children[i] == cur) { break }
941 no += chunk.children[i].chunkSize();
942 }
943 }
944 return no + cur.first
945 }
946
947 // Find the line at the given vertical position, using the height
948 // information in the document tree.
949 function lineAtHeight(chunk, h) {
950 var n = chunk.first;
951 outer: do {
952 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
953 var child = chunk.children[i$1], ch = child.height;
954 if (h < ch) { chunk = child; continue outer }
955 h -= ch;
956 n += child.chunkSize();
957 }
958 return n
959 } while (!chunk.lines)
960 var i = 0;
961 for (; i < chunk.lines.length; ++i) {
962 var line = chunk.lines[i], lh = line.height;
963 if (h < lh) { break }
964 h -= lh;
965 }
966 return n + i
967 }
968
969 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
970
971 function lineNumberFor(options, i) {
972 return String(options.lineNumberFormatter(i + options.firstLineNumber))
973 }
974
975 // A Pos instance represents a position within the text.
976 function Pos(line, ch, sticky) {
977 if ( sticky === void 0 ) sticky = null;
978
979 if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
980 this.line = line;
981 this.ch = ch;
982 this.sticky = sticky;
983 }
984
985 // Compare two positions, return 0 if they are the same, a negative
986 // number when a is less, and a positive number otherwise.
987 function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
988
989 function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
990
991 function copyPos(x) {return Pos(x.line, x.ch)}
992 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
993 function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
994
995 // Most of the external API clips given positions to make sure they
996 // actually exist within the document.
997 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
998 function clipPos(doc, pos) {
999 if (pos.line < doc.first) { return Pos(doc.first, 0) }
1000 var last = doc.first + doc.size - 1;
1001 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
1002 return clipToLen(pos, getLine(doc, pos.line).text.length)
1003 }
1004 function clipToLen(pos, linelen) {
1005 var ch = pos.ch;
1006 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
1007 else if (ch < 0) { return Pos(pos.line, 0) }
1008 else { return pos }
1009 }
1010 function clipPosArray(doc, array) {
1011 var out = [];
1012 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
1013 return out
1014 }
1015
1016 var SavedContext = function(state, lookAhead) {
1017 this.state = state;
1018 this.lookAhead = lookAhead;
1019 };
1020
1021 var Context = function(doc, state, line, lookAhead) {
1022 this.state = state;
1023 this.doc = doc;
1024 this.line = line;
1025 this.maxLookAhead = lookAhead || 0;
1026 this.baseTokens = null;
1027 this.baseTokenPos = 1;
1028 };
1029
1030 Context.prototype.lookAhead = function (n) {
1031 var line = this.doc.getLine(this.line + n);
1032 if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
1033 return line
1034 };
1035
1036 Context.prototype.baseToken = function (n) {
1037 var this$1 = this;
1038
1039 if (!this.baseTokens) { return null }
1040 while (this.baseTokens[this.baseTokenPos] <= n)
1041 { this$1.baseTokenPos += 2; }
1042 var type = this.baseTokens[this.baseTokenPos + 1];
1043 return {type: type && type.replace(/( |^)overlay .*/, ""),
1044 size: this.baseTokens[this.baseTokenPos] - n}
1045 };
1046
1047 Context.prototype.nextLine = function () {
1048 this.line++;
1049 if (this.maxLookAhead > 0) { this.maxLookAhead--; }
1050 };
1051
1052 Context.fromSaved = function (doc, saved, line) {
1053 if (saved instanceof SavedContext)
1054 { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1055 else
1056 { return new Context(doc, copyState(doc.mode, saved), line) }
1057 };
1058
1059 Context.prototype.save = function (copy) {
1060 var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
1061 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1062 };
1063
1064
1065 // Compute a style array (an array starting with a mode generation
1066 // -- for invalidation -- followed by pairs of end positions and
1067 // style strings), which is used to highlight the tokens on the
1068 // line.
1069 function highlightLine(cm, line, context, forceToEnd) {
1070 // A styles array always starts with a number identifying the
1071 // mode/overlays that it is based on (for easy invalidation).
1072 var st = [cm.state.modeGen], lineClasses = {};
1073 // Compute the base array of styles
1074 runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1075 lineClasses, forceToEnd);
1076 var state = context.state;
1077
1078 // Run overlays, adjust style array.
1079 var loop = function ( o ) {
1080 context.baseTokens = st;
1081 var overlay = cm.state.overlays[o], i = 1, at = 0;
1082 context.state = true;
1083 runMode(cm, line.text, overlay.mode, context, function (end, style) {
1084 var start = i;
1085 // Ensure there's a token end at the current position, and that i points at it
1086 while (at < end) {
1087 var i_end = st[i];
1088 if (i_end > end)
1089 { st.splice(i, 1, end, st[i+1], i_end); }
1090 i += 2;
1091 at = Math.min(end, i_end);
1092 }
1093 if (!style) { return }
1094 if (overlay.opaque) {
1095 st.splice(start, i - start, end, "overlay " + style);
1096 i = start + 2;
1097 } else {
1098 for (; start < i; start += 2) {
1099 var cur = st[start+1];
1100 st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
1101 }
1102 }
1103 }, lineClasses);
1104 context.state = state;
1105 context.baseTokens = null;
1106 context.baseTokenPos = 1;
1107 };
1108
1109 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1110
1111 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1112 }
1113
1114 function getLineStyles(cm, line, updateFrontier) {
1115 if (!line.styles || line.styles[0] != cm.state.modeGen) {
1116 var context = getContextBefore(cm, lineNo(line));
1117 var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
1118 var result = highlightLine(cm, line, context);
1119 if (resetState) { context.state = resetState; }
1120 line.stateAfter = context.save(!resetState);
1121 line.styles = result.styles;
1122 if (result.classes) { line.styleClasses = result.classes; }
1123 else if (line.styleClasses) { line.styleClasses = null; }
1124 if (updateFrontier === cm.doc.highlightFrontier)
1125 { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
1126 }
1127 return line.styles
1128 }
1129
1130 function getContextBefore(cm, n, precise) {
1131 var doc = cm.doc, display = cm.display;
1132 if (!doc.mode.startState) { return new Context(doc, true, n) }
1133 var start = findStartLine(cm, n, precise);
1134 var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
1135 var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
1136
1137 doc.iter(start, n, function (line) {
1138 processLine(cm, line.text, context);
1139 var pos = context.line;
1140 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
1141 context.nextLine();
1142 });
1143 if (precise) { doc.modeFrontier = context.line; }
1144 return context
1145 }
1146
1147 // Lightweight form of highlight -- proceed over this line and
1148 // update state, but don't save a style array. Used for lines that
1149 // aren't currently visible.
1150 function processLine(cm, text, context, startAt) {
1151 var mode = cm.doc.mode;
1152 var stream = new StringStream(text, cm.options.tabSize, context);
1153 stream.start = stream.pos = startAt || 0;
1154 if (text == "") { callBlankLine(mode, context.state); }
1155 while (!stream.eol()) {
1156 readToken(mode, stream, context.state);
1157 stream.start = stream.pos;
1158 }
1159 }
1160
1161 function callBlankLine(mode, state) {
1162 if (mode.blankLine) { return mode.blankLine(state) }
1163 if (!mode.innerMode) { return }
1164 var inner = innerMode(mode, state);
1165 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1166 }
1167
1168 function readToken(mode, stream, state, inner) {
1169 for (var i = 0; i < 10; i++) {
1170 if (inner) { inner[0] = innerMode(mode, state).mode; }
1171 var style = mode.token(stream, state);
1172 if (stream.pos > stream.start) { return style }
1173 }
1174 throw new Error("Mode " + mode.name + " failed to advance stream.")
1175 }
1176
1177 var Token = function(stream, type, state) {
1178 this.start = stream.start; this.end = stream.pos;
1179 this.string = stream.current();
1180 this.type = type || null;
1181 this.state = state;
1182 };
1183
1184 // Utility for getTokenAt and getLineTokens
1185 function takeToken(cm, pos, precise, asArray) {
1186 var doc = cm.doc, mode = doc.mode, style;
1187 pos = clipPos(doc, pos);
1188 var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
1189 var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
1190 if (asArray) { tokens = []; }
1191 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1192 stream.start = stream.pos;
1193 style = readToken(mode, stream, context.state);
1194 if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
1195 }
1196 return asArray ? tokens : new Token(stream, style, context.state)
1197 }
1198
1199 function extractLineClasses(type, output) {
1200 if (type) { for (;;) {
1201 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
1202 if (!lineClass) { break }
1203 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
1204 var prop = lineClass[1] ? "bgClass" : "textClass";
1205 if (output[prop] == null)
1206 { output[prop] = lineClass[2]; }
1207 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1208 { output[prop] += " " + lineClass[2]; }
1209 } }
1210 return type
1211 }
1212
1213 // Run the given mode's parser over a line, calling f for each token.
1214 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1215 var flattenSpans = mode.flattenSpans;
1216 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
1217 var curStart = 0, curStyle = null;
1218 var stream = new StringStream(text, cm.options.tabSize, context), style;
1219 var inner = cm.options.addModeClass && [null];
1220 if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
1221 while (!stream.eol()) {
1222 if (stream.pos > cm.options.maxHighlightLength) {
1223 flattenSpans = false;
1224 if (forceToEnd) { processLine(cm, text, context, stream.pos); }
1225 stream.pos = text.length;
1226 style = null;
1227 } else {
1228 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
1229 }
1230 if (inner) {
1231 var mName = inner[0].name;
1232 if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
1233 }
1234 if (!flattenSpans || curStyle != style) {
1235 while (curStart < stream.start) {
1236 curStart = Math.min(stream.start, curStart + 5000);
1237 f(curStart, curStyle);
1238 }
1239 curStyle = style;
1240 }
1241 stream.start = stream.pos;
1242 }
1243 while (curStart < stream.pos) {
1244 // Webkit seems to refuse to render text nodes longer than 57444
1245 // characters, and returns inaccurate measurements in nodes
1246 // starting around 5000 chars.
1247 var pos = Math.min(stream.pos, curStart + 5000);
1248 f(pos, curStyle);
1249 curStart = pos;
1250 }
1251 }
1252
1253 // Finds the line to start with when starting a parse. Tries to
1254 // find a line with a stateAfter, so that it can start with a
1255 // valid state. If that fails, it returns the line with the
1256 // smallest indentation, which tends to need the least context to
1257 // parse correctly.
1258 function findStartLine(cm, n, precise) {
1259 var minindent, minline, doc = cm.doc;
1260 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1261 for (var search = n; search > lim; --search) {
1262 if (search <= doc.first) { return doc.first }
1263 var line = getLine(doc, search - 1), after = line.stateAfter;
1264 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1265 { return search }
1266 var indented = countColumn(line.text, null, cm.options.tabSize);
1267 if (minline == null || minindent > indented) {
1268 minline = search - 1;
1269 minindent = indented;
1270 }
1271 }
1272 return minline
1273 }
1274
1275 function retreatFrontier(doc, n) {
1276 doc.modeFrontier = Math.min(doc.modeFrontier, n);
1277 if (doc.highlightFrontier < n - 10) { return }
1278 var start = doc.first;
1279 for (var line = n - 1; line > start; line--) {
1280 var saved = getLine(doc, line).stateAfter;
1281 // change is on 3
1282 // state on line 1 looked ahead 2 -- so saw 3
1283 // test 1 + 2 < 3 should cover this
1284 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1285 start = line + 1;
1286 break
1287 }
1288 }
1289 doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
1290 }
1291
53 1292 // Optimize some code when these features are not used.
54 1293 var sawReadOnlySpans = false, sawCollapsedSpans = false;
55 1294
56 // EDITOR CONSTRUCTOR
57
58 // A CodeMirror instance represents an editor. This is the object
59 // that user code is usually dealing with.
60
61 function CodeMirror(place, options) {
62 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
63
64 this.options = options = options ? copyObj(options) : {};
65 // Determine effective options based on given values and defaults.
66 copyObj(defaults, options, false);
67 setGuttersForLineNumbers(options);
68
69 var doc = options.value;
70 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
71 this.doc = doc;
72
73 var input = new CodeMirror.inputStyles[options.inputStyle](this);
74 var display = this.display = new Display(place, doc, input);
75 display.wrapper.CodeMirror = this;
76 updateGutters(this);
77 themeChanged(this);
78 if (options.lineWrapping)
79 this.display.wrapper.className += " CodeMirror-wrap";
80 if (options.autofocus && !mobile) display.input.focus();
81 initScrollbars(this);
82
83 this.state = {
84 keyMaps: [], // stores maps added by addKeyMap
85 overlays: [], // highlighting overlays, as added by addOverlay
86 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
87 overwrite: false,
88 delayingBlurEvent: false,
89 focused: false,
90 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
91 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
92 selectingText: false,
93 draggingText: false,
94 highlight: new Delayed(), // stores highlight worker timeout
95 keySeq: null, // Unfinished key sequence
96 specialChars: null
1295 function seeReadOnlySpans() {
1296 sawReadOnlySpans = true;
1297 }
1298
1299 function seeCollapsedSpans() {
1300 sawCollapsedSpans = true;
1301 }
1302
1303 // TEXTMARKER SPANS
1304
1305 function MarkedSpan(marker, from, to) {
1306 this.marker = marker;
1307 this.from = from; this.to = to;
1308 }
1309
1310 // Search an array of spans for a span matching the given marker.
1311 function getMarkedSpanFor(spans, marker) {
1312 if (spans) { for (var i = 0; i < spans.length; ++i) {
1313 var span = spans[i];
1314 if (span.marker == marker) { return span }
1315 } }
1316 }
1317 // Remove a span from an array, returning undefined if no spans are
1318 // left (we don't store arrays for lines without spans).
1319 function removeMarkedSpan(spans, span) {
1320 var r;
1321 for (var i = 0; i < spans.length; ++i)
1322 { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
1323 return r
1324 }
1325 // Add a span to a line.
1326 function addMarkedSpan(line, span) {
1327 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1328 span.marker.attachLine(line);
1329 }
1330
1331 // Used for the algorithm that adjusts markers for a change in the
1332 // document. These functions cut an array of spans at a given
1333 // character position, returning an array of remaining chunks (or
1334 // undefined if nothing remains).
1335 function markedSpansBefore(old, startCh, isInsert) {
1336 var nw;
1337 if (old) { for (var i = 0; i < old.length; ++i) {
1338 var span = old[i], marker = span.marker;
1339 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1340 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
1341 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
1342 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
1343 }
1344 } }
1345 return nw
1346 }
1347 function markedSpansAfter(old, endCh, isInsert) {
1348 var nw;
1349 if (old) { for (var i = 0; i < old.length; ++i) {
1350 var span = old[i], marker = span.marker;
1351 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1352 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
1353 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
1354 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
1355 span.to == null ? null : span.to - endCh));
1356 }
1357 } }
1358 return nw
1359 }
1360
1361 // Given a change object, compute the new set of marker spans that
1362 // cover the line in which the change took place. Removes spans
1363 // entirely within the change, reconnects spans belonging to the
1364 // same marker that appear on both sides of the change, and cuts off
1365 // spans partially within the change. Returns an array of span
1366 // arrays with one element for each line in (after) the change.
1367 function stretchSpansOverChange(doc, change) {
1368 if (change.full) { return null }
1369 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
1370 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
1371 if (!oldFirst && !oldLast) { return null }
1372
1373 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
1374 // Get the spans that 'stick out' on both sides
1375 var first = markedSpansBefore(oldFirst, startCh, isInsert);
1376 var last = markedSpansAfter(oldLast, endCh, isInsert);
1377
1378 // Next, merge those two ends
1379 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
1380 if (first) {
1381 // Fix up .to properties of first
1382 for (var i = 0; i < first.length; ++i) {
1383 var span = first[i];
1384 if (span.to == null) {
1385 var found = getMarkedSpanFor(last, span.marker);
1386 if (!found) { span.to = startCh; }
1387 else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
1388 }
1389 }
1390 }
1391 if (last) {
1392 // Fix up .from in last (or move them into first in case of sameLine)
1393 for (var i$1 = 0; i$1 < last.length; ++i$1) {
1394 var span$1 = last[i$1];
1395 if (span$1.to != null) { span$1.to += offset; }
1396 if (span$1.from == null) {
1397 var found$1 = getMarkedSpanFor(first, span$1.marker);
1398 if (!found$1) {
1399 span$1.from = offset;
1400 if (sameLine) { (first || (first = [])).push(span$1); }
1401 }
1402 } else {
1403 span$1.from += offset;
1404 if (sameLine) { (first || (first = [])).push(span$1); }
1405 }
1406 }
1407 }
1408 // Make sure we didn't create any zero-length spans
1409 if (first) { first = clearEmptySpans(first); }
1410 if (last && last != first) { last = clearEmptySpans(last); }
1411
1412 var newMarkers = [first];
1413 if (!sameLine) {
1414 // Fill gap with whole-line-spans
1415 var gap = change.text.length - 2, gapMarkers;
1416 if (gap > 0 && first)
1417 { for (var i$2 = 0; i$2 < first.length; ++i$2)
1418 { if (first[i$2].to == null)
1419 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
1420 for (var i$3 = 0; i$3 < gap; ++i$3)
1421 { newMarkers.push(gapMarkers); }
1422 newMarkers.push(last);
1423 }
1424 return newMarkers
1425 }
1426
1427 // Remove spans that are empty and don't have a clearWhenEmpty
1428 // option of false.
1429 function clearEmptySpans(spans) {
1430 for (var i = 0; i < spans.length; ++i) {
1431 var span = spans[i];
1432 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
1433 { spans.splice(i--, 1); }
1434 }
1435 if (!spans.length) { return null }
1436 return spans
1437 }
1438
1439 // Used to 'clip' out readOnly ranges when making a change.
1440 function removeReadOnlyRanges(doc, from, to) {
1441 var markers = null;
1442 doc.iter(from.line, to.line + 1, function (line) {
1443 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
1444 var mark = line.markedSpans[i].marker;
1445 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
1446 { (markers || (markers = [])).push(mark); }
1447 } }
1448 });
1449 if (!markers) { return null }
1450 var parts = [{from: from, to: to}];
1451 for (var i = 0; i < markers.length; ++i) {
1452 var mk = markers[i], m = mk.find(0);
1453 for (var j = 0; j < parts.length; ++j) {
1454 var p = parts[j];
1455 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
1456 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
1457 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
1458 { newParts.push({from: p.from, to: m.from}); }
1459 if (dto > 0 || !mk.inclusiveRight && !dto)
1460 { newParts.push({from: m.to, to: p.to}); }
1461 parts.splice.apply(parts, newParts);
1462 j += newParts.length - 3;
1463 }
1464 }
1465 return parts
1466 }
1467
1468 // Connect or disconnect spans from a line.
1469 function detachMarkedSpans(line) {
1470 var spans = line.markedSpans;
1471 if (!spans) { return }
1472 for (var i = 0; i < spans.length; ++i)
1473 { spans[i].marker.detachLine(line); }
1474 line.markedSpans = null;
1475 }
1476 function attachMarkedSpans(line, spans) {
1477 if (!spans) { return }
1478 for (var i = 0; i < spans.length; ++i)
1479 { spans[i].marker.attachLine(line); }
1480 line.markedSpans = spans;
1481 }
1482
1483 // Helpers used when computing which overlapping collapsed span
1484 // counts as the larger one.
1485 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
1486 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
1487
1488 // Returns a number indicating which of two overlapping collapsed
1489 // spans is larger (and thus includes the other). Falls back to
1490 // comparing ids when the spans cover exactly the same range.
1491 function compareCollapsedMarkers(a, b) {
1492 var lenDiff = a.lines.length - b.lines.length;
1493 if (lenDiff != 0) { return lenDiff }
1494 var aPos = a.find(), bPos = b.find();
1495 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
1496 if (fromCmp) { return -fromCmp }
1497 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
1498 if (toCmp) { return toCmp }
1499 return b.id - a.id
1500 }
1501
1502 // Find out whether a line ends or starts in a collapsed span. If
1503 // so, return the marker for that span.
1504 function collapsedSpanAtSide(line, start) {
1505 var sps = sawCollapsedSpans && line.markedSpans, found;
1506 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1507 sp = sps[i];
1508 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
1509 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
1510 { found = sp.marker; }
1511 } }
1512 return found
1513 }
1514 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
1515 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
1516
1517 function collapsedSpanAround(line, ch) {
1518 var sps = sawCollapsedSpans && line.markedSpans, found;
1519 if (sps) { for (var i = 0; i < sps.length; ++i) {
1520 var sp = sps[i];
1521 if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
1522 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
1523 } }
1524 return found
1525 }
1526
1527 // Test whether there exists a collapsed span that partially
1528 // overlaps (covers the start or end, but not both) of a new span.
1529 // Such overlap is not allowed.
1530 function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
1531 var line = getLine(doc, lineNo$$1);
1532 var sps = sawCollapsedSpans && line.markedSpans;
1533 if (sps) { for (var i = 0; i < sps.length; ++i) {
1534 var sp = sps[i];
1535 if (!sp.marker.collapsed) { continue }
1536 var found = sp.marker.find(0);
1537 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
1538 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
1539 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
1540 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
1541 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
1542 { return true }
1543 } }
1544 }
1545
1546 // A visual line is a line as drawn on the screen. Folding, for
1547 // example, can cause multiple logical lines to appear on the same
1548 // visual line. This finds the start of the visual line that the
1549 // given line is part of (usually that is the line itself).
1550 function visualLine(line) {
1551 var merged;
1552 while (merged = collapsedSpanAtStart(line))
1553 { line = merged.find(-1, true).line; }
1554 return line
1555 }
1556
1557 function visualLineEnd(line) {
1558 var merged;
1559 while (merged = collapsedSpanAtEnd(line))
1560 { line = merged.find(1, true).line; }
1561 return line
1562 }
1563
1564 // Returns an array of logical lines that continue the visual line
1565 // started by the argument, or undefined if there are no such lines.
1566 function visualLineContinued(line) {
1567 var merged, lines;
1568 while (merged = collapsedSpanAtEnd(line)) {
1569 line = merged.find(1, true).line
1570 ;(lines || (lines = [])).push(line);
1571 }
1572 return lines
1573 }
1574
1575 // Get the line number of the start of the visual line that the
1576 // given line number is part of.
1577 function visualLineNo(doc, lineN) {
1578 var line = getLine(doc, lineN), vis = visualLine(line);
1579 if (line == vis) { return lineN }
1580 return lineNo(vis)
1581 }
1582
1583 // Get the line number of the start of the next visual line after
1584 // the given line.
1585 function visualLineEndNo(doc, lineN) {
1586 if (lineN > doc.lastLine()) { return lineN }
1587 var line = getLine(doc, lineN), merged;
1588 if (!lineIsHidden(doc, line)) { return lineN }
1589 while (merged = collapsedSpanAtEnd(line))
1590 { line = merged.find(1, true).line; }
1591 return lineNo(line) + 1
1592 }
1593
1594 // Compute whether a line is hidden. Lines count as hidden when they
1595 // are part of a visual line that starts with another line, or when
1596 // they are entirely covered by collapsed, non-widget span.
1597 function lineIsHidden(doc, line) {
1598 var sps = sawCollapsedSpans && line.markedSpans;
1599 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1600 sp = sps[i];
1601 if (!sp.marker.collapsed) { continue }
1602 if (sp.from == null) { return true }
1603 if (sp.marker.widgetNode) { continue }
1604 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
1605 { return true }
1606 } }
1607 }
1608 function lineIsHiddenInner(doc, line, span) {
1609 if (span.to == null) {
1610 var end = span.marker.find(1, true);
1611 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
1612 }
1613 if (span.marker.inclusiveRight && span.to == line.text.length)
1614 { return true }
1615 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
1616 sp = line.markedSpans[i];
1617 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
1618 (sp.to == null || sp.to != span.from) &&
1619 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
1620 lineIsHiddenInner(doc, line, sp)) { return true }
1621 }
1622 }
1623
1624 // Find the height above the given line.
1625 function heightAtLine(lineObj) {
1626 lineObj = visualLine(lineObj);
1627
1628 var h = 0, chunk = lineObj.parent;
1629 for (var i = 0; i < chunk.lines.length; ++i) {
1630 var line = chunk.lines[i];
1631 if (line == lineObj) { break }
1632 else { h += line.height; }
1633 }
1634 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
1635 for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
1636 var cur = p.children[i$1];
1637 if (cur == chunk) { break }
1638 else { h += cur.height; }
1639 }
1640 }
1641 return h
1642 }
1643
1644 // Compute the character length of a line, taking into account
1645 // collapsed ranges (see markText) that might hide parts, and join
1646 // other lines onto it.
1647 function lineLength(line) {
1648 if (line.height == 0) { return 0 }
1649 var len = line.text.length, merged, cur = line;
1650 while (merged = collapsedSpanAtStart(cur)) {
1651 var found = merged.find(0, true);
1652 cur = found.from.line;
1653 len += found.from.ch - found.to.ch;
1654 }
1655 cur = line;
1656 while (merged = collapsedSpanAtEnd(cur)) {
1657 var found$1 = merged.find(0, true);
1658 len -= cur.text.length - found$1.from.ch;
1659 cur = found$1.to.line;
1660 len += cur.text.length - found$1.to.ch;
1661 }
1662 return len
1663 }
1664
1665 // Find the longest line in the document.
1666 function findMaxLine(cm) {
1667 var d = cm.display, doc = cm.doc;
1668 d.maxLine = getLine(doc, doc.first);
1669 d.maxLineLength = lineLength(d.maxLine);
1670 d.maxLineChanged = true;
1671 doc.iter(function (line) {
1672 var len = lineLength(line);
1673 if (len > d.maxLineLength) {
1674 d.maxLineLength = len;
1675 d.maxLine = line;
1676 }
1677 });
1678 }
1679
1680 // LINE DATA STRUCTURE
1681
1682 // Line objects. These hold state related to a line, including
1683 // highlighting info (the styles array).
1684 var Line = function(text, markedSpans, estimateHeight) {
1685 this.text = text;
1686 attachMarkedSpans(this, markedSpans);
1687 this.height = estimateHeight ? estimateHeight(this) : 1;
1688 };
1689
1690 Line.prototype.lineNo = function () { return lineNo(this) };
1691 eventMixin(Line);
1692
1693 // Change the content (text, markers) of a line. Automatically
1694 // invalidates cached information and tries to re-estimate the
1695 // line's height.
1696 function updateLine(line, text, markedSpans, estimateHeight) {
1697 line.text = text;
1698 if (line.stateAfter) { line.stateAfter = null; }
1699 if (line.styles) { line.styles = null; }
1700 if (line.order != null) { line.order = null; }
1701 detachMarkedSpans(line);
1702 attachMarkedSpans(line, markedSpans);
1703 var estHeight = estimateHeight ? estimateHeight(line) : 1;
1704 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
1705 }
1706
1707 // Detach a line from the document tree and its markers.
1708 function cleanUpLine(line) {
1709 line.parent = null;
1710 detachMarkedSpans(line);
1711 }
1712
1713 // Convert a style as returned by a mode (either null, or a string
1714 // containing one or more styles) to a CSS style. This is cached,
1715 // and also looks for line-wide styles.
1716 var styleToClassCache = {}, styleToClassCacheWithMode = {};
1717 function interpretTokenStyle(style, options) {
1718 if (!style || /^\s*$/.test(style)) { return null }
1719 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
1720 return cache[style] ||
1721 (cache[style] = style.replace(/\S+/g, "cm-$&"))
1722 }
1723
1724 // Render the DOM representation of the text of a line. Also builds
1725 // up a 'line map', which points at the DOM nodes that represent
1726 // specific stretches of text, and is used by the measuring code.
1727 // The returned object contains the DOM node, this map, and
1728 // information about line-wide styles that were set by the mode.
1729 function buildLineContent(cm, lineView) {
1730 // The padding-right forces the element to have a 'border', which
1731 // is needed on Webkit to be able to get line-level bounding
1732 // rectangles for it (in measureChar).
1733 var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
1734 var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1735 col: 0, pos: 0, cm: cm,
1736 trailingSpace: false,
1737 splitSpaces: cm.getOption("lineWrapping")};
1738 lineView.measure = {};
1739
1740 // Iterate over the logical lines that make up this visual line.
1741 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1742 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
1743 builder.pos = 0;
1744 builder.addToken = buildToken;
1745 // Optionally wire in some hacks into the token-rendering
1746 // algorithm, to deal with browser quirks.
1747 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1748 { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
1749 builder.map = [];
1750 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
1751 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
1752 if (line.styleClasses) {
1753 if (line.styleClasses.bgClass)
1754 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
1755 if (line.styleClasses.textClass)
1756 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
1757 }
1758
1759 // Ensure at least a single node is present, for measuring.
1760 if (builder.map.length == 0)
1761 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
1762
1763 // Store the map and a cache object for the current logical line
1764 if (i == 0) {
1765 lineView.measure.map = builder.map;
1766 lineView.measure.cache = {};
1767 } else {
1768 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1769 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
1770 }
1771 }
1772
1773 // See issue #2901
1774 if (webkit) {
1775 var last = builder.content.lastChild;
1776 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1777 { builder.content.className = "cm-tab-wrap-hack"; }
1778 }
1779
1780 signal(cm, "renderLine", cm, lineView.line, builder.pre);
1781 if (builder.pre.className)
1782 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
1783
1784 return builder
1785 }
1786
1787 function defaultSpecialCharPlaceholder(ch) {
1788 var token = elt("span", "\u2022", "cm-invalidchar");
1789 token.title = "\\u" + ch.charCodeAt(0).toString(16);
1790 token.setAttribute("aria-label", token.title);
1791 return token
1792 }
1793
1794 // Build up the DOM representation for a single token, and add it to
1795 // the line map. Takes care to render special characters separately.
1796 function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
1797 if (!text) { return }
1798 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
1799 var special = builder.cm.state.specialChars, mustWrap = false;
1800 var content;
1801 if (!special.test(text)) {
1802 builder.col += text.length;
1803 content = document.createTextNode(displayText);
1804 builder.map.push(builder.pos, builder.pos + text.length, content);
1805 if (ie && ie_version < 9) { mustWrap = true; }
1806 builder.pos += text.length;
1807 } else {
1808 content = document.createDocumentFragment();
1809 var pos = 0;
1810 while (true) {
1811 special.lastIndex = pos;
1812 var m = special.exec(text);
1813 var skipped = m ? m.index - pos : text.length - pos;
1814 if (skipped) {
1815 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
1816 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
1817 else { content.appendChild(txt); }
1818 builder.map.push(builder.pos, builder.pos + skipped, txt);
1819 builder.col += skipped;
1820 builder.pos += skipped;
1821 }
1822 if (!m) { break }
1823 pos += skipped + 1;
1824 var txt$1 = (void 0);
1825 if (m[0] == "\t") {
1826 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1827 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1828 txt$1.setAttribute("role", "presentation");
1829 txt$1.setAttribute("cm-text", "\t");
1830 builder.col += tabWidth;
1831 } else if (m[0] == "\r" || m[0] == "\n") {
1832 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
1833 txt$1.setAttribute("cm-text", m[0]);
1834 builder.col += 1;
1835 } else {
1836 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
1837 txt$1.setAttribute("cm-text", m[0]);
1838 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
1839 else { content.appendChild(txt$1); }
1840 builder.col += 1;
1841 }
1842 builder.map.push(builder.pos, builder.pos + 1, txt$1);
1843 builder.pos++;
1844 }
1845 }
1846 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
1847 if (style || startStyle || endStyle || mustWrap || css) {
1848 var fullStyle = style || "";
1849 if (startStyle) { fullStyle += startStyle; }
1850 if (endStyle) { fullStyle += endStyle; }
1851 var token = elt("span", [content], fullStyle, css);
1852 if (attributes) {
1853 for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
1854 { token.setAttribute(attr, attributes[attr]); } }
1855 }
1856 return builder.content.appendChild(token)
1857 }
1858 builder.content.appendChild(content);
1859 }
1860
1861 // Change some spaces to NBSP to prevent the browser from collapsing
1862 // trailing spaces at the end of a line when rendering text (issue #1362).
1863 function splitSpaces(text, trailingBefore) {
1864 if (text.length > 1 && !/ /.test(text)) { return text }
1865 var spaceBefore = trailingBefore, result = "";
1866 for (var i = 0; i < text.length; i++) {
1867 var ch = text.charAt(i);
1868 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1869 { ch = "\u00a0"; }
1870 result += ch;
1871 spaceBefore = ch == " ";
1872 }
1873 return result
1874 }
1875
1876 // Work around nonsense dimensions being reported for stretches of
1877 // right-to-left text.
1878 function buildTokenBadBidi(inner, order) {
1879 return function (builder, text, style, startStyle, endStyle, css, attributes) {
1880 style = style ? style + " cm-force-border" : "cm-force-border";
1881 var start = builder.pos, end = start + text.length;
1882 for (;;) {
1883 // Find the part that overlaps with the start of this text
1884 var part = (void 0);
1885 for (var i = 0; i < order.length; i++) {
1886 part = order[i];
1887 if (part.to > start && part.from <= start) { break }
1888 }
1889 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
1890 inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
1891 startStyle = null;
1892 text = text.slice(part.to - start);
1893 start = part.to;
1894 }
1895 }
1896 }
1897
1898 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1899 var widget = !ignoreWidget && marker.widgetNode;
1900 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
1901 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1902 if (!widget)
1903 { widget = builder.content.appendChild(document.createElement("span")); }
1904 widget.setAttribute("cm-marker", marker.id);
1905 }
1906 if (widget) {
1907 builder.cm.display.input.setUneditable(widget);
1908 builder.content.appendChild(widget);
1909 }
1910 builder.pos += size;
1911 builder.trailingSpace = false;
1912 }
1913
1914 // Outputs a number of spans to make up a line, taking highlighting
1915 // and marked text into account.
1916 function insertLineContent(line, builder, styles) {
1917 var spans = line.markedSpans, allText = line.text, at = 0;
1918 if (!spans) {
1919 for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1920 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
1921 return
1922 }
1923
1924 var len = allText.length, pos = 0, i = 1, text = "", style, css;
1925 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
1926 for (;;) {
1927 if (nextChange == pos) { // Update current marker set
1928 spanStyle = spanEndStyle = spanStartStyle = css = "";
1929 attributes = null;
1930 collapsed = null; nextChange = Infinity;
1931 var foundBookmarks = [], endStyles = (void 0);
1932 for (var j = 0; j < spans.length; ++j) {
1933 var sp = spans[j], m = sp.marker;
1934 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1935 foundBookmarks.push(m);
1936 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1937 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1938 nextChange = sp.to;
1939 spanEndStyle = "";
1940 }
1941 if (m.className) { spanStyle += " " + m.className; }
1942 if (m.css) { css = (css ? css + ";" : "") + m.css; }
1943 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
1944 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
1945 // support for the old title property
1946 // https://github.com/codemirror/CodeMirror/pull/5673
1947 if (m.title) { (attributes || (attributes = {})).title = m.title; }
1948 if (m.attributes) {
1949 for (var attr in m.attributes)
1950 { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
1951 }
1952 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1953 { collapsed = sp; }
1954 } else if (sp.from > pos && nextChange > sp.from) {
1955 nextChange = sp.from;
1956 }
1957 }
1958 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1959 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
1960
1961 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1962 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
1963 if (collapsed && (collapsed.from || 0) == pos) {
1964 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1965 collapsed.marker, collapsed.from == null);
1966 if (collapsed.to == null) { return }
1967 if (collapsed.to == pos) { collapsed = false; }
1968 }
1969 }
1970 if (pos >= len) { break }
1971
1972 var upto = Math.min(len, nextChange);
1973 while (true) {
1974 if (text) {
1975 var end = pos + text.length;
1976 if (!collapsed) {
1977 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
1978 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
1979 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
1980 }
1981 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1982 pos = end;
1983 spanStartStyle = "";
1984 }
1985 text = allText.slice(at, at = styles[i++]);
1986 style = interpretTokenStyle(styles[i++], builder.cm.options);
1987 }
1988 }
1989 }
1990
1991
1992 // These objects are used to represent the visible (currently drawn)
1993 // part of the document. A LineView may correspond to multiple
1994 // logical lines, if those are connected by collapsed ranges.
1995 function LineView(doc, line, lineN) {
1996 // The starting line
1997 this.line = line;
1998 // Continuing lines, if any
1999 this.rest = visualLineContinued(line);
2000 // Number of logical lines in this visual line
2001 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
2002 this.node = this.text = null;
2003 this.hidden = lineIsHidden(doc, line);
2004 }
2005
2006 // Create a range of LineView objects for the given lines.
2007 function buildViewArray(cm, from, to) {
2008 var array = [], nextPos;
2009 for (var pos = from; pos < to; pos = nextPos) {
2010 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
2011 nextPos = pos + view.size;
2012 array.push(view);
2013 }
2014 return array
2015 }
2016
2017 var operationGroup = null;
2018
2019 function pushOperation(op) {
2020 if (operationGroup) {
2021 operationGroup.ops.push(op);
2022 } else {
2023 op.ownsGroup = operationGroup = {
2024 ops: [op],
2025 delayedCallbacks: []
2026 };
2027 }
2028 }
2029
2030 function fireCallbacksForOps(group) {
2031 // Calls delayed callbacks and cursorActivity handlers until no
2032 // new ones appear
2033 var callbacks = group.delayedCallbacks, i = 0;
2034 do {
2035 for (; i < callbacks.length; i++)
2036 { callbacks[i].call(null); }
2037 for (var j = 0; j < group.ops.length; j++) {
2038 var op = group.ops[j];
2039 if (op.cursorActivityHandlers)
2040 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2041 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
2042 }
2043 } while (i < callbacks.length)
2044 }
2045
2046 function finishOperation(op, endCb) {
2047 var group = op.ownsGroup;
2048 if (!group) { return }
2049
2050 try { fireCallbacksForOps(group); }
2051 finally {
2052 operationGroup = null;
2053 endCb(group);
2054 }
2055 }
2056
2057 var orphanDelayedCallbacks = null;
2058
2059 // Often, we want to signal events at a point where we are in the
2060 // middle of some work, but don't want the handler to start calling
2061 // other methods on the editor, which might be in an inconsistent
2062 // state or simply not expect any other events to happen.
2063 // signalLater looks whether there are any handlers, and schedules
2064 // them to be executed when the last operation ends, or, if no
2065 // operation is active, when a timeout fires.
2066 function signalLater(emitter, type /*, values...*/) {
2067 var arr = getHandlers(emitter, type);
2068 if (!arr.length) { return }
2069 var args = Array.prototype.slice.call(arguments, 2), list;
2070 if (operationGroup) {
2071 list = operationGroup.delayedCallbacks;
2072 } else if (orphanDelayedCallbacks) {
2073 list = orphanDelayedCallbacks;
2074 } else {
2075 list = orphanDelayedCallbacks = [];
2076 setTimeout(fireOrphanDelayed, 0);
2077 }
2078 var loop = function ( i ) {
2079 list.push(function () { return arr[i].apply(null, args); });
97 2080 };
98 2081
99 var cm = this;
100
101 // Override magic textarea content restore that IE sometimes does
102 // on our hidden textarea on reload
103 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
104
105 registerEventHandlers(this);
106 ensureGlobalHandlers();
107
108 startOperation(this);
109 this.curOp.forceUpdate = true;
110 attachDoc(this, doc);
111
112 if ((options.autofocus && !mobile) || cm.hasFocus())
113 setTimeout(bind(onFocus, this), 20);
114 else
115 onBlur(this);
116
117 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
118 optionHandlers[opt](this, options[opt], Init);
119 maybeUpdateLineNumberWidth(this);
120 if (options.finishInit) options.finishInit(this);
121 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
122 endOperation(this);
123 // Suppress optimizelegibility in Webkit, since it breaks text
124 // measuring on line wrapping boundaries.
125 if (webkit && options.lineWrapping &&
126 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
127 display.lineDiv.style.textRendering = "auto";
128 }
129
130 // DISPLAY CONSTRUCTOR
2082 for (var i = 0; i < arr.length; ++i)
2083 loop( i );
2084 }
2085
2086 function fireOrphanDelayed() {
2087 var delayed = orphanDelayedCallbacks;
2088 orphanDelayedCallbacks = null;
2089 for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
2090 }
2091
2092 // When an aspect of a line changes, a string is added to
2093 // lineView.changes. This updates the relevant part of the line's
2094 // DOM structure.
2095 function updateLineForChanges(cm, lineView, lineN, dims) {
2096 for (var j = 0; j < lineView.changes.length; j++) {
2097 var type = lineView.changes[j];
2098 if (type == "text") { updateLineText(cm, lineView); }
2099 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
2100 else if (type == "class") { updateLineClasses(cm, lineView); }
2101 else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
2102 }
2103 lineView.changes = null;
2104 }
2105
2106 // Lines with gutter elements, widgets or a background class need to
2107 // be wrapped, and have the extra elements added to the wrapper div
2108 function ensureLineWrapped(lineView) {
2109 if (lineView.node == lineView.text) {
2110 lineView.node = elt("div", null, null, "position: relative");
2111 if (lineView.text.parentNode)
2112 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
2113 lineView.node.appendChild(lineView.text);
2114 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
2115 }
2116 return lineView.node
2117 }
2118
2119 function updateLineBackground(cm, lineView) {
2120 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
2121 if (cls) { cls += " CodeMirror-linebackground"; }
2122 if (lineView.background) {
2123 if (cls) { lineView.background.className = cls; }
2124 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
2125 } else if (cls) {
2126 var wrap = ensureLineWrapped(lineView);
2127 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
2128 cm.display.input.setUneditable(lineView.background);
2129 }
2130 }
2131
2132 // Wrapper around buildLineContent which will reuse the structure
2133 // in display.externalMeasured when possible.
2134 function getLineContent(cm, lineView) {
2135 var ext = cm.display.externalMeasured;
2136 if (ext && ext.line == lineView.line) {
2137 cm.display.externalMeasured = null;
2138 lineView.measure = ext.measure;
2139 return ext.built
2140 }
2141 return buildLineContent(cm, lineView)
2142 }
2143
2144 // Redraw the line's text. Interacts with the background and text
2145 // classes because the mode may output tokens that influence these
2146 // classes.
2147 function updateLineText(cm, lineView) {
2148 var cls = lineView.text.className;
2149 var built = getLineContent(cm, lineView);
2150 if (lineView.text == lineView.node) { lineView.node = built.pre; }
2151 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
2152 lineView.text = built.pre;
2153 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2154 lineView.bgClass = built.bgClass;
2155 lineView.textClass = built.textClass;
2156 updateLineClasses(cm, lineView);
2157 } else if (cls) {
2158 lineView.text.className = cls;
2159 }
2160 }
2161
2162 function updateLineClasses(cm, lineView) {
2163 updateLineBackground(cm, lineView);
2164 if (lineView.line.wrapClass)
2165 { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
2166 else if (lineView.node != lineView.text)
2167 { lineView.node.className = ""; }
2168 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
2169 lineView.text.className = textClass || "";
2170 }
2171
2172 function updateLineGutter(cm, lineView, lineN, dims) {
2173 if (lineView.gutter) {
2174 lineView.node.removeChild(lineView.gutter);
2175 lineView.gutter = null;
2176 }
2177 if (lineView.gutterBackground) {
2178 lineView.node.removeChild(lineView.gutterBackground);
2179 lineView.gutterBackground = null;
2180 }
2181 if (lineView.line.gutterClass) {
2182 var wrap = ensureLineWrapped(lineView);
2183 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2184 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
2185 cm.display.input.setUneditable(lineView.gutterBackground);
2186 wrap.insertBefore(lineView.gutterBackground, lineView.text);
2187 }
2188 var markers = lineView.line.gutterMarkers;
2189 if (cm.options.lineNumbers || markers) {
2190 var wrap$1 = ensureLineWrapped(lineView);
2191 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
2192 cm.display.input.setUneditable(gutterWrap);
2193 wrap$1.insertBefore(gutterWrap, lineView.text);
2194 if (lineView.line.gutterClass)
2195 { gutterWrap.className += " " + lineView.line.gutterClass; }
2196 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2197 { lineView.lineNumber = gutterWrap.appendChild(
2198 elt("div", lineNumberFor(cm.options, lineN),
2199 "CodeMirror-linenumber CodeMirror-gutter-elt",
2200 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
2201 if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
2202 var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
2203 if (found)
2204 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2205 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
2206 } }
2207 }
2208 }
2209
2210 function updateLineWidgets(cm, lineView, dims) {
2211 if (lineView.alignable) { lineView.alignable = null; }
2212 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2213 next = node.nextSibling;
2214 if (node.className == "CodeMirror-linewidget")
2215 { lineView.node.removeChild(node); }
2216 }
2217 insertLineWidgets(cm, lineView, dims);
2218 }
2219
2220 // Build a line's DOM representation from scratch
2221 function buildLineElement(cm, lineView, lineN, dims) {
2222 var built = getLineContent(cm, lineView);
2223 lineView.text = lineView.node = built.pre;
2224 if (built.bgClass) { lineView.bgClass = built.bgClass; }
2225 if (built.textClass) { lineView.textClass = built.textClass; }
2226
2227 updateLineClasses(cm, lineView);
2228 updateLineGutter(cm, lineView, lineN, dims);
2229 insertLineWidgets(cm, lineView, dims);
2230 return lineView.node
2231 }
2232
2233 // A lineView may contain multiple logical lines (when merged by
2234 // collapsed spans). The widgets for all of them need to be drawn.
2235 function insertLineWidgets(cm, lineView, dims) {
2236 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
2237 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2238 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
2239 }
2240
2241 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2242 if (!line.widgets) { return }
2243 var wrap = ensureLineWrapped(lineView);
2244 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2245 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
2246 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
2247 positionLineWidget(widget, node, lineView, dims);
2248 cm.display.input.setUneditable(node);
2249 if (allowAbove && widget.above)
2250 { wrap.insertBefore(node, lineView.gutter || lineView.text); }
2251 else
2252 { wrap.appendChild(node); }
2253 signalLater(widget, "redraw");
2254 }
2255 }
2256
2257 function positionLineWidget(widget, node, lineView, dims) {
2258 if (widget.noHScroll) {
2259 (lineView.alignable || (lineView.alignable = [])).push(node);
2260 var width = dims.wrapperWidth;
2261 node.style.left = dims.fixedPos + "px";
2262 if (!widget.coverGutter) {
2263 width -= dims.gutterTotalWidth;
2264 node.style.paddingLeft = dims.gutterTotalWidth + "px";
2265 }
2266 node.style.width = width + "px";
2267 }
2268 if (widget.coverGutter) {
2269 node.style.zIndex = 5;
2270 node.style.position = "relative";
2271 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
2272 }
2273 }
2274
2275 function widgetHeight(widget) {
2276 if (widget.height != null) { return widget.height }
2277 var cm = widget.doc.cm;
2278 if (!cm) { return 0 }
2279 if (!contains(document.body, widget.node)) {
2280 var parentStyle = "position: relative;";
2281 if (widget.coverGutter)
2282 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
2283 if (widget.noHScroll)
2284 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
2285 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
2286 }
2287 return widget.height = widget.node.parentNode.offsetHeight
2288 }
2289
2290 // Return true when the given mouse event happened in a widget
2291 function eventInWidget(display, e) {
2292 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2293 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2294 (n.parentNode == display.sizer && n != display.mover))
2295 { return true }
2296 }
2297 }
2298
2299 // POSITION MEASUREMENT
2300
2301 function paddingTop(display) {return display.lineSpace.offsetTop}
2302 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2303 function paddingH(display) {
2304 if (display.cachedPaddingH) { return display.cachedPaddingH }
2305 var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
2306 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2307 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2308 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
2309 return data
2310 }
2311
2312 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2313 function displayWidth(cm) {
2314 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2315 }
2316 function displayHeight(cm) {
2317 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2318 }
2319
2320 // Ensure the lineView.wrapping.heights array is populated. This is
2321 // an array of bottom offsets for the lines that make up a drawn
2322 // line. When lineWrapping is on, there might be more than one
2323 // height.
2324 function ensureLineHeights(cm, lineView, rect) {
2325 var wrapping = cm.options.lineWrapping;
2326 var curWidth = wrapping && displayWidth(cm);
2327 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2328 var heights = lineView.measure.heights = [];
2329 if (wrapping) {
2330 lineView.measure.width = curWidth;
2331 var rects = lineView.text.firstChild.getClientRects();
2332 for (var i = 0; i < rects.length - 1; i++) {
2333 var cur = rects[i], next = rects[i + 1];
2334 if (Math.abs(cur.bottom - next.bottom) > 2)
2335 { heights.push((cur.bottom + next.top) / 2 - rect.top); }
2336 }
2337 }
2338 heights.push(rect.bottom - rect.top);
2339 }
2340 }
2341
2342 // Find a line map (mapping character offsets to text nodes) and a
2343 // measurement cache for the given line number. (A line view might
2344 // contain multiple lines when collapsed ranges are present.)
2345 function mapFromLineView(lineView, line, lineN) {
2346 if (lineView.line == line)
2347 { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2348 for (var i = 0; i < lineView.rest.length; i++)
2349 { if (lineView.rest[i] == line)
2350 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2351 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2352 { if (lineNo(lineView.rest[i$1]) > lineN)
2353 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2354 }
2355
2356 // Render a line into the hidden node display.externalMeasured. Used
2357 // when measurement is needed for a line that's not in the viewport.
2358 function updateExternalMeasurement(cm, line) {
2359 line = visualLine(line);
2360 var lineN = lineNo(line);
2361 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2362 view.lineN = lineN;
2363 var built = view.built = buildLineContent(cm, view);
2364 view.text = built.pre;
2365 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2366 return view
2367 }
2368
2369 // Get a {top, bottom, left, right} box (in line-local coordinates)
2370 // for a given character.
2371 function measureChar(cm, line, ch, bias) {
2372 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2373 }
2374
2375 // Find a line view that corresponds to the given line number.
2376 function findViewForLine(cm, lineN) {
2377 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2378 { return cm.display.view[findViewIndex(cm, lineN)] }
2379 var ext = cm.display.externalMeasured;
2380 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2381 { return ext }
2382 }
2383
2384 // Measurement can be split in two steps, the set-up work that
2385 // applies to the whole line, and the measurement of the actual
2386 // character. Functions like coordsChar, that need to do a lot of
2387 // measurements in a row, can thus ensure that the set-up work is
2388 // only done once.
2389 function prepareMeasureForLine(cm, line) {
2390 var lineN = lineNo(line);
2391 var view = findViewForLine(cm, lineN);
2392 if (view && !view.text) {
2393 view = null;
2394 } else if (view && view.changes) {
2395 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2396 cm.curOp.forceUpdate = true;
2397 }
2398 if (!view)
2399 { view = updateExternalMeasurement(cm, line); }
2400
2401 var info = mapFromLineView(view, line, lineN);
2402 return {
2403 line: line, view: view, rect: null,
2404 map: info.map, cache: info.cache, before: info.before,
2405 hasHeights: false
2406 }
2407 }
2408
2409 // Given a prepared measurement object, measures the position of an
2410 // actual character (or fetches it from the cache).
2411 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2412 if (prepared.before) { ch = -1; }
2413 var key = ch + (bias || ""), found;
2414 if (prepared.cache.hasOwnProperty(key)) {
2415 found = prepared.cache[key];
2416 } else {
2417 if (!prepared.rect)
2418 { prepared.rect = prepared.view.text.getBoundingClientRect(); }
2419 if (!prepared.hasHeights) {
2420 ensureLineHeights(cm, prepared.view, prepared.rect);
2421 prepared.hasHeights = true;
2422 }
2423 found = measureCharInner(cm, prepared, ch, bias);
2424 if (!found.bogus) { prepared.cache[key] = found; }
2425 }
2426 return {left: found.left, right: found.right,
2427 top: varHeight ? found.rtop : found.top,
2428 bottom: varHeight ? found.rbottom : found.bottom}
2429 }
2430
2431 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2432
2433 function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2434 var node, start, end, collapse, mStart, mEnd;
2435 // First, search the line map for the text node corresponding to,
2436 // or closest to, the target character.
2437 for (var i = 0; i < map$$1.length; i += 3) {
2438 mStart = map$$1[i];
2439 mEnd = map$$1[i + 1];
2440 if (ch < mStart) {
2441 start = 0; end = 1;
2442 collapse = "left";
2443 } else if (ch < mEnd) {
2444 start = ch - mStart;
2445 end = start + 1;
2446 } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2447 end = mEnd - mStart;
2448 start = end - 1;
2449 if (ch >= mEnd) { collapse = "right"; }
2450 }
2451 if (start != null) {
2452 node = map$$1[i + 2];
2453 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2454 { collapse = bias; }
2455 if (bias == "left" && start == 0)
2456 { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2457 node = map$$1[(i -= 3) + 2];
2458 collapse = "left";
2459 } }
2460 if (bias == "right" && start == mEnd - mStart)
2461 { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2462 node = map$$1[(i += 3) + 2];
2463 collapse = "right";
2464 } }
2465 break
2466 }
2467 }
2468 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2469 }
2470
2471 function getUsefulRect(rects, bias) {
2472 var rect = nullRect;
2473 if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2474 if ((rect = rects[i]).left != rect.right) { break }
2475 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2476 if ((rect = rects[i$1]).left != rect.right) { break }
2477 } }
2478 return rect
2479 }
2480
2481 function measureCharInner(cm, prepared, ch, bias) {
2482 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2483 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2484
2485 var rect;
2486 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2487 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2488 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
2489 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
2490 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2491 { rect = node.parentNode.getBoundingClientRect(); }
2492 else
2493 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
2494 if (rect.left || rect.right || start == 0) { break }
2495 end = start;
2496 start = start - 1;
2497 collapse = "right";
2498 }
2499 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
2500 } else { // If it is a widget, simply get the box for the whole widget.
2501 if (start > 0) { collapse = bias = "right"; }
2502 var rects;
2503 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2504 { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
2505 else
2506 { rect = node.getBoundingClientRect(); }
2507 }
2508 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2509 var rSpan = node.parentNode.getClientRects()[0];
2510 if (rSpan)
2511 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
2512 else
2513 { rect = nullRect; }
2514 }
2515
2516 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2517 var mid = (rtop + rbot) / 2;
2518 var heights = prepared.view.measure.heights;
2519 var i = 0;
2520 for (; i < heights.length - 1; i++)
2521 { if (mid < heights[i]) { break } }
2522 var top = i ? heights[i - 1] : 0, bot = heights[i];
2523 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2524 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2525 top: top, bottom: bot};
2526 if (!rect.left && !rect.right) { result.bogus = true; }
2527 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2528
2529 return result
2530 }
2531
2532 // Work around problem with bounding client rects on ranges being
2533 // returned incorrectly when zoomed on IE10 and below.
2534 function maybeUpdateRectForZooming(measure, rect) {
2535 if (!window.screen || screen.logicalXDPI == null ||
2536 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2537 { return rect }
2538 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2539 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2540 return {left: rect.left * scaleX, right: rect.right * scaleX,
2541 top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2542 }
2543
2544 function clearLineMeasurementCacheFor(lineView) {
2545 if (lineView.measure) {
2546 lineView.measure.cache = {};
2547 lineView.measure.heights = null;
2548 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2549 { lineView.measure.caches[i] = {}; } }
2550 }
2551 }
2552
2553 function clearLineMeasurementCache(cm) {
2554 cm.display.externalMeasure = null;
2555 removeChildren(cm.display.lineMeasure);
2556 for (var i = 0; i < cm.display.view.length; i++)
2557 { clearLineMeasurementCacheFor(cm.display.view[i]); }
2558 }
2559
2560 function clearCaches(cm) {
2561 clearLineMeasurementCache(cm);
2562 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2563 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
2564 cm.display.lineNumChars = null;
2565 }
2566
2567 function pageScrollX() {
2568 // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2569 // which causes page_Offset and bounding client rects to use
2570 // different reference viewports and invalidate our calculations.
2571 if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2572 return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2573 }
2574 function pageScrollY() {
2575 if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2576 return window.pageYOffset || (document.documentElement || document.body).scrollTop
2577 }
2578
2579 function widgetTopHeight(lineObj) {
2580 var height = 0;
2581 if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2582 { height += widgetHeight(lineObj.widgets[i]); } } }
2583 return height
2584 }
2585
2586 // Converts a {top, bottom, left, right} box from line-local
2587 // coordinates into another coordinate system. Context may be one of
2588 // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2589 // or "page".
2590 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2591 if (!includeWidgets) {
2592 var height = widgetTopHeight(lineObj);
2593 rect.top += height; rect.bottom += height;
2594 }
2595 if (context == "line") { return rect }
2596 if (!context) { context = "local"; }
2597 var yOff = heightAtLine(lineObj);
2598 if (context == "local") { yOff += paddingTop(cm.display); }
2599 else { yOff -= cm.display.viewOffset; }
2600 if (context == "page" || context == "window") {
2601 var lOff = cm.display.lineSpace.getBoundingClientRect();
2602 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2603 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2604 rect.left += xOff; rect.right += xOff;
2605 }
2606 rect.top += yOff; rect.bottom += yOff;
2607 return rect
2608 }
2609
2610 // Coverts a box from "div" coords to another coordinate system.
2611 // Context may be "window", "page", "div", or "local"./null.
2612 function fromCoordSystem(cm, coords, context) {
2613 if (context == "div") { return coords }
2614 var left = coords.left, top = coords.top;
2615 // First move into "page" coordinate system
2616 if (context == "page") {
2617 left -= pageScrollX();
2618 top -= pageScrollY();
2619 } else if (context == "local" || !context) {
2620 var localBox = cm.display.sizer.getBoundingClientRect();
2621 left += localBox.left;
2622 top += localBox.top;
2623 }
2624
2625 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2626 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2627 }
2628
2629 function charCoords(cm, pos, context, lineObj, bias) {
2630 if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
2631 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2632 }
2633
2634 // Returns a box for a given cursor position, which may have an
2635 // 'other' property containing the position of the secondary cursor
2636 // on a bidi boundary.
2637 // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2638 // and after `char - 1` in writing order of `char - 1`
2639 // A cursor Pos(line, char, "after") is on the same visual line as `char`
2640 // and before `char` in writing order of `char`
2641 // Examples (upper-case letters are RTL, lower-case are LTR):
2642 // Pos(0, 1, ...)
2643 // before after
2644 // ab a|b a|b
2645 // aB a|B aB|
2646 // Ab |Ab A|b
2647 // AB B|A B|A
2648 // Every position after the last character on a line is considered to stick
2649 // to the last character on the line.
2650 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2651 lineObj = lineObj || getLine(cm.doc, pos.line);
2652 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2653 function get(ch, right) {
2654 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2655 if (right) { m.left = m.right; } else { m.right = m.left; }
2656 return intoCoordSystem(cm, lineObj, m, context)
2657 }
2658 var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
2659 if (ch >= lineObj.text.length) {
2660 ch = lineObj.text.length;
2661 sticky = "before";
2662 } else if (ch <= 0) {
2663 ch = 0;
2664 sticky = "after";
2665 }
2666 if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2667
2668 function getBidi(ch, partPos, invert) {
2669 var part = order[partPos], right = part.level == 1;
2670 return get(invert ? ch - 1 : ch, right != invert)
2671 }
2672 var partPos = getBidiPartAt(order, ch, sticky);
2673 var other = bidiOther;
2674 var val = getBidi(ch, partPos, sticky == "before");
2675 if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
2676 return val
2677 }
2678
2679 // Used to cheaply estimate the coordinates for a position. Used for
2680 // intermediate scroll updates.
2681 function estimateCoords(cm, pos) {
2682 var left = 0;
2683 pos = clipPos(cm.doc, pos);
2684 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
2685 var lineObj = getLine(cm.doc, pos.line);
2686 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2687 return {left: left, right: left, top: top, bottom: top + lineObj.height}
2688 }
2689
2690 // Positions returned by coordsChar contain some extra information.
2691 // xRel is the relative x position of the input coordinates compared
2692 // to the found position (so xRel > 0 means the coordinates are to
2693 // the right of the character position, for example). When outside
2694 // is true, that means the coordinates lie outside the line's
2695 // vertical range.
2696 function PosWithInfo(line, ch, sticky, outside, xRel) {
2697 var pos = Pos(line, ch, sticky);
2698 pos.xRel = xRel;
2699 if (outside) { pos.outside = outside; }
2700 return pos
2701 }
2702
2703 // Compute the character position closest to the given coordinates.
2704 // Input must be lineSpace-local ("div" coordinate system).
2705 function coordsChar(cm, x, y) {
2706 var doc = cm.doc;
2707 y += cm.display.viewOffset;
2708 if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
2709 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2710 if (lineN > last)
2711 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
2712 if (x < 0) { x = 0; }
2713
2714 var lineObj = getLine(doc, lineN);
2715 for (;;) {
2716 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2717 var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
2718 if (!collapsed) { return found }
2719 var rangeEnd = collapsed.find(1);
2720 if (rangeEnd.line == lineN) { return rangeEnd }
2721 lineObj = getLine(doc, lineN = rangeEnd.line);
2722 }
2723 }
2724
2725 function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2726 y -= widgetTopHeight(lineObj);
2727 var end = lineObj.text.length;
2728 var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
2729 end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
2730 return {begin: begin, end: end}
2731 }
2732
2733 function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2734 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2735 var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
2736 return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2737 }
2738
2739 // Returns true if the given side of a box is after the given
2740 // coordinates, in top-to-bottom, left-to-right order.
2741 function boxIsAfter(box, x, y, left) {
2742 return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2743 }
2744
2745 function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2746 // Move y into line-local coordinate space
2747 y -= heightAtLine(lineObj);
2748 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2749 // When directly calling `measureCharPrepared`, we have to adjust
2750 // for the widgets at this line.
2751 var widgetHeight$$1 = widgetTopHeight(lineObj);
2752 var begin = 0, end = lineObj.text.length, ltr = true;
2753
2754 var order = getOrder(lineObj, cm.doc.direction);
2755 // If the line isn't plain left-to-right text, first figure out
2756 // which bidi section the coordinates fall into.
2757 if (order) {
2758 var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2759 (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
2760 ltr = part.level != 1;
2761 // The awkward -1 offsets are needed because findFirst (called
2762 // on these below) will treat its first bound as inclusive,
2763 // second as exclusive, but we want to actually address the
2764 // characters in the part's range
2765 begin = ltr ? part.from : part.to - 1;
2766 end = ltr ? part.to : part.from - 1;
2767 }
2768
2769 // A binary search to find the first character whose bounding box
2770 // starts after the coordinates. If we run across any whose box wrap
2771 // the coordinates, store that.
2772 var chAround = null, boxAround = null;
2773 var ch = findFirst(function (ch) {
2774 var box = measureCharPrepared(cm, preparedMeasure, ch);
2775 box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
2776 if (!boxIsAfter(box, x, y, false)) { return false }
2777 if (box.top <= y && box.left <= x) {
2778 chAround = ch;
2779 boxAround = box;
2780 }
2781 return true
2782 }, begin, end);
2783
2784 var baseX, sticky, outside = false;
2785 // If a box around the coordinates was found, use that
2786 if (boxAround) {
2787 // Distinguish coordinates nearer to the left or right side of the box
2788 var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
2789 ch = chAround + (atStart ? 0 : 1);
2790 sticky = atStart ? "after" : "before";
2791 baseX = atLeft ? boxAround.left : boxAround.right;
2792 } else {
2793 // (Adjust for extended bound, if necessary.)
2794 if (!ltr && (ch == end || ch == begin)) { ch++; }
2795 // To determine which side to associate with, get the box to the
2796 // left of the character and compare it's vertical position to the
2797 // coordinates
2798 sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2799 (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
2800 "after" : "before";
2801 // Now get accurate coordinates for this place, in order to get a
2802 // base X position
2803 var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
2804 baseX = coords.left;
2805 outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
2806 }
2807
2808 ch = skipExtendingChars(lineObj.text, ch, 1);
2809 return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
2810 }
2811
2812 function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
2813 // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2814 // situation, we can take this ordering to correspond to the visual
2815 // ordering. This finds the first part whose end is after the given
2816 // coordinates.
2817 var index = findFirst(function (i) {
2818 var part = order[i], ltr = part.level != 1;
2819 return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
2820 "line", lineObj, preparedMeasure), x, y, true)
2821 }, 0, order.length - 1);
2822 var part = order[index];
2823 // If this isn't the first part, the part's start is also after
2824 // the coordinates, and the coordinates aren't on the same line as
2825 // that start, move one part back.
2826 if (index > 0) {
2827 var ltr = part.level != 1;
2828 var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
2829 "line", lineObj, preparedMeasure);
2830 if (boxIsAfter(start, x, y, true) && start.top > y)
2831 { part = order[index - 1]; }
2832 }
2833 return part
2834 }
2835
2836 function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2837 // In a wrapped line, rtl text on wrapping boundaries can do things
2838 // that don't correspond to the ordering in our `order` array at
2839 // all, so a binary search doesn't work, and we want to return a
2840 // part that only spans one line so that the binary search in
2841 // coordsCharInner is safe. As such, we first find the extent of the
2842 // wrapped line, and then do a flat search in which we discard any
2843 // spans that aren't on the line.
2844 var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2845 var begin = ref.begin;
2846 var end = ref.end;
2847 if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
2848 var part = null, closestDist = null;
2849 for (var i = 0; i < order.length; i++) {
2850 var p = order[i];
2851 if (p.from >= end || p.to <= begin) { continue }
2852 var ltr = p.level != 1;
2853 var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
2854 // Weigh against spans ending before this, so that they are only
2855 // picked if nothing ends after
2856 var dist = endX < x ? x - endX + 1e9 : endX - x;
2857 if (!part || closestDist > dist) {
2858 part = p;
2859 closestDist = dist;
2860 }
2861 }
2862 if (!part) { part = order[order.length - 1]; }
2863 // Clip the part to the wrapped line.
2864 if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
2865 if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
2866 return part
2867 }
2868
2869 var measureText;
2870 // Compute the default text height.
2871 function textHeight(display) {
2872 if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2873 if (measureText == null) {
2874 measureText = elt("pre", null, "CodeMirror-line-like");
2875 // Measure a bunch of lines, for browsers that compute
2876 // fractional heights.
2877 for (var i = 0; i < 49; ++i) {
2878 measureText.appendChild(document.createTextNode("x"));
2879 measureText.appendChild(elt("br"));
2880 }
2881 measureText.appendChild(document.createTextNode("x"));
2882 }
2883 removeChildrenAndAdd(display.measure, measureText);
2884 var height = measureText.offsetHeight / 50;
2885 if (height > 3) { display.cachedTextHeight = height; }
2886 removeChildren(display.measure);
2887 return height || 1
2888 }
2889
2890 // Compute the default character width.
2891 function charWidth(display) {
2892 if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2893 var anchor = elt("span", "xxxxxxxxxx");
2894 var pre = elt("pre", [anchor], "CodeMirror-line-like");
2895 removeChildrenAndAdd(display.measure, pre);
2896 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2897 if (width > 2) { display.cachedCharWidth = width; }
2898 return width || 10
2899 }
2900
2901 // Do a bulk-read of the DOM positions and sizes needed to draw the
2902 // view, so that we don't interleave reading and writing to the DOM.
2903 function getDimensions(cm) {
2904 var d = cm.display, left = {}, width = {};
2905 var gutterLeft = d.gutters.clientLeft;
2906 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2907 var id = cm.display.gutterSpecs[i].className;
2908 left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
2909 width[id] = n.clientWidth;
2910 }
2911 return {fixedPos: compensateForHScroll(d),
2912 gutterTotalWidth: d.gutters.offsetWidth,
2913 gutterLeft: left,
2914 gutterWidth: width,
2915 wrapperWidth: d.wrapper.clientWidth}
2916 }
2917
2918 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2919 // but using getBoundingClientRect to get a sub-pixel-accurate
2920 // result.
2921 function compensateForHScroll(display) {
2922 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2923 }
2924
2925 // Returns a function that estimates the height of a line, to use as
2926 // first approximation until the line becomes visible (and is thus
2927 // properly measurable).
2928 function estimateHeight(cm) {
2929 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
2930 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
2931 return function (line) {
2932 if (lineIsHidden(cm.doc, line)) { return 0 }
2933
2934 var widgetsHeight = 0;
2935 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2936 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
2937 } }
2938
2939 if (wrapping)
2940 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2941 else
2942 { return widgetsHeight + th }
2943 }
2944 }
2945
2946 function estimateLineHeights(cm) {
2947 var doc = cm.doc, est = estimateHeight(cm);
2948 doc.iter(function (line) {
2949 var estHeight = est(line);
2950 if (estHeight != line.height) { updateLineHeight(line, estHeight); }
2951 });
2952 }
2953
2954 // Given a mouse event, find the corresponding position. If liberal
2955 // is false, it checks whether a gutter or scrollbar was clicked,
2956 // and returns null if it was. forRect is used by rectangular
2957 // selections, and tries to estimate a character position even for
2958 // coordinates beyond the right of the text.
2959 function posFromMouse(cm, e, liberal, forRect) {
2960 var display = cm.display;
2961 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2962
2963 var x, y, space = display.lineSpace.getBoundingClientRect();
2964 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2965 try { x = e.clientX - space.left; y = e.clientY - space.top; }
2966 catch (e) { return null }
2967 var coords = coordsChar(cm, x, y), line;
2968 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2969 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
2970 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
2971 }
2972 return coords
2973 }
2974
2975 // Find the view element corresponding to a given line. Return null
2976 // when the line isn't visible.
2977 function findViewIndex(cm, n) {
2978 if (n >= cm.display.viewTo) { return null }
2979 n -= cm.display.viewFrom;
2980 if (n < 0) { return null }
2981 var view = cm.display.view;
2982 for (var i = 0; i < view.length; i++) {
2983 n -= view[i].size;
2984 if (n < 0) { return i }
2985 }
2986 }
2987
2988 // Updates the display.view data structure for a given change to the
2989 // document. From and to are in pre-change coordinates. Lendiff is
2990 // the amount of lines added or subtracted by the change. This is
2991 // used for changes that span multiple lines, or change the way
2992 // lines are divided into visual lines. regLineChange (below)
2993 // registers single-line changes.
2994 function regChange(cm, from, to, lendiff) {
2995 if (from == null) { from = cm.doc.first; }
2996 if (to == null) { to = cm.doc.first + cm.doc.size; }
2997 if (!lendiff) { lendiff = 0; }
2998
2999 var display = cm.display;
3000 if (lendiff && to < display.viewTo &&
3001 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3002 { display.updateLineNumbers = from; }
3003
3004 cm.curOp.viewChanged = true;
3005
3006 if (from >= display.viewTo) { // Change after
3007 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3008 { resetView(cm); }
3009 } else if (to <= display.viewFrom) { // Change before
3010 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3011 resetView(cm);
3012 } else {
3013 display.viewFrom += lendiff;
3014 display.viewTo += lendiff;
3015 }
3016 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3017 resetView(cm);
3018 } else if (from <= display.viewFrom) { // Top overlap
3019 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3020 if (cut) {
3021 display.view = display.view.slice(cut.index);
3022 display.viewFrom = cut.lineN;
3023 display.viewTo += lendiff;
3024 } else {
3025 resetView(cm);
3026 }
3027 } else if (to >= display.viewTo) { // Bottom overlap
3028 var cut$1 = viewCuttingPoint(cm, from, from, -1);
3029 if (cut$1) {
3030 display.view = display.view.slice(0, cut$1.index);
3031 display.viewTo = cut$1.lineN;
3032 } else {
3033 resetView(cm);
3034 }
3035 } else { // Gap in the middle
3036 var cutTop = viewCuttingPoint(cm, from, from, -1);
3037 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3038 if (cutTop && cutBot) {
3039 display.view = display.view.slice(0, cutTop.index)
3040 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3041 .concat(display.view.slice(cutBot.index));
3042 display.viewTo += lendiff;
3043 } else {
3044 resetView(cm);
3045 }
3046 }
3047
3048 var ext = display.externalMeasured;
3049 if (ext) {
3050 if (to < ext.lineN)
3051 { ext.lineN += lendiff; }
3052 else if (from < ext.lineN + ext.size)
3053 { display.externalMeasured = null; }
3054 }
3055 }
3056
3057 // Register a change to a single line. Type must be one of "text",
3058 // "gutter", "class", "widget"
3059 function regLineChange(cm, line, type) {
3060 cm.curOp.viewChanged = true;
3061 var display = cm.display, ext = cm.display.externalMeasured;
3062 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3063 { display.externalMeasured = null; }
3064
3065 if (line < display.viewFrom || line >= display.viewTo) { return }
3066 var lineView = display.view[findViewIndex(cm, line)];
3067 if (lineView.node == null) { return }
3068 var arr = lineView.changes || (lineView.changes = []);
3069 if (indexOf(arr, type) == -1) { arr.push(type); }
3070 }
3071
3072 // Clear the view.
3073 function resetView(cm) {
3074 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3075 cm.display.view = [];
3076 cm.display.viewOffset = 0;
3077 }
3078
3079 function viewCuttingPoint(cm, oldN, newN, dir) {
3080 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3081 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3082 { return {index: index, lineN: newN} }
3083 var n = cm.display.viewFrom;
3084 for (var i = 0; i < index; i++)
3085 { n += view[i].size; }
3086 if (n != oldN) {
3087 if (dir > 0) {
3088 if (index == view.length - 1) { return null }
3089 diff = (n + view[index].size) - oldN;
3090 index++;
3091 } else {
3092 diff = n - oldN;
3093 }
3094 oldN += diff; newN += diff;
3095 }
3096 while (visualLineNo(cm.doc, newN) != newN) {
3097 if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3098 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3099 index += dir;
3100 }
3101 return {index: index, lineN: newN}
3102 }
3103
3104 // Force the view to cover a given range, adding empty view element
3105 // or clipping off existing ones as needed.
3106 function adjustView(cm, from, to) {
3107 var display = cm.display, view = display.view;
3108 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3109 display.view = buildViewArray(cm, from, to);
3110 display.viewFrom = from;
3111 } else {
3112 if (display.viewFrom > from)
3113 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
3114 else if (display.viewFrom < from)
3115 { display.view = display.view.slice(findViewIndex(cm, from)); }
3116 display.viewFrom = from;
3117 if (display.viewTo < to)
3118 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
3119 else if (display.viewTo > to)
3120 { display.view = display.view.slice(0, findViewIndex(cm, to)); }
3121 }
3122 display.viewTo = to;
3123 }
3124
3125 // Count the number of lines in the view whose DOM representation is
3126 // out of date (or nonexistent).
3127 function countDirtyView(cm) {
3128 var view = cm.display.view, dirty = 0;
3129 for (var i = 0; i < view.length; i++) {
3130 var lineView = view[i];
3131 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
3132 }
3133 return dirty
3134 }
3135
3136 function updateSelection(cm) {
3137 cm.display.input.showSelection(cm.display.input.prepareSelection());
3138 }
3139
3140 function prepareSelection(cm, primary) {
3141 if ( primary === void 0 ) primary = true;
3142
3143 var doc = cm.doc, result = {};
3144 var curFragment = result.cursors = document.createDocumentFragment();
3145 var selFragment = result.selection = document.createDocumentFragment();
3146
3147 for (var i = 0; i < doc.sel.ranges.length; i++) {
3148 if (!primary && i == doc.sel.primIndex) { continue }
3149 var range$$1 = doc.sel.ranges[i];
3150 if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
3151 var collapsed = range$$1.empty();
3152 if (collapsed || cm.options.showCursorWhenSelecting)
3153 { drawSelectionCursor(cm, range$$1.head, curFragment); }
3154 if (!collapsed)
3155 { drawSelectionRange(cm, range$$1, selFragment); }
3156 }
3157 return result
3158 }
3159
3160 // Draws a cursor for the given range
3161 function drawSelectionCursor(cm, head, output) {
3162 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
3163
3164 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
3165 cursor.style.left = pos.left + "px";
3166 cursor.style.top = pos.top + "px";
3167 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
3168
3169 if (pos.other) {
3170 // Secondary cursor, shown when on a 'jump' in bi-directional text
3171 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
3172 otherCursor.style.display = "";
3173 otherCursor.style.left = pos.other.left + "px";
3174 otherCursor.style.top = pos.other.top + "px";
3175 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
3176 }
3177 }
3178
3179 function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3180
3181 // Draws the given range as a highlighted selection
3182 function drawSelectionRange(cm, range$$1, output) {
3183 var display = cm.display, doc = cm.doc;
3184 var fragment = document.createDocumentFragment();
3185 var padding = paddingH(cm.display), leftSide = padding.left;
3186 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
3187 var docLTR = doc.direction == "ltr";
3188
3189 function add(left, top, width, bottom) {
3190 if (top < 0) { top = 0; }
3191 top = Math.round(top);
3192 bottom = Math.round(bottom);
3193 fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")));
3194 }
3195
3196 function drawForLine(line, fromArg, toArg) {
3197 var lineObj = getLine(doc, line);
3198 var lineLen = lineObj.text.length;
3199 var start, end;
3200 function coords(ch, bias) {
3201 return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3202 }
3203
3204 function wrapX(pos, dir, side) {
3205 var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
3206 var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
3207 var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
3208 return coords(ch, prop)[prop]
3209 }
3210
3211 var order = getOrder(lineObj, doc.direction);
3212 iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3213 var ltr = dir == "ltr";
3214 var fromPos = coords(from, ltr ? "left" : "right");
3215 var toPos = coords(to - 1, ltr ? "right" : "left");
3216
3217 var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
3218 var first = i == 0, last = !order || i == order.length - 1;
3219 if (toPos.top - fromPos.top <= 3) { // Single line
3220 var openLeft = (docLTR ? openStart : openEnd) && first;
3221 var openRight = (docLTR ? openEnd : openStart) && last;
3222 var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
3223 var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
3224 add(left, fromPos.top, right - left, fromPos.bottom);
3225 } else { // Multiple lines
3226 var topLeft, topRight, botLeft, botRight;
3227 if (ltr) {
3228 topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
3229 topRight = docLTR ? rightSide : wrapX(from, dir, "before");
3230 botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
3231 botRight = docLTR && openEnd && last ? rightSide : toPos.right;
3232 } else {
3233 topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
3234 topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
3235 botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
3236 botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
3237 }
3238 add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
3239 if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
3240 add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
3241 }
3242
3243 if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
3244 if (cmpCoords(toPos, start) < 0) { start = toPos; }
3245 if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
3246 if (cmpCoords(toPos, end) < 0) { end = toPos; }
3247 });
3248 return {start: start, end: end}
3249 }
3250
3251 var sFrom = range$$1.from(), sTo = range$$1.to();
3252 if (sFrom.line == sTo.line) {
3253 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
3254 } else {
3255 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
3256 var singleVLine = visualLine(fromLine) == visualLine(toLine);
3257 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
3258 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
3259 if (singleVLine) {
3260 if (leftEnd.top < rightStart.top - 2) {
3261 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
3262 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
3263 } else {
3264 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
3265 }
3266 }
3267 if (leftEnd.bottom < rightStart.top)
3268 { add(leftSide, leftEnd.bottom, null, rightStart.top); }
3269 }
3270
3271 output.appendChild(fragment);
3272 }
3273
3274 // Cursor-blinking
3275 function restartBlink(cm) {
3276 if (!cm.state.focused) { return }
3277 var display = cm.display;
3278 clearInterval(display.blinker);
3279 var on = true;
3280 display.cursorDiv.style.visibility = "";
3281 if (cm.options.cursorBlinkRate > 0)
3282 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3283 cm.options.cursorBlinkRate); }
3284 else if (cm.options.cursorBlinkRate < 0)
3285 { display.cursorDiv.style.visibility = "hidden"; }
3286 }
3287
3288 function ensureFocus(cm) {
3289 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
3290 }
3291
3292 function delayBlurEvent(cm) {
3293 cm.state.delayingBlurEvent = true;
3294 setTimeout(function () { if (cm.state.delayingBlurEvent) {
3295 cm.state.delayingBlurEvent = false;
3296 onBlur(cm);
3297 } }, 100);
3298 }
3299
3300 function onFocus(cm, e) {
3301 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
3302
3303 if (cm.options.readOnly == "nocursor") { return }
3304 if (!cm.state.focused) {
3305 signal(cm, "focus", cm, e);
3306 cm.state.focused = true;
3307 addClass(cm.display.wrapper, "CodeMirror-focused");
3308 // This test prevents this from firing when a context
3309 // menu is closed (since the input reset would kill the
3310 // select-all detection hack)
3311 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3312 cm.display.input.reset();
3313 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
3314 }
3315 cm.display.input.receivedFocus();
3316 }
3317 restartBlink(cm);
3318 }
3319 function onBlur(cm, e) {
3320 if (cm.state.delayingBlurEvent) { return }
3321
3322 if (cm.state.focused) {
3323 signal(cm, "blur", cm, e);
3324 cm.state.focused = false;
3325 rmClass(cm.display.wrapper, "CodeMirror-focused");
3326 }
3327 clearInterval(cm.display.blinker);
3328 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
3329 }
3330
3331 // Read the actual heights of the rendered lines, and update their
3332 // stored heights to match.
3333 function updateHeightsInViewport(cm) {
3334 var display = cm.display;
3335 var prevBottom = display.lineDiv.offsetTop;
3336 for (var i = 0; i < display.view.length; i++) {
3337 var cur = display.view[i], wrapping = cm.options.lineWrapping;
3338 var height = (void 0), width = 0;
3339 if (cur.hidden) { continue }
3340 if (ie && ie_version < 8) {
3341 var bot = cur.node.offsetTop + cur.node.offsetHeight;
3342 height = bot - prevBottom;
3343 prevBottom = bot;
3344 } else {
3345 var box = cur.node.getBoundingClientRect();
3346 height = box.bottom - box.top;
3347 // Check that lines don't extend past the right of the current
3348 // editor width
3349 if (!wrapping && cur.text.firstChild)
3350 { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
3351 }
3352 var diff = cur.line.height - height;
3353 if (diff > .005 || diff < -.005) {
3354 updateLineHeight(cur.line, height);
3355 updateWidgetHeight(cur.line);
3356 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3357 { updateWidgetHeight(cur.rest[j]); } }
3358 }
3359 if (width > cm.display.sizerWidth) {
3360 var chWidth = Math.ceil(width / charWidth(cm.display));
3361 if (chWidth > cm.display.maxLineLength) {
3362 cm.display.maxLineLength = chWidth;
3363 cm.display.maxLine = cur.line;
3364 cm.display.maxLineChanged = true;
3365 }
3366 }
3367 }
3368 }
3369
3370 // Read and store the height of line widgets associated with the
3371 // given line.
3372 function updateWidgetHeight(line) {
3373 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3374 var w = line.widgets[i], parent = w.node.parentNode;
3375 if (parent) { w.height = parent.offsetHeight; }
3376 } }
3377 }
3378
3379 // Compute the lines that are visible in a given viewport (defaults
3380 // the the current scroll position). viewport may contain top,
3381 // height, and ensure (see op.scrollToPos) properties.
3382 function visibleLines(display, doc, viewport) {
3383 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
3384 top = Math.floor(top - paddingTop(display));
3385 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
3386
3387 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
3388 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3389 // forces those lines into the viewport (if possible).
3390 if (viewport && viewport.ensure) {
3391 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
3392 if (ensureFrom < from) {
3393 from = ensureFrom;
3394 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
3395 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3396 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
3397 to = ensureTo;
3398 }
3399 }
3400 return {from: from, to: Math.max(to, from + 1)}
3401 }
3402
3403 // SCROLLING THINGS INTO VIEW
3404
3405 // If an editor sits on the top or bottom of the window, partially
3406 // scrolled out of view, this ensures that the cursor is visible.
3407 function maybeScrollWindow(cm, rect) {
3408 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3409
3410 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3411 if (rect.top + box.top < 0) { doScroll = true; }
3412 else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
3413 if (doScroll != null && !phantom) {
3414 var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
3415 cm.display.lineSpace.appendChild(scrollNode);
3416 scrollNode.scrollIntoView(doScroll);
3417 cm.display.lineSpace.removeChild(scrollNode);
3418 }
3419 }
3420
3421 // Scroll a given position into view (immediately), verifying that
3422 // it actually became visible (as line heights are accurately
3423 // measured, the position of something may 'drift' during drawing).
3424 function scrollPosIntoView(cm, pos, end, margin) {
3425 if (margin == null) { margin = 0; }
3426 var rect;
3427 if (!cm.options.lineWrapping && pos == end) {
3428 // Set pos and end to the cursor positions around the character pos sticks to
3429 // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3430 // If pos == Pos(_, 0, "before"), pos and end are unchanged
3431 pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
3432 end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
3433 }
3434 for (var limit = 0; limit < 5; limit++) {
3435 var changed = false;
3436 var coords = cursorCoords(cm, pos);
3437 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3438 rect = {left: Math.min(coords.left, endCoords.left),
3439 top: Math.min(coords.top, endCoords.top) - margin,
3440 right: Math.max(coords.left, endCoords.left),
3441 bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
3442 var scrollPos = calculateScrollPos(cm, rect);
3443 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3444 if (scrollPos.scrollTop != null) {
3445 updateScrollTop(cm, scrollPos.scrollTop);
3446 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
3447 }
3448 if (scrollPos.scrollLeft != null) {
3449 setScrollLeft(cm, scrollPos.scrollLeft);
3450 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
3451 }
3452 if (!changed) { break }
3453 }
3454 return rect
3455 }
3456
3457 // Scroll a given set of coordinates into view (immediately).
3458 function scrollIntoView(cm, rect) {
3459 var scrollPos = calculateScrollPos(cm, rect);
3460 if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
3461 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
3462 }
3463
3464 // Calculate a new scroll position needed to scroll the given
3465 // rectangle into view. Returns an object with scrollTop and
3466 // scrollLeft properties. When these are undefined, the
3467 // vertical/horizontal position does not need to be adjusted.
3468 function calculateScrollPos(cm, rect) {
3469 var display = cm.display, snapMargin = textHeight(cm.display);
3470 if (rect.top < 0) { rect.top = 0; }
3471 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3472 var screen = displayHeight(cm), result = {};
3473 if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
3474 var docBottom = cm.doc.height + paddingVert(display);
3475 var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
3476 if (rect.top < screentop) {
3477 result.scrollTop = atTop ? 0 : rect.top;
3478 } else if (rect.bottom > screentop + screen) {
3479 var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
3480 if (newTop != screentop) { result.scrollTop = newTop; }
3481 }
3482
3483 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
3484 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
3485 var tooWide = rect.right - rect.left > screenw;
3486 if (tooWide) { rect.right = rect.left + screenw; }
3487 if (rect.left < 10)
3488 { result.scrollLeft = 0; }
3489 else if (rect.left < screenleft)
3490 { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
3491 else if (rect.right > screenw + screenleft - 3)
3492 { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
3493 return result
3494 }
3495
3496 // Store a relative adjustment to the scroll position in the current
3497 // operation (to be applied when the operation finishes).
3498 function addToScrollTop(cm, top) {
3499 if (top == null) { return }
3500 resolveScrollToPos(cm);
3501 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3502 }
3503
3504 // Make sure that at the end of the operation the current cursor is
3505 // shown.
3506 function ensureCursorVisible(cm) {
3507 resolveScrollToPos(cm);
3508 var cur = cm.getCursor();
3509 cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
3510 }
3511
3512 function scrollToCoords(cm, x, y) {
3513 if (x != null || y != null) { resolveScrollToPos(cm); }
3514 if (x != null) { cm.curOp.scrollLeft = x; }
3515 if (y != null) { cm.curOp.scrollTop = y; }
3516 }
3517
3518 function scrollToRange(cm, range$$1) {
3519 resolveScrollToPos(cm);
3520 cm.curOp.scrollToPos = range$$1;
3521 }
3522
3523 // When an operation has its scrollToPos property set, and another
3524 // scroll action is applied before the end of the operation, this
3525 // 'simulates' scrolling that position into view in a cheap way, so
3526 // that the effect of intermediate scroll commands is not ignored.
3527 function resolveScrollToPos(cm) {
3528 var range$$1 = cm.curOp.scrollToPos;
3529 if (range$$1) {
3530 cm.curOp.scrollToPos = null;
3531 var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
3532 scrollToCoordsRange(cm, from, to, range$$1.margin);
3533 }
3534 }
3535
3536 function scrollToCoordsRange(cm, from, to, margin) {
3537 var sPos = calculateScrollPos(cm, {
3538 left: Math.min(from.left, to.left),
3539 top: Math.min(from.top, to.top) - margin,
3540 right: Math.max(from.right, to.right),
3541 bottom: Math.max(from.bottom, to.bottom) + margin
3542 });
3543 scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
3544 }
3545
3546 // Sync the scrollable area and scrollbars, ensure the viewport
3547 // covers the visible area.
3548 function updateScrollTop(cm, val) {
3549 if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3550 if (!gecko) { updateDisplaySimple(cm, {top: val}); }
3551 setScrollTop(cm, val, true);
3552 if (gecko) { updateDisplaySimple(cm); }
3553 startWorker(cm, 100);
3554 }
3555
3556 function setScrollTop(cm, val, forceScroll) {
3557 val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
3558 if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3559 cm.doc.scrollTop = val;
3560 cm.display.scrollbars.setScrollTop(val);
3561 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
3562 }
3563
3564 // Sync scroller and scrollbar, ensure the gutter elements are
3565 // aligned.
3566 function setScrollLeft(cm, val, isScroller, forceScroll) {
3567 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3568 if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3569 cm.doc.scrollLeft = val;
3570 alignHorizontally(cm);
3571 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
3572 cm.display.scrollbars.setScrollLeft(val);
3573 }
3574
3575 // SCROLLBARS
3576
3577 // Prepare DOM reads needed to update the scrollbars. Done in one
3578 // shot to minimize update/measure roundtrips.
3579 function measureForScrollbars(cm) {
3580 var d = cm.display, gutterW = d.gutters.offsetWidth;
3581 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
3582 return {
3583 clientHeight: d.scroller.clientHeight,
3584 viewHeight: d.wrapper.clientHeight,
3585 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3586 viewWidth: d.wrapper.clientWidth,
3587 barLeft: cm.options.fixedGutter ? gutterW : 0,
3588 docHeight: docH,
3589 scrollHeight: docH + scrollGap(cm) + d.barHeight,
3590 nativeBarWidth: d.nativeBarWidth,
3591 gutterWidth: gutterW
3592 }
3593 }
3594
3595 var NativeScrollbars = function(place, scroll, cm) {
3596 this.cm = cm;
3597 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
3598 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
3599 vert.tabIndex = horiz.tabIndex = -1;
3600 place(vert); place(horiz);
3601
3602 on(vert, "scroll", function () {
3603 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
3604 });
3605 on(horiz, "scroll", function () {
3606 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
3607 });
3608
3609 this.checkedZeroWidth = false;
3610 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3611 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
3612 };
3613
3614 NativeScrollbars.prototype.update = function (measure) {
3615 var needsH = measure.scrollWidth > measure.clientWidth + 1;
3616 var needsV = measure.scrollHeight > measure.clientHeight + 1;
3617 var sWidth = measure.nativeBarWidth;
3618
3619 if (needsV) {
3620 this.vert.style.display = "block";
3621 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
3622 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
3623 // A bug in IE8 can cause this value to be negative, so guard it.
3624 this.vert.firstChild.style.height =
3625 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
3626 } else {
3627 this.vert.style.display = "";
3628 this.vert.firstChild.style.height = "0";
3629 }
3630
3631 if (needsH) {
3632 this.horiz.style.display = "block";
3633 this.horiz.style.right = needsV ? sWidth + "px" : "0";
3634 this.horiz.style.left = measure.barLeft + "px";
3635 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
3636 this.horiz.firstChild.style.width =
3637 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
3638 } else {
3639 this.horiz.style.display = "";
3640 this.horiz.firstChild.style.width = "0";
3641 }
3642
3643 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3644 if (sWidth == 0) { this.zeroWidthHack(); }
3645 this.checkedZeroWidth = true;
3646 }
3647
3648 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3649 };
3650
3651 NativeScrollbars.prototype.setScrollLeft = function (pos) {
3652 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
3653 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
3654 };
3655
3656 NativeScrollbars.prototype.setScrollTop = function (pos) {
3657 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
3658 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
3659 };
3660
3661 NativeScrollbars.prototype.zeroWidthHack = function () {
3662 var w = mac && !mac_geMountainLion ? "12px" : "18px";
3663 this.horiz.style.height = this.vert.style.width = w;
3664 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
3665 this.disableHoriz = new Delayed;
3666 this.disableVert = new Delayed;
3667 };
3668
3669 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3670 bar.style.pointerEvents = "auto";
3671 function maybeDisable() {
3672 // To find out whether the scrollbar is still visible, we
3673 // check whether the element under the pixel in the bottom
3674 // right corner of the scrollbar box is the scrollbar box
3675 // itself (when the bar is still visible) or its filler child
3676 // (when the bar is hidden). If it is still visible, we keep
3677 // it enabled, if it's hidden, we disable pointer events.
3678 var box = bar.getBoundingClientRect();
3679 var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3680 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
3681 if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
3682 else { delay.set(1000, maybeDisable); }
3683 }
3684 delay.set(1000, maybeDisable);
3685 };
3686
3687 NativeScrollbars.prototype.clear = function () {
3688 var parent = this.horiz.parentNode;
3689 parent.removeChild(this.horiz);
3690 parent.removeChild(this.vert);
3691 };
3692
3693 var NullScrollbars = function () {};
3694
3695 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3696 NullScrollbars.prototype.setScrollLeft = function () {};
3697 NullScrollbars.prototype.setScrollTop = function () {};
3698 NullScrollbars.prototype.clear = function () {};
3699
3700 function updateScrollbars(cm, measure) {
3701 if (!measure) { measure = measureForScrollbars(cm); }
3702 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
3703 updateScrollbarsInner(cm, measure);
3704 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3705 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3706 { updateHeightsInViewport(cm); }
3707 updateScrollbarsInner(cm, measureForScrollbars(cm));
3708 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
3709 }
3710 }
3711
3712 // Re-synchronize the fake scrollbars with the actual size of the
3713 // content.
3714 function updateScrollbarsInner(cm, measure) {
3715 var d = cm.display;
3716 var sizes = d.scrollbars.update(measure);
3717
3718 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
3719 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
3720 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
3721
3722 if (sizes.right && sizes.bottom) {
3723 d.scrollbarFiller.style.display = "block";
3724 d.scrollbarFiller.style.height = sizes.bottom + "px";
3725 d.scrollbarFiller.style.width = sizes.right + "px";
3726 } else { d.scrollbarFiller.style.display = ""; }
3727 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3728 d.gutterFiller.style.display = "block";
3729 d.gutterFiller.style.height = sizes.bottom + "px";
3730 d.gutterFiller.style.width = measure.gutterWidth + "px";
3731 } else { d.gutterFiller.style.display = ""; }
3732 }
3733
3734 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
3735
3736 function initScrollbars(cm) {
3737 if (cm.display.scrollbars) {
3738 cm.display.scrollbars.clear();
3739 if (cm.display.scrollbars.addClass)
3740 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3741 }
3742
3743 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3744 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
3745 // Prevent clicks in the scrollbars from killing focus
3746 on(node, "mousedown", function () {
3747 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
3748 });
3749 node.setAttribute("cm-not-content", "true");
3750 }, function (pos, axis) {
3751 if (axis == "horizontal") { setScrollLeft(cm, pos); }
3752 else { updateScrollTop(cm, pos); }
3753 }, cm);
3754 if (cm.display.scrollbars.addClass)
3755 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3756 }
3757
3758 // Operations are used to wrap a series of changes to the editor
3759 // state in such a way that each change won't have to update the
3760 // cursor and display (which would be awkward, slow, and
3761 // error-prone). Instead, display updates are batched and then all
3762 // combined and executed at once.
3763
3764 var nextOpId = 0;
3765 // Start a new operation.
3766 function startOperation(cm) {
3767 cm.curOp = {
3768 cm: cm,
3769 viewChanged: false, // Flag that indicates that lines might need to be redrawn
3770 startHeight: cm.doc.height, // Used to detect need to update scrollbar
3771 forceUpdate: false, // Used to force a redraw
3772 updateInput: 0, // Whether to reset the input textarea
3773 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3774 changeObjs: null, // Accumulated changes, for firing change events
3775 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3776 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3777 selectionChanged: false, // Whether the selection needs to be redrawn
3778 updateMaxLine: false, // Set when the widest line needs to be determined anew
3779 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3780 scrollToPos: null, // Used to scroll to a specific position
3781 focus: false,
3782 id: ++nextOpId // Unique ID
3783 };
3784 pushOperation(cm.curOp);
3785 }
3786
3787 // Finish an operation, updating the display and signalling delayed events
3788 function endOperation(cm) {
3789 var op = cm.curOp;
3790 if (op) { finishOperation(op, function (group) {
3791 for (var i = 0; i < group.ops.length; i++)
3792 { group.ops[i].cm.curOp = null; }
3793 endOperations(group);
3794 }); }
3795 }
3796
3797 // The DOM updates done when an operation finishes are batched so
3798 // that the minimum number of relayouts are required.
3799 function endOperations(group) {
3800 var ops = group.ops;
3801 for (var i = 0; i < ops.length; i++) // Read DOM
3802 { endOperation_R1(ops[i]); }
3803 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3804 { endOperation_W1(ops[i$1]); }
3805 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3806 { endOperation_R2(ops[i$2]); }
3807 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3808 { endOperation_W2(ops[i$3]); }
3809 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3810 { endOperation_finish(ops[i$4]); }
3811 }
3812
3813 function endOperation_R1(op) {
3814 var cm = op.cm, display = cm.display;
3815 maybeClipScrollbars(cm);
3816 if (op.updateMaxLine) { findMaxLine(cm); }
3817
3818 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3819 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3820 op.scrollToPos.to.line >= display.viewTo) ||
3821 display.maxLineChanged && cm.options.lineWrapping;
3822 op.update = op.mustUpdate &&
3823 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3824 }
3825
3826 function endOperation_W1(op) {
3827 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3828 }
3829
3830 function endOperation_R2(op) {
3831 var cm = op.cm, display = cm.display;
3832 if (op.updatedDisplay) { updateHeightsInViewport(cm); }
3833
3834 op.barMeasure = measureForScrollbars(cm);
3835
3836 // If the max line changed since it was last measured, measure it,
3837 // and ensure the document's width matches it.
3838 // updateDisplay_W2 will use these properties to do the actual resizing
3839 if (display.maxLineChanged && !cm.options.lineWrapping) {
3840 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3841 cm.display.sizerWidth = op.adjustWidthTo;
3842 op.barMeasure.scrollWidth =
3843 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3844 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3845 }
3846
3847 if (op.updatedDisplay || op.selectionChanged)
3848 { op.preparedSelection = display.input.prepareSelection(); }
3849 }
3850
3851 function endOperation_W2(op) {
3852 var cm = op.cm;
3853
3854 if (op.adjustWidthTo != null) {
3855 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3856 if (op.maxScrollLeft < cm.doc.scrollLeft)
3857 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
3858 cm.display.maxLineChanged = false;
3859 }
3860
3861 var takeFocus = op.focus && op.focus == activeElt();
3862 if (op.preparedSelection)
3863 { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
3864 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3865 { updateScrollbars(cm, op.barMeasure); }
3866 if (op.updatedDisplay)
3867 { setDocumentHeight(cm, op.barMeasure); }
3868
3869 if (op.selectionChanged) { restartBlink(cm); }
3870
3871 if (cm.state.focused && op.updateInput)
3872 { cm.display.input.reset(op.typing); }
3873 if (takeFocus) { ensureFocus(op.cm); }
3874 }
3875
3876 function endOperation_finish(op) {
3877 var cm = op.cm, display = cm.display, doc = cm.doc;
3878
3879 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
3880
3881 // Abort mouse wheel delta measurement, when scrolling explicitly
3882 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3883 { display.wheelStartX = display.wheelStartY = null; }
3884
3885 // Propagate the scroll position to the actual DOM scroller
3886 if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
3887
3888 if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
3889 // If we need to scroll a specific position into view, do so.
3890 if (op.scrollToPos) {
3891 var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3892 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3893 maybeScrollWindow(cm, rect);
3894 }
3895
3896 // Fire events for markers that are hidden/unidden by editing or
3897 // undoing
3898 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3899 if (hidden) { for (var i = 0; i < hidden.length; ++i)
3900 { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
3901 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3902 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
3903
3904 if (display.wrapper.offsetHeight)
3905 { doc.scrollTop = cm.display.scroller.scrollTop; }
3906
3907 // Fire change events, and delayed event handlers
3908 if (op.changeObjs)
3909 { signal(cm, "changes", cm, op.changeObjs); }
3910 if (op.update)
3911 { op.update.finish(); }
3912 }
3913
3914 // Run the given function in an operation
3915 function runInOp(cm, f) {
3916 if (cm.curOp) { return f() }
3917 startOperation(cm);
3918 try { return f() }
3919 finally { endOperation(cm); }
3920 }
3921 // Wraps a function in an operation. Returns the wrapped function.
3922 function operation(cm, f) {
3923 return function() {
3924 if (cm.curOp) { return f.apply(cm, arguments) }
3925 startOperation(cm);
3926 try { return f.apply(cm, arguments) }
3927 finally { endOperation(cm); }
3928 }
3929 }
3930 // Used to add methods to editor and doc instances, wrapping them in
3931 // operations.
3932 function methodOp(f) {
3933 return function() {
3934 if (this.curOp) { return f.apply(this, arguments) }
3935 startOperation(this);
3936 try { return f.apply(this, arguments) }
3937 finally { endOperation(this); }
3938 }
3939 }
3940 function docMethodOp(f) {
3941 return function() {
3942 var cm = this.cm;
3943 if (!cm || cm.curOp) { return f.apply(this, arguments) }
3944 startOperation(cm);
3945 try { return f.apply(this, arguments) }
3946 finally { endOperation(cm); }
3947 }
3948 }
3949
3950 // HIGHLIGHT WORKER
3951
3952 function startWorker(cm, time) {
3953 if (cm.doc.highlightFrontier < cm.display.viewTo)
3954 { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
3955 }
3956
3957 function highlightWorker(cm) {
3958 var doc = cm.doc;
3959 if (doc.highlightFrontier >= cm.display.viewTo) { return }
3960 var end = +new Date + cm.options.workTime;
3961 var context = getContextBefore(cm, doc.highlightFrontier);
3962 var changedLines = [];
3963
3964 doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3965 if (context.line >= cm.display.viewFrom) { // Visible
3966 var oldStyles = line.styles;
3967 var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
3968 var highlighted = highlightLine(cm, line, context, true);
3969 if (resetState) { context.state = resetState; }
3970 line.styles = highlighted.styles;
3971 var oldCls = line.styleClasses, newCls = highlighted.classes;
3972 if (newCls) { line.styleClasses = newCls; }
3973 else if (oldCls) { line.styleClasses = null; }
3974 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3975 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
3976 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
3977 if (ischange) { changedLines.push(context.line); }
3978 line.stateAfter = context.save();
3979 context.nextLine();
3980 } else {
3981 if (line.text.length <= cm.options.maxHighlightLength)
3982 { processLine(cm, line.text, context); }
3983 line.stateAfter = context.line % 5 == 0 ? context.save() : null;
3984 context.nextLine();
3985 }
3986 if (+new Date > end) {
3987 startWorker(cm, cm.options.workDelay);
3988 return true
3989 }
3990 });
3991 doc.highlightFrontier = context.line;
3992 doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
3993 if (changedLines.length) { runInOp(cm, function () {
3994 for (var i = 0; i < changedLines.length; i++)
3995 { regLineChange(cm, changedLines[i], "text"); }
3996 }); }
3997 }
3998
3999 // DISPLAY DRAWING
4000
4001 var DisplayUpdate = function(cm, viewport, force) {
4002 var display = cm.display;
4003
4004 this.viewport = viewport;
4005 // Store some values that we'll need later (but don't want to force a relayout for)
4006 this.visible = visibleLines(display, cm.doc, viewport);
4007 this.editorIsHidden = !display.wrapper.offsetWidth;
4008 this.wrapperHeight = display.wrapper.clientHeight;
4009 this.wrapperWidth = display.wrapper.clientWidth;
4010 this.oldDisplayWidth = displayWidth(cm);
4011 this.force = force;
4012 this.dims = getDimensions(cm);
4013 this.events = [];
4014 };
4015
4016 DisplayUpdate.prototype.signal = function (emitter, type) {
4017 if (hasHandler(emitter, type))
4018 { this.events.push(arguments); }
4019 };
4020 DisplayUpdate.prototype.finish = function () {
4021 var this$1 = this;
4022
4023 for (var i = 0; i < this.events.length; i++)
4024 { signal.apply(null, this$1.events[i]); }
4025 };
4026
4027 function maybeClipScrollbars(cm) {
4028 var display = cm.display;
4029 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4030 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
4031 display.heightForcer.style.height = scrollGap(cm) + "px";
4032 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
4033 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
4034 display.scrollbarsClipped = true;
4035 }
4036 }
4037
4038 function selectionSnapshot(cm) {
4039 if (cm.hasFocus()) { return null }
4040 var active = activeElt();
4041 if (!active || !contains(cm.display.lineDiv, active)) { return null }
4042 var result = {activeElt: active};
4043 if (window.getSelection) {
4044 var sel = window.getSelection();
4045 if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4046 result.anchorNode = sel.anchorNode;
4047 result.anchorOffset = sel.anchorOffset;
4048 result.focusNode = sel.focusNode;
4049 result.focusOffset = sel.focusOffset;
4050 }
4051 }
4052 return result
4053 }
4054
4055 function restoreSelection(snapshot) {
4056 if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4057 snapshot.activeElt.focus();
4058 if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4059 var sel = window.getSelection(), range$$1 = document.createRange();
4060 range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
4061 range$$1.collapse(false);
4062 sel.removeAllRanges();
4063 sel.addRange(range$$1);
4064 sel.extend(snapshot.focusNode, snapshot.focusOffset);
4065 }
4066 }
4067
4068 // Does the actual updating of the line display. Bails out
4069 // (returning false) when there is nothing to be done and forced is
4070 // false.
4071 function updateDisplayIfNeeded(cm, update) {
4072 var display = cm.display, doc = cm.doc;
4073
4074 if (update.editorIsHidden) {
4075 resetView(cm);
4076 return false
4077 }
4078
4079 // Bail out if the visible area is already rendered and nothing changed.
4080 if (!update.force &&
4081 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4082 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4083 display.renderedView == display.view && countDirtyView(cm) == 0)
4084 { return false }
4085
4086 if (maybeUpdateLineNumberWidth(cm)) {
4087 resetView(cm);
4088 update.dims = getDimensions(cm);
4089 }
4090
4091 // Compute a suitable new viewport (from & to)
4092 var end = doc.first + doc.size;
4093 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
4094 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
4095 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
4096 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
4097 if (sawCollapsedSpans) {
4098 from = visualLineNo(cm.doc, from);
4099 to = visualLineEndNo(cm.doc, to);
4100 }
4101
4102 var different = from != display.viewFrom || to != display.viewTo ||
4103 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
4104 adjustView(cm, from, to);
4105
4106 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
4107 // Position the mover div to align with the current scroll position
4108 cm.display.mover.style.top = display.viewOffset + "px";
4109
4110 var toUpdate = countDirtyView(cm);
4111 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4112 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4113 { return false }
4114
4115 // For big changes, we hide the enclosing element during the
4116 // update, since that speeds up the operations on most browsers.
4117 var selSnapshot = selectionSnapshot(cm);
4118 if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
4119 patchDisplay(cm, display.updateLineNumbers, update.dims);
4120 if (toUpdate > 4) { display.lineDiv.style.display = ""; }
4121 display.renderedView = display.view;
4122 // There might have been a widget with a focused element that got
4123 // hidden or updated, if so re-focus it.
4124 restoreSelection(selSnapshot);
4125
4126 // Prevent selection and cursors from interfering with the scroll
4127 // width and height.
4128 removeChildren(display.cursorDiv);
4129 removeChildren(display.selectionDiv);
4130 display.gutters.style.height = display.sizer.style.minHeight = 0;
4131
4132 if (different) {
4133 display.lastWrapHeight = update.wrapperHeight;
4134 display.lastWrapWidth = update.wrapperWidth;
4135 startWorker(cm, 400);
4136 }
4137
4138 display.updateLineNumbers = null;
4139
4140 return true
4141 }
4142
4143 function postUpdateDisplay(cm, update) {
4144 var viewport = update.viewport;
4145
4146 for (var first = true;; first = false) {
4147 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4148 // Clip forced viewport to actual scrollable area.
4149 if (viewport && viewport.top != null)
4150 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
4151 // Updated line heights might result in the drawn area not
4152 // actually covering the viewport. Keep looping until it does.
4153 update.visible = visibleLines(cm.display, cm.doc, viewport);
4154 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4155 { break }
4156 }
4157 if (!updateDisplayIfNeeded(cm, update)) { break }
4158 updateHeightsInViewport(cm);
4159 var barMeasure = measureForScrollbars(cm);
4160 updateSelection(cm);
4161 updateScrollbars(cm, barMeasure);
4162 setDocumentHeight(cm, barMeasure);
4163 update.force = false;
4164 }
4165
4166 update.signal(cm, "update", cm);
4167 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4168 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
4169 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
4170 }
4171 }
4172
4173 function updateDisplaySimple(cm, viewport) {
4174 var update = new DisplayUpdate(cm, viewport);
4175 if (updateDisplayIfNeeded(cm, update)) {
4176 updateHeightsInViewport(cm);
4177 postUpdateDisplay(cm, update);
4178 var barMeasure = measureForScrollbars(cm);
4179 updateSelection(cm);
4180 updateScrollbars(cm, barMeasure);
4181 setDocumentHeight(cm, barMeasure);
4182 update.finish();
4183 }
4184 }
4185
4186 // Sync the actual display DOM structure with display.view, removing
4187 // nodes for lines that are no longer in view, and creating the ones
4188 // that are not there yet, and updating the ones that are out of
4189 // date.
4190 function patchDisplay(cm, updateNumbersFrom, dims) {
4191 var display = cm.display, lineNumbers = cm.options.lineNumbers;
4192 var container = display.lineDiv, cur = container.firstChild;
4193
4194 function rm(node) {
4195 var next = node.nextSibling;
4196 // Works around a throw-scroll bug in OS X Webkit
4197 if (webkit && mac && cm.display.currentWheelTarget == node)
4198 { node.style.display = "none"; }
4199 else
4200 { node.parentNode.removeChild(node); }
4201 return next
4202 }
4203
4204 var view = display.view, lineN = display.viewFrom;
4205 // Loop over the elements in the view, syncing cur (the DOM nodes
4206 // in display.lineDiv) with the view as we go.
4207 for (var i = 0; i < view.length; i++) {
4208 var lineView = view[i];
4209 if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4210 var node = buildLineElement(cm, lineView, lineN, dims);
4211 container.insertBefore(node, cur);
4212 } else { // Already drawn
4213 while (cur != lineView.node) { cur = rm(cur); }
4214 var updateNumber = lineNumbers && updateNumbersFrom != null &&
4215 updateNumbersFrom <= lineN && lineView.lineNumber;
4216 if (lineView.changes) {
4217 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
4218 updateLineForChanges(cm, lineView, lineN, dims);
4219 }
4220 if (updateNumber) {
4221 removeChildren(lineView.lineNumber);
4222 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
4223 }
4224 cur = lineView.node.nextSibling;
4225 }
4226 lineN += lineView.size;
4227 }
4228 while (cur) { cur = rm(cur); }
4229 }
4230
4231 function updateGutterSpace(display) {
4232 var width = display.gutters.offsetWidth;
4233 display.sizer.style.marginLeft = width + "px";
4234 }
4235
4236 function setDocumentHeight(cm, measure) {
4237 cm.display.sizer.style.minHeight = measure.docHeight + "px";
4238 cm.display.heightForcer.style.top = measure.docHeight + "px";
4239 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
4240 }
4241
4242 // Re-align line numbers and gutter marks to compensate for
4243 // horizontal scrolling.
4244 function alignHorizontally(cm) {
4245 var display = cm.display, view = display.view;
4246 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4247 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4248 var gutterW = display.gutters.offsetWidth, left = comp + "px";
4249 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4250 if (cm.options.fixedGutter) {
4251 if (view[i].gutter)
4252 { view[i].gutter.style.left = left; }
4253 if (view[i].gutterBackground)
4254 { view[i].gutterBackground.style.left = left; }
4255 }
4256 var align = view[i].alignable;
4257 if (align) { for (var j = 0; j < align.length; j++)
4258 { align[j].style.left = left; } }
4259 } }
4260 if (cm.options.fixedGutter)
4261 { display.gutters.style.left = (comp + gutterW) + "px"; }
4262 }
4263
4264 // Used to ensure that the line number gutter is still the right
4265 // size for the current document size. Returns true when an update
4266 // is needed.
4267 function maybeUpdateLineNumberWidth(cm) {
4268 if (!cm.options.lineNumbers) { return false }
4269 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4270 if (last.length != display.lineNumChars) {
4271 var test = display.measure.appendChild(elt("div", [elt("div", last)],
4272 "CodeMirror-linenumber CodeMirror-gutter-elt"));
4273 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4274 display.lineGutter.style.width = "";
4275 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4276 display.lineNumWidth = display.lineNumInnerWidth + padding;
4277 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4278 display.lineGutter.style.width = display.lineNumWidth + "px";
4279 updateGutterSpace(cm.display);
4280 return true
4281 }
4282 return false
4283 }
4284
4285 function getGutters(gutters, lineNumbers) {
4286 var result = [], sawLineNumbers = false;
4287 for (var i = 0; i < gutters.length; i++) {
4288 var name = gutters[i], style = null;
4289 if (typeof name != "string") { style = name.style; name = name.className; }
4290 if (name == "CodeMirror-linenumbers") {
4291 if (!lineNumbers) { continue }
4292 else { sawLineNumbers = true; }
4293 }
4294 result.push({className: name, style: style});
4295 }
4296 if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
4297 return result
4298 }
4299
4300 // Rebuild the gutter elements, ensure the margin to the left of the
4301 // code matches their width.
4302 function renderGutters(display) {
4303 var gutters = display.gutters, specs = display.gutterSpecs;
4304 removeChildren(gutters);
4305 display.lineGutter = null;
4306 for (var i = 0; i < specs.length; ++i) {
4307 var ref = specs[i];
4308 var className = ref.className;
4309 var style = ref.style;
4310 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
4311 if (style) { gElt.style.cssText = style; }
4312 if (className == "CodeMirror-linenumbers") {
4313 display.lineGutter = gElt;
4314 gElt.style.width = (display.lineNumWidth || 1) + "px";
4315 }
4316 }
4317 gutters.style.display = specs.length ? "" : "none";
4318 updateGutterSpace(display);
4319 }
4320
4321 function updateGutters(cm) {
4322 renderGutters(cm.display);
4323 regChange(cm);
4324 alignHorizontally(cm);
4325 }
131 4326
132 4327 // The display handles the DOM integration, both for input reading
133 4328 // and content drawing. It holds references to DOM nodes and
134 4329 // display-related state.
135 4330
136 function Display(place, doc, input) {
4331 function Display(place, doc, input, options) {
137 4332 var d = this;
138 4333 this.input = input;
139 4334
@@ -145,7 +4340,7 b''
145 4340 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
146 4341 d.gutterFiller.setAttribute("cm-not-content", "true");
147 4342 // Will contain the actual code, positioned to cover the viewport.
148 d.lineDiv = elt("div", null, "CodeMirror-code");
4343 d.lineDiv = eltP("div", null, "CodeMirror-code");
149 4344 // Elements are added to these to represent selection and cursors.
150 4345 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
151 4346 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
@@ -154,10 +4349,11 b''
154 4349 // When lines outside of the viewport are measured, they are drawn in this.
155 4350 d.lineMeasure = elt("div", null, "CodeMirror-measure");
156 4351 // Wraps everything that needs to exist inside the vertically-padded coordinate system
157 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
4352 d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
158 4353 null, "position: relative; outline: none");
4354 var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
159 4355 // Moved around its parent to cover visible view.
160 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
4356 d.mover = elt("div", [lines], null, "position: relative");
161 4357 // Set to the height of the document, allowing scrolling.
162 4358 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
163 4359 d.sizerWidth = null;
@@ -176,11 +4372,11 b''
176 4372
177 4373 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
178 4374 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
179 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
4375 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
180 4376
181 4377 if (place) {
182 if (place.appendChild) place.appendChild(d.wrapper);
183 else place(d.wrapper);
4378 if (place.appendChild) { place.appendChild(d.wrapper); }
4379 else { place(d.wrapper); }
184 4380 }
185 4381
186 4382 // Current rendered range (may be bigger than the view window).
@@ -228,3738 +4424,12 b''
228 4424
229 4425 d.activeTouch = null;
230 4426
4427 d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
4428 renderGutters(d);
4429
231 4430 input.init(d);
232 4431 }
233 4432
234 // STATE UPDATES
235
236 // Used to get the editor into a consistent state again when options change.
237
238 function loadMode(cm) {
239 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
240 resetModeState(cm);
241 }
242
243 function resetModeState(cm) {
244 cm.doc.iter(function(line) {
245 if (line.stateAfter) line.stateAfter = null;
246 if (line.styles) line.styles = null;
247 });
248 cm.doc.frontier = cm.doc.first;
249 startWorker(cm, 100);
250 cm.state.modeGen++;
251 if (cm.curOp) regChange(cm);
252 }
253
254 function wrappingChanged(cm) {
255 if (cm.options.lineWrapping) {
256 addClass(cm.display.wrapper, "CodeMirror-wrap");
257 cm.display.sizer.style.minWidth = "";
258 cm.display.sizerWidth = null;
259 } else {
260 rmClass(cm.display.wrapper, "CodeMirror-wrap");
261 findMaxLine(cm);
262 }
263 estimateLineHeights(cm);
264 regChange(cm);
265 clearCaches(cm);
266 setTimeout(function(){updateScrollbars(cm);}, 100);
267 }
268
269 // Returns a function that estimates the height of a line, to use as
270 // first approximation until the line becomes visible (and is thus
271 // properly measurable).
272 function estimateHeight(cm) {
273 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
274 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
275 return function(line) {
276 if (lineIsHidden(cm.doc, line)) return 0;
277
278 var widgetsHeight = 0;
279 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
280 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
281 }
282
283 if (wrapping)
284 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
285 else
286 return widgetsHeight + th;
287 };
288 }
289
290 function estimateLineHeights(cm) {
291 var doc = cm.doc, est = estimateHeight(cm);
292 doc.iter(function(line) {
293 var estHeight = est(line);
294 if (estHeight != line.height) updateLineHeight(line, estHeight);
295 });
296 }
297
298 function themeChanged(cm) {
299 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
300 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
301 clearCaches(cm);
302 }
303
304 function guttersChanged(cm) {
305 updateGutters(cm);
306 regChange(cm);
307 setTimeout(function(){alignHorizontally(cm);}, 20);
308 }
309
310 // Rebuild the gutter elements, ensure the margin to the left of the
311 // code matches their width.
312 function updateGutters(cm) {
313 var gutters = cm.display.gutters, specs = cm.options.gutters;
314 removeChildren(gutters);
315 for (var i = 0; i < specs.length; ++i) {
316 var gutterClass = specs[i];
317 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
318 if (gutterClass == "CodeMirror-linenumbers") {
319 cm.display.lineGutter = gElt;
320 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
321 }
322 }
323 gutters.style.display = i ? "" : "none";
324 updateGutterSpace(cm);
325 }
326
327 function updateGutterSpace(cm) {
328 var width = cm.display.gutters.offsetWidth;
329 cm.display.sizer.style.marginLeft = width + "px";
330 }
331
332 // Compute the character length of a line, taking into account
333 // collapsed ranges (see markText) that might hide parts, and join
334 // other lines onto it.
335 function lineLength(line) {
336 if (line.height == 0) return 0;
337 var len = line.text.length, merged, cur = line;
338 while (merged = collapsedSpanAtStart(cur)) {
339 var found = merged.find(0, true);
340 cur = found.from.line;
341 len += found.from.ch - found.to.ch;
342 }
343 cur = line;
344 while (merged = collapsedSpanAtEnd(cur)) {
345 var found = merged.find(0, true);
346 len -= cur.text.length - found.from.ch;
347 cur = found.to.line;
348 len += cur.text.length - found.to.ch;
349 }
350 return len;
351 }
352
353 // Find the longest line in the document.
354 function findMaxLine(cm) {
355 var d = cm.display, doc = cm.doc;
356 d.maxLine = getLine(doc, doc.first);
357 d.maxLineLength = lineLength(d.maxLine);
358 d.maxLineChanged = true;
359 doc.iter(function(line) {
360 var len = lineLength(line);
361 if (len > d.maxLineLength) {
362 d.maxLineLength = len;
363 d.maxLine = line;
364 }
365 });
366 }
367
368 // Make sure the gutters options contains the element
369 // "CodeMirror-linenumbers" when the lineNumbers option is true.
370 function setGuttersForLineNumbers(options) {
371 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
372 if (found == -1 && options.lineNumbers) {
373 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
374 } else if (found > -1 && !options.lineNumbers) {
375 options.gutters = options.gutters.slice(0);
376 options.gutters.splice(found, 1);
377 }
378 }
379
380 // SCROLLBARS
381
382 // Prepare DOM reads needed to update the scrollbars. Done in one
383 // shot to minimize update/measure roundtrips.
384 function measureForScrollbars(cm) {
385 var d = cm.display, gutterW = d.gutters.offsetWidth;
386 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
387 return {
388 clientHeight: d.scroller.clientHeight,
389 viewHeight: d.wrapper.clientHeight,
390 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
391 viewWidth: d.wrapper.clientWidth,
392 barLeft: cm.options.fixedGutter ? gutterW : 0,
393 docHeight: docH,
394 scrollHeight: docH + scrollGap(cm) + d.barHeight,
395 nativeBarWidth: d.nativeBarWidth,
396 gutterWidth: gutterW
397 };
398 }
399
400 function NativeScrollbars(place, scroll, cm) {
401 this.cm = cm;
402 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
403 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
404 place(vert); place(horiz);
405
406 on(vert, "scroll", function() {
407 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
408 });
409 on(horiz, "scroll", function() {
410 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
411 });
412
413 this.checkedZeroWidth = false;
414 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
415 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
416 }
417
418 NativeScrollbars.prototype = copyObj({
419 update: function(measure) {
420 var needsH = measure.scrollWidth > measure.clientWidth + 1;
421 var needsV = measure.scrollHeight > measure.clientHeight + 1;
422 var sWidth = measure.nativeBarWidth;
423
424 if (needsV) {
425 this.vert.style.display = "block";
426 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
427 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
428 // A bug in IE8 can cause this value to be negative, so guard it.
429 this.vert.firstChild.style.height =
430 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
431 } else {
432 this.vert.style.display = "";
433 this.vert.firstChild.style.height = "0";
434 }
435
436 if (needsH) {
437 this.horiz.style.display = "block";
438 this.horiz.style.right = needsV ? sWidth + "px" : "0";
439 this.horiz.style.left = measure.barLeft + "px";
440 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
441 this.horiz.firstChild.style.width =
442 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
443 } else {
444 this.horiz.style.display = "";
445 this.horiz.firstChild.style.width = "0";
446 }
447
448 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
449 if (sWidth == 0) this.zeroWidthHack();
450 this.checkedZeroWidth = true;
451 }
452
453 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
454 },
455 setScrollLeft: function(pos) {
456 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
457 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz);
458 },
459 setScrollTop: function(pos) {
460 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
461 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert);
462 },
463 zeroWidthHack: function() {
464 var w = mac && !mac_geMountainLion ? "12px" : "18px";
465 this.horiz.style.height = this.vert.style.width = w;
466 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
467 this.disableHoriz = new Delayed;
468 this.disableVert = new Delayed;
469 },
470 enableZeroWidthBar: function(bar, delay) {
471 bar.style.pointerEvents = "auto";
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);
485 },
486 clear: function() {
487 var parent = this.horiz.parentNode;
488 parent.removeChild(this.horiz);
489 parent.removeChild(this.vert);
490 }
491 }, NativeScrollbars.prototype);
492
493 function NullScrollbars() {}
494
495 NullScrollbars.prototype = copyObj({
496 update: function() { return {bottom: 0, right: 0}; },
497 setScrollLeft: function() {},
498 setScrollTop: function() {},
499 clear: function() {}
500 }, NullScrollbars.prototype);
501
502 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
503
504 function initScrollbars(cm) {
505 if (cm.display.scrollbars) {
506 cm.display.scrollbars.clear();
507 if (cm.display.scrollbars.addClass)
508 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
509 }
510
511 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
512 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
513 // Prevent clicks in the scrollbars from killing focus
514 on(node, "mousedown", function() {
515 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
516 });
517 node.setAttribute("cm-not-content", "true");
518 }, function(pos, axis) {
519 if (axis == "horizontal") setScrollLeft(cm, pos);
520 else setScrollTop(cm, pos);
521 }, cm);
522 if (cm.display.scrollbars.addClass)
523 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
524 }
525
526 function updateScrollbars(cm, measure) {
527 if (!measure) measure = measureForScrollbars(cm);
528 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
529 updateScrollbarsInner(cm, measure);
530 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
531 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
532 updateHeightsInViewport(cm);
533 updateScrollbarsInner(cm, measureForScrollbars(cm));
534 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
535 }
536 }
537
538 // Re-synchronize the fake scrollbars with the actual size of the
539 // content.
540 function updateScrollbarsInner(cm, measure) {
541 var d = cm.display;
542 var sizes = d.scrollbars.update(measure);
543
544 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
545 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
546
547 if (sizes.right && sizes.bottom) {
548 d.scrollbarFiller.style.display = "block";
549 d.scrollbarFiller.style.height = sizes.bottom + "px";
550 d.scrollbarFiller.style.width = sizes.right + "px";
551 } else d.scrollbarFiller.style.display = "";
552 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
553 d.gutterFiller.style.display = "block";
554 d.gutterFiller.style.height = sizes.bottom + "px";
555 d.gutterFiller.style.width = measure.gutterWidth + "px";
556 } else d.gutterFiller.style.display = "";
557 }
558
559 // Compute the lines that are visible in a given viewport (defaults
560 // the the current scroll position). viewport may contain top,
561 // height, and ensure (see op.scrollToPos) properties.
562 function visibleLines(display, doc, viewport) {
563 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
564 top = Math.floor(top - paddingTop(display));
565 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
566
567 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
568 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
569 // forces those lines into the viewport (if possible).
570 if (viewport && viewport.ensure) {
571 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
572 if (ensureFrom < from) {
573 from = ensureFrom;
574 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
575 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
576 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
577 to = ensureTo;
578 }
579 }
580 return {from: from, to: Math.max(to, from + 1)};
581 }
582
583 // LINE NUMBERS
584
585 // Re-align line numbers and gutter marks to compensate for
586 // horizontal scrolling.
587 function alignHorizontally(cm) {
588 var display = cm.display, view = display.view;
589 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
590 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
591 var gutterW = display.gutters.offsetWidth, left = comp + "px";
592 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
593 if (cm.options.fixedGutter && view[i].gutter)
594 view[i].gutter.style.left = left;
595 var align = view[i].alignable;
596 if (align) for (var j = 0; j < align.length; j++)
597 align[j].style.left = left;
598 }
599 if (cm.options.fixedGutter)
600 display.gutters.style.left = (comp + gutterW) + "px";
601 }
602
603 // Used to ensure that the line number gutter is still the right
604 // size for the current document size. Returns true when an update
605 // is needed.
606 function maybeUpdateLineNumberWidth(cm) {
607 if (!cm.options.lineNumbers) return false;
608 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
609 if (last.length != display.lineNumChars) {
610 var test = display.measure.appendChild(elt("div", [elt("div", last)],
611 "CodeMirror-linenumber CodeMirror-gutter-elt"));
612 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
613 display.lineGutter.style.width = "";
614 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
615 display.lineNumWidth = display.lineNumInnerWidth + padding;
616 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
617 display.lineGutter.style.width = display.lineNumWidth + "px";
618 updateGutterSpace(cm);
619 return true;
620 }
621 return false;
622 }
623
624 function lineNumberFor(options, i) {
625 return String(options.lineNumberFormatter(i + options.firstLineNumber));
626 }
627
628 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
629 // but using getBoundingClientRect to get a sub-pixel-accurate
630 // result.
631 function compensateForHScroll(display) {
632 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
633 }
634
635 // DISPLAY DRAWING
636
637 function DisplayUpdate(cm, viewport, force) {
638 var display = cm.display;
639
640 this.viewport = viewport;
641 // Store some values that we'll need later (but don't want to force a relayout for)
642 this.visible = visibleLines(display, cm.doc, viewport);
643 this.editorIsHidden = !display.wrapper.offsetWidth;
644 this.wrapperHeight = display.wrapper.clientHeight;
645 this.wrapperWidth = display.wrapper.clientWidth;
646 this.oldDisplayWidth = displayWidth(cm);
647 this.force = force;
648 this.dims = getDimensions(cm);
649 this.events = [];
650 }
651
652 DisplayUpdate.prototype.signal = function(emitter, type) {
653 if (hasHandler(emitter, type))
654 this.events.push(arguments);
655 };
656 DisplayUpdate.prototype.finish = function() {
657 for (var i = 0; i < this.events.length; i++)
658 signal.apply(null, this.events[i]);
659 };
660
661 function maybeClipScrollbars(cm) {
662 var display = cm.display;
663 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
664 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
665 display.heightForcer.style.height = scrollGap(cm) + "px";
666 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
667 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
668 display.scrollbarsClipped = true;
669 }
670 }
671
672 // Does the actual updating of the line display. Bails out
673 // (returning false) when there is nothing to be done and forced is
674 // false.
675 function updateDisplayIfNeeded(cm, update) {
676 var display = cm.display, doc = cm.doc;
677
678 if (update.editorIsHidden) {
679 resetView(cm);
680 return false;
681 }
682
683 // Bail out if the visible area is already rendered and nothing changed.
684 if (!update.force &&
685 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
686 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
687 display.renderedView == display.view && countDirtyView(cm) == 0)
688 return false;
689
690 if (maybeUpdateLineNumberWidth(cm)) {
691 resetView(cm);
692 update.dims = getDimensions(cm);
693 }
694
695 // Compute a suitable new viewport (from & to)
696 var end = doc.first + doc.size;
697 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
698 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
699 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
700 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
701 if (sawCollapsedSpans) {
702 from = visualLineNo(cm.doc, from);
703 to = visualLineEndNo(cm.doc, to);
704 }
705
706 var different = from != display.viewFrom || to != display.viewTo ||
707 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
708 adjustView(cm, from, to);
709
710 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
711 // Position the mover div to align with the current scroll position
712 cm.display.mover.style.top = display.viewOffset + "px";
713
714 var toUpdate = countDirtyView(cm);
715 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
716 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
717 return false;
718
719 // For big changes, we hide the enclosing element during the
720 // update, since that speeds up the operations on most browsers.
721 var focused = activeElt();
722 if (toUpdate > 4) display.lineDiv.style.display = "none";
723 patchDisplay(cm, display.updateLineNumbers, update.dims);
724 if (toUpdate > 4) display.lineDiv.style.display = "";
725 display.renderedView = display.view;
726 // There might have been a widget with a focused element that got
727 // hidden or updated, if so re-focus it.
728 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
729
730 // Prevent selection and cursors from interfering with the scroll
731 // width and height.
732 removeChildren(display.cursorDiv);
733 removeChildren(display.selectionDiv);
734 display.gutters.style.height = display.sizer.style.minHeight = 0;
735
736 if (different) {
737 display.lastWrapHeight = update.wrapperHeight;
738 display.lastWrapWidth = update.wrapperWidth;
739 startWorker(cm, 400);
740 }
741
742 display.updateLineNumbers = null;
743
744 return true;
745 }
746
747 function postUpdateDisplay(cm, update) {
748 var viewport = update.viewport;
749 for (var first = true;; first = false) {
750 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
751 // Clip forced viewport to actual scrollable area.
752 if (viewport && viewport.top != null)
753 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
754 // Updated line heights might result in the drawn area not
755 // actually covering the viewport. Keep looping until it does.
756 update.visible = visibleLines(cm.display, cm.doc, viewport);
757 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
758 break;
759 }
760 if (!updateDisplayIfNeeded(cm, update)) break;
761 updateHeightsInViewport(cm);
762 var barMeasure = measureForScrollbars(cm);
763 updateSelection(cm);
764 setDocumentHeight(cm, barMeasure);
765 updateScrollbars(cm, barMeasure);
766 }
767
768 update.signal(cm, "update", cm);
769 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
770 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
771 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
772 }
773 }
774
775 function updateDisplaySimple(cm, viewport) {
776 var update = new DisplayUpdate(cm, viewport);
777 if (updateDisplayIfNeeded(cm, update)) {
778 updateHeightsInViewport(cm);
779 postUpdateDisplay(cm, update);
780 var barMeasure = measureForScrollbars(cm);
781 updateSelection(cm);
782 setDocumentHeight(cm, barMeasure);
783 updateScrollbars(cm, barMeasure);
784 update.finish();
785 }
786 }
787
788 function setDocumentHeight(cm, measure) {
789 cm.display.sizer.style.minHeight = measure.docHeight + "px";
790 var total = measure.docHeight + cm.display.barHeight;
791 cm.display.heightForcer.style.top = total + "px";
792 cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
793 }
794
795 // Read the actual heights of the rendered lines, and update their
796 // stored heights to match.
797 function updateHeightsInViewport(cm) {
798 var display = cm.display;
799 var prevBottom = display.lineDiv.offsetTop;
800 for (var i = 0; i < display.view.length; i++) {
801 var cur = display.view[i], height;
802 if (cur.hidden) continue;
803 if (ie && ie_version < 8) {
804 var bot = cur.node.offsetTop + cur.node.offsetHeight;
805 height = bot - prevBottom;
806 prevBottom = bot;
807 } else {
808 var box = cur.node.getBoundingClientRect();
809 height = box.bottom - box.top;
810 }
811 var diff = cur.line.height - height;
812 if (height < 2) height = textHeight(display);
813 if (diff > .001 || diff < -.001) {
814 updateLineHeight(cur.line, height);
815 updateWidgetHeight(cur.line);
816 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
817 updateWidgetHeight(cur.rest[j]);
818 }
819 }
820 }
821
822 // Read and store the height of line widgets associated with the
823 // given line.
824 function updateWidgetHeight(line) {
825 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
826 line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
827 }
828
829 // Do a bulk-read of the DOM positions and sizes needed to draw the
830 // view, so that we don't interleave reading and writing to the DOM.
831 function getDimensions(cm) {
832 var d = cm.display, left = {}, width = {};
833 var gutterLeft = d.gutters.clientLeft;
834 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
835 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
836 width[cm.options.gutters[i]] = n.clientWidth;
837 }
838 return {fixedPos: compensateForHScroll(d),
839 gutterTotalWidth: d.gutters.offsetWidth,
840 gutterLeft: left,
841 gutterWidth: width,
842 wrapperWidth: d.wrapper.clientWidth};
843 }
844
845 // Sync the actual display DOM structure with display.view, removing
846 // nodes for lines that are no longer in view, and creating the ones
847 // that are not there yet, and updating the ones that are out of
848 // date.
849 function patchDisplay(cm, updateNumbersFrom, dims) {
850 var display = cm.display, lineNumbers = cm.options.lineNumbers;
851 var container = display.lineDiv, cur = container.firstChild;
852
853 function rm(node) {
854 var next = node.nextSibling;
855 // Works around a throw-scroll bug in OS X Webkit
856 if (webkit && mac && cm.display.currentWheelTarget == node)
857 node.style.display = "none";
858 else
859 node.parentNode.removeChild(node);
860 return next;
861 }
862
863 var view = display.view, lineN = display.viewFrom;
864 // Loop over the elements in the view, syncing cur (the DOM nodes
865 // in display.lineDiv) with the view as we go.
866 for (var i = 0; i < view.length; i++) {
867 var lineView = view[i];
868 if (lineView.hidden) {
869 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
870 var node = buildLineElement(cm, lineView, lineN, dims);
871 container.insertBefore(node, cur);
872 } else { // Already drawn
873 while (cur != lineView.node) cur = rm(cur);
874 var updateNumber = lineNumbers && updateNumbersFrom != null &&
875 updateNumbersFrom <= lineN && lineView.lineNumber;
876 if (lineView.changes) {
877 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
878 updateLineForChanges(cm, lineView, lineN, dims);
879 }
880 if (updateNumber) {
881 removeChildren(lineView.lineNumber);
882 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
883 }
884 cur = lineView.node.nextSibling;
885 }
886 lineN += lineView.size;
887 }
888 while (cur) cur = rm(cur);
889 }
890
891 // When an aspect of a line changes, a string is added to
892 // lineView.changes. This updates the relevant part of the line's
893 // DOM structure.
894 function updateLineForChanges(cm, lineView, lineN, dims) {
895 for (var j = 0; j < lineView.changes.length; j++) {
896 var type = lineView.changes[j];
897 if (type == "text") updateLineText(cm, lineView);
898 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
899 else if (type == "class") updateLineClasses(lineView);
900 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
901 }
902 lineView.changes = null;
903 }
904
905 // Lines with gutter elements, widgets or a background class need to
906 // be wrapped, and have the extra elements added to the wrapper div
907 function ensureLineWrapped(lineView) {
908 if (lineView.node == lineView.text) {
909 lineView.node = elt("div", null, null, "position: relative");
910 if (lineView.text.parentNode)
911 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
912 lineView.node.appendChild(lineView.text);
913 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
914 }
915 return lineView.node;
916 }
917
918 function updateLineBackground(lineView) {
919 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
920 if (cls) cls += " CodeMirror-linebackground";
921 if (lineView.background) {
922 if (cls) lineView.background.className = cls;
923 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
924 } else if (cls) {
925 var wrap = ensureLineWrapped(lineView);
926 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
927 }
928 }
929
930 // Wrapper around buildLineContent which will reuse the structure
931 // in display.externalMeasured when possible.
932 function getLineContent(cm, lineView) {
933 var ext = cm.display.externalMeasured;
934 if (ext && ext.line == lineView.line) {
935 cm.display.externalMeasured = null;
936 lineView.measure = ext.measure;
937 return ext.built;
938 }
939 return buildLineContent(cm, lineView);
940 }
941
942 // Redraw the line's text. Interacts with the background and text
943 // classes because the mode may output tokens that influence these
944 // classes.
945 function updateLineText(cm, lineView) {
946 var cls = lineView.text.className;
947 var built = getLineContent(cm, lineView);
948 if (lineView.text == lineView.node) lineView.node = built.pre;
949 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
950 lineView.text = built.pre;
951 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
952 lineView.bgClass = built.bgClass;
953 lineView.textClass = built.textClass;
954 updateLineClasses(lineView);
955 } else if (cls) {
956 lineView.text.className = cls;
957 }
958 }
959
960 function updateLineClasses(lineView) {
961 updateLineBackground(lineView);
962 if (lineView.line.wrapClass)
963 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
964 else if (lineView.node != lineView.text)
965 lineView.node.className = "";
966 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
967 lineView.text.className = textClass || "";
968 }
969
970 function updateLineGutter(cm, lineView, lineN, dims) {
971 if (lineView.gutter) {
972 lineView.node.removeChild(lineView.gutter);
973 lineView.gutter = null;
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 }
986 var markers = lineView.line.gutterMarkers;
987 if (cm.options.lineNumbers || markers) {
988 var wrap = ensureLineWrapped(lineView);
989 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
990 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px");
991 cm.display.input.setUneditable(gutterWrap);
992 wrap.insertBefore(gutterWrap, lineView.text);
993 if (lineView.line.gutterClass)
994 gutterWrap.className += " " + lineView.line.gutterClass;
995 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
996 lineView.lineNumber = gutterWrap.appendChild(
997 elt("div", lineNumberFor(cm.options, lineN),
998 "CodeMirror-linenumber CodeMirror-gutter-elt",
999 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
1000 + cm.display.lineNumInnerWidth + "px"));
1001 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
1002 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
1003 if (found)
1004 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
1005 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
1006 }
1007 }
1008 }
1009
1010 function updateLineWidgets(cm, lineView, dims) {
1011 if (lineView.alignable) lineView.alignable = null;
1012 for (var node = lineView.node.firstChild, next; node; node = next) {
1013 var next = node.nextSibling;
1014 if (node.className == "CodeMirror-linewidget")
1015 lineView.node.removeChild(node);
1016 }
1017 insertLineWidgets(cm, lineView, dims);
1018 }
1019
1020 // Build a line's DOM representation from scratch
1021 function buildLineElement(cm, lineView, lineN, dims) {
1022 var built = getLineContent(cm, lineView);
1023 lineView.text = lineView.node = built.pre;
1024 if (built.bgClass) lineView.bgClass = built.bgClass;
1025 if (built.textClass) lineView.textClass = built.textClass;
1026
1027 updateLineClasses(lineView);
1028 updateLineGutter(cm, lineView, lineN, dims);
1029 insertLineWidgets(cm, lineView, dims);
1030 return lineView.node;
1031 }
1032
1033 // A lineView may contain multiple logical lines (when merged by
1034 // collapsed spans). The widgets for all of them need to be drawn.
1035 function insertLineWidgets(cm, lineView, dims) {
1036 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1037 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1038 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1039 }
1040
1041 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1042 if (!line.widgets) return;
1043 var wrap = ensureLineWrapped(lineView);
1044 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1045 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1046 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1047 positionLineWidget(widget, node, lineView, dims);
1048 cm.display.input.setUneditable(node);
1049 if (allowAbove && widget.above)
1050 wrap.insertBefore(node, lineView.gutter || lineView.text);
1051 else
1052 wrap.appendChild(node);
1053 signalLater(widget, "redraw");
1054 }
1055 }
1056
1057 function positionLineWidget(widget, node, lineView, dims) {
1058 if (widget.noHScroll) {
1059 (lineView.alignable || (lineView.alignable = [])).push(node);
1060 var width = dims.wrapperWidth;
1061 node.style.left = dims.fixedPos + "px";
1062 if (!widget.coverGutter) {
1063 width -= dims.gutterTotalWidth;
1064 node.style.paddingLeft = dims.gutterTotalWidth + "px";
1065 }
1066 node.style.width = width + "px";
1067 }
1068 if (widget.coverGutter) {
1069 node.style.zIndex = 5;
1070 node.style.position = "relative";
1071 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1072 }
1073 }
1074
1075 // POSITION OBJECT
1076
1077 // A Pos instance represents a position within the text.
1078 var Pos = CodeMirror.Pos = function(line, ch) {
1079 if (!(this instanceof Pos)) return new Pos(line, ch);
1080 this.line = line; this.ch = ch;
1081 };
1082
1083 // Compare two positions, return 0 if they are the same, a negative
1084 // number when a is less, and a positive number otherwise.
1085 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1086
1087 function copyPos(x) {return Pos(x.line, x.ch);}
1088 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1089 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1090
1091 // INPUT HANDLING
1092
1093 function ensureFocus(cm) {
1094 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1095 }
1096
1097 // This will be set to an array of strings when copying, so that,
1098 // when pasting, we know what kind of selections the copied text
1099 // was made out of.
1100 var lastCopied = null;
1101
1102 function applyTextInput(cm, inserted, deleted, sel, origin) {
1103 var doc = cm.doc;
1104 cm.display.shift = false;
1105 if (!sel) sel = doc.sel;
1106
1107 var paste = cm.state.pasteIncoming || origin == "paste";
1108 var textLines = doc.splitLines(inserted), multiPaste = null;
1109 // When pasing N lines into N selections, insert one line per selection
1110 if (paste && sel.ranges.length > 1) {
1111 if (lastCopied && lastCopied.join("\n") == inserted) {
1112 if (sel.ranges.length % lastCopied.length == 0) {
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) {
1118 multiPaste = map(textLines, function(l) { return [l]; });
1119 }
1120 }
1121
1122 // Normal behavior is to insert the new text into every selection
1123 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1124 var range = sel.ranges[i];
1125 var from = range.from(), to = range.to();
1126 if (range.empty()) {
1127 if (deleted && deleted > 0) // Handle deletion
1128 from = Pos(from.line, from.ch - deleted);
1129 else if (cm.state.overwrite && !paste) // Handle overwrite
1130 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1131 }
1132 var updateInput = cm.curOp.updateInput;
1133 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1134 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1135 makeChange(cm.doc, changeEvent);
1136 signalLater(cm, "inputRead", cm, changeEvent);
1137 }
1138 if (inserted && !paste)
1139 triggerElectric(cm, inserted);
1140
1141 ensureCursorVisible(cm);
1142 cm.curOp.updateInput = updateInput;
1143 cm.curOp.typing = true;
1144 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1145 }
1146
1147 function handlePaste(e, cm) {
1148 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1149 if (pasted) {
1150 e.preventDefault();
1151 if (!cm.isReadOnly() && !cm.options.disableInput)
1152 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
1153 return true;
1154 }
1155 }
1156
1157 function triggerElectric(cm, inserted) {
1158 // When an 'electric' character is inserted, immediately trigger a reindent
1159 if (!cm.options.electricChars || !cm.options.smartIndent) return;
1160 var sel = cm.doc.sel;
1161
1162 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1163 var range = sel.ranges[i];
1164 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
1165 var mode = cm.getModeAt(range.head);
1166 var indented = false;
1167 if (mode.electricChars) {
1168 for (var j = 0; j < mode.electricChars.length; j++)
1169 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1170 indented = indentLine(cm, range.head.line, "smart");
1171 break;
1172 }
1173 } else if (mode.electricInput) {
1174 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
1175 indented = indentLine(cm, range.head.line, "smart");
1176 }
1177 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1178 }
1179 }
1180
1181 function copyableRanges(cm) {
1182 var text = [], ranges = [];
1183 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1184 var line = cm.doc.sel.ranges[i].head.line;
1185 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1186 ranges.push(lineRange);
1187 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1188 }
1189 return {text: text, ranges: ranges};
1190 }
1191
1192 function disableBrowserMagic(field) {
1193 field.setAttribute("autocorrect", "off");
1194 field.setAttribute("autocapitalize", "off");
1195 field.setAttribute("spellcheck", "false");
1196 }
1197
1198 // TEXTAREA INPUT STYLE
1199
1200 function TextareaInput(cm) {
1201 this.cm = cm;
1202 // See input.poll and input.reset
1203 this.prevInput = "";
1204
1205 // Flag that indicates whether we expect input to appear real soon
1206 // now (after some event like 'keypress' or 'input') and are
1207 // polling intensively.
1208 this.pollingFast = false;
1209 // Self-resetting timeout for the poller
1210 this.polling = new Delayed();
1211 // Tracks when input.reset has punted to just putting a short
1212 // string into the textarea instead of the full selection.
1213 this.inaccurateSelection = false;
1214 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1215 this.hasSelection = false;
1216 this.composing = null;
1217 };
1218
1219 function hiddenTextarea() {
1220 var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1221 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1222 // The textarea is kept positioned near the cursor to prevent the
1223 // fact that it'll be scrolled into view on input from scrolling
1224 // our fake cursor out of view. On webkit, when wrap=off, paste is
1225 // very slow. So make the area wide instead.
1226 if (webkit) te.style.width = "1000px";
1227 else te.setAttribute("wrap", "off");
1228 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1229 if (ios) te.style.border = "1px solid black";
1230 disableBrowserMagic(te);
1231 return div;
1232 }
1233
1234 TextareaInput.prototype = copyObj({
1235 init: function(display) {
1236 var input = this, cm = this.cm;
1237
1238 // Wraps and hides input textarea
1239 var div = this.wrapper = hiddenTextarea();
1240 // The semihidden textarea that is focused when the editor is
1241 // focused, and receives input.
1242 var te = this.textarea = div.firstChild;
1243 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1244
1245 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1246 if (ios) te.style.width = "0px";
1247
1248 on(te, "input", function() {
1249 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1250 input.poll();
1251 });
1252
1253 on(te, "paste", function(e) {
1254 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
1255
1256 cm.state.pasteIncoming = true;
1257 input.fastPoll();
1258 });
1259
1260 function prepareCopyCut(e) {
1261 if (signalDOMEvent(cm, e)) return
1262 if (cm.somethingSelected()) {
1263 lastCopied = cm.getSelections();
1264 if (input.inaccurateSelection) {
1265 input.prevInput = "";
1266 input.inaccurateSelection = false;
1267 te.value = lastCopied.join("\n");
1268 selectInput(te);
1269 }
1270 } else if (!cm.options.lineWiseCopyCut) {
1271 return;
1272 } else {
1273 var ranges = copyableRanges(cm);
1274 lastCopied = ranges.text;
1275 if (e.type == "cut") {
1276 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1277 } else {
1278 input.prevInput = "";
1279 te.value = ranges.text.join("\n");
1280 selectInput(te);
1281 }
1282 }
1283 if (e.type == "cut") cm.state.cutIncoming = true;
1284 }
1285 on(te, "cut", prepareCopyCut);
1286 on(te, "copy", prepareCopyCut);
1287
1288 on(display.scroller, "paste", function(e) {
1289 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
1290 cm.state.pasteIncoming = true;
1291 input.focus();
1292 });
1293
1294 // Prevent normal selection in the editor (we handle our own)
1295 on(display.lineSpace, "selectstart", function(e) {
1296 if (!eventInWidget(display, e)) e_preventDefault(e);
1297 });
1298
1299 on(te, "compositionstart", function() {
1300 var start = cm.getCursor("from");
1301 if (input.composing) input.composing.range.clear()
1302 input.composing = {
1303 start: start,
1304 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1305 };
1306 });
1307 on(te, "compositionend", function() {
1308 if (input.composing) {
1309 input.poll();
1310 input.composing.range.clear();
1311 input.composing = null;
1312 }
1313 });
1314 },
1315
1316 prepareSelection: function() {
1317 // Redraw the selection and/or cursor
1318 var cm = this.cm, display = cm.display, doc = cm.doc;
1319 var result = prepareSelection(cm);
1320
1321 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1322 if (cm.options.moveInputWithCursor) {
1323 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1324 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1325 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1326 headPos.top + lineOff.top - wrapOff.top));
1327 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1328 headPos.left + lineOff.left - wrapOff.left));
1329 }
1330
1331 return result;
1332 },
1333
1334 showSelection: function(drawn) {
1335 var cm = this.cm, display = cm.display;
1336 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1337 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1338 if (drawn.teTop != null) {
1339 this.wrapper.style.top = drawn.teTop + "px";
1340 this.wrapper.style.left = drawn.teLeft + "px";
1341 }
1342 },
1343
1344 // Reset the input to correspond to the selection (or to be empty,
1345 // when not typing and nothing is selected)
1346 reset: function(typing) {
1347 if (this.contextMenuPending) return;
1348 var minimal, selected, cm = this.cm, doc = cm.doc;
1349 if (cm.somethingSelected()) {
1350 this.prevInput = "";
1351 var range = doc.sel.primary();
1352 minimal = hasCopyEvent &&
1353 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1354 var content = minimal ? "-" : selected || cm.getSelection();
1355 this.textarea.value = content;
1356 if (cm.state.focused) selectInput(this.textarea);
1357 if (ie && ie_version >= 9) this.hasSelection = content;
1358 } else if (!typing) {
1359 this.prevInput = this.textarea.value = "";
1360 if (ie && ie_version >= 9) this.hasSelection = null;
1361 }
1362 this.inaccurateSelection = minimal;
1363 },
1364
1365 getField: function() { return this.textarea; },
1366
1367 supportsTouch: function() { return false; },
1368
1369 focus: function() {
1370 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1371 try { this.textarea.focus(); }
1372 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
1373 }
1374 },
1375
1376 blur: function() { this.textarea.blur(); },
1377
1378 resetPosition: function() {
1379 this.wrapper.style.top = this.wrapper.style.left = 0;
1380 },
1381
1382 receivedFocus: function() { this.slowPoll(); },
1383
1384 // Poll for input changes, using the normal rate of polling. This
1385 // runs as long as the editor is focused.
1386 slowPoll: function() {
1387 var input = this;
1388 if (input.pollingFast) return;
1389 input.polling.set(this.cm.options.pollInterval, function() {
1390 input.poll();
1391 if (input.cm.state.focused) input.slowPoll();
1392 });
1393 },
1394
1395 // When an event has just come in that is likely to add or change
1396 // something in the input textarea, we poll faster, to ensure that
1397 // the change appears on the screen quickly.
1398 fastPoll: function() {
1399 var missed = false, input = this;
1400 input.pollingFast = true;
1401 function p() {
1402 var changed = input.poll();
1403 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1404 else {input.pollingFast = false; input.slowPoll();}
1405 }
1406 input.polling.set(20, p);
1407 },
1408
1409 // Read input from the textarea, and update the document to match.
1410 // When something is selected, it is present in the textarea, and
1411 // selected (unless it is huge, in which case a placeholder is
1412 // used). When nothing is selected, the cursor sits after previously
1413 // seen text (can be empty), which is stored in prevInput (we must
1414 // not reset the textarea when typing, because that breaks IME).
1415 poll: function() {
1416 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1417 // Since this is called a *lot*, try to bail out as cheaply as
1418 // possible when it is clear that nothing happened. hasSelection
1419 // will be the case when there is a lot of text in the textarea,
1420 // in which case reading its value would be expensive.
1421 if (this.contextMenuPending || !cm.state.focused ||
1422 (hasSelection(input) && !prevInput && !this.composing) ||
1423 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
1424 return false;
1425
1426 var text = input.value;
1427 // If nothing changed, bail.
1428 if (text == prevInput && !cm.somethingSelected()) return false;
1429 // Work around nonsensical selection resetting in IE9/10, and
1430 // inexplicable appearance of private area unicode characters on
1431 // some key combos in Mac (#2689).
1432 if (ie && ie_version >= 9 && this.hasSelection === text ||
1433 mac && /[\uf700-\uf7ff]/.test(text)) {
1434 cm.display.input.reset();
1435 return false;
1436 }
1437
1438 if (cm.doc.sel == cm.display.selForContextMenu) {
1439 var first = text.charCodeAt(0);
1440 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1441 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1442 }
1443 // Find the part of the input that is actually new
1444 var same = 0, l = Math.min(prevInput.length, text.length);
1445 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1446
1447 var self = this;
1448 runInOp(cm, function() {
1449 applyTextInput(cm, text.slice(same), prevInput.length - same,
1450 null, self.composing ? "*compose" : null);
1451
1452 // Don't leave long text in the textarea, since it makes further polling slow
1453 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1454 else self.prevInput = text;
1455
1456 if (self.composing) {
1457 self.composing.range.clear();
1458 self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1459 {className: "CodeMirror-composing"});
1460 }
1461 });
1462 return true;
1463 },
1464
1465 ensurePolled: function() {
1466 if (this.pollingFast && this.poll()) this.pollingFast = false;
1467 },
1468
1469 onKeyPress: function() {
1470 if (ie && ie_version >= 9) this.hasSelection = null;
1471 this.fastPoll();
1472 },
1473
1474 onContextMenu: function(e) {
1475 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1476 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1477 if (!pos || presto) return; // Opera is difficult.
1478
1479 // Reset the current text selection only if the click is done outside of the selection
1480 // and 'resetSelectionOnContextMenu' option is true.
1481 var reset = cm.options.resetSelectionOnContextMenu;
1482 if (reset && cm.doc.sel.contains(pos) == -1)
1483 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1484
1485 var oldCSS = te.style.cssText;
1486 input.wrapper.style.position = "absolute";
1487 te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1488 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
1489 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1490 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1491 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1492 display.input.focus();
1493 if (webkit) window.scrollTo(null, oldScrollY);
1494 display.input.reset();
1495 // Adds "Select all" to context menu in FF
1496 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1497 input.contextMenuPending = true;
1498 display.selForContextMenu = cm.doc.sel;
1499 clearTimeout(display.detectingSelectAll);
1500
1501 // Select-all will be greyed out if there's nothing to select, so
1502 // this adds a zero-width space so that we can later check whether
1503 // it got selected.
1504 function prepareSelectAllHack() {
1505 if (te.selectionStart != null) {
1506 var selected = cm.somethingSelected();
1507 var extval = "\u200b" + (selected ? te.value : "");
1508 te.value = "\u21da"; // Used to catch context-menu undo
1509 te.value = extval;
1510 input.prevInput = selected ? "" : "\u200b";
1511 te.selectionStart = 1; te.selectionEnd = extval.length;
1512 // Re-set this, in case some other handler touched the
1513 // selection in the meantime.
1514 display.selForContextMenu = cm.doc.sel;
1515 }
1516 }
1517 function rehide() {
1518 input.contextMenuPending = false;
1519 input.wrapper.style.position = "relative";
1520 te.style.cssText = oldCSS;
1521 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1522
1523 // Try to detect the user choosing select-all
1524 if (te.selectionStart != null) {
1525 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1526 var i = 0, poll = function() {
1527 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1528 te.selectionEnd > 0 && input.prevInput == "\u200b")
1529 operation(cm, commands.selectAll)(cm);
1530 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1531 else display.input.reset();
1532 };
1533 display.detectingSelectAll = setTimeout(poll, 200);
1534 }
1535 }
1536
1537 if (ie && ie_version >= 9) prepareSelectAllHack();
1538 if (captureRightClick) {
1539 e_stop(e);
1540 var mouseup = function() {
1541 off(window, "mouseup", mouseup);
1542 setTimeout(rehide, 20);
1543 };
1544 on(window, "mouseup", mouseup);
1545 } else {
1546 setTimeout(rehide, 50);
1547 }
1548 },
1549
1550 readOnlyChanged: function(val) {
1551 if (!val) this.reset();
1552 },
1553
1554 setUneditable: nothing,
1555
1556 needsContentAttribute: false
1557 }, TextareaInput.prototype);
1558
1559 // CONTENTEDITABLE INPUT STYLE
1560
1561 function ContentEditableInput(cm) {
1562 this.cm = cm;
1563 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1564 this.polling = new Delayed();
1565 this.gracePeriod = false;
1566 }
1567
1568 ContentEditableInput.prototype = copyObj({
1569 init: function(display) {
1570 var input = this, cm = input.cm;
1571 var div = input.div = display.lineDiv;
1572 disableBrowserMagic(div);
1573
1574 on(div, "paste", function(e) {
1575 if (!signalDOMEvent(cm, e)) handlePaste(e, cm);
1576 })
1577
1578 on(div, "compositionstart", function(e) {
1579 var data = e.data;
1580 input.composing = {sel: cm.doc.sel, data: data, startData: data};
1581 if (!data) return;
1582 var prim = cm.doc.sel.primary();
1583 var line = cm.getLine(prim.head.line);
1584 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1585 if (found > -1 && found <= prim.head.ch)
1586 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1587 Pos(prim.head.line, found + data.length));
1588 });
1589 on(div, "compositionupdate", function(e) {
1590 input.composing.data = e.data;
1591 });
1592 on(div, "compositionend", function(e) {
1593 var ours = input.composing;
1594 if (!ours) return;
1595 if (e.data != ours.startData && !/\u200b/.test(e.data))
1596 ours.data = e.data;
1597 // Need a small delay to prevent other code (input event,
1598 // selection polling) from doing damage when fired right after
1599 // compositionend.
1600 setTimeout(function() {
1601 if (!ours.handled)
1602 input.applyComposition(ours);
1603 if (input.composing == ours)
1604 input.composing = null;
1605 }, 50);
1606 });
1607
1608 on(div, "touchstart", function() {
1609 input.forceCompositionEnd();
1610 });
1611
1612 on(div, "input", function() {
1613 if (input.composing) return;
1614 if (cm.isReadOnly() || !input.pollContent())
1615 runInOp(input.cm, function() {regChange(cm);});
1616 });
1617
1618 function onCopyCut(e) {
1619 if (signalDOMEvent(cm, e)) return
1620 if (cm.somethingSelected()) {
1621 lastCopied = cm.getSelections();
1622 if (e.type == "cut") cm.replaceSelection("", null, "cut");
1623 } else if (!cm.options.lineWiseCopyCut) {
1624 return;
1625 } else {
1626 var ranges = copyableRanges(cm);
1627 lastCopied = ranges.text;
1628 if (e.type == "cut") {
1629 cm.operation(function() {
1630 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1631 cm.replaceSelection("", null, "cut");
1632 });
1633 }
1634 }
1635 // iOS exposes the clipboard API, but seems to discard content inserted into it
1636 if (e.clipboardData && !ios) {
1637 e.preventDefault();
1638 e.clipboardData.clearData();
1639 e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1640 } else {
1641 // Old-fashioned briefly-focus-a-textarea hack
1642 var kludge = hiddenTextarea(), te = kludge.firstChild;
1643 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1644 te.value = lastCopied.join("\n");
1645 var hadFocus = document.activeElement;
1646 selectInput(te);
1647 setTimeout(function() {
1648 cm.display.lineSpace.removeChild(kludge);
1649 hadFocus.focus();
1650 }, 50);
1651 }
1652 }
1653 on(div, "copy", onCopyCut);
1654 on(div, "cut", onCopyCut);
1655 },
1656
1657 prepareSelection: function() {
1658 var result = prepareSelection(this.cm, false);
1659 result.focus = this.cm.state.focused;
1660 return result;
1661 },
1662
1663 showSelection: function(info) {
1664 if (!info || !this.cm.display.view.length) return;
1665 if (info.focus) this.showPrimarySelection();
1666 this.showMultipleSelections(info);
1667 },
1668
1669 showPrimarySelection: function() {
1670 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1671 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1672 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1673 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1674 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1675 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1676 return;
1677
1678 var start = posToDOM(this.cm, prim.from());
1679 var end = posToDOM(this.cm, prim.to());
1680 if (!start && !end) return;
1681
1682 var view = this.cm.display.view;
1683 var old = sel.rangeCount && sel.getRangeAt(0);
1684 if (!start) {
1685 start = {node: view[0].measure.map[2], offset: 0};
1686 } else if (!end) { // FIXME dangerously hacky
1687 var measure = view[view.length - 1].measure;
1688 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1689 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1690 }
1691
1692 try { var rng = range(start.node, start.offset, end.offset, end.node); }
1693 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
1694 if (rng) {
1695 if (!gecko && this.cm.state.focused) {
1696 sel.collapse(start.node, start.offset);
1697 if (!rng.collapsed) sel.addRange(rng);
1698 } else {
1699 sel.removeAllRanges();
1700 sel.addRange(rng);
1701 }
1702 if (old && sel.anchorNode == null) sel.addRange(old);
1703 else if (gecko) this.startGracePeriod();
1704 }
1705 this.rememberSelection();
1706 },
1707
1708 startGracePeriod: function() {
1709 var input = this;
1710 clearTimeout(this.gracePeriod);
1711 this.gracePeriod = setTimeout(function() {
1712 input.gracePeriod = false;
1713 if (input.selectionChanged())
1714 input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1715 }, 20);
1716 },
1717
1718 showMultipleSelections: function(info) {
1719 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1720 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1721 },
1722
1723 rememberSelection: function() {
1724 var sel = window.getSelection();
1725 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1726 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1727 },
1728
1729 selectionInEditor: function() {
1730 var sel = window.getSelection();
1731 if (!sel.rangeCount) return false;
1732 var node = sel.getRangeAt(0).commonAncestorContainer;
1733 return contains(this.div, node);
1734 },
1735
1736 focus: function() {
1737 if (this.cm.options.readOnly != "nocursor") this.div.focus();
1738 },
1739 blur: function() { this.div.blur(); },
1740 getField: function() { return this.div; },
1741
1742 supportsTouch: function() { return true; },
1743
1744 receivedFocus: function() {
1745 var input = this;
1746 if (this.selectionInEditor())
1747 this.pollSelection();
1748 else
1749 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1750
1751 function poll() {
1752 if (input.cm.state.focused) {
1753 input.pollSelection();
1754 input.polling.set(input.cm.options.pollInterval, poll);
1755 }
1756 }
1757 this.polling.set(this.cm.options.pollInterval, poll);
1758 },
1759
1760 selectionChanged: function() {
1761 var sel = window.getSelection();
1762 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1763 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1764 },
1765
1766 pollSelection: function() {
1767 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1768 var sel = window.getSelection(), cm = this.cm;
1769 this.rememberSelection();
1770 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1771 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1772 if (anchor && head) runInOp(cm, function() {
1773 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1774 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1775 });
1776 }
1777 },
1778
1779 pollContent: function() {
1780 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1781 var from = sel.from(), to = sel.to();
1782 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1783
1784 var fromIndex;
1785 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
1786 var fromLine = lineNo(display.view[0].line);
1787 var fromNode = display.view[0].node;
1788 } else {
1789 var fromLine = lineNo(display.view[fromIndex].line);
1790 var fromNode = display.view[fromIndex - 1].node.nextSibling;
1791 }
1792 var toIndex = findViewIndex(cm, to.line);
1793 if (toIndex == display.view.length - 1) {
1794 var toLine = display.viewTo - 1;
1795 var toNode = display.lineDiv.lastChild;
1796 } else {
1797 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1798 var toNode = display.view[toIndex + 1].node.previousSibling;
1799 }
1800
1801 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1802 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1803 while (newText.length > 1 && oldText.length > 1) {
1804 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1805 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1806 else break;
1807 }
1808
1809 var cutFront = 0, cutEnd = 0;
1810 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1811 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1812 ++cutFront;
1813 var newBot = lst(newText), oldBot = lst(oldText);
1814 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1815 oldBot.length - (oldText.length == 1 ? cutFront : 0));
1816 while (cutEnd < maxCutEnd &&
1817 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1818 ++cutEnd;
1819
1820 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1821 newText[0] = newText[0].slice(cutFront);
1822
1823 var chFrom = Pos(fromLine, cutFront);
1824 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1825 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1826 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1827 return true;
1828 }
1829 },
1830
1831 ensurePolled: function() {
1832 this.forceCompositionEnd();
1833 },
1834 reset: function() {
1835 this.forceCompositionEnd();
1836 },
1837 forceCompositionEnd: function() {
1838 if (!this.composing || this.composing.handled) return;
1839 this.applyComposition(this.composing);
1840 this.composing.handled = true;
1841 this.div.blur();
1842 this.div.focus();
1843 },
1844 applyComposition: function(composing) {
1845 if (this.cm.isReadOnly())
1846 operation(this.cm, regChange)(this.cm)
1847 else if (composing.data && composing.data != composing.startData)
1848 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1849 },
1850
1851 setUneditable: function(node) {
1852 node.contentEditable = "false"
1853 },
1854
1855 onKeyPress: function(e) {
1856 e.preventDefault();
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")
1863 },
1864
1865 onContextMenu: nothing,
1866 resetPosition: nothing,
1867
1868 needsContentAttribute: true
1869 }, ContentEditableInput.prototype);
1870
1871 function posToDOM(cm, pos) {
1872 var view = findViewForLine(cm, pos.line);
1873 if (!view || view.hidden) return null;
1874 var line = getLine(cm.doc, pos.line);
1875 var info = mapFromLineView(view, line, pos.line);
1876
1877 var order = getOrder(line), side = "left";
1878 if (order) {
1879 var partPos = getBidiPartAt(order, pos.ch);
1880 side = partPos % 2 ? "right" : "left";
1881 }
1882 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1883 result.offset = result.collapse == "right" ? result.end : result.start;
1884 return result;
1885 }
1886
1887 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1888
1889 function domToPos(cm, node, offset) {
1890 var lineNode;
1891 if (node == cm.display.lineDiv) {
1892 lineNode = cm.display.lineDiv.childNodes[offset];
1893 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1894 node = null; offset = 0;
1895 } else {
1896 for (lineNode = node;; lineNode = lineNode.parentNode) {
1897 if (!lineNode || lineNode == cm.display.lineDiv) return null;
1898 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1899 }
1900 }
1901 for (var i = 0; i < cm.display.view.length; i++) {
1902 var lineView = cm.display.view[i];
1903 if (lineView.node == lineNode)
1904 return locateNodeInLineView(lineView, node, offset);
1905 }
1906 }
1907
1908 function locateNodeInLineView(lineView, node, offset) {
1909 var wrapper = lineView.text.firstChild, bad = false;
1910 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1911 if (node == wrapper) {
1912 bad = true;
1913 node = wrapper.childNodes[offset];
1914 offset = 0;
1915 if (!node) {
1916 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1917 return badPos(Pos(lineNo(line), line.text.length), bad);
1918 }
1919 }
1920
1921 var textNode = node.nodeType == 3 ? node : null, topNode = node;
1922 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1923 textNode = node.firstChild;
1924 if (offset) offset = textNode.nodeValue.length;
1925 }
1926 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1927 var measure = lineView.measure, maps = measure.maps;
1928
1929 function find(textNode, topNode, offset) {
1930 for (var i = -1; i < (maps ? maps.length : 0); i++) {
1931 var map = i < 0 ? measure.map : maps[i];
1932 for (var j = 0; j < map.length; j += 3) {
1933 var curNode = map[j + 2];
1934 if (curNode == textNode || curNode == topNode) {
1935 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1936 var ch = map[j] + offset;
1937 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1938 return Pos(line, ch);
1939 }
1940 }
1941 }
1942 }
1943 var found = find(textNode, topNode, offset);
1944 if (found) return badPos(found, bad);
1945
1946 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1947 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1948 found = find(after, after.firstChild, 0);
1949 if (found)
1950 return badPos(Pos(found.line, found.ch - dist), bad);
1951 else
1952 dist += after.textContent.length;
1953 }
1954 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
1955 found = find(before, before.firstChild, -1);
1956 if (found)
1957 return badPos(Pos(found.line, found.ch + dist), bad);
1958 else
1959 dist += after.textContent.length;
1960 }
1961 }
1962
1963 function domTextBetween(cm, from, to, fromLine, toLine) {
1964 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
1965 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1966 function walk(node) {
1967 if (node.nodeType == 1) {
1968 var cmText = node.getAttribute("cm-text");
1969 if (cmText != null) {
1970 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1971 text += cmText;
1972 return;
1973 }
1974 var markerID = node.getAttribute("cm-marker"), range;
1975 if (markerID) {
1976 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1977 if (found.length && (range = found[0].find()))
1978 text += getBetween(cm.doc, range.from, range.to).join(lineSep);
1979 return;
1980 }
1981 if (node.getAttribute("contenteditable") == "false") return;
1982 for (var i = 0; i < node.childNodes.length; i++)
1983 walk(node.childNodes[i]);
1984 if (/^(pre|div|p)$/i.test(node.nodeName))
1985 closing = true;
1986 } else if (node.nodeType == 3) {
1987 var val = node.nodeValue;
1988 if (!val) return;
1989 if (closing) {
1990 text += lineSep;
1991 closing = false;
1992 }
1993 text += val;
1994 }
1995 }
1996 for (;;) {
1997 walk(from);
1998 if (from == to) break;
1999 from = from.nextSibling;
2000 }
2001 return text;
2002 }
2003
2004 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
2005
2006 // SELECTION / CURSOR
2007
2008 // Selection objects are immutable. A new one is created every time
2009 // the selection changes. A selection is one or more non-overlapping
2010 // (and non-touching) ranges, sorted, and an integer that indicates
2011 // which one is the primary selection (the one that's scrolled into
2012 // view, that getCursor returns, etc).
2013 function Selection(ranges, primIndex) {
2014 this.ranges = ranges;
2015 this.primIndex = primIndex;
2016 }
2017
2018 Selection.prototype = {
2019 primary: function() { return this.ranges[this.primIndex]; },
2020 equals: function(other) {
2021 if (other == this) return true;
2022 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
2023 for (var i = 0; i < this.ranges.length; i++) {
2024 var here = this.ranges[i], there = other.ranges[i];
2025 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
2026 }
2027 return true;
2028 },
2029 deepCopy: function() {
2030 for (var out = [], i = 0; i < this.ranges.length; i++)
2031 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
2032 return new Selection(out, this.primIndex);
2033 },
2034 somethingSelected: function() {
2035 for (var i = 0; i < this.ranges.length; i++)
2036 if (!this.ranges[i].empty()) return true;
2037 return false;
2038 },
2039 contains: function(pos, end) {
2040 if (!end) end = pos;
2041 for (var i = 0; i < this.ranges.length; i++) {
2042 var range = this.ranges[i];
2043 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2044 return i;
2045 }
2046 return -1;
2047 }
2048 };
2049
2050 function Range(anchor, head) {
2051 this.anchor = anchor; this.head = head;
2052 }
2053
2054 Range.prototype = {
2055 from: function() { return minPos(this.anchor, this.head); },
2056 to: function() { return maxPos(this.anchor, this.head); },
2057 empty: function() {
2058 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2059 }
2060 };
2061
2062 // Take an unsorted, potentially overlapping set of ranges, and
2063 // build a selection out of it. 'Consumes' ranges array (modifying
2064 // it).
2065 function normalizeSelection(ranges, primIndex) {
2066 var prim = ranges[primIndex];
2067 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2068 primIndex = indexOf(ranges, prim);
2069 for (var i = 1; i < ranges.length; i++) {
2070 var cur = ranges[i], prev = ranges[i - 1];
2071 if (cmp(prev.to(), cur.from()) >= 0) {
2072 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2073 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2074 if (i <= primIndex) --primIndex;
2075 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2076 }
2077 }
2078 return new Selection(ranges, primIndex);
2079 }
2080
2081 function simpleSelection(anchor, head) {
2082 return new Selection([new Range(anchor, head || anchor)], 0);
2083 }
2084
2085 // Most of the external API clips given positions to make sure they
2086 // actually exist within the document.
2087 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2088 function clipPos(doc, pos) {
2089 if (pos.line < doc.first) return Pos(doc.first, 0);
2090 var last = doc.first + doc.size - 1;
2091 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2092 return clipToLen(pos, getLine(doc, pos.line).text.length);
2093 }
2094 function clipToLen(pos, linelen) {
2095 var ch = pos.ch;
2096 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2097 else if (ch < 0) return Pos(pos.line, 0);
2098 else return pos;
2099 }
2100 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2101 function clipPosArray(doc, array) {
2102 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2103 return out;
2104 }
2105
2106 // SELECTION UPDATES
2107
2108 // The 'scroll' parameter given to many of these indicated whether
2109 // the new cursor position should be scrolled into view after
2110 // modifying the selection.
2111
2112 // If shift is held or the extend flag is set, extends a range to
2113 // include a given position (and optionally a second position).
2114 // Otherwise, simply returns the range between the given positions.
2115 // Used for cursor motion and such.
2116 function extendRange(doc, range, head, other) {
2117 if (doc.cm && doc.cm.display.shift || doc.extend) {
2118 var anchor = range.anchor;
2119 if (other) {
2120 var posBefore = cmp(head, anchor) < 0;
2121 if (posBefore != (cmp(other, anchor) < 0)) {
2122 anchor = head;
2123 head = other;
2124 } else if (posBefore != (cmp(head, other) < 0)) {
2125 head = other;
2126 }
2127 }
2128 return new Range(anchor, head);
2129 } else {
2130 return new Range(other || head, head);
2131 }
2132 }
2133
2134 // Extend the primary selection range, discard the rest.
2135 function extendSelection(doc, head, other, options) {
2136 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2137 }
2138
2139 // Extend all selections (pos is an array of selections with length
2140 // equal the number of selections)
2141 function extendSelections(doc, heads, options) {
2142 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2143 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2144 var newSel = normalizeSelection(out, doc.sel.primIndex);
2145 setSelection(doc, newSel, options);
2146 }
2147
2148 // Updates a single range in the selection.
2149 function replaceOneSelection(doc, i, range, options) {
2150 var ranges = doc.sel.ranges.slice(0);
2151 ranges[i] = range;
2152 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2153 }
2154
2155 // Reset the selection to a single range.
2156 function setSimpleSelection(doc, anchor, head, options) {
2157 setSelection(doc, simpleSelection(anchor, head), options);
2158 }
2159
2160 // Give beforeSelectionChange handlers a change to influence a
2161 // selection update.
2162 function filterSelectionChange(doc, sel, options) {
2163 var obj = {
2164 ranges: sel.ranges,
2165 update: function(ranges) {
2166 this.ranges = [];
2167 for (var i = 0; i < ranges.length; i++)
2168 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2169 clipPos(doc, ranges[i].head));
2170 },
2171 origin: options && options.origin
2172 };
2173 signal(doc, "beforeSelectionChange", doc, obj);
2174 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2175 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2176 else return sel;
2177 }
2178
2179 function setSelectionReplaceHistory(doc, sel, options) {
2180 var done = doc.history.done, last = lst(done);
2181 if (last && last.ranges) {
2182 done[done.length - 1] = sel;
2183 setSelectionNoUndo(doc, sel, options);
2184 } else {
2185 setSelection(doc, sel, options);
2186 }
2187 }
2188
2189 // Set a new selection.
2190 function setSelection(doc, sel, options) {
2191 setSelectionNoUndo(doc, sel, options);
2192 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2193 }
2194
2195 function setSelectionNoUndo(doc, sel, options) {
2196 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2197 sel = filterSelectionChange(doc, sel, options);
2198
2199 var bias = options && options.bias ||
2200 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2201 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2202
2203 if (!(options && options.scroll === false) && doc.cm)
2204 ensureCursorVisible(doc.cm);
2205 }
2206
2207 function setSelectionInner(doc, sel) {
2208 if (sel.equals(doc.sel)) return;
2209
2210 doc.sel = sel;
2211
2212 if (doc.cm) {
2213 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2214 signalCursorActivity(doc.cm);
2215 }
2216 signalLater(doc, "cursorActivity", doc);
2217 }
2218
2219 // Verify that the selection does not partially select any atomic
2220 // marked ranges.
2221 function reCheckSelection(doc) {
2222 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
2223 }
2224
2225 // Return a selection that does not partially select any atomic
2226 // ranges.
2227 function skipAtomicInSelection(doc, sel, bias, mayClear) {
2228 var out;
2229 for (var i = 0; i < sel.ranges.length; i++) {
2230 var range = sel.ranges[i];
2231 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
2232 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
2233 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
2234 if (out || newAnchor != range.anchor || newHead != range.head) {
2235 if (!out) out = sel.ranges.slice(0, i);
2236 out[i] = new Range(newAnchor, newHead);
2237 }
2238 }
2239 return out ? normalizeSelection(out, sel.primIndex) : sel;
2240 }
2241
2242 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
2243 var line = getLine(doc, pos.line);
2244 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
2245 var sp = line.markedSpans[i], m = sp.marker;
2246 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
2247 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
2248 if (mayClear) {
2249 signal(m, "beforeCursorEnter");
2250 if (m.explicitlyCleared) {
2251 if (!line.markedSpans) break;
2252 else {--i; continue;}
2253 }
2254 }
2255 if (!m.atomic) continue;
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);
2295 }
2296 }
2297
2298 // SELECTION DRAWING
2299
2300 function updateSelection(cm) {
2301 cm.display.input.showSelection(cm.display.input.prepareSelection());
2302 }
2303
2304 function prepareSelection(cm, primary) {
2305 var doc = cm.doc, result = {};
2306 var curFragment = result.cursors = document.createDocumentFragment();
2307 var selFragment = result.selection = document.createDocumentFragment();
2308
2309 for (var i = 0; i < doc.sel.ranges.length; i++) {
2310 if (primary === false && i == doc.sel.primIndex) continue;
2311 var range = doc.sel.ranges[i];
2312 var collapsed = range.empty();
2313 if (collapsed || cm.options.showCursorWhenSelecting)
2314 drawSelectionCursor(cm, range.head, curFragment);
2315 if (!collapsed)
2316 drawSelectionRange(cm, range, selFragment);
2317 }
2318 return result;
2319 }
2320
2321 // Draws a cursor for the given range
2322 function drawSelectionCursor(cm, head, output) {
2323 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2324
2325 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2326 cursor.style.left = pos.left + "px";
2327 cursor.style.top = pos.top + "px";
2328 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2329
2330 if (pos.other) {
2331 // Secondary cursor, shown when on a 'jump' in bi-directional text
2332 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2333 otherCursor.style.display = "";
2334 otherCursor.style.left = pos.other.left + "px";
2335 otherCursor.style.top = pos.other.top + "px";
2336 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2337 }
2338 }
2339
2340 // Draws the given range as a highlighted selection
2341 function drawSelectionRange(cm, range, output) {
2342 var display = cm.display, doc = cm.doc;
2343 var fragment = document.createDocumentFragment();
2344 var padding = paddingH(cm.display), leftSide = padding.left;
2345 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2346
2347 function add(left, top, width, bottom) {
2348 if (top < 0) top = 0;
2349 top = Math.round(top);
2350 bottom = Math.round(bottom);
2351 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2352 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
2353 "px; height: " + (bottom - top) + "px"));
2354 }
2355
2356 function drawForLine(line, fromArg, toArg) {
2357 var lineObj = getLine(doc, line);
2358 var lineLen = lineObj.text.length;
2359 var start, end;
2360 function coords(ch, bias) {
2361 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2362 }
2363
2364 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
2365 var leftPos = coords(from, "left"), rightPos, left, right;
2366 if (from == to) {
2367 rightPos = leftPos;
2368 left = right = leftPos.left;
2369 } else {
2370 rightPos = coords(to - 1, "right");
2371 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2372 left = leftPos.left;
2373 right = rightPos.right;
2374 }
2375 if (fromArg == null && from == 0) left = leftSide;
2376 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2377 add(left, leftPos.top, null, leftPos.bottom);
2378 left = leftSide;
2379 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2380 }
2381 if (toArg == null && to == lineLen) right = rightSide;
2382 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2383 start = leftPos;
2384 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2385 end = rightPos;
2386 if (left < leftSide + 1) left = leftSide;
2387 add(left, rightPos.top, right - left, rightPos.bottom);
2388 });
2389 return {start: start, end: end};
2390 }
2391
2392 var sFrom = range.from(), sTo = range.to();
2393 if (sFrom.line == sTo.line) {
2394 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2395 } else {
2396 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2397 var singleVLine = visualLine(fromLine) == visualLine(toLine);
2398 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2399 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2400 if (singleVLine) {
2401 if (leftEnd.top < rightStart.top - 2) {
2402 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2403 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2404 } else {
2405 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2406 }
2407 }
2408 if (leftEnd.bottom < rightStart.top)
2409 add(leftSide, leftEnd.bottom, null, rightStart.top);
2410 }
2411
2412 output.appendChild(fragment);
2413 }
2414
2415 // Cursor-blinking
2416 function restartBlink(cm) {
2417 if (!cm.state.focused) return;
2418 var display = cm.display;
2419 clearInterval(display.blinker);
2420 var on = true;
2421 display.cursorDiv.style.visibility = "";
2422 if (cm.options.cursorBlinkRate > 0)
2423 display.blinker = setInterval(function() {
2424 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2425 }, cm.options.cursorBlinkRate);
2426 else if (cm.options.cursorBlinkRate < 0)
2427 display.cursorDiv.style.visibility = "hidden";
2428 }
2429
2430 // HIGHLIGHT WORKER
2431
2432 function startWorker(cm, time) {
2433 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2434 cm.state.highlight.set(time, bind(highlightWorker, cm));
2435 }
2436
2437 function highlightWorker(cm) {
2438 var doc = cm.doc;
2439 if (doc.frontier < doc.first) doc.frontier = doc.first;
2440 if (doc.frontier >= cm.display.viewTo) return;
2441 var end = +new Date + cm.options.workTime;
2442 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2443 var changedLines = [];
2444
2445 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2446 if (doc.frontier >= cm.display.viewFrom) { // Visible
2447 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
2448 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
2449 line.styles = highlighted.styles;
2450 var oldCls = line.styleClasses, newCls = highlighted.classes;
2451 if (newCls) line.styleClasses = newCls;
2452 else if (oldCls) line.styleClasses = null;
2453 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2454 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2455 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2456 if (ischange) changedLines.push(doc.frontier);
2457 line.stateAfter = tooLong ? state : copyState(doc.mode, state);
2458 } else {
2459 if (line.text.length <= cm.options.maxHighlightLength)
2460 processLine(cm, line.text, state);
2461 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
2462 }
2463 ++doc.frontier;
2464 if (+new Date > end) {
2465 startWorker(cm, cm.options.workDelay);
2466 return true;
2467 }
2468 });
2469 if (changedLines.length) runInOp(cm, function() {
2470 for (var i = 0; i < changedLines.length; i++)
2471 regLineChange(cm, changedLines[i], "text");
2472 });
2473 }
2474
2475 // Finds the line to start with when starting a parse. Tries to
2476 // find a line with a stateAfter, so that it can start with a
2477 // valid state. If that fails, it returns the line with the
2478 // smallest indentation, which tends to need the least context to
2479 // parse correctly.
2480 function findStartLine(cm, n, precise) {
2481 var minindent, minline, doc = cm.doc;
2482 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2483 for (var search = n; search > lim; --search) {
2484 if (search <= doc.first) return doc.first;
2485 var line = getLine(doc, search - 1);
2486 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2487 var indented = countColumn(line.text, null, cm.options.tabSize);
2488 if (minline == null || minindent > indented) {
2489 minline = search - 1;
2490 minindent = indented;
2491 }
2492 }
2493 return minline;
2494 }
2495
2496 function getStateBefore(cm, n, precise) {
2497 var doc = cm.doc, display = cm.display;
2498 if (!doc.mode.startState) return true;
2499 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2500 if (!state) state = startState(doc.mode);
2501 else state = copyState(doc.mode, state);
2502 doc.iter(pos, n, function(line) {
2503 processLine(cm, line.text, state);
2504 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2505 line.stateAfter = save ? copyState(doc.mode, state) : null;
2506 ++pos;
2507 });
2508 if (precise) doc.frontier = pos;
2509 return state;
2510 }
2511
2512 // POSITION MEASUREMENT
2513
2514 function paddingTop(display) {return display.lineSpace.offsetTop;}
2515 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2516 function paddingH(display) {
2517 if (display.cachedPaddingH) return display.cachedPaddingH;
2518 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2519 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2520 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2521 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2522 return data;
2523 }
2524
2525 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2526 function displayWidth(cm) {
2527 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2528 }
2529 function displayHeight(cm) {
2530 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2531 }
2532
2533 // Ensure the lineView.wrapping.heights array is populated. This is
2534 // an array of bottom offsets for the lines that make up a drawn
2535 // line. When lineWrapping is on, there might be more than one
2536 // height.
2537 function ensureLineHeights(cm, lineView, rect) {
2538 var wrapping = cm.options.lineWrapping;
2539 var curWidth = wrapping && displayWidth(cm);
2540 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2541 var heights = lineView.measure.heights = [];
2542 if (wrapping) {
2543 lineView.measure.width = curWidth;
2544 var rects = lineView.text.firstChild.getClientRects();
2545 for (var i = 0; i < rects.length - 1; i++) {
2546 var cur = rects[i], next = rects[i + 1];
2547 if (Math.abs(cur.bottom - next.bottom) > 2)
2548 heights.push((cur.bottom + next.top) / 2 - rect.top);
2549 }
2550 }
2551 heights.push(rect.bottom - rect.top);
2552 }
2553 }
2554
2555 // Find a line map (mapping character offsets to text nodes) and a
2556 // measurement cache for the given line number. (A line view might
2557 // contain multiple lines when collapsed ranges are present.)
2558 function mapFromLineView(lineView, line, lineN) {
2559 if (lineView.line == line)
2560 return {map: lineView.measure.map, cache: lineView.measure.cache};
2561 for (var i = 0; i < lineView.rest.length; i++)
2562 if (lineView.rest[i] == line)
2563 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2564 for (var i = 0; i < lineView.rest.length; i++)
2565 if (lineNo(lineView.rest[i]) > lineN)
2566 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
2567 }
2568
2569 // Render a line into the hidden node display.externalMeasured. Used
2570 // when measurement is needed for a line that's not in the viewport.
2571 function updateExternalMeasurement(cm, line) {
2572 line = visualLine(line);
2573 var lineN = lineNo(line);
2574 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2575 view.lineN = lineN;
2576 var built = view.built = buildLineContent(cm, view);
2577 view.text = built.pre;
2578 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2579 return view;
2580 }
2581
2582 // Get a {top, bottom, left, right} box (in line-local coordinates)
2583 // for a given character.
2584 function measureChar(cm, line, ch, bias) {
2585 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2586 }
2587
2588 // Find a line view that corresponds to the given line number.
2589 function findViewForLine(cm, lineN) {
2590 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2591 return cm.display.view[findViewIndex(cm, lineN)];
2592 var ext = cm.display.externalMeasured;
2593 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2594 return ext;
2595 }
2596
2597 // Measurement can be split in two steps, the set-up work that
2598 // applies to the whole line, and the measurement of the actual
2599 // character. Functions like coordsChar, that need to do a lot of
2600 // measurements in a row, can thus ensure that the set-up work is
2601 // only done once.
2602 function prepareMeasureForLine(cm, line) {
2603 var lineN = lineNo(line);
2604 var view = findViewForLine(cm, lineN);
2605 if (view && !view.text) {
2606 view = null;
2607 } else if (view && view.changes) {
2608 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2609 cm.curOp.forceUpdate = true;
2610 }
2611 if (!view)
2612 view = updateExternalMeasurement(cm, line);
2613
2614 var info = mapFromLineView(view, line, lineN);
2615 return {
2616 line: line, view: view, rect: null,
2617 map: info.map, cache: info.cache, before: info.before,
2618 hasHeights: false
2619 };
2620 }
2621
2622 // Given a prepared measurement object, measures the position of an
2623 // actual character (or fetches it from the cache).
2624 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2625 if (prepared.before) ch = -1;
2626 var key = ch + (bias || ""), found;
2627 if (prepared.cache.hasOwnProperty(key)) {
2628 found = prepared.cache[key];
2629 } else {
2630 if (!prepared.rect)
2631 prepared.rect = prepared.view.text.getBoundingClientRect();
2632 if (!prepared.hasHeights) {
2633 ensureLineHeights(cm, prepared.view, prepared.rect);
2634 prepared.hasHeights = true;
2635 }
2636 found = measureCharInner(cm, prepared, ch, bias);
2637 if (!found.bogus) prepared.cache[key] = found;
2638 }
2639 return {left: found.left, right: found.right,
2640 top: varHeight ? found.rtop : found.top,
2641 bottom: varHeight ? found.rbottom : found.bottom};
2642 }
2643
2644 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2645
2646 function nodeAndOffsetInLineMap(map, ch, bias) {
2647 var node, start, end, collapse;
2648 // First, search the line map for the text node corresponding to,
2649 // or closest to, the target character.
2650 for (var i = 0; i < map.length; i += 3) {
2651 var mStart = map[i], mEnd = map[i + 1];
2652 if (ch < mStart) {
2653 start = 0; end = 1;
2654 collapse = "left";
2655 } else if (ch < mEnd) {
2656 start = ch - mStart;
2657 end = start + 1;
2658 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2659 end = mEnd - mStart;
2660 start = end - 1;
2661 if (ch >= mEnd) collapse = "right";
2662 }
2663 if (start != null) {
2664 node = map[i + 2];
2665 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2666 collapse = bias;
2667 if (bias == "left" && start == 0)
2668 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2669 node = map[(i -= 3) + 2];
2670 collapse = "left";
2671 }
2672 if (bias == "right" && start == mEnd - mStart)
2673 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2674 node = map[(i += 3) + 2];
2675 collapse = "right";
2676 }
2677 break;
2678 }
2679 }
2680 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2681 }
2682
2683 function measureCharInner(cm, prepared, ch, bias) {
2684 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2685 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2686
2687 var rect;
2688 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2689 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2690 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2691 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2692 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
2693 rect = node.parentNode.getBoundingClientRect();
2694 } else if (ie && cm.options.lineWrapping) {
2695 var rects = range(node, start, end).getClientRects();
2696 if (rects.length)
2697 rect = rects[bias == "right" ? rects.length - 1 : 0];
2698 else
2699 rect = nullRect;
2700 } else {
2701 rect = range(node, start, end).getBoundingClientRect() || nullRect;
2702 }
2703 if (rect.left || rect.right || start == 0) break;
2704 end = start;
2705 start = start - 1;
2706 collapse = "right";
2707 }
2708 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
2709 } else { // If it is a widget, simply get the box for the whole widget.
2710 if (start > 0) collapse = bias = "right";
2711 var rects;
2712 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2713 rect = rects[bias == "right" ? rects.length - 1 : 0];
2714 else
2715 rect = node.getBoundingClientRect();
2716 }
2717 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2718 var rSpan = node.parentNode.getClientRects()[0];
2719 if (rSpan)
2720 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2721 else
2722 rect = nullRect;
2723 }
2724
2725 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2726 var mid = (rtop + rbot) / 2;
2727 var heights = prepared.view.measure.heights;
2728 for (var i = 0; i < heights.length - 1; i++)
2729 if (mid < heights[i]) break;
2730 var top = i ? heights[i - 1] : 0, bot = heights[i];
2731 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2732 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2733 top: top, bottom: bot};
2734 if (!rect.left && !rect.right) result.bogus = true;
2735 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2736
2737 return result;
2738 }
2739
2740 // Work around problem with bounding client rects on ranges being
2741 // returned incorrectly when zoomed on IE10 and below.
2742 function maybeUpdateRectForZooming(measure, rect) {
2743 if (!window.screen || screen.logicalXDPI == null ||
2744 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2745 return rect;
2746 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2747 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2748 return {left: rect.left * scaleX, right: rect.right * scaleX,
2749 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2750 }
2751
2752 function clearLineMeasurementCacheFor(lineView) {
2753 if (lineView.measure) {
2754 lineView.measure.cache = {};
2755 lineView.measure.heights = null;
2756 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2757 lineView.measure.caches[i] = {};
2758 }
2759 }
2760
2761 function clearLineMeasurementCache(cm) {
2762 cm.display.externalMeasure = null;
2763 removeChildren(cm.display.lineMeasure);
2764 for (var i = 0; i < cm.display.view.length; i++)
2765 clearLineMeasurementCacheFor(cm.display.view[i]);
2766 }
2767
2768 function clearCaches(cm) {
2769 clearLineMeasurementCache(cm);
2770 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2771 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2772 cm.display.lineNumChars = null;
2773 }
2774
2775 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2776 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2777
2778 // Converts a {top, bottom, left, right} box from line-local
2779 // coordinates into another coordinate system. Context may be one of
2780 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2781 // or "page".
2782 function intoCoordSystem(cm, lineObj, rect, context) {
2783 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2784 var size = widgetHeight(lineObj.widgets[i]);
2785 rect.top += size; rect.bottom += size;
2786 }
2787 if (context == "line") return rect;
2788 if (!context) context = "local";
2789 var yOff = heightAtLine(lineObj);
2790 if (context == "local") yOff += paddingTop(cm.display);
2791 else yOff -= cm.display.viewOffset;
2792 if (context == "page" || context == "window") {
2793 var lOff = cm.display.lineSpace.getBoundingClientRect();
2794 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2795 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2796 rect.left += xOff; rect.right += xOff;
2797 }
2798 rect.top += yOff; rect.bottom += yOff;
2799 return rect;
2800 }
2801
2802 // Coverts a box from "div" coords to another coordinate system.
2803 // Context may be "window", "page", "div", or "local"/null.
2804 function fromCoordSystem(cm, coords, context) {
2805 if (context == "div") return coords;
2806 var left = coords.left, top = coords.top;
2807 // First move into "page" coordinate system
2808 if (context == "page") {
2809 left -= pageScrollX();
2810 top -= pageScrollY();
2811 } else if (context == "local" || !context) {
2812 var localBox = cm.display.sizer.getBoundingClientRect();
2813 left += localBox.left;
2814 top += localBox.top;
2815 }
2816
2817 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2818 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2819 }
2820
2821 function charCoords(cm, pos, context, lineObj, bias) {
2822 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2823 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2824 }
2825
2826 // Returns a box for a given cursor position, which may have an
2827 // 'other' property containing the position of the secondary cursor
2828 // on a bidi boundary.
2829 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2830 lineObj = lineObj || getLine(cm.doc, pos.line);
2831 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2832 function get(ch, right) {
2833 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2834 if (right) m.left = m.right; else m.right = m.left;
2835 return intoCoordSystem(cm, lineObj, m, context);
2836 }
2837 function getBidi(ch, partPos) {
2838 var part = order[partPos], right = part.level % 2;
2839 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2840 part = order[--partPos];
2841 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2842 right = true;
2843 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2844 part = order[++partPos];
2845 ch = bidiLeft(part) - part.level % 2;
2846 right = false;
2847 }
2848 if (right && ch == part.to && ch > part.from) return get(ch - 1);
2849 return get(ch, right);
2850 }
2851 var order = getOrder(lineObj), ch = pos.ch;
2852 if (!order) return get(ch);
2853 var partPos = getBidiPartAt(order, ch);
2854 var val = getBidi(ch, partPos);
2855 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2856 return val;
2857 }
2858
2859 // Used to cheaply estimate the coordinates for a position. Used for
2860 // intermediate scroll updates.
2861 function estimateCoords(cm, pos) {
2862 var left = 0, pos = clipPos(cm.doc, pos);
2863 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2864 var lineObj = getLine(cm.doc, pos.line);
2865 var top = heightAtLine(lineObj) + paddingTop(cm.display);
2866 return {left: left, right: left, top: top, bottom: top + lineObj.height};
2867 }
2868
2869 // Positions returned by coordsChar contain some extra information.
2870 // xRel is the relative x position of the input coordinates compared
2871 // to the found position (so xRel > 0 means the coordinates are to
2872 // the right of the character position, for example). When outside
2873 // is true, that means the coordinates lie outside the line's
2874 // vertical range.
2875 function PosWithInfo(line, ch, outside, xRel) {
2876 var pos = Pos(line, ch);
2877 pos.xRel = xRel;
2878 if (outside) pos.outside = true;
2879 return pos;
2880 }
2881
2882 // Compute the character position closest to the given coordinates.
2883 // Input must be lineSpace-local ("div" coordinate system).
2884 function coordsChar(cm, x, y) {
2885 var doc = cm.doc;
2886 y += cm.display.viewOffset;
2887 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2888 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2889 if (lineN > last)
2890 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2891 if (x < 0) x = 0;
2892
2893 var lineObj = getLine(doc, lineN);
2894 for (;;) {
2895 var found = coordsCharInner(cm, lineObj, lineN, x, y);
2896 var merged = collapsedSpanAtEnd(lineObj);
2897 var mergedPos = merged && merged.find(0, true);
2898 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2899 lineN = lineNo(lineObj = mergedPos.to.line);
2900 else
2901 return found;
2902 }
2903 }
2904
2905 function coordsCharInner(cm, lineObj, lineNo, x, y) {
2906 var innerOff = y - heightAtLine(lineObj);
2907 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2908 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2909
2910 function getX(ch) {
2911 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2912 wrongLine = true;
2913 if (innerOff > sp.bottom) return sp.left - adjust;
2914 else if (innerOff < sp.top) return sp.left + adjust;
2915 else wrongLine = false;
2916 return sp.left;
2917 }
2918
2919 var bidi = getOrder(lineObj), dist = lineObj.text.length;
2920 var from = lineLeft(lineObj), to = lineRight(lineObj);
2921 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2922
2923 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2924 // Do a binary search between these bounds.
2925 for (;;) {
2926 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2927 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2928 var xDiff = x - (ch == from ? fromX : toX);
2929 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2930 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2931 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2932 return pos;
2933 }
2934 var step = Math.ceil(dist / 2), middle = from + step;
2935 if (bidi) {
2936 middle = from;
2937 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2938 }
2939 var middleX = getX(middle);
2940 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2941 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2942 }
2943 }
2944
2945 var measureText;
2946 // Compute the default text height.
2947 function textHeight(display) {
2948 if (display.cachedTextHeight != null) return display.cachedTextHeight;
2949 if (measureText == null) {
2950 measureText = elt("pre");
2951 // Measure a bunch of lines, for browsers that compute
2952 // fractional heights.
2953 for (var i = 0; i < 49; ++i) {
2954 measureText.appendChild(document.createTextNode("x"));
2955 measureText.appendChild(elt("br"));
2956 }
2957 measureText.appendChild(document.createTextNode("x"));
2958 }
2959 removeChildrenAndAdd(display.measure, measureText);
2960 var height = measureText.offsetHeight / 50;
2961 if (height > 3) display.cachedTextHeight = height;
2962 removeChildren(display.measure);
2963 return height || 1;
2964 }
2965
2966 // Compute the default character width.
2967 function charWidth(display) {
2968 if (display.cachedCharWidth != null) return display.cachedCharWidth;
2969 var anchor = elt("span", "xxxxxxxxxx");
2970 var pre = elt("pre", [anchor]);
2971 removeChildrenAndAdd(display.measure, pre);
2972 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2973 if (width > 2) display.cachedCharWidth = width;
2974 return width || 10;
2975 }
2976
2977 // OPERATIONS
2978
2979 // Operations are used to wrap a series of changes to the editor
2980 // state in such a way that each change won't have to update the
2981 // cursor and display (which would be awkward, slow, and
2982 // error-prone). Instead, display updates are batched and then all
2983 // combined and executed at once.
2984
2985 var operationGroup = null;
2986
2987 var nextOpId = 0;
2988 // Start a new operation.
2989 function startOperation(cm) {
2990 cm.curOp = {
2991 cm: cm,
2992 viewChanged: false, // Flag that indicates that lines might need to be redrawn
2993 startHeight: cm.doc.height, // Used to detect need to update scrollbar
2994 forceUpdate: false, // Used to force a redraw
2995 updateInput: null, // Whether to reset the input textarea
2996 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2997 changeObjs: null, // Accumulated changes, for firing change events
2998 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2999 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3000 selectionChanged: false, // Whether the selection needs to be redrawn
3001 updateMaxLine: false, // Set when the widest line needs to be determined anew
3002 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3003 scrollToPos: null, // Used to scroll to a specific position
3004 focus: false,
3005 id: ++nextOpId // Unique ID
3006 };
3007 if (operationGroup) {
3008 operationGroup.ops.push(cm.curOp);
3009 } else {
3010 cm.curOp.ownsGroup = operationGroup = {
3011 ops: [cm.curOp],
3012 delayedCallbacks: []
3013 };
3014 }
3015 }
3016
3017 function fireCallbacksForOps(group) {
3018 // Calls delayed callbacks and cursorActivity handlers until no
3019 // new ones appear
3020 var callbacks = group.delayedCallbacks, i = 0;
3021 do {
3022 for (; i < callbacks.length; i++)
3023 callbacks[i].call(null);
3024 for (var j = 0; j < group.ops.length; j++) {
3025 var op = group.ops[j];
3026 if (op.cursorActivityHandlers)
3027 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3028 op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
3029 }
3030 } while (i < callbacks.length);
3031 }
3032
3033 // Finish an operation, updating the display and signalling delayed events
3034 function endOperation(cm) {
3035 var op = cm.curOp, group = op.ownsGroup;
3036 if (!group) return;
3037
3038 try { fireCallbacksForOps(group); }
3039 finally {
3040 operationGroup = null;
3041 for (var i = 0; i < group.ops.length; i++)
3042 group.ops[i].cm.curOp = null;
3043 endOperations(group);
3044 }
3045 }
3046
3047 // The DOM updates done when an operation finishes are batched so
3048 // that the minimum number of relayouts are required.
3049 function endOperations(group) {
3050 var ops = group.ops;
3051 for (var i = 0; i < ops.length; i++) // Read DOM
3052 endOperation_R1(ops[i]);
3053 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3054 endOperation_W1(ops[i]);
3055 for (var i = 0; i < ops.length; i++) // Read DOM
3056 endOperation_R2(ops[i]);
3057 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3058 endOperation_W2(ops[i]);
3059 for (var i = 0; i < ops.length; i++) // Read DOM
3060 endOperation_finish(ops[i]);
3061 }
3062
3063 function endOperation_R1(op) {
3064 var cm = op.cm, display = cm.display;
3065 maybeClipScrollbars(cm);
3066 if (op.updateMaxLine) findMaxLine(cm);
3067
3068 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3069 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3070 op.scrollToPos.to.line >= display.viewTo) ||
3071 display.maxLineChanged && cm.options.lineWrapping;
3072 op.update = op.mustUpdate &&
3073 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3074 }
3075
3076 function endOperation_W1(op) {
3077 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3078 }
3079
3080 function endOperation_R2(op) {
3081 var cm = op.cm, display = cm.display;
3082 if (op.updatedDisplay) updateHeightsInViewport(cm);
3083
3084 op.barMeasure = measureForScrollbars(cm);
3085
3086 // If the max line changed since it was last measured, measure it,
3087 // and ensure the document's width matches it.
3088 // updateDisplay_W2 will use these properties to do the actual resizing
3089 if (display.maxLineChanged && !cm.options.lineWrapping) {
3090 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3091 cm.display.sizerWidth = op.adjustWidthTo;
3092 op.barMeasure.scrollWidth =
3093 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3094 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3095 }
3096
3097 if (op.updatedDisplay || op.selectionChanged)
3098 op.preparedSelection = display.input.prepareSelection();
3099 }
3100
3101 function endOperation_W2(op) {
3102 var cm = op.cm;
3103
3104 if (op.adjustWidthTo != null) {
3105 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3106 if (op.maxScrollLeft < cm.doc.scrollLeft)
3107 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3108 cm.display.maxLineChanged = false;
3109 }
3110
3111 if (op.preparedSelection)
3112 cm.display.input.showSelection(op.preparedSelection);
3113 if (op.updatedDisplay)
3114 setDocumentHeight(cm, op.barMeasure);
3115 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3116 updateScrollbars(cm, op.barMeasure);
3117
3118 if (op.selectionChanged) restartBlink(cm);
3119
3120 if (cm.state.focused && op.updateInput)
3121 cm.display.input.reset(op.typing);
3122 if (op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()))
3123 ensureFocus(op.cm);
3124 }
3125
3126 function endOperation_finish(op) {
3127 var cm = op.cm, display = cm.display, doc = cm.doc;
3128
3129 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3130
3131 // Abort mouse wheel delta measurement, when scrolling explicitly
3132 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3133 display.wheelStartX = display.wheelStartY = null;
3134
3135 // Propagate the scroll position to the actual DOM scroller
3136 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3137 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3138 display.scrollbars.setScrollTop(doc.scrollTop);
3139 display.scroller.scrollTop = doc.scrollTop;
3140 }
3141 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3142 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
3143 display.scrollbars.setScrollLeft(doc.scrollLeft);
3144 display.scroller.scrollLeft = doc.scrollLeft;
3145 alignHorizontally(cm);
3146 }
3147 // If we need to scroll a specific position into view, do so.
3148 if (op.scrollToPos) {
3149 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3150 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3151 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3152 }
3153
3154 // Fire events for markers that are hidden/unidden by editing or
3155 // undoing
3156 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3157 if (hidden) for (var i = 0; i < hidden.length; ++i)
3158 if (!hidden[i].lines.length) signal(hidden[i], "hide");
3159 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3160 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3161
3162 if (display.wrapper.offsetHeight)
3163 doc.scrollTop = cm.display.scroller.scrollTop;
3164
3165 // Fire change events, and delayed event handlers
3166 if (op.changeObjs)
3167 signal(cm, "changes", cm, op.changeObjs);
3168 if (op.update)
3169 op.update.finish();
3170 }
3171
3172 // Run the given function in an operation
3173 function runInOp(cm, f) {
3174 if (cm.curOp) return f();
3175 startOperation(cm);
3176 try { return f(); }
3177 finally { endOperation(cm); }
3178 }
3179 // Wraps a function in an operation. Returns the wrapped function.
3180 function operation(cm, f) {
3181 return function() {
3182 if (cm.curOp) return f.apply(cm, arguments);
3183 startOperation(cm);
3184 try { return f.apply(cm, arguments); }
3185 finally { endOperation(cm); }
3186 };
3187 }
3188 // Used to add methods to editor and doc instances, wrapping them in
3189 // operations.
3190 function methodOp(f) {
3191 return function() {
3192 if (this.curOp) return f.apply(this, arguments);
3193 startOperation(this);
3194 try { return f.apply(this, arguments); }
3195 finally { endOperation(this); }
3196 };
3197 }
3198 function docMethodOp(f) {
3199 return function() {
3200 var cm = this.cm;
3201 if (!cm || cm.curOp) return f.apply(this, arguments);
3202 startOperation(cm);
3203 try { return f.apply(this, arguments); }
3204 finally { endOperation(cm); }
3205 };
3206 }
3207
3208 // VIEW TRACKING
3209
3210 // These objects are used to represent the visible (currently drawn)
3211 // part of the document. A LineView may correspond to multiple
3212 // logical lines, if those are connected by collapsed ranges.
3213 function LineView(doc, line, lineN) {
3214 // The starting line
3215 this.line = line;
3216 // Continuing lines, if any
3217 this.rest = visualLineContinued(line);
3218 // Number of logical lines in this visual line
3219 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3220 this.node = this.text = null;
3221 this.hidden = lineIsHidden(doc, line);
3222 }
3223
3224 // Create a range of LineView objects for the given lines.
3225 function buildViewArray(cm, from, to) {
3226 var array = [], nextPos;
3227 for (var pos = from; pos < to; pos = nextPos) {
3228 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3229 nextPos = pos + view.size;
3230 array.push(view);
3231 }
3232 return array;
3233 }
3234
3235 // Updates the display.view data structure for a given change to the
3236 // document. From and to are in pre-change coordinates. Lendiff is
3237 // the amount of lines added or subtracted by the change. This is
3238 // used for changes that span multiple lines, or change the way
3239 // lines are divided into visual lines. regLineChange (below)
3240 // registers single-line changes.
3241 function regChange(cm, from, to, lendiff) {
3242 if (from == null) from = cm.doc.first;
3243 if (to == null) to = cm.doc.first + cm.doc.size;
3244 if (!lendiff) lendiff = 0;
3245
3246 var display = cm.display;
3247 if (lendiff && to < display.viewTo &&
3248 (display.updateLineNumbers == null || display.updateLineNumbers > from))
3249 display.updateLineNumbers = from;
3250
3251 cm.curOp.viewChanged = true;
3252
3253 if (from >= display.viewTo) { // Change after
3254 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3255 resetView(cm);
3256 } else if (to <= display.viewFrom) { // Change before
3257 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3258 resetView(cm);
3259 } else {
3260 display.viewFrom += lendiff;
3261 display.viewTo += lendiff;
3262 }
3263 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3264 resetView(cm);
3265 } else if (from <= display.viewFrom) { // Top overlap
3266 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3267 if (cut) {
3268 display.view = display.view.slice(cut.index);
3269 display.viewFrom = cut.lineN;
3270 display.viewTo += lendiff;
3271 } else {
3272 resetView(cm);
3273 }
3274 } else if (to >= display.viewTo) { // Bottom overlap
3275 var cut = viewCuttingPoint(cm, from, from, -1);
3276 if (cut) {
3277 display.view = display.view.slice(0, cut.index);
3278 display.viewTo = cut.lineN;
3279 } else {
3280 resetView(cm);
3281 }
3282 } else { // Gap in the middle
3283 var cutTop = viewCuttingPoint(cm, from, from, -1);
3284 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3285 if (cutTop && cutBot) {
3286 display.view = display.view.slice(0, cutTop.index)
3287 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3288 .concat(display.view.slice(cutBot.index));
3289 display.viewTo += lendiff;
3290 } else {
3291 resetView(cm);
3292 }
3293 }
3294
3295 var ext = display.externalMeasured;
3296 if (ext) {
3297 if (to < ext.lineN)
3298 ext.lineN += lendiff;
3299 else if (from < ext.lineN + ext.size)
3300 display.externalMeasured = null;
3301 }
3302 }
3303
3304 // Register a change to a single line. Type must be one of "text",
3305 // "gutter", "class", "widget"
3306 function regLineChange(cm, line, type) {
3307 cm.curOp.viewChanged = true;
3308 var display = cm.display, ext = cm.display.externalMeasured;
3309 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3310 display.externalMeasured = null;
3311
3312 if (line < display.viewFrom || line >= display.viewTo) return;
3313 var lineView = display.view[findViewIndex(cm, line)];
3314 if (lineView.node == null) return;
3315 var arr = lineView.changes || (lineView.changes = []);
3316 if (indexOf(arr, type) == -1) arr.push(type);
3317 }
3318
3319 // Clear the view.
3320 function resetView(cm) {
3321 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3322 cm.display.view = [];
3323 cm.display.viewOffset = 0;
3324 }
3325
3326 // Find the view element corresponding to a given line. Return null
3327 // when the line isn't visible.
3328 function findViewIndex(cm, n) {
3329 if (n >= cm.display.viewTo) return null;
3330 n -= cm.display.viewFrom;
3331 if (n < 0) return null;
3332 var view = cm.display.view;
3333 for (var i = 0; i < view.length; i++) {
3334 n -= view[i].size;
3335 if (n < 0) return i;
3336 }
3337 }
3338
3339 function viewCuttingPoint(cm, oldN, newN, dir) {
3340 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3341 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3342 return {index: index, lineN: newN};
3343 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3344 n += view[i].size;
3345 if (n != oldN) {
3346 if (dir > 0) {
3347 if (index == view.length - 1) return null;
3348 diff = (n + view[index].size) - oldN;
3349 index++;
3350 } else {
3351 diff = n - oldN;
3352 }
3353 oldN += diff; newN += diff;
3354 }
3355 while (visualLineNo(cm.doc, newN) != newN) {
3356 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3357 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3358 index += dir;
3359 }
3360 return {index: index, lineN: newN};
3361 }
3362
3363 // Force the view to cover a given range, adding empty view element
3364 // or clipping off existing ones as needed.
3365 function adjustView(cm, from, to) {
3366 var display = cm.display, view = display.view;
3367 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3368 display.view = buildViewArray(cm, from, to);
3369 display.viewFrom = from;
3370 } else {
3371 if (display.viewFrom > from)
3372 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3373 else if (display.viewFrom < from)
3374 display.view = display.view.slice(findViewIndex(cm, from));
3375 display.viewFrom = from;
3376 if (display.viewTo < to)
3377 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3378 else if (display.viewTo > to)
3379 display.view = display.view.slice(0, findViewIndex(cm, to));
3380 }
3381 display.viewTo = to;
3382 }
3383
3384 // Count the number of lines in the view whose DOM representation is
3385 // out of date (or nonexistent).
3386 function countDirtyView(cm) {
3387 var view = cm.display.view, dirty = 0;
3388 for (var i = 0; i < view.length; i++) {
3389 var lineView = view[i];
3390 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3391 }
3392 return dirty;
3393 }
3394
3395 // EVENT HANDLERS
3396
3397 // Attach the necessary event handlers when initializing the editor
3398 function registerEventHandlers(cm) {
3399 var d = cm.display;
3400 on(d.scroller, "mousedown", operation(cm, onMouseDown));
3401 // Older IE's will not fire a second mousedown for a double click
3402 if (ie && ie_version < 11)
3403 on(d.scroller, "dblclick", operation(cm, function(e) {
3404 if (signalDOMEvent(cm, e)) return;
3405 var pos = posFromMouse(cm, e);
3406 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3407 e_preventDefault(e);
3408 var word = cm.findWordAt(pos);
3409 extendSelection(cm.doc, word.anchor, word.head);
3410 }));
3411 else
3412 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3413 // Some browsers fire contextmenu *after* opening the menu, at
3414 // which point we can't mess with it anymore. Context menu is
3415 // handled in onMouseDown for these browsers.
3416 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3417
3418 // Used to suppress mouse event handling when a touch happens
3419 var touchFinished, prevTouch = {end: 0};
3420 function finishTouch() {
3421 if (d.activeTouch) {
3422 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3423 prevTouch = d.activeTouch;
3424 prevTouch.end = +new Date;
3425 }
3426 };
3427 function isMouseLikeTouchEvent(e) {
3428 if (e.touches.length != 1) return false;
3429 var touch = e.touches[0];
3430 return touch.radiusX <= 1 && touch.radiusY <= 1;
3431 }
3432 function farAway(touch, other) {
3433 if (other.left == null) return true;
3434 var dx = other.left - touch.left, dy = other.top - touch.top;
3435 return dx * dx + dy * dy > 20 * 20;
3436 }
3437 on(d.scroller, "touchstart", function(e) {
3438 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
3439 clearTimeout(touchFinished);
3440 var now = +new Date;
3441 d.activeTouch = {start: now, moved: false,
3442 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3443 if (e.touches.length == 1) {
3444 d.activeTouch.left = e.touches[0].pageX;
3445 d.activeTouch.top = e.touches[0].pageY;
3446 }
3447 }
3448 });
3449 on(d.scroller, "touchmove", function() {
3450 if (d.activeTouch) d.activeTouch.moved = true;
3451 });
3452 on(d.scroller, "touchend", function(e) {
3453 var touch = d.activeTouch;
3454 if (touch && !eventInWidget(d, e) && touch.left != null &&
3455 !touch.moved && new Date - touch.start < 300) {
3456 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3457 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3458 range = new Range(pos, pos);
3459 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3460 range = cm.findWordAt(pos);
3461 else // Triple tap
3462 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3463 cm.setSelection(range.anchor, range.head);
3464 cm.focus();
3465 e_preventDefault(e);
3466 }
3467 finishTouch();
3468 });
3469 on(d.scroller, "touchcancel", finishTouch);
3470
3471 // Sync scrolling between fake scrollbars and real scrollable
3472 // area, ensure viewport is updated when scrolling.
3473 on(d.scroller, "scroll", function() {
3474 if (d.scroller.clientHeight) {
3475 setScrollTop(cm, d.scroller.scrollTop);
3476 setScrollLeft(cm, d.scroller.scrollLeft, true);
3477 signal(cm, "scroll", cm);
3478 }
3479 });
3480
3481 // Listen to wheel events in order to try and update the viewport on time.
3482 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3483 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3484
3485 // Prevent wrapper from ever scrolling
3486 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3487
3488 d.dragFunctions = {
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); }},
3491 start: function(e){onDragStart(cm, e);},
3492 drop: operation(cm, onDrop),
3493 leave: function() {clearDragCursor(cm);}
3494 };
3495
3496 var inp = d.input.getField();
3497 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3498 on(inp, "keydown", operation(cm, onKeyDown));
3499 on(inp, "keypress", operation(cm, onKeyPress));
3500 on(inp, "focus", bind(onFocus, cm));
3501 on(inp, "blur", bind(onBlur, cm));
3502 }
3503
3504 function dragDropChanged(cm, value, old) {
3505 var wasOn = old && old != CodeMirror.Init;
3506 if (!value != !wasOn) {
3507 var funcs = cm.display.dragFunctions;
3508 var toggle = value ? on : off;
3509 toggle(cm.display.scroller, "dragstart", funcs.start);
3510 toggle(cm.display.scroller, "dragenter", funcs.enter);
3511 toggle(cm.display.scroller, "dragover", funcs.over);
3512 toggle(cm.display.scroller, "dragleave", funcs.leave);
3513 toggle(cm.display.scroller, "drop", funcs.drop);
3514 }
3515 }
3516
3517 // Called when the window resizes
3518 function onResize(cm) {
3519 var d = cm.display;
3520 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3521 return;
3522 // Might be a text scaling operation, clear size caches.
3523 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3524 d.scrollbarsClipped = false;
3525 cm.setSize();
3526 }
3527
3528 // MOUSE EVENTS
3529
3530 // Return true when the given mouse event happened in a widget
3531 function eventInWidget(display, e) {
3532 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3533 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3534 (n.parentNode == display.sizer && n != display.mover))
3535 return true;
3536 }
3537 }
3538
3539 // Given a mouse event, find the corresponding position. If liberal
3540 // is false, it checks whether a gutter or scrollbar was clicked,
3541 // and returns null if it was. forRect is used by rectangular
3542 // selections, and tries to estimate a character position even for
3543 // coordinates beyond the right of the text.
3544 function posFromMouse(cm, e, liberal, forRect) {
3545 var display = cm.display;
3546 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3547
3548 var x, y, space = display.lineSpace.getBoundingClientRect();
3549 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3550 try { x = e.clientX - space.left; y = e.clientY - space.top; }
3551 catch (e) { return null; }
3552 var coords = coordsChar(cm, x, y), line;
3553 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3554 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3555 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3556 }
3557 return coords;
3558 }
3559
3560 // A mouse down can be a single click, double click, triple click,
3561 // start of selection drag, start of text drag, new cursor
3562 // (ctrl-click), rectangle drag (alt-drag), or xwin
3563 // middle-click-paste. Or it might be a click on something we should
3564 // not interfere with, such as a scrollbar or widget.
3565 function onMouseDown(e) {
3566 var cm = this, display = cm.display;
3567 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return;
3568 display.shift = e.shiftKey;
3569
3570 if (eventInWidget(display, e)) {
3571 if (!webkit) {
3572 // Briefly turn off draggability, to allow widgets to do
3573 // normal dragging things.
3574 display.scroller.draggable = false;
3575 setTimeout(function(){display.scroller.draggable = true;}, 100);
3576 }
3577 return;
3578 }
3579 if (clickInGutter(cm, e)) return;
3580 var start = posFromMouse(cm, e);
3581 window.focus();
3582
3583 switch (e_button(e)) {
3584 case 1:
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)
3589 leftButtonDown(cm, e, start);
3590 else if (e_target(e) == display.scroller)
3591 e_preventDefault(e);
3592 break;
3593 case 2:
3594 if (webkit) cm.state.lastMiddleDown = +new Date;
3595 if (start) extendSelection(cm.doc, start);
3596 setTimeout(function() {display.input.focus();}, 20);
3597 e_preventDefault(e);
3598 break;
3599 case 3:
3600 if (captureRightClick) onContextMenu(cm, e);
3601 else delayBlurEvent(cm);
3602 break;
3603 }
3604 }
3605
3606 var lastClick, lastDoubleClick;
3607 function leftButtonDown(cm, e, start) {
3608 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3609 else cm.curOp.focus = activeElt();
3610
3611 var now = +new Date, type;
3612 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
3613 type = "triple";
3614 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
3615 type = "double";
3616 lastDoubleClick = {time: now, pos: start};
3617 } else {
3618 type = "single";
3619 lastClick = {time: now, pos: start};
3620 }
3621
3622 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3623 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
3624 type == "single" && (contained = sel.contains(start)) > -1 &&
3625 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
3626 (cmp(contained.to(), start) > 0 || start.xRel < 0))
3627 leftButtonStartDrag(cm, e, start, modifier);
3628 else
3629 leftButtonSelect(cm, e, start, type, modifier);
3630 }
3631
3632 // Start a text drag. When it ends, see if any dragging actually
3633 // happen, and treat as a click if it didn't.
3634 function leftButtonStartDrag(cm, e, start, modifier) {
3635 var display = cm.display, startTime = +new Date;
3636 var dragEnd = operation(cm, function(e2) {
3637 if (webkit) display.scroller.draggable = false;
3638 cm.state.draggingText = false;
3639 off(document, "mouseup", dragEnd);
3640 off(display.scroller, "drop", dragEnd);
3641 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3642 e_preventDefault(e2);
3643 if (!modifier && +new Date - 200 < startTime)
3644 extendSelection(cm.doc, start);
3645 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3646 if (webkit || ie && ie_version == 9)
3647 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3648 else
3649 display.input.focus();
3650 }
3651 });
3652 // Let the drag handler handle this.
3653 if (webkit) display.scroller.draggable = true;
3654 cm.state.draggingText = dragEnd;
3655 // IE's approach to draggable
3656 if (display.scroller.dragDrop) display.scroller.dragDrop();
3657 on(document, "mouseup", dragEnd);
3658 on(display.scroller, "drop", dragEnd);
3659 }
3660
3661 // Normal selection, as opposed to text dragging.
3662 function leftButtonSelect(cm, e, start, type, addNew) {
3663 var display = cm.display, doc = cm.doc;
3664 e_preventDefault(e);
3665
3666 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3667 if (addNew && !e.shiftKey) {
3668 ourIndex = doc.sel.contains(start);
3669 if (ourIndex > -1)
3670 ourRange = ranges[ourIndex];
3671 else
3672 ourRange = new Range(start, start);
3673 } else {
3674 ourRange = doc.sel.primary();
3675 ourIndex = doc.sel.primIndex;
3676 }
3677
3678 if (e.altKey) {
3679 type = "rect";
3680 if (!addNew) ourRange = new Range(start, start);
3681 start = posFromMouse(cm, e, true, true);
3682 ourIndex = -1;
3683 } else if (type == "double") {
3684 var word = cm.findWordAt(start);
3685 if (cm.display.shift || doc.extend)
3686 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3687 else
3688 ourRange = word;
3689 } else if (type == "triple") {
3690 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3691 if (cm.display.shift || doc.extend)
3692 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3693 else
3694 ourRange = line;
3695 } else {
3696 ourRange = extendRange(doc, ourRange, start);
3697 }
3698
3699 if (!addNew) {
3700 ourIndex = 0;
3701 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3702 startSel = doc.sel;
3703 } else if (ourIndex == -1) {
3704 ourIndex = ranges.length;
3705 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3706 {scroll: false, origin: "*mouse"});
3707 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3708 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
3709 {scroll: false, origin: "*mouse"});
3710 startSel = doc.sel;
3711 } else {
3712 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3713 }
3714
3715 var lastPos = start;
3716 function extendTo(pos) {
3717 if (cmp(lastPos, pos) == 0) return;
3718 lastPos = pos;
3719
3720 if (type == "rect") {
3721 var ranges = [], tabSize = cm.options.tabSize;
3722 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3723 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3724 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3725 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3726 line <= end; line++) {
3727 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3728 if (left == right)
3729 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3730 else if (text.length > leftPos)
3731 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3732 }
3733 if (!ranges.length) ranges.push(new Range(start, start));
3734 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3735 {origin: "*mouse", scroll: false});
3736 cm.scrollIntoView(pos);
3737 } else {
3738 var oldRange = ourRange;
3739 var anchor = oldRange.anchor, head = pos;
3740 if (type != "single") {
3741 if (type == "double")
3742 var range = cm.findWordAt(pos);
3743 else
3744 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3745 if (cmp(range.anchor, anchor) > 0) {
3746 head = range.head;
3747 anchor = minPos(oldRange.from(), range.anchor);
3748 } else {
3749 head = range.anchor;
3750 anchor = maxPos(oldRange.to(), range.head);
3751 }
3752 }
3753 var ranges = startSel.ranges.slice(0);
3754 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3755 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3756 }
3757 }
3758
3759 var editorSize = display.wrapper.getBoundingClientRect();
3760 // Used to ensure timeout re-tries don't fire when another extend
3761 // happened in the meantime (clearTimeout isn't reliable -- at
3762 // least on Chrome, the timeouts still happen even when cleared,
3763 // if the clear happens after their scheduled firing time).
3764 var counter = 0;
3765
3766 function extend(e) {
3767 var curCount = ++counter;
3768 var cur = posFromMouse(cm, e, true, type == "rect");
3769 if (!cur) return;
3770 if (cmp(cur, lastPos) != 0) {
3771 cm.curOp.focus = activeElt();
3772 extendTo(cur);
3773 var visible = visibleLines(display, doc);
3774 if (cur.line >= visible.to || cur.line < visible.from)
3775 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3776 } else {
3777 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3778 if (outside) setTimeout(operation(cm, function() {
3779 if (counter != curCount) return;
3780 display.scroller.scrollTop += outside;
3781 extend(e);
3782 }), 50);
3783 }
3784 }
3785
3786 function done(e) {
3787 cm.state.selectingText = false;
3788 counter = Infinity;
3789 e_preventDefault(e);
3790 display.input.focus();
3791 off(document, "mousemove", move);
3792 off(document, "mouseup", up);
3793 doc.history.lastSelOrigin = null;
3794 }
3795
3796 var move = operation(cm, function(e) {
3797 if (!e_button(e)) done(e);
3798 else extend(e);
3799 });
3800 var up = operation(cm, done);
3801 cm.state.selectingText = up;
3802 on(document, "mousemove", move);
3803 on(document, "mouseup", up);
3804 }
3805
3806 // Determines whether an event happened in the gutter, and fires the
3807 // handlers for the corresponding event.
3808 function gutterEvent(cm, e, type, prevent) {
3809 try { var mX = e.clientX, mY = e.clientY; }
3810 catch(e) { return false; }
3811 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3812 if (prevent) e_preventDefault(e);
3813
3814 var display = cm.display;
3815 var lineBox = display.lineDiv.getBoundingClientRect();
3816
3817 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3818 mY -= lineBox.top - display.viewOffset;
3819
3820 for (var i = 0; i < cm.options.gutters.length; ++i) {
3821 var g = display.gutters.childNodes[i];
3822 if (g && g.getBoundingClientRect().right >= mX) {
3823 var line = lineAtHeight(cm.doc, mY);
3824 var gutter = cm.options.gutters[i];
3825 signal(cm, type, cm, line, gutter, e);
3826 return e_defaultPrevented(e);
3827 }
3828 }
3829 }
3830
3831 function clickInGutter(cm, e) {
3832 return gutterEvent(cm, e, "gutterClick", true);
3833 }
3834
3835 // Kludge to work around strange IE behavior where it'll sometimes
3836 // re-fire a series of drag-related events right after the drop (#1551)
3837 var lastDrop = 0;
3838
3839 function onDrop(e) {
3840 var cm = this;
3841 clearDragCursor(cm);
3842 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3843 return;
3844 e_preventDefault(e);
3845 if (ie) lastDrop = +new Date;
3846 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3847 if (!pos || cm.isReadOnly()) return;
3848 // Might be a file drop, in which case we simply extract the text
3849 // and insert it.
3850 if (files && files.length && window.FileReader && window.File) {
3851 var n = files.length, text = Array(n), read = 0;
3852 var loadFile = function(file, i) {
3853 if (cm.options.allowDropFileTypes &&
3854 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
3855 return;
3856
3857 var reader = new FileReader;
3858 reader.onload = operation(cm, function() {
3859 var content = reader.result;
3860 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
3861 text[i] = content;
3862 if (++read == n) {
3863 pos = clipPos(cm.doc, pos);
3864 var change = {from: pos, to: pos,
3865 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
3866 origin: "paste"};
3867 makeChange(cm.doc, change);
3868 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3869 }
3870 });
3871 reader.readAsText(file);
3872 };
3873 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3874 } else { // Normal drop
3875 // Don't do a replace if the drop happened inside of the selected text.
3876 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3877 cm.state.draggingText(e);
3878 // Ensure the editor is re-focused
3879 setTimeout(function() {cm.display.input.focus();}, 20);
3880 return;
3881 }
3882 try {
3883 var text = e.dataTransfer.getData("Text");
3884 if (text) {
3885 if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3886 var selected = cm.listSelections();
3887 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3888 if (selected) for (var i = 0; i < selected.length; ++i)
3889 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3890 cm.replaceSelection(text, "around", "paste");
3891 cm.display.input.focus();
3892 }
3893 }
3894 catch(e){}
3895 }
3896 }
3897
3898 function onDragStart(cm, e) {
3899 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3900 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3901
3902 e.dataTransfer.setData("Text", cm.getSelection());
3903
3904 // Use dummy image instead of default browsers image.
3905 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3906 if (e.dataTransfer.setDragImage && !safari) {
3907 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3908 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3909 if (presto) {
3910 img.width = img.height = 1;
3911 cm.display.wrapper.appendChild(img);
3912 // Force a relayout, or Opera won't use our image for some obscure reason
3913 img._top = img.offsetTop;
3914 }
3915 e.dataTransfer.setDragImage(img, 0, 0);
3916 if (presto) img.parentNode.removeChild(img);
3917 }
3918 }
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
3939 // SCROLL EVENTS
3940
3941 // Sync the scrollable area and scrollbars, ensure the viewport
3942 // covers the visible area.
3943 function setScrollTop(cm, val) {
3944 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3945 cm.doc.scrollTop = val;
3946 if (!gecko) updateDisplaySimple(cm, {top: val});
3947 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3948 cm.display.scrollbars.setScrollTop(val);
3949 if (gecko) updateDisplaySimple(cm);
3950 startWorker(cm, 100);
3951 }
3952 // Sync scroller and scrollbar, ensure the gutter elements are
3953 // aligned.
3954 function setScrollLeft(cm, val, isScroller) {
3955 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3956 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3957 cm.doc.scrollLeft = val;
3958 alignHorizontally(cm);
3959 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3960 cm.display.scrollbars.setScrollLeft(val);
3961 }
3962
3963 4433 // Since the delta values reported on mouse wheel events are
3964 4434 // unstandardized between browsers and even browser versions, and
3965 4435 // generally horribly unpredictable, this code starts by measuring
@@ -3976,24 +4446,24 b''
3976 4446 // know one. These don't have to be accurate -- the result of them
3977 4447 // being wrong would just be a slight flicker on the first wheel
3978 4448 // scroll (if it is large enough).
3979 if (ie) wheelPixelsPerUnit = -.53;
3980 else if (gecko) wheelPixelsPerUnit = 15;
3981 else if (chrome) wheelPixelsPerUnit = -.7;
3982 else if (safari) wheelPixelsPerUnit = -1/3;
3983
3984 var wheelEventDelta = function(e) {
4449 if (ie) { wheelPixelsPerUnit = -.53; }
4450 else if (gecko) { wheelPixelsPerUnit = 15; }
4451 else if (chrome) { wheelPixelsPerUnit = -.7; }
4452 else if (safari) { wheelPixelsPerUnit = -1/3; }
4453
4454 function wheelEventDelta(e) {
3985 4455 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3986 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
3987 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
3988 else if (dy == null) dy = e.wheelDelta;
3989 return {x: dx, y: dy};
3990 };
3991 CodeMirror.wheelEventPixels = function(e) {
4456 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
4457 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
4458 else if (dy == null) { dy = e.wheelDelta; }
4459 return {x: dx, y: dy}
4460 }
4461 function wheelEventPixels(e) {
3992 4462 var delta = wheelEventDelta(e);
3993 4463 delta.x *= wheelPixelsPerUnit;
3994 4464 delta.y *= wheelPixelsPerUnit;
3995 return delta;
3996 };
4465 return delta
4466 }
3997 4467
3998 4468 function onScrollWheel(cm, e) {
3999 4469 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
@@ -4002,7 +4472,7 b''
4002 4472 // Quit if there's nothing to scroll here
4003 4473 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4004 4474 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4005 if (!(dx && canScrollX || dy && canScrollY)) return;
4475 if (!(dx && canScrollX || dy && canScrollY)) { return }
4006 4476
4007 4477 // Webkit browsers on OS X abort momentum scrolls when the target
4008 4478 // of the scroll event is removed from the scrollable element.
@@ -4013,7 +4483,7 b''
4013 4483 for (var i = 0; i < view.length; i++) {
4014 4484 if (view[i].node == cur) {
4015 4485 cm.display.currentWheelTarget = cur;
4016 break outer;
4486 break outer
4017 4487 }
4018 4488 }
4019 4489 }
@@ -4027,16 +4497,16 b''
4027 4497 // better than glitching out.
4028 4498 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4029 4499 if (dy && canScrollY)
4030 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
4031 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
4500 { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
4501 setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
4032 4502 // Only prevent default scrolling if vertical scrolling is
4033 4503 // actually possible. Otherwise, it causes vertical scroll
4034 4504 // jitter on OSX trackpads when deltaX is small and deltaY
4035 4505 // is large (issue #3579)
4036 4506 if (!dy || (dy && canScrollY))
4037 e_preventDefault(e);
4507 { e_preventDefault(e); }
4038 4508 display.wheelStartX = null; // Abort measurement, if in progress
4039 return;
4509 return
4040 4510 }
4041 4511
4042 4512 // 'Project' the visible viewport to cover the area that is being
@@ -4044,8 +4514,8 b''
4044 4514 if (dy && wheelPixelsPerUnit != null) {
4045 4515 var pixels = dy * wheelPixelsPerUnit;
4046 4516 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4047 if (pixels < 0) top = Math.max(0, top + pixels - 50);
4048 else bot = Math.min(cm.doc.height, bot + pixels + 50);
4517 if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
4518 else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
4049 4519 updateDisplaySimple(cm, {top: top, bottom: bot});
4050 4520 }
4051 4521
@@ -4053,14 +4523,14 b''
4053 4523 if (display.wheelStartX == null) {
4054 4524 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4055 4525 display.wheelDX = dx; display.wheelDY = dy;
4056 setTimeout(function() {
4057 if (display.wheelStartX == null) return;
4526 setTimeout(function () {
4527 if (display.wheelStartX == null) { return }
4058 4528 var movedX = scroll.scrollLeft - display.wheelStartX;
4059 4529 var movedY = scroll.scrollTop - display.wheelStartY;
4060 4530 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4061 4531 (movedX && display.wheelDX && movedX / display.wheelDX);
4062 4532 display.wheelStartX = display.wheelStartY = null;
4063 if (!sample) return;
4533 if (!sample) { return }
4064 4534 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4065 4535 ++wheelSamples;
4066 4536 }, 200);
@@ -4070,226 +4540,109 b''
4070 4540 }
4071 4541 }
4072 4542
4073 // KEY EVENTS
4074
4075 // Run a handler that was bound to a key.
4076 function doHandleBinding(cm, bound, dropShift) {
4077 if (typeof bound == "string") {
4078 bound = commands[bound];
4079 if (!bound) return false;
4080 }
4081 // Ensure previous input has been read, so that the handler sees a
4082 // consistent view of the document
4083 cm.display.input.ensurePolled();
4084 var prevShift = cm.display.shift, done = false;
4085 try {
4086 if (cm.isReadOnly()) cm.state.suppressEdits = true;
4087 if (dropShift) cm.display.shift = false;
4088 done = bound(cm) != Pass;
4089 } finally {
4090 cm.display.shift = prevShift;
4091 cm.state.suppressEdits = false;
4092 }
4093 return done;
4094 }
4095
4096 function lookupKeyForEditor(cm, name, handle) {
4097 for (var i = 0; i < cm.state.keyMaps.length; i++) {
4098 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4099 if (result) return result;
4100 }
4101 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
4102 || lookupKey(name, cm.options.keyMap, handle, cm);
4103 }
4104
4105 var stopSeq = new Delayed;
4106 function dispatchKey(cm, name, e, handle) {
4107 var seq = cm.state.keySeq;
4108 if (seq) {
4109 if (isModifierKey(name)) return "handled";
4110 stopSeq.set(50, function() {
4111 if (cm.state.keySeq == seq) {
4112 cm.state.keySeq = null;
4113 cm.display.input.reset();
4114 }
4115 });
4116 name = seq + " " + name;
4117 }
4118 var result = lookupKeyForEditor(cm, name, handle);
4119
4120 if (result == "multi")
4121 cm.state.keySeq = name;
4122 if (result == "handled")
4123 signalLater(cm, "keyHandled", cm, name, e);
4124
4125 if (result == "handled" || result == "multi") {
4126 e_preventDefault(e);
4127 restartBlink(cm);
4128 }
4129
4130 if (seq && !result && /\'$/.test(name)) {
4131 e_preventDefault(e);
4132 return true;
4133 }
4134 return !!result;
4135 }
4136
4137 // Handle a key from the keydown event.
4138 function handleKeyBinding(cm, e) {
4139 var name = keyName(e, true);
4140 if (!name) return false;
4141
4142 if (e.shiftKey && !cm.state.keySeq) {
4143 // First try to resolve full name (including 'Shift-'). Failing
4144 // that, see if there is a cursor-motion command (starting with
4145 // 'go') bound to the keyname without 'Shift-'.
4146 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4147 || dispatchKey(cm, name, e, function(b) {
4148 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4149 return doHandleBinding(cm, b);
4150 });
4151 } else {
4152 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4153 }
4154 }
4155
4156 // Handle a key from the keypress event
4157 function handleCharBinding(cm, e, ch) {
4158 return dispatchKey(cm, "'" + ch + "'", e,
4159 function(b) { return doHandleBinding(cm, b, true); });
4160 }
4161
4162 var lastStoppedKey = null;
4163 function onKeyDown(e) {
4164 var cm = this;
4165 cm.curOp.focus = activeElt();
4166 if (signalDOMEvent(cm, e)) return;
4167 // IE does strange things with escape.
4168 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4169 var code = e.keyCode;
4170 cm.display.shift = code == 16 || e.shiftKey;
4171 var handled = handleKeyBinding(cm, e);
4172 if (presto) {
4173 lastStoppedKey = handled ? code : null;
4174 // Opera has no cut event... we try to at least catch the key combo
4175 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4176 cm.replaceSelection("", null, "cut");
4177 }
4178
4179 // Turn mouse into crosshair when Alt is held on Mac.
4180 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4181 showCrossHair(cm);
4182 }
4183
4184 function showCrossHair(cm) {
4185 var lineDiv = cm.display.lineDiv;
4186 addClass(lineDiv, "CodeMirror-crosshair");
4187
4188 function up(e) {
4189 if (e.keyCode == 18 || !e.altKey) {
4190 rmClass(lineDiv, "CodeMirror-crosshair");
4191 off(document, "keyup", up);
4192 off(document, "mouseover", up);
4193 }
4194 }
4195 on(document, "keyup", up);
4196 on(document, "mouseover", up);
4197 }
4198
4199 function onKeyUp(e) {
4200 if (e.keyCode == 16) this.doc.sel.shift = false;
4201 signalDOMEvent(this, e);
4202 }
4203
4204 function onKeyPress(e) {
4205 var cm = this;
4206 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4207 var keyCode = e.keyCode, charCode = e.charCode;
4208 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4209 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4210 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4211 if (handleCharBinding(cm, e, ch)) return;
4212 cm.display.input.onKeyPress(e);
4213 }
4214
4215 // FOCUS/BLUR EVENTS
4216
4217 function delayBlurEvent(cm) {
4218 cm.state.delayingBlurEvent = true;
4219 setTimeout(function() {
4220 if (cm.state.delayingBlurEvent) {
4221 cm.state.delayingBlurEvent = false;
4222 onBlur(cm);
4223 }
4224 }, 100);
4225 }
4226
4227 function onFocus(cm) {
4228 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4229
4230 if (cm.options.readOnly == "nocursor") return;
4231 if (!cm.state.focused) {
4232 signal(cm, "focus", cm);
4233 cm.state.focused = true;
4234 addClass(cm.display.wrapper, "CodeMirror-focused");
4235 // This test prevents this from firing when a context
4236 // menu is closed (since the input reset would kill the
4237 // select-all detection hack)
4238 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4239 cm.display.input.reset();
4240 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4241 }
4242 cm.display.input.receivedFocus();
4243 }
4244 restartBlink(cm);
4245 }
4246 function onBlur(cm) {
4247 if (cm.state.delayingBlurEvent) return;
4248
4249 if (cm.state.focused) {
4250 signal(cm, "blur", cm);
4251 cm.state.focused = false;
4252 rmClass(cm.display.wrapper, "CodeMirror-focused");
4253 }
4254 clearInterval(cm.display.blinker);
4255 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4256 }
4257
4258 // CONTEXT MENU HANDLING
4259
4260 // To make the context menu work, we need to briefly unhide the
4261 // textarea (making it as unobtrusive as possible) to let the
4262 // right-click take effect on it.
4263 function onContextMenu(cm, e) {
4264 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4265 if (signalDOMEvent(cm, e, "contextmenu")) return;
4266 cm.display.input.onContextMenu(e);
4267 }
4268
4269 function contextMenuInGutter(cm, e) {
4270 if (!hasHandler(cm, "gutterContextMenu")) return false;
4271 return gutterEvent(cm, e, "gutterContextMenu", false);
4272 }
4273
4274 // UPDATING
4543 // Selection objects are immutable. A new one is created every time
4544 // the selection changes. A selection is one or more non-overlapping
4545 // (and non-touching) ranges, sorted, and an integer that indicates
4546 // which one is the primary selection (the one that's scrolled into
4547 // view, that getCursor returns, etc).
4548 var Selection = function(ranges, primIndex) {
4549 this.ranges = ranges;
4550 this.primIndex = primIndex;
4551 };
4552
4553 Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4554
4555 Selection.prototype.equals = function (other) {
4556 var this$1 = this;
4557
4558 if (other == this) { return true }
4559 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4560 for (var i = 0; i < this.ranges.length; i++) {
4561 var here = this$1.ranges[i], there = other.ranges[i];
4562 if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4563 }
4564 return true
4565 };
4566
4567 Selection.prototype.deepCopy = function () {
4568 var this$1 = this;
4569
4570 var out = [];
4571 for (var i = 0; i < this.ranges.length; i++)
4572 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
4573 return new Selection(out, this.primIndex)
4574 };
4575
4576 Selection.prototype.somethingSelected = function () {
4577 var this$1 = this;
4578
4579 for (var i = 0; i < this.ranges.length; i++)
4580 { if (!this$1.ranges[i].empty()) { return true } }
4581 return false
4582 };
4583
4584 Selection.prototype.contains = function (pos, end) {
4585 var this$1 = this;
4586
4587 if (!end) { end = pos; }
4588 for (var i = 0; i < this.ranges.length; i++) {
4589 var range = this$1.ranges[i];
4590 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4591 { return i }
4592 }
4593 return -1
4594 };
4595
4596 var Range = function(anchor, head) {
4597 this.anchor = anchor; this.head = head;
4598 };
4599
4600 Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4601 Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4602 Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4603
4604 // Take an unsorted, potentially overlapping set of ranges, and
4605 // build a selection out of it. 'Consumes' ranges array (modifying
4606 // it).
4607 function normalizeSelection(cm, ranges, primIndex) {
4608 var mayTouch = cm && cm.options.selectionsMayTouch;
4609 var prim = ranges[primIndex];
4610 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
4611 primIndex = indexOf(ranges, prim);
4612 for (var i = 1; i < ranges.length; i++) {
4613 var cur = ranges[i], prev = ranges[i - 1];
4614 var diff = cmp(prev.to(), cur.from());
4615 if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
4616 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4617 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4618 if (i <= primIndex) { --primIndex; }
4619 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4620 }
4621 }
4622 return new Selection(ranges, primIndex)
4623 }
4624
4625 function simpleSelection(anchor, head) {
4626 return new Selection([new Range(anchor, head || anchor)], 0)
4627 }
4275 4628
4276 4629 // Compute the position of the end of a change (its 'to' property
4277 4630 // refers to the pre-change end).
4278 var changeEnd = CodeMirror.changeEnd = function(change) {
4279 if (!change.text) return change.to;
4631 function changeEnd(change) {
4632 if (!change.text) { return change.to }
4280 4633 return Pos(change.from.line + change.text.length - 1,
4281 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
4282 };
4634 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4635 }
4283 4636
4284 4637 // Adjust a position to refer to the post-change position of the
4285 4638 // same text, or the end of the change if the change covers it.
4286 4639 function adjustForChange(pos, change) {
4287 if (cmp(pos, change.from) < 0) return pos;
4288 if (cmp(pos, change.to) <= 0) return changeEnd(change);
4640 if (cmp(pos, change.from) < 0) { return pos }
4641 if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4289 4642
4290 4643 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4291 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4292 return Pos(line, ch);
4644 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
4645 return Pos(line, ch)
4293 4646 }
4294 4647
4295 4648 function computeSelAfterChange(doc, change) {
@@ -4299,14 +4652,14 b''
4299 4652 out.push(new Range(adjustForChange(range.anchor, change),
4300 4653 adjustForChange(range.head, change)));
4301 4654 }
4302 return normalizeSelection(out, doc.sel.primIndex);
4655 return normalizeSelection(doc.cm, out, doc.sel.primIndex)
4303 4656 }
4304 4657
4305 4658 function offsetPos(pos, old, nw) {
4306 4659 if (pos.line == old.line)
4307 return Pos(nw.line, pos.ch - old.ch + nw.ch);
4660 { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4308 4661 else
4309 return Pos(nw.line + (pos.line - old.line), pos.ch);
4662 { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4310 4663 }
4311 4664
4312 4665 // Used by replaceSelections to allow moving the selection to the
@@ -4327,2823 +4680,25 b''
4327 4680 out[i] = new Range(from, from);
4328 4681 }
4329 4682 }
4330 return new Selection(out, doc.sel.primIndex);
4331 }
4332
4333 // Allow "beforeChange" event handlers to influence a change
4334 function filterChange(doc, change, update) {
4335 var obj = {
4336 canceled: false,
4337 from: change.from,
4338 to: change.to,
4339 text: change.text,
4340 origin: change.origin,
4341 cancel: function() { this.canceled = true; }
4342 };
4343 if (update) obj.update = function(from, to, text, origin) {
4344 if (from) this.from = clipPos(doc, from);
4345 if (to) this.to = clipPos(doc, to);
4346 if (text) this.text = text;
4347 if (origin !== undefined) this.origin = origin;
4348 };
4349 signal(doc, "beforeChange", doc, obj);
4350 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4351
4352 if (obj.canceled) return null;
4353 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4354 }
4355
4356 // Apply a change to a document, and add it to the document's
4357 // history, and propagating it to all linked documents.
4358 function makeChange(doc, change, ignoreReadOnly) {
4359 if (doc.cm) {
4360 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4361 if (doc.cm.state.suppressEdits) return;
4362 }
4363
4364 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4365 change = filterChange(doc, change, true);
4366 if (!change) return;
4367 }
4368
4369 // Possibly split or suppress the update based on the presence
4370 // of read-only spans in its range.
4371 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4372 if (split) {
4373 for (var i = split.length - 1; i >= 0; --i)
4374 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
4375 } else {
4376 makeChangeInner(doc, change);
4377 }
4378 }
4379
4380 function makeChangeInner(doc, change) {
4381 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
4382 var selAfter = computeSelAfterChange(doc, change);
4383 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4384
4385 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4386 var rebased = [];
4387
4388 linkedDocs(doc, function(doc, sharedHist) {
4389 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4390 rebaseHist(doc.history, change);
4391 rebased.push(doc.history);
4392 }
4393 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4394 });
4395 }
4396
4397 // Revert a change stored in a document's history.
4398 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4399 if (doc.cm && doc.cm.state.suppressEdits) return;
4400
4401 var hist = doc.history, event, selAfter = doc.sel;
4402 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4403
4404 // Verify that there is a useable event (so that ctrl-z won't
4405 // needlessly clear selection events)
4406 for (var i = 0; i < source.length; i++) {
4407 event = source[i];
4408 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4409 break;
4410 }
4411 if (i == source.length) return;
4412 hist.lastOrigin = hist.lastSelOrigin = null;
4413
4414 for (;;) {
4415 event = source.pop();
4416 if (event.ranges) {
4417 pushSelectionToHistory(event, dest);
4418 if (allowSelectionOnly && !event.equals(doc.sel)) {
4419 setSelection(doc, event, {clearRedo: false});
4420 return;
4421 }
4422 selAfter = event;
4423 }
4424 else break;
4425 }
4426
4427 // Build up a reverse change object to add to the opposite history
4428 // stack (redo when undoing, and vice versa).
4429 var antiChanges = [];
4430 pushSelectionToHistory(selAfter, dest);
4431 dest.push({changes: antiChanges, generation: hist.generation});
4432 hist.generation = event.generation || ++hist.maxGeneration;
4433
4434 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4435
4436 for (var i = event.changes.length - 1; i >= 0; --i) {
4437 var change = event.changes[i];
4438 change.origin = type;
4439 if (filter && !filterChange(doc, change, false)) {
4440 source.length = 0;
4441 return;
4442 }
4443
4444 antiChanges.push(historyChangeFromChange(doc, change));
4445
4446 var after = i ? computeSelAfterChange(doc, change) : lst(source);
4447 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4448 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4449 var rebased = [];
4450
4451 // Propagate to the linked documents
4452 linkedDocs(doc, function(doc, sharedHist) {
4453 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4454 rebaseHist(doc.history, change);
4455 rebased.push(doc.history);
4456 }
4457 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4458 });
4459 }
4460 }
4461
4462 // Sub-views need their line numbers shifted when text is added
4463 // above or below them in the parent document.
4464 function shiftDoc(doc, distance) {
4465 if (distance == 0) return;
4466 doc.first += distance;
4467 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4468 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4469 Pos(range.head.line + distance, range.head.ch));
4470 }), doc.sel.primIndex);
4471 if (doc.cm) {
4472 regChange(doc.cm, doc.first, doc.first - distance, distance);
4473 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4474 regLineChange(doc.cm, l, "gutter");
4475 }
4476 }
4477
4478 // More lower-level change function, handling only a single document
4479 // (not linked ones).
4480 function makeChangeSingleDoc(doc, change, selAfter, spans) {
4481 if (doc.cm && !doc.cm.curOp)
4482 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4483
4484 if (change.to.line < doc.first) {
4485 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4486 return;
4487 }
4488 if (change.from.line > doc.lastLine()) return;
4489
4490 // Clip the change to the size of this doc
4491 if (change.from.line < doc.first) {
4492 var shift = change.text.length - 1 - (doc.first - change.from.line);
4493 shiftDoc(doc, shift);
4494 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4495 text: [lst(change.text)], origin: change.origin};
4496 }
4497 var last = doc.lastLine();
4498 if (change.to.line > last) {
4499 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4500 text: [change.text[0]], origin: change.origin};
4501 }
4502
4503 change.removed = getBetween(doc, change.from, change.to);
4504
4505 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4506 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4507 else updateDoc(doc, change, spans);
4508 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4509 }
4510
4511 // Handle the interaction of a change to a document with the editor
4512 // that this document is part of.
4513 function makeChangeSingleDocInEditor(cm, change, spans) {
4514 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4515
4516 var recomputeMaxLength = false, checkWidthStart = from.line;
4517 if (!cm.options.lineWrapping) {
4518 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4519 doc.iter(checkWidthStart, to.line + 1, function(line) {
4520 if (line == display.maxLine) {
4521 recomputeMaxLength = true;
4522 return true;
4523 }
4524 });
4525 }
4526
4527 if (doc.sel.contains(change.from, change.to) > -1)
4528 signalCursorActivity(cm);
4529
4530 updateDoc(doc, change, spans, estimateHeight(cm));
4531
4532 if (!cm.options.lineWrapping) {
4533 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4534 var len = lineLength(line);
4535 if (len > display.maxLineLength) {
4536 display.maxLine = line;
4537 display.maxLineLength = len;
4538 display.maxLineChanged = true;
4539 recomputeMaxLength = false;
4540 }
4541 });
4542 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4543 }
4544
4545 // Adjust frontier, schedule worker
4546 doc.frontier = Math.min(doc.frontier, from.line);
4547 startWorker(cm, 400);
4548
4549 var lendiff = change.text.length - (to.line - from.line) - 1;
4550 // Remember that these lines changed, for updating the display
4551 if (change.full)
4552 regChange(cm);
4553 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
4554 regLineChange(cm, from.line, "text");
4555 else
4556 regChange(cm, from.line, to.line + 1, lendiff);
4557
4558 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4559 if (changeHandler || changesHandler) {
4560 var obj = {
4561 from: from, to: to,
4562 text: change.text,
4563 removed: change.removed,
4564 origin: change.origin
4565 };
4566 if (changeHandler) signalLater(cm, "change", cm, obj);
4567 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4568 }
4569 cm.display.selForContextMenu = null;
4570 }
4571
4572 function replaceRange(doc, code, from, to, origin) {
4573 if (!to) to = from;
4574 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4575 if (typeof code == "string") code = doc.splitLines(code);
4576 makeChange(doc, {from: from, to: to, text: code, origin: origin});
4577 }
4578
4579 // SCROLLING THINGS INTO VIEW
4580
4581 // If an editor sits on the top or bottom of the window, partially
4582 // scrolled out of view, this ensures that the cursor is visible.
4583 function maybeScrollWindow(cm, coords) {
4584 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4585
4586 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4587 if (coords.top + box.top < 0) doScroll = true;
4588 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4589 if (doScroll != null && !phantom) {
4590 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4591 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4592 (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4593 coords.left + "px; width: 2px;");
4594 cm.display.lineSpace.appendChild(scrollNode);
4595 scrollNode.scrollIntoView(doScroll);
4596 cm.display.lineSpace.removeChild(scrollNode);
4597 }
4598 }
4599
4600 // Scroll a given position into view (immediately), verifying that
4601 // it actually became visible (as line heights are accurately
4602 // measured, the position of something may 'drift' during drawing).
4603 function scrollPosIntoView(cm, pos, end, margin) {
4604 if (margin == null) margin = 0;
4605 for (var limit = 0; limit < 5; limit++) {
4606 var changed = false, coords = cursorCoords(cm, pos);
4607 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4608 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4609 Math.min(coords.top, endCoords.top) - margin,
4610 Math.max(coords.left, endCoords.left),
4611 Math.max(coords.bottom, endCoords.bottom) + margin);
4612 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4613 if (scrollPos.scrollTop != null) {
4614 setScrollTop(cm, scrollPos.scrollTop);
4615 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4616 }
4617 if (scrollPos.scrollLeft != null) {
4618 setScrollLeft(cm, scrollPos.scrollLeft);
4619 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4620 }
4621 if (!changed) break;
4622 }
4623 return coords;
4624 }
4625
4626 // Scroll a given set of coordinates into view (immediately).
4627 function scrollIntoView(cm, x1, y1, x2, y2) {
4628 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4629 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4630 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4631 }
4632
4633 // Calculate a new scroll position needed to scroll the given
4634 // rectangle into view. Returns an object with scrollTop and
4635 // scrollLeft properties. When these are undefined, the
4636 // vertical/horizontal position does not need to be adjusted.
4637 function calculateScrollPos(cm, x1, y1, x2, y2) {
4638 var display = cm.display, snapMargin = textHeight(cm.display);
4639 if (y1 < 0) y1 = 0;
4640 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4641 var screen = displayHeight(cm), result = {};
4642 if (y2 - y1 > screen) y2 = y1 + screen;
4643 var docBottom = cm.doc.height + paddingVert(display);
4644 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4645 if (y1 < screentop) {
4646 result.scrollTop = atTop ? 0 : y1;
4647 } else if (y2 > screentop + screen) {
4648 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4649 if (newTop != screentop) result.scrollTop = newTop;
4650 }
4651
4652 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4653 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4654 var tooWide = x2 - x1 > screenw;
4655 if (tooWide) x2 = x1 + screenw;
4656 if (x1 < 10)
4657 result.scrollLeft = 0;
4658 else if (x1 < screenleft)
4659 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4660 else if (x2 > screenw + screenleft - 3)
4661 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4662 return result;
4663 }
4664
4665 // Store a relative adjustment to the scroll position in the current
4666 // operation (to be applied when the operation finishes).
4667 function addToScrollPos(cm, left, top) {
4668 if (left != null || top != null) resolveScrollToPos(cm);
4669 if (left != null)
4670 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
4671 if (top != null)
4672 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4673 }
4674
4675 // Make sure that at the end of the operation the current cursor is
4676 // shown.
4677 function ensureCursorVisible(cm) {
4678 resolveScrollToPos(cm);
4679 var cur = cm.getCursor(), from = cur, to = cur;
4680 if (!cm.options.lineWrapping) {
4681 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4682 to = Pos(cur.line, cur.ch + 1);
4683 }
4684 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4685 }
4686
4687 // When an operation has its scrollToPos property set, and another
4688 // scroll action is applied before the end of the operation, this
4689 // 'simulates' scrolling that position into view in a cheap way, so
4690 // that the effect of intermediate scroll commands is not ignored.
4691 function resolveScrollToPos(cm) {
4692 var range = cm.curOp.scrollToPos;
4693 if (range) {
4694 cm.curOp.scrollToPos = null;
4695 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4696 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4697 Math.min(from.top, to.top) - range.margin,
4698 Math.max(from.right, to.right),
4699 Math.max(from.bottom, to.bottom) + range.margin);
4700 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4701 }
4702 }
4703
4704 // API UTILITIES
4705
4706 // Indent the given line. The how parameter can be "smart",
4707 // "add"/null, "subtract", or "prev". When aggressive is false
4708 // (typically set to true for forced single-line indents), empty
4709 // lines are not indented, and places where the mode returns Pass
4710 // are left alone.
4711 function indentLine(cm, n, how, aggressive) {
4712 var doc = cm.doc, state;
4713 if (how == null) how = "add";
4714 if (how == "smart") {
4715 // Fall back to "prev" when the mode doesn't have an indentation
4716 // method.
4717 if (!doc.mode.indent) how = "prev";
4718 else state = getStateBefore(cm, n);
4719 }
4720
4721 var tabSize = cm.options.tabSize;
4722 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4723 if (line.stateAfter) line.stateAfter = null;
4724 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4725 if (!aggressive && !/\S/.test(line.text)) {
4726 indentation = 0;
4727 how = "not";
4728 } else if (how == "smart") {
4729 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
4730 if (indentation == Pass || indentation > 150) {
4731 if (!aggressive) return;
4732 how = "prev";
4733 }
4734 }
4735 if (how == "prev") {
4736 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4737 else indentation = 0;
4738 } else if (how == "add") {
4739 indentation = curSpace + cm.options.indentUnit;
4740 } else if (how == "subtract") {
4741 indentation = curSpace - cm.options.indentUnit;
4742 } else if (typeof how == "number") {
4743 indentation = curSpace + how;
4744 }
4745 indentation = Math.max(0, indentation);
4746
4747 var indentString = "", pos = 0;
4748 if (cm.options.indentWithTabs)
4749 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4750 if (pos < indentation) indentString += spaceStr(indentation - pos);
4751
4752 if (indentString != curSpaceString) {
4753 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4754 line.stateAfter = null;
4755 return true;
4756 } else {
4757 // Ensure that, if the cursor was in the whitespace at the start
4758 // of the line, it is moved to the end of that space.
4759 for (var i = 0; i < doc.sel.ranges.length; i++) {
4760 var range = doc.sel.ranges[i];
4761 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4762 var pos = Pos(n, curSpaceString.length);
4763 replaceOneSelection(doc, i, new Range(pos, pos));
4764 break;
4765 }
4766 }
4767 }
4768 }
4769
4770 // Utility for applying a change to a line by handle or number,
4771 // returning the number and optionally registering the line as
4772 // changed.
4773 function changeLine(doc, handle, changeType, op) {
4774 var no = handle, line = handle;
4775 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4776 else no = lineNo(handle);
4777 if (no == null) return null;
4778 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4779 return line;
4780 }
4781
4782 // Helper for deleting text near the selection(s), used to implement
4783 // backspace, delete, and similar functionality.
4784 function deleteNearSelection(cm, compute) {
4785 var ranges = cm.doc.sel.ranges, kill = [];
4786 // Build up a set of ranges to kill first, merging overlapping
4787 // ranges.
4788 for (var i = 0; i < ranges.length; i++) {
4789 var toKill = compute(ranges[i]);
4790 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4791 var replaced = kill.pop();
4792 if (cmp(replaced.from, toKill.from) < 0) {
4793 toKill.from = replaced.from;
4794 break;
4795 }
4796 }
4797 kill.push(toKill);
4798 }
4799 // Next, remove those actual ranges.
4800 runInOp(cm, function() {
4801 for (var i = kill.length - 1; i >= 0; i--)
4802 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4803 ensureCursorVisible(cm);
4804 });
4805 }
4806
4807 // Used for horizontal relative motion. Dir is -1 or 1 (left or
4808 // right), unit can be "char", "column" (like char, but doesn't
4809 // cross line boundaries), "word" (across next word), or "group" (to
4810 // the start of next group of word or non-word-non-whitespace
4811 // chars). The visually param controls whether, in right-to-left
4812 // text, direction 1 means to move towards the next index in the
4813 // string, or towards the character to the right of the current
4814 // position. The resulting position will have a hitSide=true
4815 // property if it reached the end of the document.
4816 function findPosH(doc, pos, dir, unit, visually) {
4817 var line = pos.line, ch = pos.ch, origDir = dir;
4818 var lineObj = getLine(doc, line);
4819 function findNextLine() {
4820 var l = line + dir;
4821 if (l < doc.first || l >= doc.first + doc.size) return false
4822 line = l;
4823 return lineObj = getLine(doc, l);
4824 }
4825 function moveOnce(boundToLine) {
4826 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4827 if (next == null) {
4828 if (!boundToLine && findNextLine()) {
4829 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4830 else ch = dir < 0 ? lineObj.text.length : 0;
4831 } else return false
4832 } else ch = next;
4833 return true;
4834 }
4835
4836 if (unit == "char") {
4837 moveOnce()
4838 } else if (unit == "column") {
4839 moveOnce(true)
4840 } else if (unit == "word" || unit == "group") {
4841 var sawType = null, group = unit == "group";
4842 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4843 for (var first = true;; first = false) {
4844 if (dir < 0 && !moveOnce(!first)) break;
4845 var cur = lineObj.text.charAt(ch) || "\n";
4846 var type = isWordChar(cur, helper) ? "w"
4847 : group && cur == "\n" ? "n"
4848 : !group || /\s/.test(cur) ? null
4849 : "p";
4850 if (group && !first && !type) type = "s";
4851 if (sawType && sawType != type) {
4852 if (dir < 0) {dir = 1; moveOnce();}
4853 break;
4854 }
4855
4856 if (type) sawType = type;
4857 if (dir > 0 && !moveOnce(!first)) break;
4858 }
4859 }
4860 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
4861 if (!cmp(pos, result)) result.hitSide = true;
4862 return result;
4863 }
4864
4865 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4866 // "page" or "line". The resulting position will have a hitSide=true
4867 // property if it reached the end of the document.
4868 function findPosV(cm, pos, dir, unit) {
4869 var doc = cm.doc, x = pos.left, y;
4870 if (unit == "page") {
4871 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4872 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4873 } else if (unit == "line") {
4874 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4875 }
4876 for (;;) {
4877 var target = coordsChar(cm, x, y);
4878 if (!target.outside) break;
4879 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4880 y += dir * 5;
4881 }
4882 return target;
4883 }
4884
4885 // EDITOR METHODS
4886
4887 // The publicly visible API. Note that methodOp(f) means
4888 // 'wrap f in an operation, performed on its `this` parameter'.
4889
4890 // This is not the complete set of editor methods. Most of the
4891 // methods defined on the Doc type are also injected into
4892 // CodeMirror.prototype, for backwards compatibility and
4893 // convenience.
4894
4895 CodeMirror.prototype = {
4896 constructor: CodeMirror,
4897 focus: function(){window.focus(); this.display.input.focus();},
4898
4899 setOption: function(option, value) {
4900 var options = this.options, old = options[option];
4901 if (options[option] == value && option != "mode") return;
4902 options[option] = value;
4903 if (optionHandlers.hasOwnProperty(option))
4904 operation(this, optionHandlers[option])(this, value, old);
4905 },
4906
4907 getOption: function(option) {return this.options[option];},
4908 getDoc: function() {return this.doc;},
4909
4910 addKeyMap: function(map, bottom) {
4911 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4912 },
4913 removeKeyMap: function(map) {
4914 var maps = this.state.keyMaps;
4915 for (var i = 0; i < maps.length; ++i)
4916 if (maps[i] == map || maps[i].name == map) {
4917 maps.splice(i, 1);
4918 return true;
4919 }
4920 },
4921
4922 addOverlay: methodOp(function(spec, options) {
4923 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4924 if (mode.startState) throw new Error("Overlays may not be stateful.");
4925 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4926 this.state.modeGen++;
4927 regChange(this);
4928 }),
4929 removeOverlay: methodOp(function(spec) {
4930 var overlays = this.state.overlays;
4931 for (var i = 0; i < overlays.length; ++i) {
4932 var cur = overlays[i].modeSpec;
4933 if (cur == spec || typeof spec == "string" && cur.name == spec) {
4934 overlays.splice(i, 1);
4935 this.state.modeGen++;
4936 regChange(this);
4937 return;
4938 }
4939 }
4940 }),
4941
4942 indentLine: methodOp(function(n, dir, aggressive) {
4943 if (typeof dir != "string" && typeof dir != "number") {
4944 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4945 else dir = dir ? "add" : "subtract";
4946 }
4947 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4948 }),
4949 indentSelection: methodOp(function(how) {
4950 var ranges = this.doc.sel.ranges, end = -1;
4951 for (var i = 0; i < ranges.length; i++) {
4952 var range = ranges[i];
4953 if (!range.empty()) {
4954 var from = range.from(), to = range.to();
4955 var start = Math.max(end, from.line);
4956 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4957 for (var j = start; j < end; ++j)
4958 indentLine(this, j, how);
4959 var newRanges = this.doc.sel.ranges;
4960 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4961 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4962 } else if (range.head.line > end) {
4963 indentLine(this, range.head.line, how, true);
4964 end = range.head.line;
4965 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4966 }
4967 }
4968 }),
4969
4970 // Fetch the parser token for a given character. Useful for hacks
4971 // that want to inspect the mode state (say, for completion).
4972 getTokenAt: function(pos, precise) {
4973 return takeToken(this, pos, precise);
4974 },
4975
4976 getLineTokens: function(line, precise) {
4977 return takeToken(this, Pos(line), precise, true);
4978 },
4979
4980 getTokenTypeAt: function(pos) {
4981 pos = clipPos(this.doc, pos);
4982 var styles = getLineStyles(this, getLine(this.doc, pos.line));
4983 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4984 var type;
4985 if (ch == 0) type = styles[2];
4986 else for (;;) {
4987 var mid = (before + after) >> 1;
4988 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4989 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4990 else { type = styles[mid * 2 + 2]; break; }
4991 }
4992 var cut = type ? type.indexOf("cm-overlay ") : -1;
4993 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
4994 },
4995
4996 getModeAt: function(pos) {
4997 var mode = this.doc.mode;
4998 if (!mode.innerMode) return mode;
4999 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
5000 },
5001
5002 getHelper: function(pos, type) {
5003 return this.getHelpers(pos, type)[0];
5004 },
5005
5006 getHelpers: function(pos, type) {
5007 var found = [];
5008 if (!helpers.hasOwnProperty(type)) return found;
5009 var help = helpers[type], mode = this.getModeAt(pos);
5010 if (typeof mode[type] == "string") {
5011 if (help[mode[type]]) found.push(help[mode[type]]);
5012 } else if (mode[type]) {
5013 for (var i = 0; i < mode[type].length; i++) {
5014 var val = help[mode[type][i]];
5015 if (val) found.push(val);
5016 }
5017 } else if (mode.helperType && help[mode.helperType]) {
5018 found.push(help[mode.helperType]);
5019 } else if (help[mode.name]) {
5020 found.push(help[mode.name]);
5021 }
5022 for (var i = 0; i < help._global.length; i++) {
5023 var cur = help._global[i];
5024 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
5025 found.push(cur.val);
5026 }
5027 return found;
5028 },
5029
5030 getStateAfter: function(line, precise) {
5031 var doc = this.doc;
5032 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
5033 return getStateBefore(this, line + 1, precise);
5034 },
5035
5036 cursorCoords: function(start, mode) {
5037 var pos, range = this.doc.sel.primary();
5038 if (start == null) pos = range.head;
5039 else if (typeof start == "object") pos = clipPos(this.doc, start);
5040 else pos = start ? range.from() : range.to();
5041 return cursorCoords(this, pos, mode || "page");
5042 },
5043
5044 charCoords: function(pos, mode) {
5045 return charCoords(this, clipPos(this.doc, pos), mode || "page");
5046 },
5047
5048 coordsChar: function(coords, mode) {
5049 coords = fromCoordSystem(this, coords, mode || "page");
5050 return coordsChar(this, coords.left, coords.top);
5051 },
5052
5053 lineAtHeight: function(height, mode) {
5054 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
5055 return lineAtHeight(this.doc, height + this.display.viewOffset);
5056 },
5057 heightAtLine: function(line, mode) {
5058 var end = false, lineObj;
5059 if (typeof line == "number") {
5060 var last = this.doc.first + this.doc.size - 1;
5061 if (line < this.doc.first) line = this.doc.first;
5062 else if (line > last) { line = last; end = true; }
5063 lineObj = getLine(this.doc, line);
5064 } else {
5065 lineObj = line;
5066 }
5067 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
5068 (end ? this.doc.height - heightAtLine(lineObj) : 0);
5069 },
5070
5071 defaultTextHeight: function() { return textHeight(this.display); },
5072 defaultCharWidth: function() { return charWidth(this.display); },
5073
5074 setGutterMarker: methodOp(function(line, gutterID, value) {
5075 return changeLine(this.doc, line, "gutter", function(line) {
5076 var markers = line.gutterMarkers || (line.gutterMarkers = {});
5077 markers[gutterID] = value;
5078 if (!value && isEmpty(markers)) line.gutterMarkers = null;
5079 return true;
5080 });
5081 }),
5082
5083 clearGutter: methodOp(function(gutterID) {
5084 var cm = this, doc = cm.doc, i = doc.first;
5085 doc.iter(function(line) {
5086 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5087 line.gutterMarkers[gutterID] = null;
5088 regLineChange(cm, i, "gutter");
5089 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
5090 }
5091 ++i;
5092 });
5093 }),
5094
5095 lineInfo: function(line) {
5096 if (typeof line == "number") {
5097 if (!isLine(this.doc, line)) return null;
5098 var n = line;
5099 line = getLine(this.doc, line);
5100 if (!line) return null;
5101 } else {
5102 var n = lineNo(line);
5103 if (n == null) return null;
5104 }
5105 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5106 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5107 widgets: line.widgets};
5108 },
5109
5110 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5111
5112 addWidget: function(pos, node, scroll, vert, horiz) {
5113 var display = this.display;
5114 pos = cursorCoords(this, clipPos(this.doc, pos));
5115 var top = pos.bottom, left = pos.left;
5116 node.style.position = "absolute";
5117 node.setAttribute("cm-ignore-events", "true");
5118 this.display.input.setUneditable(node);
5119 display.sizer.appendChild(node);
5120 if (vert == "over") {
5121 top = pos.top;
5122 } else if (vert == "above" || vert == "near") {
5123 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5124 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5125 // Default to positioning above (if specified and possible); otherwise default to positioning below
5126 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5127 top = pos.top - node.offsetHeight;
5128 else if (pos.bottom + node.offsetHeight <= vspace)
5129 top = pos.bottom;
5130 if (left + node.offsetWidth > hspace)
5131 left = hspace - node.offsetWidth;
5132 }
5133 node.style.top = top + "px";
5134 node.style.left = node.style.right = "";
5135 if (horiz == "right") {
5136 left = display.sizer.clientWidth - node.offsetWidth;
5137 node.style.right = "0px";
5138 } else {
5139 if (horiz == "left") left = 0;
5140 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5141 node.style.left = left + "px";
5142 }
5143 if (scroll)
5144 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5145 },
5146
5147 triggerOnKeyDown: methodOp(onKeyDown),
5148 triggerOnKeyPress: methodOp(onKeyPress),
5149 triggerOnKeyUp: onKeyUp,
5150
5151 execCommand: function(cmd) {
5152 if (commands.hasOwnProperty(cmd))
5153 return commands[cmd].call(null, this);
5154 },
5155
5156 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5157
5158 findPosH: function(from, amount, unit, visually) {
5159 var dir = 1;
5160 if (amount < 0) { dir = -1; amount = -amount; }
5161 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5162 cur = findPosH(this.doc, cur, dir, unit, visually);
5163 if (cur.hitSide) break;
5164 }
5165 return cur;
5166 },
5167
5168 moveH: methodOp(function(dir, unit) {
5169 var cm = this;
5170 cm.extendSelectionsBy(function(range) {
5171 if (cm.display.shift || cm.doc.extend || range.empty())
5172 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5173 else
5174 return dir < 0 ? range.from() : range.to();
5175 }, sel_move);
5176 }),
5177
5178 deleteH: methodOp(function(dir, unit) {
5179 var sel = this.doc.sel, doc = this.doc;
5180 if (sel.somethingSelected())
5181 doc.replaceSelection("", null, "+delete");
5182 else
5183 deleteNearSelection(this, function(range) {
5184 var other = findPosH(doc, range.head, dir, unit, false);
5185 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5186 });
5187 }),
5188
5189 findPosV: function(from, amount, unit, goalColumn) {
5190 var dir = 1, x = goalColumn;
5191 if (amount < 0) { dir = -1; amount = -amount; }
5192 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5193 var coords = cursorCoords(this, cur, "div");
5194 if (x == null) x = coords.left;
5195 else coords.left = x;
5196 cur = findPosV(this, coords, dir, unit);
5197 if (cur.hitSide) break;
5198 }
5199 return cur;
5200 },
5201
5202 moveV: methodOp(function(dir, unit) {
5203 var cm = this, doc = this.doc, goals = [];
5204 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5205 doc.extendSelectionsBy(function(range) {
5206 if (collapse)
5207 return dir < 0 ? range.from() : range.to();
5208 var headPos = cursorCoords(cm, range.head, "div");
5209 if (range.goalColumn != null) headPos.left = range.goalColumn;
5210 goals.push(headPos.left);
5211 var pos = findPosV(cm, headPos, dir, unit);
5212 if (unit == "page" && range == doc.sel.primary())
5213 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5214 return pos;
5215 }, sel_move);
5216 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5217 doc.sel.ranges[i].goalColumn = goals[i];
5218 }),
5219
5220 // Find the word at the given position (as returned by coordsChar).
5221 findWordAt: function(pos) {
5222 var doc = this.doc, line = getLine(doc, pos.line).text;
5223 var start = pos.ch, end = pos.ch;
5224 if (line) {
5225 var helper = this.getHelper(pos, "wordChars");
5226 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5227 var startChar = line.charAt(start);
5228 var check = isWordChar(startChar, helper)
5229 ? function(ch) { return isWordChar(ch, helper); }
5230 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5231 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5232 while (start > 0 && check(line.charAt(start - 1))) --start;
5233 while (end < line.length && check(line.charAt(end))) ++end;
5234 }
5235 return new Range(Pos(pos.line, start), Pos(pos.line, end));
5236 },
5237
5238 toggleOverwrite: function(value) {
5239 if (value != null && value == this.state.overwrite) return;
5240 if (this.state.overwrite = !this.state.overwrite)
5241 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5242 else
5243 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5244
5245 signal(this, "overwriteToggle", this, this.state.overwrite);
5246 },
5247 hasFocus: function() { return this.display.input.getField() == activeElt(); },
5248 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); },
5249
5250 scrollTo: methodOp(function(x, y) {
5251 if (x != null || y != null) resolveScrollToPos(this);
5252 if (x != null) this.curOp.scrollLeft = x;
5253 if (y != null) this.curOp.scrollTop = y;
5254 }),
5255 getScrollInfo: function() {
5256 var scroller = this.display.scroller;
5257 return {left: scroller.scrollLeft, top: scroller.scrollTop,
5258 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5259 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5260 clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5261 },
5262
5263 scrollIntoView: methodOp(function(range, margin) {
5264 if (range == null) {
5265 range = {from: this.doc.sel.primary().head, to: null};
5266 if (margin == null) margin = this.options.cursorScrollMargin;
5267 } else if (typeof range == "number") {
5268 range = {from: Pos(range, 0), to: null};
5269 } else if (range.from == null) {
5270 range = {from: range, to: null};
5271 }
5272 if (!range.to) range.to = range.from;
5273 range.margin = margin || 0;
5274
5275 if (range.from.line != null) {
5276 resolveScrollToPos(this);
5277 this.curOp.scrollToPos = range;
5278 } else {
5279 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5280 Math.min(range.from.top, range.to.top) - range.margin,
5281 Math.max(range.from.right, range.to.right),
5282 Math.max(range.from.bottom, range.to.bottom) + range.margin);
5283 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5284 }
5285 }),
5286
5287 setSize: methodOp(function(width, height) {
5288 var cm = this;
5289 function interpret(val) {
5290 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5291 }
5292 if (width != null) cm.display.wrapper.style.width = interpret(width);
5293 if (height != null) cm.display.wrapper.style.height = interpret(height);
5294 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5295 var lineNo = cm.display.viewFrom;
5296 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5297 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5298 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5299 ++lineNo;
5300 });
5301 cm.curOp.forceUpdate = true;
5302 signal(cm, "refresh", this);
5303 }),
5304
5305 operation: function(f){return runInOp(this, f);},
5306
5307 refresh: methodOp(function() {
5308 var oldHeight = this.display.cachedTextHeight;
5309 regChange(this);
5310 this.curOp.forceUpdate = true;
5311 clearCaches(this);
5312 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5313 updateGutterSpace(this);
5314 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
5315 estimateLineHeights(this);
5316 signal(this, "refresh", this);
5317 }),
5318
5319 swapDoc: methodOp(function(doc) {
5320 var old = this.doc;
5321 old.cm = null;
5322 attachDoc(this, doc);
5323 clearCaches(this);
5324 this.display.input.reset();
5325 this.scrollTo(doc.scrollLeft, doc.scrollTop);
5326 this.curOp.forceScroll = true;
5327 signalLater(this, "swapDoc", this, old);
5328 return old;
5329 }),
5330
5331 getInputField: function(){return this.display.input.getField();},
5332 getWrapperElement: function(){return this.display.wrapper;},
5333 getScrollerElement: function(){return this.display.scroller;},
5334 getGutterElement: function(){return this.display.gutters;}
5335 };
5336 eventMixin(CodeMirror);
5337
5338 // OPTION DEFAULTS
5339
5340 // The default configuration options.
5341 var defaults = CodeMirror.defaults = {};
5342 // Functions to run when options are changed.
5343 var optionHandlers = CodeMirror.optionHandlers = {};
5344
5345 function option(name, deflt, handle, notOnInit) {
5346 CodeMirror.defaults[name] = deflt;
5347 if (handle) optionHandlers[name] =
5348 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5349 }
5350
5351 // Passed to option handlers when there is no old value.
5352 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5353
5354 // These two are, on init, called from the constructor because they
5355 // have to be initialized before the editor can start at all.
5356 option("value", "", function(cm, val) {
5357 cm.setValue(val);
5358 }, true);
5359 option("mode", null, function(cm, val) {
5360 cm.doc.modeOption = val;
5361 loadMode(cm);
5362 }, true);
5363
5364 option("indentUnit", 2, loadMode, true);
5365 option("indentWithTabs", false);
5366 option("smartIndent", true);
5367 option("tabSize", 4, function(cm) {
4683 return new Selection(out, doc.sel.primIndex)
4684 }
4685
4686 // Used to get the editor into a consistent state again when options change.
4687
4688 function loadMode(cm) {
4689 cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
5368 4690 resetModeState(cm);
5369 clearCaches(cm);
5370 regChange(cm);
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++;
4691 }
4692
4693 function resetModeState(cm) {
4694 cm.doc.iter(function (line) {
4695 if (line.stateAfter) { line.stateAfter = null; }
4696 if (line.styles) { line.styles = null; }
5384 4697 });
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 });
5388 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5389 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5390 if (old != CodeMirror.Init) cm.refresh();
5391 });
5392 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5393 option("electricChars", true);
5394 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5395 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5396 }, true);
5397 option("rtlMoveVisually", !windows);
5398 option("wholeLineUpdateBefore", true);
5399
5400 option("theme", "default", function(cm) {
5401 themeChanged(cm);
5402 guttersChanged(cm);
5403 }, true);
5404 option("keyMap", "default", function(cm, val, old) {
5405 var next = getKeyMap(val);
5406 var prev = old != CodeMirror.Init && getKeyMap(old);
5407 if (prev && prev.detach) prev.detach(cm, next);
5408 if (next.attach) next.attach(cm, prev || null);
5409 });
5410 option("extraKeys", null);
5411
5412 option("lineWrapping", false, wrappingChanged, true);
5413 option("gutters", [], function(cm) {
5414 setGuttersForLineNumbers(cm.options);
5415 guttersChanged(cm);
5416 }, true);
5417 option("fixedGutter", true, function(cm, val) {
5418 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5419 cm.refresh();
5420 }, true);
5421 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5422 option("scrollbarStyle", "native", function(cm) {
5423 initScrollbars(cm);
5424 updateScrollbars(cm);
5425 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5426 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5427 }, true);
5428 option("lineNumbers", false, function(cm) {
5429 setGuttersForLineNumbers(cm.options);
5430 guttersChanged(cm);
5431 }, true);
5432 option("firstLineNumber", 1, guttersChanged, true);
5433 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5434 option("showCursorWhenSelecting", false, updateSelection, true);
5435
5436 option("resetSelectionOnContextMenu", true);
5437 option("lineWiseCopyCut", true);
5438
5439 option("readOnly", false, function(cm, val) {
5440 if (val == "nocursor") {
5441 onBlur(cm);
5442 cm.display.input.blur();
5443 cm.display.disabled = true;
5444 } else {
5445 cm.display.disabled = false;
5446 }
5447 cm.display.input.readOnlyChanged(val)
5448 });
5449 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5450 option("dragDrop", true, dragDropChanged);
5451 option("allowDropFileTypes", null);
5452
5453 option("cursorBlinkRate", 530);
5454 option("cursorScrollMargin", 0);
5455 option("cursorHeight", 1, updateSelection, true);
5456 option("singleCursorHeightPerLine", true, updateSelection, true);
5457 option("workTime", 100);
5458 option("workDelay", 100);
5459 option("flattenSpans", true, resetModeState, true);
5460 option("addModeClass", false, resetModeState, true);
5461 option("pollInterval", 100);
5462 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5463 option("historyEventDelay", 1250);
5464 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5465 option("maxHighlightLength", 10000, resetModeState, true);
5466 option("moveInputWithCursor", true, function(cm, val) {
5467 if (!val) cm.display.input.resetPosition();
5468 });
5469
5470 option("tabindex", null, function(cm, val) {
5471 cm.display.input.getField().tabIndex = val || "";
5472 });
5473 option("autofocus", null);
5474
5475 // MODE DEFINITION AND QUERYING
5476
5477 // Known modes, by name and by MIME
5478 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5479
5480 // Extra arguments are stored as the mode's dependencies, which is
5481 // used by (legacy) mechanisms like loadmode.js to automatically
5482 // load a mode. (Preferred mechanism is the require/define calls.)
5483 CodeMirror.defineMode = function(name, mode) {
5484 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5485 if (arguments.length > 2)
5486 mode.dependencies = Array.prototype.slice.call(arguments, 2);
5487 modes[name] = mode;
5488 };
5489
5490 CodeMirror.defineMIME = function(mime, spec) {
5491 mimeModes[mime] = spec;
5492 };
5493
5494 // Given a MIME type, a {name, ...options} config object, or a name
5495 // string, return a mode config object.
5496 CodeMirror.resolveMode = function(spec) {
5497 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5498 spec = mimeModes[spec];
5499 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5500 var found = mimeModes[spec.name];
5501 if (typeof found == "string") found = {name: found};
5502 spec = createObj(found, spec);
5503 spec.name = found.name;
5504 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5505 return CodeMirror.resolveMode("application/xml");
5506 }
5507 if (typeof spec == "string") return {name: spec};
5508 else return spec || {name: "null"};
5509 };
5510
5511 // Given a mode spec (anything that resolveMode accepts), find and
5512 // initialize an actual mode object.
5513 CodeMirror.getMode = function(options, spec) {
5514 var spec = CodeMirror.resolveMode(spec);
5515 var mfactory = modes[spec.name];
5516 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5517 var modeObj = mfactory(options, spec);
5518 if (modeExtensions.hasOwnProperty(spec.name)) {
5519 var exts = modeExtensions[spec.name];
5520 for (var prop in exts) {
5521 if (!exts.hasOwnProperty(prop)) continue;
5522 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5523 modeObj[prop] = exts[prop];
5524 }
5525 }
5526 modeObj.name = spec.name;
5527 if (spec.helperType) modeObj.helperType = spec.helperType;
5528 if (spec.modeProps) for (var prop in spec.modeProps)
5529 modeObj[prop] = spec.modeProps[prop];
5530
5531 return modeObj;
5532 };
5533
5534 // Minimal default mode.
5535 CodeMirror.defineMode("null", function() {
5536 return {token: function(stream) {stream.skipToEnd();}};
5537 });
5538 CodeMirror.defineMIME("text/plain", "null");
5539
5540 // This can be used to attach properties to mode objects from
5541 // outside the actual mode definition.
5542 var modeExtensions = CodeMirror.modeExtensions = {};
5543 CodeMirror.extendMode = function(mode, properties) {
5544 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5545 copyObj(properties, exts);
5546 };
5547
5548 // EXTENSIONS
5549
5550 CodeMirror.defineExtension = function(name, func) {
5551 CodeMirror.prototype[name] = func;
5552 };
5553 CodeMirror.defineDocExtension = function(name, func) {
5554 Doc.prototype[name] = func;
5555 };
5556 CodeMirror.defineOption = option;
5557
5558 var initHooks = [];
5559 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5560
5561 var helpers = CodeMirror.helpers = {};
5562 CodeMirror.registerHelper = function(type, name, value) {
5563 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5564 helpers[type][name] = value;
5565 };
5566 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5567 CodeMirror.registerHelper(type, name, value);
5568 helpers[type]._global.push({pred: predicate, val: value});
5569 };
5570
5571 // MODE STATE HANDLING
5572
5573 // Utility functions for working with state. Exported because nested
5574 // modes need to do this for their inner modes.
5575
5576 var copyState = CodeMirror.copyState = function(mode, state) {
5577 if (state === true) return state;
5578 if (mode.copyState) return mode.copyState(state);
5579 var nstate = {};
5580 for (var n in state) {
5581 var val = state[n];
5582 if (val instanceof Array) val = val.concat([]);
5583 nstate[n] = val;
5584 }
5585 return nstate;
5586 };
5587
5588 var startState = CodeMirror.startState = function(mode, a1, a2) {
5589 return mode.startState ? mode.startState(a1, a2) : true;
5590 };
5591
5592 // Given a mode and a state (for that mode), find the inner mode and
5593 // state at the position that the state refers to.
5594 CodeMirror.innerMode = function(mode, state) {
5595 while (mode.innerMode) {
5596 var info = mode.innerMode(state);
5597 if (!info || info.mode == mode) break;
5598 state = info.state;
5599 mode = info.mode;
5600 }
5601 return info || {mode: mode, state: state};
5602 };
5603
5604 // STANDARD COMMANDS
5605
5606 // Commands are parameter-less actions that can be performed on an
5607 // editor, mostly used for keybindings.
5608 var commands = CodeMirror.commands = {
5609 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5610 singleSelection: function(cm) {
5611 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5612 },
5613 killLine: function(cm) {
5614 deleteNearSelection(cm, function(range) {
5615 if (range.empty()) {
5616 var len = getLine(cm.doc, range.head.line).text.length;
5617 if (range.head.ch == len && range.head.line < cm.lastLine())
5618 return {from: range.head, to: Pos(range.head.line + 1, 0)};
5619 else
5620 return {from: range.head, to: Pos(range.head.line, len)};
5621 } else {
5622 return {from: range.from(), to: range.to()};
5623 }
5624 });
5625 },
5626 deleteLine: function(cm) {
5627 deleteNearSelection(cm, function(range) {
5628 return {from: Pos(range.from().line, 0),
5629 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5630 });
5631 },
5632 delLineLeft: function(cm) {
5633 deleteNearSelection(cm, function(range) {
5634 return {from: Pos(range.from().line, 0), to: range.from()};
5635 });
5636 },
5637 delWrappedLineLeft: function(cm) {
5638 deleteNearSelection(cm, function(range) {
5639 var top = cm.charCoords(range.head, "div").top + 5;
5640 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5641 return {from: leftPos, to: range.from()};
5642 });
5643 },
5644 delWrappedLineRight: function(cm) {
5645 deleteNearSelection(cm, function(range) {
5646 var top = cm.charCoords(range.head, "div").top + 5;
5647 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5648 return {from: range.from(), to: rightPos };
5649 });
5650 },
5651 undo: function(cm) {cm.undo();},
5652 redo: function(cm) {cm.redo();},
5653 undoSelection: function(cm) {cm.undoSelection();},
5654 redoSelection: function(cm) {cm.redoSelection();},
5655 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5656 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5657 goLineStart: function(cm) {
5658 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5659 {origin: "+move", bias: 1});
5660 },
5661 goLineStartSmart: function(cm) {
5662 cm.extendSelectionsBy(function(range) {
5663 return lineStartSmart(cm, range.head);
5664 }, {origin: "+move", bias: 1});
5665 },
5666 goLineEnd: function(cm) {
5667 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5668 {origin: "+move", bias: -1});
5669 },
5670 goLineRight: function(cm) {
5671 cm.extendSelectionsBy(function(range) {
5672 var top = cm.charCoords(range.head, "div").top + 5;
5673 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5674 }, sel_move);
5675 },
5676 goLineLeft: function(cm) {
5677 cm.extendSelectionsBy(function(range) {
5678 var top = cm.charCoords(range.head, "div").top + 5;
5679 return cm.coordsChar({left: 0, top: top}, "div");
5680 }, sel_move);
5681 },
5682 goLineLeftSmart: function(cm) {
5683 cm.extendSelectionsBy(function(range) {
5684 var top = cm.charCoords(range.head, "div").top + 5;
5685 var pos = cm.coordsChar({left: 0, top: top}, "div");
5686 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5687 return pos;
5688 }, sel_move);
5689 },
5690 goLineUp: function(cm) {cm.moveV(-1, "line");},
5691 goLineDown: function(cm) {cm.moveV(1, "line");},
5692 goPageUp: function(cm) {cm.moveV(-1, "page");},
5693 goPageDown: function(cm) {cm.moveV(1, "page");},
5694 goCharLeft: function(cm) {cm.moveH(-1, "char");},
5695 goCharRight: function(cm) {cm.moveH(1, "char");},
5696 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5697 goColumnRight: function(cm) {cm.moveH(1, "column");},
5698 goWordLeft: function(cm) {cm.moveH(-1, "word");},
5699 goGroupRight: function(cm) {cm.moveH(1, "group");},
5700 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5701 goWordRight: function(cm) {cm.moveH(1, "word");},
5702 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5703 delCharAfter: function(cm) {cm.deleteH(1, "char");},
5704 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5705 delWordAfter: function(cm) {cm.deleteH(1, "word");},
5706 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5707 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5708 indentAuto: function(cm) {cm.indentSelection("smart");},
5709 indentMore: function(cm) {cm.indentSelection("add");},
5710 indentLess: function(cm) {cm.indentSelection("subtract");},
5711 insertTab: function(cm) {cm.replaceSelection("\t");},
5712 insertSoftTab: function(cm) {
5713 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5714 for (var i = 0; i < ranges.length; i++) {
5715 var pos = ranges[i].from();
5716 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5717 spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5718 }
5719 cm.replaceSelections(spaces);
5720 },
5721 defaultTab: function(cm) {
5722 if (cm.somethingSelected()) cm.indentSelection("add");
5723 else cm.execCommand("insertTab");
5724 },
5725 transposeChars: function(cm) {
5726 runInOp(cm, function() {
5727 var ranges = cm.listSelections(), newSel = [];
5728 for (var i = 0; i < ranges.length; i++) {
5729 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5730 if (line) {
5731 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5732 if (cur.ch > 0) {
5733 cur = new Pos(cur.line, cur.ch + 1);
5734 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5735 Pos(cur.line, cur.ch - 2), cur, "+transpose");
5736 } else if (cur.line > cm.doc.first) {
5737 var prev = getLine(cm.doc, cur.line - 1).text;
5738 if (prev)
5739 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
5740 prev.charAt(prev.length - 1),
5741 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5742 }
5743 }
5744 newSel.push(new Range(cur, cur));
5745 }
5746 cm.setSelections(newSel);
5747 });
5748 },
5749 newlineAndIndent: function(cm) {
5750 runInOp(cm, function() {
5751 var len = cm.listSelections().length;
5752 for (var i = 0; i < len; i++) {
5753 var range = cm.listSelections()[i];
5754 cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
5755 cm.indentLine(range.from().line + 1, null, true);
5756 }
5757 ensureCursorVisible(cm);
5758 });
5759 },
5760 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5761 };
5762
5763
5764 // STANDARD KEYMAPS
5765
5766 var keyMap = CodeMirror.keyMap = {};
5767
5768 keyMap.basic = {
5769 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5770 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5771 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5772 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5773 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5774 "Esc": "singleSelection"
5775 };
5776 // Note that the save and find-related commands aren't defined by
5777 // default. User code or addons can define them. Unknown commands
5778 // are simply ignored.
5779 keyMap.pcDefault = {
5780 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5781 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5782 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5783 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5784 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5785 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5786 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5787 fallthrough: "basic"
5788 };
5789 // Very basic readline/emacs-style bindings, which are standard on Mac.
5790 keyMap.emacsy = {
5791 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5792 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5793 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5794 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5795 };
5796 keyMap.macDefault = {
5797 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5798 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5799 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5800 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5801 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5802 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5803 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5804 fallthrough: ["basic", "emacsy"]
5805 };
5806 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5807
5808 // KEYMAP DISPATCH
5809
5810 function normalizeKeyName(name) {
5811 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5812 var alt, ctrl, shift, cmd;
5813 for (var i = 0; i < parts.length - 1; i++) {
5814 var mod = parts[i];
5815 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5816 else if (/^a(lt)?$/i.test(mod)) alt = true;
5817 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5818 else if (/^s(hift)$/i.test(mod)) shift = true;
5819 else throw new Error("Unrecognized modifier name: " + mod);
5820 }
5821 if (alt) name = "Alt-" + name;
5822 if (ctrl) name = "Ctrl-" + name;
5823 if (cmd) name = "Cmd-" + name;
5824 if (shift) name = "Shift-" + name;
5825 return name;
5826 }
5827
5828 // This is a kludge to keep keymaps mostly working as raw objects
5829 // (backwards compatibility) while at the same time support features
5830 // like normalization and multi-stroke key bindings. It compiles a
5831 // new normalized keymap, and then updates the old object to reflect
5832 // this.
5833 CodeMirror.normalizeKeyMap = function(keymap) {
5834 var copy = {};
5835 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5836 var value = keymap[keyname];
5837 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5838 if (value == "...") { delete keymap[keyname]; continue; }
5839
5840 var keys = map(keyname.split(" "), normalizeKeyName);
5841 for (var i = 0; i < keys.length; i++) {
5842 var val, name;
5843 if (i == keys.length - 1) {
5844 name = keys.join(" ");
5845 val = value;
5846 } else {
5847 name = keys.slice(0, i + 1).join(" ");
5848 val = "...";
5849 }
5850 var prev = copy[name];
5851 if (!prev) copy[name] = val;
5852 else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5853 }
5854 delete keymap[keyname];
5855 }
5856 for (var prop in copy) keymap[prop] = copy[prop];
5857 return keymap;
5858 };
5859
5860 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5861 map = getKeyMap(map);
5862 var found = map.call ? map.call(key, context) : map[key];
5863 if (found === false) return "nothing";
5864 if (found === "...") return "multi";
5865 if (found != null && handle(found)) return "handled";
5866
5867 if (map.fallthrough) {
5868 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5869 return lookupKey(key, map.fallthrough, handle, context);
5870 for (var i = 0; i < map.fallthrough.length; i++) {
5871 var result = lookupKey(key, map.fallthrough[i], handle, context);
5872 if (result) return result;
5873 }
5874 }
5875 };
5876
5877 // Modifier key presses don't count as 'real' key presses for the
5878 // purpose of keymap fallthrough.
5879 var isModifierKey = CodeMirror.isModifierKey = function(value) {
5880 var name = typeof value == "string" ? value : keyNames[value.keyCode];
5881 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5882 };
5883
5884 // Look up the name of a key as indicated by an event object.
5885 var keyName = CodeMirror.keyName = function(event, noShift) {
5886 if (presto && event.keyCode == 34 && event["char"]) return false;
5887 var base = keyNames[event.keyCode], name = base;
5888 if (name == null || event.altGraphKey) return false;
5889 if (event.altKey && base != "Alt") name = "Alt-" + name;
5890 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5891 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5892 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5893 return name;
5894 };
5895
5896 function getKeyMap(val) {
5897 return typeof val == "string" ? keyMap[val] : val;
5898 }
5899
5900 // FROMTEXTAREA
5901
5902 CodeMirror.fromTextArea = function(textarea, options) {
5903 options = options ? copyObj(options) : {};
5904 options.value = textarea.value;
5905 if (!options.tabindex && textarea.tabIndex)
5906 options.tabindex = textarea.tabIndex;
5907 if (!options.placeholder && textarea.placeholder)
5908 options.placeholder = textarea.placeholder;
5909 // Set autofocus to true if this textarea is focused, or if it has
5910 // autofocus and no other element is focused.
5911 if (options.autofocus == null) {
5912 var hasFocus = activeElt();
5913 options.autofocus = hasFocus == textarea ||
5914 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5915 }
5916
5917 function save() {textarea.value = cm.getValue();}
5918 if (textarea.form) {
5919 on(textarea.form, "submit", save);
5920 // Deplorable hack to make the submit method do the right thing.
5921 if (!options.leaveSubmitMethodAlone) {
5922 var form = textarea.form, realSubmit = form.submit;
5923 try {
5924 var wrappedSubmit = form.submit = function() {
5925 save();
5926 form.submit = realSubmit;
5927 form.submit();
5928 form.submit = wrappedSubmit;
5929 };
5930 } catch(e) {}
5931 }
5932 }
5933
5934 options.finishInit = function(cm) {
5935 cm.save = save;
5936 cm.getTextArea = function() { return textarea; };
5937 cm.toTextArea = function() {
5938 cm.toTextArea = isNaN; // Prevent this from being ran twice
5939 save();
5940 textarea.parentNode.removeChild(cm.getWrapperElement());
5941 textarea.style.display = "";
5942 if (textarea.form) {
5943 off(textarea.form, "submit", save);
5944 if (typeof textarea.form.submit == "function")
5945 textarea.form.submit = realSubmit;
5946 }
5947 };
5948 };
5949
5950 textarea.style.display = "none";
5951 var cm = CodeMirror(function(node) {
5952 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5953 }, options);
5954 return cm;
5955 };
5956
5957 // STRING STREAM
5958
5959 // Fed to the mode parsers, provides helper functions to make
5960 // parsers more succinct.
5961
5962 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5963 this.pos = this.start = 0;
5964 this.string = string;
5965 this.tabSize = tabSize || 8;
5966 this.lastColumnPos = this.lastColumnValue = 0;
5967 this.lineStart = 0;
5968 };
5969
5970 StringStream.prototype = {
5971 eol: function() {return this.pos >= this.string.length;},
5972 sol: function() {return this.pos == this.lineStart;},
5973 peek: function() {return this.string.charAt(this.pos) || undefined;},
5974 next: function() {
5975 if (this.pos < this.string.length)
5976 return this.string.charAt(this.pos++);
5977 },
5978 eat: function(match) {
5979 var ch = this.string.charAt(this.pos);
5980 if (typeof match == "string") var ok = ch == match;
5981 else var ok = ch && (match.test ? match.test(ch) : match(ch));
5982 if (ok) {++this.pos; return ch;}
5983 },
5984 eatWhile: function(match) {
5985 var start = this.pos;
5986 while (this.eat(match)){}
5987 return this.pos > start;
5988 },
5989 eatSpace: function() {
5990 var start = this.pos;
5991 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5992 return this.pos > start;
5993 },
5994 skipToEnd: function() {this.pos = this.string.length;},
5995 skipTo: function(ch) {
5996 var found = this.string.indexOf(ch, this.pos);
5997 if (found > -1) {this.pos = found; return true;}
5998 },
5999 backUp: function(n) {this.pos -= n;},
6000 column: function() {
6001 if (this.lastColumnPos < this.start) {
6002 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
6003 this.lastColumnPos = this.start;
6004 }
6005 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6006 },
6007 indentation: function() {
6008 return countColumn(this.string, null, this.tabSize) -
6009 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6010 },
6011 match: function(pattern, consume, caseInsensitive) {
6012 if (typeof pattern == "string") {
6013 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
6014 var substr = this.string.substr(this.pos, pattern.length);
6015 if (cased(substr) == cased(pattern)) {
6016 if (consume !== false) this.pos += pattern.length;
6017 return true;
6018 }
6019 } else {
6020 var match = this.string.slice(this.pos).match(pattern);
6021 if (match && match.index > 0) return null;
6022 if (match && consume !== false) this.pos += match[0].length;
6023 return match;
6024 }
6025 },
6026 current: function(){return this.string.slice(this.start, this.pos);},
6027 hideFirstChars: function(n, inner) {
6028 this.lineStart += n;
6029 try { return inner(); }
6030 finally { this.lineStart -= n; }
6031 }
6032 };
6033
6034 // TEXTMARKERS
6035
6036 // Created with markText and setBookmark methods. A TextMarker is a
6037 // handle that can be used to clear or find a marked position in the
6038 // document. Line objects hold arrays (markedSpans) containing
6039 // {from, to, marker} object pointing to such marker objects, and
6040 // indicating that such a marker is present on that line. Multiple
6041 // lines may point to the same marker when it spans across lines.
6042 // The spans will have null for their from/to properties when the
6043 // marker continues beyond the start/end of the line. Markers have
6044 // links back to the lines they currently touch.
6045
6046 var nextMarkerId = 0;
6047
6048 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
6049 this.lines = [];
6050 this.type = type;
6051 this.doc = doc;
6052 this.id = ++nextMarkerId;
6053 };
6054 eventMixin(TextMarker);
6055
6056 // Clear the marker.
6057 TextMarker.prototype.clear = function() {
6058 if (this.explicitlyCleared) return;
6059 var cm = this.doc.cm, withOp = cm && !cm.curOp;
6060 if (withOp) startOperation(cm);
6061 if (hasHandler(this, "clear")) {
6062 var found = this.find();
6063 if (found) signalLater(this, "clear", found.from, found.to);
6064 }
6065 var min = null, max = null;
6066 for (var i = 0; i < this.lines.length; ++i) {
6067 var line = this.lines[i];
6068 var span = getMarkedSpanFor(line.markedSpans, this);
6069 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
6070 else if (cm) {
6071 if (span.to != null) max = lineNo(line);
6072 if (span.from != null) min = lineNo(line);
6073 }
6074 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
6075 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
6076 updateLineHeight(line, textHeight(cm.display));
6077 }
6078 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
6079 var visual = visualLine(this.lines[i]), len = lineLength(visual);
6080 if (len > cm.display.maxLineLength) {
6081 cm.display.maxLine = visual;
6082 cm.display.maxLineLength = len;
6083 cm.display.maxLineChanged = true;
6084 }
6085 }
6086
6087 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
6088 this.lines.length = 0;
6089 this.explicitlyCleared = true;
6090 if (this.atomic && this.doc.cantEdit) {
6091 this.doc.cantEdit = false;
6092 if (cm) reCheckSelection(cm.doc);
6093 }
6094 if (cm) signalLater(cm, "markerCleared", cm, this);
6095 if (withOp) endOperation(cm);
6096 if (this.parent) this.parent.clear();
6097 };
6098
6099 // Find the position of the marker in the document. Returns a {from,
6100 // to} object by default. Side can be passed to get a specific side
6101 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
6102 // Pos objects returned contain a line object, rather than a line
6103 // number (used to prevent looking up the same line twice).
6104 TextMarker.prototype.find = function(side, lineObj) {
6105 if (side == null && this.type == "bookmark") side = 1;
6106 var from, to;
6107 for (var i = 0; i < this.lines.length; ++i) {
6108 var line = this.lines[i];
6109 var span = getMarkedSpanFor(line.markedSpans, this);
6110 if (span.from != null) {
6111 from = Pos(lineObj ? line : lineNo(line), span.from);
6112 if (side == -1) return from;
6113 }
6114 if (span.to != null) {
6115 to = Pos(lineObj ? line : lineNo(line), span.to);
6116 if (side == 1) return to;
6117 }
6118 }
6119 return from && {from: from, to: to};
6120 };
6121
6122 // Signals that the marker's widget changed, and surrounding layout
6123 // should be recomputed.
6124 TextMarker.prototype.changed = function() {
6125 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6126 if (!pos || !cm) return;
6127 runInOp(cm, function() {
6128 var line = pos.line, lineN = lineNo(pos.line);
6129 var view = findViewForLine(cm, lineN);
6130 if (view) {
6131 clearLineMeasurementCacheFor(view);
6132 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6133 }
6134 cm.curOp.updateMaxLine = true;
6135 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6136 var oldHeight = widget.height;
6137 widget.height = null;
6138 var dHeight = widgetHeight(widget) - oldHeight;
6139 if (dHeight)
6140 updateLineHeight(line, line.height + dHeight);
6141 }
6142 });
6143 };
6144
6145 TextMarker.prototype.attachLine = function(line) {
6146 if (!this.lines.length && this.doc.cm) {
6147 var op = this.doc.cm.curOp;
6148 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6149 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6150 }
6151 this.lines.push(line);
6152 };
6153 TextMarker.prototype.detachLine = function(line) {
6154 this.lines.splice(indexOf(this.lines, line), 1);
6155 if (!this.lines.length && this.doc.cm) {
6156 var op = this.doc.cm.curOp;
6157 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6158 }
6159 };
6160
6161 // Collapsed markers have unique ids, in order to be able to order
6162 // them, which is needed for uniquely determining an outer marker
6163 // when they overlap (they may nest, but not partially overlap).
6164 var nextMarkerId = 0;
6165
6166 // Create a marker, wire it up to the right lines, and
6167 function markText(doc, from, to, options, type) {
6168 // Shared markers (across linked documents) are handled separately
6169 // (markTextShared will call out to this again, once per
6170 // document).
6171 if (options && options.shared) return markTextShared(doc, from, to, options, type);
6172 // Ensure we are in an operation.
6173 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6174
6175 var marker = new TextMarker(doc, type), diff = cmp(from, to);
6176 if (options) copyObj(options, marker, false);
6177 // Don't connect empty markers unless clearWhenEmpty is false
6178 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6179 return marker;
6180 if (marker.replacedWith) {
6181 // Showing up as a widget implies collapsed (widget replaces text)
6182 marker.collapsed = true;
6183 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6184 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6185 if (options.insertLeft) marker.widgetNode.insertLeft = true;
6186 }
6187 if (marker.collapsed) {
6188 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6189 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6190 throw new Error("Inserting collapsed marker partially overlapping an existing one");
6191 sawCollapsedSpans = true;
6192 }
6193
6194 if (marker.addToHistory)
6195 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6196
6197 var curLine = from.line, cm = doc.cm, updateMaxLine;
6198 doc.iter(curLine, to.line + 1, function(line) {
6199 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6200 updateMaxLine = true;
6201 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6202 addMarkedSpan(line, new MarkedSpan(marker,
6203 curLine == from.line ? from.ch : null,
6204 curLine == to.line ? to.ch : null));
6205 ++curLine;
6206 });
6207 // lineIsHidden depends on the presence of the spans, so needs a second pass
6208 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6209 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6210 });
6211
6212 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6213
6214 if (marker.readOnly) {
6215 sawReadOnlySpans = true;
6216 if (doc.history.done.length || doc.history.undone.length)
6217 doc.clearHistory();
6218 }
6219 if (marker.collapsed) {
6220 marker.id = ++nextMarkerId;
6221 marker.atomic = true;
6222 }
6223 if (cm) {
6224 // Sync editor state
6225 if (updateMaxLine) cm.curOp.updateMaxLine = true;
6226 if (marker.collapsed)
6227 regChange(cm, from.line, to.line + 1);
6228 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6229 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6230 if (marker.atomic) reCheckSelection(cm.doc);
6231 signalLater(cm, "markerAdded", cm, marker);
6232 }
6233 return marker;
6234 }
6235
6236 // SHARED TEXTMARKERS
6237
6238 // A shared marker spans multiple linked documents. It is
6239 // implemented as a meta-marker-object controlling multiple normal
6240 // markers.
6241 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6242 this.markers = markers;
6243 this.primary = primary;
6244 for (var i = 0; i < markers.length; ++i)
6245 markers[i].parent = this;
6246 };
6247 eventMixin(SharedTextMarker);
6248
6249 SharedTextMarker.prototype.clear = function() {
6250 if (this.explicitlyCleared) return;
6251 this.explicitlyCleared = true;
6252 for (var i = 0; i < this.markers.length; ++i)
6253 this.markers[i].clear();
6254 signalLater(this, "clear");
6255 };
6256 SharedTextMarker.prototype.find = function(side, lineObj) {
6257 return this.primary.find(side, lineObj);
6258 };
6259
6260 function markTextShared(doc, from, to, options, type) {
6261 options = copyObj(options);
6262 options.shared = false;
6263 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6264 var widget = options.widgetNode;
6265 linkedDocs(doc, function(doc) {
6266 if (widget) options.widgetNode = widget.cloneNode(true);
6267 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6268 for (var i = 0; i < doc.linked.length; ++i)
6269 if (doc.linked[i].isParent) return;
6270 primary = lst(markers);
6271 });
6272 return new SharedTextMarker(markers, primary);
6273 }
6274
6275 function findSharedMarkers(doc) {
6276 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6277 function(m) { return m.parent; });
6278 }
6279
6280 function copySharedMarkers(doc, markers) {
6281 for (var i = 0; i < markers.length; i++) {
6282 var marker = markers[i], pos = marker.find();
6283 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6284 if (cmp(mFrom, mTo)) {
6285 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6286 marker.markers.push(subMark);
6287 subMark.parent = marker;
6288 }
6289 }
6290 }
6291
6292 function detachSharedMarkers(markers) {
6293 for (var i = 0; i < markers.length; i++) {
6294 var marker = markers[i], linked = [marker.primary.doc];;
6295 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6296 for (var j = 0; j < marker.markers.length; j++) {
6297 var subMarker = marker.markers[j];
6298 if (indexOf(linked, subMarker.doc) == -1) {
6299 subMarker.parent = null;
6300 marker.markers.splice(j--, 1);
6301 }
6302 }
6303 }
6304 }
6305
6306 // TEXTMARKER SPANS
6307
6308 function MarkedSpan(marker, from, to) {
6309 this.marker = marker;
6310 this.from = from; this.to = to;
6311 }
6312
6313 // Search an array of spans for a span matching the given marker.
6314 function getMarkedSpanFor(spans, marker) {
6315 if (spans) for (var i = 0; i < spans.length; ++i) {
6316 var span = spans[i];
6317 if (span.marker == marker) return span;
6318 }
6319 }
6320 // Remove a span from an array, returning undefined if no spans are
6321 // left (we don't store arrays for lines without spans).
6322 function removeMarkedSpan(spans, span) {
6323 for (var r, i = 0; i < spans.length; ++i)
6324 if (spans[i] != span) (r || (r = [])).push(spans[i]);
6325 return r;
6326 }
6327 // Add a span to a line.
6328 function addMarkedSpan(line, span) {
6329 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6330 span.marker.attachLine(line);
6331 }
6332
6333 // Used for the algorithm that adjusts markers for a change in the
6334 // document. These functions cut an array of spans at a given
6335 // character position, returning an array of remaining chunks (or
6336 // undefined if nothing remains).
6337 function markedSpansBefore(old, startCh, isInsert) {
6338 if (old) for (var i = 0, nw; i < old.length; ++i) {
6339 var span = old[i], marker = span.marker;
6340 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
6341 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6342 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
6343 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6344 }
6345 }
6346 return nw;
6347 }
6348 function markedSpansAfter(old, endCh, isInsert) {
6349 if (old) for (var i = 0, nw; i < old.length; ++i) {
6350 var span = old[i], marker = span.marker;
6351 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
6352 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6353 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
6354 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6355 span.to == null ? null : span.to - endCh));
6356 }
6357 }
6358 return nw;
6359 }
6360
6361 // Given a change object, compute the new set of marker spans that
6362 // cover the line in which the change took place. Removes spans
6363 // entirely within the change, reconnects spans belonging to the
6364 // same marker that appear on both sides of the change, and cuts off
6365 // spans partially within the change. Returns an array of span
6366 // arrays with one element for each line in (after) the change.
6367 function stretchSpansOverChange(doc, change) {
6368 if (change.full) return null;
6369 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6370 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6371 if (!oldFirst && !oldLast) return null;
6372
6373 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
6374 // Get the spans that 'stick out' on both sides
6375 var first = markedSpansBefore(oldFirst, startCh, isInsert);
6376 var last = markedSpansAfter(oldLast, endCh, isInsert);
6377
6378 // Next, merge those two ends
6379 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
6380 if (first) {
6381 // Fix up .to properties of first
6382 for (var i = 0; i < first.length; ++i) {
6383 var span = first[i];
6384 if (span.to == null) {
6385 var found = getMarkedSpanFor(last, span.marker);
6386 if (!found) span.to = startCh;
6387 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
6388 }
6389 }
6390 }
6391 if (last) {
6392 // Fix up .from in last (or move them into first in case of sameLine)
6393 for (var i = 0; i < last.length; ++i) {
6394 var span = last[i];
6395 if (span.to != null) span.to += offset;
6396 if (span.from == null) {
6397 var found = getMarkedSpanFor(first, span.marker);
6398 if (!found) {
6399 span.from = offset;
6400 if (sameLine) (first || (first = [])).push(span);
6401 }
6402 } else {
6403 span.from += offset;
6404 if (sameLine) (first || (first = [])).push(span);
6405 }
6406 }
6407 }
6408 // Make sure we didn't create any zero-length spans
6409 if (first) first = clearEmptySpans(first);
6410 if (last && last != first) last = clearEmptySpans(last);
6411
6412 var newMarkers = [first];
6413 if (!sameLine) {
6414 // Fill gap with whole-line-spans
6415 var gap = change.text.length - 2, gapMarkers;
6416 if (gap > 0 && first)
6417 for (var i = 0; i < first.length; ++i)
6418 if (first[i].to == null)
6419 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6420 for (var i = 0; i < gap; ++i)
6421 newMarkers.push(gapMarkers);
6422 newMarkers.push(last);
6423 }
6424 return newMarkers;
6425 }
6426
6427 // Remove spans that are empty and don't have a clearWhenEmpty
6428 // option of false.
6429 function clearEmptySpans(spans) {
6430 for (var i = 0; i < spans.length; ++i) {
6431 var span = spans[i];
6432 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
6433 spans.splice(i--, 1);
6434 }
6435 if (!spans.length) return null;
6436 return spans;
6437 }
6438
6439 // Used for un/re-doing changes from the history. Combines the
6440 // result of computing the existing spans with the set of spans that
6441 // existed in the history (so that deleting around a span and then
6442 // undoing brings back the span).
6443 function mergeOldSpans(doc, change) {
6444 var old = getOldSpans(doc, change);
6445 var stretched = stretchSpansOverChange(doc, change);
6446 if (!old) return stretched;
6447 if (!stretched) return old;
6448
6449 for (var i = 0; i < old.length; ++i) {
6450 var oldCur = old[i], stretchCur = stretched[i];
6451 if (oldCur && stretchCur) {
6452 spans: for (var j = 0; j < stretchCur.length; ++j) {
6453 var span = stretchCur[j];
6454 for (var k = 0; k < oldCur.length; ++k)
6455 if (oldCur[k].marker == span.marker) continue spans;
6456 oldCur.push(span);
6457 }
6458 } else if (stretchCur) {
6459 old[i] = stretchCur;
6460 }
6461 }
6462 return old;
6463 }
6464
6465 // Used to 'clip' out readOnly ranges when making a change.
6466 function removeReadOnlyRanges(doc, from, to) {
6467 var markers = null;
6468 doc.iter(from.line, to.line + 1, function(line) {
6469 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6470 var mark = line.markedSpans[i].marker;
6471 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
6472 (markers || (markers = [])).push(mark);
6473 }
6474 });
6475 if (!markers) return null;
6476 var parts = [{from: from, to: to}];
6477 for (var i = 0; i < markers.length; ++i) {
6478 var mk = markers[i], m = mk.find(0);
6479 for (var j = 0; j < parts.length; ++j) {
6480 var p = parts[j];
6481 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6482 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6483 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6484 newParts.push({from: p.from, to: m.from});
6485 if (dto > 0 || !mk.inclusiveRight && !dto)
6486 newParts.push({from: m.to, to: p.to});
6487 parts.splice.apply(parts, newParts);
6488 j += newParts.length - 1;
6489 }
6490 }
6491 return parts;
6492 }
6493
6494 // Connect or disconnect spans from a line.
6495 function detachMarkedSpans(line) {
6496 var spans = line.markedSpans;
6497 if (!spans) return;
6498 for (var i = 0; i < spans.length; ++i)
6499 spans[i].marker.detachLine(line);
6500 line.markedSpans = null;
6501 }
6502 function attachMarkedSpans(line, spans) {
6503 if (!spans) return;
6504 for (var i = 0; i < spans.length; ++i)
6505 spans[i].marker.attachLine(line);
6506 line.markedSpans = spans;
6507 }
6508
6509 // Helpers used when computing which overlapping collapsed span
6510 // counts as the larger one.
6511 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6512 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6513
6514 // Returns a number indicating which of two overlapping collapsed
6515 // spans is larger (and thus includes the other). Falls back to
6516 // comparing ids when the spans cover exactly the same range.
6517 function compareCollapsedMarkers(a, b) {
6518 var lenDiff = a.lines.length - b.lines.length;
6519 if (lenDiff != 0) return lenDiff;
6520 var aPos = a.find(), bPos = b.find();
6521 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6522 if (fromCmp) return -fromCmp;
6523 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6524 if (toCmp) return toCmp;
6525 return b.id - a.id;
6526 }
6527
6528 // Find out whether a line ends or starts in a collapsed span. If
6529 // so, return the marker for that span.
6530 function collapsedSpanAtSide(line, start) {
6531 var sps = sawCollapsedSpans && line.markedSpans, found;
6532 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6533 sp = sps[i];
6534 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6535 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6536 found = sp.marker;
6537 }
6538 return found;
6539 }
6540 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6541 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6542
6543 // Test whether there exists a collapsed span that partially
6544 // overlaps (covers the start or end, but not both) of a new span.
6545 // Such overlap is not allowed.
6546 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6547 var line = getLine(doc, lineNo);
6548 var sps = sawCollapsedSpans && line.markedSpans;
6549 if (sps) for (var i = 0; i < sps.length; ++i) {
6550 var sp = sps[i];
6551 if (!sp.marker.collapsed) continue;
6552 var found = sp.marker.find(0);
6553 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6554 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6555 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6556 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6557 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6558 return true;
6559 }
6560 }
6561
6562 // A visual line is a line as drawn on the screen. Folding, for
6563 // example, can cause multiple logical lines to appear on the same
6564 // visual line. This finds the start of the visual line that the
6565 // given line is part of (usually that is the line itself).
6566 function visualLine(line) {
6567 var merged;
6568 while (merged = collapsedSpanAtStart(line))
6569 line = merged.find(-1, true).line;
6570 return line;
6571 }
6572
6573 // Returns an array of logical lines that continue the visual line
6574 // started by the argument, or undefined if there are no such lines.
6575 function visualLineContinued(line) {
6576 var merged, lines;
6577 while (merged = collapsedSpanAtEnd(line)) {
6578 line = merged.find(1, true).line;
6579 (lines || (lines = [])).push(line);
6580 }
6581 return lines;
6582 }
6583
6584 // Get the line number of the start of the visual line that the
6585 // given line number is part of.
6586 function visualLineNo(doc, lineN) {
6587 var line = getLine(doc, lineN), vis = visualLine(line);
6588 if (line == vis) return lineN;
6589 return lineNo(vis);
6590 }
6591 // Get the line number of the start of the next visual line after
6592 // the given line.
6593 function visualLineEndNo(doc, lineN) {
6594 if (lineN > doc.lastLine()) return lineN;
6595 var line = getLine(doc, lineN), merged;
6596 if (!lineIsHidden(doc, line)) return lineN;
6597 while (merged = collapsedSpanAtEnd(line))
6598 line = merged.find(1, true).line;
6599 return lineNo(line) + 1;
6600 }
6601
6602 // Compute whether a line is hidden. Lines count as hidden when they
6603 // are part of a visual line that starts with another line, or when
6604 // they are entirely covered by collapsed, non-widget span.
6605 function lineIsHidden(doc, line) {
6606 var sps = sawCollapsedSpans && line.markedSpans;
6607 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6608 sp = sps[i];
6609 if (!sp.marker.collapsed) continue;
6610 if (sp.from == null) return true;
6611 if (sp.marker.widgetNode) continue;
6612 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
6613 return true;
6614 }
6615 }
6616 function lineIsHiddenInner(doc, line, span) {
6617 if (span.to == null) {
6618 var end = span.marker.find(1, true);
6619 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6620 }
6621 if (span.marker.inclusiveRight && span.to == line.text.length)
6622 return true;
6623 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6624 sp = line.markedSpans[i];
6625 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6626 (sp.to == null || sp.to != span.from) &&
6627 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6628 lineIsHiddenInner(doc, line, sp)) return true;
6629 }
6630 }
6631
6632 // LINE WIDGETS
6633
6634 // Line widgets are block elements displayed above or below a line.
6635
6636 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6637 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6638 this[opt] = options[opt];
6639 this.doc = doc;
6640 this.node = node;
6641 };
6642 eventMixin(LineWidget);
6643
6644 function adjustScrollWhenAboveVisible(cm, line, diff) {
6645 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6646 addToScrollPos(cm, null, diff);
6647 }
6648
6649 LineWidget.prototype.clear = function() {
6650 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6651 if (no == null || !ws) return;
6652 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6653 if (!ws.length) line.widgets = null;
6654 var height = widgetHeight(this);
6655 updateLineHeight(line, Math.max(0, line.height - height));
6656 if (cm) runInOp(cm, function() {
6657 adjustScrollWhenAboveVisible(cm, line, -height);
6658 regLineChange(cm, no, "widget");
6659 });
6660 };
6661 LineWidget.prototype.changed = function() {
6662 var oldH = this.height, cm = this.doc.cm, line = this.line;
6663 this.height = null;
6664 var diff = widgetHeight(this) - oldH;
6665 if (!diff) return;
6666 updateLineHeight(line, line.height + diff);
6667 if (cm) runInOp(cm, function() {
6668 cm.curOp.forceUpdate = true;
6669 adjustScrollWhenAboveVisible(cm, line, diff);
6670 });
6671 };
6672
6673 function widgetHeight(widget) {
6674 if (widget.height != null) return widget.height;
6675 var cm = widget.doc.cm;
6676 if (!cm) return 0;
6677 if (!contains(document.body, widget.node)) {
6678 var parentStyle = "position: relative;";
6679 if (widget.coverGutter)
6680 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6681 if (widget.noHScroll)
6682 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6683 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6684 }
6685 return widget.height = widget.node.parentNode.offsetHeight;
6686 }
6687
6688 function addLineWidget(doc, handle, node, options) {
6689 var widget = new LineWidget(doc, node, options);
6690 var cm = doc.cm;
6691 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6692 changeLine(doc, handle, "widget", function(line) {
6693 var widgets = line.widgets || (line.widgets = []);
6694 if (widget.insertAt == null) widgets.push(widget);
6695 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6696 widget.line = line;
6697 if (cm && !lineIsHidden(doc, line)) {
6698 var aboveVisible = heightAtLine(line) < doc.scrollTop;
6699 updateLineHeight(line, line.height + widgetHeight(widget));
6700 if (aboveVisible) addToScrollPos(cm, null, widget.height);
6701 cm.curOp.forceUpdate = true;
6702 }
6703 return true;
6704 });
6705 return widget;
6706 }
6707
6708 // LINE DATA STRUCTURE
6709
6710 // Line objects. These hold state related to a line, including
6711 // highlighting info (the styles array).
6712 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6713 this.text = text;
6714 attachMarkedSpans(this, markedSpans);
6715 this.height = estimateHeight ? estimateHeight(this) : 1;
6716 };
6717 eventMixin(Line);
6718 Line.prototype.lineNo = function() { return lineNo(this); };
6719
6720 // Change the content (text, markers) of a line. Automatically
6721 // invalidates cached information and tries to re-estimate the
6722 // line's height.
6723 function updateLine(line, text, markedSpans, estimateHeight) {
6724 line.text = text;
6725 if (line.stateAfter) line.stateAfter = null;
6726 if (line.styles) line.styles = null;
6727 if (line.order != null) line.order = null;
6728 detachMarkedSpans(line);
6729 attachMarkedSpans(line, markedSpans);
6730 var estHeight = estimateHeight ? estimateHeight(line) : 1;
6731 if (estHeight != line.height) updateLineHeight(line, estHeight);
6732 }
6733
6734 // Detach a line from the document tree and its markers.
6735 function cleanUpLine(line) {
6736 line.parent = null;
6737 detachMarkedSpans(line);
6738 }
6739
6740 function extractLineClasses(type, output) {
6741 if (type) for (;;) {
6742 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6743 if (!lineClass) break;
6744 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6745 var prop = lineClass[1] ? "bgClass" : "textClass";
6746 if (output[prop] == null)
6747 output[prop] = lineClass[2];
6748 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6749 output[prop] += " " + lineClass[2];
6750 }
6751 return type;
6752 }
6753
6754 function callBlankLine(mode, state) {
6755 if (mode.blankLine) return mode.blankLine(state);
6756 if (!mode.innerMode) return;
6757 var inner = CodeMirror.innerMode(mode, state);
6758 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6759 }
6760
6761 function readToken(mode, stream, state, inner) {
6762 for (var i = 0; i < 10; i++) {
6763 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6764 var style = mode.token(stream, state);
6765 if (stream.pos > stream.start) return style;
6766 }
6767 throw new Error("Mode " + mode.name + " failed to advance stream.");
6768 }
6769
6770 // Utility for getTokenAt and getLineTokens
6771 function takeToken(cm, pos, precise, asArray) {
6772 function getObj(copy) {
6773 return {start: stream.start, end: stream.pos,
6774 string: stream.current(),
6775 type: style || null,
6776 state: copy ? copyState(doc.mode, state) : state};
6777 }
6778
6779 var doc = cm.doc, mode = doc.mode, style;
6780 pos = clipPos(doc, pos);
6781 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6782 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6783 if (asArray) tokens = [];
6784 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6785 stream.start = stream.pos;
6786 style = readToken(mode, stream, state);
6787 if (asArray) tokens.push(getObj(true));
6788 }
6789 return asArray ? tokens : getObj();
6790 }
6791
6792 // Run the given mode's parser over a line, calling f for each token.
6793 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6794 var flattenSpans = mode.flattenSpans;
6795 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6796 var curStart = 0, curStyle = null;
6797 var stream = new StringStream(text, cm.options.tabSize), style;
6798 var inner = cm.options.addModeClass && [null];
6799 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6800 while (!stream.eol()) {
6801 if (stream.pos > cm.options.maxHighlightLength) {
6802 flattenSpans = false;
6803 if (forceToEnd) processLine(cm, text, state, stream.pos);
6804 stream.pos = text.length;
6805 style = null;
6806 } else {
6807 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6808 }
6809 if (inner) {
6810 var mName = inner[0].name;
6811 if (mName) style = "m-" + (style ? mName + " " + style : mName);
6812 }
6813 if (!flattenSpans || curStyle != style) {
6814 while (curStart < stream.start) {
6815 curStart = Math.min(stream.start, curStart + 50000);
6816 f(curStart, curStyle);
6817 }
6818 curStyle = style;
6819 }
6820 stream.start = stream.pos;
6821 }
6822 while (curStart < stream.pos) {
6823 // Webkit seems to refuse to render text nodes longer than 57444 characters
6824 var pos = Math.min(stream.pos, curStart + 50000);
6825 f(pos, curStyle);
6826 curStart = pos;
6827 }
6828 }
6829
6830 // Compute a style array (an array starting with a mode generation
6831 // -- for invalidation -- followed by pairs of end positions and
6832 // style strings), which is used to highlight the tokens on the
6833 // line.
6834 function highlightLine(cm, line, state, forceToEnd) {
6835 // A styles array always starts with a number identifying the
6836 // mode/overlays that it is based on (for easy invalidation).
6837 var st = [cm.state.modeGen], lineClasses = {};
6838 // Compute the base array of styles
6839 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6840 st.push(end, style);
6841 }, lineClasses, forceToEnd);
6842
6843 // Run overlays, adjust style array.
6844 for (var o = 0; o < cm.state.overlays.length; ++o) {
6845 var overlay = cm.state.overlays[o], i = 1, at = 0;
6846 runMode(cm, line.text, overlay.mode, true, function(end, style) {
6847 var start = i;
6848 // Ensure there's a token end at the current position, and that i points at it
6849 while (at < end) {
6850 var i_end = st[i];
6851 if (i_end > end)
6852 st.splice(i, 1, end, st[i+1], i_end);
6853 i += 2;
6854 at = Math.min(end, i_end);
6855 }
6856 if (!style) return;
6857 if (overlay.opaque) {
6858 st.splice(start, i - start, end, "cm-overlay " + style);
6859 i = start + 2;
6860 } else {
6861 for (; start < i; start += 2) {
6862 var cur = st[start+1];
6863 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6864 }
6865 }
6866 }, lineClasses);
6867 }
6868
6869 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6870 }
6871
6872 function getLineStyles(cm, line, updateFrontier) {
6873 if (!line.styles || line.styles[0] != cm.state.modeGen) {
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;
6877 line.styles = result.styles;
6878 if (result.classes) line.styleClasses = result.classes;
6879 else if (line.styleClasses) line.styleClasses = null;
6880 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6881 }
6882 return line.styles;
6883 }
6884
6885 // Lightweight form of highlight -- proceed over this line and
6886 // update state, but don't save a style array. Used for lines that
6887 // aren't currently visible.
6888 function processLine(cm, text, state, startAt) {
6889 var mode = cm.doc.mode;
6890 var stream = new StringStream(text, cm.options.tabSize);
6891 stream.start = stream.pos = startAt || 0;
6892 if (text == "") callBlankLine(mode, state);
6893 while (!stream.eol()) {
6894 readToken(mode, stream, state);
6895 stream.start = stream.pos;
6896 }
6897 }
6898
6899 // Convert a style as returned by a mode (either null, or a string
6900 // containing one or more styles) to a CSS style. This is cached,
6901 // and also looks for line-wide styles.
6902 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6903 function interpretTokenStyle(style, options) {
6904 if (!style || /^\s*$/.test(style)) return null;
6905 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6906 return cache[style] ||
6907 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6908 }
6909
6910 // Render the DOM representation of the text of a line. Also builds
6911 // up a 'line map', which points at the DOM nodes that represent
6912 // specific stretches of text, and is used by the measuring code.
6913 // The returned object contains the DOM node, this map, and
6914 // information about line-wide styles that were set by the mode.
6915 function buildLineContent(cm, lineView) {
6916 // The padding-right forces the element to have a 'border', which
6917 // is needed on Webkit to be able to get line-level bounding
6918 // rectangles for it (in measureChar).
6919 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6920 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
6921 col: 0, pos: 0, cm: cm,
6922 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6923 lineView.measure = {};
6924
6925 // Iterate over the logical lines that make up this visual line.
6926 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6927 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6928 builder.pos = 0;
6929 builder.addToken = buildToken;
6930 // Optionally wire in some hacks into the token-rendering
6931 // algorithm, to deal with browser quirks.
6932 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6933 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6934 builder.map = [];
6935 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6936 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6937 if (line.styleClasses) {
6938 if (line.styleClasses.bgClass)
6939 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6940 if (line.styleClasses.textClass)
6941 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6942 }
6943
6944 // Ensure at least a single node is present, for measuring.
6945 if (builder.map.length == 0)
6946 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6947
6948 // Store the map and a cache object for the current logical line
6949 if (i == 0) {
6950 lineView.measure.map = builder.map;
6951 lineView.measure.cache = {};
6952 } else {
6953 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6954 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6955 }
6956 }
6957
6958 // See issue #2901
6959 if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6960 builder.content.className = "cm-tab-wrap-hack";
6961
6962 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6963 if (builder.pre.className)
6964 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6965
6966 return builder;
6967 }
6968
6969 function defaultSpecialCharPlaceholder(ch) {
6970 var token = elt("span", "\u2022", "cm-invalidchar");
6971 token.title = "\\u" + ch.charCodeAt(0).toString(16);
6972 token.setAttribute("aria-label", token.title);
6973 return token;
6974 }
6975
6976 // Build up the DOM representation for a single token, and add it to
6977 // the line map. Takes care to render special characters separately.
6978 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6979 if (!text) return;
6980 var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6981 var special = builder.cm.state.specialChars, mustWrap = false;
6982 if (!special.test(text)) {
6983 builder.col += text.length;
6984 var content = document.createTextNode(displayText);
6985 builder.map.push(builder.pos, builder.pos + text.length, content);
6986 if (ie && ie_version < 9) mustWrap = true;
6987 builder.pos += text.length;
6988 } else {
6989 var content = document.createDocumentFragment(), pos = 0;
6990 while (true) {
6991 special.lastIndex = pos;
6992 var m = special.exec(text);
6993 var skipped = m ? m.index - pos : text.length - pos;
6994 if (skipped) {
6995 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6996 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6997 else content.appendChild(txt);
6998 builder.map.push(builder.pos, builder.pos + skipped, txt);
6999 builder.col += skipped;
7000 builder.pos += skipped;
7001 }
7002 if (!m) break;
7003 pos += skipped + 1;
7004 if (m[0] == "\t") {
7005 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
7006 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
7007 txt.setAttribute("role", "presentation");
7008 txt.setAttribute("cm-text", "\t");
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;
7014 } else {
7015 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
7016 txt.setAttribute("cm-text", m[0]);
7017 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
7018 else content.appendChild(txt);
7019 builder.col += 1;
7020 }
7021 builder.map.push(builder.pos, builder.pos + 1, txt);
7022 builder.pos++;
7023 }
7024 }
7025 if (style || startStyle || endStyle || mustWrap || css) {
7026 var fullStyle = style || "";
7027 if (startStyle) fullStyle += startStyle;
7028 if (endStyle) fullStyle += endStyle;
7029 var token = elt("span", [content], fullStyle, css);
7030 if (title) token.title = title;
7031 return builder.content.appendChild(token);
7032 }
7033 builder.content.appendChild(content);
7034 }
7035
7036 function splitSpaces(old) {
7037 var out = " ";
7038 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
7039 out += " ";
7040 return out;
7041 }
7042
7043 // Work around nonsense dimensions being reported for stretches of
7044 // right-to-left text.
7045 function buildTokenBadBidi(inner, order) {
7046 return function(builder, text, style, startStyle, endStyle, title, css) {
7047 style = style ? style + " cm-force-border" : "cm-force-border";
7048 var start = builder.pos, end = start + text.length;
7049 for (;;) {
7050 // Find the part that overlaps with the start of this text
7051 for (var i = 0; i < order.length; i++) {
7052 var part = order[i];
7053 if (part.to > start && part.from <= start) break;
7054 }
7055 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
7056 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
7057 startStyle = null;
7058 text = text.slice(part.to - start);
7059 start = part.to;
7060 }
7061 };
7062 }
7063
7064 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
7065 var widget = !ignoreWidget && marker.widgetNode;
7066 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
7067 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
7068 if (!widget)
7069 widget = builder.content.appendChild(document.createElement("span"));
7070 widget.setAttribute("cm-marker", marker.id);
7071 }
7072 if (widget) {
7073 builder.cm.display.input.setUneditable(widget);
7074 builder.content.appendChild(widget);
7075 }
7076 builder.pos += size;
7077 }
7078
7079 // Outputs a number of spans to make up a line, taking highlighting
7080 // and marked text into account.
7081 function insertLineContent(line, builder, styles) {
7082 var spans = line.markedSpans, allText = line.text, at = 0;
7083 if (!spans) {
7084 for (var i = 1; i < styles.length; i+=2)
7085 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
7086 return;
7087 }
7088
7089 var len = allText.length, pos = 0, i = 1, text = "", style, css;
7090 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
7091 for (;;) {
7092 if (nextChange == pos) { // Update current marker set
7093 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
7094 collapsed = null; nextChange = Infinity;
7095 var foundBookmarks = [], endStyles
7096 for (var j = 0; j < spans.length; ++j) {
7097 var sp = spans[j], m = sp.marker;
7098 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
7099 foundBookmarks.push(m);
7100 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
7101 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7102 nextChange = sp.to;
7103 spanEndStyle = "";
7104 }
7105 if (m.className) spanStyle += " " + m.className;
7106 if (m.css) css = (css ? css + ";" : "") + m.css;
7107 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
7108 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to)
7109 if (m.title && !title) title = m.title;
7110 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
7111 collapsed = sp;
7112 } else if (sp.from > pos && nextChange > sp.from) {
7113 nextChange = sp.from;
7114 }
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]);
7121 if (collapsed && (collapsed.from || 0) == pos) {
7122 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
7123 collapsed.marker, collapsed.from == null);
7124 if (collapsed.to == null) return;
7125 if (collapsed.to == pos) collapsed = false;
7126 }
7127 }
7128 if (pos >= len) break;
7129
7130 var upto = Math.min(len, nextChange);
7131 while (true) {
7132 if (text) {
7133 var end = pos + text.length;
7134 if (!collapsed) {
7135 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7136 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
7137 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
7138 }
7139 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7140 pos = end;
7141 spanStartStyle = "";
7142 }
7143 text = allText.slice(at, at = styles[i++]);
7144 style = interpretTokenStyle(styles[i++], builder.cm.options);
7145 }
7146 }
4698 cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
4699 startWorker(cm, 100);
4700 cm.state.modeGen++;
4701 if (cm.curOp) { regChange(cm); }
7147 4702 }
7148 4703
7149 4704 // DOCUMENT DATA STRUCTURE
@@ -7153,20 +4708,21 b''
7153 4708 // widgets and marker elements with the text behave more intuitive.
7154 4709 function isWholeLineUpdate(doc, change) {
7155 4710 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7156 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
4711 (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
7157 4712 }
7158 4713
7159 4714 // Perform a change on the document data structure.
7160 function updateDoc(doc, change, markedSpans, estimateHeight) {
7161 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4715 function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4716 function spansFor(n) {return markedSpans ? markedSpans[n] : null}
7162 4717 function update(line, text, spans) {
7163 updateLine(line, text, spans, estimateHeight);
4718 updateLine(line, text, spans, estimateHeight$$1);
7164 4719 signalLater(line, "change", line, change);
7165 4720 }
7166 4721 function linesFor(start, end) {
7167 for (var i = start, result = []; i < end; ++i)
7168 result.push(new Line(text[i], spansFor(i), estimateHeight));
7169 return result;
4722 var result = [];
4723 for (var i = start; i < end; ++i)
4724 { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
4725 return result
7170 4726 }
7171 4727
7172 4728 var from = change.from, to = change.to, text = change.text;
@@ -7182,16 +4738,16 b''
7182 4738 // sure line objects move the way they are supposed to.
7183 4739 var added = linesFor(0, text.length - 1);
7184 4740 update(lastLine, lastLine.text, lastSpans);
7185 if (nlines) doc.remove(from.line, nlines);
7186 if (added.length) doc.insert(from.line, added);
4741 if (nlines) { doc.remove(from.line, nlines); }
4742 if (added.length) { doc.insert(from.line, added); }
7187 4743 } else if (firstLine == lastLine) {
7188 4744 if (text.length == 1) {
7189 4745 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7190 4746 } else {
7191 var added = linesFor(1, text.length - 1);
7192 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4747 var added$1 = linesFor(1, text.length - 1);
4748 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
7193 4749 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7194 doc.insert(from.line + 1, added);
4750 doc.insert(from.line + 1, added$1);
7195 4751 }
7196 4752 } else if (text.length == 1) {
7197 4753 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
@@ -7199,671 +4755,52 b''
7199 4755 } else {
7200 4756 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7201 4757 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7202 var added = linesFor(1, text.length - 1);
7203 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7204 doc.insert(from.line + 1, added);
4758 var added$2 = linesFor(1, text.length - 1);
4759 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
4760 doc.insert(from.line + 1, added$2);
7205 4761 }
7206 4762
7207 4763 signalLater(doc, "change", doc, change);
7208 4764 }
7209 4765
7210 // The document is represented as a BTree consisting of leaves, with
7211 // chunk of lines in them, and branches, with up to ten leaves or
7212 // other branch nodes below them. The top node is always a branch
7213 // node, and is the document object itself (meaning it has
7214 // additional methods and properties).
7215 //
7216 // All nodes have parent links. The tree is used both to go from
7217 // line numbers to line objects, and to go from objects to numbers.
7218 // It also indexes by height, and is used to convert between height
7219 // and line object, and to find the total height of the document.
7220 //
7221 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7222
7223 function LeafChunk(lines) {
7224 this.lines = lines;
7225 this.parent = null;
7226 for (var i = 0, height = 0; i < lines.length; ++i) {
7227 lines[i].parent = this;
7228 height += lines[i].height;
7229 }
7230 this.height = height;
7231 }
7232
7233 LeafChunk.prototype = {
7234 chunkSize: function() { return this.lines.length; },
7235 // Remove the n lines at offset 'at'.
7236 removeInner: function(at, n) {
7237 for (var i = at, e = at + n; i < e; ++i) {
7238 var line = this.lines[i];
7239 this.height -= line.height;
7240 cleanUpLine(line);
7241 signalLater(line, "delete");
7242 }
7243 this.lines.splice(at, n);
7244 },
7245 // Helper used to collapse a small branch into a single leaf.
7246 collapse: function(lines) {
7247 lines.push.apply(lines, this.lines);
7248 },
7249 // Insert the given array of lines at offset 'at', count them as
7250 // having the given height.
7251 insertInner: function(at, lines, height) {
7252 this.height += height;
7253 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7254 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7255 },
7256 // Used to iterate over a part of the tree.
7257 iterN: function(at, n, op) {
7258 for (var e = at + n; at < e; ++at)
7259 if (op(this.lines[at])) return true;
7260 }
7261 };
7262
7263 function BranchChunk(children) {
7264 this.children = children;
7265 var size = 0, height = 0;
7266 for (var i = 0; i < children.length; ++i) {
7267 var ch = children[i];
7268 size += ch.chunkSize(); height += ch.height;
7269 ch.parent = this;
7270 }
7271 this.size = size;
7272 this.height = height;
7273 this.parent = null;
7274 }
7275
7276 BranchChunk.prototype = {
7277 chunkSize: function() { return this.size; },
7278 removeInner: function(at, n) {
7279 this.size -= n;
7280 for (var i = 0; i < this.children.length; ++i) {
7281 var child = this.children[i], sz = child.chunkSize();
7282 if (at < sz) {
7283 var rm = Math.min(n, sz - at), oldHeight = child.height;
7284 child.removeInner(at, rm);
7285 this.height -= oldHeight - child.height;
7286 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7287 if ((n -= rm) == 0) break;
7288 at = 0;
7289 } else at -= sz;
7290 }
7291 // If the result is smaller than 25 lines, ensure that it is a
7292 // single leaf node.
7293 if (this.size - n < 25 &&
7294 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7295 var lines = [];
7296 this.collapse(lines);
7297 this.children = [new LeafChunk(lines)];
7298 this.children[0].parent = this;
7299 }
7300 },
7301 collapse: function(lines) {
7302 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7303 },
7304 insertInner: function(at, lines, height) {
7305 this.size += lines.length;
7306 this.height += height;
7307 for (var i = 0; i < this.children.length; ++i) {
7308 var child = this.children[i], sz = child.chunkSize();
7309 if (at <= sz) {
7310 child.insertInner(at, lines, height);
7311 if (child.lines && child.lines.length > 50) {
7312 while (child.lines.length > 50) {
7313 var spilled = child.lines.splice(child.lines.length - 25, 25);
7314 var newleaf = new LeafChunk(spilled);
7315 child.height -= newleaf.height;
7316 this.children.splice(i + 1, 0, newleaf);
7317 newleaf.parent = this;
7318 }
7319 this.maybeSpill();
7320 }
7321 break;
7322 }
7323 at -= sz;
7324 }
7325 },
7326 // When a node has grown, check whether it should be split.
7327 maybeSpill: function() {
7328 if (this.children.length <= 10) return;
7329 var me = this;
7330 do {
7331 var spilled = me.children.splice(me.children.length - 5, 5);
7332 var sibling = new BranchChunk(spilled);
7333 if (!me.parent) { // Become the parent node
7334 var copy = new BranchChunk(me.children);
7335 copy.parent = me;
7336 me.children = [copy, sibling];
7337 me = copy;
7338 } else {
7339 me.size -= sibling.size;
7340 me.height -= sibling.height;
7341 var myIndex = indexOf(me.parent.children, me);
7342 me.parent.children.splice(myIndex + 1, 0, sibling);
7343 }
7344 sibling.parent = me.parent;
7345 } while (me.children.length > 10);
7346 me.parent.maybeSpill();
7347 },
7348 iterN: function(at, n, op) {
7349 for (var i = 0; i < this.children.length; ++i) {
7350 var child = this.children[i], sz = child.chunkSize();
7351 if (at < sz) {
7352 var used = Math.min(n, sz - at);
7353 if (child.iterN(at, used, op)) return true;
7354 if ((n -= used) == 0) break;
7355 at = 0;
7356 } else at -= sz;
7357 }
7358 }
7359 };
7360
7361 var nextDocId = 0;
7362 var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
7363 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
7364 if (firstLine == null) firstLine = 0;
7365
7366 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7367 this.first = firstLine;
7368 this.scrollTop = this.scrollLeft = 0;
7369 this.cantEdit = false;
7370 this.cleanGeneration = 1;
7371 this.frontier = firstLine;
7372 var start = Pos(firstLine, 0);
7373 this.sel = simpleSelection(start);
7374 this.history = new History(null);
7375 this.id = ++nextDocId;
7376 this.modeOption = mode;
7377 this.lineSep = lineSep;
7378 this.extend = false;
7379
7380 if (typeof text == "string") text = this.splitLines(text);
7381 updateDoc(this, {from: start, to: start, text: text});
7382 setSelection(this, simpleSelection(start), sel_dontScroll);
7383 };
7384
7385 Doc.prototype = createObj(BranchChunk.prototype, {
7386 constructor: Doc,
7387 // Iterate over the document. Supports two forms -- with only one
7388 // argument, it calls that for each line in the document. With
7389 // three, it iterates over the range given by the first two (with
7390 // the second being non-inclusive).
7391 iter: function(from, to, op) {
7392 if (op) this.iterN(from - this.first, to - from, op);
7393 else this.iterN(this.first, this.first + this.size, from);
7394 },
7395
7396 // Non-public interface for adding and removing lines.
7397 insert: function(at, lines) {
7398 var height = 0;
7399 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7400 this.insertInner(at - this.first, lines, height);
7401 },
7402 remove: function(at, n) { this.removeInner(at - this.first, n); },
7403
7404 // From here, the methods are part of the public interface. Most
7405 // are also available from CodeMirror (editor) instances.
7406
7407 getValue: function(lineSep) {
7408 var lines = getLines(this, this.first, this.first + this.size);
7409 if (lineSep === false) return lines;
7410 return lines.join(lineSep || this.lineSeparator());
7411 },
7412 setValue: docMethodOp(function(code) {
7413 var top = Pos(this.first, 0), last = this.first + this.size - 1;
7414 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7415 text: this.splitLines(code), origin: "setValue", full: true}, true);
7416 setSelection(this, simpleSelection(top));
7417 }),
7418 replaceRange: function(code, from, to, origin) {
7419 from = clipPos(this, from);
7420 to = to ? clipPos(this, to) : from;
7421 replaceRange(this, code, from, to, origin);
7422 },
7423 getRange: function(from, to, lineSep) {
7424 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7425 if (lineSep === false) return lines;
7426 return lines.join(lineSep || this.lineSeparator());
7427 },
7428
7429 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7430
7431 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7432 getLineNumber: function(line) {return lineNo(line);},
7433
7434 getLineHandleVisualStart: function(line) {
7435 if (typeof line == "number") line = getLine(this, line);
7436 return visualLine(line);
7437 },
7438
7439 lineCount: function() {return this.size;},
7440 firstLine: function() {return this.first;},
7441 lastLine: function() {return this.first + this.size - 1;},
7442
7443 clipPos: function(pos) {return clipPos(this, pos);},
7444
7445 getCursor: function(start) {
7446 var range = this.sel.primary(), pos;
7447 if (start == null || start == "head") pos = range.head;
7448 else if (start == "anchor") pos = range.anchor;
7449 else if (start == "end" || start == "to" || start === false) pos = range.to();
7450 else pos = range.from();
7451 return pos;
7452 },
7453 listSelections: function() { return this.sel.ranges; },
7454 somethingSelected: function() {return this.sel.somethingSelected();},
7455
7456 setCursor: docMethodOp(function(line, ch, options) {
7457 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7458 }),
7459 setSelection: docMethodOp(function(anchor, head, options) {
7460 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7461 }),
7462 extendSelection: docMethodOp(function(head, other, options) {
7463 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7464 }),
7465 extendSelections: docMethodOp(function(heads, options) {
7466 extendSelections(this, clipPosArray(this, heads), options);
7467 }),
7468 extendSelectionsBy: docMethodOp(function(f, options) {
7469 var heads = map(this.sel.ranges, f);
7470 extendSelections(this, clipPosArray(this, heads), options);
7471 }),
7472 setSelections: docMethodOp(function(ranges, primary, options) {
7473 if (!ranges.length) return;
7474 for (var i = 0, out = []; i < ranges.length; i++)
7475 out[i] = new Range(clipPos(this, ranges[i].anchor),
7476 clipPos(this, ranges[i].head));
7477 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
7478 setSelection(this, normalizeSelection(out, primary), options);
7479 }),
7480 addSelection: docMethodOp(function(anchor, head, options) {
7481 var ranges = this.sel.ranges.slice(0);
7482 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7483 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7484 }),
7485
7486 getSelection: function(lineSep) {
7487 var ranges = this.sel.ranges, lines;
7488 for (var i = 0; i < ranges.length; i++) {
7489 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7490 lines = lines ? lines.concat(sel) : sel;
7491 }
7492 if (lineSep === false) return lines;
7493 else return lines.join(lineSep || this.lineSeparator());
7494 },
7495 getSelections: function(lineSep) {
7496 var parts = [], ranges = this.sel.ranges;
7497 for (var i = 0; i < ranges.length; i++) {
7498 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7499 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
7500 parts[i] = sel;
7501 }
7502 return parts;
7503 },
7504 replaceSelection: function(code, collapse, origin) {
7505 var dup = [];
7506 for (var i = 0; i < this.sel.ranges.length; i++)
7507 dup[i] = code;
7508 this.replaceSelections(dup, collapse, origin || "+input");
7509 },
7510 replaceSelections: docMethodOp(function(code, collapse, origin) {
7511 var changes = [], sel = this.sel;
7512 for (var i = 0; i < sel.ranges.length; i++) {
7513 var range = sel.ranges[i];
7514 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
7515 }
7516 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7517 for (var i = changes.length - 1; i >= 0; i--)
7518 makeChange(this, changes[i]);
7519 if (newSel) setSelectionReplaceHistory(this, newSel);
7520 else if (this.cm) ensureCursorVisible(this.cm);
7521 }),
7522 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7523 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7524 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7525 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7526
7527 setExtending: function(val) {this.extend = val;},
7528 getExtending: function() {return this.extend;},
7529
7530 historySize: function() {
7531 var hist = this.history, done = 0, undone = 0;
7532 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7533 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
7534 return {undo: done, redo: undone};
7535 },
7536 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7537
7538 markClean: function() {
7539 this.cleanGeneration = this.changeGeneration(true);
7540 },
7541 changeGeneration: function(forceSplit) {
7542 if (forceSplit)
7543 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7544 return this.history.generation;
7545 },
7546 isClean: function (gen) {
7547 return this.history.generation == (gen || this.cleanGeneration);
7548 },
7549
7550 getHistory: function() {
7551 return {done: copyHistoryArray(this.history.done),
7552 undone: copyHistoryArray(this.history.undone)};
7553 },
7554 setHistory: function(histData) {
7555 var hist = this.history = new History(this.history.maxGeneration);
7556 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7557 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7558 },
7559
7560 addLineClass: docMethodOp(function(handle, where, cls) {
7561 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7562 var prop = where == "text" ? "textClass"
7563 : where == "background" ? "bgClass"
7564 : where == "gutter" ? "gutterClass" : "wrapClass";
7565 if (!line[prop]) line[prop] = cls;
7566 else if (classTest(cls).test(line[prop])) return false;
7567 else line[prop] += " " + cls;
7568 return true;
7569 });
7570 }),
7571 removeLineClass: docMethodOp(function(handle, where, cls) {
7572 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7573 var prop = where == "text" ? "textClass"
7574 : where == "background" ? "bgClass"
7575 : where == "gutter" ? "gutterClass" : "wrapClass";
7576 var cur = line[prop];
7577 if (!cur) return false;
7578 else if (cls == null) line[prop] = null;
7579 else {
7580 var found = cur.match(classTest(cls));
7581 if (!found) return false;
7582 var end = found.index + found[0].length;
7583 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7584 }
7585 return true;
7586 });
7587 }),
7588
7589 addLineWidget: docMethodOp(function(handle, node, options) {
7590 return addLineWidget(this, handle, node, options);
7591 }),
7592 removeLineWidget: function(widget) { widget.clear(); },
7593
7594 markText: function(from, to, options) {
7595 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range");
7596 },
7597 setBookmark: function(pos, options) {
7598 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7599 insertLeft: options && options.insertLeft,
7600 clearWhenEmpty: false, shared: options && options.shared,
7601 handleMouseEvents: options && options.handleMouseEvents};
7602 pos = clipPos(this, pos);
7603 return markText(this, pos, pos, realOpts, "bookmark");
7604 },
7605 findMarksAt: function(pos) {
7606 pos = clipPos(this, pos);
7607 var markers = [], spans = getLine(this, pos.line).markedSpans;
7608 if (spans) for (var i = 0; i < spans.length; ++i) {
7609 var span = spans[i];
7610 if ((span.from == null || span.from <= pos.ch) &&
7611 (span.to == null || span.to >= pos.ch))
7612 markers.push(span.marker.parent || span.marker);
7613 }
7614 return markers;
7615 },
7616 findMarks: function(from, to, filter) {
7617 from = clipPos(this, from); to = clipPos(this, to);
7618 var found = [], lineNo = from.line;
7619 this.iter(from.line, to.line + 1, function(line) {
7620 var spans = line.markedSpans;
7621 if (spans) for (var i = 0; i < spans.length; i++) {
7622 var span = spans[i];
7623 if (!(lineNo == from.line && from.ch > span.to ||
7624 span.from == null && lineNo != from.line||
7625 lineNo == to.line && span.from > to.ch) &&
7626 (!filter || filter(span.marker)))
7627 found.push(span.marker.parent || span.marker);
7628 }
7629 ++lineNo;
7630 });
7631 return found;
7632 },
7633 getAllMarks: function() {
7634 var markers = [];
7635 this.iter(function(line) {
7636 var sps = line.markedSpans;
7637 if (sps) for (var i = 0; i < sps.length; ++i)
7638 if (sps[i].from != null) markers.push(sps[i].marker);
7639 });
7640 return markers;
7641 },
7642
7643 posFromIndex: function(off) {
7644 var ch, lineNo = this.first;
7645 this.iter(function(line) {
7646 var sz = line.text.length + 1;
7647 if (sz > off) { ch = off; return true; }
7648 off -= sz;
7649 ++lineNo;
7650 });
7651 return clipPos(this, Pos(lineNo, ch));
7652 },
7653 indexFromPos: function (coords) {
7654 coords = clipPos(this, coords);
7655 var index = coords.ch;
7656 if (coords.line < this.first || coords.ch < 0) return 0;
7657 this.iter(this.first, coords.line, function (line) {
7658 index += line.text.length + 1;
7659 });
7660 return index;
7661 },
7662
7663 copy: function(copyHistory) {
7664 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7665 this.modeOption, this.first, this.lineSep);
7666 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7667 doc.sel = this.sel;
7668 doc.extend = false;
7669 if (copyHistory) {
7670 doc.history.undoDepth = this.history.undoDepth;
7671 doc.setHistory(this.getHistory());
7672 }
7673 return doc;
7674 },
7675
7676 linkedDoc: function(options) {
7677 if (!options) options = {};
7678 var from = this.first, to = this.first + this.size;
7679 if (options.from != null && options.from > from) from = options.from;
7680 if (options.to != null && options.to < to) to = options.to;
7681 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep);
7682 if (options.sharedHist) copy.history = this.history;
7683 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7684 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7685 copySharedMarkers(copy, findSharedMarkers(this));
7686 return copy;
7687 },
7688 unlinkDoc: function(other) {
7689 if (other instanceof CodeMirror) other = other.doc;
7690 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7691 var link = this.linked[i];
7692 if (link.doc != other) continue;
7693 this.linked.splice(i, 1);
7694 other.unlinkDoc(this);
7695 detachSharedMarkers(findSharedMarkers(this));
7696 break;
7697 }
7698 // If the histories were shared, split them again
7699 if (other.history == this.history) {
7700 var splitIds = [other.id];
7701 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7702 other.history = new History(null);
7703 other.history.done = copyHistoryArray(this.history.done, splitIds);
7704 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7705 }
7706 },
7707 iterLinkedDocs: function(f) {linkedDocs(this, f);},
7708
7709 getMode: function() {return this.mode;},
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"; }
7717 });
7718
7719 // Public alias.
7720 Doc.prototype.eachLine = Doc.prototype.iter;
7721
7722 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7723 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7724 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7725 CodeMirror.prototype[prop] = (function(method) {
7726 return function() {return method.apply(this.doc, arguments);};
7727 })(Doc.prototype[prop]);
7728
7729 eventMixin(Doc);
7730
7731 4766 // Call f for all linked documents.
7732 4767 function linkedDocs(doc, f, sharedHistOnly) {
7733 4768 function propagate(doc, skip, sharedHist) {
7734 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4769 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
7735 4770 var rel = doc.linked[i];
7736 if (rel.doc == skip) continue;
4771 if (rel.doc == skip) { continue }
7737 4772 var shared = sharedHist && rel.sharedHist;
7738 if (sharedHistOnly && !shared) continue;
4773 if (sharedHistOnly && !shared) { continue }
7739 4774 f(rel.doc, shared);
7740 4775 propagate(rel.doc, doc, shared);
7741 }
4776 } }
7742 4777 }
7743 4778 propagate(doc, null, true);
7744 4779 }
7745 4780
7746 4781 // Attach a document to an editor.
7747 4782 function attachDoc(cm, doc) {
7748 if (doc.cm) throw new Error("This document is already in use.");
4783 if (doc.cm) { throw new Error("This document is already in use.") }
7749 4784 cm.doc = doc;
7750 4785 doc.cm = cm;
7751 4786 estimateLineHeights(cm);
7752 4787 loadMode(cm);
7753 if (!cm.options.lineWrapping) findMaxLine(cm);
4788 setDirectionClass(cm);
4789 if (!cm.options.lineWrapping) { findMaxLine(cm); }
7754 4790 cm.options.mode = doc.modeOption;
7755 4791 regChange(cm);
7756 4792 }
7757 4793
7758 // LINE UTILITIES
7759
7760 // Find the line object corresponding to the given line number.
7761 function getLine(doc, n) {
7762 n -= doc.first;
7763 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7764 for (var chunk = doc; !chunk.lines;) {
7765 for (var i = 0;; ++i) {
7766 var child = chunk.children[i], sz = child.chunkSize();
7767 if (n < sz) { chunk = child; break; }
7768 n -= sz;
7769 }
7770 }
7771 return chunk.lines[n];
7772 }
7773
7774 // Get the part of a document between two positions, as an array of
7775 // strings.
7776 function getBetween(doc, start, end) {
7777 var out = [], n = start.line;
7778 doc.iter(start.line, end.line + 1, function(line) {
7779 var text = line.text;
7780 if (n == end.line) text = text.slice(0, end.ch);
7781 if (n == start.line) text = text.slice(start.ch);
7782 out.push(text);
7783 ++n;
4794 function setDirectionClass(cm) {
4795 (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
4796 }
4797
4798 function directionChanged(cm) {
4799 runInOp(cm, function () {
4800 setDirectionClass(cm);
4801 regChange(cm);
7784 4802 });
7785 return out;
7786 }
7787 // Get the lines between from and to, as array of strings.
7788 function getLines(doc, from, to) {
7789 var out = [];
7790 doc.iter(from, to, function(line) { out.push(line.text); });
7791 return out;
7792 }
7793
7794 // Update the height of a line, propagating the height change
7795 // upwards to parent nodes.
7796 function updateLineHeight(line, height) {
7797 var diff = height - line.height;
7798 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7799 }
7800
7801 // Given a line object, find its line number by walking up through
7802 // its parent links.
7803 function lineNo(line) {
7804 if (line.parent == null) return null;
7805 var cur = line.parent, no = indexOf(cur.lines, line);
7806 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7807 for (var i = 0;; ++i) {
7808 if (chunk.children[i] == cur) break;
7809 no += chunk.children[i].chunkSize();
7810 }
7811 }
7812 return no + cur.first;
7813 }
7814
7815 // Find the line at the given vertical position, using the height
7816 // information in the document tree.
7817 function lineAtHeight(chunk, h) {
7818 var n = chunk.first;
7819 outer: do {
7820 for (var i = 0; i < chunk.children.length; ++i) {
7821 var child = chunk.children[i], ch = child.height;
7822 if (h < ch) { chunk = child; continue outer; }
7823 h -= ch;
7824 n += child.chunkSize();
7825 }
7826 return n;
7827 } while (!chunk.lines);
7828 for (var i = 0; i < chunk.lines.length; ++i) {
7829 var line = chunk.lines[i], lh = line.height;
7830 if (h < lh) break;
7831 h -= lh;
7832 }
7833 return n + i;
7834 }
7835
7836
7837 // Find the height above the given line.
7838 function heightAtLine(lineObj) {
7839 lineObj = visualLine(lineObj);
7840
7841 var h = 0, chunk = lineObj.parent;
7842 for (var i = 0; i < chunk.lines.length; ++i) {
7843 var line = chunk.lines[i];
7844 if (line == lineObj) break;
7845 else h += line.height;
7846 }
7847 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7848 for (var i = 0; i < p.children.length; ++i) {
7849 var cur = p.children[i];
7850 if (cur == chunk) break;
7851 else h += cur.height;
7852 }
7853 }
7854 return h;
7855 }
7856
7857 // Get the bidi ordering for the given line (and cache it). Returns
7858 // false for lines that are fully left-to-right, and an array of
7859 // BidiSpan objects otherwise.
7860 function getOrder(line) {
7861 var order = line.order;
7862 if (order == null) order = line.order = bidiOrdering(line.text);
7863 return order;
7864 }
7865
7866 // HISTORY
4803 }
7867 4804
7868 4805 function History(startGen) {
7869 4806 // Arrays of change events and selections. Doing something adds an
@@ -7885,8 +4822,8 b''
7885 4822 function historyChangeFromChange(doc, change) {
7886 4823 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7887 4824 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7888 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7889 return histChange;
4825 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
4826 return histChange
7890 4827 }
7891 4828
7892 4829 // Pop all selection events off the end of a history array. Stop at
@@ -7894,8 +4831,8 b''
7894 4831 function clearSelectionEvents(array) {
7895 4832 while (array.length) {
7896 4833 var last = lst(array);
7897 if (last.ranges) array.pop();
7898 else break;
4834 if (last.ranges) { array.pop(); }
4835 else { break }
7899 4836 }
7900 4837 }
7901 4838
@@ -7904,30 +4841,31 b''
7904 4841 function lastChangeEvent(hist, force) {
7905 4842 if (force) {
7906 4843 clearSelectionEvents(hist.done);
7907 return lst(hist.done);
4844 return lst(hist.done)
7908 4845 } else if (hist.done.length && !lst(hist.done).ranges) {
7909 return lst(hist.done);
4846 return lst(hist.done)
7910 4847 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7911 4848 hist.done.pop();
7912 return lst(hist.done);
4849 return lst(hist.done)
7913 4850 }
7914 4851 }
7915 4852
7916 4853 // Register a change in the history. Merges changes that are within
7917 // a single operation, ore are close together with an origin that
4854 // a single operation, or are close together with an origin that
7918 4855 // allows merging (starting with "+") into a single event.
7919 4856 function addChangeToHistory(doc, change, selAfter, opId) {
7920 4857 var hist = doc.history;
7921 4858 hist.undone.length = 0;
7922 4859 var time = +new Date, cur;
4860 var last;
7923 4861
7924 4862 if ((hist.lastOp == opId ||
7925 4863 hist.lastOrigin == change.origin && change.origin &&
7926 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4864 ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
7927 4865 change.origin.charAt(0) == "*")) &&
7928 4866 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7929 4867 // Merge this change into the last event
7930 var last = lst(cur.changes);
4868 last = lst(cur.changes);
7931 4869 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7932 4870 // Optimized case for simple insertion -- don't want to add
7933 4871 // new changesets for every character typed
@@ -7940,13 +4878,13 b''
7940 4878 // Can not be merged, start a new event.
7941 4879 var before = lst(hist.done);
7942 4880 if (!before || !before.ranges)
7943 pushSelectionToHistory(doc.sel, hist.done);
4881 { pushSelectionToHistory(doc.sel, hist.done); }
7944 4882 cur = {changes: [historyChangeFromChange(doc, change)],
7945 4883 generation: hist.generation};
7946 4884 hist.done.push(cur);
7947 4885 while (hist.done.length > hist.undoDepth) {
7948 4886 hist.done.shift();
7949 if (!hist.done[0].ranges) hist.done.shift();
4887 if (!hist.done[0].ranges) { hist.done.shift(); }
7950 4888 }
7951 4889 }
7952 4890 hist.done.push(selAfter);
@@ -7955,7 +4893,7 b''
7955 4893 hist.lastOp = hist.lastSelOp = opId;
7956 4894 hist.lastOrigin = hist.lastSelOrigin = change.origin;
7957 4895
7958 if (!last) signal(doc, "historyAdded");
4896 if (!last) { signal(doc, "historyAdded"); }
7959 4897 }
7960 4898
7961 4899 function selectionEventCanBeMerged(doc, origin, prev, sel) {
@@ -7964,7 +4902,7 b''
7964 4902 ch == "+" &&
7965 4903 prev.ranges.length == sel.ranges.length &&
7966 4904 prev.somethingSelected() == sel.somethingSelected() &&
7967 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
4905 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
7968 4906 }
7969 4907
7970 4908 // Called whenever the selection changes, sets the new selection as
@@ -7982,29 +4920,29 b''
7982 4920 (origin && hist.lastSelOrigin == origin &&
7983 4921 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7984 4922 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7985 hist.done[hist.done.length - 1] = sel;
4923 { hist.done[hist.done.length - 1] = sel; }
7986 4924 else
7987 pushSelectionToHistory(sel, hist.done);
4925 { pushSelectionToHistory(sel, hist.done); }
7988 4926
7989 4927 hist.lastSelTime = +new Date;
7990 4928 hist.lastSelOrigin = origin;
7991 4929 hist.lastSelOp = opId;
7992 4930 if (options && options.clearRedo !== false)
7993 clearSelectionEvents(hist.undone);
4931 { clearSelectionEvents(hist.undone); }
7994 4932 }
7995 4933
7996 4934 function pushSelectionToHistory(sel, dest) {
7997 4935 var top = lst(dest);
7998 4936 if (!(top && top.ranges && top.equals(sel)))
7999 dest.push(sel);
4937 { dest.push(sel); }
8000 4938 }
8001 4939
8002 4940 // Used to store marked span information in the history.
8003 4941 function attachLocalSpans(doc, change, from, to) {
8004 4942 var existing = change["spans_" + doc.id], n = 0;
8005 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4943 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
8006 4944 if (line.markedSpans)
8007 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4945 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
8008 4946 ++n;
8009 4947 });
8010 4948 }
@@ -8012,46 +4950,549 b''
8012 4950 // When un/re-doing restores text containing marked spans, those
8013 4951 // that have been explicitly cleared should not be restored.
8014 4952 function removeClearedSpans(spans) {
8015 if (!spans) return null;
8016 for (var i = 0, out; i < spans.length; ++i) {
8017 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
8018 else if (out) out.push(spans[i]);
8019 }
8020 return !out ? spans : out.length ? out : null;
4953 if (!spans) { return null }
4954 var out;
4955 for (var i = 0; i < spans.length; ++i) {
4956 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
4957 else if (out) { out.push(spans[i]); }
4958 }
4959 return !out ? spans : out.length ? out : null
8021 4960 }
8022 4961
8023 4962 // Retrieve and filter the old marked spans stored in a change event.
8024 4963 function getOldSpans(doc, change) {
8025 4964 var found = change["spans_" + doc.id];
8026 if (!found) return null;
8027 for (var i = 0, nw = []; i < change.text.length; ++i)
8028 nw.push(removeClearedSpans(found[i]));
8029 return nw;
4965 if (!found) { return null }
4966 var nw = [];
4967 for (var i = 0; i < change.text.length; ++i)
4968 { nw.push(removeClearedSpans(found[i])); }
4969 return nw
4970 }
4971
4972 // Used for un/re-doing changes from the history. Combines the
4973 // result of computing the existing spans with the set of spans that
4974 // existed in the history (so that deleting around a span and then
4975 // undoing brings back the span).
4976 function mergeOldSpans(doc, change) {
4977 var old = getOldSpans(doc, change);
4978 var stretched = stretchSpansOverChange(doc, change);
4979 if (!old) { return stretched }
4980 if (!stretched) { return old }
4981
4982 for (var i = 0; i < old.length; ++i) {
4983 var oldCur = old[i], stretchCur = stretched[i];
4984 if (oldCur && stretchCur) {
4985 spans: for (var j = 0; j < stretchCur.length; ++j) {
4986 var span = stretchCur[j];
4987 for (var k = 0; k < oldCur.length; ++k)
4988 { if (oldCur[k].marker == span.marker) { continue spans } }
4989 oldCur.push(span);
4990 }
4991 } else if (stretchCur) {
4992 old[i] = stretchCur;
4993 }
4994 }
4995 return old
8030 4996 }
8031 4997
8032 4998 // Used both to provide a JSON-safe object in .getHistory, and, when
8033 4999 // detaching a document, to split the history in two
8034 5000 function copyHistoryArray(events, newGroup, instantiateSel) {
8035 for (var i = 0, copy = []; i < events.length; ++i) {
5001 var copy = [];
5002 for (var i = 0; i < events.length; ++i) {
8036 5003 var event = events[i];
8037 5004 if (event.ranges) {
8038 5005 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
8039 continue;
5006 continue
8040 5007 }
8041 5008 var changes = event.changes, newChanges = [];
8042 5009 copy.push({changes: newChanges});
8043 5010 for (var j = 0; j < changes.length; ++j) {
8044 var change = changes[j], m;
5011 var change = changes[j], m = (void 0);
8045 5012 newChanges.push({from: change.from, to: change.to, text: change.text});
8046 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
5013 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
8047 5014 if (indexOf(newGroup, Number(m[1])) > -1) {
8048 5015 lst(newChanges)[prop] = change[prop];
8049 5016 delete change[prop];
8050 5017 }
5018 } } }
5019 }
5020 }
5021 return copy
5022 }
5023
5024 // The 'scroll' parameter given to many of these indicated whether
5025 // the new cursor position should be scrolled into view after
5026 // modifying the selection.
5027
5028 // If shift is held or the extend flag is set, extends a range to
5029 // include a given position (and optionally a second position).
5030 // Otherwise, simply returns the range between the given positions.
5031 // Used for cursor motion and such.
5032 function extendRange(range, head, other, extend) {
5033 if (extend) {
5034 var anchor = range.anchor;
5035 if (other) {
5036 var posBefore = cmp(head, anchor) < 0;
5037 if (posBefore != (cmp(other, anchor) < 0)) {
5038 anchor = head;
5039 head = other;
5040 } else if (posBefore != (cmp(head, other) < 0)) {
5041 head = other;
8051 5042 }
8052 5043 }
8053 }
8054 return copy;
5044 return new Range(anchor, head)
5045 } else {
5046 return new Range(other || head, head)
5047 }
5048 }
5049
5050 // Extend the primary selection range, discard the rest.
5051 function extendSelection(doc, head, other, options, extend) {
5052 if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
5053 setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
5054 }
5055
5056 // Extend all selections (pos is an array of selections with length
5057 // equal the number of selections)
5058 function extendSelections(doc, heads, options) {
5059 var out = [];
5060 var extend = doc.cm && (doc.cm.display.shift || doc.extend);
5061 for (var i = 0; i < doc.sel.ranges.length; i++)
5062 { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
5063 var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
5064 setSelection(doc, newSel, options);
5065 }
5066
5067 // Updates a single range in the selection.
5068 function replaceOneSelection(doc, i, range, options) {
5069 var ranges = doc.sel.ranges.slice(0);
5070 ranges[i] = range;
5071 setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
5072 }
5073
5074 // Reset the selection to a single range.
5075 function setSimpleSelection(doc, anchor, head, options) {
5076 setSelection(doc, simpleSelection(anchor, head), options);
5077 }
5078
5079 // Give beforeSelectionChange handlers a change to influence a
5080 // selection update.
5081 function filterSelectionChange(doc, sel, options) {
5082 var obj = {
5083 ranges: sel.ranges,
5084 update: function(ranges) {
5085 var this$1 = this;
5086
5087 this.ranges = [];
5088 for (var i = 0; i < ranges.length; i++)
5089 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5090 clipPos(doc, ranges[i].head)); }
5091 },
5092 origin: options && options.origin
5093 };
5094 signal(doc, "beforeSelectionChange", doc, obj);
5095 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
5096 if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
5097 else { return sel }
5098 }
5099
5100 function setSelectionReplaceHistory(doc, sel, options) {
5101 var done = doc.history.done, last = lst(done);
5102 if (last && last.ranges) {
5103 done[done.length - 1] = sel;
5104 setSelectionNoUndo(doc, sel, options);
5105 } else {
5106 setSelection(doc, sel, options);
5107 }
5108 }
5109
5110 // Set a new selection.
5111 function setSelection(doc, sel, options) {
5112 setSelectionNoUndo(doc, sel, options);
5113 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
5114 }
5115
5116 function setSelectionNoUndo(doc, sel, options) {
5117 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5118 { sel = filterSelectionChange(doc, sel, options); }
5119
5120 var bias = options && options.bias ||
5121 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
5122 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
5123
5124 if (!(options && options.scroll === false) && doc.cm)
5125 { ensureCursorVisible(doc.cm); }
5126 }
5127
5128 function setSelectionInner(doc, sel) {
5129 if (sel.equals(doc.sel)) { return }
5130
5131 doc.sel = sel;
5132
5133 if (doc.cm) {
5134 doc.cm.curOp.updateInput = 1;
5135 doc.cm.curOp.selectionChanged = true;
5136 signalCursorActivity(doc.cm);
5137 }
5138 signalLater(doc, "cursorActivity", doc);
5139 }
5140
5141 // Verify that the selection does not partially select any atomic
5142 // marked ranges.
5143 function reCheckSelection(doc) {
5144 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
5145 }
5146
5147 // Return a selection that does not partially select any atomic
5148 // ranges.
5149 function skipAtomicInSelection(doc, sel, bias, mayClear) {
5150 var out;
5151 for (var i = 0; i < sel.ranges.length; i++) {
5152 var range = sel.ranges[i];
5153 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
5154 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
5155 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
5156 if (out || newAnchor != range.anchor || newHead != range.head) {
5157 if (!out) { out = sel.ranges.slice(0, i); }
5158 out[i] = new Range(newAnchor, newHead);
5159 }
5160 }
5161 return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
5162 }
5163
5164 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5165 var line = getLine(doc, pos.line);
5166 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5167 var sp = line.markedSpans[i], m = sp.marker;
5168
5169 // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
5170 // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
5171 // is with selectLeft/Right
5172 var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
5173 var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
5174
5175 if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5176 (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5177 if (mayClear) {
5178 signal(m, "beforeCursorEnter");
5179 if (m.explicitlyCleared) {
5180 if (!line.markedSpans) { break }
5181 else {--i; continue}
5182 }
5183 }
5184 if (!m.atomic) { continue }
5185
5186 if (oldPos) {
5187 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
5188 if (dir < 0 ? preventCursorRight : preventCursorLeft)
5189 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
5190 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5191 { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5192 }
5193
5194 var far = m.find(dir < 0 ? -1 : 1);
5195 if (dir < 0 ? preventCursorLeft : preventCursorRight)
5196 { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
5197 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5198 }
5199 } }
5200 return pos
5201 }
5202
5203 // Ensure a given position is not inside an atomic range.
5204 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5205 var dir = bias || 1;
5206 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5207 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5208 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5209 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
5210 if (!found) {
5211 doc.cantEdit = true;
5212 return Pos(doc.first, 0)
5213 }
5214 return found
5215 }
5216
5217 function movePos(doc, pos, dir, line) {
5218 if (dir < 0 && pos.ch == 0) {
5219 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5220 else { return null }
5221 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5222 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5223 else { return null }
5224 } else {
5225 return new Pos(pos.line, pos.ch + dir)
5226 }
5227 }
5228
5229 function selectAll(cm) {
5230 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
5231 }
5232
5233 // UPDATING
5234
5235 // Allow "beforeChange" event handlers to influence a change
5236 function filterChange(doc, change, update) {
5237 var obj = {
5238 canceled: false,
5239 from: change.from,
5240 to: change.to,
5241 text: change.text,
5242 origin: change.origin,
5243 cancel: function () { return obj.canceled = true; }
5244 };
5245 if (update) { obj.update = function (from, to, text, origin) {
5246 if (from) { obj.from = clipPos(doc, from); }
5247 if (to) { obj.to = clipPos(doc, to); }
5248 if (text) { obj.text = text; }
5249 if (origin !== undefined) { obj.origin = origin; }
5250 }; }
5251 signal(doc, "beforeChange", doc, obj);
5252 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
5253
5254 if (obj.canceled) {
5255 if (doc.cm) { doc.cm.curOp.updateInput = 2; }
5256 return null
5257 }
5258 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5259 }
5260
5261 // Apply a change to a document, and add it to the document's
5262 // history, and propagating it to all linked documents.
5263 function makeChange(doc, change, ignoreReadOnly) {
5264 if (doc.cm) {
5265 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5266 if (doc.cm.state.suppressEdits) { return }
5267 }
5268
5269 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5270 change = filterChange(doc, change, true);
5271 if (!change) { return }
5272 }
5273
5274 // Possibly split or suppress the update based on the presence
5275 // of read-only spans in its range.
5276 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
5277 if (split) {
5278 for (var i = split.length - 1; i >= 0; --i)
5279 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
5280 } else {
5281 makeChangeInner(doc, change);
5282 }
5283 }
5284
5285 function makeChangeInner(doc, change) {
5286 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5287 var selAfter = computeSelAfterChange(doc, change);
5288 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
5289
5290 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
5291 var rebased = [];
5292
5293 linkedDocs(doc, function (doc, sharedHist) {
5294 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5295 rebaseHist(doc.history, change);
5296 rebased.push(doc.history);
5297 }
5298 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
5299 });
5300 }
5301
5302 // Revert a change stored in a document's history.
5303 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5304 var suppress = doc.cm && doc.cm.state.suppressEdits;
5305 if (suppress && !allowSelectionOnly) { return }
5306
5307 var hist = doc.history, event, selAfter = doc.sel;
5308 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
5309
5310 // Verify that there is a useable event (so that ctrl-z won't
5311 // needlessly clear selection events)
5312 var i = 0;
5313 for (; i < source.length; i++) {
5314 event = source[i];
5315 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5316 { break }
5317 }
5318 if (i == source.length) { return }
5319 hist.lastOrigin = hist.lastSelOrigin = null;
5320
5321 for (;;) {
5322 event = source.pop();
5323 if (event.ranges) {
5324 pushSelectionToHistory(event, dest);
5325 if (allowSelectionOnly && !event.equals(doc.sel)) {
5326 setSelection(doc, event, {clearRedo: false});
5327 return
5328 }
5329 selAfter = event;
5330 } else if (suppress) {
5331 source.push(event);
5332 return
5333 } else { break }
5334 }
5335
5336 // Build up a reverse change object to add to the opposite history
5337 // stack (redo when undoing, and vice versa).
5338 var antiChanges = [];
5339 pushSelectionToHistory(selAfter, dest);
5340 dest.push({changes: antiChanges, generation: hist.generation});
5341 hist.generation = event.generation || ++hist.maxGeneration;
5342
5343 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
5344
5345 var loop = function ( i ) {
5346 var change = event.changes[i];
5347 change.origin = type;
5348 if (filter && !filterChange(doc, change, false)) {
5349 source.length = 0;
5350 return {}
5351 }
5352
5353 antiChanges.push(historyChangeFromChange(doc, change));
5354
5355 var after = i ? computeSelAfterChange(doc, change) : lst(source);
5356 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
5357 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
5358 var rebased = [];
5359
5360 // Propagate to the linked documents
5361 linkedDocs(doc, function (doc, sharedHist) {
5362 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5363 rebaseHist(doc.history, change);
5364 rebased.push(doc.history);
5365 }
5366 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
5367 });
5368 };
5369
5370 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5371 var returned = loop( i$1 );
5372
5373 if ( returned ) return returned.v;
5374 }
5375 }
5376
5377 // Sub-views need their line numbers shifted when text is added
5378 // above or below them in the parent document.
5379 function shiftDoc(doc, distance) {
5380 if (distance == 0) { return }
5381 doc.first += distance;
5382 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5383 Pos(range.anchor.line + distance, range.anchor.ch),
5384 Pos(range.head.line + distance, range.head.ch)
5385 ); }), doc.sel.primIndex);
5386 if (doc.cm) {
5387 regChange(doc.cm, doc.first, doc.first - distance, distance);
5388 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5389 { regLineChange(doc.cm, l, "gutter"); }
5390 }
5391 }
5392
5393 // More lower-level change function, handling only a single document
5394 // (not linked ones).
5395 function makeChangeSingleDoc(doc, change, selAfter, spans) {
5396 if (doc.cm && !doc.cm.curOp)
5397 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5398
5399 if (change.to.line < doc.first) {
5400 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
5401 return
5402 }
5403 if (change.from.line > doc.lastLine()) { return }
5404
5405 // Clip the change to the size of this doc
5406 if (change.from.line < doc.first) {
5407 var shift = change.text.length - 1 - (doc.first - change.from.line);
5408 shiftDoc(doc, shift);
5409 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5410 text: [lst(change.text)], origin: change.origin};
5411 }
5412 var last = doc.lastLine();
5413 if (change.to.line > last) {
5414 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5415 text: [change.text[0]], origin: change.origin};
5416 }
5417
5418 change.removed = getBetween(doc, change.from, change.to);
5419
5420 if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
5421 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
5422 else { updateDoc(doc, change, spans); }
5423 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
5424
5425 if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
5426 { doc.cantEdit = false; }
5427 }
5428
5429 // Handle the interaction of a change to a document with the editor
5430 // that this document is part of.
5431 function makeChangeSingleDocInEditor(cm, change, spans) {
5432 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
5433
5434 var recomputeMaxLength = false, checkWidthStart = from.line;
5435 if (!cm.options.lineWrapping) {
5436 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
5437 doc.iter(checkWidthStart, to.line + 1, function (line) {
5438 if (line == display.maxLine) {
5439 recomputeMaxLength = true;
5440 return true
5441 }
5442 });
5443 }
5444
5445 if (doc.sel.contains(change.from, change.to) > -1)
5446 { signalCursorActivity(cm); }
5447
5448 updateDoc(doc, change, spans, estimateHeight(cm));
5449
5450 if (!cm.options.lineWrapping) {
5451 doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5452 var len = lineLength(line);
5453 if (len > display.maxLineLength) {
5454 display.maxLine = line;
5455 display.maxLineLength = len;
5456 display.maxLineChanged = true;
5457 recomputeMaxLength = false;
5458 }
5459 });
5460 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
5461 }
5462
5463 retreatFrontier(doc, from.line);
5464 startWorker(cm, 400);
5465
5466 var lendiff = change.text.length - (to.line - from.line) - 1;
5467 // Remember that these lines changed, for updating the display
5468 if (change.full)
5469 { regChange(cm); }
5470 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5471 { regLineChange(cm, from.line, "text"); }
5472 else
5473 { regChange(cm, from.line, to.line + 1, lendiff); }
5474
5475 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
5476 if (changeHandler || changesHandler) {
5477 var obj = {
5478 from: from, to: to,
5479 text: change.text,
5480 removed: change.removed,
5481 origin: change.origin
5482 };
5483 if (changeHandler) { signalLater(cm, "change", cm, obj); }
5484 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
5485 }
5486 cm.display.selForContextMenu = null;
5487 }
5488
5489 function replaceRange(doc, code, from, to, origin) {
5490 var assign;
5491
5492 if (!to) { to = from; }
5493 if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
5494 if (typeof code == "string") { code = doc.splitLines(code); }
5495 makeChange(doc, {from: from, to: to, text: code, origin: origin});
8055 5496 }
8056 5497
8057 5498 // Rebasing/resetting history to deal with externally-sourced changes
@@ -8081,16 +5522,16 b''
8081 5522 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
8082 5523 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
8083 5524 }
8084 continue;
8085 }
8086 for (var j = 0; j < sub.changes.length; ++j) {
8087 var cur = sub.changes[j];
5525 continue
5526 }
5527 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5528 var cur = sub.changes[j$1];
8088 5529 if (to < cur.from.line) {
8089 5530 cur.from = Pos(cur.from.line + diff, cur.from.ch);
8090 5531 cur.to = Pos(cur.to.line + diff, cur.to.ch);
8091 5532 } else if (from <= cur.to.line) {
8092 5533 ok = false;
8093 break;
5534 break
8094 5535 }
8095 5536 }
8096 5537 if (!ok) {
@@ -8106,785 +5547,4237 b''
8106 5547 rebaseHistArray(hist.undone, from, to, diff);
8107 5548 }
8108 5549
8109 // EVENT UTILITIES
8110
8111 // Due to the fact that we still support jurassic IE versions, some
8112 // compatibility wrappers are needed.
8113
8114 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
8115 if (e.preventDefault) e.preventDefault();
8116 else e.returnValue = false;
8117 };
8118 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
8119 if (e.stopPropagation) e.stopPropagation();
8120 else e.cancelBubble = true;
8121 };
8122 function e_defaultPrevented(e) {
8123 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
8124 }
8125 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
8126
8127 function e_target(e) {return e.target || e.srcElement;}
8128 function e_button(e) {
8129 var b = e.which;
8130 if (b == null) {
8131 if (e.button & 1) b = 1;
8132 else if (e.button & 2) b = 3;
8133 else if (e.button & 4) b = 2;
8134 }
8135 if (mac && e.ctrlKey && b == 1) b = 3;
8136 return b;
8137 }
8138
8139 // EVENT HANDLING
8140
8141 // Lightweight event framework. on/off also work on DOM nodes,
8142 // registering native DOM handlers.
8143
8144 var on = CodeMirror.on = function(emitter, type, f) {
8145 if (emitter.addEventListener)
8146 emitter.addEventListener(type, f, false);
8147 else if (emitter.attachEvent)
8148 emitter.attachEvent("on" + type, f);
8149 else {
8150 var map = emitter._handlers || (emitter._handlers = {});
8151 var arr = map[type] || (map[type] = []);
8152 arr.push(f);
8153 }
8154 };
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
8163 var off = CodeMirror.off = function(emitter, type, f) {
8164 if (emitter.removeEventListener)
8165 emitter.removeEventListener(type, f, false);
8166 else if (emitter.detachEvent)
8167 emitter.detachEvent("on" + type, f);
8168 else {
8169 var handlers = getHandlers(emitter, type, false)
8170 for (var i = 0; i < handlers.length; ++i)
8171 if (handlers[i] == f) { handlers.splice(i, 1); break; }
8172 }
8173 };
8174
8175 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8176 var handlers = getHandlers(emitter, type, true)
8177 if (!handlers.length) return;
8178 var args = Array.prototype.slice.call(arguments, 2);
8179 for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
8180 };
8181
8182 var orphanDelayedCallbacks = null;
8183
8184 // Often, we want to signal events at a point where we are in the
8185 // middle of some work, but don't want the handler to start calling
8186 // other methods on the editor, which might be in an inconsistent
8187 // state or simply not expect any other events to happen.
8188 // signalLater looks whether there are any handlers, and schedules
8189 // them to be executed when the last operation ends, or, if no
8190 // operation is active, when a timeout fires.
8191 function signalLater(emitter, type /*, values...*/) {
8192 var arr = getHandlers(emitter, type, false)
8193 if (!arr.length) return;
8194 var args = Array.prototype.slice.call(arguments, 2), list;
8195 if (operationGroup) {
8196 list = operationGroup.delayedCallbacks;
8197 } else if (orphanDelayedCallbacks) {
8198 list = orphanDelayedCallbacks;
8199 } else {
8200 list = orphanDelayedCallbacks = [];
8201 setTimeout(fireOrphanDelayed, 0);
8202 }
8203 function bnd(f) {return function(){f.apply(null, args);};};
8204 for (var i = 0; i < arr.length; ++i)
8205 list.push(bnd(arr[i]));
8206 }
8207
8208 function fireOrphanDelayed() {
8209 var delayed = orphanDelayedCallbacks;
8210 orphanDelayedCallbacks = null;
8211 for (var i = 0; i < delayed.length; ++i) delayed[i]();
8212 }
8213
8214 // The DOM events that CodeMirror handles can be overridden by
8215 // registering a (non-DOM) handler on the editor for the event name,
8216 // and preventDefault-ing the event in that handler.
8217 function signalDOMEvent(cm, e, override) {
8218 if (typeof e == "string")
8219 e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8220 signal(cm, override || e.type, cm, e);
8221 return e_defaultPrevented(e) || e.codemirrorIgnore;
8222 }
8223
8224 function signalCursorActivity(cm) {
8225 var arr = cm._handlers && cm._handlers.cursorActivity;
8226 if (!arr) return;
8227 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8228 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8229 set.push(arr[i]);
8230 }
8231
8232 function hasHandler(emitter, type) {
8233 return getHandlers(emitter, type).length > 0
8234 }
8235
8236 // Add on and off methods to a constructor's prototype, to make
8237 // registering events on such objects more convenient.
8238 function eventMixin(ctor) {
8239 ctor.prototype.on = function(type, f) {on(this, type, f);};
8240 ctor.prototype.off = function(type, f) {off(this, type, f);};
8241 }
8242
8243 // MISC UTILITIES
8244
8245 // Number of pixels added to scroller and sizer to hide scrollbar
8246 var scrollerGap = 30;
8247
8248 // Returned or thrown by various protocols to signal 'I'm not
8249 // handling this'.
8250 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8251
8252 // Reused option objects for setSelection & friends
8253 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8254
8255 function Delayed() {this.id = null;}
8256 Delayed.prototype.set = function(ms, f) {
8257 clearTimeout(this.id);
8258 this.id = setTimeout(f, ms);
8259 };
8260
8261 // Counts the column offset in a string, taking tabs into account.
8262 // Used mostly to find indentation.
8263 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8264 if (end == null) {
8265 end = string.search(/[^\s\u00a0]/);
8266 if (end == -1) end = string.length;
8267 }
8268 for (var i = startIndex || 0, n = startValue || 0;;) {
8269 var nextTab = string.indexOf("\t", i);
8270 if (nextTab < 0 || nextTab >= end)
8271 return n + (end - i);
8272 n += nextTab - i;
8273 n += tabSize - (n % tabSize);
8274 i = nextTab + 1;
8275 }
8276 };
8277
8278 // The inverse of countColumn -- find the offset that corresponds to
8279 // a particular column.
8280 var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
8281 for (var pos = 0, col = 0;;) {
8282 var nextTab = string.indexOf("\t", pos);
8283 if (nextTab == -1) nextTab = string.length;
8284 var skipped = nextTab - pos;
8285 if (nextTab == string.length || col + skipped >= goal)
8286 return pos + Math.min(skipped, goal - col);
8287 col += nextTab - pos;
8288 col += tabSize - (col % tabSize);
8289 pos = nextTab + 1;
8290 if (col >= goal) return pos;
8291 }
8292 }
8293
8294 var spaceStrs = [""];
8295 function spaceStr(n) {
8296 while (spaceStrs.length <= n)
8297 spaceStrs.push(lst(spaceStrs) + " ");
8298 return spaceStrs[n];
8299 }
8300
8301 function lst(arr) { return arr[arr.length-1]; }
8302
8303 var selectInput = function(node) { node.select(); };
8304 if (ios) // Mobile Safari apparently has a bug where select() is broken.
8305 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8306 else if (ie) // Suppress mysterious IE10 errors
8307 selectInput = function(node) { try { node.select(); } catch(_e) {} };
8308
8309 function indexOf(array, elt) {
8310 for (var i = 0; i < array.length; ++i)
8311 if (array[i] == elt) return i;
8312 return -1;
8313 }
8314 function map(array, f) {
8315 var out = [];
8316 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8317 return out;
8318 }
8319
8320 function nothing() {}
8321
8322 function createObj(base, props) {
8323 var inst;
8324 if (Object.create) {
8325 inst = Object.create(base);
8326 } else {
8327 nothing.prototype = base;
8328 inst = new nothing();
8329 }
8330 if (props) copyObj(props, inst);
8331 return inst;
8332 };
8333
8334 function copyObj(obj, target, overwrite) {
8335 if (!target) target = {};
8336 for (var prop in obj)
8337 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8338 target[prop] = obj[prop];
8339 return target;
8340 }
8341
8342 function bind(f) {
8343 var args = Array.prototype.slice.call(arguments, 1);
8344 return function(){return f.apply(null, args);};
8345 }
8346
8347 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8348 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8349 return /\w/.test(ch) || ch > "\x80" &&
8350 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8351 };
8352 function isWordChar(ch, helper) {
8353 if (!helper) return isWordCharBasic(ch);
8354 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8355 return helper.test(ch);
8356 }
8357
8358 function isEmpty(obj) {
8359 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8360 return true;
8361 }
8362
8363 // Extending unicode characters. A series of a non-extending char +
8364 // any number of extending chars is treated as a single unit as far
8365 // as editing and measuring is concerned. This is not fully correct,
8366 // since some scripts/fonts/browsers also treat other configurations
8367 // of code points as a group.
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]/;
8369 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8370
8371 // DOM UTILITIES
8372
8373 function elt(tag, content, className, style) {
8374 var e = document.createElement(tag);
8375 if (className) e.className = className;
8376 if (style) e.style.cssText = style;
8377 if (typeof content == "string") e.appendChild(document.createTextNode(content));
8378 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8379 return e;
8380 }
8381
8382 var range;
8383 if (document.createRange) range = function(node, start, end, endNode) {
8384 var r = document.createRange();
8385 r.setEnd(endNode || node, end);
8386 r.setStart(node, start);
8387 return r;
8388 };
8389 else range = function(node, start, end) {
8390 var r = document.body.createTextRange();
8391 try { r.moveToElementText(node.parentNode); }
8392 catch(e) { return r; }
8393 r.collapse(true);
8394 r.moveEnd("character", end);
8395 r.moveStart("character", start);
8396 return r;
8397 };
8398
8399 function removeChildren(e) {
8400 for (var count = e.childNodes.length; count > 0; --count)
8401 e.removeChild(e.firstChild);
8402 return e;
8403 }
8404
8405 function removeChildrenAndAdd(parent, e) {
8406 return removeChildren(parent).appendChild(e);
8407 }
8408
8409 var contains = CodeMirror.contains = function(parent, child) {
8410 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8411 child = child.parentNode;
8412 if (parent.contains)
8413 return parent.contains(child);
8414 do {
8415 if (child.nodeType == 11) child = child.host;
8416 if (child == parent) return true;
8417 } while (child = child.parentNode);
8418 };
8419
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 }
8426 // Older versions of IE throws unspecified error when touching
8427 // document.activeElement in some cases (during loading, in iframe)
8428 if (ie && ie_version < 11) activeElt = function() {
8429 try { return document.activeElement; }
8430 catch(e) { return document.body; }
8431 };
8432
8433 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8434 var rmClass = CodeMirror.rmClass = function(node, cls) {
8435 var current = node.className;
8436 var match = classTest(cls).exec(current);
8437 if (match) {
8438 var after = current.slice(match.index + match[0].length);
8439 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8440 }
8441 };
8442 var addClass = CodeMirror.addClass = function(node, cls) {
8443 var current = node.className;
8444 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8445 };
8446 function joinClasses(a, b) {
8447 var as = a.split(" ");
8448 for (var i = 0; i < as.length; i++)
8449 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8450 return b;
8451 }
8452
8453 // WINDOW-WIDE EVENTS
5550 // Utility for applying a change to a line by handle or number,
5551 // returning the number and optionally registering the line as
5552 // changed.
5553 function changeLine(doc, handle, changeType, op) {
5554 var no = handle, line = handle;
5555 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
5556 else { no = lineNo(handle); }
5557 if (no == null) { return null }
5558 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
5559 return line
5560 }
5561
5562 // The document is represented as a BTree consisting of leaves, with
5563 // chunk of lines in them, and branches, with up to ten leaves or
5564 // other branch nodes below them. The top node is always a branch
5565 // node, and is the document object itself (meaning it has
5566 // additional methods and properties).
5567 //
5568 // All nodes have parent links. The tree is used both to go from
5569 // line numbers to line objects, and to go from objects to numbers.
5570 // It also indexes by height, and is used to convert between height
5571 // and line object, and to find the total height of the document.
5572 //
5573 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5574
5575 function LeafChunk(lines) {
5576 var this$1 = this;
5577
5578 this.lines = lines;
5579 this.parent = null;
5580 var height = 0;
5581 for (var i = 0; i < lines.length; ++i) {
5582 lines[i].parent = this$1;
5583 height += lines[i].height;
5584 }
5585 this.height = height;
5586 }
5587
5588 LeafChunk.prototype = {
5589 chunkSize: function() { return this.lines.length },
5590
5591 // Remove the n lines at offset 'at'.
5592 removeInner: function(at, n) {
5593 var this$1 = this;
5594
5595 for (var i = at, e = at + n; i < e; ++i) {
5596 var line = this$1.lines[i];
5597 this$1.height -= line.height;
5598 cleanUpLine(line);
5599 signalLater(line, "delete");
5600 }
5601 this.lines.splice(at, n);
5602 },
5603
5604 // Helper used to collapse a small branch into a single leaf.
5605 collapse: function(lines) {
5606 lines.push.apply(lines, this.lines);
5607 },
5608
5609 // Insert the given array of lines at offset 'at', count them as
5610 // having the given height.
5611 insertInner: function(at, lines, height) {
5612 var this$1 = this;
5613
5614 this.height += height;
5615 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
5616 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
5617 },
5618
5619 // Used to iterate over a part of the tree.
5620 iterN: function(at, n, op) {
5621 var this$1 = this;
5622
5623 for (var e = at + n; at < e; ++at)
5624 { if (op(this$1.lines[at])) { return true } }
5625 }
5626 };
5627
5628 function BranchChunk(children) {
5629 var this$1 = this;
5630
5631 this.children = children;
5632 var size = 0, height = 0;
5633 for (var i = 0; i < children.length; ++i) {
5634 var ch = children[i];
5635 size += ch.chunkSize(); height += ch.height;
5636 ch.parent = this$1;
5637 }
5638 this.size = size;
5639 this.height = height;
5640 this.parent = null;
5641 }
5642
5643 BranchChunk.prototype = {
5644 chunkSize: function() { return this.size },
5645
5646 removeInner: function(at, n) {
5647 var this$1 = this;
5648
5649 this.size -= n;
5650 for (var i = 0; i < this.children.length; ++i) {
5651 var child = this$1.children[i], sz = child.chunkSize();
5652 if (at < sz) {
5653 var rm = Math.min(n, sz - at), oldHeight = child.height;
5654 child.removeInner(at, rm);
5655 this$1.height -= oldHeight - child.height;
5656 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
5657 if ((n -= rm) == 0) { break }
5658 at = 0;
5659 } else { at -= sz; }
5660 }
5661 // If the result is smaller than 25 lines, ensure that it is a
5662 // single leaf node.
5663 if (this.size - n < 25 &&
5664 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5665 var lines = [];
5666 this.collapse(lines);
5667 this.children = [new LeafChunk(lines)];
5668 this.children[0].parent = this;
5669 }
5670 },
5671
5672 collapse: function(lines) {
5673 var this$1 = this;
5674
5675 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
5676 },
5677
5678 insertInner: function(at, lines, height) {
5679 var this$1 = this;
5680
5681 this.size += lines.length;
5682 this.height += height;
5683 for (var i = 0; i < this.children.length; ++i) {
5684 var child = this$1.children[i], sz = child.chunkSize();
5685 if (at <= sz) {
5686 child.insertInner(at, lines, height);
5687 if (child.lines && child.lines.length > 50) {
5688 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5689 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5690 var remaining = child.lines.length % 25 + 25;
5691 for (var pos = remaining; pos < child.lines.length;) {
5692 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
5693 child.height -= leaf.height;
5694 this$1.children.splice(++i, 0, leaf);
5695 leaf.parent = this$1;
5696 }
5697 child.lines = child.lines.slice(0, remaining);
5698 this$1.maybeSpill();
5699 }
5700 break
5701 }
5702 at -= sz;
5703 }
5704 },
5705
5706 // When a node has grown, check whether it should be split.
5707 maybeSpill: function() {
5708 if (this.children.length <= 10) { return }
5709 var me = this;
5710 do {
5711 var spilled = me.children.splice(me.children.length - 5, 5);
5712 var sibling = new BranchChunk(spilled);
5713 if (!me.parent) { // Become the parent node
5714 var copy = new BranchChunk(me.children);
5715 copy.parent = me;
5716 me.children = [copy, sibling];
5717 me = copy;
5718 } else {
5719 me.size -= sibling.size;
5720 me.height -= sibling.height;
5721 var myIndex = indexOf(me.parent.children, me);
5722 me.parent.children.splice(myIndex + 1, 0, sibling);
5723 }
5724 sibling.parent = me.parent;
5725 } while (me.children.length > 10)
5726 me.parent.maybeSpill();
5727 },
5728
5729 iterN: function(at, n, op) {
5730 var this$1 = this;
5731
5732 for (var i = 0; i < this.children.length; ++i) {
5733 var child = this$1.children[i], sz = child.chunkSize();
5734 if (at < sz) {
5735 var used = Math.min(n, sz - at);
5736 if (child.iterN(at, used, op)) { return true }
5737 if ((n -= used) == 0) { break }
5738 at = 0;
5739 } else { at -= sz; }
5740 }
5741 }
5742 };
5743
5744 // Line widgets are block elements displayed above or below a line.
5745
5746 var LineWidget = function(doc, node, options) {
5747 var this$1 = this;
5748
5749 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5750 { this$1[opt] = options[opt]; } } }
5751 this.doc = doc;
5752 this.node = node;
5753 };
5754
5755 LineWidget.prototype.clear = function () {
5756 var this$1 = this;
5757
5758 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
5759 if (no == null || !ws) { return }
5760 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
5761 if (!ws.length) { line.widgets = null; }
5762 var height = widgetHeight(this);
5763 updateLineHeight(line, Math.max(0, line.height - height));
5764 if (cm) {
5765 runInOp(cm, function () {
5766 adjustScrollWhenAboveVisible(cm, line, -height);
5767 regLineChange(cm, no, "widget");
5768 });
5769 signalLater(cm, "lineWidgetCleared", cm, this, no);
5770 }
5771 };
5772
5773 LineWidget.prototype.changed = function () {
5774 var this$1 = this;
5775
5776 var oldH = this.height, cm = this.doc.cm, line = this.line;
5777 this.height = null;
5778 var diff = widgetHeight(this) - oldH;
5779 if (!diff) { return }
5780 if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
5781 if (cm) {
5782 runInOp(cm, function () {
5783 cm.curOp.forceUpdate = true;
5784 adjustScrollWhenAboveVisible(cm, line, diff);
5785 signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
5786 });
5787 }
5788 };
5789 eventMixin(LineWidget);
5790
5791 function adjustScrollWhenAboveVisible(cm, line, diff) {
5792 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5793 { addToScrollTop(cm, diff); }
5794 }
5795
5796 function addLineWidget(doc, handle, node, options) {
5797 var widget = new LineWidget(doc, node, options);
5798 var cm = doc.cm;
5799 if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
5800 changeLine(doc, handle, "widget", function (line) {
5801 var widgets = line.widgets || (line.widgets = []);
5802 if (widget.insertAt == null) { widgets.push(widget); }
5803 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
5804 widget.line = line;
5805 if (cm && !lineIsHidden(doc, line)) {
5806 var aboveVisible = heightAtLine(line) < doc.scrollTop;
5807 updateLineHeight(line, line.height + widgetHeight(widget));
5808 if (aboveVisible) { addToScrollTop(cm, widget.height); }
5809 cm.curOp.forceUpdate = true;
5810 }
5811 return true
5812 });
5813 if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
5814 return widget
5815 }
5816
5817 // TEXTMARKERS
5818
5819 // Created with markText and setBookmark methods. A TextMarker is a
5820 // handle that can be used to clear or find a marked position in the
5821 // document. Line objects hold arrays (markedSpans) containing
5822 // {from, to, marker} object pointing to such marker objects, and
5823 // indicating that such a marker is present on that line. Multiple
5824 // lines may point to the same marker when it spans across lines.
5825 // The spans will have null for their from/to properties when the
5826 // marker continues beyond the start/end of the line. Markers have
5827 // links back to the lines they currently touch.
5828
5829 // Collapsed markers have unique ids, in order to be able to order
5830 // them, which is needed for uniquely determining an outer marker
5831 // when they overlap (they may nest, but not partially overlap).
5832 var nextMarkerId = 0;
5833
5834 var TextMarker = function(doc, type) {
5835 this.lines = [];
5836 this.type = type;
5837 this.doc = doc;
5838 this.id = ++nextMarkerId;
5839 };
5840
5841 // Clear the marker.
5842 TextMarker.prototype.clear = function () {
5843 var this$1 = this;
5844
5845 if (this.explicitlyCleared) { return }
5846 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5847 if (withOp) { startOperation(cm); }
5848 if (hasHandler(this, "clear")) {
5849 var found = this.find();
5850 if (found) { signalLater(this, "clear", found.from, found.to); }
5851 }
5852 var min = null, max = null;
5853 for (var i = 0; i < this.lines.length; ++i) {
5854 var line = this$1.lines[i];
5855 var span = getMarkedSpanFor(line.markedSpans, this$1);
5856 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
5857 else if (cm) {
5858 if (span.to != null) { max = lineNo(line); }
5859 if (span.from != null) { min = lineNo(line); }
5860 }
5861 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5862 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5863 { updateLineHeight(line, textHeight(cm.display)); }
5864 }
5865 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5866 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
5867 if (len > cm.display.maxLineLength) {
5868 cm.display.maxLine = visual;
5869 cm.display.maxLineLength = len;
5870 cm.display.maxLineChanged = true;
5871 }
5872 } }
5873
5874 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
5875 this.lines.length = 0;
5876 this.explicitlyCleared = true;
5877 if (this.atomic && this.doc.cantEdit) {
5878 this.doc.cantEdit = false;
5879 if (cm) { reCheckSelection(cm.doc); }
5880 }
5881 if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
5882 if (withOp) { endOperation(cm); }
5883 if (this.parent) { this.parent.clear(); }
5884 };
5885
5886 // Find the position of the marker in the document. Returns a {from,
5887 // to} object by default. Side can be passed to get a specific side
5888 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5889 // Pos objects returned contain a line object, rather than a line
5890 // number (used to prevent looking up the same line twice).
5891 TextMarker.prototype.find = function (side, lineObj) {
5892 var this$1 = this;
5893
5894 if (side == null && this.type == "bookmark") { side = 1; }
5895 var from, to;
5896 for (var i = 0; i < this.lines.length; ++i) {
5897 var line = this$1.lines[i];
5898 var span = getMarkedSpanFor(line.markedSpans, this$1);
5899 if (span.from != null) {
5900 from = Pos(lineObj ? line : lineNo(line), span.from);
5901 if (side == -1) { return from }
5902 }
5903 if (span.to != null) {
5904 to = Pos(lineObj ? line : lineNo(line), span.to);
5905 if (side == 1) { return to }
5906 }
5907 }
5908 return from && {from: from, to: to}
5909 };
5910
5911 // Signals that the marker's widget changed, and surrounding layout
5912 // should be recomputed.
5913 TextMarker.prototype.changed = function () {
5914 var this$1 = this;
5915
5916 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
5917 if (!pos || !cm) { return }
5918 runInOp(cm, function () {
5919 var line = pos.line, lineN = lineNo(pos.line);
5920 var view = findViewForLine(cm, lineN);
5921 if (view) {
5922 clearLineMeasurementCacheFor(view);
5923 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
5924 }
5925 cm.curOp.updateMaxLine = true;
5926 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5927 var oldHeight = widget.height;
5928 widget.height = null;
5929 var dHeight = widgetHeight(widget) - oldHeight;
5930 if (dHeight)
5931 { updateLineHeight(line, line.height + dHeight); }
5932 }
5933 signalLater(cm, "markerChanged", cm, this$1);
5934 });
5935 };
5936
5937 TextMarker.prototype.attachLine = function (line) {
5938 if (!this.lines.length && this.doc.cm) {
5939 var op = this.doc.cm.curOp;
5940 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5941 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
5942 }
5943 this.lines.push(line);
5944 };
5945
5946 TextMarker.prototype.detachLine = function (line) {
5947 this.lines.splice(indexOf(this.lines, line), 1);
5948 if (!this.lines.length && this.doc.cm) {
5949 var op = this.doc.cm.curOp
5950 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
5951 }
5952 };
5953 eventMixin(TextMarker);
5954
5955 // Create a marker, wire it up to the right lines, and
5956 function markText(doc, from, to, options, type) {
5957 // Shared markers (across linked documents) are handled separately
5958 // (markTextShared will call out to this again, once per
5959 // document).
5960 if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5961 // Ensure we are in an operation.
5962 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5963
5964 var marker = new TextMarker(doc, type), diff = cmp(from, to);
5965 if (options) { copyObj(options, marker, false); }
5966 // Don't connect empty markers unless clearWhenEmpty is false
5967 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5968 { return marker }
5969 if (marker.replacedWith) {
5970 // Showing up as a widget implies collapsed (widget replaces text)
5971 marker.collapsed = true;
5972 marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
5973 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
5974 if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
5975 }
5976 if (marker.collapsed) {
5977 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5978 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5979 { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5980 seeCollapsedSpans();
5981 }
5982
5983 if (marker.addToHistory)
5984 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
5985
5986 var curLine = from.line, cm = doc.cm, updateMaxLine;
5987 doc.iter(curLine, to.line + 1, function (line) {
5988 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5989 { updateMaxLine = true; }
5990 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
5991 addMarkedSpan(line, new MarkedSpan(marker,
5992 curLine == from.line ? from.ch : null,
5993 curLine == to.line ? to.ch : null));
5994 ++curLine;
5995 });
5996 // lineIsHidden depends on the presence of the spans, so needs a second pass
5997 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5998 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
5999 }); }
6000
6001 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
6002
6003 if (marker.readOnly) {
6004 seeReadOnlySpans();
6005 if (doc.history.done.length || doc.history.undone.length)
6006 { doc.clearHistory(); }
6007 }
6008 if (marker.collapsed) {
6009 marker.id = ++nextMarkerId;
6010 marker.atomic = true;
6011 }
6012 if (cm) {
6013 // Sync editor state
6014 if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
6015 if (marker.collapsed)
6016 { regChange(cm, from.line, to.line + 1); }
6017 else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
6018 marker.attributes || marker.title)
6019 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
6020 if (marker.atomic) { reCheckSelection(cm.doc); }
6021 signalLater(cm, "markerAdded", cm, marker);
6022 }
6023 return marker
6024 }
6025
6026 // SHARED TEXTMARKERS
6027
6028 // A shared marker spans multiple linked documents. It is
6029 // implemented as a meta-marker-object controlling multiple normal
6030 // markers.
6031 var SharedTextMarker = function(markers, primary) {
6032 var this$1 = this;
6033
6034 this.markers = markers;
6035 this.primary = primary;
6036 for (var i = 0; i < markers.length; ++i)
6037 { markers[i].parent = this$1; }
6038 };
6039
6040 SharedTextMarker.prototype.clear = function () {
6041 var this$1 = this;
6042
6043 if (this.explicitlyCleared) { return }
6044 this.explicitlyCleared = true;
6045 for (var i = 0; i < this.markers.length; ++i)
6046 { this$1.markers[i].clear(); }
6047 signalLater(this, "clear");
6048 };
6049
6050 SharedTextMarker.prototype.find = function (side, lineObj) {
6051 return this.primary.find(side, lineObj)
6052 };
6053 eventMixin(SharedTextMarker);
6054
6055 function markTextShared(doc, from, to, options, type) {
6056 options = copyObj(options);
6057 options.shared = false;
6058 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6059 var widget = options.widgetNode;
6060 linkedDocs(doc, function (doc) {
6061 if (widget) { options.widgetNode = widget.cloneNode(true); }
6062 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6063 for (var i = 0; i < doc.linked.length; ++i)
6064 { if (doc.linked[i].isParent) { return } }
6065 primary = lst(markers);
6066 });
6067 return new SharedTextMarker(markers, primary)
6068 }
6069
6070 function findSharedMarkers(doc) {
6071 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
6072 }
6073
6074 function copySharedMarkers(doc, markers) {
6075 for (var i = 0; i < markers.length; i++) {
6076 var marker = markers[i], pos = marker.find();
6077 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6078 if (cmp(mFrom, mTo)) {
6079 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6080 marker.markers.push(subMark);
6081 subMark.parent = marker;
6082 }
6083 }
6084 }
6085
6086 function detachSharedMarkers(markers) {
6087 var loop = function ( i ) {
6088 var marker = markers[i], linked = [marker.primary.doc];
6089 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
6090 for (var j = 0; j < marker.markers.length; j++) {
6091 var subMarker = marker.markers[j];
6092 if (indexOf(linked, subMarker.doc) == -1) {
6093 subMarker.parent = null;
6094 marker.markers.splice(j--, 1);
6095 }
6096 }
6097 };
6098
6099 for (var i = 0; i < markers.length; i++) loop( i );
6100 }
6101
6102 var nextDocId = 0;
6103 var Doc = function(text, mode, firstLine, lineSep, direction) {
6104 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6105 if (firstLine == null) { firstLine = 0; }
6106
6107 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
6108 this.first = firstLine;
6109 this.scrollTop = this.scrollLeft = 0;
6110 this.cantEdit = false;
6111 this.cleanGeneration = 1;
6112 this.modeFrontier = this.highlightFrontier = firstLine;
6113 var start = Pos(firstLine, 0);
6114 this.sel = simpleSelection(start);
6115 this.history = new History(null);
6116 this.id = ++nextDocId;
6117 this.modeOption = mode;
6118 this.lineSep = lineSep;
6119 this.direction = (direction == "rtl") ? "rtl" : "ltr";
6120 this.extend = false;
6121
6122 if (typeof text == "string") { text = this.splitLines(text); }
6123 updateDoc(this, {from: start, to: start, text: text});
6124 setSelection(this, simpleSelection(start), sel_dontScroll);
6125 };
6126
6127 Doc.prototype = createObj(BranchChunk.prototype, {
6128 constructor: Doc,
6129 // Iterate over the document. Supports two forms -- with only one
6130 // argument, it calls that for each line in the document. With
6131 // three, it iterates over the range given by the first two (with
6132 // the second being non-inclusive).
6133 iter: function(from, to, op) {
6134 if (op) { this.iterN(from - this.first, to - from, op); }
6135 else { this.iterN(this.first, this.first + this.size, from); }
6136 },
6137
6138 // Non-public interface for adding and removing lines.
6139 insert: function(at, lines) {
6140 var height = 0;
6141 for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
6142 this.insertInner(at - this.first, lines, height);
6143 },
6144 remove: function(at, n) { this.removeInner(at - this.first, n); },
6145
6146 // From here, the methods are part of the public interface. Most
6147 // are also available from CodeMirror (editor) instances.
6148
6149 getValue: function(lineSep) {
6150 var lines = getLines(this, this.first, this.first + this.size);
6151 if (lineSep === false) { return lines }
6152 return lines.join(lineSep || this.lineSeparator())
6153 },
6154 setValue: docMethodOp(function(code) {
6155 var top = Pos(this.first, 0), last = this.first + this.size - 1;
6156 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6157 text: this.splitLines(code), origin: "setValue", full: true}, true);
6158 if (this.cm) { scrollToCoords(this.cm, 0, 0); }
6159 setSelection(this, simpleSelection(top), sel_dontScroll);
6160 }),
6161 replaceRange: function(code, from, to, origin) {
6162 from = clipPos(this, from);
6163 to = to ? clipPos(this, to) : from;
6164 replaceRange(this, code, from, to, origin);
6165 },
6166 getRange: function(from, to, lineSep) {
6167 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
6168 if (lineSep === false) { return lines }
6169 return lines.join(lineSep || this.lineSeparator())
6170 },
6171
6172 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6173
6174 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6175 getLineNumber: function(line) {return lineNo(line)},
6176
6177 getLineHandleVisualStart: function(line) {
6178 if (typeof line == "number") { line = getLine(this, line); }
6179 return visualLine(line)
6180 },
6181
6182 lineCount: function() {return this.size},
6183 firstLine: function() {return this.first},
6184 lastLine: function() {return this.first + this.size - 1},
6185
6186 clipPos: function(pos) {return clipPos(this, pos)},
6187
6188 getCursor: function(start) {
6189 var range$$1 = this.sel.primary(), pos;
6190 if (start == null || start == "head") { pos = range$$1.head; }
6191 else if (start == "anchor") { pos = range$$1.anchor; }
6192 else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
6193 else { pos = range$$1.from(); }
6194 return pos
6195 },
6196 listSelections: function() { return this.sel.ranges },
6197 somethingSelected: function() {return this.sel.somethingSelected()},
6198
6199 setCursor: docMethodOp(function(line, ch, options) {
6200 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
6201 }),
6202 setSelection: docMethodOp(function(anchor, head, options) {
6203 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
6204 }),
6205 extendSelection: docMethodOp(function(head, other, options) {
6206 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6207 }),
6208 extendSelections: docMethodOp(function(heads, options) {
6209 extendSelections(this, clipPosArray(this, heads), options);
6210 }),
6211 extendSelectionsBy: docMethodOp(function(f, options) {
6212 var heads = map(this.sel.ranges, f);
6213 extendSelections(this, clipPosArray(this, heads), options);
6214 }),
6215 setSelections: docMethodOp(function(ranges, primary, options) {
6216 var this$1 = this;
6217
6218 if (!ranges.length) { return }
6219 var out = [];
6220 for (var i = 0; i < ranges.length; i++)
6221 { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6222 clipPos(this$1, ranges[i].head)); }
6223 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
6224 setSelection(this, normalizeSelection(this.cm, out, primary), options);
6225 }),
6226 addSelection: docMethodOp(function(anchor, head, options) {
6227 var ranges = this.sel.ranges.slice(0);
6228 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
6229 setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
6230 }),
6231
6232 getSelection: function(lineSep) {
6233 var this$1 = this;
6234
6235 var ranges = this.sel.ranges, lines;
6236 for (var i = 0; i < ranges.length; i++) {
6237 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6238 lines = lines ? lines.concat(sel) : sel;
6239 }
6240 if (lineSep === false) { return lines }
6241 else { return lines.join(lineSep || this.lineSeparator()) }
6242 },
6243 getSelections: function(lineSep) {
6244 var this$1 = this;
6245
6246 var parts = [], ranges = this.sel.ranges;
6247 for (var i = 0; i < ranges.length; i++) {
6248 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6249 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
6250 parts[i] = sel;
6251 }
6252 return parts
6253 },
6254 replaceSelection: function(code, collapse, origin) {
6255 var dup = [];
6256 for (var i = 0; i < this.sel.ranges.length; i++)
6257 { dup[i] = code; }
6258 this.replaceSelections(dup, collapse, origin || "+input");
6259 },
6260 replaceSelections: docMethodOp(function(code, collapse, origin) {
6261 var this$1 = this;
6262
6263 var changes = [], sel = this.sel;
6264 for (var i = 0; i < sel.ranges.length; i++) {
6265 var range$$1 = sel.ranges[i];
6266 changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
6267 }
6268 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
6269 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6270 { makeChange(this$1, changes[i$1]); }
6271 if (newSel) { setSelectionReplaceHistory(this, newSel); }
6272 else if (this.cm) { ensureCursorVisible(this.cm); }
6273 }),
6274 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
6275 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
6276 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
6277 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
6278
6279 setExtending: function(val) {this.extend = val;},
6280 getExtending: function() {return this.extend},
6281
6282 historySize: function() {
6283 var hist = this.history, done = 0, undone = 0;
6284 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
6285 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
6286 return {undo: done, redo: undone}
6287 },
6288 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
6289
6290 markClean: function() {
6291 this.cleanGeneration = this.changeGeneration(true);
6292 },
6293 changeGeneration: function(forceSplit) {
6294 if (forceSplit)
6295 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
6296 return this.history.generation
6297 },
6298 isClean: function (gen) {
6299 return this.history.generation == (gen || this.cleanGeneration)
6300 },
6301
6302 getHistory: function() {
6303 return {done: copyHistoryArray(this.history.done),
6304 undone: copyHistoryArray(this.history.undone)}
6305 },
6306 setHistory: function(histData) {
6307 var hist = this.history = new History(this.history.maxGeneration);
6308 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
6309 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
6310 },
6311
6312 setGutterMarker: docMethodOp(function(line, gutterID, value) {
6313 return changeLine(this, line, "gutter", function (line) {
6314 var markers = line.gutterMarkers || (line.gutterMarkers = {});
6315 markers[gutterID] = value;
6316 if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
6317 return true
6318 })
6319 }),
6320
6321 clearGutter: docMethodOp(function(gutterID) {
6322 var this$1 = this;
6323
6324 this.iter(function (line) {
6325 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6326 changeLine(this$1, line, "gutter", function () {
6327 line.gutterMarkers[gutterID] = null;
6328 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
6329 return true
6330 });
6331 }
6332 });
6333 }),
6334
6335 lineInfo: function(line) {
6336 var n;
6337 if (typeof line == "number") {
6338 if (!isLine(this, line)) { return null }
6339 n = line;
6340 line = getLine(this, line);
6341 if (!line) { return null }
6342 } else {
6343 n = lineNo(line);
6344 if (n == null) { return null }
6345 }
6346 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6347 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6348 widgets: line.widgets}
6349 },
6350
6351 addLineClass: docMethodOp(function(handle, where, cls) {
6352 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6353 var prop = where == "text" ? "textClass"
6354 : where == "background" ? "bgClass"
6355 : where == "gutter" ? "gutterClass" : "wrapClass";
6356 if (!line[prop]) { line[prop] = cls; }
6357 else if (classTest(cls).test(line[prop])) { return false }
6358 else { line[prop] += " " + cls; }
6359 return true
6360 })
6361 }),
6362 removeLineClass: docMethodOp(function(handle, where, cls) {
6363 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6364 var prop = where == "text" ? "textClass"
6365 : where == "background" ? "bgClass"
6366 : where == "gutter" ? "gutterClass" : "wrapClass";
6367 var cur = line[prop];
6368 if (!cur) { return false }
6369 else if (cls == null) { line[prop] = null; }
6370 else {
6371 var found = cur.match(classTest(cls));
6372 if (!found) { return false }
6373 var end = found.index + found[0].length;
6374 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
6375 }
6376 return true
6377 })
6378 }),
6379
6380 addLineWidget: docMethodOp(function(handle, node, options) {
6381 return addLineWidget(this, handle, node, options)
6382 }),
6383 removeLineWidget: function(widget) { widget.clear(); },
6384
6385 markText: function(from, to, options) {
6386 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6387 },
6388 setBookmark: function(pos, options) {
6389 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6390 insertLeft: options && options.insertLeft,
6391 clearWhenEmpty: false, shared: options && options.shared,
6392 handleMouseEvents: options && options.handleMouseEvents};
6393 pos = clipPos(this, pos);
6394 return markText(this, pos, pos, realOpts, "bookmark")
6395 },
6396 findMarksAt: function(pos) {
6397 pos = clipPos(this, pos);
6398 var markers = [], spans = getLine(this, pos.line).markedSpans;
6399 if (spans) { for (var i = 0; i < spans.length; ++i) {
6400 var span = spans[i];
6401 if ((span.from == null || span.from <= pos.ch) &&
6402 (span.to == null || span.to >= pos.ch))
6403 { markers.push(span.marker.parent || span.marker); }
6404 } }
6405 return markers
6406 },
6407 findMarks: function(from, to, filter) {
6408 from = clipPos(this, from); to = clipPos(this, to);
6409 var found = [], lineNo$$1 = from.line;
6410 this.iter(from.line, to.line + 1, function (line) {
6411 var spans = line.markedSpans;
6412 if (spans) { for (var i = 0; i < spans.length; i++) {
6413 var span = spans[i];
6414 if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6415 span.from == null && lineNo$$1 != from.line ||
6416 span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6417 (!filter || filter(span.marker)))
6418 { found.push(span.marker.parent || span.marker); }
6419 } }
6420 ++lineNo$$1;
6421 });
6422 return found
6423 },
6424 getAllMarks: function() {
6425 var markers = [];
6426 this.iter(function (line) {
6427 var sps = line.markedSpans;
6428 if (sps) { for (var i = 0; i < sps.length; ++i)
6429 { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
6430 });
6431 return markers
6432 },
6433
6434 posFromIndex: function(off) {
6435 var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
6436 this.iter(function (line) {
6437 var sz = line.text.length + sepSize;
6438 if (sz > off) { ch = off; return true }
6439 off -= sz;
6440 ++lineNo$$1;
6441 });
6442 return clipPos(this, Pos(lineNo$$1, ch))
6443 },
6444 indexFromPos: function (coords) {
6445 coords = clipPos(this, coords);
6446 var index = coords.ch;
6447 if (coords.line < this.first || coords.ch < 0) { return 0 }
6448 var sepSize = this.lineSeparator().length;
6449 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6450 index += line.text.length + sepSize;
6451 });
6452 return index
6453 },
6454
6455 copy: function(copyHistory) {
6456 var doc = new Doc(getLines(this, this.first, this.first + this.size),
6457 this.modeOption, this.first, this.lineSep, this.direction);
6458 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6459 doc.sel = this.sel;
6460 doc.extend = false;
6461 if (copyHistory) {
6462 doc.history.undoDepth = this.history.undoDepth;
6463 doc.setHistory(this.getHistory());
6464 }
6465 return doc
6466 },
6467
6468 linkedDoc: function(options) {
6469 if (!options) { options = {}; }
6470 var from = this.first, to = this.first + this.size;
6471 if (options.from != null && options.from > from) { from = options.from; }
6472 if (options.to != null && options.to < to) { to = options.to; }
6473 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
6474 if (options.sharedHist) { copy.history = this.history
6475 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
6476 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6477 copySharedMarkers(copy, findSharedMarkers(this));
6478 return copy
6479 },
6480 unlinkDoc: function(other) {
6481 var this$1 = this;
6482
6483 if (other instanceof CodeMirror) { other = other.doc; }
6484 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6485 var link = this$1.linked[i];
6486 if (link.doc != other) { continue }
6487 this$1.linked.splice(i, 1);
6488 other.unlinkDoc(this$1);
6489 detachSharedMarkers(findSharedMarkers(this$1));
6490 break
6491 } }
6492 // If the histories were shared, split them again
6493 if (other.history == this.history) {
6494 var splitIds = [other.id];
6495 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
6496 other.history = new History(null);
6497 other.history.done = copyHistoryArray(this.history.done, splitIds);
6498 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6499 }
6500 },
6501 iterLinkedDocs: function(f) {linkedDocs(this, f);},
6502
6503 getMode: function() {return this.mode},
6504 getEditor: function() {return this.cm},
6505
6506 splitLines: function(str) {
6507 if (this.lineSep) { return str.split(this.lineSep) }
6508 return splitLinesAuto(str)
6509 },
6510 lineSeparator: function() { return this.lineSep || "\n" },
6511
6512 setDirection: docMethodOp(function (dir) {
6513 if (dir != "rtl") { dir = "ltr"; }
6514 if (dir == this.direction) { return }
6515 this.direction = dir;
6516 this.iter(function (line) { return line.order = null; });
6517 if (this.cm) { directionChanged(this.cm); }
6518 })
6519 });
6520
6521 // Public alias.
6522 Doc.prototype.eachLine = Doc.prototype.iter;
6523
6524 // Kludge to work around strange IE behavior where it'll sometimes
6525 // re-fire a series of drag-related events right after the drop (#1551)
6526 var lastDrop = 0;
6527
6528 function onDrop(e) {
6529 var cm = this;
6530 clearDragCursor(cm);
6531 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6532 { return }
6533 e_preventDefault(e);
6534 if (ie) { lastDrop = +new Date; }
6535 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
6536 if (!pos || cm.isReadOnly()) { return }
6537 // Might be a file drop, in which case we simply extract the text
6538 // and insert it.
6539 if (files && files.length && window.FileReader && window.File) {
6540 var n = files.length, text = Array(n), read = 0;
6541 var loadFile = function (file, i) {
6542 if (cm.options.allowDropFileTypes &&
6543 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6544 { return }
6545
6546 var reader = new FileReader;
6547 reader.onload = operation(cm, function () {
6548 var content = reader.result;
6549 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
6550 text[i] = content;
6551 if (++read == n) {
6552 pos = clipPos(cm.doc, pos);
6553 var change = {from: pos, to: pos,
6554 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6555 origin: "paste"};
6556 makeChange(cm.doc, change);
6557 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
6558 }
6559 });
6560 reader.readAsText(file);
6561 };
6562 for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
6563 } else { // Normal drop
6564 // Don't do a replace if the drop happened inside of the selected text.
6565 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6566 cm.state.draggingText(e);
6567 // Ensure the editor is re-focused
6568 setTimeout(function () { return cm.display.input.focus(); }, 20);
6569 return
6570 }
6571 try {
6572 var text$1 = e.dataTransfer.getData("Text");
6573 if (text$1) {
6574 var selected;
6575 if (cm.state.draggingText && !cm.state.draggingText.copy)
6576 { selected = cm.listSelections(); }
6577 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
6578 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6579 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
6580 cm.replaceSelection(text$1, "around", "paste");
6581 cm.display.input.focus();
6582 }
6583 }
6584 catch(e){}
6585 }
6586 }
6587
6588 function onDragStart(cm, e) {
6589 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6590 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6591
6592 e.dataTransfer.setData("Text", cm.getSelection());
6593 e.dataTransfer.effectAllowed = "copyMove";
6594
6595 // Use dummy image instead of default browsers image.
6596 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6597 if (e.dataTransfer.setDragImage && !safari) {
6598 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
6599 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
6600 if (presto) {
6601 img.width = img.height = 1;
6602 cm.display.wrapper.appendChild(img);
6603 // Force a relayout, or Opera won't use our image for some obscure reason
6604 img._top = img.offsetTop;
6605 }
6606 e.dataTransfer.setDragImage(img, 0, 0);
6607 if (presto) { img.parentNode.removeChild(img); }
6608 }
6609 }
6610
6611 function onDragOver(cm, e) {
6612 var pos = posFromMouse(cm, e);
6613 if (!pos) { return }
6614 var frag = document.createDocumentFragment();
6615 drawSelectionCursor(cm, pos, frag);
6616 if (!cm.display.dragCursor) {
6617 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
6618 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
6619 }
6620 removeChildrenAndAdd(cm.display.dragCursor, frag);
6621 }
6622
6623 function clearDragCursor(cm) {
6624 if (cm.display.dragCursor) {
6625 cm.display.lineSpace.removeChild(cm.display.dragCursor);
6626 cm.display.dragCursor = null;
6627 }
6628 }
8454 6629
8455 6630 // These must be handled carefully, because naively registering a
8456 6631 // handler for each editor will cause the editors to never be
8457 6632 // garbage collected.
8458 6633
8459 6634 function forEachCodeMirror(f) {
8460 if (!document.body.getElementsByClassName) return;
8461 var byClass = document.body.getElementsByClassName("CodeMirror");
6635 if (!document.getElementsByClassName) { return }
6636 var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
8462 6637 for (var i = 0; i < byClass.length; i++) {
8463 6638 var cm = byClass[i].CodeMirror;
8464 if (cm) f(cm);
8465 }
6639 if (cm) { editors.push(cm); }
6640 }
6641 if (editors.length) { editors[0].operation(function () {
6642 for (var i = 0; i < editors.length; i++) { f(editors[i]); }
6643 }); }
8466 6644 }
8467 6645
8468 6646 var globalsRegistered = false;
8469 6647 function ensureGlobalHandlers() {
8470 if (globalsRegistered) return;
6648 if (globalsRegistered) { return }
8471 6649 registerGlobalHandlers();
8472 6650 globalsRegistered = true;
8473 6651 }
8474 6652 function registerGlobalHandlers() {
8475 6653 // When the window resizes, we need to refresh active editors.
8476 6654 var resizeTimer;
8477 on(window, "resize", function() {
8478 if (resizeTimer == null) resizeTimer = setTimeout(function() {
6655 on(window, "resize", function () {
6656 if (resizeTimer == null) { resizeTimer = setTimeout(function () {
8479 6657 resizeTimer = null;
8480 6658 forEachCodeMirror(onResize);
8481 }, 100);
6659 }, 100); }
8482 6660 });
8483 6661 // When the window loses focus, we want to show the editor as blurred
8484 on(window, "blur", function() {
8485 forEachCodeMirror(onBlur);
8486 });
8487 }
8488
8489 // FEATURE DETECTION
8490
8491 // Detect drag-and-drop
8492 var dragAndDrop = function() {
8493 // There is *some* kind of drag-and-drop support in IE6-8, but I
8494 // couldn't get it to work yet.
8495 if (ie && ie_version < 9) return false;
8496 var div = elt('div');
8497 return "draggable" in div || "dragDrop" in div;
8498 }();
8499
8500 var zwspSupported;
8501 function zeroWidthElement(measure) {
8502 if (zwspSupported == null) {
8503 var test = elt("span", "\u200b");
8504 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8505 if (measure.firstChild.offsetHeight != 0)
8506 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8507 }
8508 var node = zwspSupported ? elt("span", "\u200b") :
8509 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8510 node.setAttribute("cm-text", "");
8511 return node;
8512 }
8513
8514 // Feature-detect IE's crummy client rect reporting for bidi text
8515 var badBidiRects;
8516 function hasBadBidiRects(measure) {
8517 if (badBidiRects != null) return badBidiRects;
8518 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8519 var r0 = range(txt, 0, 1).getBoundingClientRect();
8520 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8521 var r1 = range(txt, 1, 2).getBoundingClientRect();
8522 return badBidiRects = (r1.right - r0.right < 3);
8523 }
8524
8525 // See if "".split is the broken IE version, if so, provide an
8526 // alternative way to split lines.
8527 var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8528 var pos = 0, result = [], l = string.length;
8529 while (pos <= l) {
8530 var nl = string.indexOf("\n", pos);
8531 if (nl == -1) nl = string.length;
8532 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8533 var rt = line.indexOf("\r");
8534 if (rt != -1) {
8535 result.push(line.slice(0, rt));
8536 pos += rt + 1;
8537 } else {
8538 result.push(line);
8539 pos = nl + 1;
8540 }
8541 }
8542 return result;
8543 } : function(string){return string.split(/\r\n?|\n/);};
8544
8545 var hasSelection = window.getSelection ? function(te) {
8546 try { return te.selectionStart != te.selectionEnd; }
8547 catch(e) { return false; }
8548 } : function(te) {
8549 try {var range = te.ownerDocument.selection.createRange();}
8550 catch(e) {}
8551 if (!range || range.parentElement() != te) return false;
8552 return range.compareEndPoints("StartToEnd", range) != 0;
8553 };
8554
8555 var hasCopyEvent = (function() {
8556 var e = elt("div");
8557 if ("oncopy" in e) return true;
8558 e.setAttribute("oncopy", "return;");
8559 return typeof e.oncopy == "function";
8560 })();
8561
8562 var badZoomedRects = null;
8563 function hasBadZoomedRects(measure) {
8564 if (badZoomedRects != null) return badZoomedRects;
8565 var node = removeChildrenAndAdd(measure, elt("span", "x"));
8566 var normal = node.getBoundingClientRect();
8567 var fromRange = range(node, 0, 1).getBoundingClientRect();
8568 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8569 }
8570
8571 // KEY NAMES
8572
8573 var keyNames = CodeMirror.keyNames = {
8574 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6662 on(window, "blur", function () { return forEachCodeMirror(onBlur); });
6663 }
6664 // Called when the window resizes
6665 function onResize(cm) {
6666 var d = cm.display;
6667 // Might be a text scaling operation, clear size caches.
6668 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
6669 d.scrollbarsClipped = false;
6670 cm.setSize();
6671 }
6672
6673 var keyNames = {
6674 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8575 6675 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8576 6676 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8577 6677 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8578 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6678 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
8579 6679 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8580 6680 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8581 6681 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8582 6682 };
8583 (function() {
8584 // Number keys
8585 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8586 // Alphabetic keys
8587 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8588 // Function keys
8589 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8590 })();
8591
8592 // BIDI HELPERS
8593
8594 function iterateBidiSections(order, from, to, f) {
8595 if (!order) return f(from, to, "ltr");
8596 var found = false;
8597 for (var i = 0; i < order.length; ++i) {
8598 var part = order[i];
8599 if (part.from < to && part.to > from || from == to && part.to == from) {
8600 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
8601 found = true;
8602 }
8603 }
8604 if (!found) f(from, to, "ltr");
8605 }
8606
8607 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8608 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8609
8610 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8611 function lineRight(line) {
8612 var order = getOrder(line);
8613 if (!order) return line.text.length;
8614 return bidiRight(lst(order));
8615 }
6683
6684 // Number keys
6685 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
6686 // Alphabetic keys
6687 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
6688 // Function keys
6689 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
6690
6691 var keyMap = {};
6692
6693 keyMap.basic = {
6694 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6695 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6696 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6697 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6698 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6699 "Esc": "singleSelection"
6700 };
6701 // Note that the save and find-related commands aren't defined by
6702 // default. User code or addons can define them. Unknown commands
6703 // are simply ignored.
6704 keyMap.pcDefault = {
6705 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6706 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6707 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6708 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6709 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6710 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6711 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6712 "fallthrough": "basic"
6713 };
6714 // Very basic readline/emacs-style bindings, which are standard on Mac.
6715 keyMap.emacsy = {
6716 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6717 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6718 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6719 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6720 "Ctrl-O": "openLine"
6721 };
6722 keyMap.macDefault = {
6723 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6724 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6725 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6726 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6727 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6728 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6729 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6730 "fallthrough": ["basic", "emacsy"]
6731 };
6732 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
6733
6734 // KEYMAP DISPATCH
6735
6736 function normalizeKeyName(name) {
6737 var parts = name.split(/-(?!$)/);
6738 name = parts[parts.length - 1];
6739 var alt, ctrl, shift, cmd;
6740 for (var i = 0; i < parts.length - 1; i++) {
6741 var mod = parts[i];
6742 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
6743 else if (/^a(lt)?$/i.test(mod)) { alt = true; }
6744 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
6745 else if (/^s(hift)?$/i.test(mod)) { shift = true; }
6746 else { throw new Error("Unrecognized modifier name: " + mod) }
6747 }
6748 if (alt) { name = "Alt-" + name; }
6749 if (ctrl) { name = "Ctrl-" + name; }
6750 if (cmd) { name = "Cmd-" + name; }
6751 if (shift) { name = "Shift-" + name; }
6752 return name
6753 }
6754
6755 // This is a kludge to keep keymaps mostly working as raw objects
6756 // (backwards compatibility) while at the same time support features
6757 // like normalization and multi-stroke key bindings. It compiles a
6758 // new normalized keymap, and then updates the old object to reflect
6759 // this.
6760 function normalizeKeyMap(keymap) {
6761 var copy = {};
6762 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6763 var value = keymap[keyname];
6764 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6765 if (value == "...") { delete keymap[keyname]; continue }
6766
6767 var keys = map(keyname.split(" "), normalizeKeyName);
6768 for (var i = 0; i < keys.length; i++) {
6769 var val = (void 0), name = (void 0);
6770 if (i == keys.length - 1) {
6771 name = keys.join(" ");
6772 val = value;
6773 } else {
6774 name = keys.slice(0, i + 1).join(" ");
6775 val = "...";
6776 }
6777 var prev = copy[name];
6778 if (!prev) { copy[name] = val; }
6779 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6780 }
6781 delete keymap[keyname];
6782 } }
6783 for (var prop in copy) { keymap[prop] = copy[prop]; }
6784 return keymap
6785 }
6786
6787 function lookupKey(key, map$$1, handle, context) {
6788 map$$1 = getKeyMap(map$$1);
6789 var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
6790 if (found === false) { return "nothing" }
6791 if (found === "...") { return "multi" }
6792 if (found != null && handle(found)) { return "handled" }
6793
6794 if (map$$1.fallthrough) {
6795 if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6796 { return lookupKey(key, map$$1.fallthrough, handle, context) }
6797 for (var i = 0; i < map$$1.fallthrough.length; i++) {
6798 var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
6799 if (result) { return result }
6800 }
6801 }
6802 }
6803
6804 // Modifier key presses don't count as 'real' key presses for the
6805 // purpose of keymap fallthrough.
6806 function isModifierKey(value) {
6807 var name = typeof value == "string" ? value : keyNames[value.keyCode];
6808 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6809 }
6810
6811 function addModifierNames(name, event, noShift) {
6812 var base = name;
6813 if (event.altKey && base != "Alt") { name = "Alt-" + name; }
6814 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
6815 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
6816 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
6817 return name
6818 }
6819
6820 // Look up the name of a key as indicated by an event object.
6821 function keyName(event, noShift) {
6822 if (presto && event.keyCode == 34 && event["char"]) { return false }
6823 var name = keyNames[event.keyCode];
6824 if (name == null || event.altGraphKey) { return false }
6825 // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
6826 // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
6827 if (event.keyCode == 3 && event.code) { name = event.code; }
6828 return addModifierNames(name, event, noShift)
6829 }
6830
6831 function getKeyMap(val) {
6832 return typeof val == "string" ? keyMap[val] : val
6833 }
6834
6835 // Helper for deleting text near the selection(s), used to implement
6836 // backspace, delete, and similar functionality.
6837 function deleteNearSelection(cm, compute) {
6838 var ranges = cm.doc.sel.ranges, kill = [];
6839 // Build up a set of ranges to kill first, merging overlapping
6840 // ranges.
6841 for (var i = 0; i < ranges.length; i++) {
6842 var toKill = compute(ranges[i]);
6843 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6844 var replaced = kill.pop();
6845 if (cmp(replaced.from, toKill.from) < 0) {
6846 toKill.from = replaced.from;
6847 break
6848 }
6849 }
6850 kill.push(toKill);
6851 }
6852 // Next, remove those actual ranges.
6853 runInOp(cm, function () {
6854 for (var i = kill.length - 1; i >= 0; i--)
6855 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
6856 ensureCursorVisible(cm);
6857 });
6858 }
6859
6860 function moveCharLogically(line, ch, dir) {
6861 var target = skipExtendingChars(line.text, ch + dir, dir);
6862 return target < 0 || target > line.text.length ? null : target
6863 }
6864
6865 function moveLogically(line, start, dir) {
6866 var ch = moveCharLogically(line, start.ch, dir);
6867 return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6868 }
6869
6870 function endOfLine(visually, cm, lineObj, lineNo, dir) {
6871 if (visually) {
6872 var order = getOrder(lineObj, cm.doc.direction);
6873 if (order) {
6874 var part = dir < 0 ? lst(order) : order[0];
6875 var moveInStorageOrder = (dir < 0) == (part.level == 1);
6876 var sticky = moveInStorageOrder ? "after" : "before";
6877 var ch;
6878 // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6879 // it could be that the last bidi part is not on the last visual line,
6880 // since visual lines contain content order-consecutive chunks.
6881 // Thus, in rtl, we are looking for the first (content-order) character
6882 // in the rtl chunk that is on the last line (that is, the same line
6883 // as the last (content-order) character).
6884 if (part.level > 0 || cm.doc.direction == "rtl") {
6885 var prep = prepareMeasureForLine(cm, lineObj);
6886 ch = dir < 0 ? lineObj.text.length - 1 : 0;
6887 var targetTop = measureCharPrepared(cm, prep, ch).top;
6888 ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
6889 if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
6890 } else { ch = dir < 0 ? part.to : part.from; }
6891 return new Pos(lineNo, ch, sticky)
6892 }
6893 }
6894 return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6895 }
6896
6897 function moveVisually(cm, line, start, dir) {
6898 var bidi = getOrder(line, cm.doc.direction);
6899 if (!bidi) { return moveLogically(line, start, dir) }
6900 if (start.ch >= line.text.length) {
6901 start.ch = line.text.length;
6902 start.sticky = "before";
6903 } else if (start.ch <= 0) {
6904 start.ch = 0;
6905 start.sticky = "after";
6906 }
6907 var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
6908 if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6909 // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6910 // nothing interesting happens.
6911 return moveLogically(line, start, dir)
6912 }
6913
6914 var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
6915 var prep;
6916 var getWrappedLineExtent = function (ch) {
6917 if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6918 prep = prep || prepareMeasureForLine(cm, line);
6919 return wrappedLineExtentChar(cm, line, prep, ch)
6920 };
6921 var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
6922
6923 if (cm.doc.direction == "rtl" || part.level == 1) {
6924 var moveInStorageOrder = (part.level == 1) == (dir < 0);
6925 var ch = mv(start, moveInStorageOrder ? 1 : -1);
6926 if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6927 // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6928 var sticky = moveInStorageOrder ? "before" : "after";
6929 return new Pos(start.line, ch, sticky)
6930 }
6931 }
6932
6933 // Case 3: Could not move within this bidi part in this visual line, so leave
6934 // the current bidi part
6935
6936 var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6937 var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6938 ? new Pos(start.line, mv(ch, 1), "before")
6939 : new Pos(start.line, ch, "after"); };
6940
6941 for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6942 var part = bidi[partPos];
6943 var moveInStorageOrder = (dir > 0) == (part.level != 1);
6944 var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
6945 if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6946 ch = moveInStorageOrder ? part.from : mv(part.to, -1);
6947 if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6948 }
6949 };
6950
6951 // Case 3a: Look for other bidi parts on the same visual line
6952 var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
6953 if (res) { return res }
6954
6955 // Case 3b: Look for other bidi parts on the next visual line
6956 var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
6957 if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6958 res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
6959 if (res) { return res }
6960 }
6961
6962 // Case 4: Nowhere to move
6963 return null
6964 }
6965
6966 // Commands are parameter-less actions that can be performed on an
6967 // editor, mostly used for keybindings.
6968 var commands = {
6969 selectAll: selectAll,
6970 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6971 killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6972 if (range.empty()) {
6973 var len = getLine(cm.doc, range.head.line).text.length;
6974 if (range.head.ch == len && range.head.line < cm.lastLine())
6975 { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6976 else
6977 { return {from: range.head, to: Pos(range.head.line, len)} }
6978 } else {
6979 return {from: range.from(), to: range.to()}
6980 }
6981 }); },
6982 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6983 from: Pos(range.from().line, 0),
6984 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6985 }); }); },
6986 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6987 from: Pos(range.from().line, 0), to: range.from()
6988 }); }); },
6989 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6990 var top = cm.charCoords(range.head, "div").top + 5;
6991 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
6992 return {from: leftPos, to: range.from()}
6993 }); },
6994 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6995 var top = cm.charCoords(range.head, "div").top + 5;
6996 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
6997 return {from: range.from(), to: rightPos }
6998 }); },
6999 undo: function (cm) { return cm.undo(); },
7000 redo: function (cm) { return cm.redo(); },
7001 undoSelection: function (cm) { return cm.undoSelection(); },
7002 redoSelection: function (cm) { return cm.redoSelection(); },
7003 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
7004 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
7005 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
7006 {origin: "+move", bias: 1}
7007 ); },
7008 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
7009 {origin: "+move", bias: 1}
7010 ); },
7011 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
7012 {origin: "+move", bias: -1}
7013 ); },
7014 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
7015 var top = cm.cursorCoords(range.head, "div").top + 5;
7016 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
7017 }, sel_move); },
7018 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
7019 var top = cm.cursorCoords(range.head, "div").top + 5;
7020 return cm.coordsChar({left: 0, top: top}, "div")
7021 }, sel_move); },
7022 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
7023 var top = cm.cursorCoords(range.head, "div").top + 5;
7024 var pos = cm.coordsChar({left: 0, top: top}, "div");
7025 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
7026 return pos
7027 }, sel_move); },
7028 goLineUp: function (cm) { return cm.moveV(-1, "line"); },
7029 goLineDown: function (cm) { return cm.moveV(1, "line"); },
7030 goPageUp: function (cm) { return cm.moveV(-1, "page"); },
7031 goPageDown: function (cm) { return cm.moveV(1, "page"); },
7032 goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
7033 goCharRight: function (cm) { return cm.moveH(1, "char"); },
7034 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
7035 goColumnRight: function (cm) { return cm.moveH(1, "column"); },
7036 goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
7037 goGroupRight: function (cm) { return cm.moveH(1, "group"); },
7038 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
7039 goWordRight: function (cm) { return cm.moveH(1, "word"); },
7040 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
7041 delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
7042 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
7043 delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
7044 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
7045 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
7046 indentAuto: function (cm) { return cm.indentSelection("smart"); },
7047 indentMore: function (cm) { return cm.indentSelection("add"); },
7048 indentLess: function (cm) { return cm.indentSelection("subtract"); },
7049 insertTab: function (cm) { return cm.replaceSelection("\t"); },
7050 insertSoftTab: function (cm) {
7051 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
7052 for (var i = 0; i < ranges.length; i++) {
7053 var pos = ranges[i].from();
7054 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
7055 spaces.push(spaceStr(tabSize - col % tabSize));
7056 }
7057 cm.replaceSelections(spaces);
7058 },
7059 defaultTab: function (cm) {
7060 if (cm.somethingSelected()) { cm.indentSelection("add"); }
7061 else { cm.execCommand("insertTab"); }
7062 },
7063 // Swap the two chars left and right of each selection's head.
7064 // Move cursor behind the two swapped characters afterwards.
7065 //
7066 // Doesn't consider line feeds a character.
7067 // Doesn't scan more than one line above to find a character.
7068 // Doesn't do anything on an empty line.
7069 // Doesn't do anything with non-empty selections.
7070 transposeChars: function (cm) { return runInOp(cm, function () {
7071 var ranges = cm.listSelections(), newSel = [];
7072 for (var i = 0; i < ranges.length; i++) {
7073 if (!ranges[i].empty()) { continue }
7074 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
7075 if (line) {
7076 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
7077 if (cur.ch > 0) {
7078 cur = new Pos(cur.line, cur.ch + 1);
7079 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
7080 Pos(cur.line, cur.ch - 2), cur, "+transpose");
7081 } else if (cur.line > cm.doc.first) {
7082 var prev = getLine(cm.doc, cur.line - 1).text;
7083 if (prev) {
7084 cur = new Pos(cur.line, 1);
7085 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
7086 prev.charAt(prev.length - 1),
7087 Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
7088 }
7089 }
7090 }
7091 newSel.push(new Range(cur, cur));
7092 }
7093 cm.setSelections(newSel);
7094 }); },
7095 newlineAndIndent: function (cm) { return runInOp(cm, function () {
7096 var sels = cm.listSelections();
7097 for (var i = sels.length - 1; i >= 0; i--)
7098 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
7099 sels = cm.listSelections();
7100 for (var i$1 = 0; i$1 < sels.length; i$1++)
7101 { cm.indentLine(sels[i$1].from().line, null, true); }
7102 ensureCursorVisible(cm);
7103 }); },
7104 openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7105 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7106 };
7107
8616 7108
8617 7109 function lineStart(cm, lineN) {
8618 7110 var line = getLine(cm.doc, lineN);
8619 7111 var visual = visualLine(line);
8620 if (visual != line) lineN = lineNo(visual);
8621 var order = getOrder(visual);
8622 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8623 return Pos(lineN, ch);
7112 if (visual != line) { lineN = lineNo(visual); }
7113 return endOfLine(true, cm, visual, lineN, 1)
8624 7114 }
8625 7115 function lineEnd(cm, lineN) {
8626 var merged, line = getLine(cm.doc, lineN);
8627 while (merged = collapsedSpanAtEnd(line)) {
8628 line = merged.find(1, true).line;
8629 lineN = null;
8630 }
8631 var order = getOrder(line);
8632 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8633 return Pos(lineN == null ? lineNo(line) : lineN, ch);
7116 var line = getLine(cm.doc, lineN);
7117 var visual = visualLineEnd(line);
7118 if (visual != line) { lineN = lineNo(visual); }
7119 return endOfLine(true, cm, line, lineN, -1)
8634 7120 }
8635 7121 function lineStartSmart(cm, pos) {
8636 7122 var start = lineStart(cm, pos.line);
8637 7123 var line = getLine(cm.doc, start.line);
8638 var order = getOrder(line);
7124 var order = getOrder(line, cm.doc.direction);
8639 7125 if (!order || order[0].level == 0) {
8640 7126 var firstNonWS = Math.max(0, line.text.search(/\S/));
8641 7127 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8642 return Pos(start.line, inWS ? 0 : firstNonWS);
8643 }
8644 return start;
8645 }
8646
8647 function compareBidiLevel(order, a, b) {
8648 var linedir = order[0].level;
8649 if (a == linedir) return true;
8650 if (b == linedir) return false;
8651 return a < b;
8652 }
8653 var bidiOther;
8654 function getBidiPartAt(order, pos) {
8655 bidiOther = null;
8656 for (var i = 0, found; i < order.length; ++i) {
8657 var cur = order[i];
8658 if (cur.from < pos && cur.to > pos) return i;
8659 if ((cur.from == pos || cur.to == pos)) {
8660 if (found == null) {
8661 found = i;
8662 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8663 if (cur.from != cur.to) bidiOther = found;
8664 return i;
8665 } else {
8666 if (cur.from != cur.to) bidiOther = i;
8667 return found;
8668 }
8669 }
8670 }
8671 return found;
8672 }
8673
8674 function moveInLine(line, pos, dir, byUnit) {
8675 if (!byUnit) return pos + dir;
8676 do pos += dir;
8677 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8678 return pos;
8679 }
8680
8681 // This is needed in order to move 'visually' through bi-directional
8682 // text -- i.e., pressing left should make the cursor go left, even
8683 // when in RTL text. The tricky part is the 'jumps', where RTL and
8684 // LTR text touch each other. This often requires the cursor offset
8685 // to move more than one unit, in order to visually move one unit.
8686 function moveVisually(line, start, dir, byUnit) {
8687 var bidi = getOrder(line);
8688 if (!bidi) return moveLogically(line, start, dir, byUnit);
8689 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8690 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8691
8692 for (;;) {
8693 if (target > part.from && target < part.to) return target;
8694 if (target == part.from || target == part.to) {
8695 if (getBidiPartAt(bidi, target) == pos) return target;
8696 part = bidi[pos += dir];
8697 return (dir > 0) == part.level % 2 ? part.to : part.from;
8698 } else {
8699 part = bidi[pos += dir];
8700 if (!part) return null;
8701 if ((dir > 0) == part.level % 2)
8702 target = moveInLine(line, part.to, -1, byUnit);
7128 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7129 }
7130 return start
7131 }
7132
7133 // Run a handler that was bound to a key.
7134 function doHandleBinding(cm, bound, dropShift) {
7135 if (typeof bound == "string") {
7136 bound = commands[bound];
7137 if (!bound) { return false }
7138 }
7139 // Ensure previous input has been read, so that the handler sees a
7140 // consistent view of the document
7141 cm.display.input.ensurePolled();
7142 var prevShift = cm.display.shift, done = false;
7143 try {
7144 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7145 if (dropShift) { cm.display.shift = false; }
7146 done = bound(cm) != Pass;
7147 } finally {
7148 cm.display.shift = prevShift;
7149 cm.state.suppressEdits = false;
7150 }
7151 return done
7152 }
7153
7154 function lookupKeyForEditor(cm, name, handle) {
7155 for (var i = 0; i < cm.state.keyMaps.length; i++) {
7156 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
7157 if (result) { return result }
7158 }
7159 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7160 || lookupKey(name, cm.options.keyMap, handle, cm)
7161 }
7162
7163 // Note that, despite the name, this function is also used to check
7164 // for bound mouse clicks.
7165
7166 var stopSeq = new Delayed;
7167
7168 function dispatchKey(cm, name, e, handle) {
7169 var seq = cm.state.keySeq;
7170 if (seq) {
7171 if (isModifierKey(name)) { return "handled" }
7172 if (/\'$/.test(name))
7173 { cm.state.keySeq = null; }
7174 else
7175 { stopSeq.set(50, function () {
7176 if (cm.state.keySeq == seq) {
7177 cm.state.keySeq = null;
7178 cm.display.input.reset();
7179 }
7180 }); }
7181 if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
7182 }
7183 return dispatchKeyInner(cm, name, e, handle)
7184 }
7185
7186 function dispatchKeyInner(cm, name, e, handle) {
7187 var result = lookupKeyForEditor(cm, name, handle);
7188
7189 if (result == "multi")
7190 { cm.state.keySeq = name; }
7191 if (result == "handled")
7192 { signalLater(cm, "keyHandled", cm, name, e); }
7193
7194 if (result == "handled" || result == "multi") {
7195 e_preventDefault(e);
7196 restartBlink(cm);
7197 }
7198
7199 return !!result
7200 }
7201
7202 // Handle a key from the keydown event.
7203 function handleKeyBinding(cm, e) {
7204 var name = keyName(e, true);
7205 if (!name) { return false }
7206
7207 if (e.shiftKey && !cm.state.keySeq) {
7208 // First try to resolve full name (including 'Shift-'). Failing
7209 // that, see if there is a cursor-motion command (starting with
7210 // 'go') bound to the keyname without 'Shift-'.
7211 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7212 || dispatchKey(cm, name, e, function (b) {
7213 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7214 { return doHandleBinding(cm, b) }
7215 })
7216 } else {
7217 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7218 }
7219 }
7220
7221 // Handle a key from the keypress event
7222 function handleCharBinding(cm, e, ch) {
7223 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7224 }
7225
7226 var lastStoppedKey = null;
7227 function onKeyDown(e) {
7228 var cm = this;
7229 cm.curOp.focus = activeElt();
7230 if (signalDOMEvent(cm, e)) { return }
7231 // IE does strange things with escape.
7232 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
7233 var code = e.keyCode;
7234 cm.display.shift = code == 16 || e.shiftKey;
7235 var handled = handleKeyBinding(cm, e);
7236 if (presto) {
7237 lastStoppedKey = handled ? code : null;
7238 // Opera has no cut event... we try to at least catch the key combo
7239 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7240 { cm.replaceSelection("", null, "cut"); }
7241 }
7242
7243 // Turn mouse into crosshair when Alt is held on Mac.
7244 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7245 { showCrossHair(cm); }
7246 }
7247
7248 function showCrossHair(cm) {
7249 var lineDiv = cm.display.lineDiv;
7250 addClass(lineDiv, "CodeMirror-crosshair");
7251
7252 function up(e) {
7253 if (e.keyCode == 18 || !e.altKey) {
7254 rmClass(lineDiv, "CodeMirror-crosshair");
7255 off(document, "keyup", up);
7256 off(document, "mouseover", up);
7257 }
7258 }
7259 on(document, "keyup", up);
7260 on(document, "mouseover", up);
7261 }
7262
7263 function onKeyUp(e) {
7264 if (e.keyCode == 16) { this.doc.sel.shift = false; }
7265 signalDOMEvent(this, e);
7266 }
7267
7268 function onKeyPress(e) {
7269 var cm = this;
7270 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7271 var keyCode = e.keyCode, charCode = e.charCode;
7272 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7273 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7274 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
7275 // Some browsers fire keypress events for backspace
7276 if (ch == "\x08") { return }
7277 if (handleCharBinding(cm, e, ch)) { return }
7278 cm.display.input.onKeyPress(e);
7279 }
7280
7281 var DOUBLECLICK_DELAY = 400;
7282
7283 var PastClick = function(time, pos, button) {
7284 this.time = time;
7285 this.pos = pos;
7286 this.button = button;
7287 };
7288
7289 PastClick.prototype.compare = function (time, pos, button) {
7290 return this.time + DOUBLECLICK_DELAY > time &&
7291 cmp(pos, this.pos) == 0 && button == this.button
7292 };
7293
7294 var lastClick, lastDoubleClick;
7295 function clickRepeat(pos, button) {
7296 var now = +new Date;
7297 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7298 lastClick = lastDoubleClick = null;
7299 return "triple"
7300 } else if (lastClick && lastClick.compare(now, pos, button)) {
7301 lastDoubleClick = new PastClick(now, pos, button);
7302 lastClick = null;
7303 return "double"
7304 } else {
7305 lastClick = new PastClick(now, pos, button);
7306 lastDoubleClick = null;
7307 return "single"
7308 }
7309 }
7310
7311 // A mouse down can be a single click, double click, triple click,
7312 // start of selection drag, start of text drag, new cursor
7313 // (ctrl-click), rectangle drag (alt-drag), or xwin
7314 // middle-click-paste. Or it might be a click on something we should
7315 // not interfere with, such as a scrollbar or widget.
7316 function onMouseDown(e) {
7317 var cm = this, display = cm.display;
7318 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7319 display.input.ensurePolled();
7320 display.shift = e.shiftKey;
7321
7322 if (eventInWidget(display, e)) {
7323 if (!webkit) {
7324 // Briefly turn off draggability, to allow widgets to do
7325 // normal dragging things.
7326 display.scroller.draggable = false;
7327 setTimeout(function () { return display.scroller.draggable = true; }, 100);
7328 }
7329 return
7330 }
7331 if (clickInGutter(cm, e)) { return }
7332 var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
7333 window.focus();
7334
7335 // #3261: make sure, that we're not starting a second selection
7336 if (button == 1 && cm.state.selectingText)
7337 { cm.state.selectingText(e); }
7338
7339 if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7340
7341 if (button == 1) {
7342 if (pos) { leftButtonDown(cm, pos, repeat, e); }
7343 else if (e_target(e) == display.scroller) { e_preventDefault(e); }
7344 } else if (button == 2) {
7345 if (pos) { extendSelection(cm.doc, pos); }
7346 setTimeout(function () { return display.input.focus(); }, 20);
7347 } else if (button == 3) {
7348 if (captureRightClick) { cm.display.input.onContextMenu(e); }
7349 else { delayBlurEvent(cm); }
7350 }
7351 }
7352
7353 function handleMappedButton(cm, button, pos, repeat, event) {
7354 var name = "Click";
7355 if (repeat == "double") { name = "Double" + name; }
7356 else if (repeat == "triple") { name = "Triple" + name; }
7357 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
7358
7359 return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
7360 if (typeof bound == "string") { bound = commands[bound]; }
7361 if (!bound) { return false }
7362 var done = false;
7363 try {
7364 if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7365 done = bound(cm, pos) != Pass;
7366 } finally {
7367 cm.state.suppressEdits = false;
7368 }
7369 return done
7370 })
7371 }
7372
7373 function configureMouse(cm, repeat, event) {
7374 var option = cm.getOption("configureMouse");
7375 var value = option ? option(cm, repeat, event) : {};
7376 if (value.unit == null) {
7377 var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
7378 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
7379 }
7380 if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
7381 if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
7382 if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
7383 return value
7384 }
7385
7386 function leftButtonDown(cm, pos, repeat, event) {
7387 if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
7388 else { cm.curOp.focus = activeElt(); }
7389
7390 var behavior = configureMouse(cm, repeat, event);
7391
7392 var sel = cm.doc.sel, contained;
7393 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7394 repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7395 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7396 (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7397 { leftButtonStartDrag(cm, event, pos, behavior); }
7398 else
7399 { leftButtonSelect(cm, event, pos, behavior); }
7400 }
7401
7402 // Start a text drag. When it ends, see if any dragging actually
7403 // happen, and treat as a click if it didn't.
7404 function leftButtonStartDrag(cm, event, pos, behavior) {
7405 var display = cm.display, moved = false;
7406 var dragEnd = operation(cm, function (e) {
7407 if (webkit) { display.scroller.draggable = false; }
7408 cm.state.draggingText = false;
7409 off(display.wrapper.ownerDocument, "mouseup", dragEnd);
7410 off(display.wrapper.ownerDocument, "mousemove", mouseMove);
7411 off(display.scroller, "dragstart", dragStart);
7412 off(display.scroller, "drop", dragEnd);
7413 if (!moved) {
7414 e_preventDefault(e);
7415 if (!behavior.addNew)
7416 { extendSelection(cm.doc, pos, null, null, behavior.extend); }
7417 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7418 if (webkit || ie && ie_version == 9)
7419 { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
8703 7420 else
8704 target = moveInLine(line, part.from, 1, byUnit);
8705 }
8706 }
8707 }
8708
8709 function moveLogically(line, start, dir, byUnit) {
8710 var target = start + dir;
8711 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8712 return target < 0 || target > line.text.length ? null : target;
8713 }
8714
8715 // Bidirectional ordering algorithm
8716 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8717 // that this (partially) implements.
8718
8719 // One-char codes used for character types:
8720 // L (L): Left-to-Right
8721 // R (R): Right-to-Left
8722 // r (AL): Right-to-Left Arabic
8723 // 1 (EN): European Number
8724 // + (ES): European Number Separator
8725 // % (ET): European Number Terminator
8726 // n (AN): Arabic Number
8727 // , (CS): Common Number Separator
8728 // m (NSM): Non-Spacing Mark
8729 // b (BN): Boundary Neutral
8730 // s (B): Paragraph Separator
8731 // t (S): Segment Separator
8732 // w (WS): Whitespace
8733 // N (ON): Other Neutrals
8734
8735 // Returns null if characters are ordered as they appear
8736 // (left-to-right), or an array of sections ({from, to, level}
8737 // objects) in the order in which they occur visually.
8738 var bidiOrdering = (function() {
8739 // Character types for codepoints 0 to 0xff
8740 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8741 // Character types for codepoints 0x600 to 0x6ff
8742 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8743 function charType(code) {
8744 if (code <= 0xf7) return lowTypes.charAt(code);
8745 else if (0x590 <= code && code <= 0x5f4) return "R";
8746 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8747 else if (0x6ee <= code && code <= 0x8ac) return "r";
8748 else if (0x2000 <= code && code <= 0x200b) return "w";
8749 else if (code == 0x200c) return "b";
8750 else return "L";
8751 }
8752
8753 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8754 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8755 // Browsers seem to always treat the boundaries of block elements as being L.
8756 var outerType = "L";
8757
8758 function BidiSpan(level, from, to) {
8759 this.level = level;
8760 this.from = from; this.to = to;
8761 }
8762
8763 return function(str) {
8764 if (!bidiRE.test(str)) return false;
8765 var len = str.length, types = [];
8766 for (var i = 0, type; i < len; ++i)
8767 types.push(type = charType(str.charCodeAt(i)));
8768
8769 // W1. Examine each non-spacing mark (NSM) in the level run, and
8770 // change the type of the NSM to the type of the previous
8771 // character. If the NSM is at the start of the level run, it will
8772 // get the type of sor.
8773 for (var i = 0, prev = outerType; i < len; ++i) {
8774 var type = types[i];
8775 if (type == "m") types[i] = prev;
8776 else prev = type;
8777 }
8778
8779 // W2. Search backwards from each instance of a European number
8780 // until the first strong type (R, L, AL, or sor) is found. If an
8781 // AL is found, change the type of the European number to Arabic
8782 // number.
8783 // W3. Change all ALs to R.
8784 for (var i = 0, cur = outerType; i < len; ++i) {
8785 var type = types[i];
8786 if (type == "1" && cur == "r") types[i] = "n";
8787 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8788 }
8789
8790 // W4. A single European separator between two European numbers
8791 // changes to a European number. A single common separator between
8792 // two numbers of the same type changes to that type.
8793 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8794 var type = types[i];
8795 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8796 else if (type == "," && prev == types[i+1] &&
8797 (prev == "1" || prev == "n")) types[i] = prev;
8798 prev = type;
8799 }
8800
8801 // W5. A sequence of European terminators adjacent to European
8802 // numbers changes to all European numbers.
8803 // W6. Otherwise, separators and terminators change to Other
8804 // Neutral.
8805 for (var i = 0; i < len; ++i) {
8806 var type = types[i];
8807 if (type == ",") types[i] = "N";
8808 else if (type == "%") {
8809 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8810 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8811 for (var j = i; j < end; ++j) types[j] = replace;
8812 i = end - 1;
7421 { display.input.focus(); }
7422 }
7423 });
7424 var mouseMove = function(e2) {
7425 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
7426 };
7427 var dragStart = function () { return moved = true; };
7428 // Let the drag handler handle this.
7429 if (webkit) { display.scroller.draggable = true; }
7430 cm.state.draggingText = dragEnd;
7431 dragEnd.copy = !behavior.moveOnDrag;
7432 // IE's approach to draggable
7433 if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
7434 on(display.wrapper.ownerDocument, "mouseup", dragEnd);
7435 on(display.wrapper.ownerDocument, "mousemove", mouseMove);
7436 on(display.scroller, "dragstart", dragStart);
7437 on(display.scroller, "drop", dragEnd);
7438
7439 delayBlurEvent(cm);
7440 setTimeout(function () { return display.input.focus(); }, 20);
7441 }
7442
7443 function rangeForUnit(cm, pos, unit) {
7444 if (unit == "char") { return new Range(pos, pos) }
7445 if (unit == "word") { return cm.findWordAt(pos) }
7446 if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7447 var result = unit(cm, pos);
7448 return new Range(result.from, result.to)
7449 }
7450
7451 // Normal selection, as opposed to text dragging.
7452 function leftButtonSelect(cm, event, start, behavior) {
7453 var display = cm.display, doc = cm.doc;
7454 e_preventDefault(event);
7455
7456 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
7457 if (behavior.addNew && !behavior.extend) {
7458 ourIndex = doc.sel.contains(start);
7459 if (ourIndex > -1)
7460 { ourRange = ranges[ourIndex]; }
7461 else
7462 { ourRange = new Range(start, start); }
7463 } else {
7464 ourRange = doc.sel.primary();
7465 ourIndex = doc.sel.primIndex;
7466 }
7467
7468 if (behavior.unit == "rectangle") {
7469 if (!behavior.addNew) { ourRange = new Range(start, start); }
7470 start = posFromMouse(cm, event, true, true);
7471 ourIndex = -1;
7472 } else {
7473 var range$$1 = rangeForUnit(cm, start, behavior.unit);
7474 if (behavior.extend)
7475 { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
7476 else
7477 { ourRange = range$$1; }
7478 }
7479
7480 if (!behavior.addNew) {
7481 ourIndex = 0;
7482 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
7483 startSel = doc.sel;
7484 } else if (ourIndex == -1) {
7485 ourIndex = ranges.length;
7486 setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
7487 {scroll: false, origin: "*mouse"});
7488 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7489 setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7490 {scroll: false, origin: "*mouse"});
7491 startSel = doc.sel;
7492 } else {
7493 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
7494 }
7495
7496 var lastPos = start;
7497 function extendTo(pos) {
7498 if (cmp(lastPos, pos) == 0) { return }
7499 lastPos = pos;
7500
7501 if (behavior.unit == "rectangle") {
7502 var ranges = [], tabSize = cm.options.tabSize;
7503 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
7504 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
7505 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
7506 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7507 line <= end; line++) {
7508 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
7509 if (left == right)
7510 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
7511 else if (text.length > leftPos)
7512 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
7513 }
7514 if (!ranges.length) { ranges.push(new Range(start, start)); }
7515 setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7516 {origin: "*mouse", scroll: false});
7517 cm.scrollIntoView(pos);
7518 } else {
7519 var oldRange = ourRange;
7520 var range$$1 = rangeForUnit(cm, pos, behavior.unit);
7521 var anchor = oldRange.anchor, head;
7522 if (cmp(range$$1.anchor, anchor) > 0) {
7523 head = range$$1.head;
7524 anchor = minPos(oldRange.from(), range$$1.anchor);
7525 } else {
7526 head = range$$1.anchor;
7527 anchor = maxPos(oldRange.to(), range$$1.head);
7528 }
7529 var ranges$1 = startSel.ranges.slice(0);
7530 ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
7531 setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
7532 }
7533 }
7534
7535 var editorSize = display.wrapper.getBoundingClientRect();
7536 // Used to ensure timeout re-tries don't fire when another extend
7537 // happened in the meantime (clearTimeout isn't reliable -- at
7538 // least on Chrome, the timeouts still happen even when cleared,
7539 // if the clear happens after their scheduled firing time).
7540 var counter = 0;
7541
7542 function extend(e) {
7543 var curCount = ++counter;
7544 var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
7545 if (!cur) { return }
7546 if (cmp(cur, lastPos) != 0) {
7547 cm.curOp.focus = activeElt();
7548 extendTo(cur);
7549 var visible = visibleLines(display, doc);
7550 if (cur.line >= visible.to || cur.line < visible.from)
7551 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
7552 } else {
7553 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
7554 if (outside) { setTimeout(operation(cm, function () {
7555 if (counter != curCount) { return }
7556 display.scroller.scrollTop += outside;
7557 extend(e);
7558 }), 50); }
7559 }
7560 }
7561
7562 function done(e) {
7563 cm.state.selectingText = false;
7564 counter = Infinity;
7565 // If e is null or undefined we interpret this as someone trying
7566 // to explicitly cancel the selection rather than the user
7567 // letting go of the mouse button.
7568 if (e) {
7569 e_preventDefault(e);
7570 display.input.focus();
7571 }
7572 off(display.wrapper.ownerDocument, "mousemove", move);
7573 off(display.wrapper.ownerDocument, "mouseup", up);
7574 doc.history.lastSelOrigin = null;
7575 }
7576
7577 var move = operation(cm, function (e) {
7578 if (e.buttons === 0 || !e_button(e)) { done(e); }
7579 else { extend(e); }
7580 });
7581 var up = operation(cm, done);
7582 cm.state.selectingText = up;
7583 on(display.wrapper.ownerDocument, "mousemove", move);
7584 on(display.wrapper.ownerDocument, "mouseup", up);
7585 }
7586
7587 // Used when mouse-selecting to adjust the anchor to the proper side
7588 // of a bidi jump depending on the visual position of the head.
7589 function bidiSimplify(cm, range$$1) {
7590 var anchor = range$$1.anchor;
7591 var head = range$$1.head;
7592 var anchorLine = getLine(cm.doc, anchor.line);
7593 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
7594 var order = getOrder(anchorLine);
7595 if (!order) { return range$$1 }
7596 var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
7597 if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
7598 var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
7599 if (boundary == 0 || boundary == order.length) { return range$$1 }
7600
7601 // Compute the relative visual position of the head compared to the
7602 // anchor (<0 is to the left, >0 to the right)
7603 var leftSide;
7604 if (head.line != anchor.line) {
7605 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
7606 } else {
7607 var headIndex = getBidiPartAt(order, head.ch, head.sticky);
7608 var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
7609 if (headIndex == boundary - 1 || headIndex == boundary)
7610 { leftSide = dir < 0; }
7611 else
7612 { leftSide = dir > 0; }
7613 }
7614
7615 var usePart = order[boundary + (leftSide ? -1 : 0)];
7616 var from = leftSide == (usePart.level == 1);
7617 var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
7618 return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
7619 }
7620
7621
7622 // Determines whether an event happened in the gutter, and fires the
7623 // handlers for the corresponding event.
7624 function gutterEvent(cm, e, type, prevent) {
7625 var mX, mY;
7626 if (e.touches) {
7627 mX = e.touches[0].clientX;
7628 mY = e.touches[0].clientY;
7629 } else {
7630 try { mX = e.clientX; mY = e.clientY; }
7631 catch(e) { return false }
7632 }
7633 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7634 if (prevent) { e_preventDefault(e); }
7635
7636 var display = cm.display;
7637 var lineBox = display.lineDiv.getBoundingClientRect();
7638
7639 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7640 mY -= lineBox.top - display.viewOffset;
7641
7642 for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
7643 var g = display.gutters.childNodes[i];
7644 if (g && g.getBoundingClientRect().right >= mX) {
7645 var line = lineAtHeight(cm.doc, mY);
7646 var gutter = cm.display.gutterSpecs[i];
7647 signal(cm, type, cm, line, gutter.className, e);
7648 return e_defaultPrevented(e)
7649 }
7650 }
7651 }
7652
7653 function clickInGutter(cm, e) {
7654 return gutterEvent(cm, e, "gutterClick", true)
7655 }
7656
7657 // CONTEXT MENU HANDLING
7658
7659 // To make the context menu work, we need to briefly unhide the
7660 // textarea (making it as unobtrusive as possible) to let the
7661 // right-click take effect on it.
7662 function onContextMenu(cm, e) {
7663 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7664 if (signalDOMEvent(cm, e, "contextmenu")) { return }
7665 if (!captureRightClick) { cm.display.input.onContextMenu(e); }
7666 }
7667
7668 function contextMenuInGutter(cm, e) {
7669 if (!hasHandler(cm, "gutterContextMenu")) { return false }
7670 return gutterEvent(cm, e, "gutterContextMenu", false)
7671 }
7672
7673 function themeChanged(cm) {
7674 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7675 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
7676 clearCaches(cm);
7677 }
7678
7679 var Init = {toString: function(){return "CodeMirror.Init"}};
7680
7681 var defaults = {};
7682 var optionHandlers = {};
7683
7684 function defineOptions(CodeMirror) {
7685 var optionHandlers = CodeMirror.optionHandlers;
7686
7687 function option(name, deflt, handle, notOnInit) {
7688 CodeMirror.defaults[name] = deflt;
7689 if (handle) { optionHandlers[name] =
7690 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
7691 }
7692
7693 CodeMirror.defineOption = option;
7694
7695 // Passed to option handlers when there is no old value.
7696 CodeMirror.Init = Init;
7697
7698 // These two are, on init, called from the constructor because they
7699 // have to be initialized before the editor can start at all.
7700 option("value", "", function (cm, val) { return cm.setValue(val); }, true);
7701 option("mode", null, function (cm, val) {
7702 cm.doc.modeOption = val;
7703 loadMode(cm);
7704 }, true);
7705
7706 option("indentUnit", 2, loadMode, true);
7707 option("indentWithTabs", false);
7708 option("smartIndent", true);
7709 option("tabSize", 4, function (cm) {
7710 resetModeState(cm);
7711 clearCaches(cm);
7712 regChange(cm);
7713 }, true);
7714
7715 option("lineSeparator", null, function (cm, val) {
7716 cm.doc.lineSep = val;
7717 if (!val) { return }
7718 var newBreaks = [], lineNo = cm.doc.first;
7719 cm.doc.iter(function (line) {
7720 for (var pos = 0;;) {
7721 var found = line.text.indexOf(val, pos);
7722 if (found == -1) { break }
7723 pos = found + val.length;
7724 newBreaks.push(Pos(lineNo, found));
7725 }
7726 lineNo++;
7727 });
7728 for (var i = newBreaks.length - 1; i >= 0; i--)
7729 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
7730 });
7731 option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
7732 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
7733 if (old != Init) { cm.refresh(); }
7734 });
7735 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
7736 option("electricChars", true);
7737 option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7738 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7739 }, true);
7740 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
7741 option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
7742 option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
7743 option("rtlMoveVisually", !windows);
7744 option("wholeLineUpdateBefore", true);
7745
7746 option("theme", "default", function (cm) {
7747 themeChanged(cm);
7748 updateGutters(cm);
7749 }, true);
7750 option("keyMap", "default", function (cm, val, old) {
7751 var next = getKeyMap(val);
7752 var prev = old != Init && getKeyMap(old);
7753 if (prev && prev.detach) { prev.detach(cm, next); }
7754 if (next.attach) { next.attach(cm, prev || null); }
7755 });
7756 option("extraKeys", null);
7757 option("configureMouse", null);
7758
7759 option("lineWrapping", false, wrappingChanged, true);
7760 option("gutters", [], function (cm, val) {
7761 cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
7762 updateGutters(cm);
7763 }, true);
7764 option("fixedGutter", true, function (cm, val) {
7765 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
7766 cm.refresh();
7767 }, true);
7768 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
7769 option("scrollbarStyle", "native", function (cm) {
7770 initScrollbars(cm);
7771 updateScrollbars(cm);
7772 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
7773 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
7774 }, true);
7775 option("lineNumbers", false, function (cm, val) {
7776 cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
7777 updateGutters(cm);
7778 }, true);
7779 option("firstLineNumber", 1, updateGutters, true);
7780 option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
7781 option("showCursorWhenSelecting", false, updateSelection, true);
7782
7783 option("resetSelectionOnContextMenu", true);
7784 option("lineWiseCopyCut", true);
7785 option("pasteLinesPerSelection", true);
7786 option("selectionsMayTouch", false);
7787
7788 option("readOnly", false, function (cm, val) {
7789 if (val == "nocursor") {
7790 onBlur(cm);
7791 cm.display.input.blur();
7792 }
7793 cm.display.input.readOnlyChanged(val);
7794 });
7795 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
7796 option("dragDrop", true, dragDropChanged);
7797 option("allowDropFileTypes", null);
7798
7799 option("cursorBlinkRate", 530);
7800 option("cursorScrollMargin", 0);
7801 option("cursorHeight", 1, updateSelection, true);
7802 option("singleCursorHeightPerLine", true, updateSelection, true);
7803 option("workTime", 100);
7804 option("workDelay", 100);
7805 option("flattenSpans", true, resetModeState, true);
7806 option("addModeClass", false, resetModeState, true);
7807 option("pollInterval", 100);
7808 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
7809 option("historyEventDelay", 1250);
7810 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
7811 option("maxHighlightLength", 10000, resetModeState, true);
7812 option("moveInputWithCursor", true, function (cm, val) {
7813 if (!val) { cm.display.input.resetPosition(); }
7814 });
7815
7816 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
7817 option("autofocus", null);
7818 option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
7819 option("phrases", null);
7820 }
7821
7822 function dragDropChanged(cm, value, old) {
7823 var wasOn = old && old != Init;
7824 if (!value != !wasOn) {
7825 var funcs = cm.display.dragFunctions;
7826 var toggle = value ? on : off;
7827 toggle(cm.display.scroller, "dragstart", funcs.start);
7828 toggle(cm.display.scroller, "dragenter", funcs.enter);
7829 toggle(cm.display.scroller, "dragover", funcs.over);
7830 toggle(cm.display.scroller, "dragleave", funcs.leave);
7831 toggle(cm.display.scroller, "drop", funcs.drop);
7832 }
7833 }
7834
7835 function wrappingChanged(cm) {
7836 if (cm.options.lineWrapping) {
7837 addClass(cm.display.wrapper, "CodeMirror-wrap");
7838 cm.display.sizer.style.minWidth = "";
7839 cm.display.sizerWidth = null;
7840 } else {
7841 rmClass(cm.display.wrapper, "CodeMirror-wrap");
7842 findMaxLine(cm);
7843 }
7844 estimateLineHeights(cm);
7845 regChange(cm);
7846 clearCaches(cm);
7847 setTimeout(function () { return updateScrollbars(cm); }, 100);
7848 }
7849
7850 // A CodeMirror instance represents an editor. This is the object
7851 // that user code is usually dealing with.
7852
7853 function CodeMirror(place, options) {
7854 var this$1 = this;
7855
7856 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7857
7858 this.options = options = options ? copyObj(options) : {};
7859 // Determine effective options based on given values and defaults.
7860 copyObj(defaults, options, false);
7861
7862 var doc = options.value;
7863 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
7864 else if (options.mode) { doc.modeOption = options.mode; }
7865 this.doc = doc;
7866
7867 var input = new CodeMirror.inputStyles[options.inputStyle](this);
7868 var display = this.display = new Display(place, doc, input, options);
7869 display.wrapper.CodeMirror = this;
7870 themeChanged(this);
7871 if (options.lineWrapping)
7872 { this.display.wrapper.className += " CodeMirror-wrap"; }
7873 initScrollbars(this);
7874
7875 this.state = {
7876 keyMaps: [], // stores maps added by addKeyMap
7877 overlays: [], // highlighting overlays, as added by addOverlay
7878 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7879 overwrite: false,
7880 delayingBlurEvent: false,
7881 focused: false,
7882 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7883 pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
7884 selectingText: false,
7885 draggingText: false,
7886 highlight: new Delayed(), // stores highlight worker timeout
7887 keySeq: null, // Unfinished key sequence
7888 specialChars: null
7889 };
7890
7891 if (options.autofocus && !mobile) { display.input.focus(); }
7892
7893 // Override magic textarea content restore that IE sometimes does
7894 // on our hidden textarea on reload
7895 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
7896
7897 registerEventHandlers(this);
7898 ensureGlobalHandlers();
7899
7900 startOperation(this);
7901 this.curOp.forceUpdate = true;
7902 attachDoc(this, doc);
7903
7904 if ((options.autofocus && !mobile) || this.hasFocus())
7905 { setTimeout(bind(onFocus, this), 20); }
7906 else
7907 { onBlur(this); }
7908
7909 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7910 { optionHandlers[opt](this$1, options[opt], Init); } }
7911 maybeUpdateLineNumberWidth(this);
7912 if (options.finishInit) { options.finishInit(this); }
7913 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
7914 endOperation(this);
7915 // Suppress optimizelegibility in Webkit, since it breaks text
7916 // measuring on line wrapping boundaries.
7917 if (webkit && options.lineWrapping &&
7918 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7919 { display.lineDiv.style.textRendering = "auto"; }
7920 }
7921
7922 // The default configuration options.
7923 CodeMirror.defaults = defaults;
7924 // Functions to run when options are changed.
7925 CodeMirror.optionHandlers = optionHandlers;
7926
7927 // Attach the necessary event handlers when initializing the editor
7928 function registerEventHandlers(cm) {
7929 var d = cm.display;
7930 on(d.scroller, "mousedown", operation(cm, onMouseDown));
7931 // Older IE's will not fire a second mousedown for a double click
7932 if (ie && ie_version < 11)
7933 { on(d.scroller, "dblclick", operation(cm, function (e) {
7934 if (signalDOMEvent(cm, e)) { return }
7935 var pos = posFromMouse(cm, e);
7936 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7937 e_preventDefault(e);
7938 var word = cm.findWordAt(pos);
7939 extendSelection(cm.doc, word.anchor, word.head);
7940 })); }
7941 else
7942 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
7943 // Some browsers fire contextmenu *after* opening the menu, at
7944 // which point we can't mess with it anymore. Context menu is
7945 // handled in onMouseDown for these browsers.
7946 on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
7947
7948 // Used to suppress mouse event handling when a touch happens
7949 var touchFinished, prevTouch = {end: 0};
7950 function finishTouch() {
7951 if (d.activeTouch) {
7952 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
7953 prevTouch = d.activeTouch;
7954 prevTouch.end = +new Date;
7955 }
7956 }
7957 function isMouseLikeTouchEvent(e) {
7958 if (e.touches.length != 1) { return false }
7959 var touch = e.touches[0];
7960 return touch.radiusX <= 1 && touch.radiusY <= 1
7961 }
7962 function farAway(touch, other) {
7963 if (other.left == null) { return true }
7964 var dx = other.left - touch.left, dy = other.top - touch.top;
7965 return dx * dx + dy * dy > 20 * 20
7966 }
7967 on(d.scroller, "touchstart", function (e) {
7968 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7969 d.input.ensurePolled();
7970 clearTimeout(touchFinished);
7971 var now = +new Date;
7972 d.activeTouch = {start: now, moved: false,
7973 prev: now - prevTouch.end <= 300 ? prevTouch : null};
7974 if (e.touches.length == 1) {
7975 d.activeTouch.left = e.touches[0].pageX;
7976 d.activeTouch.top = e.touches[0].pageY;
7977 }
7978 }
7979 });
7980 on(d.scroller, "touchmove", function () {
7981 if (d.activeTouch) { d.activeTouch.moved = true; }
7982 });
7983 on(d.scroller, "touchend", function (e) {
7984 var touch = d.activeTouch;
7985 if (touch && !eventInWidget(d, e) && touch.left != null &&
7986 !touch.moved && new Date - touch.start < 300) {
7987 var pos = cm.coordsChar(d.activeTouch, "page"), range;
7988 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7989 { range = new Range(pos, pos); }
7990 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7991 { range = cm.findWordAt(pos); }
7992 else // Triple tap
7993 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
7994 cm.setSelection(range.anchor, range.head);
7995 cm.focus();
7996 e_preventDefault(e);
7997 }
7998 finishTouch();
7999 });
8000 on(d.scroller, "touchcancel", finishTouch);
8001
8002 // Sync scrolling between fake scrollbars and real scrollable
8003 // area, ensure viewport is updated when scrolling.
8004 on(d.scroller, "scroll", function () {
8005 if (d.scroller.clientHeight) {
8006 updateScrollTop(cm, d.scroller.scrollTop);
8007 setScrollLeft(cm, d.scroller.scrollLeft, true);
8008 signal(cm, "scroll", cm);
8009 }
8010 });
8011
8012 // Listen to wheel events in order to try and update the viewport on time.
8013 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
8014 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
8015
8016 // Prevent wrapper from ever scrolling
8017 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
8018
8019 d.dragFunctions = {
8020 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
8021 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
8022 start: function (e) { return onDragStart(cm, e); },
8023 drop: operation(cm, onDrop),
8024 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
8025 };
8026
8027 var inp = d.input.getField();
8028 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
8029 on(inp, "keydown", operation(cm, onKeyDown));
8030 on(inp, "keypress", operation(cm, onKeyPress));
8031 on(inp, "focus", function (e) { return onFocus(cm, e); });
8032 on(inp, "blur", function (e) { return onBlur(cm, e); });
8033 }
8034
8035 var initHooks = [];
8036 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
8037
8038 // Indent the given line. The how parameter can be "smart",
8039 // "add"/null, "subtract", or "prev". When aggressive is false
8040 // (typically set to true for forced single-line indents), empty
8041 // lines are not indented, and places where the mode returns Pass
8042 // are left alone.
8043 function indentLine(cm, n, how, aggressive) {
8044 var doc = cm.doc, state;
8045 if (how == null) { how = "add"; }
8046 if (how == "smart") {
8047 // Fall back to "prev" when the mode doesn't have an indentation
8048 // method.
8049 if (!doc.mode.indent) { how = "prev"; }
8050 else { state = getContextBefore(cm, n).state; }
8051 }
8052
8053 var tabSize = cm.options.tabSize;
8054 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
8055 if (line.stateAfter) { line.stateAfter = null; }
8056 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
8057 if (!aggressive && !/\S/.test(line.text)) {
8058 indentation = 0;
8059 how = "not";
8060 } else if (how == "smart") {
8061 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
8062 if (indentation == Pass || indentation > 150) {
8063 if (!aggressive) { return }
8064 how = "prev";
8065 }
8066 }
8067 if (how == "prev") {
8068 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
8069 else { indentation = 0; }
8070 } else if (how == "add") {
8071 indentation = curSpace + cm.options.indentUnit;
8072 } else if (how == "subtract") {
8073 indentation = curSpace - cm.options.indentUnit;
8074 } else if (typeof how == "number") {
8075 indentation = curSpace + how;
8076 }
8077 indentation = Math.max(0, indentation);
8078
8079 var indentString = "", pos = 0;
8080 if (cm.options.indentWithTabs)
8081 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
8082 if (pos < indentation) { indentString += spaceStr(indentation - pos); }
8083
8084 if (indentString != curSpaceString) {
8085 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
8086 line.stateAfter = null;
8087 return true
8088 } else {
8089 // Ensure that, if the cursor was in the whitespace at the start
8090 // of the line, it is moved to the end of that space.
8091 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
8092 var range = doc.sel.ranges[i$1];
8093 if (range.head.line == n && range.head.ch < curSpaceString.length) {
8094 var pos$1 = Pos(n, curSpaceString.length);
8095 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
8096 break
8097 }
8098 }
8099 }
8100 }
8101
8102 // This will be set to a {lineWise: bool, text: [string]} object, so
8103 // that, when pasting, we know what kind of selections the copied
8104 // text was made out of.
8105 var lastCopied = null;
8106
8107 function setLastCopied(newLastCopied) {
8108 lastCopied = newLastCopied;
8109 }
8110
8111 function applyTextInput(cm, inserted, deleted, sel, origin) {
8112 var doc = cm.doc;
8113 cm.display.shift = false;
8114 if (!sel) { sel = doc.sel; }
8115
8116 var recent = +new Date - 200;
8117 var paste = origin == "paste" || cm.state.pasteIncoming > recent;
8118 var textLines = splitLinesAuto(inserted), multiPaste = null;
8119 // When pasting N lines into N selections, insert one line per selection
8120 if (paste && sel.ranges.length > 1) {
8121 if (lastCopied && lastCopied.text.join("\n") == inserted) {
8122 if (sel.ranges.length % lastCopied.text.length == 0) {
8123 multiPaste = [];
8124 for (var i = 0; i < lastCopied.text.length; i++)
8125 { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
8126 }
8127 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8128 multiPaste = map(textLines, function (l) { return [l]; });
8129 }
8130 }
8131
8132 var updateInput = cm.curOp.updateInput;
8133 // Normal behavior is to insert the new text into every selection
8134 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8135 var range$$1 = sel.ranges[i$1];
8136 var from = range$$1.from(), to = range$$1.to();
8137 if (range$$1.empty()) {
8138 if (deleted && deleted > 0) // Handle deletion
8139 { from = Pos(from.line, from.ch - deleted); }
8140 else if (cm.state.overwrite && !paste) // Handle overwrite
8141 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
8142 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8143 { from = to = Pos(from.line, 0); }
8144 }
8145 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8146 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
8147 makeChange(cm.doc, changeEvent);
8148 signalLater(cm, "inputRead", cm, changeEvent);
8149 }
8150 if (inserted && !paste)
8151 { triggerElectric(cm, inserted); }
8152
8153 ensureCursorVisible(cm);
8154 if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
8155 cm.curOp.typing = true;
8156 cm.state.pasteIncoming = cm.state.cutIncoming = -1;
8157 }
8158
8159 function handlePaste(e, cm) {
8160 var pasted = e.clipboardData && e.clipboardData.getData("Text");
8161 if (pasted) {
8162 e.preventDefault();
8163 if (!cm.isReadOnly() && !cm.options.disableInput)
8164 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
8165 return true
8166 }
8167 }
8168
8169 function triggerElectric(cm, inserted) {
8170 // When an 'electric' character is inserted, immediately trigger a reindent
8171 if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8172 var sel = cm.doc.sel;
8173
8174 for (var i = sel.ranges.length - 1; i >= 0; i--) {
8175 var range$$1 = sel.ranges[i];
8176 if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
8177 var mode = cm.getModeAt(range$$1.head);
8178 var indented = false;
8179 if (mode.electricChars) {
8180 for (var j = 0; j < mode.electricChars.length; j++)
8181 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8182 indented = indentLine(cm, range$$1.head.line, "smart");
8183 break
8184 } }
8185 } else if (mode.electricInput) {
8186 if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
8187 { indented = indentLine(cm, range$$1.head.line, "smart"); }
8188 }
8189 if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
8190 }
8191 }
8192
8193 function copyableRanges(cm) {
8194 var text = [], ranges = [];
8195 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8196 var line = cm.doc.sel.ranges[i].head.line;
8197 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
8198 ranges.push(lineRange);
8199 text.push(cm.getRange(lineRange.anchor, lineRange.head));
8200 }
8201 return {text: text, ranges: ranges}
8202 }
8203
8204 function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
8205 field.setAttribute("autocorrect", autocorrect ? "" : "off");
8206 field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
8207 field.setAttribute("spellcheck", !!spellcheck);
8208 }
8209
8210 function hiddenTextarea() {
8211 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
8212 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
8213 // The textarea is kept positioned near the cursor to prevent the
8214 // fact that it'll be scrolled into view on input from scrolling
8215 // our fake cursor out of view. On webkit, when wrap=off, paste is
8216 // very slow. So make the area wide instead.
8217 if (webkit) { te.style.width = "1000px"; }
8218 else { te.setAttribute("wrap", "off"); }
8219 // If border: 0; -- iOS fails to open keyboard (issue #1287)
8220 if (ios) { te.style.border = "1px solid black"; }
8221 disableBrowserMagic(te);
8222 return div
8223 }
8224
8225 // The publicly visible API. Note that methodOp(f) means
8226 // 'wrap f in an operation, performed on its `this` parameter'.
8227
8228 // This is not the complete set of editor methods. Most of the
8229 // methods defined on the Doc type are also injected into
8230 // CodeMirror.prototype, for backwards compatibility and
8231 // convenience.
8232
8233 function addEditorMethods(CodeMirror) {
8234 var optionHandlers = CodeMirror.optionHandlers;
8235
8236 var helpers = CodeMirror.helpers = {};
8237
8238 CodeMirror.prototype = {
8239 constructor: CodeMirror,
8240 focus: function(){window.focus(); this.display.input.focus();},
8241
8242 setOption: function(option, value) {
8243 var options = this.options, old = options[option];
8244 if (options[option] == value && option != "mode") { return }
8245 options[option] = value;
8246 if (optionHandlers.hasOwnProperty(option))
8247 { operation(this, optionHandlers[option])(this, value, old); }
8248 signal(this, "optionChange", this, option);
8249 },
8250
8251 getOption: function(option) {return this.options[option]},
8252 getDoc: function() {return this.doc},
8253
8254 addKeyMap: function(map$$1, bottom) {
8255 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
8256 },
8257 removeKeyMap: function(map$$1) {
8258 var maps = this.state.keyMaps;
8259 for (var i = 0; i < maps.length; ++i)
8260 { if (maps[i] == map$$1 || maps[i].name == map$$1) {
8261 maps.splice(i, 1);
8262 return true
8263 } }
8264 },
8265
8266 addOverlay: methodOp(function(spec, options) {
8267 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
8268 if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8269 insertSorted(this.state.overlays,
8270 {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8271 priority: (options && options.priority) || 0},
8272 function (overlay) { return overlay.priority; });
8273 this.state.modeGen++;
8274 regChange(this);
8275 }),
8276 removeOverlay: methodOp(function(spec) {
8277 var this$1 = this;
8278
8279 var overlays = this.state.overlays;
8280 for (var i = 0; i < overlays.length; ++i) {
8281 var cur = overlays[i].modeSpec;
8282 if (cur == spec || typeof spec == "string" && cur.name == spec) {
8283 overlays.splice(i, 1);
8284 this$1.state.modeGen++;
8285 regChange(this$1);
8286 return
8287 }
8288 }
8289 }),
8290
8291 indentLine: methodOp(function(n, dir, aggressive) {
8292 if (typeof dir != "string" && typeof dir != "number") {
8293 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
8294 else { dir = dir ? "add" : "subtract"; }
8295 }
8296 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
8297 }),
8298 indentSelection: methodOp(function(how) {
8299 var this$1 = this;
8300
8301 var ranges = this.doc.sel.ranges, end = -1;
8302 for (var i = 0; i < ranges.length; i++) {
8303 var range$$1 = ranges[i];
8304 if (!range$$1.empty()) {
8305 var from = range$$1.from(), to = range$$1.to();
8306 var start = Math.max(end, from.line);
8307 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
8308 for (var j = start; j < end; ++j)
8309 { indentLine(this$1, j, how); }
8310 var newRanges = this$1.doc.sel.ranges;
8311 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8312 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
8313 } else if (range$$1.head.line > end) {
8314 indentLine(this$1, range$$1.head.line, how, true);
8315 end = range$$1.head.line;
8316 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
8317 }
8318 }
8319 }),
8320
8321 // Fetch the parser token for a given character. Useful for hacks
8322 // that want to inspect the mode state (say, for completion).
8323 getTokenAt: function(pos, precise) {
8324 return takeToken(this, pos, precise)
8325 },
8326
8327 getLineTokens: function(line, precise) {
8328 return takeToken(this, Pos(line), precise, true)
8329 },
8330
8331 getTokenTypeAt: function(pos) {
8332 pos = clipPos(this.doc, pos);
8333 var styles = getLineStyles(this, getLine(this.doc, pos.line));
8334 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
8335 var type;
8336 if (ch == 0) { type = styles[2]; }
8337 else { for (;;) {
8338 var mid = (before + after) >> 1;
8339 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
8340 else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
8341 else { type = styles[mid * 2 + 2]; break }
8342 } }
8343 var cut = type ? type.indexOf("overlay ") : -1;
8344 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8345 },
8346
8347 getModeAt: function(pos) {
8348 var mode = this.doc.mode;
8349 if (!mode.innerMode) { return mode }
8350 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8351 },
8352
8353 getHelper: function(pos, type) {
8354 return this.getHelpers(pos, type)[0]
8355 },
8356
8357 getHelpers: function(pos, type) {
8358 var this$1 = this;
8359
8360 var found = [];
8361 if (!helpers.hasOwnProperty(type)) { return found }
8362 var help = helpers[type], mode = this.getModeAt(pos);
8363 if (typeof mode[type] == "string") {
8364 if (help[mode[type]]) { found.push(help[mode[type]]); }
8365 } else if (mode[type]) {
8366 for (var i = 0; i < mode[type].length; i++) {
8367 var val = help[mode[type][i]];
8368 if (val) { found.push(val); }
8369 }
8370 } else if (mode.helperType && help[mode.helperType]) {
8371 found.push(help[mode.helperType]);
8372 } else if (help[mode.name]) {
8373 found.push(help[mode.name]);
8374 }
8375 for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8376 var cur = help._global[i$1];
8377 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8378 { found.push(cur.val); }
8379 }
8380 return found
8381 },
8382
8383 getStateAfter: function(line, precise) {
8384 var doc = this.doc;
8385 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
8386 return getContextBefore(this, line + 1, precise).state
8387 },
8388
8389 cursorCoords: function(start, mode) {
8390 var pos, range$$1 = this.doc.sel.primary();
8391 if (start == null) { pos = range$$1.head; }
8392 else if (typeof start == "object") { pos = clipPos(this.doc, start); }
8393 else { pos = start ? range$$1.from() : range$$1.to(); }
8394 return cursorCoords(this, pos, mode || "page")
8395 },
8396
8397 charCoords: function(pos, mode) {
8398 return charCoords(this, clipPos(this.doc, pos), mode || "page")
8399 },
8400
8401 coordsChar: function(coords, mode) {
8402 coords = fromCoordSystem(this, coords, mode || "page");
8403 return coordsChar(this, coords.left, coords.top)
8404 },
8405
8406 lineAtHeight: function(height, mode) {
8407 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
8408 return lineAtHeight(this.doc, height + this.display.viewOffset)
8409 },
8410 heightAtLine: function(line, mode, includeWidgets) {
8411 var end = false, lineObj;
8412 if (typeof line == "number") {
8413 var last = this.doc.first + this.doc.size - 1;
8414 if (line < this.doc.first) { line = this.doc.first; }
8415 else if (line > last) { line = last; end = true; }
8416 lineObj = getLine(this.doc, line);
8417 } else {
8418 lineObj = line;
8419 }
8420 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8421 (end ? this.doc.height - heightAtLine(lineObj) : 0)
8422 },
8423
8424 defaultTextHeight: function() { return textHeight(this.display) },
8425 defaultCharWidth: function() { return charWidth(this.display) },
8426
8427 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8428
8429 addWidget: function(pos, node, scroll, vert, horiz) {
8430 var display = this.display;
8431 pos = cursorCoords(this, clipPos(this.doc, pos));
8432 var top = pos.bottom, left = pos.left;
8433 node.style.position = "absolute";
8434 node.setAttribute("cm-ignore-events", "true");
8435 this.display.input.setUneditable(node);
8436 display.sizer.appendChild(node);
8437 if (vert == "over") {
8438 top = pos.top;
8439 } else if (vert == "above" || vert == "near") {
8440 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8441 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
8442 // Default to positioning above (if specified and possible); otherwise default to positioning below
8443 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8444 { top = pos.top - node.offsetHeight; }
8445 else if (pos.bottom + node.offsetHeight <= vspace)
8446 { top = pos.bottom; }
8447 if (left + node.offsetWidth > hspace)
8448 { left = hspace - node.offsetWidth; }
8449 }
8450 node.style.top = top + "px";
8451 node.style.left = node.style.right = "";
8452 if (horiz == "right") {
8453 left = display.sizer.clientWidth - node.offsetWidth;
8454 node.style.right = "0px";
8455 } else {
8456 if (horiz == "left") { left = 0; }
8457 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
8458 node.style.left = left + "px";
8459 }
8460 if (scroll)
8461 { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
8462 },
8463
8464 triggerOnKeyDown: methodOp(onKeyDown),
8465 triggerOnKeyPress: methodOp(onKeyPress),
8466 triggerOnKeyUp: onKeyUp,
8467 triggerOnMouseDown: methodOp(onMouseDown),
8468
8469 execCommand: function(cmd) {
8470 if (commands.hasOwnProperty(cmd))
8471 { return commands[cmd].call(null, this) }
8472 },
8473
8474 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
8475
8476 findPosH: function(from, amount, unit, visually) {
8477 var this$1 = this;
8478
8479 var dir = 1;
8480 if (amount < 0) { dir = -1; amount = -amount; }
8481 var cur = clipPos(this.doc, from);
8482 for (var i = 0; i < amount; ++i) {
8483 cur = findPosH(this$1.doc, cur, dir, unit, visually);
8484 if (cur.hitSide) { break }
8485 }
8486 return cur
8487 },
8488
8489 moveH: methodOp(function(dir, unit) {
8490 var this$1 = this;
8491
8492 this.extendSelectionsBy(function (range$$1) {
8493 if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
8494 { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
8495 else
8496 { return dir < 0 ? range$$1.from() : range$$1.to() }
8497 }, sel_move);
8498 }),
8499
8500 deleteH: methodOp(function(dir, unit) {
8501 var sel = this.doc.sel, doc = this.doc;
8502 if (sel.somethingSelected())
8503 { doc.replaceSelection("", null, "+delete"); }
8504 else
8505 { deleteNearSelection(this, function (range$$1) {
8506 var other = findPosH(doc, range$$1.head, dir, unit, false);
8507 return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
8508 }); }
8509 }),
8510
8511 findPosV: function(from, amount, unit, goalColumn) {
8512 var this$1 = this;
8513
8514 var dir = 1, x = goalColumn;
8515 if (amount < 0) { dir = -1; amount = -amount; }
8516 var cur = clipPos(this.doc, from);
8517 for (var i = 0; i < amount; ++i) {
8518 var coords = cursorCoords(this$1, cur, "div");
8519 if (x == null) { x = coords.left; }
8520 else { coords.left = x; }
8521 cur = findPosV(this$1, coords, dir, unit);
8522 if (cur.hitSide) { break }
8523 }
8524 return cur
8525 },
8526
8527 moveV: methodOp(function(dir, unit) {
8528 var this$1 = this;
8529
8530 var doc = this.doc, goals = [];
8531 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
8532 doc.extendSelectionsBy(function (range$$1) {
8533 if (collapse)
8534 { return dir < 0 ? range$$1.from() : range$$1.to() }
8535 var headPos = cursorCoords(this$1, range$$1.head, "div");
8536 if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
8537 goals.push(headPos.left);
8538 var pos = findPosV(this$1, headPos, dir, unit);
8539 if (unit == "page" && range$$1 == doc.sel.primary())
8540 { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
8541 return pos
8542 }, sel_move);
8543 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8544 { doc.sel.ranges[i].goalColumn = goals[i]; } }
8545 }),
8546
8547 // Find the word at the given position (as returned by coordsChar).
8548 findWordAt: function(pos) {
8549 var doc = this.doc, line = getLine(doc, pos.line).text;
8550 var start = pos.ch, end = pos.ch;
8551 if (line) {
8552 var helper = this.getHelper(pos, "wordChars");
8553 if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
8554 var startChar = line.charAt(start);
8555 var check = isWordChar(startChar, helper)
8556 ? function (ch) { return isWordChar(ch, helper); }
8557 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8558 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
8559 while (start > 0 && check(line.charAt(start - 1))) { --start; }
8560 while (end < line.length && check(line.charAt(end))) { ++end; }
8561 }
8562 return new Range(Pos(pos.line, start), Pos(pos.line, end))
8563 },
8564
8565 toggleOverwrite: function(value) {
8566 if (value != null && value == this.state.overwrite) { return }
8567 if (this.state.overwrite = !this.state.overwrite)
8568 { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8569 else
8570 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8571
8572 signal(this, "overwriteToggle", this, this.state.overwrite);
8573 },
8574 hasFocus: function() { return this.display.input.getField() == activeElt() },
8575 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8576
8577 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
8578 getScrollInfo: function() {
8579 var scroller = this.display.scroller;
8580 return {left: scroller.scrollLeft, top: scroller.scrollTop,
8581 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8582 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8583 clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8584 },
8585
8586 scrollIntoView: methodOp(function(range$$1, margin) {
8587 if (range$$1 == null) {
8588 range$$1 = {from: this.doc.sel.primary().head, to: null};
8589 if (margin == null) { margin = this.options.cursorScrollMargin; }
8590 } else if (typeof range$$1 == "number") {
8591 range$$1 = {from: Pos(range$$1, 0), to: null};
8592 } else if (range$$1.from == null) {
8593 range$$1 = {from: range$$1, to: null};
8813 8594 }
8814 }
8815
8816 // W7. Search backwards from each instance of a European number
8817 // until the first strong type (R, L, or sor) is found. If an L is
8818 // found, then change the type of the European number to L.
8819 for (var i = 0, cur = outerType; i < len; ++i) {
8820 var type = types[i];
8821 if (cur == "L" && type == "1") types[i] = "L";
8822 else if (isStrong.test(type)) cur = type;
8823 }
8824
8825 // N1. A sequence of neutrals takes the direction of the
8826 // surrounding strong text if the text on both sides has the same
8827 // direction. European and Arabic numbers act as if they were R in
8828 // terms of their influence on neutrals. Start-of-level-run (sor)
8829 // and end-of-level-run (eor) are used at level run boundaries.
8830 // N2. Any remaining neutrals take the embedding direction.
8831 for (var i = 0; i < len; ++i) {
8832 if (isNeutral.test(types[i])) {
8833 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8834 var before = (i ? types[i-1] : outerType) == "L";
8835 var after = (end < len ? types[end] : outerType) == "L";
8836 var replace = before || after ? "L" : "R";
8837 for (var j = i; j < end; ++j) types[j] = replace;
8838 i = end - 1;
8595 if (!range$$1.to) { range$$1.to = range$$1.from; }
8596 range$$1.margin = margin || 0;
8597
8598 if (range$$1.from.line != null) {
8599 scrollToRange(this, range$$1);
8600 } else {
8601 scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
8602 }
8603 }),
8604
8605 setSize: methodOp(function(width, height) {
8606 var this$1 = this;
8607
8608 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
8609 if (width != null) { this.display.wrapper.style.width = interpret(width); }
8610 if (height != null) { this.display.wrapper.style.height = interpret(height); }
8611 if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
8612 var lineNo$$1 = this.display.viewFrom;
8613 this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8614 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8615 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8616 ++lineNo$$1;
8617 });
8618 this.curOp.forceUpdate = true;
8619 signal(this, "refresh", this);
8620 }),
8621
8622 operation: function(f){return runInOp(this, f)},
8623 startOperation: function(){return startOperation(this)},
8624 endOperation: function(){return endOperation(this)},
8625
8626 refresh: methodOp(function() {
8627 var oldHeight = this.display.cachedTextHeight;
8628 regChange(this);
8629 this.curOp.forceUpdate = true;
8630 clearCaches(this);
8631 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
8632 updateGutterSpace(this.display);
8633 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8634 { estimateLineHeights(this); }
8635 signal(this, "refresh", this);
8636 }),
8637
8638 swapDoc: methodOp(function(doc) {
8639 var old = this.doc;
8640 old.cm = null;
8641 // Cancel the current text selection if any (#5821)
8642 if (this.state.selectingText) { this.state.selectingText(); }
8643 attachDoc(this, doc);
8644 clearCaches(this);
8645 this.display.input.reset();
8646 scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
8647 this.curOp.forceScroll = true;
8648 signalLater(this, "swapDoc", this, old);
8649 return old
8650 }),
8651
8652 phrase: function(phraseText) {
8653 var phrases = this.options.phrases;
8654 return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
8655 },
8656
8657 getInputField: function(){return this.display.input.getField()},
8658 getWrapperElement: function(){return this.display.wrapper},
8659 getScrollerElement: function(){return this.display.scroller},
8660 getGutterElement: function(){return this.display.gutters}
8661 };
8662 eventMixin(CodeMirror);
8663
8664 CodeMirror.registerHelper = function(type, name, value) {
8665 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
8666 helpers[type][name] = value;
8667 };
8668 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8669 CodeMirror.registerHelper(type, name, value);
8670 helpers[type]._global.push({pred: predicate, val: value});
8671 };
8672 }
8673
8674 // Used for horizontal relative motion. Dir is -1 or 1 (left or
8675 // right), unit can be "char", "column" (like char, but doesn't
8676 // cross line boundaries), "word" (across next word), or "group" (to
8677 // the start of next group of word or non-word-non-whitespace
8678 // chars). The visually param controls whether, in right-to-left
8679 // text, direction 1 means to move towards the next index in the
8680 // string, or towards the character to the right of the current
8681 // position. The resulting position will have a hitSide=true
8682 // property if it reached the end of the document.
8683 function findPosH(doc, pos, dir, unit, visually) {
8684 var oldPos = pos;
8685 var origDir = dir;
8686 var lineObj = getLine(doc, pos.line);
8687 function findNextLine() {
8688 var l = pos.line + dir;
8689 if (l < doc.first || l >= doc.first + doc.size) { return false }
8690 pos = new Pos(l, pos.ch, pos.sticky);
8691 return lineObj = getLine(doc, l)
8692 }
8693 function moveOnce(boundToLine) {
8694 var next;
8695 if (visually) {
8696 next = moveVisually(doc.cm, lineObj, pos, dir);
8697 } else {
8698 next = moveLogically(lineObj, pos, dir);
8699 }
8700 if (next == null) {
8701 if (!boundToLine && findNextLine())
8702 { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
8703 else
8704 { return false }
8705 } else {
8706 pos = next;
8707 }
8708 return true
8709 }
8710
8711 if (unit == "char") {
8712 moveOnce();
8713 } else if (unit == "column") {
8714 moveOnce(true);
8715 } else if (unit == "word" || unit == "group") {
8716 var sawType = null, group = unit == "group";
8717 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
8718 for (var first = true;; first = false) {
8719 if (dir < 0 && !moveOnce(!first)) { break }
8720 var cur = lineObj.text.charAt(pos.ch) || "\n";
8721 var type = isWordChar(cur, helper) ? "w"
8722 : group && cur == "\n" ? "n"
8723 : !group || /\s/.test(cur) ? null
8724 : "p";
8725 if (group && !first && !type) { type = "s"; }
8726 if (sawType && sawType != type) {
8727 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
8728 break
8729 }
8730
8731 if (type) { sawType = type; }
8732 if (dir > 0 && !moveOnce(!first)) { break }
8733 }
8734 }
8735 var result = skipAtomic(doc, pos, oldPos, origDir, true);
8736 if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
8737 return result
8738 }
8739
8740 // For relative vertical movement. Dir may be -1 or 1. Unit can be
8741 // "page" or "line". The resulting position will have a hitSide=true
8742 // property if it reached the end of the document.
8743 function findPosV(cm, pos, dir, unit) {
8744 var doc = cm.doc, x = pos.left, y;
8745 if (unit == "page") {
8746 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
8747 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
8748 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
8749
8750 } else if (unit == "line") {
8751 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
8752 }
8753 var target;
8754 for (;;) {
8755 target = coordsChar(cm, x, y);
8756 if (!target.outside) { break }
8757 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8758 y += dir * 5;
8759 }
8760 return target
8761 }
8762
8763 // CONTENTEDITABLE INPUT STYLE
8764
8765 var ContentEditableInput = function(cm) {
8766 this.cm = cm;
8767 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
8768 this.polling = new Delayed();
8769 this.composing = null;
8770 this.gracePeriod = false;
8771 this.readDOMTimeout = null;
8772 };
8773
8774 ContentEditableInput.prototype.init = function (display) {
8775 var this$1 = this;
8776
8777 var input = this, cm = input.cm;
8778 var div = input.div = display.lineDiv;
8779 disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
8780
8781 on(div, "paste", function (e) {
8782 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8783 // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8784 if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
8785 });
8786
8787 on(div, "compositionstart", function (e) {
8788 this$1.composing = {data: e.data, done: false};
8789 });
8790 on(div, "compositionupdate", function (e) {
8791 if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
8792 });
8793 on(div, "compositionend", function (e) {
8794 if (this$1.composing) {
8795 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
8796 this$1.composing.done = true;
8797 }
8798 });
8799
8800 on(div, "touchstart", function () { return input.forceCompositionEnd(); });
8801
8802 on(div, "input", function () {
8803 if (!this$1.composing) { this$1.readFromDOMSoon(); }
8804 });
8805
8806 function onCopyCut(e) {
8807 if (signalDOMEvent(cm, e)) { return }
8808 if (cm.somethingSelected()) {
8809 setLastCopied({lineWise: false, text: cm.getSelections()});
8810 if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
8811 } else if (!cm.options.lineWiseCopyCut) {
8812 return
8813 } else {
8814 var ranges = copyableRanges(cm);
8815 setLastCopied({lineWise: true, text: ranges.text});
8816 if (e.type == "cut") {
8817 cm.operation(function () {
8818 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
8819 cm.replaceSelection("", null, "cut");
8820 });
8821 }
8822 }
8823 if (e.clipboardData) {
8824 e.clipboardData.clearData();
8825 var content = lastCopied.text.join("\n");
8826 // iOS exposes the clipboard API, but seems to discard content inserted into it
8827 e.clipboardData.setData("Text", content);
8828 if (e.clipboardData.getData("Text") == content) {
8829 e.preventDefault();
8830 return
8831 }
8832 }
8833 // Old-fashioned briefly-focus-a-textarea hack
8834 var kludge = hiddenTextarea(), te = kludge.firstChild;
8835 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
8836 te.value = lastCopied.text.join("\n");
8837 var hadFocus = document.activeElement;
8838 selectInput(te);
8839 setTimeout(function () {
8840 cm.display.lineSpace.removeChild(kludge);
8841 hadFocus.focus();
8842 if (hadFocus == div) { input.showPrimarySelection(); }
8843 }, 50);
8844 }
8845 on(div, "copy", onCopyCut);
8846 on(div, "cut", onCopyCut);
8847 };
8848
8849 ContentEditableInput.prototype.prepareSelection = function () {
8850 var result = prepareSelection(this.cm, false);
8851 result.focus = this.cm.state.focused;
8852 return result
8853 };
8854
8855 ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8856 if (!info || !this.cm.display.view.length) { return }
8857 if (info.focus || takeFocus) { this.showPrimarySelection(); }
8858 this.showMultipleSelections(info);
8859 };
8860
8861 ContentEditableInput.prototype.getSelection = function () {
8862 return this.cm.display.wrapper.ownerDocument.getSelection()
8863 };
8864
8865 ContentEditableInput.prototype.showPrimarySelection = function () {
8866 var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
8867 var from = prim.from(), to = prim.to();
8868
8869 if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8870 sel.removeAllRanges();
8871 return
8872 }
8873
8874 var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8875 var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
8876 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8877 cmp(minPos(curAnchor, curFocus), from) == 0 &&
8878 cmp(maxPos(curAnchor, curFocus), to) == 0)
8879 { return }
8880
8881 var view = cm.display.view;
8882 var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8883 {node: view[0].measure.map[2], offset: 0};
8884 var end = to.line < cm.display.viewTo && posToDOM(cm, to);
8885 if (!end) {
8886 var measure = view[view.length - 1].measure;
8887 var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
8888 end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
8889 }
8890
8891 if (!start || !end) {
8892 sel.removeAllRanges();
8893 return
8894 }
8895
8896 var old = sel.rangeCount && sel.getRangeAt(0), rng;
8897 try { rng = range(start.node, start.offset, end.offset, end.node); }
8898 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8899 if (rng) {
8900 if (!gecko && cm.state.focused) {
8901 sel.collapse(start.node, start.offset);
8902 if (!rng.collapsed) {
8903 sel.removeAllRanges();
8904 sel.addRange(rng);
8905 }
8906 } else {
8907 sel.removeAllRanges();
8908 sel.addRange(rng);
8909 }
8910 if (old && sel.anchorNode == null) { sel.addRange(old); }
8911 else if (gecko) { this.startGracePeriod(); }
8912 }
8913 this.rememberSelection();
8914 };
8915
8916 ContentEditableInput.prototype.startGracePeriod = function () {
8917 var this$1 = this;
8918
8919 clearTimeout(this.gracePeriod);
8920 this.gracePeriod = setTimeout(function () {
8921 this$1.gracePeriod = false;
8922 if (this$1.selectionChanged())
8923 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
8924 }, 20);
8925 };
8926
8927 ContentEditableInput.prototype.showMultipleSelections = function (info) {
8928 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
8929 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
8930 };
8931
8932 ContentEditableInput.prototype.rememberSelection = function () {
8933 var sel = this.getSelection();
8934 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
8935 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
8936 };
8937
8938 ContentEditableInput.prototype.selectionInEditor = function () {
8939 var sel = this.getSelection();
8940 if (!sel.rangeCount) { return false }
8941 var node = sel.getRangeAt(0).commonAncestorContainer;
8942 return contains(this.div, node)
8943 };
8944
8945 ContentEditableInput.prototype.focus = function () {
8946 if (this.cm.options.readOnly != "nocursor") {
8947 if (!this.selectionInEditor())
8948 { this.showSelection(this.prepareSelection(), true); }
8949 this.div.focus();
8950 }
8951 };
8952 ContentEditableInput.prototype.blur = function () { this.div.blur(); };
8953 ContentEditableInput.prototype.getField = function () { return this.div };
8954
8955 ContentEditableInput.prototype.supportsTouch = function () { return true };
8956
8957 ContentEditableInput.prototype.receivedFocus = function () {
8958 var input = this;
8959 if (this.selectionInEditor())
8960 { this.pollSelection(); }
8961 else
8962 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
8963
8964 function poll() {
8965 if (input.cm.state.focused) {
8966 input.pollSelection();
8967 input.polling.set(input.cm.options.pollInterval, poll);
8968 }
8969 }
8970 this.polling.set(this.cm.options.pollInterval, poll);
8971 };
8972
8973 ContentEditableInput.prototype.selectionChanged = function () {
8974 var sel = this.getSelection();
8975 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8976 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8977 };
8978
8979 ContentEditableInput.prototype.pollSelection = function () {
8980 if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8981 var sel = this.getSelection(), cm = this.cm;
8982 // On Android Chrome (version 56, at least), backspacing into an
8983 // uneditable block element will put the cursor in that element,
8984 // and then, because it's not editable, hide the virtual keyboard.
8985 // Because Android doesn't allow us to actually detect backspace
8986 // presses in a sane way, this code checks for when that happens
8987 // and simulates a backspace press in this case.
8988 if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
8989 this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
8990 this.blur();
8991 this.focus();
8992 return
8993 }
8994 if (this.composing) { return }
8995 this.rememberSelection();
8996 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8997 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
8998 if (anchor && head) { runInOp(cm, function () {
8999 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
9000 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
9001 }); }
9002 };
9003
9004 ContentEditableInput.prototype.pollContent = function () {
9005 if (this.readDOMTimeout != null) {
9006 clearTimeout(this.readDOMTimeout);
9007 this.readDOMTimeout = null;
9008 }
9009
9010 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
9011 var from = sel.from(), to = sel.to();
9012 if (from.ch == 0 && from.line > cm.firstLine())
9013 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
9014 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
9015 { to = Pos(to.line + 1, 0); }
9016 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
9017
9018 var fromIndex, fromLine, fromNode;
9019 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
9020 fromLine = lineNo(display.view[0].line);
9021 fromNode = display.view[0].node;
9022 } else {
9023 fromLine = lineNo(display.view[fromIndex].line);
9024 fromNode = display.view[fromIndex - 1].node.nextSibling;
9025 }
9026 var toIndex = findViewIndex(cm, to.line);
9027 var toLine, toNode;
9028 if (toIndex == display.view.length - 1) {
9029 toLine = display.viewTo - 1;
9030 toNode = display.lineDiv.lastChild;
9031 } else {
9032 toLine = lineNo(display.view[toIndex + 1].line) - 1;
9033 toNode = display.view[toIndex + 1].node.previousSibling;
9034 }
9035
9036 if (!fromNode) { return false }
9037 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
9038 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
9039 while (newText.length > 1 && oldText.length > 1) {
9040 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
9041 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
9042 else { break }
9043 }
9044
9045 var cutFront = 0, cutEnd = 0;
9046 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
9047 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
9048 { ++cutFront; }
9049 var newBot = lst(newText), oldBot = lst(oldText);
9050 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
9051 oldBot.length - (oldText.length == 1 ? cutFront : 0));
9052 while (cutEnd < maxCutEnd &&
9053 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
9054 { ++cutEnd; }
9055 // Try to move start of change to start of selection if ambiguous
9056 if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
9057 while (cutFront && cutFront > from.ch &&
9058 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
9059 cutFront--;
9060 cutEnd++;
9061 }
9062 }
9063
9064 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
9065 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
9066
9067 var chFrom = Pos(fromLine, cutFront);
9068 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
9069 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
9070 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
9071 return true
9072 }
9073 };
9074
9075 ContentEditableInput.prototype.ensurePolled = function () {
9076 this.forceCompositionEnd();
9077 };
9078 ContentEditableInput.prototype.reset = function () {
9079 this.forceCompositionEnd();
9080 };
9081 ContentEditableInput.prototype.forceCompositionEnd = function () {
9082 if (!this.composing) { return }
9083 clearTimeout(this.readDOMTimeout);
9084 this.composing = null;
9085 this.updateFromDOM();
9086 this.div.blur();
9087 this.div.focus();
9088 };
9089 ContentEditableInput.prototype.readFromDOMSoon = function () {
9090 var this$1 = this;
9091
9092 if (this.readDOMTimeout != null) { return }
9093 this.readDOMTimeout = setTimeout(function () {
9094 this$1.readDOMTimeout = null;
9095 if (this$1.composing) {
9096 if (this$1.composing.done) { this$1.composing = null; }
9097 else { return }
9098 }
9099 this$1.updateFromDOM();
9100 }, 80);
9101 };
9102
9103 ContentEditableInput.prototype.updateFromDOM = function () {
9104 var this$1 = this;
9105
9106 if (this.cm.isReadOnly() || !this.pollContent())
9107 { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
9108 };
9109
9110 ContentEditableInput.prototype.setUneditable = function (node) {
9111 node.contentEditable = "false";
9112 };
9113
9114 ContentEditableInput.prototype.onKeyPress = function (e) {
9115 if (e.charCode == 0 || this.composing) { return }
9116 e.preventDefault();
9117 if (!this.cm.isReadOnly())
9118 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
9119 };
9120
9121 ContentEditableInput.prototype.readOnlyChanged = function (val) {
9122 this.div.contentEditable = String(val != "nocursor");
9123 };
9124
9125 ContentEditableInput.prototype.onContextMenu = function () {};
9126 ContentEditableInput.prototype.resetPosition = function () {};
9127
9128 ContentEditableInput.prototype.needsContentAttribute = true;
9129
9130 function posToDOM(cm, pos) {
9131 var view = findViewForLine(cm, pos.line);
9132 if (!view || view.hidden) { return null }
9133 var line = getLine(cm.doc, pos.line);
9134 var info = mapFromLineView(view, line, pos.line);
9135
9136 var order = getOrder(line, cm.doc.direction), side = "left";
9137 if (order) {
9138 var partPos = getBidiPartAt(order, pos.ch);
9139 side = partPos % 2 ? "right" : "left";
9140 }
9141 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
9142 result.offset = result.collapse == "right" ? result.end : result.start;
9143 return result
9144 }
9145
9146 function isInGutter(node) {
9147 for (var scan = node; scan; scan = scan.parentNode)
9148 { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9149 return false
9150 }
9151
9152 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9153
9154 function domTextBetween(cm, from, to, fromLine, toLine) {
9155 var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
9156 function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9157 function close() {
9158 if (closing) {
9159 text += lineSep;
9160 if (extraLinebreak) { text += lineSep; }
9161 closing = extraLinebreak = false;
9162 }
9163 }
9164 function addText(str) {
9165 if (str) {
9166 close();
9167 text += str;
9168 }
9169 }
9170 function walk(node) {
9171 if (node.nodeType == 1) {
9172 var cmText = node.getAttribute("cm-text");
9173 if (cmText) {
9174 addText(cmText);
9175 return
9176 }
9177 var markerID = node.getAttribute("cm-marker"), range$$1;
9178 if (markerID) {
9179 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
9180 if (found.length && (range$$1 = found[0].find(0)))
9181 { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
9182 return
8839 9183 }
8840 }
8841
8842 // Here we depart from the documented algorithm, in order to avoid
8843 // building up an actual levels array. Since there are only three
8844 // levels (0, 1, 2) in an implementation that doesn't take
8845 // explicit embedding into account, we can build up the order on
8846 // the fly, without following the level-based algorithm.
8847 var order = [], m;
8848 for (var i = 0; i < len;) {
8849 if (countsAsLeft.test(types[i])) {
8850 var start = i;
8851 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8852 order.push(new BidiSpan(0, start, i));
9184 if (node.getAttribute("contenteditable") == "false") { return }
9185 var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
9186 if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
9187
9188 if (isBlock) { close(); }
9189 for (var i = 0; i < node.childNodes.length; i++)
9190 { walk(node.childNodes[i]); }
9191
9192 if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
9193 if (isBlock) { closing = true; }
9194 } else if (node.nodeType == 3) {
9195 addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
9196 }
9197 }
9198 for (;;) {
9199 walk(from);
9200 if (from == to) { break }
9201 from = from.nextSibling;
9202 extraLinebreak = false;
9203 }
9204 return text
9205 }
9206
9207 function domToPos(cm, node, offset) {
9208 var lineNode;
9209 if (node == cm.display.lineDiv) {
9210 lineNode = cm.display.lineDiv.childNodes[offset];
9211 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9212 node = null; offset = 0;
9213 } else {
9214 for (lineNode = node;; lineNode = lineNode.parentNode) {
9215 if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9216 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9217 }
9218 }
9219 for (var i = 0; i < cm.display.view.length; i++) {
9220 var lineView = cm.display.view[i];
9221 if (lineView.node == lineNode)
9222 { return locateNodeInLineView(lineView, node, offset) }
9223 }
9224 }
9225
9226 function locateNodeInLineView(lineView, node, offset) {
9227 var wrapper = lineView.text.firstChild, bad = false;
9228 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9229 if (node == wrapper) {
9230 bad = true;
9231 node = wrapper.childNodes[offset];
9232 offset = 0;
9233 if (!node) {
9234 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
9235 return badPos(Pos(lineNo(line), line.text.length), bad)
9236 }
9237 }
9238
9239 var textNode = node.nodeType == 3 ? node : null, topNode = node;
9240 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9241 textNode = node.firstChild;
9242 if (offset) { offset = textNode.nodeValue.length; }
9243 }
9244 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
9245 var measure = lineView.measure, maps = measure.maps;
9246
9247 function find(textNode, topNode, offset) {
9248 for (var i = -1; i < (maps ? maps.length : 0); i++) {
9249 var map$$1 = i < 0 ? measure.map : maps[i];
9250 for (var j = 0; j < map$$1.length; j += 3) {
9251 var curNode = map$$1[j + 2];
9252 if (curNode == textNode || curNode == topNode) {
9253 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
9254 var ch = map$$1[j] + offset;
9255 if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
9256 return Pos(line, ch)
9257 }
9258 }
9259 }
9260 }
9261 var found = find(textNode, topNode, offset);
9262 if (found) { return badPos(found, bad) }
9263
9264 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9265 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9266 found = find(after, after.firstChild, 0);
9267 if (found)
9268 { return badPos(Pos(found.line, found.ch - dist), bad) }
9269 else
9270 { dist += after.textContent.length; }
9271 }
9272 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9273 found = find(before, before.firstChild, -1);
9274 if (found)
9275 { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9276 else
9277 { dist$1 += before.textContent.length; }
9278 }
9279 }
9280
9281 // TEXTAREA INPUT STYLE
9282
9283 var TextareaInput = function(cm) {
9284 this.cm = cm;
9285 // See input.poll and input.reset
9286 this.prevInput = "";
9287
9288 // Flag that indicates whether we expect input to appear real soon
9289 // now (after some event like 'keypress' or 'input') and are
9290 // polling intensively.
9291 this.pollingFast = false;
9292 // Self-resetting timeout for the poller
9293 this.polling = new Delayed();
9294 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9295 this.hasSelection = false;
9296 this.composing = null;
9297 };
9298
9299 TextareaInput.prototype.init = function (display) {
9300 var this$1 = this;
9301
9302 var input = this, cm = this.cm;
9303 this.createField(display);
9304 var te = this.textarea;
9305
9306 display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
9307
9308 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9309 if (ios) { te.style.width = "0px"; }
9310
9311 on(te, "input", function () {
9312 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
9313 input.poll();
9314 });
9315
9316 on(te, "paste", function (e) {
9317 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9318
9319 cm.state.pasteIncoming = +new Date;
9320 input.fastPoll();
9321 });
9322
9323 function prepareCopyCut(e) {
9324 if (signalDOMEvent(cm, e)) { return }
9325 if (cm.somethingSelected()) {
9326 setLastCopied({lineWise: false, text: cm.getSelections()});
9327 } else if (!cm.options.lineWiseCopyCut) {
9328 return
9329 } else {
9330 var ranges = copyableRanges(cm);
9331 setLastCopied({lineWise: true, text: ranges.text});
9332 if (e.type == "cut") {
9333 cm.setSelections(ranges.ranges, null, sel_dontScroll);
8853 9334 } else {
8854 var pos = i, at = order.length;
8855 for (++i; i < len && types[i] != "L"; ++i) {}
8856 for (var j = pos; j < i;) {
8857 if (countsAsNum.test(types[j])) {
8858 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8859 var nstart = j;
8860 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8861 order.splice(at, 0, new BidiSpan(2, nstart, j));
8862 pos = j;
8863 } else ++j;
8864 }
8865 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
9335 input.prevInput = "";
9336 te.value = ranges.text.join("\n");
9337 selectInput(te);
8866 9338 }
8867 9339 }
8868 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8869 order[0].from = m[0].length;
8870 order.unshift(new BidiSpan(0, 0, m[0].length));
8871 }
8872 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8873 lst(order).to -= m[0].length;
8874 order.push(new BidiSpan(0, len - m[0].length, len));
8875 }
8876 if (order[0].level == 2)
8877 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8878 if (order[0].level != lst(order).level)
8879 order.push(new BidiSpan(order[0].level, len, len));
8880
8881 return order;
9340 if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
9341 }
9342 on(te, "cut", prepareCopyCut);
9343 on(te, "copy", prepareCopyCut);
9344
9345 on(display.scroller, "paste", function (e) {
9346 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9347 if (!te.dispatchEvent) {
9348 cm.state.pasteIncoming = +new Date;
9349 input.focus();
9350 return
9351 }
9352
9353 // Pass the `paste` event to the textarea so it's handled by its event listener.
9354 var event = new Event("paste");
9355 event.clipboardData = e.clipboardData;
9356 te.dispatchEvent(event);
9357 });
9358
9359 // Prevent normal selection in the editor (we handle our own)
9360 on(display.lineSpace, "selectstart", function (e) {
9361 if (!eventInWidget(display, e)) { e_preventDefault(e); }
9362 });
9363
9364 on(te, "compositionstart", function () {
9365 var start = cm.getCursor("from");
9366 if (input.composing) { input.composing.range.clear(); }
9367 input.composing = {
9368 start: start,
9369 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9370 };
9371 });
9372 on(te, "compositionend", function () {
9373 if (input.composing) {
9374 input.poll();
9375 input.composing.range.clear();
9376 input.composing = null;
9377 }
9378 });
9379 };
9380
9381 TextareaInput.prototype.createField = function (_display) {
9382 // Wraps and hides input textarea
9383 this.wrapper = hiddenTextarea();
9384 // The semihidden textarea that is focused when the editor is
9385 // focused, and receives input.
9386 this.textarea = this.wrapper.firstChild;
9387 };
9388
9389 TextareaInput.prototype.prepareSelection = function () {
9390 // Redraw the selection and/or cursor
9391 var cm = this.cm, display = cm.display, doc = cm.doc;
9392 var result = prepareSelection(cm);
9393
9394 // Move the hidden textarea near the cursor to prevent scrolling artifacts
9395 if (cm.options.moveInputWithCursor) {
9396 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
9397 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
9398 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9399 headPos.top + lineOff.top - wrapOff.top));
9400 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9401 headPos.left + lineOff.left - wrapOff.left));
9402 }
9403
9404 return result
9405 };
9406
9407 TextareaInput.prototype.showSelection = function (drawn) {
9408 var cm = this.cm, display = cm.display;
9409 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
9410 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
9411 if (drawn.teTop != null) {
9412 this.wrapper.style.top = drawn.teTop + "px";
9413 this.wrapper.style.left = drawn.teLeft + "px";
9414 }
9415 };
9416
9417 // Reset the input to correspond to the selection (or to be empty,
9418 // when not typing and nothing is selected)
9419 TextareaInput.prototype.reset = function (typing) {
9420 if (this.contextMenuPending || this.composing) { return }
9421 var cm = this.cm;
9422 if (cm.somethingSelected()) {
9423 this.prevInput = "";
9424 var content = cm.getSelection();
9425 this.textarea.value = content;
9426 if (cm.state.focused) { selectInput(this.textarea); }
9427 if (ie && ie_version >= 9) { this.hasSelection = content; }
9428 } else if (!typing) {
9429 this.prevInput = this.textarea.value = "";
9430 if (ie && ie_version >= 9) { this.hasSelection = null; }
9431 }
9432 };
9433
9434 TextareaInput.prototype.getField = function () { return this.textarea };
9435
9436 TextareaInput.prototype.supportsTouch = function () { return false };
9437
9438 TextareaInput.prototype.focus = function () {
9439 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9440 try { this.textarea.focus(); }
9441 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9442 }
9443 };
9444
9445 TextareaInput.prototype.blur = function () { this.textarea.blur(); };
9446
9447 TextareaInput.prototype.resetPosition = function () {
9448 this.wrapper.style.top = this.wrapper.style.left = 0;
9449 };
9450
9451 TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
9452
9453 // Poll for input changes, using the normal rate of polling. This
9454 // runs as long as the editor is focused.
9455 TextareaInput.prototype.slowPoll = function () {
9456 var this$1 = this;
9457
9458 if (this.pollingFast) { return }
9459 this.polling.set(this.cm.options.pollInterval, function () {
9460 this$1.poll();
9461 if (this$1.cm.state.focused) { this$1.slowPoll(); }
9462 });
9463 };
9464
9465 // When an event has just come in that is likely to add or change
9466 // something in the input textarea, we poll faster, to ensure that
9467 // the change appears on the screen quickly.
9468 TextareaInput.prototype.fastPoll = function () {
9469 var missed = false, input = this;
9470 input.pollingFast = true;
9471 function p() {
9472 var changed = input.poll();
9473 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
9474 else {input.pollingFast = false; input.slowPoll();}
9475 }
9476 input.polling.set(20, p);
9477 };
9478
9479 // Read input from the textarea, and update the document to match.
9480 // When something is selected, it is present in the textarea, and
9481 // selected (unless it is huge, in which case a placeholder is
9482 // used). When nothing is selected, the cursor sits after previously
9483 // seen text (can be empty), which is stored in prevInput (we must
9484 // not reset the textarea when typing, because that breaks IME).
9485 TextareaInput.prototype.poll = function () {
9486 var this$1 = this;
9487
9488 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
9489 // Since this is called a *lot*, try to bail out as cheaply as
9490 // possible when it is clear that nothing happened. hasSelection
9491 // will be the case when there is a lot of text in the textarea,
9492 // in which case reading its value would be expensive.
9493 if (this.contextMenuPending || !cm.state.focused ||
9494 (hasSelection(input) && !prevInput && !this.composing) ||
9495 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9496 { return false }
9497
9498 var text = input.value;
9499 // If nothing changed, bail.
9500 if (text == prevInput && !cm.somethingSelected()) { return false }
9501 // Work around nonsensical selection resetting in IE9/10, and
9502 // inexplicable appearance of private area unicode characters on
9503 // some key combos in Mac (#2689).
9504 if (ie && ie_version >= 9 && this.hasSelection === text ||
9505 mac && /[\uf700-\uf7ff]/.test(text)) {
9506 cm.display.input.reset();
9507 return false
9508 }
9509
9510 if (cm.doc.sel == cm.display.selForContextMenu) {
9511 var first = text.charCodeAt(0);
9512 if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
9513 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9514 }
9515 // Find the part of the input that is actually new
9516 var same = 0, l = Math.min(prevInput.length, text.length);
9517 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
9518
9519 runInOp(cm, function () {
9520 applyTextInput(cm, text.slice(same), prevInput.length - same,
9521 null, this$1.composing ? "*compose" : null);
9522
9523 // Don't leave long text in the textarea, since it makes further polling slow
9524 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
9525 else { this$1.prevInput = text; }
9526
9527 if (this$1.composing) {
9528 this$1.composing.range.clear();
9529 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9530 {className: "CodeMirror-composing"});
9531 }
9532 });
9533 return true
9534 };
9535
9536 TextareaInput.prototype.ensurePolled = function () {
9537 if (this.pollingFast && this.poll()) { this.pollingFast = false; }
9538 };
9539
9540 TextareaInput.prototype.onKeyPress = function () {
9541 if (ie && ie_version >= 9) { this.hasSelection = null; }
9542 this.fastPoll();
9543 };
9544
9545 TextareaInput.prototype.onContextMenu = function (e) {
9546 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
9547 if (input.contextMenuPending) { input.contextMenuPending(); }
9548 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
9549 if (!pos || presto) { return } // Opera is difficult.
9550
9551 // Reset the current text selection only if the click is done outside of the selection
9552 // and 'resetSelectionOnContextMenu' option is true.
9553 var reset = cm.options.resetSelectionOnContextMenu;
9554 if (reset && cm.doc.sel.contains(pos) == -1)
9555 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
9556
9557 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
9558 var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
9559 input.wrapper.style.cssText = "position: static";
9560 te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
9561 var oldScrollY;
9562 if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
9563 display.input.focus();
9564 if (webkit) { window.scrollTo(null, oldScrollY); }
9565 display.input.reset();
9566 // Adds "Select all" to context menu in FF
9567 if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
9568 input.contextMenuPending = rehide;
9569 display.selForContextMenu = cm.doc.sel;
9570 clearTimeout(display.detectingSelectAll);
9571
9572 // Select-all will be greyed out if there's nothing to select, so
9573 // this adds a zero-width space so that we can later check whether
9574 // it got selected.
9575 function prepareSelectAllHack() {
9576 if (te.selectionStart != null) {
9577 var selected = cm.somethingSelected();
9578 var extval = "\u200b" + (selected ? te.value : "");
9579 te.value = "\u21da"; // Used to catch context-menu undo
9580 te.value = extval;
9581 input.prevInput = selected ? "" : "\u200b";
9582 te.selectionStart = 1; te.selectionEnd = extval.length;
9583 // Re-set this, in case some other handler touched the
9584 // selection in the meantime.
9585 display.selForContextMenu = cm.doc.sel;
9586 }
9587 }
9588 function rehide() {
9589 if (input.contextMenuPending != rehide) { return }
9590 input.contextMenuPending = false;
9591 input.wrapper.style.cssText = oldWrapperCSS;
9592 te.style.cssText = oldCSS;
9593 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
9594
9595 // Try to detect the user choosing select-all
9596 if (te.selectionStart != null) {
9597 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
9598 var i = 0, poll = function () {
9599 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9600 te.selectionEnd > 0 && input.prevInput == "\u200b") {
9601 operation(cm, selectAll)(cm);
9602 } else if (i++ < 10) {
9603 display.detectingSelectAll = setTimeout(poll, 500);
9604 } else {
9605 display.selForContextMenu = null;
9606 display.input.reset();
9607 }
9608 };
9609 display.detectingSelectAll = setTimeout(poll, 200);
9610 }
9611 }
9612
9613 if (ie && ie_version >= 9) { prepareSelectAllHack(); }
9614 if (captureRightClick) {
9615 e_stop(e);
9616 var mouseup = function () {
9617 off(window, "mouseup", mouseup);
9618 setTimeout(rehide, 20);
9619 };
9620 on(window, "mouseup", mouseup);
9621 } else {
9622 setTimeout(rehide, 50);
9623 }
9624 };
9625
9626 TextareaInput.prototype.readOnlyChanged = function (val) {
9627 if (!val) { this.reset(); }
9628 this.textarea.disabled = val == "nocursor";
9629 };
9630
9631 TextareaInput.prototype.setUneditable = function () {};
9632
9633 TextareaInput.prototype.needsContentAttribute = false;
9634
9635 function fromTextArea(textarea, options) {
9636 options = options ? copyObj(options) : {};
9637 options.value = textarea.value;
9638 if (!options.tabindex && textarea.tabIndex)
9639 { options.tabindex = textarea.tabIndex; }
9640 if (!options.placeholder && textarea.placeholder)
9641 { options.placeholder = textarea.placeholder; }
9642 // Set autofocus to true if this textarea is focused, or if it has
9643 // autofocus and no other element is focused.
9644 if (options.autofocus == null) {
9645 var hasFocus = activeElt();
9646 options.autofocus = hasFocus == textarea ||
9647 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
9648 }
9649
9650 function save() {textarea.value = cm.getValue();}
9651
9652 var realSubmit;
9653 if (textarea.form) {
9654 on(textarea.form, "submit", save);
9655 // Deplorable hack to make the submit method do the right thing.
9656 if (!options.leaveSubmitMethodAlone) {
9657 var form = textarea.form;
9658 realSubmit = form.submit;
9659 try {
9660 var wrappedSubmit = form.submit = function () {
9661 save();
9662 form.submit = realSubmit;
9663 form.submit();
9664 form.submit = wrappedSubmit;
9665 };
9666 } catch(e) {}
9667 }
9668 }
9669
9670 options.finishInit = function (cm) {
9671 cm.save = save;
9672 cm.getTextArea = function () { return textarea; };
9673 cm.toTextArea = function () {
9674 cm.toTextArea = isNaN; // Prevent this from being ran twice
9675 save();
9676 textarea.parentNode.removeChild(cm.getWrapperElement());
9677 textarea.style.display = "";
9678 if (textarea.form) {
9679 off(textarea.form, "submit", save);
9680 if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
9681 { textarea.form.submit = realSubmit; }
9682 }
9683 };
8882 9684 };
8883 })();
8884
8885 // THE END
8886
8887 CodeMirror.version = "5.11.0";
9685
9686 textarea.style.display = "none";
9687 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9688 options);
9689 return cm
9690 }
9691
9692 function addLegacyProps(CodeMirror) {
9693 CodeMirror.off = off;
9694 CodeMirror.on = on;
9695 CodeMirror.wheelEventPixels = wheelEventPixels;
9696 CodeMirror.Doc = Doc;
9697 CodeMirror.splitLines = splitLinesAuto;
9698 CodeMirror.countColumn = countColumn;
9699 CodeMirror.findColumn = findColumn;
9700 CodeMirror.isWordChar = isWordCharBasic;
9701 CodeMirror.Pass = Pass;
9702 CodeMirror.signal = signal;
9703 CodeMirror.Line = Line;
9704 CodeMirror.changeEnd = changeEnd;
9705 CodeMirror.scrollbarModel = scrollbarModel;
9706 CodeMirror.Pos = Pos;
9707 CodeMirror.cmpPos = cmp;
9708 CodeMirror.modes = modes;
9709 CodeMirror.mimeModes = mimeModes;
9710 CodeMirror.resolveMode = resolveMode;
9711 CodeMirror.getMode = getMode;
9712 CodeMirror.modeExtensions = modeExtensions;
9713 CodeMirror.extendMode = extendMode;
9714 CodeMirror.copyState = copyState;
9715 CodeMirror.startState = startState;
9716 CodeMirror.innerMode = innerMode;
9717 CodeMirror.commands = commands;
9718 CodeMirror.keyMap = keyMap;
9719 CodeMirror.keyName = keyName;
9720 CodeMirror.isModifierKey = isModifierKey;
9721 CodeMirror.lookupKey = lookupKey;
9722 CodeMirror.normalizeKeyMap = normalizeKeyMap;
9723 CodeMirror.StringStream = StringStream;
9724 CodeMirror.SharedTextMarker = SharedTextMarker;
9725 CodeMirror.TextMarker = TextMarker;
9726 CodeMirror.LineWidget = LineWidget;
9727 CodeMirror.e_preventDefault = e_preventDefault;
9728 CodeMirror.e_stopPropagation = e_stopPropagation;
9729 CodeMirror.e_stop = e_stop;
9730 CodeMirror.addClass = addClass;
9731 CodeMirror.contains = contains;
9732 CodeMirror.rmClass = rmClass;
9733 CodeMirror.keyNames = keyNames;
9734 }
9735
9736 // EDITOR CONSTRUCTOR
9737
9738 defineOptions(CodeMirror);
9739
9740 addEditorMethods(CodeMirror);
9741
9742 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9743 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
9744 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9745 { CodeMirror.prototype[prop] = (function(method) {
9746 return function() {return method.apply(this.doc, arguments)}
9747 })(Doc.prototype[prop]); } }
9748
9749 eventMixin(Doc);
9750 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
9751
9752 // Extra arguments are stored as the mode's dependencies, which is
9753 // used by (legacy) mechanisms like loadmode.js to automatically
9754 // load a mode. (Preferred mechanism is the require/define calls.)
9755 CodeMirror.defineMode = function(name/*, mode, …*/) {
9756 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
9757 defineMode.apply(this, arguments);
9758 };
9759
9760 CodeMirror.defineMIME = defineMIME;
9761
9762 // Minimal default mode.
9763 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
9764 CodeMirror.defineMIME("text/plain", "null");
9765
9766 // EXTENSIONS
9767
9768 CodeMirror.defineExtension = function (name, func) {
9769 CodeMirror.prototype[name] = func;
9770 };
9771 CodeMirror.defineDocExtension = function (name, func) {
9772 Doc.prototype[name] = func;
9773 };
9774
9775 CodeMirror.fromTextArea = fromTextArea;
9776
9777 addLegacyProps(CodeMirror);
9778
9779 CodeMirror.version = "5.49.2";
8888 9780
8889 9781 return CodeMirror;
8890 });
9782
9783 })));
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -46,6 +46,10 b''
46 46 completion.update(true);
47 47 });
48 48
49 CodeMirror.defineExtension("closeHint", function() {
50 if (this.state.completionActive) this.state.completionActive.close()
51 })
52
49 53 function Completion(cm, options) {
50 54 this.cm = cm;
51 55 this.options = options;
@@ -98,7 +102,7 b''
98 102 var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
99 103 if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
100 104 pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
101 (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
105 (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
102 106 this.close();
103 107 } else {
104 108 var self = this;
@@ -108,24 +112,21 b''
108 112 },
109 113
110 114 update: function(first) {
111 if (this.tick == null) return;
112 if (!this.options.hint.async) {
113 this.finishUpdate(this.options.hint(this.cm, this.options), first);
114 } else {
115 var myTick = ++this.tick, self = this;
116 this.options.hint(this.cm, function(data) {
117 if (self.tick == myTick) self.finishUpdate(data, first);
118 }, this.options);
119 }
115 if (this.tick == null) return
116 var self = this, myTick = ++this.tick
117 fetchHints(this.options.hint, this.cm, this.options, function(data) {
118 if (self.tick == myTick) self.finishUpdate(data, first)
119 })
120 120 },
121 121
122 122 finishUpdate: function(data, first) {
123 123 if (this.data) CodeMirror.signal(this.data, "update");
124 if (data && this.data && CodeMirror.cmpPos(data.from, this.data.from)) data = null;
125 this.data = data;
126 124
127 125 var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
128 126 if (this.widget) this.widget.close();
127
128 this.data = data;
129
129 130 if (data && data.list.length) {
130 131 if (picked && data.list.length == 1) {
131 132 this.pick(data, 0);
@@ -166,6 +167,14 b''
166 167 Tab: handle.pick,
167 168 Esc: handle.close
168 169 };
170
171 var mac = /Mac/.test(navigator.platform);
172
173 if (mac) {
174 baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
175 baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
176 }
177
169 178 var custom = completion.options.customKeys;
170 179 var ourMap = custom ? {} : baseMap;
171 180 function addBinding(key, val) {
@@ -201,43 +210,61 b''
201 210 this.data = data;
202 211 this.picked = false;
203 212 var widget = this, cm = completion.cm;
213 var ownerDocument = cm.getInputField().ownerDocument;
214 var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
204 215
205 var hints = this.hints = document.createElement("ul");
206 hints.className = "CodeMirror-hints";
216 var hints = this.hints = ownerDocument.createElement("ul");
217 var theme = completion.cm.options.theme;
218 hints.className = "CodeMirror-hints " + theme;
207 219 this.selectedHint = data.selectedHint || 0;
208 220
209 221 var completions = data.list;
210 222 for (var i = 0; i < completions.length; ++i) {
211 var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
223 var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
212 224 var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
213 225 if (cur.className != null) className = cur.className + " " + className;
214 226 elt.className = className;
215 227 if (cur.render) cur.render(elt, data, cur);
216 else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
228 else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
217 229 elt.hintId = i;
218 230 }
219 231
232 var container = completion.options.container || ownerDocument.body;
220 233 var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
221 234 var left = pos.left, top = pos.bottom, below = true;
222 hints.style.left = left + "px";
223 hints.style.top = top + "px";
235 var offsetLeft = 0, offsetTop = 0;
236 if (container !== ownerDocument.body) {
237 // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
238 var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
239 var offsetParent = isContainerPositioned ? container : container.offsetParent;
240 var offsetParentPosition = offsetParent.getBoundingClientRect();
241 var bodyPosition = ownerDocument.body.getBoundingClientRect();
242 offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
243 offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
244 }
245 hints.style.left = (left - offsetLeft) + "px";
246 hints.style.top = (top - offsetTop) + "px";
247
224 248 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
225 var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
226 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
227 (completion.options.container || document.body).appendChild(hints);
249 var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
250 var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
251 container.appendChild(hints);
228 252 var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
253 var scrolls = hints.scrollHeight > hints.clientHeight + 1
254 var startScroll = cm.getScrollInfo();
255
229 256 if (overlapY > 0) {
230 257 var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
231 258 if (curTop - height > 0) { // Fits above cursor
232 hints.style.top = (top = pos.top - height) + "px";
259 hints.style.top = (top = pos.top - height - offsetTop) + "px";
233 260 below = false;
234 261 } else if (height > winH) {
235 262 hints.style.height = (winH - 5) + "px";
236 hints.style.top = (top = pos.bottom - box.top) + "px";
263 hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
237 264 var cursor = cm.getCursor();
238 265 if (data.from.ch != cursor.ch) {
239 266 pos = cm.cursorCoords(cursor);
240 hints.style.left = (left = pos.left) + "px";
267 hints.style.left = (left = pos.left - offsetLeft) + "px";
241 268 box = hints.getBoundingClientRect();
242 269 }
243 270 }
@@ -248,8 +275,10 b''
248 275 hints.style.width = (winW - 5) + "px";
249 276 overlapX -= (box.right - box.left) - winW;
250 277 }
251 hints.style.left = (left = pos.left - overlapX) + "px";
278 hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
252 279 }
280 if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
281 node.style.paddingRight = cm.display.nativeBarWidth + "px"
253 282
254 283 cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
255 284 moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
@@ -267,11 +296,10 b''
267 296 cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
268 297 }
269 298
270 var startScroll = cm.getScrollInfo();
271 299 cm.on("scroll", this.onScroll = function() {
272 300 var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
273 301 var newTop = top + startScroll.top - curScroll.top;
274 var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
302 var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
275 303 if (!below) point += hints.offsetHeight;
276 304 if (point <= editor.top || point >= editor.bottom) return completion.close();
277 305 hints.style.top = newTop + "px";
@@ -295,7 +323,7 b''
295 323 setTimeout(function(){cm.focus();}, 20);
296 324 });
297 325
298 CodeMirror.signal(data, "select", completions[0], hints.firstChild);
326 CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
299 327 return true;
300 328 }
301 329
@@ -332,7 +360,7 b''
332 360 i = avoidWrap ? 0 : this.data.list.length - 1;
333 361 if (this.selectedHint == i) return;
334 362 var node = this.hints.childNodes[this.selectedHint];
335 node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
363 if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
336 364 node = this.hints.childNodes[this.selectedHint = i];
337 365 node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
338 366 if (node.offsetTop < this.hints.scrollTop)
@@ -355,40 +383,31 b''
355 383 return result
356 384 }
357 385
386 function fetchHints(hint, cm, options, callback) {
387 if (hint.async) {
388 hint(cm, callback, options)
389 } else {
390 var result = hint(cm, options)
391 if (result && result.then) result.then(callback)
392 else callback(result)
393 }
394 }
395
358 396 function resolveAutoHints(cm, pos) {
359 397 var helpers = cm.getHelpers(pos, "hint"), words
360 398 if (helpers.length) {
361 var async = false, resolved
362 for (var i = 0; i < helpers.length; i++) if (helpers[i].async) async = true
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)
399 var resolved = function(cm, callback, options) {
400 var app = applicableHelpers(cm, helpers);
401 function run(i) {
402 if (i == app.length) return callback(null)
403 fetchHints(app[i], cm, options, function(result) {
404 if (result && result.list.length > 0) callback(result)
405 else run(i + 1)
406 })
381 407 }
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 }
408 run(0)
391 409 }
410 resolved.async = true
392 411 resolved.supportsSelection = true
393 412 return resolved
394 413 } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
@@ -405,12 +424,13 b''
405 424 });
406 425
407 426 CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
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);
427 var cur = cm.getCursor(), token = cm.getTokenAt(cur)
428 var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
429 if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
430 term = token.string.substr(0, cur.ch - token.start)
412 431 } else {
413 var term = "", from = to;
432 term = ""
433 from = cur
414 434 }
415 435 var found = [];
416 436 for (var i = 0; i < options.words.length; i++) {
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 // Utility function that allows modes to be combined. The mode given
5 5 // as the base argument takes care of most of the normal mode
@@ -68,16 +68,21 b' CodeMirror.overlayMode = function(base, '
68 68 else return state.overlayCur;
69 69 },
70 70
71 indent: base.indent && function(state, textAfter) {
72 return base.indent(state.base, textAfter);
71 indent: base.indent && function(state, textAfter, line) {
72 return base.indent(state.base, textAfter, line);
73 73 },
74 74 electricChars: base.electricChars,
75 75
76 76 innerMode: function(state) { return {state: state.base, mode: base}; },
77 77
78 78 blankLine: function(state) {
79 if (base.blankLine) base.blankLine(state.base);
80 if (overlay.blankLine) overlay.blankLine(state.overlay);
79 var baseToken, overlayToken;
80 if (base.blankLine) baseToken = base.blankLine(state.base);
81 if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);
82
83 return overlayToken == null ?
84 baseToken :
85 (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken);
81 86 }
82 87 };
83 88 };
@@ -1,5 +1,5 b''
1 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: https://codemirror.net/LICENSE
3 3
4 4 (function(mod) {
5 5 if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,10 +14,12 b''
14 14 if (val && !prev) {
15 15 cm.on("blur", onBlur);
16 16 cm.on("change", onChange);
17 cm.on("swapDoc", onChange);
17 18 onChange(cm);
18 19 } else if (!val && prev) {
19 20 cm.off("blur", onBlur);
20 21 cm.off("change", onChange);
22 cm.off("swapDoc", onChange);
21 23 clearPlaceholder(cm);
22 24 var wrapper = cm.getWrapperElement();
23 25 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
@@ -36,7 +38,8 b''
36 38 clearPlaceholder(cm);
37 39 var elt = cm.state.placeholder = document.createElement("pre");
38 40 elt.style.cssText = "height: 0; overflow: visible";
39 elt.className = "CodeMirror-placeholder";
41 elt.style.direction = cm.getOption("direction");
42 elt.className = "CodeMirror-placeholder CodeMirror-line-like";
40 43 var placeHolder = cm.getOption("placeholder")
41 44 if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
42 45 elt.appendChild(placeHolder)
General Comments 0
You need to be logged in to leave comments. Login now