erp_app 3.0.12 → 3.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. data/lib/erp_app/version.rb +1 -1
  2. data/public/javascripts/erp_app/codemirror/doc/compress.html +27 -25
  3. data/public/javascripts/erp_app/codemirror/doc/manual.html +8 -1
  4. data/public/javascripts/erp_app/codemirror/doc/oldrelease.html +51 -0
  5. data/public/javascripts/erp_app/codemirror/doc/realworld.html +72 -0
  6. data/public/javascripts/erp_app/codemirror/keymap/vim.js +9 -10
  7. data/public/javascripts/erp_app/codemirror/lib/codemirror.css +1 -0
  8. data/public/javascripts/erp_app/codemirror/lib/codemirror.js +53 -31
  9. data/public/javascripts/erp_app/codemirror/lib/util/overlay.js +6 -1
  10. data/public/javascripts/erp_app/codemirror/lib/util/runmode.js +1 -1
  11. data/public/javascripts/erp_app/codemirror/lib/util/searchcursor.js +3 -3
  12. data/public/javascripts/erp_app/codemirror/lib/util/simple-hint.js +9 -4
  13. data/public/javascripts/erp_app/codemirror/mode/clike/clike.js +1 -0
  14. data/public/javascripts/erp_app/codemirror/mode/css/css.js +1 -1
  15. data/public/javascripts/erp_app/codemirror/mode/css/test.js +2 -2
  16. data/public/javascripts/erp_app/codemirror/mode/gfm/gfm.js +83 -139
  17. data/public/javascripts/erp_app/codemirror/mode/gfm/index.html +27 -4
  18. data/public/javascripts/erp_app/codemirror/mode/gfm/test.js +225 -0
  19. data/public/javascripts/erp_app/codemirror/mode/htmlembedded/htmlembedded.js +1 -0
  20. data/public/javascripts/erp_app/codemirror/mode/javascript/index.html +11 -4
  21. data/public/javascripts/erp_app/codemirror/mode/javascript/javascript.js +63 -15
  22. data/public/javascripts/erp_app/codemirror/mode/javascript/typescript.html +48 -0
  23. data/public/javascripts/erp_app/codemirror/mode/lua/lua.js +1 -1
  24. data/public/javascripts/erp_app/codemirror/mode/markdown/markdown.js +122 -23
  25. data/public/javascripts/erp_app/codemirror/mode/markdown/test.js +183 -1
  26. data/public/javascripts/erp_app/codemirror/package.json +1 -1
  27. data/public/javascripts/erp_app/codemirror/test/index.html +3 -0
  28. data/public/javascripts/erp_app/codemirror/test/test.js +11 -6
  29. metadata +15 -32
@@ -1,6 +1,9 @@
1
+ // TODO actually recognize syntax of TypeScript constructs
2
+
1
3
  CodeMirror.defineMode("javascript", function(config, parserConfig) {
2
4
  var indentUnit = config.indentUnit;
3
5
  var jsonMode = parserConfig.json;
6
+ var isTS = parserConfig.typescript;
4
7
 
5
8
  // Tokenizer
6
9
 
@@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
8
11
  function kw(type) {return {type: type, style: "keyword"};}
9
12
  var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10
13
  var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11
- return {
14
+
15
+ var jsKeywords = {
12
16
  "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13
17
  "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14
18
  "var": kw("var"), "const": kw("var"), "let": kw("var"),
@@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
17
21
  "in": operator, "typeof": operator, "instanceof": operator,
18
22
  "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
19
23
  };
24
+
25
+ // Extend the 'normal' keywords with the TypeScript language extensions
26
+ if (isTS) {
27
+ var type = {type: "variable", style: "variable-3"};
28
+ var tsKeywords = {
29
+ // object-like things
30
+ "interface": kw("interface"),
31
+ "class": kw("class"),
32
+ "extends": kw("extends"),
33
+ "constructor": kw("constructor"),
34
+
35
+ // scope modifiers
36
+ "public": kw("public"),
37
+ "private": kw("private"),
38
+ "protected": kw("protected"),
39
+ "static": kw("static"),
40
+
41
+ "super": kw("super"),
42
+
43
+ // types
44
+ "string": type, "number": type, "bool": type, "any": type
45
+ };
46
+
47
+ for (var attr in tsKeywords) {
48
+ jsKeywords[attr] = tsKeywords[attr];
49
+ }
50
+ }
51
+
52
+ return jsKeywords;
20
53
  }();
21
54
 
22
55
  var isOperatorChar = /[+\-*&%=<>!?|]/;
@@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
66
99
  stream.skipToEnd();
67
100
  return ret("comment", "comment");
68
101
  }
69
- else if (state.reAllowed) {
102
+ else if (state.lastType == "operator" || state.lastType == "keyword c" ||
103
+ /^[\[{}\(,;:]$/.test(state.lastType)) {
70
104
  nextUntilUnescaped(stream, "/");
71
105
  stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
72
106
  return ret("regexp", "string-2");
@@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
87
121
  else {
88
122
  stream.eatWhile(/[\w\$_]/);
89
123
  var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
90
- return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
124
+ return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
91
125
  ret("variable", "variable", word);
92
126
  }
93
127
  }
@@ -275,19 +309,30 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
275
309
  if (type == "}") return cont();
276
310
  return pass(statement, block);
277
311
  }
312
+ function maybetype(type) {
313
+ if (type == ":") return cont(typedef);
314
+ return pass();
315
+ }
316
+ function typedef(type) {
317
+ if (type == "variable"){cx.marked = "variable-3"; return cont();}
318
+ return pass();
319
+ }
278
320
  function vardef1(type, value) {
279
- if (type == "variable"){register(value); return cont(vardef2);}
280
- return cont();
321
+ if (type == "variable") {
322
+ register(value);
323
+ return isTS ? cont(maybetype, vardef2) : cont(vardef2);
324
+ }
325
+ return pass();
281
326
  }
282
327
  function vardef2(type, value) {
283
328
  if (value == "=") return cont(expression, vardef2);
284
329
  if (type == ",") return cont(vardef1);
285
330
  }
286
331
  function forspec1(type) {
287
- if (type == "var") return cont(vardef1, forspec2);
288
- if (type == ";") return pass(forspec2);
332
+ if (type == "var") return cont(vardef1, expect(";"), forspec2);
333
+ if (type == ";") return cont(forspec2);
289
334
  if (type == "variable") return cont(formaybein);
290
- return pass(forspec2);
335
+ return cont(forspec2);
291
336
  }
292
337
  function formaybein(type, value) {
293
338
  if (value == "in") return cont(expression);
@@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
306
351
  if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
307
352
  }
308
353
  function funarg(type, value) {
309
- if (type == "variable") {register(value); return cont();}
354
+ if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
310
355
  }
311
356
 
312
357
  // Interface
@@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
315
360
  startState: function(basecolumn) {
316
361
  return {
317
362
  tokenize: jsTokenBase,
318
- reAllowed: true,
319
- kwAllowed: true,
363
+ lastType: null,
320
364
  cc: [],
321
365
  lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
322
366
  localVars: parserConfig.localVars,
@@ -334,19 +378,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
334
378
  if (stream.eatSpace()) return null;
335
379
  var style = state.tokenize(stream, state);
336
380
  if (type == "comment") return style;
337
- state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
338
- state.kwAllowed = type != '.';
381
+ state.lastType = type;
339
382
  return parseJS(state, style, type, content, stream);
340
383
  },
341
384
 
342
385
  indent: function(state, textAfter) {
386
+ if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
343
387
  if (state.tokenize != jsTokenBase) return 0;
344
388
  var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
345
389
  if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
346
390
  var type = lexical.type, closing = firstChar == type;
347
- if (type == "vardef") return lexical.indented + 4;
391
+ if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
348
392
  else if (type == "form" && firstChar == "{") return lexical.indented;
349
- else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
393
+ else if (type == "form") return lexical.indented + indentUnit;
394
+ else if (type == "stat")
395
+ return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
350
396
  else if (lexical.info == "switch" && !closing)
351
397
  return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
352
398
  else if (lexical.align) return lexical.column + (closing ? 0 : 1);
@@ -359,3 +405,5 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
359
405
 
360
406
  CodeMirror.defineMIME("text/javascript", "javascript");
361
407
  CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
408
+ CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
409
+ CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
@@ -0,0 +1,48 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>CodeMirror: TypeScript mode</title>
6
+ <link rel="stylesheet" href="../../lib/codemirror.css">
7
+ <script src="../../lib/codemirror.js"></script>
8
+ <script src="javascript.js"></script>
9
+ <link rel="stylesheet" href="../../doc/docs.css">
10
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11
+ </head>
12
+ <body>
13
+ <h1>CodeMirror: TypeScript mode</h1>
14
+
15
+ <div><textarea id="code" name="code">
16
+ class Greeter {
17
+ greeting: string;
18
+ constructor (message: string) {
19
+ this.greeting = message;
20
+ }
21
+ greet() {
22
+ return "Hello, " + this.greeting;
23
+ }
24
+ }
25
+
26
+ var greeter = new Greeter("world");
27
+
28
+ var button = document.createElement('button')
29
+ button.innerText = "Say Hello"
30
+ button.onclick = function() {
31
+ alert(greeter.greet())
32
+ }
33
+
34
+ document.body.appendChild(button)
35
+
36
+ </textarea></div>
37
+
38
+ <script>
39
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
40
+ lineNumbers: true,
41
+ matchBrackets: true,
42
+ mode: "text/typescript"
43
+ });
44
+ </script>
45
+
46
+ <p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
47
+ </body>
48
+ </html>
@@ -64,7 +64,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) {
64
64
  function normal(stream, state) {
65
65
  var ch = stream.next();
66
66
  if (ch == "-" && stream.eat("-")) {
67
- if (stream.eat("["))
67
+ if (stream.eat("[") && stream.eat("["))
68
68
  return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state);
69
69
  stream.skipToEnd();
70
70
  return "comment";
@@ -2,6 +2,46 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
2
2
 
3
3
  var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html");
4
4
  var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain");
5
+ var aliases = {
6
+ html: "htmlmixed",
7
+ js: "javascript",
8
+ json: "application/json",
9
+ c: "text/x-csrc",
10
+ "c++": "text/x-c++src",
11
+ java: "text/x-java",
12
+ csharp: "text/x-csharp",
13
+ "c#": "text/x-csharp"
14
+ };
15
+
16
+ var getMode = (function () {
17
+ var i, modes = {}, mimes = {}, mime;
18
+
19
+ var list = CodeMirror.listModes();
20
+ for (i = 0; i < list.length; i++) {
21
+ modes[list[i]] = list[i];
22
+ }
23
+ var mimesList = CodeMirror.listMIMEs();
24
+ for (i = 0; i < mimesList.length; i++) {
25
+ mime = mimesList[i].mime;
26
+ mimes[mime] = mimesList[i].mime;
27
+ }
28
+
29
+ for (var a in aliases) {
30
+ if (aliases[a] in modes || aliases[a] in mimes)
31
+ modes[a] = aliases[a];
32
+ }
33
+
34
+ return function (lang) {
35
+ return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null;
36
+ };
37
+ }());
38
+
39
+ // Should underscores in words open/close em/strong?
40
+ if (modeCfg.underscoresBreakWords === undefined)
41
+ modeCfg.underscoresBreakWords = true;
42
+
43
+ // Turn on fenced code blocks? ("```" to start/end)
44
+ if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
5
45
 
6
46
  var codeDepth = 0;
7
47
  var prevLineHasContent = false
@@ -12,6 +52,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
12
52
  , quote = 'quote'
13
53
  , list = 'string'
14
54
  , hr = 'hr'
55
+ , image = 'tag'
15
56
  , linkinline = 'link'
16
57
  , linkemail = 'link'
17
58
  , linktext = 'link'
@@ -24,7 +65,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
24
65
  , ulRE = /^[*\-+]\s+/
25
66
  , olRE = /^[0-9]+\.\s+/
26
67
  , headerRE = /^(?:\={1,}|-{1,})$/
27
- , textRE = /^[^\[*_\\<>` "'(]+/;
68
+ , textRE = /^[^!\[\]*_\\<>` "'(]+/;
28
69
 
29
70
  function switchInline(stream, state, f) {
30
71
  state.f = state.inline = f;
@@ -42,8 +83,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
42
83
  function blankLine(state) {
43
84
  // Reset linkTitle state
44
85
  state.linkTitle = false;
45
- // Reset CODE state
46
- state.code = false;
47
86
  // Reset EM state
48
87
  state.em = false;
49
88
  // Reset STRONG state
@@ -58,7 +97,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
58
97
  }
59
98
 
60
99
  function blockNormal(stream, state) {
61
- var match;
62
100
 
63
101
  if (state.list !== false && state.indentationDiff >= 0) { // Continued list
64
102
  if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
@@ -84,9 +122,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
84
122
  return switchInline(stream, state, footnoteLink);
85
123
  } else if (stream.match(hrRE, true)) {
86
124
  return hr;
87
- } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
125
+ } else if (stream.match(ulRE, true) || stream.match(olRE, true)) {
88
126
  state.indentation += 4;
89
127
  state.list = true;
128
+ } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) {
129
+ // try switching mode
130
+ state.localMode = getMode(RegExp.$1);
131
+ if (state.localMode) state.localState = state.localMode.startState();
132
+ switchBlock(stream, state, local);
133
+ return code;
90
134
  }
91
135
 
92
136
  return switchInline(stream, state, state.inline);
@@ -106,6 +150,30 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
106
150
  return style;
107
151
  }
108
152
 
153
+ function local(stream, state) {
154
+ if (stream.sol() && stream.match(/^```/, true)) {
155
+ state.localMode = state.localState = null;
156
+ state.f = inlineNormal;
157
+ state.block = blockNormal;
158
+ return code;
159
+ } else if (state.localMode) {
160
+ return state.localMode.token(stream, state.localState);
161
+ } else {
162
+ stream.skipToEnd();
163
+ return code;
164
+ }
165
+ }
166
+
167
+ function codeBlock(stream, state) {
168
+ if(stream.match(codeBlockRE, true)){
169
+ state.f = inlineNormal;
170
+ state.block = blockNormal;
171
+ switchInline(stream, state, state.inline);
172
+ return code;
173
+ }
174
+ stream.skipToEnd();
175
+ return code;
176
+ }
109
177
 
110
178
  // Inline
111
179
  function getType(state) {
@@ -114,6 +182,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
114
182
  if (state.strong) { styles.push(state.em ? emstrong : strong); }
115
183
  else if (state.em) { styles.push(em); }
116
184
 
185
+ if (state.linkText) { styles.push(linktext); }
186
+
117
187
  if (state.code) { styles.push(code); }
118
188
 
119
189
  if (state.header) { styles.push(header); }
@@ -161,6 +231,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
161
231
  }
162
232
  }
163
233
 
234
+ // If this block is changed, it may need to be updated in GFM mode
164
235
  if (ch === '`') {
165
236
  var t = getType(state);
166
237
  var before = stream.pos;
@@ -181,8 +252,22 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
181
252
  return getType(state);
182
253
  }
183
254
 
184
- if (ch === '[' && stream.match(/.*\] ?(?:\(|\[)/, false)) {
185
- return switchInline(stream, state, linkText);
255
+ if (ch === '!' && stream.match(/\[.*\] ?(?:\(|\[)/, false)) {
256
+ stream.match(/\[.*\]/);
257
+ state.inline = state.f = linkHref;
258
+ return image;
259
+ }
260
+
261
+ if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
262
+ state.linkText = true;
263
+ return getType(state);
264
+ }
265
+
266
+ if (ch === ']' && state.linkText) {
267
+ var type = getType(state);
268
+ state.linkText = false;
269
+ state.inline = state.f = linkHref;
270
+ return type;
186
271
  }
187
272
 
188
273
  if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) {
@@ -210,8 +295,20 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
210
295
  return "tag";
211
296
  }
212
297
 
298
+ var ignoreUnderscore = false;
299
+ if (!modeCfg.underscoresBreakWords) {
300
+ if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
301
+ var prevPos = stream.pos - 2;
302
+ if (prevPos >= 0) {
303
+ var prevCh = stream.string.charAt(prevPos);
304
+ if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
305
+ ignoreUnderscore = true;
306
+ }
307
+ }
308
+ }
309
+ }
213
310
  var t = getType(state);
214
- if (ch === '*' || ch === '_') {
311
+ if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
215
312
  if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
216
313
  state.strong = false;
217
314
  return t;
@@ -238,18 +335,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
238
335
  return getType(state);
239
336
  }
240
337
 
241
- function linkText(stream, state) {
242
- while (!stream.eol()) {
243
- var ch = stream.next();
244
- if (ch === '\\') stream.next();
245
- if (ch === ']') {
246
- state.inline = state.f = linkHref;
247
- return linktext;
248
- }
249
- }
250
- return linktext;
251
- }
252
-
253
338
  function linkHref(stream, state) {
254
339
  // Check if space, and return NULL if so (to avoid marking the space)
255
340
  if(stream.eatSpace()){
@@ -287,15 +372,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
287
372
  return linkhref;
288
373
  }
289
374
 
375
+ var savedInlineRE = [];
290
376
  function inlineRE(endChar) {
291
- if (!inlineRE[endChar]) {
377
+ if (!savedInlineRE[endChar]) {
292
378
  // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
293
379
  endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
294
380
  // Match any non-endChar, escaped character, as well as the closing
295
381
  // endChar.
296
- inlineRE[endChar] = new RegExp('^(?:[^\\\\]+?|\\\\.)*?(' + endChar + ')');
382
+ savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
297
383
  }
298
- return inlineRE[endChar];
384
+ return savedInlineRE[endChar];
299
385
  }
300
386
 
301
387
  function inlineElement(type, endChar, next) {
@@ -309,6 +395,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
309
395
 
310
396
  return {
311
397
  startState: function() {
398
+ prevLineHasContent = false;
399
+ thisLineHasContent = false;
312
400
  return {
313
401
  f: blockNormal,
314
402
 
@@ -318,6 +406,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
318
406
 
319
407
  inline: inlineNormal,
320
408
  text: handleText,
409
+
410
+ linkText: false,
321
411
  linkTitle: false,
322
412
  em: false,
323
413
  strong: false,
@@ -334,6 +424,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
334
424
  block: s.block,
335
425
  htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
336
426
  indentation: s.indentation,
427
+
428
+ localMode: s.localMode,
429
+ localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
337
430
 
338
431
  inline: s.inline,
339
432
  text: s.text,
@@ -362,9 +455,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
362
455
 
363
456
  // Reset state.header
364
457
  state.header = false;
458
+
459
+ // Reset state.code
460
+ state.code = false;
365
461
 
366
462
  state.f = state.block;
367
463
  var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
464
+ var difference = Math.floor((indentation - state.indentation) / 4) * 4;
465
+ if (difference > 4) difference = 4;
466
+ indentation = state.indentation + difference;
368
467
  state.indentationDiff = indentation - state.indentation;
369
468
  state.indentation = indentation;
370
469
  if (indentation > 0) { return null; }