diff --git a/AUTHORS b/AUTHORS index 737acedaba..9af00e54e4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,8 +18,10 @@ Alexander Schepanovski Alexander Solovyov alexey-k Alex Piggott +Amsul Amy Ananya Sen +anaran AndersMad Anderson Mesquita Andre von Houck @@ -42,6 +44,9 @@ benbro Beni Cherniavsky-Paskin Benjamin DeCoste Ben Keen +Bernhard Sirlinger +Billy Moon +Blaine G boomyjee borawjm Brandon Frohs @@ -53,11 +58,13 @@ Chandra Sekhar Pydi Charles Skelton Chris Coyier Chris Granger +Chris Houseknecht Chris Morgan Christopher Brown ciaranj CodeAnimal ComFreek +Curtis Gagliardi dagsta Dan Heberden Daniel, Dao Quang Minh @@ -72,6 +79,7 @@ David Mignot David Pathakjee deebugger Deep Thought +domagoj412 Dominator008 Domizio Demichelis Drew Bratcher @@ -85,6 +93,7 @@ ekhaled Enam Mijbah Noor Eric Allam eustas +Fabien O'Carroll Fabio Zendhi Nagao Fauntleroy fbuchinger @@ -96,6 +105,7 @@ flack ForbesLindesay Forbes Lindesay Ford_Lawnmower +Frank Wiegand Gabriel Nahmias galambalazs Gautam Mehta @@ -103,6 +113,7 @@ Glenn Jorde Glenn Ruehle Golevka Gordon Smith +Grant Skinner greengiant Guillaume Massé Guillaume Massé @@ -122,6 +133,7 @@ Ingo Richter Irakli Gozalishvili Ivan Kurnosov Jacob Lee +Jakob Miland Jakub Vrana James Campos James Thorne @@ -146,6 +158,7 @@ John Connor John Lees-Miller John Snelson John Van Der Loo +Jonathan Malmaud jongalloway Jon Malmaud Joost-Wim Boekesteijn @@ -179,8 +192,10 @@ Luciano Longo lynschinzer Maksim Lin Maksym Taran +Manuel Rego Casasnovas Marat Dreizin Marco Aurélio +Marco Munizaga Marijn Haverbeke Mario Pietsch Mark Lentczner @@ -216,10 +231,14 @@ Miraculix87 misfo mloginov mps +mtaran-google Narciso Jaramillo Nathan Williams +ndr nerbert +nextrevision nguillaumin +Nicholas Bollweg Niels van Groningen Nikita Beloglazov Nikita Vasilyev @@ -240,10 +259,12 @@ prasanthj Prasanth J Rahul Randy Edmunds +Rasmus Erik Voel Jensen Richard Z.H. Wang robertop23 Robert Plummer Ruslan Osmanov +Ryan Prior sabaca Samuel Ainsworth sandeepshetty @@ -256,6 +277,7 @@ Sebastian Zaha shaund shaun gilchrist Shawn A +sheopory Shiv Deepak Shmuel Englard soliton4 @@ -284,6 +306,7 @@ Tom Erik Støwer Tom MacWright Tony Jian Travis Heppe +Triangle717 Vestimir Markov vf Volker Mische diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index a3370a6072..c59cee1aa2 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -1,5 +1,6 @@ (function() { var modes = ["clike", "css", "javascript"]; + for (var i = 0; i < modes.length; ++i) CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); @@ -12,7 +13,7 @@ if (mode.blockCommentStart && mode.blockCommentContinue) { var end = token.string.indexOf(mode.blockCommentEnd); var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; - if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { + if (end != -1 && end == token.string.length - mode.blockCommentEnd.length && pos.ch >= end) { // Comment ended, don't continue it } else if (token.string.indexOf(mode.blockCommentStart) == 0) { insert = full.slice(0, token.start); @@ -27,7 +28,7 @@ } if (insert != null) insert += mode.blockCommentContinue; } - if (insert == null && mode.lineComment) { + if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) { var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); if (found > -1) { insert = line.slice(0, found); @@ -42,12 +43,24 @@ return CodeMirror.Pass; } + function continueLineCommentEnabled(cm) { + var opt = cm.getOption("continueComments"); + if (opt && typeof opt == "object") + return opt.continueLineComment !== false; + return true; + } + CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { if (prev && prev != CodeMirror.Init) cm.removeKeyMap("continueComment"); if (val) { + var key = "Enter"; + if (typeof val == "string") + key = val; + else if (typeof val == "object" && val.key) + key = val.key; var map = {name: "continueComment"}; - map[typeof val == "string" ? val : "Enter"] = continueComment; + map[key] = continueComment; cm.addKeyMap(map); } }); diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 41d7bf8663..d21643490c 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -39,6 +39,7 @@ CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { + inp.blur(); CodeMirror.e_stop(e); close(); me.focus(); diff --git a/addon/display/rulers.js b/addon/display/rulers.js new file mode 100644 index 0000000000..958c8eaf8d --- /dev/null +++ b/addon/display/rulers.js @@ -0,0 +1,47 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("rulers", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearRulers(cm); + cm.off("refresh", refreshRulers); + } + if (val && val.length) { + setRulers(cm); + cm.on("refresh", refreshRulers); + } + }); + + function clearRulers(cm) { + for (var i = cm.display.lineSpace.childNodes.length - 1; i >= 0; i--) { + var node = cm.display.lineSpace.childNodes[i]; + if (/(^|\s)CodeMirror-ruler($|\s)/.test(node.className)) + node.parentNode.removeChild(node); + } + } + + function setRulers(cm) { + var val = cm.getOption("rulers"); + var cw = cm.defaultCharWidth(); + var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; + var bot = -cm.display.scroller.offsetHeight; + for (var i = 0; i < val.length; i++) { + var elt = document.createElement("div"); + var col, cls = null; + if (typeof val[i] == "number") { + col = val[i]; + } else { + col = val[i].column; + cls = val[i].className; + } + elt.className = "CodeMirror-ruler" + (cls ? " " + cls : ""); + elt.style.cssText = "left: " + (left + col * cw) + "px; top: -50px; bottom: " + bot + "px"; + cm.display.lineSpace.insertBefore(elt, cm.display.cursorDiv); + } + } + + function refreshRulers(cm) { + clearRulers(cm); + setRulers(cm); + } +})(); diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index cad776a783..762a94ccbb 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -82,6 +82,7 @@ var tagName = state.context && state.context.tagName; if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); + else return CodeMirror.Pass; } function indexOf(collection, elt) { diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 5c00d7093c..3c0817b8fe 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -62,6 +62,12 @@ doFold(this, pos, options, force); }); + CodeMirror.defineExtension("isFolded", function(pos) { + var marks = this.findMarksAt(pos); + for (var i = 0; i < marks.length; ++i) + if (marks[i].__isFold) return true; + }); + CodeMirror.commands.fold = function(cm) { cm.foldCode(cm.getCursor()); }; diff --git a/addon/fold/markdown-fold.js b/addon/fold/markdown-fold.js new file mode 100644 index 0000000000..aeb3c04565 --- /dev/null +++ b/addon/fold/markdown-fold.js @@ -0,0 +1,34 @@ +CodeMirror.registerHelper("fold", "markdown", function(cm, start) { + var maxDepth = 100; + + function isHeader(lineNo) { + var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)); + return tokentype && /\bheader\b/.test(tokentype); + } + + function headerLevel(lineNo, line, nextLine) { + var match = line && line.match(/^#+/); + if (match && isHeader(lineNo)) return match[0].length; + match = nextLine && nextLine.match(/^[=\-]+\s*$/); + if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2; + return maxDepth; + } + + var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1); + var level = headerLevel(start.line, firstLine, nextLine); + if (level === maxDepth) return undefined; + + var lastLineNo = cm.lastLine(); + var end = start.line, nextNextLine = cm.getLine(end + 2); + while (end < lastLineNo) { + if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break; + ++end; + nextLine = nextNextLine; + nextNextLine = cm.getLine(end + 2); + } + + return { + from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(end, cm.getLine(end).length) + }; +}); diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index a144768c81..c3358cd9b5 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -15,8 +15,8 @@ var list = [], seen = {}; var re = new RegExp(word.source, "g"); for (var dir = -1; dir <= 1; dir += 2) { - var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; - for (; line != end; line += dir) { + var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; + for (; line != endLine; line += dir) { var text = editor.getLine(line), m; while (m = re.exec(text)) { if (line == cur.line && m[0] === curWord) continue; diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 7e03d1139a..9b143fdd04 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -46,7 +46,7 @@ pick: function(data, i) { var completion = data.list[i]; if (completion.hint) completion.hint(this.cm, data, completion); - else this.cm.replaceRange(getText(completion), data.from, data.to); + else this.cm.replaceRange(getText(completion), completion.from||data.from, completion.to||data.to); CodeMirror.signal(data, "pick", completion); this.close(); }, @@ -193,8 +193,24 @@ var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); (options.container || document.body).appendChild(hints); - var box = hints.getBoundingClientRect(); - var overlapX = box.right - winW, overlapY = box.bottom - winH; + var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; + if (overlapY > 0) { + var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top); + if (curTop - height > 0) { // Fits above cursor + hints.style.top = (top = curTop - height) + "px"; + below = false; + } else if (height > winH) { + hints.style.height = (winH - 5) + "px"; + hints.style.top = (top = pos.bottom - box.top) + "px"; + var cursor = cm.getCursor(); + if (data.from.ch != cursor.ch) { + pos = cm.cursorCoords(cursor); + hints.style.left = (left = pos.left) + "px"; + box = hints.getBoundingClientRect(); + } + } + } + var overlapX = box.left - winW; if (overlapX > 0) { if (box.right - box.left > winW) { hints.style.width = (winW - 5) + "px"; @@ -202,17 +218,6 @@ } hints.style.left = (left = pos.left - overlapX) + "px"; } - if (overlapY > 0) { - var height = box.bottom - box.top; - if (box.top - (pos.bottom - pos.top) - height > 0) { - overlapY = height + (pos.bottom - pos.top); - below = false; - } else if (height > winH) { - hints.style.height = (winH - 5) + "px"; - overlapY -= height - winH; - } - hints.style.top = (top = pos.bottom - overlapY) + "px"; - } cm.addKeyMap(this.keyMap = buildKeyMap(options, { moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, @@ -220,7 +225,8 @@ menuSize: function() { return widget.screenAmount(); }, length: completions.length, close: function() { completion.close(); }, - pick: function() { widget.pick(); } + pick: function() { widget.pick(); }, + data: data })); if (options.closeOnUnfocus !== false) { @@ -303,15 +309,16 @@ }; CodeMirror.registerHelper("hint", "auto", function(cm, options) { - var helpers = cm.getHelpers(cm.getCursor(), "hint"); + var helpers = cm.getHelpers(cm.getCursor(), "hint"), words; if (helpers.length) { for (var i = 0; i < helpers.length; i++) { var cur = helpers[i](cm, options); if (cur && cur.list.length) return cur; } - } else { - var words = cm.getHelper(cm.getCursor(), "hintWords"); + } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { if (words) return CodeMirror.hint.fromList(cm, {words: words}); + } else if (CodeMirror.hint.anyword) { + return CodeMirror.hint.anyword(cm, options); } }); diff --git a/addon/lint/yaml-lint.js b/addon/lint/yaml-lint.js new file mode 100644 index 0000000000..b7d6a3de11 --- /dev/null +++ b/addon/lint/yaml-lint.js @@ -0,0 +1,14 @@ +// Depends on js-yaml.js from https://github.com/nodeca/js-yaml + +// declare global: jsyaml + +CodeMirror.registerHelper("lint", "yaml", function(text) { + var found = []; + try { jsyaml.load(text); } + catch(e) { + var loc = e.mark; + found.push({ from: CodeMirror.Pos(loc.line, loc.column), to: CodeMirror.Pos(loc.line, loc.column), message: e.message }); + } + return found; +}); +CodeMirror.yamlValidator = CodeMirror.lint.yaml; // deprecated diff --git a/addon/merge/dep/diff_match_patch.js b/addon/merge/dep/diff_match_patch.js index ac34105fc4..9d615dc9d5 100644 --- a/addon/merge/dep/diff_match_patch.js +++ b/addon/merge/dep/diff_match_patch.js @@ -47,4 +47,4 @@ j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.le diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;cversion 3.20<\/strong>/, + return index.replace(/version \d+\.\d+<\/strong>/, "version " + simple + ""); }); diff --git a/bin/source-highlight b/bin/source-highlight index 7596ed776c..1373f6626f 100755 --- a/bin/source-highlight +++ b/bin/source-highlight @@ -40,7 +40,7 @@ function ensureMode(name) { ensureMode(modeName); function esc(str) { - return str.replace(/[<&]/, function(ch) { return ch == "&" ? "&" : "<"; }); + return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&" : "<"; }); } var code = fs.readFileSync("/dev/stdin", "utf8"); diff --git a/demo/changemode.html b/demo/changemode.html index 9ea99b2d04..ad4c9d1b2c 100644 --- a/demo/changemode.html +++ b/demo/changemode.html @@ -41,8 +41,7 @@ + + @@ -39,8 +41,10 @@
JavaScript:
-
HTML:
+
HTML:
+
Markdown:
+
diff --git a/demo/fullscreen.html b/demo/fullscreen.html index 827d55d0cd..0a11f31924 100644 --- a/demo/fullscreen.html +++ b/demo/fullscreen.html @@ -27,85 +27,38 @@

Full Screen Editing

+ + + + +
+

Ruler Demo

+ + + +

Demonstration of +the rulers addon, which +displays vertical lines at given column offsets.

+ +
diff --git a/demo/search.html b/demo/search.html index 4aef2a81c0..21c9b278a9 100644 --- a/demo/search.html +++ b/demo/search.html @@ -31,45 +31,38 @@

Search/Replace Demo

DOWNLOAD LATEST RELEASE -
version 3.21 (Release notes)
+
version 3.22 (Release notes)
DONATE WITH PAYPAL @@ -141,11 +141,12 @@

Community

CodeMirror is an open-source project shared under - an MIT license. It is the editor used in - Light + an MIT license. It is the editor used in the + dev tools for + both Firefox + and Chrome, Light Table, Adobe - Brackets, Google Apps - Script, Bitbucket, + Brackets, Bitbucket, and many other projects.

Development and bug tracking happens diff --git a/keymap/vim.js b/keymap/vim.js index 75b4e454d0..a5d6812a19 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -74,7 +74,7 @@ { keys: [''], type: 'keyToKey', toKeys: ['b'] }, { keys: [''], type: 'keyToKey', toKeys: ['j'] }, { keys: [''], type: 'keyToKey', toKeys: ['k'] }, - { keys: ['C-['], type: 'keyToKey', toKeys: [''] }, + { keys: [''], type: 'keyToKey', toKeys: [''] }, { keys: [''], type: 'keyToKey', toKeys: [''] }, { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' }, { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'}, @@ -1457,7 +1457,7 @@ motionArgs.selectedCharacter); var increment = motionArgs.forward ? -1 : 1; recordLastCharacterSearch(increment, motionArgs); - if(!curEnd)return cm.getCursor(); + if (!curEnd) return null; curEnd.ch += increment; return curEnd; }, @@ -1532,22 +1532,44 @@ ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) }; }, textObjectManipulation: function(cm, motionArgs) { + // TODO: lots of possible exceptions that can be thrown here. Try da( + // outside of a () block. + + // TODO: adding <> >< to this map doesn't work, presumably because + // they're operators + var mirroredPairs = {'(': ')', ')': '(', + '{': '}', '}': '{', + '[': ']', ']': '['}; + var selfPaired = {'\'': true, '"': true}; + var character = motionArgs.selectedCharacter; + // Inclusive is the difference between a and i // TODO: Instead of using the additional text object map to perform text // object operations, merge the map into the defaultKeyMap and use // motionArgs to define behavior. Define separate entries for 'aw', // 'iw', 'a[', 'i[', etc. var inclusive = !motionArgs.textObjectInner; - if (!textObjects[character]) { + + var tmp; + if (mirroredPairs[character]) { + tmp = selectCompanionObject(cm, mirroredPairs[character], inclusive); + } else if (selfPaired[character]) { + tmp = findBeginningAndEnd(cm, character, inclusive); + } else if (character === 'W') { + tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, + true /** bigWord */); + } else if (character === 'w') { + tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, + false /** bigWord */); + } else { // No text object defined for this, don't move. return null; } - var tmp = textObjects[character](cm, inclusive); - var start = tmp.start; - var end = tmp.end; - return [start, end]; + + return [tmp.start, tmp.end]; }, + repeatLastCharacterSearch: function(cm, motionArgs) { var lastSearch = vimGlobalState.lastChararacterSearch; var repeat = motionArgs.repeat; @@ -2015,36 +2037,6 @@ } }; - var textObjects = { - // TODO: lots of possible exceptions that can be thrown here. Try da( - // outside of a () block. - // TODO: implement text objects for the reverse like }. Should just be - // an additional mapping after moving to the defaultKeyMap. - 'w': function(cm, inclusive) { - return expandWordUnderCursor(cm, inclusive, true /** forward */, - false /** bigWord */); - }, - 'W': function(cm, inclusive) { - return expandWordUnderCursor(cm, inclusive, - true /** forward */, true /** bigWord */); - }, - '{': function(cm, inclusive) { - return selectCompanionObject(cm, '}', inclusive); - }, - '(': function(cm, inclusive) { - return selectCompanionObject(cm, ')', inclusive); - }, - '[': function(cm, inclusive) { - return selectCompanionObject(cm, ']', inclusive); - }, - '\'': function(cm, inclusive) { - return findBeginningAndEnd(cm, "'", inclusive); - }, - '"': function(cm, inclusive) { - return findBeginningAndEnd(cm, '"', inclusive); - } - }; - /* * Below are miscellaneous utility functions used by vim.js */ @@ -2634,13 +2626,25 @@ return cur; } + // TODO: perhaps this finagling of start and end positions belonds + // in codmirror/replaceRange? function selectCompanionObject(cm, revSymb, inclusive) { var cur = cm.getCursor(); - var end = findMatchedSymbol(cm, cur, revSymb); var start = findMatchedSymbol(cm, end); - start.ch += inclusive ? 1 : 0; - end.ch += inclusive ? 0 : 1; + + if((start.line == end.line && start.ch > end.ch) + || (start.line > end.line)) { + var tmp = start; + start = end; + end = tmp; + } + + if(inclusive) { + end.ch += 1; + } else { + start.ch += 1; + } return { start: start, end: end }; } @@ -2750,10 +2754,84 @@ if (!escapeNextChar && c == '/') { slashes.push(i); } - escapeNextChar = (c == '\\'); + escapeNextChar = !escapeNextChar && (c == '\\'); } return slashes; } + + // Translates a search string from ex (vim) syntax into javascript form. + function fixRegex(str) { + // When these match, add a '\' if unescaped or remove one if escaped. + var specials = ['|', '(', ')', '{']; + // Remove, but never add, a '\' for these. + var unescape = ['}']; + var escapeNextChar = false; + var out = []; + for (var i = -1; i < str.length; i++) { + var c = str.charAt(i) || ''; + var n = str.charAt(i+1) || ''; + var specialComesNext = (specials.indexOf(n) != -1); + if (escapeNextChar) { + if (c !== '\\' || !specialComesNext) { + out.push(c); + } + escapeNextChar = false; + } else { + if (c === '\\') { + escapeNextChar = true; + // Treat the unescape list as special for removing, but not adding '\'. + if (unescape.indexOf(n) != -1) { + specialComesNext = true; + } + // Not passing this test means removing a '\'. + if (!specialComesNext || n === '\\') { + out.push(c); + } + } else { + out.push(c); + if (specialComesNext && n !== '\\') { + out.push('\\'); + } + } + } + } + return out.join(''); + } + + // Translates the replace part of a search and replace from ex (vim) syntax into + // javascript form. Similar to fixRegex, but additionally fixes back references + // (translates '\[0..9]' to '$[0..9]') and follows different rules for escaping '$'. + function fixRegexReplace(str) { + var escapeNextChar = false; + var out = []; + for (var i = -1; i < str.length; i++) { + var c = str.charAt(i) || ''; + var n = str.charAt(i+1) || ''; + if (escapeNextChar) { + out.push(c); + escapeNextChar = false; + } else { + if (c === '\\') { + escapeNextChar = true; + if ((isNumber(n) || n === '$')) { + out.push('$'); + } else if (n !== '/' && n !== '\\') { + out.push('\\'); + } + } else { + if (c === '$') { + out.push('$'); + } + out.push(c); + if (n === '/') { + out.push('\\'); + } + } + } + } + return out.join(''); + } + /** * Extract the regular expression from the query and return a Regexp object. * Returns null if the query is blank. @@ -2785,6 +2863,7 @@ if (!regexPart) { return null; } + regexPart = fixRegex(regexPart); if (smartCase) { ignoreCase = (/^[^A-Z]*$/).test(regexPart); } @@ -3279,6 +3358,7 @@ var confirm = false; // Whether to confirm each replace. if (slashes[1]) { replacePart = argString.substring(slashes[1] + 1, slashes[2]); + replacePart = fixRegexReplace(replacePart); } if (slashes[2]) { // After the 3rd slash, we can have flags followed by a space followed @@ -3495,18 +3575,10 @@ * Shift + key modifier to the resulting letter, while preserving other * modifers. */ - // TODO: Figure out a way to catch capslock. function cmKeyToVimKey(key, modifier) { var vimKey = key; - if (isUpperCase(vimKey)) { - // Convert to lower case if shift is not the modifier since the key - // we get from CodeMirror is always upper case. - if (modifier == 'Shift') { - modifier = null; - } - else { + if (isUpperCase(vimKey) && modifier == 'Ctrl') { vimKey = vimKey.toLowerCase(); - } } if (modifier) { // Vim will parse modifier+key combination as a single key. @@ -3532,9 +3604,9 @@ function bindKeys(keys, modifier) { for (var i = 0; i < keys.length; i++) { var key = keys[i]; - if (!modifier && inArray(key, specialSymbols)) { - // Wrap special symbols with '' because that's how CodeMirror binds - // them. + if (!modifier && key.length == 1) { + // Wrap all keys without modifiers with '' to identify them by their + // key characters instead of key identifiers. key = "'" + key + "'"; } var vimKey = cmKeyToVimKey(keys[i], modifier); @@ -3543,7 +3615,7 @@ } } bindKeys(upperCaseAlphabet); - bindKeys(upperCaseAlphabet, 'Shift'); + bindKeys(lowerCaseAlphabet); bindKeys(upperCaseAlphabet, 'Ctrl'); bindKeys(specialSymbols); bindKeys(specialSymbols, 'Ctrl'); diff --git a/lib/codemirror.css b/lib/codemirror.css index 23eaf74d44..2b050e19f2 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -36,6 +36,8 @@ min-width: 20px; text-align: right; color: #999; + -moz-box-sizing: content-box; + box-sizing: content-box; } /* CURSOR */ @@ -59,6 +61,11 @@ .cm-tab { display: inline-block; } +.CodeMirror-ruler { + border-left: 1px solid #ccc; + position: absolute; +} + /* DEFAULT THEME */ .cm-s-default .cm-keyword {color: #708;} @@ -114,7 +121,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; - padding-bottom: 30px; padding-right: 30px; + padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; @@ -123,6 +130,9 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } .CodeMirror-sizer { position: relative; + border-right: 30px solid transparent; + -moz-box-sizing: content-box; + box-sizing: content-box; } /* The fake, visible scrollbars. Used to force redraw during scrolling @@ -197,16 +207,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} white-space: pre-wrap; word-break: normal; } -.CodeMirror-code pre { - border-right: 30px solid transparent; - width: -webkit-fit-content; - width: -moz-fit-content; - width: fit-content; -} -.CodeMirror-wrap .CodeMirror-code pre { - border-right: none; - width: auto; -} + .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; diff --git a/lib/codemirror.js b/lib/codemirror.js index 23ca5355b9..2eecf811d3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -13,6 +13,7 @@ window.CodeMirror = (function() { var old_ie = /MSIE \d/.test(navigator.userAgent); var ie_lt8 = old_ie && (document.documentMode == null || document.documentMode < 8); var ie_lt9 = old_ie && (document.documentMode == null || document.documentMode < 9); + var ie_lt10 = old_ie && (document.documentMode == null || document.documentMode < 10); var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent); var ie = old_ie || ie_gt10; var webkit = /WebKit\//.test(navigator.userAgent); @@ -36,7 +37,7 @@ window.CodeMirror = (function() { if (opera_version && opera_version >= 15) { opera = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11)); - var captureMiddleClick = gecko || (old_ie && !ie_lt9); + var captureMiddleClick = gecko || (ie && !ie_lt9); // Optimize some code when these features are not used var sawReadOnlySpans = false, sawCollapsedSpans = false; @@ -99,7 +100,7 @@ window.CodeMirror = (function() { function makeDisplay(place, docStart) { var d = {}; - var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;"); + var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none"); if (webkit) input.style.width = "1000px"; else input.setAttribute("wrap", "off"); // if border: 0; -- iOS fails to open keyboard (issue #1287) @@ -171,7 +172,7 @@ window.CodeMirror = (function() { // Self-resetting timeout for the poller d.poll = new Delayed(); - d.cachedCharWidth = d.cachedTextHeight = null; + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.measureLineCache = []; d.measureLineCachePos = 0; @@ -337,8 +338,9 @@ window.CodeMirror = (function() { if (needsV) { d.scrollbarV.style.display = "block"; d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; + // A bug in IE8 can cause this value to be negative, so guard it. d.scrollbarV.firstChild.style.height = - (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; + Math.max(0, scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; } else { d.scrollbarV.style.display = ""; d.scrollbarV.firstChild.style.height = "0"; @@ -818,12 +820,12 @@ window.CodeMirror = (function() { function updateSelectionRange(cm) { var display = cm.display, doc = cm.doc, sel = cm.doc.sel; var fragment = document.createDocumentFragment(); - var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display); + var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right; function add(left, top, width, bottom) { if (top < 0) top = 0; fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) + + "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px; height: " + (bottom - top) + "px")); } @@ -846,18 +848,18 @@ window.CodeMirror = (function() { left = leftPos.left; right = rightPos.right; } - if (fromArg == null && from == 0) left = pl; + if (fromArg == null && from == 0) left = leftSide; if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, null, leftPos.bottom); - left = pl; + left = leftSide; if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); } - if (toArg == null && to == lineLen) right = clientWidth; + if (toArg == null && to == lineLen) right = rightSide; if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) start = leftPos; if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) end = rightPos; - if (left < pl + 1) left = pl; + if (left < leftSide + 1) left = leftSide; add(left, rightPos.top, right - left, rightPos.bottom); }); return {start: start, end: end}; @@ -873,13 +875,13 @@ window.CodeMirror = (function() { if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); - add(pl, rightStart.top, rightStart.left, rightStart.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) - add(pl, leftEnd.bottom, null, rightStart.top); + add(leftSide, leftEnd.bottom, null, rightStart.top); } removeChildrenAndAdd(display.selectionDiv, fragment); @@ -982,9 +984,12 @@ window.CodeMirror = (function() { function paddingTop(display) {return display.lineSpace.offsetTop;} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;} - function paddingLeft(display) { - var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x")); - return e.offsetLeft; + function paddingH(display) { + if (display.cachedPaddingH) return display.cachedPaddingH; + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + return display.cachedPaddingH = {left: parseInt(style.paddingLeft), + right: parseInt(style.paddingRight)}; } function measureChar(cm, line, ch, data, bias) { @@ -1158,7 +1163,7 @@ window.CodeMirror = (function() { function clearCaches(cm) { cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; - cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; cm.display.lineNumChars = null; } @@ -1374,7 +1379,7 @@ window.CodeMirror = (function() { if (op.updateMaxLine) computeMaxLength(cm); if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) { var width = measureLineWidth(cm, display.maxLine); - display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px"; + display.sizer.style.minWidth = Math.max(0, width + 3) + "px"; display.maxLineChanged = false; var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth); if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos) @@ -1556,6 +1561,10 @@ window.CodeMirror = (function() { cm.display.input.focus(); } + function ensureFocus(cm) { + if (!cm.state.focused) { focusInput(cm); onFocus(cm); } + } + function isReadOnly(cm) { return cm.options.readOnly || cm.doc.cantEdit; } @@ -1612,7 +1621,7 @@ window.CodeMirror = (function() { if (resizeTimer == null) resizeTimer = setTimeout(function() { resizeTimer = null; // Might be a text scaling operation, clear size caches. - d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null; + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = knownScrollbarWidth = null; clearCaches(cm); runInOp(cm, bind(regChange, cm)); }, 100); @@ -1628,10 +1637,7 @@ window.CodeMirror = (function() { } setTimeout(unregister, 5000); - on(d.input, "keyup", operation(cm, function(e) { - if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - if (e.keyCode == 16) cm.doc.sel.shift = false; - })); + on(d.input, "keyup", operation(cm, onKeyUp)); on(d.input, "input", function() { if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; fastPoll(cm); @@ -1725,6 +1731,7 @@ window.CodeMirror = (function() { } if (clickInGutter(cm, e)) return; var start = posFromMouse(cm, e); + window.focus(); switch (e_button(e)) { case 3: @@ -1742,7 +1749,7 @@ window.CodeMirror = (function() { // selection. if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;} - if (!cm.state.focused) onFocus(cm); + setTimeout(bind(ensureFocus, cm), 0); var now = +new Date, type = "single"; if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { @@ -1822,7 +1829,7 @@ window.CodeMirror = (function() { var cur = posFromMouse(cm, e, true); if (!cur) return; if (!posEq(cur, last)) { - if (!cm.state.focused) onFocus(cm); + ensureFocus(cm); last = cur; doSelect(cur); var visible = visibleLines(display, doc); @@ -1847,7 +1854,7 @@ window.CodeMirror = (function() { } var move = operation(cm, function(e) { - if (!old_ie && !e_button(e)) done(e); + if ((ie && !ie_lt10) ? !e.buttons : !e_button(e)) done(e); else extend(e); }); var up = operation(cm, done); @@ -1992,7 +1999,7 @@ window.CodeMirror = (function() { // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). - if (old_ie) wheelPixelsPerUnit = -.53; + if (ie) wheelPixelsPerUnit = -.53; else if (gecko) wheelPixelsPerUnit = 15; else if (chrome) wheelPixelsPerUnit = -.7; else if (safari) wheelPixelsPerUnit = -1/3; @@ -2141,10 +2148,16 @@ window.CodeMirror = (function() { return handled; } + function onKeyUp(e) { + var cm = this; + if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (e.keyCode == 16) cm.doc.sel.shift = false; + } + var lastStoppedKey = null; function onKeyDown(e) { var cm = this; - if (!cm.state.focused) onFocus(cm); + ensureFocus(cm); if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; if (old_ie && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; @@ -2237,7 +2250,7 @@ window.CodeMirror = (function() { // Try to detect the user choosing select-all if (display.input.selectionStart != null) { - if (!old_ie || ie_lt9) prepareSelectAllHack(); + if (!ie || ie_lt9) prepareSelectAllHack(); clearTimeout(detectingSelectAll); var i = 0, poll = function(){ if (display.prevInput == "\u200b" && display.input.selectionStart == 0) @@ -2249,7 +2262,7 @@ window.CodeMirror = (function() { } } - if (old_ie && !ie_lt9) prepareSelectAllHack(); + if (ie && !ie_lt9) prepareSelectAllHack(); if (captureMiddleClick) { e_stop(e); var mouseup = function() { @@ -2746,15 +2759,16 @@ window.CodeMirror = (function() { // API UTILITIES function indentLine(cm, n, how, aggressive) { - var doc = cm.doc; + var doc = cm.doc, state; if (how == null) how = "add"; if (how == "smart") { if (!cm.doc.mode.indent) how = "prev"; - else var state = getStateBefore(cm, n); + else state = getStateBefore(cm, n); } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) line.stateAfter = null; var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; @@ -2829,13 +2843,15 @@ window.CodeMirror = (function() { if (dir < 0 && !moveOnce(!first)) break; var cur = lineObj.text.charAt(ch) || "\n"; var type = isWordChar(cur) ? "w" - : !group ? null - : /\s/.test(cur) ? null + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null : "p"; + if (group && !first && !type) type = "s"; if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce();} break; } + if (type) sawType = type; if (dir > 0 && !moveOnce(!first)) break; } @@ -3156,6 +3172,8 @@ window.CodeMirror = (function() { }, triggerOnKeyDown: operation(null, onKeyDown), + triggerOnKeyPress: operation(null, onKeyPress), + triggerOnKeyUp: operation(null, onKeyUp), execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) @@ -3222,8 +3240,10 @@ window.CodeMirror = (function() { this.display.cursor.className += " CodeMirror-overwrite"; else this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", ""); + + signal(this, "overwriteToggle", this, this.state.overwrite); }, - hasFocus: function() { return this.state.focused; }, + hasFocus: function() { return document.activeElement == this.display.input; }, scrollTo: operation(null, function(x, y) { updateScrollPos(this, x, y); @@ -3264,16 +3284,19 @@ window.CodeMirror = (function() { if (this.options.lineWrapping) this.display.measureLineCache.length = this.display.measureLineCachePos = 0; this.curOp.forceUpdate = true; + signal(this, "refresh", this); }), operation: function(f){return runInOp(this, f);}, refresh: operation(null, function() { - var badHeight = this.display.cachedTextHeight == null; + var oldHeight = this.display.cachedTextHeight; clearCaches(this); updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop); regChange(this); - if (badHeight) estimateLineHeights(this); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + estimateLineHeights(this); + signal(this, "refresh", this); }), swapDoc: operation(null, function(doc) { @@ -3424,6 +3447,7 @@ window.CodeMirror = (function() { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; + if (typeof found == "string") found = {name: found}; spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { @@ -3536,7 +3560,7 @@ window.CodeMirror = (function() { }, deleteLine: function(cm) { var l = cm.getCursor().line; - cm.replaceRange("", Pos(l, 0), Pos(l), "+delete"); + cm.replaceRange("", Pos(l, 0), Pos(l + 1, 0), "+delete"); }, delLineLeft: function(cm) { var cur = cm.getCursor(); @@ -3627,7 +3651,7 @@ window.CodeMirror = (function() { // default. Unknown commands are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", - "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", + "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", @@ -4344,6 +4368,7 @@ window.CodeMirror = (function() { var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) addToScrollPos(cm, 0, widget.height); + cm.curOp.forceUpdate = true; } return true; }); @@ -4475,7 +4500,7 @@ window.CodeMirror = (function() { function interpretTokenStyle(style, builder) { if (!style) return null; for (;;) { - var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/); + var lineClass = style.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) break; style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; @@ -4484,9 +4509,10 @@ window.CodeMirror = (function() { else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop])) builder[prop] += " " + lineClass[2]; } + if (/^\s*$/.test(style)) return null; var cache = builder.cm.options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || - (cache[style] = "cm-" + style.replace(/ +/g, " cm-")); + (cache[style] = style.replace(/\S+/g, "cm-$&")); } function buildLineContent(cm, realLine, measure, copyWidgets) { @@ -4503,7 +4529,7 @@ window.CodeMirror = (function() { builder.measure = line == realLine && measure; builder.pos = 0; builder.addToken = builder.measure ? buildTokenMeasure : buildToken; - if ((old_ie || webkit) && cm.getOption("lineWrapping")) + if ((ie || webkit) && cm.getOption("lineWrapping")) builder.addToken = buildTokenSplitSpaces(builder.addToken); var next = insertLineContent(line, builder, getLineStyles(cm, line)); if (measure && line == realLine && !builder.measuredSomething) { @@ -5046,6 +5072,22 @@ window.CodeMirror = (function() { } return markers; }, + findMarks: function(from, to) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function(line) { + var spans = line.markedSpans; + if (spans) for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(lineNo == from.line && from.ch > span.to || + span.from == null && lineNo != from.line|| + lineNo == to.line && span.from > to.ch)) + found.push(span.marker.parent || span.marker); + } + ++lineNo; + }); + return found; + }, getAllMarks: function() { var markers = []; this.iter(function(line) { @@ -5319,6 +5361,8 @@ window.CodeMirror = (function() { hist.lastTime = time; hist.lastOp = opId; hist.lastOrigin = change.origin; + + if (!last) signal(doc, "historyAdded"); } function removeClearedSpans(spans) { @@ -5600,7 +5644,7 @@ window.CodeMirror = (function() { return function(){return f.apply(null, args);}; } - var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordChar(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); @@ -6041,7 +6085,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.21.0"; + CodeMirror.version = "3.22.0"; return CodeMirror; })(); diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index ee22a12fed..a45a95de59 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -2,10 +2,11 @@ * Author: Hans Engel * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) */ -CodeMirror.defineMode("clojure", function () { +CodeMirror.defineMode("clojure", function (options) { var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword"; - var INDENT_WORD_SKIP = 2; + var INDENT_WORD_SKIP = options.indentUnit || 2; + var NORMAL_INDENT_UNIT = options.indentUnit || 2; function makeKeywords(str) { var obj = {}, words = str.split(" "); @@ -44,7 +45,7 @@ CodeMirror.defineMode("clojure", function () { sign: /[+-]/, exponent: /e/i, keyword_char: /[^\s\(\[\;\)\]]/, - symbol: /[\w*+!\-\._?:\/]/ + symbol: /[\w*+!\-\._?:<>\/]/ }; function stateStack(indent, type, prev) { // represents a state stack object @@ -179,8 +180,8 @@ CodeMirror.defineMode("clojure", function () { stream.eatSpace(); if (stream.eol() || stream.peek() == ";") { // nothing significant after - // we restart indentation 1 space after - pushStack(state, indentTemp + 1, ch); + // we restart indentation the user defined spaces after + pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch); } else { pushStack(state, indentTemp + stream.current().length, ch); // else we match } diff --git a/mode/css/css.js b/mode/css/css.js index 8f6fe7df4d..6cc4b71b05 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -140,6 +140,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return pushContext(state, stream, "media"); } else if (type == "@font-face") { return "font_face_before"; + } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { + return "keyframes"; } else if (type && type.charAt(0) == "@") { return pushContext(state, stream, "at"); } else if (type == "hash") { @@ -264,6 +266,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return "font_face"; }; + states.keyframes = function(type, stream, state) { + if (type == "word") { override = "variable"; return "keyframes"; } + if (type == "{") return pushContext(state, stream, "top"); + return pass(type, stream, state); + }; + states.at = function(type, stream, state) { if (type == ";") return popContext(state); if (type == "{" || type == "}") return popAndPass(type, stream, state); @@ -308,6 +316,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { indent: function(state, textAfter) { var cx = state.context, ch = textAfter && textAfter.charAt(0); var indent = cx.indent; + if (cx.type == "prop" && ch == "}") cx = cx.prev; if (cx.prev && (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") || @@ -353,10 +362,10 @@ CodeMirror.defineMode("css", function(config, parserConfig) { var propertyKeywords_ = [ "align-content", "align-items", "align-self", "alignment-adjust", "alignment-baseline", "anchor-point", "animation", "animation-delay", - "animation-direction", "animation-duration", "animation-iteration-count", - "animation-name", "animation-play-state", "animation-timing-function", - "appearance", "azimuth", "backface-visibility", "background", - "background-attachment", "background-clip", "background-color", + "animation-direction", "animation-duration", "animation-fill-mode", + "animation-iteration-count", "animation-name", "animation-play-state", + "animation-timing-function", "appearance", "azimuth", "backface-visibility", + "background", "background-attachment", "background-clip", "background-color", "background-image", "background-origin", "background-position", "background-repeat", "background-size", "baseline-shift", "binding", "bleed", "bookmark-label", "bookmark-level", "bookmark-state", @@ -387,10 +396,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "font-stretch", "font-style", "font-synthesis", "font-variant", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position", - "font-weight", "grid-cell", "grid-column", "grid-column-align", - "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", - "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", - "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", + "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", + "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end", + "grid-column-start", "grid-row", "grid-row-end", "grid-row-start", + "grid-template", "grid-template-areas", "grid-template-columns", + "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon", "image-orientation", "image-rendering", "image-resolution", "inline-box-align", "justify-content", "left", "letter-spacing", "line-break", "line-height", "line-stacking", "line-stacking-ruby", @@ -667,7 +677,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { } }, "@": function(stream) { - if (stream.match(/^(charset|document|font-face|import|keyframes|media|namespace|page|supports)\b/, false)) return false; + if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; stream.eatWhile(/[\w\\\-]/); if (stream.match(/^\s*:/, false)) return ["variable-2", "variable-definition"]; diff --git a/mode/eiffel/index.html b/mode/eiffel/index.html index 5d395a05cd..95c5f885b0 100644 --- a/mode/eiffel/index.html +++ b/mode/eiffel/index.html @@ -417,7 +417,6 @@ diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index e9eab3b92d..8cc9c4e723 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -1,5 +1,8 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { - var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); + var htmlMode = CodeMirror.getMode(config, {name: "xml", + htmlMode: true, + multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, + multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag}); var cssMode = CodeMirror.getMode(config, "css"); var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes; diff --git a/mode/htmlmixed/index.html b/mode/htmlmixed/index.html index b46174351a..bd06bb504c 100644 --- a/mode/htmlmixed/index.html +++ b/mode/htmlmixed/index.html @@ -62,7 +62,7 @@ {matches: /(text|application)\/(x-)?vb(a|script)/i, mode: "vbscript"}] }; - var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: mixedMode, tabMode: "indent"}); + var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: mixedMode});

The HTML mixed mode depends on the XML, JavaScript, and CSS modes.

diff --git a/mode/index.html b/mode/index.html index 7c1f740615..26ea93ef02 100644 --- a/mode/index.html +++ b/mode/index.html @@ -77,6 +77,7 @@
  • PHP
  • Pig Latin
  • Properties files
  • +
  • Puppet
  • Python
  • Q
  • R
  • @@ -93,6 +94,7 @@
  • Smalltalk
  • Smarty
  • Smarty/HTML mixed
  • +
  • Solr
  • SQL (several dialects)
  • SPARQL
  • sTeX, LaTeX
  • diff --git a/mode/javascript/index.html b/mode/javascript/index.html index 45d70ffdbb..faf50363e8 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -89,11 +89,13 @@

    - JavaScript mode supports a two configuration - options: + JavaScript mode supports several configuration options:

    • json which will set the mode to expect JSON data rather than a JavaScript program.
    • +
    • jsonld which will set the mode to expect + JSON-LD linked data rather + than a JavaScript program (demo).
    • typescript which will activate additional syntax highlighting and some other things for TypeScript code (demo).
    • @@ -103,5 +105,5 @@

    -

    MIME types defined: text/javascript, application/json, text/typescript, application/typescript.

    +

    MIME types defined: text/javascript, application/json, application/ld+json, text/typescript, application/typescript.

    diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fbf574b4fe..07ba8a6f4b 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -3,7 +3,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var statementIndent = parserConfig.statementIndent; - var jsonMode = parserConfig.json; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; var isTS = parserConfig.typescript; // Tokenizer @@ -53,6 +54,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }(); var isOperatorChar = /[+\-*&%=<>!?|~^]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; function readRegexp(stream) { var escaped = false, next, inSet = false; @@ -128,6 +130,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function tokenString(quote) { return function(stream, state) { var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } while ((next = stream.next()) != null) { if (next == quote && !escaped) break; escaped = !escaped && next == "\\"; @@ -195,7 +201,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { // Parser - var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true}; + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; function JSLexical(indented, column, type, align, prev, info) { this.indented = indented; @@ -408,7 +414,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.marked = "property"; if (value == "get" || value == "set") return cont(getterSetter); } else if (type == "number" || type == "string") { - cx.marked = type + " property"; + cx.marked = jsonldMode ? "property" : (type + " property"); } else if (type == "[") { return cont(expression, expect("]"), afterprop); } @@ -616,6 +622,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { fold: "brace", helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, jsonMode: jsonMode }; }); @@ -626,5 +633,6 @@ CodeMirror.defineMIME("application/javascript", "javascript"); CodeMirror.defineMIME("application/ecmascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/mode/javascript/json-ld.html b/mode/javascript/json-ld.html new file mode 100644 index 0000000000..256bb0f624 --- /dev/null +++ b/mode/javascript/json-ld.html @@ -0,0 +1,72 @@ + + +CodeMirror: JSON-LD mode + + + + + + + + + + + + +
    +

    JSON-LD mode

    + + +
    + + + +

    This is a specialization of the JavaScript mode.

    +
    diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 911bbbfd7b..3f73196118 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -110,4 +110,42 @@ MT("scary_regexp", "[string-2 /foo[[/]]bar/];"); + + var jsonld_mode = CodeMirror.getMode( + {indentUnit: 2}, + {name: "javascript", jsonld: true} + ); + function LD(name) { + test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1)); + } + + LD("json_ld_keywords", + '{', + ' [meta "@context"]: {', + ' [meta "@base"]: [string "http://example.com"],', + ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],', + ' [property "likesFlavor"]: {', + ' [meta "@container"]: [meta "@list"]', + ' [meta "@reverse"]: [string "@beFavoriteOf"]', + ' },', + ' [property "nick"]: { [meta "@container"]: [meta "@set"] },', + ' [property "nick"]: { [meta "@container"]: [meta "@index"] }', + ' },', + ' [meta "@graph"]: [[ {', + ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],', + ' [property "name"]: [string "John Lennon"],', + ' [property "modified"]: {', + ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],', + ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]', + ' }', + ' } ]]', + '}'); + + LD("json_ld_fake", + '{', + ' [property "@fake"]: [string "@fake"],', + ' [property "@contextual"]: [string "@identifier"],', + ' [property "user@domain.com"]: [string "@graphical"],', + ' [property "@ID"]: [string "@@ID"]', + '}'); })(); diff --git a/mode/julia/index.html b/mode/julia/index.html index dd567a623b..945e1a91fc 100644 --- a/mode/julia/index.html +++ b/mode/julia/index.html @@ -178,7 +178,6 @@ }, lineNumbers: true, indentUnit: 4, - tabMode: "shift", matchBrackets: true }); diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 9ec2428cd4..cc17eb0157 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -5,21 +5,22 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } - var operators = parserConf.operators || /^(?:\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|<:|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b|\.{3})/; + var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/; var delimiters = parserConf.delimiters || /^[;,()[\]{}]/; var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*!*/; - var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch"]; + var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"]; var blockClosers = ["end", "else", "elseif", "catch", "finally"]; var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall']; - var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf']; + var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf']; //var stringPrefixes = new RegExp("^[br]?('|\")") - var stringPrefixes = /^[br]?('|"{3}|")/; + var stringPrefixes = /^(`|'|"{3}|([br]?"))/; var keywords = wordRegexp(keywordList); var builtins = wordRegexp(builtinList); var openers = wordRegexp(blockOpeners); var closers = wordRegexp(blockClosers); - var macro = /@[_A-Za-z][_A-Za-z0-9]*!*/; + var macro = /^@[_A-Za-z][_A-Za-z0-9]*/; + var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/; var indentInfo = null; function in_array(state) { @@ -43,14 +44,19 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { function tokenBase(stream, state) { // Handle scope changes var leaving_expr = state.leaving_expr; + if(stream.sol()) { + leaving_expr = false; + } state.leaving_expr = false; if(leaving_expr) { if(stream.match(/^'+/)) { return 'operator'; } - if(stream.match("...")) { - return 'operator'; - } + + } + + if(stream.match(/^\.{2,3}/)) { + return 'operator'; } if (stream.eatSpace()) { @@ -83,8 +89,12 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { state.leaving_expr=true; } + if(ch===')') { + state.leaving_expr = true; + } + var match; - if(match=stream.match(openers, false)) { + if(!in_array(state) && (match=stream.match(openers, false))) { state.scopes.push(match); } @@ -93,25 +103,29 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { } if(in_array(state)) { - if(stream.match("end")) { + if(stream.match(/^end/)) { return 'number'; } } - if(stream.match("=>")) { + + if(stream.match(/^=>/)) { return 'operator'; } + + // Handle Number Literals if (stream.match(/^[0-9\.]/, false)) { var imMatcher = RegExp(/^im\b/); var floatLiteral = false; // Floats - if (stream.match(/^\d*\.\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; } - if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; } if (stream.match(/^\.\d+/)) { floatLiteral = true; } if (floatLiteral) { // Float literals may be "imaginary" stream.match(imMatcher); + state.leaving_expr = true; return 'number'; } // Integers @@ -124,9 +138,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } // Decimal if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { - // Decimal literals may be "imaginary" - stream.eat(/J/i); - // TODO - Can you have imaginary longs? intLiteral = true; } // Zero by itself with no other piece of number. @@ -134,21 +145,37 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (intLiteral) { // Integer literals may be "long" stream.match(imMatcher); + state.leaving_expr = true; return 'number'; } } + if(stream.match(/^(::)|(<:)/)) { + return 'operator'; + } + + // Handle symbols + if(!leaving_expr && stream.match(symbol)) { + return 'string'; + } + + // Handle operators and Delimiters + if (stream.match(operators)) { + return 'operator'; + } + + // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } - // Handle operators and Delimiters - if (stream.match(operators)) { - return 'operator'; + if (stream.match(macro)) { + return 'meta'; } + if (stream.match(delimiters)) { return null; } @@ -161,9 +188,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return 'builtin'; } - if (stream.match(macro)) { - return 'meta'; - } if (stream.match(identifiers)) { state.leaving_expr=true; @@ -248,7 +272,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") { delta = -1; } - return (state.scopes.length + delta) * 2; + return (state.scopes.length + delta) * 4; }, lineComment: "#", diff --git a/mode/livescript/index.html b/mode/livescript/index.html index b5944697d8..3ce8974854 100644 --- a/mode/livescript/index.html +++ b/mode/livescript/index.html @@ -454,6 +454,6 @@

    MIME types defined: text/x-livescript.

    -

    The LiveScript mode was written by Kenneth Bentley (license).

    +

    The LiveScript mode was written by Kenneth Bentley.

    diff --git a/mode/lua/index.html b/mode/lua/index.html index 69433e44ab..7f4eb2c84a 100644 --- a/mode/lua/index.html +++ b/mode/lua/index.html @@ -69,7 +69,6 @@ diff --git a/mode/octave/octave.js b/mode/octave/octave.js index 23cd2fe222..dbf4ce1d17 100644 --- a/mode/octave/octave.js +++ b/mode/octave/octave.js @@ -13,17 +13,19 @@ CodeMirror.defineMode("octave", function() { var builtins = wordRegexp([ 'error', 'eval', 'function', 'abs', 'acos', 'atan', 'asin', 'cos', - 'cosh', 'exp', 'log', 'prod', 'log10', 'max', 'min', 'sign', 'sin', 'sinh', + 'cosh', 'exp', 'log', 'prod', 'sum', 'log10', 'max', 'min', 'sign', 'sin', 'sinh', 'sqrt', 'tan', 'reshape', 'break', 'zeros', 'default', 'margin', 'round', 'ones', 'rand', 'syn', 'ceil', 'floor', 'size', 'clear', 'zeros', 'eye', 'mean', 'std', 'cov', 'det', 'eig', 'inv', 'norm', 'rank', 'trace', 'expm', 'logm', 'sqrtm', 'linspace', 'plot', - 'title', 'xlabel', 'ylabel', 'legend', 'text', 'meshgrid', 'mesh', 'num2str' + 'title', 'xlabel', 'ylabel', 'legend', 'text', 'grid', 'meshgrid', 'mesh', 'num2str', + 'fft', 'ifft', 'arrayfun', 'cellfun', 'input', 'fliplr', 'flipud', 'ismember' ]); var keywords = wordRegexp([ 'return', 'case', 'switch', 'else', 'elseif', 'end', 'endif', 'endfunction', 'if', 'otherwise', 'do', 'for', 'while', 'try', 'catch', 'classdef', 'properties', 'events', - 'methods', 'global', 'persistent', 'endfor', 'endwhile', 'printf', 'disp', 'until', 'continue' + 'methods', 'global', 'persistent', 'endfor', 'endwhile', 'printf', 'sprintf', 'disp', 'until', + 'continue', 'pkg' ]); @@ -59,7 +61,7 @@ CodeMirror.defineMode("octave", function() { return 'comment'; } - if (stream.match(/^(%)|(\.\.\.)/)){ + if (stream.match(/^[%#]/)){ stream.skipToEnd(); return 'comment'; } diff --git a/mode/php/index.html b/mode/php/index.html index aaf51c0f5a..1fb7435bb1 100644 --- a/mode/php/index.html +++ b/mode/php/index.html @@ -48,9 +48,7 @@ matchBrackets: true, mode: "application/x-httpd-php", indentUnit: 4, - indentWithTabs: true, - enterMode: "keep", - tabMode: "shift" + indentWithTabs: true }); diff --git a/mode/puppet/index.html b/mode/puppet/index.html new file mode 100644 index 0000000000..f307636b2e --- /dev/null +++ b/mode/puppet/index.html @@ -0,0 +1,121 @@ + + +CodeMirror: Puppet mode + + + + + + + + + + +
    +

    Puppet mode

    +
    + + +

    MIME types defined: text/x-puppet.

    + +
    diff --git a/mode/puppet/puppet.js b/mode/puppet/puppet.js new file mode 100644 index 0000000000..0153c80120 --- /dev/null +++ b/mode/puppet/puppet.js @@ -0,0 +1,204 @@ +CodeMirror.defineMode("puppet", function () { + // Stores the words from the define method + var words = {}; + // Taken, mostly, from the Puppet official variable standards regex + var variable_regex = /({)?([a-z][a-z0-9_]*)?((::[a-z][a-z0-9_]*)*::)?[a-zA-Z0-9_]+(})?/; + + // Takes a string of words separated by spaces and adds them as + // keys with the value of the first argument 'style' + function define(style, string) { + var split = string.split(' '); + for (var i = 0; i < split.length; i++) { + words[split[i]] = style; + } + } + + // Takes commonly known puppet types/words and classifies them to a style + define('keyword', 'class define site node include import inherits'); + define('keyword', 'case if else in and elsif default or'); + define('atom', 'false true running present absent file directory undef'); + define('builtin', 'action augeas burst chain computer cron destination dport exec ' + + 'file filebucket group host icmp iniface interface jump k5login limit log_level ' + + 'log_prefix macauthorization mailalias maillist mcx mount nagios_command ' + + 'nagios_contact nagios_contactgroup nagios_host nagios_hostdependency ' + + 'nagios_hostescalation nagios_hostextinfo nagios_hostgroup nagios_service ' + + 'nagios_servicedependency nagios_serviceescalation nagios_serviceextinfo ' + + 'nagios_servicegroup nagios_timeperiod name notify outiface package proto reject ' + + 'resources router schedule scheduled_task selboolean selmodule service source ' + + 'sport ssh_authorized_key sshkey stage state table tidy todest toports tosource ' + + 'user vlan yumrepo zfs zone zpool'); + + // After finding a start of a string ('|") this function attempts to find the end; + // If a variable is encountered along the way, we display it differently when it + // is encapsulated in a double-quoted string. + function tokenString(stream, state) { + var current, prev, found_var = false; + while (!stream.eol() && (current = stream.next()) != state.pending) { + if (current === '$' && prev != '\\' && state.pending == '"') { + found_var = true; + break; + } + prev = current; + } + if (found_var) { + stream.backUp(1); + } + if (current == state.pending) { + state.continueString = false; + } else { + state.continueString = true; + } + return "string"; + } + + // Main function + function tokenize(stream, state) { + // Matches one whole word + var word = stream.match(/[\w]+/, false); + // Matches attributes (i.e. ensure => present ; 'ensure' would be matched) + var attribute = stream.match(/(\s+)?\w+\s+=>.*/, false); + // Matches non-builtin resource declarations + // (i.e. "apache::vhost {" or "mycustomclasss {" would be matched) + var resource = stream.match(/(\s+)?[\w:_]+(\s+)?{/, false); + // Matches virtual and exported resources (i.e. @@user { ; and the like) + var special_resource = stream.match(/(\s+)?[@]{1,2}[\w:_]+(\s+)?{/, false); + + // Finally advance the stream + var ch = stream.next(); + + // Have we found a variable? + if (ch === '$') { + if (stream.match(variable_regex)) { + // If so, and its in a string, assign it a different color + return state.continueString ? 'variable-2' : 'variable'; + } + // Otherwise return an invalid variable + return "error"; + } + // Should we still be looking for the end of a string? + if (state.continueString) { + // If so, go through the loop again + stream.backUp(1); + return tokenString(stream, state); + } + // Are we in a definition (class, node, define)? + if (state.inDefinition) { + // If so, return def (i.e. for 'class myclass {' ; 'myclass' would be matched) + if (stream.match(/(\s+)?[\w:_]+(\s+)?/)) { + return 'def'; + } + // Match the rest it the next time around + stream.match(/\s+{/); + state.inDefinition = false; + } + // Are we in an 'include' statement? + if (state.inInclude) { + // Match and return the included class + stream.match(/(\s+)?\S+(\s+)?/); + state.inInclude = false; + return 'def'; + } + // Do we just have a function on our hands? + // In 'ensure_resource("myclass")', 'ensure_resource' is matched + if (stream.match(/(\s+)?\w+\(/)) { + stream.backUp(1); + return 'def'; + } + // Have we matched the prior attribute regex? + if (attribute) { + stream.match(/(\s+)?\w+/); + return 'tag'; + } + // Do we have Puppet specific words? + if (word && words.hasOwnProperty(word)) { + // Negates the initial next() + stream.backUp(1); + // Acutally move the stream + stream.match(/[\w]+/); + // We want to process these words differently + // do to the importance they have in Puppet + if (stream.match(/\s+\S+\s+{/, false)) { + state.inDefinition = true; + } + if (word == 'include') { + state.inInclude = true; + } + // Returns their value as state in the prior define methods + return words[word]; + } + // Is there a match on a reference? + if (/(\s+)?[A-Z]/.test(word)) { + // Negate the next() + stream.backUp(1); + // Match the full reference + stream.match(/(\s+)?[A-Z][\w:_]+/); + return 'def'; + } + // Have we matched the prior resource regex? + if (resource) { + stream.match(/(\s+)?[\w:_]+/); + return 'def'; + } + // Have we matched the prior special_resource regex? + if (special_resource) { + stream.match(/(\s+)?[@]{1,2}/); + return 'special'; + } + // Match all the comments. All of them. + if (ch == "#") { + stream.skipToEnd(); + return "comment"; + } + // Have we found a string? + if (ch == "'" || ch == '"') { + // Store the type (single or double) + state.pending = ch; + // Perform the looping function to find the end + return tokenString(stream, state); + } + // Match all the brackets + if (ch == '{' || ch == '}') { + return 'bracket'; + } + // Match characters that we are going to assume + // are trying to be regex + if (ch == '/') { + stream.match(/.*\//); + return 'variable-3'; + } + // Match all the numbers + if (ch.match(/[0-9]/)) { + stream.eatWhile(/[0-9]+/); + return 'number'; + } + // Match the '=' and '=>' operators + if (ch == '=') { + if (stream.peek() == '>') { + stream.next(); + } + return "operator"; + } + // Keep advancing through all the rest + stream.eatWhile(/[\w-]/); + // Return a blank line for everything else + return null; + } + // Start it all + return { + startState: function () { + var state = {}; + state.inDefinition = false; + state.inInclude = false; + state.continueString = false; + state.pending = false; + return state; + }, + token: function (stream, state) { + // Strip the spaces, but regex will account for them eitherway + if (stream.eatSpace()) return null; + // Go through the main process + return tokenize(stream, state); + } + }; +}); +CodeMirror.defineMIME("text/x-puppet", "puppet"); diff --git a/mode/python/index.html b/mode/python/index.html index e8422fb77a..a1d6d2d838 100644 --- a/mode/python/index.html +++ b/mode/python/index.html @@ -150,7 +150,6 @@ singleLineStringErrors: false}, lineNumbers: true, indentUnit: 4, - tabMode: "shift", matchBrackets: true }); @@ -160,7 +159,6 @@ singleLineStringErrors: false}, lineNumbers: true, indentUnit: 4, - tabMode: "shift", matchBrackets: true }); diff --git a/mode/python/python.js b/mode/python/python.js index 8bea5d19d1..2a7445209a 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -151,6 +151,10 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return 'builtin'; } + if (stream.match(/^(self|cls)\b/)) { + return "variable-2"; + } + if (stream.match(identifiers)) { if (state.lastToken == 'def' || state.lastToken == 'class') { return 'def'; diff --git a/mode/rpm/changes/index.html b/mode/rpm/changes/index.html index 18fe7ab7bf..0ade017966 100644 --- a/mode/rpm/changes/index.html +++ b/mode/rpm/changes/index.html @@ -58,8 +58,7 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: {name: "changes"}, lineNumbers: true, - indentUnit: 4, - tabMode: "shift" + indentUnit: 4 }); diff --git a/mode/rst/index.html b/mode/rst/index.html index 78030ebe94..cb8269751a 100644 --- a/mode/rst/index.html +++ b/mode/rst/index.html @@ -6,6 +6,7 @@ +