diff --git a/AUTHORS b/AUTHORS index 260c13a864..737acedaba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,7 @@ List of CodeMirror contributors. Updated before every release. 4r2r Aaron Brooks +Abe Fettig Adam King adanlobato Adán Lobato @@ -10,16 +11,20 @@ Ahmad Amireh Ahmad M. Zawawi ahoward Akeksandr Motsjonov +Alberto Pose Albert Xing Alexander Pavlov Alexander Schepanovski +Alexander Solovyov alexey-k Alex Piggott Amy Ananya Sen AndersMad +Anderson Mesquita Andre von Houck Andrey Lushnikov +Andy Joslin Andy Kimball Andy Li angelozerr @@ -27,16 +32,20 @@ angelo.zerr@gmail.com Ankit Ahuja Ansel Santosa Anthony Grimes +Anton Kovalyov areos +AtomicPages LLC Atul Bhouraskar Aurelian Oancea Bastian Müller benbro +Beni Cherniavsky-Paskin Benjamin DeCoste Ben Keen boomyjee borawjm Brandon Frohs +Brandon Wamboldt Brett Zamir Brian Sletten Bruce Mitchener @@ -54,9 +63,11 @@ Dan Heberden Daniel, Dao Quang Minh Daniel Faust Daniel Huigens +Daniel KJ Daniel Neel Daniel Parnell Danny Yoo +Darius Roberts David Mignot David Pathakjee deebugger @@ -68,10 +79,13 @@ Drew Hintz Drew Khoury Dror BG duralog +eborden edsharp ekhaled +Enam Mijbah Noor Eric Allam eustas +Fabio Zendhi Nagao Fauntleroy fbuchinger feizhang365 @@ -80,6 +94,7 @@ Felix Raab Filip Noetzel flack ForbesLindesay +Forbes Lindesay Ford_Lawnmower Gabriel Nahmias galambalazs @@ -94,12 +109,15 @@ Guillaume Massé Hans Engel Hardest Hasan Karahan +hitsthings Hocdoc Ian Beck +Ian Dickinson Ian Wehrman Ian Wetherbee Ice White ICHIKAWA, Yuji +ilvalle Ingo Richter Irakli Gozalishvili Ivan Kurnosov @@ -111,6 +129,7 @@ Jamie Hill Jan Jongboom jankeromnes Jan Keromnes +Jan Odvarko Jan T. Sott Jason Jason Grout @@ -122,16 +141,21 @@ jeffkenton Jeff Pickhardt jem (graphite) Jochen Berger +Johan Ask John Connor John Lees-Miller John Snelson +John Van Der Loo jongalloway +Jon Malmaud Joost-Wim Boekesteijn Joseph Pecoraro Joshua Newman jots +jsoojeon Juan Benavides Romero Jucovschi Constantin +Juho Vuori jwallers@gmail.com kaniga Ken Newman @@ -145,11 +169,13 @@ koops ks-ifware kubelsmieci Lanny +Laszlo Vidacs leaf corcoran Leonya Khachaturov Liam Newman LM Lorenzo Stoakes +Luciano Longo lynschinzer Maksim Lin Maksym Taran @@ -158,8 +184,10 @@ Marco Aurélio Marijn Haverbeke Mario Pietsch Mark Lentczner +Marko Bonaci Martin Balek Martín Gaitán +Martin Hasoň Mason Malone Mateusz Paprocki mats cronqvist @@ -169,6 +197,7 @@ Matt McDonald Matt Pass Matt Sacks Maximilian Hils +Maxim Kraev Max Kirsch mbarkhau Metatheos @@ -183,7 +212,9 @@ Mike Diaz Mike Ivanov Mike Kadin MinRK +Miraculix87 misfo +mloginov mps Narciso Jaramillo Nathan Williams @@ -192,6 +223,7 @@ nguillaumin Niels van Groningen Nikita Beloglazov Nikita Vasilyev +Nikolay Kostov nlwillia pablo Page @@ -199,6 +231,7 @@ Patrick Strawderman Paul Garvin Paul Ivanov Pavel Feldman +Pavel Strashkin Paweł Bartkiewicz peteguhl peterkroon @@ -219,16 +252,22 @@ Sascha Peilicke satchmorun sathyamoorthi SCLINIC\jdecker +Sebastian Zaha shaund shaun gilchrist +Shawn A +Shiv Deepak Shmuel Englard +soliton4 sonson spastorelli +Stanislav Oaserele Stas Kobzar Stefan Borsje Steffen Beyer Steve O'Hara stoskov +Taha Jahangir Tarmil tfjgeorge Thaddee Tyl @@ -244,12 +283,18 @@ Tomas Varaneckas Tom Erik Støwer Tom MacWright Tony Jian +Travis Heppe Vestimir Markov vf Volker Mische +wenli +Wesley Wiser William Jamieson Wojtek Ptak Xavier Mendez +YNH Webdev Yunchi Luo Yuvi Panda Zachary Dremann +zziuni +魏鹏刚 diff --git a/addon/comment/comment.js b/addon/comment/comment.js index cd2123e175..5975b0bf69 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -96,8 +96,9 @@ for (var i = start; i <= end; ++i) { var line = self.getLine(i); var found = line.indexOf(lineString); + if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; - if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; + if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; lines.push(line); } self.operation(function() { @@ -124,7 +125,10 @@ endLine = self.getLine(--end); close = endLine.lastIndexOf(endString); } - if (open == -1 || close == -1) return false; + if (open == -1 || close == -1 || + !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || + !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) + return false; self.operation(function() { self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 94e5a3760f..a3370a6072 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -5,7 +5,7 @@ function continueComment(cm) { var pos = cm.getCursor(), token = cm.getTokenAt(pos); - if (token.type != "comment") return CodeMirror.Pass; + if (token.type != "comment" || cm.getOption("disableInput")) return CodeMirror.Pass; var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; var insert; diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 499964fdb2..41d7bf8663 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -35,6 +35,7 @@ } var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { + if (options && options.value) inp.value = options.value; CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 88718b7729..0575222be6 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -28,7 +28,7 @@ var map = { name : "autoCloseBrackets", Backspace: function(cm) { - if (cm.somethingSelected()) return CodeMirror.Pass; + if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass; var cur = cm.getCursor(), around = charsAround(cm, cur); if (around && pairs.indexOf(around) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); @@ -49,7 +49,8 @@ else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { - if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment") + if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" || + cm.getOption("disableInput")) return CodeMirror.Pass; if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; @@ -70,7 +71,8 @@ function buildExplodeHandler(pairs) { return function(cm) { var cur = cm.getCursor(), around = charsAround(cm, cur); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput")) + return CodeMirror.Pass; cm.operation(function() { var newPos = CodeMirror.Pos(cur.line + 1, 0); cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 1da89ba6dc..cad776a783 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -43,7 +43,7 @@ function autoCloseGT(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; + if (inner.mode.name != "xml" || !state.tagName || cm.getOption("disableInput")) return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); @@ -53,10 +53,13 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || + if (!tagName || + tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || + CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName, + Math.min(cm.lastLine() + 1, pos.line + 50))) return CodeMirror.Pass; var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; @@ -64,8 +67,8 @@ cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", {head: curPos, anchor: curPos}); if (doIndent) { - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); + cm.indentLine(pos.line + 1, null, true); + cm.indentLine(pos.line + 2, null); } } @@ -73,7 +76,8 @@ var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; if (tok.type == "string" || tok.string.charAt(0) != "<" || - tok.start != pos.ch - 1 || inner.mode.name != "xml") + tok.start != pos.ch - 1 || inner.mode.name != "xml" || + cm.getOption("disableInput")) return CodeMirror.Pass; var tagName = state.context && state.context.tagName; diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 826d17d716..190b41dcbe 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -5,6 +5,8 @@ unorderedBullets = '*+-'; CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var pos = cm.getCursor(), inList = cm.getStateAfter(pos.line).list !== false, match; diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 9d9b3882f7..465b6ccaa9 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -54,7 +54,7 @@ var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textare whever this fires. + // input stops going to the textarea whenever this fires. if (ie_lt8 && cm.state.focused) cm.display.input.focus(); var clear = function() { cm.operation(function() { one.clear(); two && two.clear(); }); diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js index a064cf8f57..26e72f09f5 100644 --- a/addon/fold/comment-fold.js +++ b/addon/fold/comment-fold.js @@ -1,4 +1,6 @@ -CodeMirror.registerHelper("fold", "comment", function(cm, start) { +CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { + return mode.blockCommentStart && mode.blockCommentEnd; +}, function(cm, start) { var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; if (!startToken || !endToken) return; var line = start.line, lineText = cm.getLine(line); diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index c497bc29b6..5c00d7093c 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -3,8 +3,7 @@ function doFold(cm, pos, options, force) { var finder = options && (options.call ? options : options.rangeFinder); - if (!finder) finder = cm.getHelper(pos, "fold"); - if (!finder) return; + if (!finder) finder = CodeMirror.fold.auto; if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); var minSize = options && options.minFoldSize || 0; @@ -63,6 +62,10 @@ doFold(this, pos, options, force); }); + CodeMirror.commands.fold = function(cm) { + cm.foldCode(cm.getCursor()); + }; + CodeMirror.registerHelper("fold", "combine", function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { @@ -72,4 +75,12 @@ } }; }); + + CodeMirror.registerHelper("fold", "auto", function(cm, start) { + var helpers = cm.getHelpers(start, "fold"); + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, start); + if (cur) return cur; + } + }); })(); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index 57336fbff8..a4f3bb3186 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -62,7 +62,7 @@ if (isFolded(cm, cur)) { mark = marker(opts.indicatorFolded); } else { - var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold"); + var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; var range = func && func(cm, pos); if (range && range.from.line + 1 < range.to.line) mark = marker(opts.indicatorOpen); diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index 1bd600be42..434c2bc5ca 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,8 +1,8 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); if (!/\S/.test(firstLine)) return; - var getIndent = function(lineNum) { - return CodeMirror.countColumn(lineNum, null, tabSize); + var getIndent = function(line) { + return CodeMirror.countColumn(line, null, tabSize); }; var myIndent = getIndent(firstLine); var lastLineInFold = null; diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 88a107c4a2..db5aed7041 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -164,4 +164,10 @@ if (close) return {open: open, close: close}; } }; + + // Used by addon/edit/closetag.js + CodeMirror.scanForClosingTag = function(cm, pos, name, end) { + var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); + return !!findMatchingClose(iter, name); + }; })(); diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index 36ff618e09..a144768c81 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -13,22 +13,20 @@ var curWord = start != end && curLine.slice(start, end); var list = [], seen = {}; - function scan(dir) { + 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 text = editor.getLine(line), m; - var re = new RegExp(word.source, "g"); while (m = re.exec(text)) { if (line == cur.line && m[0] === curWord) continue; - if ((!curWord || m[0].indexOf(curWord) == 0) && !seen.hasOwnProperty(m[0])) { + if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { seen[m[0]] = true; list.push(m[0]); } } } } - scan(-1); - scan(1); return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; }); })(); diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js index 2b15300d0c..6789c458ba 100644 --- a/addon/hint/css-hint.js +++ b/addon/hint/css-hint.js @@ -1,50 +1,46 @@ (function () { "use strict"; - function getHints(cm) { + var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, + "first-letter": 1, "first-line": 1, "first-child": 1, + before: 1, after: 1, lang: 1}; + + CodeMirror.registerHelper("hint", "css", function(cm) { var cur = cm.getCursor(), token = cm.getTokenAt(cur); var inner = CodeMirror.innerMode(cm.getMode(), token.state); if (inner.mode.name != "css") return; - // If it's not a 'word-style' token, ignore the token. - if (!/^[\w$_-]*$/.test(token.string)) { - token = { - start: cur.ch, end: cur.ch, string: "", state: token.state, - type: null - }; - var stack = token.state.stack; - var lastToken = stack && stack.length > 0 ? stack[stack.length - 1] : ""; - if (token.string == ":" || lastToken.indexOf("property") == 0) - token.type = "variable"; - else if (token.string == "{" || lastToken.indexOf("rule") == 0) - token.type = "property"; + var word = token.string, start = token.start, end = token.end; + if (/[^\w$_-]/.test(word)) { + word = ""; start = end = cur.ch; } - if (!token.type) - return; - var spec = CodeMirror.resolveMode("text/css"); - var keywords = null; - if (token.type.indexOf("property") == 0) - keywords = spec.propertyKeywords; - else if (token.type.indexOf("variable") == 0) - keywords = spec.valueKeywords; - - if (!keywords) - return; var result = []; - for (var name in keywords) { - if (name.indexOf(token.string) == 0 /* > -1 */) - result.push(name); + function add(keywords) { + for (var name in keywords) + if (!word || name.lastIndexOf(word, 0) == 0) + result.push(name); } - return { + var st = token.state.state; + if (st == "pseudo" || token.type == "variable-3") { + add(pseudoClasses); + } else if (st == "block" || st == "maybeprop") { + add(spec.propertyKeywords); + } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { + add(spec.valueKeywords); + add(spec.colorKeywords); + } else if (st == "media" || st == "media_parens") { + add(spec.mediaTypes); + add(spec.mediaFeatures); + } + + if (result.length) return { list: result, - from: CodeMirror.Pos(cur.line, token.start), - to: CodeMirror.Pos(cur.line, token.end) + from: CodeMirror.Pos(cur.line, start), + to: CodeMirror.Pos(cur.line, end) }; - } - - CodeMirror.registerHelper("hint", "css", getHints); + }); })(); diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index c66b0a7a5b..c347c206ec 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -87,7 +87,7 @@ function getCompletions(token, context, keywords, options) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { if (typeof obj == "string") forEach(stringProps, maybeAdd); diff --git a/addon/hint/pig-hint.js b/addon/hint/pig-hint.js index 7ef336cecf..6c0c523774 100644 --- a/addon/hint/pig-hint.js +++ b/addon/hint/pig-hint.js @@ -84,7 +84,7 @@ function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js index 98d2a5897f..248284e8c5 100644 --- a/addon/hint/python-hint.js +++ b/addon/hint/python-hint.js @@ -31,10 +31,6 @@ var completionList = getCompletions(token, context); completionList = completionList.sort(); - //prevent autocomplete for last word, instead show dropdown with one word - if(completionList.length == 1) { - completionList.push(" "); - } return {list: completionList, from: CodeMirror.Pos(cur.line, token.start), @@ -66,7 +62,7 @@ function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(_obj) { diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 46103b719b..7e03d1139a 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -7,8 +7,10 @@ CodeMirror.showHint = function(cm, getHints, options) { // We want a single cursor position. if (cm.somethingSelected()) return; - if (getHints == null) getHints = cm.getHelper(cm.getCursor(), "hint"); - if (getHints == null) return; + if (getHints == null) { + if (options && options.async) return; + else getHints = CodeMirror.hint.auto; + } if (cm.state.completionActive) cm.state.completionActive.close(); @@ -45,6 +47,7 @@ var completion = data.list[i]; if (completion.hint) completion.hint(this.cm, data, completion); else this.cm.replaceRange(getText(completion), data.from, data.to); + CodeMirror.signal(data, "pick", completion); this.close(); }, @@ -61,10 +64,15 @@ this.widget = new Widget(this, data); CodeMirror.signal(data, "shown"); - var debounce = null, completion = this, finished; + var debounce = 0, completion = this, finished; var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/; var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length; + var requestAnimationFrame = window.requestAnimationFrame || function(fn) { + return setTimeout(fn, 1000/60); + }; + var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; + function done() { if (finished) return; finished = true; @@ -88,15 +96,22 @@ completion.widget = new Widget(completion, data); } + function clearDebounce() { + if (debounce) { + cancelAnimationFrame(debounce); + debounce = 0; + } + } + function activity() { - clearTimeout(debounce); + clearDebounce(); var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line); if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch || pos.ch < startPos.ch || completion.cm.somethingSelected() || (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) { completion.close(); } else { - debounce = setTimeout(update, 170); + debounce = requestAnimationFrame(update); if (completion.widget) completion.widget.close(); } } @@ -143,9 +158,9 @@ return ourMap; } - function getHintElement(stopAt, el) { - while (el && el != stopAt) { - if (el.nodeName.toUpperCase() === "LI") return el; + function getHintElement(hintsElement, el) { + while (el && el != hintsElement) { + if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; el = el.parentNode; } } @@ -232,7 +247,10 @@ CodeMirror.on(hints, "click", function(e) { var t = getHintElement(hints, e.target || e.srcElement); - if (t && t.hintId != null) widget.changeActive(t.hintId); + if (t && t.hintId != null) { + widget.changeActive(t.hintId); + if (options.completeOnSingleClick) widget.pick(); + } }); CodeMirror.on(hints, "mousedown", function() { @@ -283,4 +301,35 @@ return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; } }; + + CodeMirror.registerHelper("hint", "auto", function(cm, options) { + var helpers = cm.getHelpers(cm.getCursor(), "hint"); + 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"); + if (words) return CodeMirror.hint.fromList(cm, {words: words}); + } + }); + + CodeMirror.registerHelper("hint", "fromList", function(cm, options) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var found = []; + for (var i = 0; i < options.words.length; i++) { + var word = options.words[i]; + if (word.slice(0, token.string.length) == token.string) + found.push(word); + } + + if (found.length) return { + list: found, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end) + }; + }); + + CodeMirror.commands.autocomplete = CodeMirror.showHint; })(); diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index 95f6b50a15..d00781cff9 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -3,6 +3,10 @@ var tables; var keywords; + var CONS = { + QUERY_DIV: ";", + ALIAS_KEYWORD: "AS" + }; function getKeywords(editor) { var mode = editor.doc.modeOption; @@ -34,11 +38,10 @@ var string = token.string.substr(1); var prevCur = CodeMirror.Pos(cur.line, token.start); var table = editor.getTokenAt(prevCur).string; - var columns = tables[table]; - if(!columns) { + if( !tables.hasOwnProperty( table ) ){ table = findTableByAlias(table, editor); } - columns = tables[table]; + var columns = tables[table]; if(!columns) { return; } @@ -46,32 +49,72 @@ function(w) {return "." + w;}); } - function eachWord(line, f) { - var words = line.text.split(" "); - for(var i = 0; i < words.length; i++) { - f(words[i]); + function eachWord(lineText, f) { + if( !lineText ){return;} + var excepted = /[,;]/g; + var words = lineText.split( " " ); + for( var i = 0; i < words.length; i++ ){ + f( words[i]?words[i].replace( excepted, '' ) : '' ); } } - // Tries to find possible table name from alias. + function convertCurToNumber( cur ){ + // max characters of a line is 999,999. + return cur.line + cur.ch / Math.pow( 10, 6 ); + } + + function convertNumberToCur( num ){ + return CodeMirror.Pos(Math.floor( num ), +num.toString().split( '.' ).pop()); + } + function findTableByAlias(alias, editor) { + var doc = editor.doc; + var fullQuery = doc.getValue(); var aliasUpperCase = alias.toUpperCase(); var previousWord = ""; var table = ""; + var separator = []; + var validRange = { + start: CodeMirror.Pos( 0, 0 ), + end: CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length ) + }; - editor.eachLine(function(line) { - eachWord(line, function(word) { + //add separator + var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV ); + while( indexOfSeparator != -1 ){ + separator.push( doc.posFromIndex(indexOfSeparator)); + indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1); + } + separator.unshift( CodeMirror.Pos( 0, 0 ) ); + separator.push( CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) ); + + //find valieRange + var prevItem = 0; + var current = convertCurToNumber( editor.getCursor() ); + for( var i=0; i< separator.length; i++){ + var _v = convertCurToNumber( separator[i] ); + if( current > prevItem && current <= _v ){ + validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) }; + break; + } + prevItem = _v; + } + + var query = doc.getRange(validRange.start, validRange.end, false); + + for(var i=0; i < query.length; i++){ + var lineText = query[i]; + eachWord( lineText, function( word ){ var wordUpperCase = word.toUpperCase(); - if(wordUpperCase === aliasUpperCase) { - if(tables.hasOwnProperty(previousWord)) { + if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){ table = previousWord; - } } - if(wordUpperCase !== "AS") { + if( wordUpperCase !== CONS.ALIAS_KEYWORD ){ previousWord = word; } }); - }); + if( table ){ break; } + } return table; } @@ -80,9 +123,7 @@ keywords = keywords || getKeywords(editor); var cur = editor.getCursor(); var token = editor.getTokenAt(cur); - var result = []; - var search = token.string.trim(); addMatches(result, search, keywords, diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index a721743449..69f2b771fe 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -20,13 +20,13 @@ var cx = inner.state.context, curTag = cx && tags[cx.tagName]; var childList = cx ? curTag && curTag.children : tags["!top"]; if (childList) { - for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0) + for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) result.push("<" + childList[i]); } else { - for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0)) + for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) result.push("<" + name); } - if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0)) + if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0)) result.push(""); } else { // Attribute completion @@ -46,14 +46,14 @@ } replaceToken = true; } - for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0) + for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) result.push(quote + atValues[i] + quote); } else { // An attribute name if (token.type == "attribute") { prefix = token.string; replaceToken = true; } - for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0)) + for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) result.push(attr); } } diff --git a/addon/lint/lint.js b/addon/lint/lint.js index b502ee41f1..6121735d3f 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -112,7 +112,7 @@ if (options.async) options.getAnnotations(cm, updateLinting, options); else - updateLinting(cm, options.getAnnotations(cm.getValue(), options)); + updateLinting(cm, options.getAnnotations(cm.getValue(), options.options)); } function updateLinting(cm, annotationsNotSorted) { diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 70341e4d12..89f852652b 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -29,7 +29,7 @@ this.edit = this.mv.edit; this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options))); - this.diff = getDiff(orig, options.value); + this.diff = getDiff(asString(orig), asString(options.value)); this.diffOutOfDate = false; this.showDifferences = options.showDifferences !== false; @@ -352,6 +352,11 @@ } }; + function asString(obj) { + if (typeof obj == "string") return obj; + else return obj.getValue(); + } + // Operations on diffs var dmp = new diff_match_patch(); diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index 32cc579c3a..614ab1add3 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -47,7 +47,11 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return outerToken; } else { var curInner = state.innerActive, oldContent = stream.string; - var found = indexOf(oldContent, curInner.close, stream.pos); + if (!curInner.close && stream.sol()) { + state.innerActive = state.inner = null; + return this.token(stream, state); + } + var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos) : -1; if (found == stream.pos) { stream.match(curInner.close); state.innerActive = state.inner = null; @@ -56,8 +60,6 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { if (found > -1) stream.string = oldContent.slice(0, found); var innerToken = curInner.mode.token(stream, state.inner); if (found > -1) stream.string = oldContent; - var cur = stream.current(), found = cur.indexOf(curInner.close); - if (found > -1) stream.backUp(cur.length - found); if (curInner.innerStyle) { if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle; diff --git a/addon/runmode/runmode-standalone.js b/addon/runmode/runmode-standalone.js index d117166c98..ec06ba11cb 100644 --- a/addon/runmode/runmode-standalone.js +++ b/addon/runmode/runmode-standalone.js @@ -10,6 +10,7 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -41,7 +42,7 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { @@ -58,7 +59,12 @@ StringStream.prototype = { return match; } }, - current: function(){return this.string.slice(this.start, this.pos);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; CodeMirror.StringStream = StringStream; @@ -69,17 +75,26 @@ CodeMirror.startState = function (mode, a1, a2) { var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; CodeMirror.defineMode = function (name, mode) { modes[name] = mode; }; CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; -CodeMirror.getMode = function (options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) +CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + spec = mimeModes[spec.name]; + } + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; +}; +CodeMirror.getMode = function (options, spec) { + spec = CodeMirror.resolveMode(spec); + var mfactory = modes[spec.name]; if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); + return mfactory(options, spec); }; +CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min; +CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; +}); +CodeMirror.defineMIME("text/plain", "null"); CodeMirror.runMode = function (string, modespec, callback, options) { var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec); @@ -122,7 +137,7 @@ CodeMirror.runMode = function (string, modespec, callback, options) { }; } - var lines = splitLines(string), state = CodeMirror.startState(mode); + var lines = splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new CodeMirror.StringStream(lines[i]); diff --git a/addon/runmode/runmode.js b/addon/runmode/runmode.js index 7aafa2ad8f..2cafa811f8 100644 --- a/addon/runmode/runmode.js +++ b/addon/runmode/runmode.js @@ -43,7 +43,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { }; } - var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); + var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new CodeMirror.StringStream(lines[i]); diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js index 0f1088fa2e..e8bccb0cf2 100644 --- a/addon/runmode/runmode.node.js +++ b/addon/runmode/runmode.node.js @@ -5,6 +5,7 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -36,7 +37,7 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { @@ -53,7 +54,12 @@ StringStream.prototype = { return match; } }, - current: function(){return this.string.slice(this.start, this.pos);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; exports.StringStream = StringStream; @@ -76,21 +82,26 @@ exports.defineMode("null", function() { }); exports.defineMIME("text/plain", "null"); -exports.getMode = function(options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) +exports.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + spec = mimeModes[spec.name]; + } + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; +}; +exports.getMode = function(options, spec) { + spec = exports.resolveMode(mimeModes[spec]); + var mfactory = modes[spec.name]; if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); + return mfactory(options, spec); }; +exports.registerHelper = exports.registerGlobalHelper = Math.min; exports.runMode = function(string, modespec, callback) { var mode = exports.getMode({indentUnit: 2}, modespec); - var lines = splitLines(string), state = exports.startState(mode); + var lines = splitLines(string), state = (options && options.state) || exports.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new exports.StringStream(lines[i]); diff --git a/addon/search/search.js b/addon/search/search.js index 71ed75b500..049f72f3dc 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -7,7 +7,15 @@ // Ctrl-G. (function() { - function searchOverlay(query) { + function searchOverlay(query, caseInsensitive) { + var startChar; + if (typeof query == "string") { + startChar = query.charAt(0); + query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), + caseInsensitive ? "i" : ""); + } else { + query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : ""); + } if (typeof query == "string") return {token: function(stream) { if (stream.match(query)) return "searching"; stream.next(); @@ -17,6 +25,8 @@ if (stream.match(query)) return "searching"; while (!stream.eol()) { stream.next(); + if (startChar) + stream.skipTo(startChar) || stream.skipToEnd(); if (stream.match(query, false)) break; } }}; @@ -29,13 +39,16 @@ function getSearchState(cm) { return cm.state.search || (cm.state.search = new SearchState()); } + function queryCaseInsensitive(query) { + return typeof query == "string" && query == query.toLowerCase(); + } function getSearchCursor(cm, query, pos) { // Heuristic: if the query string is all lowercase, do a case insensitive search. - return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); + return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); } - function dialog(cm, text, shortText, f) { - if (cm.openDialog) cm.openDialog(text, f); - else f(prompt(shortText, "")); + function dialog(cm, text, shortText, deflt, f) { + if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); + else f(prompt(shortText, deflt)); } function confirmDialog(cm, text, shortText, fs) { if (cm.openConfirm) cm.openConfirm(text, fs); @@ -50,11 +63,11 @@ function doSearch(cm, rev) { var state = getSearchState(cm); if (state.query) return findNext(cm, rev); - dialog(cm, queryDialog, "Search for:", function(query) { + dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { cm.operation(function() { if (!query || state.query) return; state.query = parseQuery(query); - cm.removeOverlay(state.overlay); + cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); state.overlay = searchOverlay(state.query); cm.addOverlay(state.overlay); state.posFrom = state.posTo = cm.getCursor(); @@ -85,10 +98,10 @@ var replacementQueryDialog = 'With: '; var doReplaceConfirm = "Replace? "; function replace(cm, all) { - dialog(cm, replaceQueryDialog, "Replace:", function(query) { + dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { if (!query) return; query = parseQuery(query); - dialog(cm, replacementQueryDialog, "Replace with:", function(text) { + dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { if (all) { cm.operation(function() { for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index c034d5865b..711cf4ce5a 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -47,6 +47,7 @@ match: match}; }; } else { // String query + var origQuery = query; if (caseFold) query = query.toLowerCase(); var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; var target = query.split("\n"); @@ -58,33 +59,45 @@ this.matches = function() {}; } else { this.matches = function(reverse, pos) { - var line = fold(doc.getLine(pos.line)), len = query.length, match; - if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) - : (match = line.indexOf(query, pos.ch)) != -1) - return {from: Pos(pos.line, match), - to: Pos(pos.line, match + len)}; + if (reverse) { + var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig); + var match = line.lastIndexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match); + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } else { + var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig); + var match = line.indexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match) + pos.ch; + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } }; } } else { + var origTarget = origQuery.split("\n"); this.matches = function(reverse, pos) { - var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln)); - var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); - if (reverse ? offsetA > pos.ch || offsetA != match.length - : offsetA < pos.ch || offsetA != line.length - match.length) - return; - for (;;) { - if (reverse ? !ln : ln == doc.lineCount() - 1) return; - line = fold(doc.getLine(ln += reverse ? -1 : 1)); - match = target[reverse ? --idx : ++idx]; - if (idx > 0 && idx < target.length - 1) { - if (line != match) return; - else continue; - } - var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); - if (reverse ? offsetB != line.length - match.length : offsetB != match.length) - return; - var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); - return {from: reverse ? end : start, to: reverse ? start : end}; + var last = target.length - 1; + if (reverse) { + if (pos.line - (target.length - 1) < doc.firstLine()) return; + if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return; + var to = Pos(pos.line, origTarget[last].length); + for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln) + if (target[i] != fold(doc.getLine(ln))) return; + var line = doc.getLine(ln), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + return {from: Pos(ln, cut), to: to}; + } else { + if (pos.line + (target.length - 1) > doc.lastLine()) return; + var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + var from = Pos(pos.line, cut); + for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln) + if (target[i] != fold(doc.getLine(ln))) return; + if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return; + return {from: from, to: Pos(ln, origTarget[last].length)}; } }; } @@ -106,7 +119,6 @@ for (;;) { if (this.pos = this.matches(reverse, pos)) { - if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } this.atOccurrence = true; return this.pos.match || true; } @@ -134,6 +146,18 @@ } }; + // Maps a position in a case-folded line back to a position in the original line + // (compensating for codepoints increasing in number during folding) + function adjustPos(orig, folded, pos) { + if (orig.length == folded.length) return pos; + for (var pos1 = Math.min(pos, orig.length);;) { + var len1 = orig.slice(0, pos1).toLowerCase().length; + if (len1 < pos) ++pos1; + else if (len1 > pos) --pos1; + else return pos1; + } + } + CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this.doc, query, pos, caseFold); }); diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index e50508653a..7cf7793c07 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -12,10 +12,10 @@ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - updateActiveLine(cm); - cm.on("cursorActivity", updateActiveLine); + updateActiveLine(cm, cm.getCursor().line); + cm.on("beforeSelectionChange", selectionChange); } else if (!val && prev) { - cm.off("cursorActivity", updateActiveLine); + cm.off("beforeSelectionChange", selectionChange); clearActiveLine(cm); delete cm.state.activeLine; } @@ -28,12 +28,18 @@ } } - function updateActiveLine(cm) { - var line = cm.getLineHandleVisualStart(cm.getCursor().line); + function updateActiveLine(cm, selectedLine) { + var line = cm.getLineHandleVisualStart(selectedLine); if (cm.state.activeLine == line) return; - clearActiveLine(cm); - cm.addLineClass(line, "wrap", WRAP_CLASS); - cm.addLineClass(line, "background", BACK_CLASS); - cm.state.activeLine = line; + cm.operation(function() { + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm.state.activeLine = line; + }); + } + + function selectionChange(cm, sel) { + updateActiveLine(cm, sel.head.line); } })(); diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 1f18a657fb..7f83c4e4c0 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -251,7 +251,7 @@ var lex = inner.state.lexical; if (lex.info != "call") return; - var ch, pos = lex.pos || 0, tabSize = cm.getOption("tabSize"); + var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize"); for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) { var str = cm.getLine(line), extra = 0; for (var pos = 0;;) { @@ -268,7 +268,7 @@ var start = Pos(line, ch); var cache = ts.cachedArgHints; if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) - return showArgHints(ts, cm, pos); + return showArgHints(ts, cm, argPos); ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { if (error || !data.type || !(/^fn\(/).test(data.type)) return; @@ -279,7 +279,7 @@ guess: data.guess, doc: cm.getDoc() }; - showArgHints(ts, cm, pos); + showArgHints(ts, cm, argPos); }); } diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js index f252ffc9e9..f6d99212a8 100644 --- a/addon/wrap/hardwrap.js +++ b/addon/wrap/hardwrap.js @@ -36,31 +36,40 @@ var killTrailing = options.killTrailingSpace !== false; var changes = [], curLine = "", curNo = from.line; var lines = cm.getRange(from, to, false); + if (!lines.length) return null; + var leadingSpace = lines[0].match(/^[ \t]*/)[0]; + for (var i = 0; i < lines.length; ++i) { var text = lines[i], oldLen = curLine.length, spaceInserted = 0; if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) { curLine += " "; spaceInserted = 1; } + var spaceTrimmed = ""; + if (i) { + spaceTrimmed = text.match(/^\s*/)[0]; + text = text.slice(spaceTrimmed.length); + } curLine += text; if (i) { - var firstBreak = curLine.length > column && findBreakPoint(curLine, column, wrapOn, killTrailing); + var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed && + findBreakPoint(curLine, column, wrapOn, killTrailing); // If this isn't broken, or is broken at a different point, remove old break if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) { - changes.push({text: spaceInserted ? " " : "", + changes.push({text: [spaceInserted ? " " : ""], from: Pos(curNo, oldLen), - to: Pos(curNo + 1, 0)}); + to: Pos(curNo + 1, spaceTrimmed.length)}); } else { - curLine = text; + curLine = leadingSpace + text; ++curNo; } } while (curLine.length > column) { var bp = findBreakPoint(curLine, column, wrapOn, killTrailing); - changes.push({text: "\n", + changes.push({text: ["", leadingSpace], from: Pos(curNo, bp.from), to: Pos(curNo, bp.to)}); - curLine = curLine.slice(bp.to); + curLine = leadingSpace + curLine.slice(bp.to); ++curNo; } } @@ -70,17 +79,18 @@ cm.replaceRange(change.text, change.from, change.to); } }); + return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null; } CodeMirror.defineExtension("wrapParagraph", function(pos, options) { options = options || {}; if (!pos) pos = this.getCursor(); var para = findParagraph(this, pos, options); - wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options); + return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options); }); CodeMirror.defineExtension("wrapRange", function(from, to, options) { - wrapRange(this, from, to, options || {}); + return wrapRange(this, from, to, options || {}); }); CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) { @@ -91,9 +101,11 @@ paras.push(para); line = para.to; } + var madeChange = false; if (paras.length) cm.operation(function() { for (var i = paras.length - 1; i >= 0; --i) - wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options); + madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options); }); + return madeChange; }); })(); diff --git a/bin/release b/bin/release new file mode 100755 index 0000000000..f92ab006d5 --- /dev/null +++ b/bin/release @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +var fs = require("fs"), child = require("child_process"); + +var number, bumpOnly; + +for (var i = 2; i < process.argv.length; i++) { + if (process.argv[i] == "-bump") bumpOnly = true; + else if (/^\d+\.\d+\.\d+$/.test(process.argv[i])) number = process.argv[i]; + else { console.log("Bogus command line arg: " + process.argv[i]); process.exit(1); } +} + +if (!number) { console.log("Must give a version"); process.exit(1); } + +function rewrite(file, f) { + fs.writeFileSync(file, f(fs.readFileSync(file, "utf8")), "utf8"); +} + +rewrite("lib/codemirror.js", function(lib) { + return lib.replace(/CodeMirror\.version = "\d+\.\d+\.\d+"/, + "CodeMirror.version = \"" + number + "\""); +}); +rewrite("package.json", function(pack) { + return pack.replace(/"version":"\d+\.\d+\.\d+"/, "\"version\":\"" + number + "\""); +}); + +if (bumpOnly) process.exit(0); + +child.exec("bash bin/authors.sh", function(){}); + +var simple = number.slice(0, number.lastIndexOf(".")); + +rewrite("doc/compress.html", function(cmp) { + return cmp.replace(/\n "); +}); + +rewrite("index.html", function(index) { + return index.replace(/version 3.20<\/strong>/, + "version " + simple + ""); +}); diff --git a/demo/changemode.html b/demo/changemode.html index 61c1786074..9ea99b2d04 100644 --- a/demo/changemode.html +++ b/demo/changemode.html @@ -44,11 +44,11 @@ lineNumbers: true, tabMode: "indent" }); + var pending; editor.on("change", function() { clearTimeout(pending); - setTimeout(update, 400); + pending = setTimeout(update, 400); }); - var pending; function looksLikeScheme(code) { return !/^\s*\(\s*function\b/.test(code) && /^\s*[;\(]/.test(code); } diff --git a/demo/closetag.html b/demo/closetag.html index 8981f7eb3a..39ca8b3f51 100644 --- a/demo/closetag.html +++ b/demo/closetag.html @@ -7,6 +7,7 @@ + diff --git a/demo/complete.html b/demo/complete.html index e256a908b4..44b3b2db73 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -10,6 +10,7 @@ +