diff --git a/.travis.yml b/.travis.yml index baa0031d50..20fd86b6a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - 0.8 + - 0.10 diff --git a/AUTHORS b/AUTHORS index afa3cc083a..2ca41e67b7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -43,6 +43,7 @@ Andy Kimball Andy Li angelozerr angelo.zerr@gmail.com +Ankit Ankit Ahuja Ansel Santosa Anthony Grimes @@ -138,6 +139,7 @@ flack ForbesLindesay Forbes Lindesay Ford_Lawnmower +Forrest Oliphant Frank Wiegand Gabriel Gheorghian Gabriel Horner @@ -256,6 +258,7 @@ Marcel Gerber Marco Aurélio Marco Munizaga Marcus Bointon +Marek Rudnicki Marijn Haverbeke Mário Gonçalves Mario Pietsch @@ -383,6 +386,7 @@ stoskov Taha Jahangir Takuji Shimokawa Tarmil +tel tfjgeorge Thaddee Tyl TheHowl diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index a0bec7dd43..369bea30c4 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -94,15 +94,15 @@ } } - function autoCloseSlash(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; + function autoCloseCurrent(cm, typingSlash) { var ranges = cm.listSelections(), replacements = []; + var head = typingSlash ? "/" : ""; + replacements[i] = head + "script>"; else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") - replacements[i] = "/style>"; + replacements[i] = head + "style>"; else return CodeMirror.Pass; } else { if (!state.context || !state.context.tagName || closingTagExists(cm, state.context.tagName, pos, state)) return CodeMirror.Pass; - replacements[i] = "/" + state.context.tagName + ">"; + replacements[i] = head + state.context.tagName + ">"; } } cm.replaceSelections(replacements); @@ -129,6 +129,13 @@ cm.indentLine(ranges[i].head.line); } + function autoCloseSlash(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + autoCloseCurrent(cm, true); + } + + CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); }; + function indexOf(collection, elt) { if (collection.indexOf) return collection.indexOf(elt); for (var i = 0, e = collection.length; i < e; ++i) diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 3abeb83e76..62911f9352 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -142,4 +142,8 @@ return editorOptions[name]; return defaultOptions[name]; } + + CodeMirror.defineExtension("foldOption", function(options, name) { + return getOption(this, options, name); + }); }); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index bd31ec4d9b..3359476796 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -67,14 +67,16 @@ function updateFoldInfo(cm, from, to) { var opts = cm.state.foldGutter.options, cur = from; + var minSize = cm.foldOption(opts, "minFoldSize"); + var func = cm.foldOption(opts, "rangeFinder"); cm.eachLine(from, to, function(line) { var mark = null; if (isFolded(cm, cur)) { mark = marker(opts.indicatorFolded); } else { - var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; + var pos = Pos(cur, 0); var range = func && func(cm, pos); - if (range && range.from.line + 1 < range.to.line) + if (range && range.to.line - range.from.line >= minSize) mark = marker(opts.indicatorOpen); } cm.setGutterMarker(line, opts.gutter, mark); diff --git a/addon/merge/merge.js b/addon/merge/merge.js index ed22b60024..3e9df42fb7 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -259,11 +259,13 @@ function makeConnections(dv) { if (!dv.showDifferences) return; - var align = dv.mv.options.connect == "align"; + var align = dv.mv.options.connect == "align", oldScrollEdit, oldScrollOrig; if (align) { if (!dv.orig.curOp) return dv.orig.operation(function() { makeConnections(dv); }); + oldScrollEdit = dv.edit.getScrollInfo().top; + oldScrollOrig = dv.orig.getScrollInfo().top; for (var i = 0; i < dv.aligners.length; i++) dv.aligners[i].clear(); dv.aligners.length = 0; @@ -293,6 +295,8 @@ dv.aligners.push(padBelow(dv.edit, 0, extraSpaceAbove.edit)); if (extraSpaceAbove.orig) dv.aligners.push(padBelow(dv.orig, 0, extraSpaceAbove.orig)); + dv.edit.scrollTo(null, oldScrollEdit); + dv.orig.scrollTo(null, oldScrollOrig); } } diff --git a/addon/mode/simple.js b/addon/mode/simple.js index 0a48a95367..795328b83d 100644 --- a/addon/mode/simple.js +++ b/addon/mode/simple.js @@ -116,7 +116,7 @@ var curState = states[state.state]; for (var i = 0; i < curState.length; i++) { var rule = curState[i]; - var matches = stream.match(rule.regex); + var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex); if (matches) { if (rule.data.next) { state.state = rule.data.next; diff --git a/addon/scroll/simplescrollbars.js b/addon/scroll/simplescrollbars.js index 739aa7beb4..bb06adb86a 100644 --- a/addon/scroll/simplescrollbars.js +++ b/addon/scroll/simplescrollbars.js @@ -27,14 +27,16 @@ CodeMirror.e_preventDefault(e); var axis = self.orientation == "horizontal" ? "pageX" : "pageY"; var start = e[axis], startpos = self.pos; + function done() { + CodeMirror.off(document, "mousemove", move); + CodeMirror.off(document, "mouseup", done); + } function move(e) { - if (e.which != 1) { - CodeMirror.off(document, "mousemove", move); - return; - } + if (e.which != 1) return done(); self.moveTo(startpos + (e[axis] - start) * (self.total / self.size)); } CodeMirror.on(document, "mousemove", move); + CodeMirror.on(document, "mouseup", done); }); CodeMirror.on(this.node, "click", function(e) { diff --git a/addon/selection/selection-pointer.js b/addon/selection/selection-pointer.js new file mode 100644 index 0000000000..8cc0fc6860 --- /dev/null +++ b/addon/selection/selection-pointer.js @@ -0,0 +1,95 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineOption("selectionPointer", false, function(cm, val) { + var data = cm.state.selectionPointer; + if (data) { + CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove); + CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout); + cm.off("cursorActivity", reset); + cm.off("scroll", reset); + cm.state.selectionPointer = null; + cm.display.lineDiv.style.cursor = ""; + } + if (val) { + data = cm.state.selectionPointer = { + value: typeof val == "string" ? val : "default", + mousemove: function(event) { mousemove(cm, event); }, + mouseout: function(event) { mouseout(cm, event); }, + rects: null, + mouseX: null, mouseY: null, + willUpdate: false + }; + CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove); + CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout); + cm.on("cursorActivity", reset); + cm.on("scroll", reset); + } + }); + + function mousemove(cm, event) { + var data = cm.state.selectionPointer; + if (event.buttons == null ? event.which : event.buttons) { + data.mouseX = data.mouseY = null; + } else { + data.mouseX = event.clientX; + data.mouseY = event.clientY; + } + scheduleUpdate(cm); + } + + function mouseout(cm, event) { + if (!cm.getWrapperElement().contains(event.relatedTarget)) { + var data = cm.state.selectionPointer; + data.mouseX = data.mouseY = null; + scheduleUpdate(cm); + } + } + + function reset(cm) { + cm.state.selectionPointer.rects = null; + scheduleUpdate(cm); + } + + function scheduleUpdate(cm) { + if (!cm.state.selectionPointer.willUpdate) { + cm.state.selectionPointer.willUpdate = true; + setTimeout(function() { + update(cm); + cm.state.selectionPointer.willUpdate = false; + }, 50); + } + } + + function update(cm) { + var data = cm.state.selectionPointer; + if (!data) return; + if (data.rects == null && data.mouseX != null) { + data.rects = []; + if (cm.somethingSelected()) { + for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling) + data.rects.push(sel.getBoundingClientRect()); + } + } + var inside = false; + if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) { + var rect = data.rects[i]; + if (rect.left <= data.mouseX && rect.right >= data.mouseX && + rect.top <= data.mouseY && rect.bottom >= data.mouseY) + inside = true; + } + var cursor = inside ? data.value : ""; + if (cm.display.lineDiv.style.cursor != cursor) + cm.display.lineDiv.style.cursor = cursor; + } +}); diff --git a/bin/lint b/bin/lint index 4f70994c51..47f45f3676 100755 --- a/bin/lint +++ b/bin/lint @@ -1,16 +1,3 @@ #!/usr/bin/env node -var lint = require("../test/lint/lint"), - path = require("path"); - -if (process.argv.length > 2) { - lint.checkDir(process.argv[2]); -} else { - process.chdir(path.resolve(__dirname, "..")); - lint.checkDir("lib"); - lint.checkDir("mode"); - lint.checkDir("addon"); - lint.checkDir("keymap"); -} - -process.exit(lint.success() ? 0 : 1); +process.exit(require("../test/lint").ok ? 0 : 1); diff --git a/bower.json b/bower.json index 318a2454f1..d378331c4c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"4.11.0", + "version":"4.12.0", "main": ["lib/codemirror.js", "lib/codemirror.css"], "ignore": [ "**/.*", diff --git a/demo/simplemode.html b/demo/simplemode.html index ad8baf0b50..49778ef1e2 100644 --- a/demo/simplemode.html +++ b/demo/simplemode.html @@ -74,6 +74,11 @@ string (since JS provides no way to find out where a group matched), and this property must hold an array of token styles that has one style for each matched group. +
sol: boolean
+
When true, this token will only match at the start of the line. + (The ^ regexp marker doesn't work as you'd expect in + this context because of limitations in JavaScript's RegExp + API.)
next: string
When a next property is present, the mode will transfer to the state named by the property when the token is diff --git a/doc/compress.html b/doc/compress.html index 53314b7514..cf785ef75a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

Version: