summaryrefslogtreecommitdiff
path: root/codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js
diff options
context:
space:
mode:
Diffstat (limited to 'codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js')
-rw-r--r--codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js297
1 files changed, 297 insertions, 0 deletions
diff --git a/codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js b/codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js
new file mode 100644
index 0000000..3a1a987
--- /dev/null
+++ b/codemirror_ui/lib/CodeMirror-2.3/lib/util/formatting.js
@@ -0,0 +1,297 @@
+// ============== Formatting extensions ============================
+// A common storage for all mode-specific formatting features
+if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
+
+// Returns the extension of the editor's current mode
+CodeMirror.defineExtension("getModeExt", function () {
+ var mname = CodeMirror.resolveMode(this.getOption("mode")).name;
+ var ext = CodeMirror.modeExtensions[mname];
+ if (!ext) throw new Error("No extensions found for mode " + mname);
+ return ext;
+});
+
+// If the current mode is 'htmlmixed', returns the extension of a mode located at
+// the specified position (can be htmlmixed, css or javascript). Otherwise, simply
+// returns the extension of the editor's current mode.
+CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
+ var token = this.getTokenAt(pos);
+ if (token && token.state && token.state.mode)
+ return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
+ else
+ return this.getModeExt();
+});
+
+// Comment/uncomment the specified range
+CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
+ var curMode = this.getModeExtAtPos(this.getCursor());
+ if (isComment) { // Comment range
+ var commentedText = this.getRange(from, to);
+ this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
+ , from, to);
+ if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
+ this.setCursor(from.line, from.ch + curMode.commentStart.length);
+ }
+ }
+ else { // Uncomment range
+ var selText = this.getRange(from, to);
+ var startIndex = selText.indexOf(curMode.commentStart);
+ var endIndex = selText.lastIndexOf(curMode.commentEnd);
+ if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
+ // Take string till comment start
+ selText = selText.substr(0, startIndex)
+ // From comment start till comment end
+ + selText.substring(startIndex + curMode.commentStart.length, endIndex)
+ // From comment end till string end
+ + selText.substr(endIndex + curMode.commentEnd.length);
+ }
+ this.replaceRange(selText, from, to);
+ }
+});
+
+// Applies automatic mode-aware indentation to the specified range
+CodeMirror.defineExtension("autoIndentRange", function (from, to) {
+ var cmInstance = this;
+ this.operation(function () {
+ for (var i = from.line; i <= to.line; i++) {
+ cmInstance.indentLine(i, "smart");
+ }
+ });
+});
+
+// Applies automatic formatting to the specified range
+CodeMirror.defineExtension("autoFormatRange", function (from, to) {
+ var absStart = this.indexFromPos(from);
+ var absEnd = this.indexFromPos(to);
+ // Insert additional line breaks where necessary according to the
+ // mode's syntax
+ var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
+ var cmInstance = this;
+
+ // Replace and auto-indent the range
+ this.operation(function () {
+ cmInstance.replaceRange(res, from, to);
+ var startLine = cmInstance.posFromIndex(absStart).line;
+ var endLine = cmInstance.posFromIndex(absStart + res.length).line;
+ for (var i = startLine; i <= endLine; i++) {
+ cmInstance.indentLine(i, "smart");
+ }
+ });
+});
+
+// Define extensions for a few modes
+
+CodeMirror.modeExtensions["css"] = {
+ commentStart: "/*",
+ commentEnd: "*/",
+ wordWrapChars: [";", "\\{", "\\}"],
+ autoFormatLineBreaks: function (text, startPos, endPos) {
+ text = text.substring(startPos, endPos);
+ return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
+ }
+};
+
+CodeMirror.modeExtensions["javascript"] = {
+ commentStart: "/*",
+ commentEnd: "*/",
+ wordWrapChars: [";", "\\{", "\\}"],
+
+ getNonBreakableBlocks: function (text) {
+ var nonBreakableRegexes = [
+ new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
+ new RegExp("'([\\s\\S]*?)('|$)"),
+ new RegExp("\"([\\s\\S]*?)(\"|$)"),
+ new RegExp("//.*([\r\n]|$)")
+ ];
+ var nonBreakableBlocks = new Array();
+ for (var i = 0; i < nonBreakableRegexes.length; i++) {
+ var curPos = 0;
+ while (curPos < text.length) {
+ var m = text.substr(curPos).match(nonBreakableRegexes[i]);
+ if (m != null) {
+ nonBreakableBlocks.push({
+ start: curPos + m.index,
+ end: curPos + m.index + m[0].length
+ });
+ curPos += m.index + Math.max(1, m[0].length);
+ }
+ else { // No more matches
+ break;
+ }
+ }
+ }
+ nonBreakableBlocks.sort(function (a, b) {
+ return a.start - b.start;
+ });
+
+ return nonBreakableBlocks;
+ },
+
+ autoFormatLineBreaks: function (text, startPos, endPos) {
+ text = text.substring(startPos, endPos);
+ var curPos = 0;
+ var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g");
+ var nonBreakableBlocks = this.getNonBreakableBlocks(text);
+ if (nonBreakableBlocks != null) {
+ var res = "";
+ for (var i = 0; i < nonBreakableBlocks.length; i++) {
+ if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
+ res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
+ curPos = nonBreakableBlocks[i].start;
+ }
+ if (nonBreakableBlocks[i].start <= curPos
+ && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
+ res += text.substring(curPos, nonBreakableBlocks[i].end);
+ curPos = nonBreakableBlocks[i].end;
+ }
+ }
+ if (curPos < text.length - 1) {
+ res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
+ }
+ return res;
+ }
+ else {
+ return text.replace(reLinesSplitter, "$1\n$2");
+ }
+ }
+};
+
+CodeMirror.modeExtensions["xml"] = {
+ commentStart: "<!--",
+ commentEnd: "-->",
+ wordWrapChars: [">"],
+
+ autoFormatLineBreaks: function (text, startPos, endPos) {
+ text = text.substring(startPos, endPos);
+ var lines = text.split("\n");
+ var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
+ var reOpenBrackets = new RegExp("<", "g");
+ var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
+ for (var i = 0; i < lines.length; i++) {
+ var mToProcess = lines[i].match(reProcessedPortion);
+ if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
+ lines[i] = mToProcess[1]
+ + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
+ + mToProcess[3];
+ continue;
+ }
+ }
+
+ return lines.join("\n");
+ }
+};
+
+CodeMirror.modeExtensions["htmlmixed"] = {
+ commentStart: "<!--",
+ commentEnd: "-->",
+ wordWrapChars: [">", ";", "\\{", "\\}"],
+
+ getModeInfos: function (text, absPos) {
+ var modeInfos = new Array();
+ modeInfos[0] =
+ {
+ pos: 0,
+ modeExt: CodeMirror.modeExtensions["xml"],
+ modeName: "xml"
+ };
+
+ var modeMatchers = new Array();
+ modeMatchers[0] =
+ {
+ regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"),
+ modeExt: CodeMirror.modeExtensions["css"],
+ modeName: "css"
+ };
+ modeMatchers[1] =
+ {
+ regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"),
+ modeExt: CodeMirror.modeExtensions["javascript"],
+ modeName: "javascript"
+ };
+
+ var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
+ // Detect modes for the entire text
+ for (var i = 0; i < modeMatchers.length; i++) {
+ var curPos = 0;
+ while (curPos <= lastCharPos) {
+ var m = text.substr(curPos).match(modeMatchers[i].regex);
+ if (m != null) {
+ if (m.length > 1 && m[1].length > 0) {
+ // Push block begin pos
+ var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
+ modeInfos.push(
+ {
+ pos: blockBegin,
+ modeExt: modeMatchers[i].modeExt,
+ modeName: modeMatchers[i].modeName
+ });
+ // Push block end pos
+ modeInfos.push(
+ {
+ pos: blockBegin + m[1].length,
+ modeExt: modeInfos[0].modeExt,
+ modeName: modeInfos[0].modeName
+ });
+ curPos += m.index + m[0].length;
+ continue;
+ }
+ else {
+ curPos += m.index + Math.max(m[0].length, 1);
+ }
+ }
+ else { // No more matches
+ break;
+ }
+ }
+ }
+ // Sort mode infos
+ modeInfos.sort(function sortModeInfo(a, b) {
+ return a.pos - b.pos;
+ });
+
+ return modeInfos;
+ },
+
+ autoFormatLineBreaks: function (text, startPos, endPos) {
+ var modeInfos = this.getModeInfos(text);
+ var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
+ var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
+ var res = "";
+ // Use modes info to break lines correspondingly
+ if (modeInfos.length > 1) { // Deal with multi-mode text
+ for (var i = 1; i <= modeInfos.length; i++) {
+ var selStart = modeInfos[i - 1].pos;
+ var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
+
+ if (selStart >= endPos) { // The block starts later than the needed fragment
+ break;
+ }
+ if (selStart < startPos) {
+ if (selEnd <= startPos) { // The block starts earlier than the needed fragment
+ continue;
+ }
+ selStart = startPos;
+ }
+ if (selEnd > endPos) {
+ selEnd = endPos;
+ }
+ var textPortion = text.substring(selStart, selEnd);
+ if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
+ if (!reBlockStartsWithNewline.test(textPortion)
+ && selStart > 0) { // The block does not start with a line break
+ textPortion = "\n" + textPortion;
+ }
+ if (!reBlockEndsWithNewline.test(textPortion)
+ && selEnd < text.length - 1) { // The block does not end with a line break
+ textPortion += "\n";
+ }
+ }
+ res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
+ }
+ }
+ else { // Single-mode text
+ res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
+ }
+
+ return res;
+ }
+};