codemirror-rails 2.33 → 2.34

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 (25) hide show
  1. data/lib/codemirror/rails/version.rb +2 -2
  2. data/vendor/assets/javascripts/codemirror.js +359 -453
  3. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +4 -1
  4. data/vendor/assets/javascripts/codemirror/modes/clojure.js +7 -8
  5. data/vendor/assets/javascripts/codemirror/modes/commonlisp.js +101 -0
  6. data/vendor/assets/javascripts/codemirror/modes/css.js +336 -62
  7. data/vendor/assets/javascripts/codemirror/modes/gfm.js +6 -1
  8. data/vendor/assets/javascripts/codemirror/modes/haxe.js +0 -3
  9. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +5 -1
  10. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +11 -12
  11. data/vendor/assets/javascripts/codemirror/modes/javascript.js +1 -1
  12. data/vendor/assets/javascripts/codemirror/modes/markdown.js +131 -17
  13. data/vendor/assets/javascripts/codemirror/modes/php.js +4 -6
  14. data/vendor/assets/javascripts/codemirror/modes/shell.js +1 -1
  15. data/vendor/assets/javascripts/codemirror/modes/tiki.js +0 -7
  16. data/vendor/assets/javascripts/codemirror/modes/vb.js +2 -2
  17. data/vendor/assets/javascripts/codemirror/modes/xml.js +0 -8
  18. data/vendor/assets/javascripts/codemirror/utils/closetag.js +35 -35
  19. data/vendor/assets/javascripts/codemirror/utils/formatting.js +147 -253
  20. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +4 -8
  21. data/vendor/assets/javascripts/codemirror/utils/overlay.js +3 -1
  22. data/vendor/assets/javascripts/codemirror/utils/runmode-standalone.js +90 -0
  23. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +1 -1
  24. data/vendor/assets/stylesheets/codemirror.css +2 -2
  25. metadata +14 -12
@@ -301,13 +301,6 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) {
301
301
  if (context) return context.indent + indentUnit;
302
302
  else return 0;
303
303
  },
304
- compareStates: function(a, b) {
305
- if (a.indented != b.indented || a.pluginName != b.pluginName) return false;
306
- for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
307
- if (!ca || !cb) return ca == cb;
308
- if (ca.pluginName != cb.pluginName) return false;
309
- }
310
- },
311
304
  electricChars: "/"
312
305
  };
313
306
  });
@@ -12,8 +12,8 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
12
12
  var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
13
13
  var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
14
14
 
15
- var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property'];
16
- var middleKeywords = ['else','elseif','case'];
15
+ var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try'];
16
+ var middleKeywords = ['else','elseif','case', 'catch'];
17
17
  var endKeywords = ['next','loop'];
18
18
 
19
19
  var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'in']);
@@ -308,14 +308,6 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
308
308
  else return 0;
309
309
  },
310
310
 
311
- compareStates: function(a, b) {
312
- if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
313
- for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
314
- if (!ca || !cb) return ca == cb;
315
- if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false;
316
- }
317
- },
318
-
319
311
  electricChars: "/"
320
312
  };
321
313
  });
@@ -26,6 +26,11 @@
26
26
  /** Array of tag names where an end tag is forbidden. */
27
27
  CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
28
28
 
29
+ function innerState(cm, state) {
30
+ return CodeMirror.innerMode(cm.getMode(), state).state;
31
+ }
32
+
33
+
29
34
  /**
30
35
  * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass.
31
36
  * - cm: The editor instance.
@@ -39,40 +44,34 @@
39
44
  throw CodeMirror.Pass;
40
45
  }
41
46
 
42
- var mode = cm.getOption('mode');
43
-
44
- if (mode == 'text/html' || mode == 'xml') {
47
+ /*
48
+ * Relevant structure of token:
49
+ *
50
+ * htmlmixed
51
+ * className
52
+ * state
53
+ * htmlState
54
+ * type
55
+ * tagName
56
+ * context
57
+ * tagName
58
+ * mode
59
+ *
60
+ * xml
61
+ * className
62
+ * state
63
+ * tagName
64
+ * type
65
+ */
45
66
 
46
- /*
47
- * Relevant structure of token:
48
- *
49
- * htmlmixed
50
- * className
51
- * state
52
- * htmlState
53
- * type
54
- * tagName
55
- * context
56
- * tagName
57
- * mode
58
- *
59
- * xml
60
- * className
61
- * state
62
- * tagName
63
- * type
64
- */
65
-
66
- var pos = cm.getCursor();
67
- var tok = cm.getTokenAt(pos);
68
- var state = tok.state;
69
-
70
- if (state.mode && state.mode != 'html') {
71
- throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode.
72
- }
67
+ var pos = cm.getCursor();
68
+ var tok = cm.getTokenAt(pos);
69
+ var state = innerState(cm, tok.state);
70
+
71
+ if (state) {
73
72
 
74
73
  if (ch == '>') {
75
- var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
74
+ var type = state.type;
76
75
 
77
76
  if (tok.className == 'tag' && type == 'closeTag') {
78
77
  throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag.
@@ -83,11 +82,12 @@
83
82
  cm.setCursor(pos);
84
83
 
85
84
  tok = cm.getTokenAt(cm.getCursor());
86
- state = tok.state;
87
- type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
85
+ state = innerState(cm, tok.state);
86
+ if (!state) throw CodeMirror.Pass;
87
+ var type = state.type;
88
88
 
89
89
  if (tok.className == 'tag' && type != 'selfcloseTag') {
90
- var tagName = state.htmlState ? state.htmlState.tagName : state.tagName; // htmlmixed : xml
90
+ var tagName = state.tagName;
91
91
  if (tagName.length > 0 && shouldClose(cm, vd, tagName)) {
92
92
  insertEndTag(cm, indent, pos, tagName);
93
93
  }
@@ -100,7 +100,7 @@
100
100
 
101
101
  } else if (ch == '/') {
102
102
  if (tok.className == 'tag' && tok.string == '<') {
103
- var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : (state.context ? state.context.tagName : ''); // htmlmixed : xml
103
+ var ctx = state.context, tagName = ctx ? ctx.tagName : '';
104
104
  if (tagName.length > 0) {
105
105
  completeEndTag(cm, pos, tagName);
106
106
  return;
@@ -1,110 +1,22 @@
1
1
  // ============== Formatting extensions ============================
2
- // A common storage for all mode-specific formatting features
3
- if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
4
-
5
- // Returns the extension of the editor's current mode
6
- CodeMirror.defineExtension("getModeExt", function () {
7
- var mname = CodeMirror.resolveMode(this.getOption("mode")).name;
8
- var ext = CodeMirror.modeExtensions[mname];
9
- if (!ext) throw new Error("No extensions found for mode " + mname);
10
- return ext;
11
- });
12
-
13
- // If the current mode is 'htmlmixed', returns the extension of a mode located at
14
- // the specified position (can be htmlmixed, css or javascript). Otherwise, simply
15
- // returns the extension of the editor's current mode.
16
- CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
17
- var token = this.getTokenAt(pos);
18
- if (token && token.state && token.state.mode)
19
- return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
20
- else
21
- return this.getModeExt();
22
- });
23
-
24
- // Comment/uncomment the specified range
25
- CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
26
- var curMode = this.getModeExtAtPos(this.getCursor());
27
- if (isComment) { // Comment range
28
- var commentedText = this.getRange(from, to);
29
- this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
30
- , from, to);
31
- if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
32
- this.setCursor(from.line, from.ch + curMode.commentStart.length);
33
- }
34
- }
35
- else { // Uncomment range
36
- var selText = this.getRange(from, to);
37
- var startIndex = selText.indexOf(curMode.commentStart);
38
- var endIndex = selText.lastIndexOf(curMode.commentEnd);
39
- if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
40
- // Take string till comment start
41
- selText = selText.substr(0, startIndex)
42
- // From comment start till comment end
43
- + selText.substring(startIndex + curMode.commentStart.length, endIndex)
44
- // From comment end till string end
45
- + selText.substr(endIndex + curMode.commentEnd.length);
46
- }
47
- this.replaceRange(selText, from, to);
48
- }
49
- });
50
-
51
- // Applies automatic mode-aware indentation to the specified range
52
- CodeMirror.defineExtension("autoIndentRange", function (from, to) {
53
- var cmInstance = this;
54
- this.operation(function () {
55
- for (var i = from.line; i <= to.line; i++) {
56
- cmInstance.indentLine(i, "smart");
2
+ (function() {
3
+ // Define extensions for a few modes
4
+ CodeMirror.extendMode("css", {
5
+ commentStart: "/*",
6
+ commentEnd: "*/",
7
+ wordWrapChars: [";", "\\{", "\\}"],
8
+ autoFormatLineBreaks: function (text) {
9
+ return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
57
10
  }
58
11
  });
59
- });
60
-
61
- // Applies automatic formatting to the specified range
62
- CodeMirror.defineExtension("autoFormatRange", function (from, to) {
63
- var absStart = this.indexFromPos(from);
64
- var absEnd = this.indexFromPos(to);
65
- // Insert additional line breaks where necessary according to the
66
- // mode's syntax
67
- var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
68
- var cmInstance = this;
69
-
70
- // Replace and auto-indent the range
71
- this.operation(function () {
72
- cmInstance.replaceRange(res, from, to);
73
- var startLine = cmInstance.posFromIndex(absStart).line;
74
- var endLine = cmInstance.posFromIndex(absStart + res.length).line;
75
- for (var i = startLine; i <= endLine; i++) {
76
- cmInstance.indentLine(i, "smart");
77
- }
78
- });
79
- });
80
-
81
- // Define extensions for a few modes
82
-
83
- CodeMirror.modeExtensions["css"] = {
84
- commentStart: "/*",
85
- commentEnd: "*/",
86
- wordWrapChars: [";", "\\{", "\\}"],
87
- autoFormatLineBreaks: function (text, startPos, endPos) {
88
- text = text.substring(startPos, endPos);
89
- return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
90
- }
91
- };
92
12
 
93
- CodeMirror.modeExtensions["javascript"] = {
94
- commentStart: "/*",
95
- commentEnd: "*/",
96
- wordWrapChars: [";", "\\{", "\\}"],
97
-
98
- getNonBreakableBlocks: function (text) {
99
- var nonBreakableRegexes = [
100
- new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
101
- new RegExp("\\\\\"([\\s\\S]*?)(\\\\\"|$)"),
102
- new RegExp("\\\\\'([\\s\\S]*?)(\\\\\'|$)"),
103
- new RegExp("'([\\s\\S]*?)('|$)"),
104
- new RegExp("\"([\\s\\S]*?)(\"|$)"),
105
- new RegExp("//.*([\r\n]|$)")
106
- ];
107
- var nonBreakableBlocks = new Array();
13
+ function jsNonBreakableBlocks(text) {
14
+ var nonBreakableRegexes = [/for\s*?\((.*?)\)/,
15
+ /\"(.*?)(\"|$)/,
16
+ /\'(.*?)(\'|$)/,
17
+ /\/\*(.*?)(\*\/|$)/,
18
+ /\/\/.*/];
19
+ var nonBreakableBlocks = [];
108
20
  for (var i = 0; i < nonBreakableRegexes.length; i++) {
109
21
  var curPos = 0;
110
22
  while (curPos < text.length) {
@@ -126,174 +38,156 @@ CodeMirror.modeExtensions["javascript"] = {
126
38
  });
127
39
 
128
40
  return nonBreakableBlocks;
129
- },
41
+ }
130
42
 
131
- autoFormatLineBreaks: function (text, startPos, endPos) {
132
- text = text.substring(startPos, endPos);
133
- var curPos = 0;
134
- var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n;])", "g");
135
- var nonBreakableBlocks = this.getNonBreakableBlocks(text);
136
- if (nonBreakableBlocks != null) {
137
- var res = "";
138
- for (var i = 0; i < nonBreakableBlocks.length; i++) {
139
- if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
140
- res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
141
- curPos = nonBreakableBlocks[i].start;
142
- }
143
- if (nonBreakableBlocks[i].start <= curPos
144
- && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
145
- res += text.substring(curPos, nonBreakableBlocks[i].end);
146
- curPos = nonBreakableBlocks[i].end;
43
+ CodeMirror.extendMode("javascript", {
44
+ commentStart: "/*",
45
+ commentEnd: "*/",
46
+ wordWrapChars: [";", "\\{", "\\}"],
47
+
48
+ autoFormatLineBreaks: function (text) {
49
+ var curPos = 0;
50
+ var reLinesSplitter = /(;|\{|\})([^\r\n;])/g;
51
+ var nonBreakableBlocks = jsNonBreakableBlocks(text);
52
+ if (nonBreakableBlocks != null) {
53
+ var res = "";
54
+ for (var i = 0; i < nonBreakableBlocks.length; i++) {
55
+ if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
56
+ res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
57
+ curPos = nonBreakableBlocks[i].start;
58
+ }
59
+ if (nonBreakableBlocks[i].start <= curPos
60
+ && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
61
+ res += text.substring(curPos, nonBreakableBlocks[i].end);
62
+ curPos = nonBreakableBlocks[i].end;
63
+ }
147
64
  }
65
+ if (curPos < text.length)
66
+ res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
67
+ return res;
68
+ } else {
69
+ return text.replace(reLinesSplitter, "$1\n$2");
148
70
  }
149
- if (curPos < text.length - 1) {
150
- res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
151
- }
152
- return res;
153
71
  }
154
- else {
155
- return text.replace(reLinesSplitter, "$1\n$2");
156
- }
157
- }
158
- };
159
-
160
- CodeMirror.modeExtensions["xml"] = {
161
- commentStart: "<!--",
162
- commentEnd: "-->",
163
- wordWrapChars: [">"],
72
+ });
164
73
 
165
- autoFormatLineBreaks: function (text, startPos, endPos) {
166
- text = text.substring(startPos, endPos);
167
- var lines = text.split("\n");
168
- var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
169
- var reOpenBrackets = new RegExp("<", "g");
170
- var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
171
- for (var i = 0; i < lines.length; i++) {
172
- var mToProcess = lines[i].match(reProcessedPortion);
173
- if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
174
- lines[i] = mToProcess[1]
74
+ CodeMirror.extendMode("xml", {
75
+ commentStart: "<!--",
76
+ commentEnd: "-->",
77
+ wordWrapChars: [">"],
78
+
79
+ autoFormatLineBreaks: function (text) {
80
+ var lines = text.split("\n");
81
+ var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
82
+ var reOpenBrackets = new RegExp("<", "g");
83
+ var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
84
+ for (var i = 0; i < lines.length; i++) {
85
+ var mToProcess = lines[i].match(reProcessedPortion);
86
+ if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
87
+ lines[i] = mToProcess[1]
175
88
  + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
176
89
  + mToProcess[3];
177
- continue;
90
+ continue;
91
+ }
178
92
  }
93
+ return lines.join("\n");
179
94
  }
95
+ });
180
96
 
181
- return lines.join("\n");
97
+ function localModeAt(cm, pos) {
98
+ return CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(pos).state).mode;
182
99
  }
183
- };
184
-
185
- CodeMirror.modeExtensions["htmlmixed"] = {
186
- commentStart: "<!--",
187
- commentEnd: "-->",
188
- wordWrapChars: [">", ";", "\\{", "\\}"],
189
100
 
190
- getModeInfos: function (text, absPos) {
191
- var modeInfos = new Array();
192
- modeInfos[0] =
193
- {
194
- pos: 0,
195
- modeExt: CodeMirror.modeExtensions["xml"],
196
- modeName: "xml"
197
- };
198
-
199
- var modeMatchers = new Array();
200
- modeMatchers[0] =
201
- {
202
- regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"),
203
- modeExt: CodeMirror.modeExtensions["css"],
204
- modeName: "css"
205
- };
206
- modeMatchers[1] =
207
- {
208
- regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"),
209
- modeExt: CodeMirror.modeExtensions["javascript"],
210
- modeName: "javascript"
211
- };
101
+ function enumerateModesBetween(cm, line, start, end) {
102
+ var outer = cm.getMode(), text = cm.getLine(line);
103
+ if (end == null) end = text.length;
104
+ if (!outer.innerMode) return [{from: start, to: end, mode: outer}];
105
+ var state = cm.getTokenAt({line: line, ch: start}).state;
106
+ var mode = CodeMirror.innerMode(outer, state).mode;
107
+ var found = [], stream = new CodeMirror.StringStream(text);
108
+ stream.pos = stream.start = start;
109
+ for (;;) {
110
+ outer.token(stream, state);
111
+ var curMode = CodeMirror.innerMode(outer, state).mode;
112
+ if (curMode != mode) {
113
+ var cut = stream.start;
114
+ // Crappy heuristic to deal with the fact that a change in
115
+ // mode can occur both at the end and the start of a token,
116
+ // and we don't know which it was.
117
+ if (mode.name == "xml" && text.charAt(stream.pos - 1) == ">") cut = stream.pos;
118
+ found.push({from: start, to: cut, mode: mode});
119
+ start = cut;
120
+ mode = curMode;
121
+ }
122
+ if (stream.pos >= end) break;
123
+ stream.start = stream.pos;
124
+ }
125
+ if (start < end) found.push({from: start, to: end, mode: mode});
126
+ return found;
127
+ }
212
128
 
213
- var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
214
- // Detect modes for the entire text
215
- for (var i = 0; i < modeMatchers.length; i++) {
216
- var curPos = 0;
217
- while (curPos <= lastCharPos) {
218
- var m = text.substr(curPos).match(modeMatchers[i].regex);
219
- if (m != null) {
220
- if (m.length > 1 && m[1].length > 0) {
221
- // Push block begin pos
222
- var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
223
- modeInfos.push(
224
- {
225
- pos: blockBegin,
226
- modeExt: modeMatchers[i].modeExt,
227
- modeName: modeMatchers[i].modeName
228
- });
229
- // Push block end pos
230
- modeInfos.push(
231
- {
232
- pos: blockBegin + m[1].length,
233
- modeExt: modeInfos[0].modeExt,
234
- modeName: modeInfos[0].modeName
235
- });
236
- curPos += m.index + m[0].length;
237
- continue;
238
- }
239
- else {
240
- curPos += m.index + Math.max(m[0].length, 1);
241
- }
242
- }
243
- else { // No more matches
244
- break;
129
+ // Comment/uncomment the specified range
130
+ CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
131
+ var curMode = localModeAt(this, from), cm = this;
132
+ this.operation(function() {
133
+ if (isComment) { // Comment range
134
+ cm.replaceRange(curMode.commentEnd, to);
135
+ cm.replaceRange(curMode.commentStart, from);
136
+ if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
137
+ cm.setCursor(from.line, from.ch + curMode.commentStart.length);
138
+ } else { // Uncomment range
139
+ var selText = cm.getRange(from, to);
140
+ var startIndex = selText.indexOf(curMode.commentStart);
141
+ var endIndex = selText.lastIndexOf(curMode.commentEnd);
142
+ if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
143
+ // Take string till comment start
144
+ selText = selText.substr(0, startIndex)
145
+ // From comment start till comment end
146
+ + selText.substring(startIndex + curMode.commentStart.length, endIndex)
147
+ // From comment end till string end
148
+ + selText.substr(endIndex + curMode.commentEnd.length);
245
149
  }
150
+ cm.replaceRange(selText, from, to);
246
151
  }
247
- }
248
- // Sort mode infos
249
- modeInfos.sort(function sortModeInfo(a, b) {
250
- return a.pos - b.pos;
251
152
  });
153
+ });
252
154
 
253
- return modeInfos;
254
- },
255
-
256
- autoFormatLineBreaks: function (text, startPos, endPos) {
257
- var modeInfos = this.getModeInfos(text);
258
- var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
259
- var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
260
- var res = "";
261
- // Use modes info to break lines correspondingly
262
- if (modeInfos.length > 1) { // Deal with multi-mode text
263
- for (var i = 1; i <= modeInfos.length; i++) {
264
- var selStart = modeInfos[i - 1].pos;
265
- var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
155
+ // Applies automatic mode-aware indentation to the specified range
156
+ CodeMirror.defineExtension("autoIndentRange", function (from, to) {
157
+ var cmInstance = this;
158
+ this.operation(function () {
159
+ for (var i = from.line; i <= to.line; i++) {
160
+ cmInstance.indentLine(i, "smart");
161
+ }
162
+ });
163
+ });
266
164
 
267
- if (selStart >= endPos) { // The block starts later than the needed fragment
268
- break;
165
+ // Applies automatic formatting to the specified range
166
+ CodeMirror.defineExtension("autoFormatRange", function (from, to) {
167
+ var cm = this;
168
+ cm.operation(function () {
169
+ for (var cur = from.line, end = to.line; cur <= end; ++cur) {
170
+ var f = {line: cur, ch: cur == from.line ? from.ch : 0};
171
+ var t = {line: cur, ch: cur == end ? to.ch : null};
172
+ var modes = enumerateModesBetween(cm, cur, f.ch, t.ch), mangled = "";
173
+ var text = cm.getRange(f, t);
174
+ for (var i = 0; i < modes.length; ++i) {
175
+ var part = modes.length > 1 ? text.slice(modes[i].from, modes[i].to) : text;
176
+ if (mangled) mangled += "\n";
177
+ if (modes[i].mode.autoFormatLineBreaks) {
178
+ mangled += modes[i].mode.autoFormatLineBreaks(part);
179
+ } else mangled += text;
269
180
  }
270
- if (selStart < startPos) {
271
- if (selEnd <= startPos) { // The block starts earlier than the needed fragment
272
- continue;
273
- }
274
- selStart = startPos;
275
- }
276
- if (selEnd > endPos) {
277
- selEnd = endPos;
278
- }
279
- var textPortion = text.substring(selStart, selEnd);
280
- if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
281
- if (!reBlockStartsWithNewline.test(textPortion)
282
- && selStart > 0) { // The block does not start with a line break
283
- textPortion = "\n" + textPortion;
284
- }
285
- if (!reBlockEndsWithNewline.test(textPortion)
286
- && selEnd < text.length - 1) { // The block does not end with a line break
287
- textPortion += "\n";
288
- }
181
+ if (mangled != text) {
182
+ for (var count = 0, pos = mangled.indexOf("\n"); pos != -1; pos = mangled.indexOf("\n", pos + 1), ++count) {}
183
+ cm.replaceRange(mangled, f, t);
184
+ cur += count;
185
+ end += count;
289
186
  }
290
- res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
291
187
  }
292
- }
293
- else { // Single-mode text
294
- res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
295
- }
296
-
297
- return res;
298
- }
299
- };
188
+ for (var cur = from.line + 1; cur <= end; ++cur)
189
+ cm.indentLine(cur, "smart");
190
+ cm.setSelection(from, cm.getCursor(false));
191
+ });
192
+ });
193
+ })();