diff options
Diffstat (limited to 'imports/codemirror/lib/util/formatting.js')
-rwxr-xr-x | imports/codemirror/lib/util/formatting.js | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/imports/codemirror/lib/util/formatting.js b/imports/codemirror/lib/util/formatting.js new file mode 100755 index 00000000..986bcb8f --- /dev/null +++ b/imports/codemirror/lib/util/formatting.js | |||
@@ -0,0 +1,291 @@ | |||
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 | return CodeMirror.modeExtensions[this.getOption("mode")]; | ||
8 | }); | ||
9 | |||
10 | // If the current mode is 'htmlmixed', returns the extension of a mode located at | ||
11 | // the specified position (can be htmlmixed, css or javascript). Otherwise, simply | ||
12 | // returns the extension of the editor's current mode. | ||
13 | CodeMirror.defineExtension("getModeExtAtPos", function (pos) { | ||
14 | var token = this.getTokenAt(pos); | ||
15 | if (token && token.state && token.state.mode) | ||
16 | return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode]; | ||
17 | else | ||
18 | return this.getModeExt(); | ||
19 | }); | ||
20 | |||
21 | // Comment/uncomment the specified range | ||
22 | CodeMirror.defineExtension("commentRange", function (isComment, from, to) { | ||
23 | var curMode = this.getModeExtAtPos(this.getCursor()); | ||
24 | if (isComment) { // Comment range | ||
25 | var commentedText = this.getRange(from, to); | ||
26 | this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd | ||
27 | , from, to); | ||
28 | if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside | ||
29 | this.setCursor(from.line, from.ch + curMode.commentStart.length); | ||
30 | } | ||
31 | } | ||
32 | else { // Uncomment range | ||
33 | var selText = this.getRange(from, to); | ||
34 | var startIndex = selText.indexOf(curMode.commentStart); | ||
35 | var endIndex = selText.lastIndexOf(curMode.commentEnd); | ||
36 | if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { | ||
37 | // Take string till comment start | ||
38 | selText = selText.substr(0, startIndex) | ||
39 | // From comment start till comment end | ||
40 | + selText.substring(startIndex + curMode.commentStart.length, endIndex) | ||
41 | // From comment end till string end | ||
42 | + selText.substr(endIndex + curMode.commentEnd.length); | ||
43 | } | ||
44 | this.replaceRange(selText, from, to); | ||
45 | } | ||
46 | }); | ||
47 | |||
48 | // Applies automatic mode-aware indentation to the specified range | ||
49 | CodeMirror.defineExtension("autoIndentRange", function (from, to) { | ||
50 | var cmInstance = this; | ||
51 | this.operation(function () { | ||
52 | for (var i = from.line; i <= to.line; i++) { | ||
53 | cmInstance.indentLine(i); | ||
54 | } | ||
55 | }); | ||
56 | }); | ||
57 | |||
58 | // Applies automatic formatting to the specified range | ||
59 | CodeMirror.defineExtension("autoFormatRange", function (from, to) { | ||
60 | var absStart = this.indexFromPos(from); | ||
61 | var absEnd = this.indexFromPos(to); | ||
62 | // Insert additional line breaks where necessary according to the | ||
63 | // mode's syntax | ||
64 | var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd); | ||
65 | var cmInstance = this; | ||
66 | |||
67 | // Replace and auto-indent the range | ||
68 | this.operation(function () { | ||
69 | cmInstance.replaceRange(res, from, to); | ||
70 | var startLine = cmInstance.posFromIndex(absStart).line; | ||
71 | var endLine = cmInstance.posFromIndex(absStart + res.length).line; | ||
72 | for (var i = startLine; i <= endLine; i++) { | ||
73 | cmInstance.indentLine(i); | ||
74 | } | ||
75 | }); | ||
76 | }); | ||
77 | |||
78 | // Define extensions for a few modes | ||
79 | |||
80 | CodeMirror.modeExtensions["css"] = { | ||
81 | commentStart: "/*", | ||
82 | commentEnd: "*/", | ||
83 | wordWrapChars: [";", "\\{", "\\}"], | ||
84 | autoFormatLineBreaks: function (text) { | ||
85 | return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); | ||
86 | } | ||
87 | }; | ||
88 | |||
89 | CodeMirror.modeExtensions["javascript"] = { | ||
90 | commentStart: "/*", | ||
91 | commentEnd: "*/", | ||
92 | wordWrapChars: [";", "\\{", "\\}"], | ||
93 | |||
94 | getNonBreakableBlocks: function (text) { | ||
95 | var nonBreakableRegexes = [ | ||
96 | new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"), | ||
97 | new RegExp("'([\\s\\S]*?)('|$)"), | ||
98 | new RegExp("\"([\\s\\S]*?)(\"|$)"), | ||
99 | new RegExp("//.*([\r\n]|$)") | ||
100 | ]; | ||
101 | var nonBreakableBlocks = new Array(); | ||
102 | for (var i = 0; i < nonBreakableRegexes.length; i++) { | ||
103 | var curPos = 0; | ||
104 | while (curPos < text.length) { | ||
105 | var m = text.substr(curPos).match(nonBreakableRegexes[i]); | ||
106 | if (m != null) { | ||
107 | nonBreakableBlocks.push({ | ||
108 | start: curPos + m.index, | ||
109 | end: curPos + m.index + m[0].length | ||
110 | }); | ||
111 | curPos += m.index + Math.max(1, m[0].length); | ||
112 | } | ||
113 | else { // No more matches | ||
114 | break; | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | nonBreakableBlocks.sort(function (a, b) { | ||
119 | return a.start - b.start; | ||
120 | }); | ||
121 | |||
122 | return nonBreakableBlocks; | ||
123 | }, | ||
124 | |||
125 | autoFormatLineBreaks: function (text) { | ||
126 | var curPos = 0; | ||
127 | var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g"); | ||
128 | var nonBreakableBlocks = this.getNonBreakableBlocks(text); | ||
129 | if (nonBreakableBlocks != null) { | ||
130 | var res = ""; | ||
131 | for (var i = 0; i < nonBreakableBlocks.length; i++) { | ||
132 | if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block | ||
133 | res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); | ||
134 | curPos = nonBreakableBlocks[i].start; | ||
135 | } | ||
136 | if (nonBreakableBlocks[i].start <= curPos | ||
137 | && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block | ||
138 | res += text.substring(curPos, nonBreakableBlocks[i].end); | ||
139 | curPos = nonBreakableBlocks[i].end; | ||
140 | } | ||
141 | } | ||
142 | if (curPos < text.length - 1) { | ||
143 | res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); | ||
144 | } | ||
145 | return res; | ||
146 | } | ||
147 | else { | ||
148 | return text.replace(reLinesSplitter, "$1\n$2"); | ||
149 | } | ||
150 | } | ||
151 | }; | ||
152 | |||
153 | CodeMirror.modeExtensions["xml"] = { | ||
154 | commentStart: "<!--", | ||
155 | commentEnd: "-->", | ||
156 | wordWrapChars: [">"], | ||
157 | |||
158 | autoFormatLineBreaks: function (text) { | ||
159 | var lines = text.split("\n"); | ||
160 | var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); | ||
161 | var reOpenBrackets = new RegExp("<", "g"); | ||
162 | var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); | ||
163 | for (var i = 0; i < lines.length; i++) { | ||
164 | var mToProcess = lines[i].match(reProcessedPortion); | ||
165 | if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces | ||
166 | lines[i] = mToProcess[1] | ||
167 | + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2") | ||
168 | + mToProcess[3]; | ||
169 | continue; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return lines.join("\n"); | ||
174 | } | ||
175 | }; | ||
176 | |||
177 | CodeMirror.modeExtensions["htmlmixed"] = { | ||
178 | commentStart: "<!--", | ||
179 | commentEnd: "-->", | ||
180 | wordWrapChars: [">", ";", "\\{", "\\}"], | ||
181 | |||
182 | getModeInfos: function (text, absPos) { | ||
183 | var modeInfos = new Array(); | ||
184 | modeInfos[0] = | ||
185 | { | ||
186 | pos: 0, | ||
187 | modeExt: CodeMirror.modeExtensions["xml"], | ||
188 | modeName: "xml" | ||
189 | }; | ||
190 | |||
191 | var modeMatchers = new Array(); | ||
192 | modeMatchers[0] = | ||
193 | { | ||
194 | regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"), | ||
195 | modeExt: CodeMirror.modeExtensions["css"], | ||
196 | modeName: "css" | ||
197 | }; | ||
198 | modeMatchers[1] = | ||
199 | { | ||
200 | regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"), | ||
201 | modeExt: CodeMirror.modeExtensions["javascript"], | ||
202 | modeName: "javascript" | ||
203 | }; | ||
204 | |||
205 | var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1); | ||
206 | // Detect modes for the entire text | ||
207 | for (var i = 0; i < modeMatchers.length; i++) { | ||
208 | var curPos = 0; | ||
209 | while (curPos <= lastCharPos) { | ||
210 | var m = text.substr(curPos).match(modeMatchers[i].regex); | ||
211 | if (m != null) { | ||
212 | if (m.length > 1 && m[1].length > 0) { | ||
213 | // Push block begin pos | ||
214 | var blockBegin = curPos + m.index + m[0].indexOf(m[1]); | ||
215 | modeInfos.push( | ||
216 | { | ||
217 | pos: blockBegin, | ||
218 | modeExt: modeMatchers[i].modeExt, | ||
219 | modeName: modeMatchers[i].modeName | ||
220 | }); | ||
221 | // Push block end pos | ||
222 | modeInfos.push( | ||
223 | { | ||
224 | pos: blockBegin + m[1].length, | ||
225 | modeExt: modeInfos[0].modeExt, | ||
226 | modeName: modeInfos[0].modeName | ||
227 | }); | ||
228 | curPos += m.index + m[0].length; | ||
229 | continue; | ||