aboutsummaryrefslogtreecommitdiff
path: root/imports/codemirror/lib/util/formatting.js
diff options
context:
space:
mode:
Diffstat (limited to 'imports/codemirror/lib/util/formatting.js')
-rwxr-xr-ximports/codemirror/lib/util/formatting.js291
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
3if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
4
5// Returns the extension of the editor's current mode
6CodeMirror.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.
13CodeMirror.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
22CodeMirror.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
49CodeMirror.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
59CodeMirror.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
80CodeMirror.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
89CodeMirror.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
153CodeMirror.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
177CodeMirror.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 });
<