diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index a2fea74f..adf9bd73 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -306,13 +306,7 @@ var Autocomplete = function() { }; this.cancelContextMenu = function() { - var stop = function(e) { - this.editor.off("nativecontextmenu", stop); - if (e && e.domEvent) - event.stopEvent(e.domEvent); - }.bind(this); - setTimeout(stop, 10); - this.editor.on("nativecontextmenu", stop); + this.editor.$mouseHandler.cancelContextMenu(); }; }).call(Autocomplete.prototype); diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 09d67b95..a727b15a 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -485,15 +485,15 @@ function Folding() { }; this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) { - if (startRow == null) { + if (startRow == null) startRow = foldLine.start.row; + if (startColumn == null) startColumn = 0; - } - - if (endRow == null) { + if (endRow == null) endRow = foldLine.end.row; + if (endColumn == null) endColumn = this.getLine(endRow).length; - } + // Build the textline using the FoldLine walker. var doc = this.doc; diff --git a/lib/ace/ext/language_tools.js b/lib/ace/ext/language_tools.js index 17d23bf7..dc56e6e0 100644 --- a/lib/ace/ext/language_tools.js +++ b/lib/ace/ext/language_tools.js @@ -121,40 +121,45 @@ var loadSnippetFile = function(id) { }); }; -var doLiveAutocomplete = function(e) { - var editor = e.editor; - var text = e.args || ""; +function getCompletionPrefix(editor) { var pos = editor.getCursorPosition(); var line = editor.session.getLine(pos.row); - var hasCompleter = editor.completer && editor.completer.activated; var prefix = util.retrievePrecedingIdentifier(line, pos.column); - - //Try to find custom prefixes on the completers - completers.forEach(function(completer) { + // Try to find custom prefixes on the completers + editor.completers.forEach(function(completer) { if (completer.identifierRegexps) { completer.identifierRegexps.forEach(function(identifierRegex) { - if (!prefix) { + if (!prefix && identifierRegex) prefix = util.retrievePrecedingIdentifier(line, pos.column, identifierRegex); - } }); } }); + return prefix; +} + +var doLiveAutocomplete = function(e) { + var editor = e.editor; + var text = e.args || ""; + var hasCompleter = editor.completer && editor.completer.activated; + + // We don't want to autocomplete with no prefix - if (e.command.name === "backspace" && !prefix) { - if (hasCompleter) + if (e.command.name === "backspace") { + if (hasCompleter && !getCompletionPrefix(editor)) editor.completer.detach(); } else if (e.command.name === "insertstring") { + var prefix = getCompletionPrefix(editor); // Only autocomplete if there's a prefix that can be matched if (prefix && !hasCompleter) { if (!editor.completer) { // Create new autocompleter editor.completer = new Autocomplete(); - // Disable autoInsert - editor.completer.autoSelect = false; - editor.completer.autoInsert = false; } + // Disable autoInsert + editor.completer.autoSelect = false; + editor.completer.autoInsert = false; editor.completer.showPopup(editor); } else if (!prefix && hasCompleter) { // When the prefix is empty @@ -169,7 +174,8 @@ require("../config").defineOptions(Editor.prototype, "editor", { enableBasicAutocompletion: { set: function(val) { if (val) { - this.completers = Array.isArray(val)? val: completers; + if (!this.completers) + this.completers = Array.isArray(val)? val: completers; this.commands.addCommand(Autocomplete.startCommand); } else { this.commands.removeCommand(Autocomplete.startCommand); @@ -184,7 +190,8 @@ require("../config").defineOptions(Editor.prototype, "editor", { enableLiveAutocompletion: { set: function(val) { if (val) { - this.completers = Array.isArray(val)? val: completers; + if (!this.completers) + this.completers = Array.isArray(val)? val: completers; // On each change automatically trigger the autocomplete this.commands.on('afterExec', doLiveAutocomplete); } else { diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js index f6436ffb..57470ed0 100644 --- a/lib/ace/ext/modelist.js +++ b/lib/ace/ext/modelist.js @@ -53,7 +53,7 @@ var supportedModes = { C9Search: ["c9search_results"], C_Cpp: ["cpp|c|cc|cxx|h|hh|hpp"], Cirru: ["cirru|cr"], - Clojure: ["clj"], + Clojure: ["clj|cljs"], Cobol: ["CBL|COB"], coffee: ["coffee|cf|cson|^Cakefile"], ColdFusion: ["cfm"], diff --git a/lib/ace/ext/whitespace.js b/lib/ace/ext/whitespace.js index a3c179f3..fdbf7360 100644 --- a/lib/ace/ext/whitespace.js +++ b/lib/ace/ext/whitespace.js @@ -46,7 +46,6 @@ exports.$detectIndentation = function(lines, fallback) { if (!/^\s*[^*+\-\s]/.test(line)) continue; - var tabs = line.match(/^\t*/)[0].length; if (line[0] == "\t") tabIndents++; @@ -65,9 +64,6 @@ exports.$detectIndentation = function(lines, fallback) { line = lines[i++]; } - if (!stats.length) - return; - function getScore(indent) { var score = 0; for (var i = indent; i < stats.length; i += indent) @@ -82,7 +78,7 @@ exports.$detectIndentation = function(lines, fallback) { for (var i = 1; i < 12; i++) { if (i == 1) { spaceIndents = getScore(i); - var score = 1; + var score = stats.length && 1; } else var score = getScore(i) / spaceIndents; @@ -99,7 +95,7 @@ exports.$detectIndentation = function(lines, fallback) { if (tabIndents > spaceIndents + 1) return {ch: "\t", length: tabLength}; - if (spaceIndents + 1 > tabIndents) + if (spaceIndents > tabIndents + 1) return {ch: " ", length: tabLength}; }; diff --git a/lib/ace/keyboard/vim/maps/operators.js b/lib/ace/keyboard/vim/maps/operators.js index 350bbd05..6df55b0f 100644 --- a/lib/ace/keyboard/vim/maps/operators.js +++ b/lib/ace/keyboard/vim/maps/operators.js @@ -34,6 +34,7 @@ define(function(require, exports, module) { var util = require("./util"); var registers = require("../registers"); +var Range = require("../../../range").Range; module.exports = { "d": { @@ -90,15 +91,18 @@ module.exports = { count = count || 1; switch (param) { case "c": - for (var i = 0; i < count; i++) { - editor.removeLines(); - util.insertMode(editor); - } - + editor.$blockScrolling++; + editor.selection.$moveSelection(function() { + editor.selection.moveCursorBy(count - 1, 0); + }); + var rows = editor.$getSelectedRows(); + range = new Range(rows.first, 0, rows.last, Infinity); + editor.session.remove(range); + editor.$blockScrolling--; + util.insertMode(editor); break; default: if (range) { - // range.end.column ++; editor.session.remove(range); util.insertMode(editor); diff --git a/lib/ace/lib/event.js b/lib/ace/lib/event.js index 39d115b4..612b6a34 100644 --- a/lib/ace/lib/event.js +++ b/lib/ace/lib/event.js @@ -85,7 +85,7 @@ exports.preventDefault = function(e) { exports.getButton = function(e) { if (e.type == "dblclick") return 0; - if (e.type == "contextmenu" || (e.ctrlKey && useragent.isMac)) + if (e.type == "contextmenu" || (useragent.isMac && (e.ctrlKey && !e.altKey && !e.shiftKey))) return 2; // DOM Event @@ -183,7 +183,7 @@ exports.addMultiMouseDownListener = function(el, timeouts, eventHandler, callbac if (!timer || isNewClick) clicks = 1; if (timer) - clearTimeout(timer) + clearTimeout(timer); timer = setTimeout(function() {timer = null}, timeouts[clicks - 1] || 600); if (clicks == 1) { diff --git a/lib/ace/mode/javascript/jshint.js b/lib/ace/mode/javascript/jshint.js index fb2039b6..12590467 100644 --- a/lib/ace/mode/javascript/jshint.js +++ b/lib/ace/mode/javascript/jshint.js @@ -27,9 +27,9 @@ module.exports = { }, {}], 2:[function(_dereq_,module,exports){ -// Underscore.js 1.4.4 +// Underscore.js 1.6.0 // http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { @@ -37,7 +37,7 @@ module.exports = { // Baseline setup // -------------- - // Establish the root object, `window` in the browser, or `global` on the server. + // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. @@ -50,11 +50,12 @@ module.exports = { var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. - var push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. @@ -93,7 +94,7 @@ module.exports = { } // Current version. - _.VERSION = '1.4.4'; + _.VERSION = '1.6.0'; // Collection Functions // -------------------- @@ -102,20 +103,20 @@ module.exports = { // Handles objects with the built-in `forEach`, arrays, and raw objects. // Delegates to **ECMAScript 5**'s native `forEach` if available. var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; + if (obj == null) return obj; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { + for (var i = 0, length = obj.length; i < length; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; } } + return obj; }; // Return the results of applying the iterator to each element. @@ -125,7 +126,7 @@ module.exports = { if (obj == null) return results; if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); + results.push(iterator.call(context, value, index, list)); }); return results; }; @@ -181,10 +182,10 @@ module.exports = { }; // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { + _.find = _.detect = function(obj, predicate, context) { var result; any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { + if (predicate.call(context, value, index, list)) { result = value; return true; } @@ -195,33 +196,33 @@ module.exports = { // Return all the elements that pass a truth test. // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { + _.filter = _.select = function(obj, predicate, context) { var results = []; if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results[results.length] = value; + if (predicate.call(context, value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { + _.reject = function(obj, predicate, context) { return _.filter(obj, function(value, index, list) { - return !iterator.call(context, value, index, list); + return !predicate.call(context, value, index, list); }, context); }; // Determine whether all of the elements match a truth test. // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); + _.every = _.all = function(obj, predicate, context) { + predicate || (predicate = _.identity); var result = true; if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; + if (!(result = result && predicate.call(context, value, index, list))) return breaker; }); return !!result; }; @@ -229,13 +230,13 @@ module.exports = { // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); + var any = _.some = _.any = function(obj, predicate, context) { + predicate || (predicate = _.identity); var result = false; if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; + if (result || (result = predicate.call(context, value, index, list))) return breaker; }); return !!result; }; @@ -261,41 +262,37 @@ module.exports = { // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); + return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. - _.where = function(obj, attrs, first) { - if (_.isEmpty(attrs)) return first ? null : []; - return _[first ? 'find' : 'filter'](obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { - return _.where(obj, attrs, true); + return _.find(obj, _.matches(attrs)); }; // Return the maximum element or (element-based computation). // Can't optimize arrays of integers longer than 65,535 elements. - // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.max.apply(Math, obj); } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity, value: -Infinity}; + var result = -Infinity, lastComputed = -Infinity; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; - computed >= result.computed && (result = {value : value, computed : computed}); + if (computed > lastComputed) { + result = value; + lastComputed = computed; + } }); - return result.value; + return result; }; // Return the minimum element (or element-based computation). @@ -303,16 +300,19 @@ module.exports = { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.min.apply(Math, obj); } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity, value: Infinity}; + var result = Infinity, lastComputed = Infinity; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); + if (computed < lastComputed) { + result = value; + lastComputed = computed; + } }); - return result.value; + return result; }; - // Shuffle an array. + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var rand; var index = 0; @@ -325,19 +325,32 @@ module.exports = { return shuffled; }; + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + // An internal function to generate lookup iterators. var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); }; // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); return _.pluck(_.map(obj, function(value, index, list) { return { - value : value, - index : index, - criteria : iterator.call(context, value, index, list) + value: value, + index: index, + criteria: iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria; @@ -346,43 +359,46 @@ module.exports = { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } - return left.index < right.index ? -1 : 1; + return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. - var group = function(obj, value, context, behavior) { - var result = {}; - var iterator = lookupIterator(value || _.identity); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); - }); - return result; + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. - _.groupBy = function(obj, value, context) { - return group(obj, value, context, function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); - }); - }; + _.groupBy = group(function(result, key, value) { + _.has(result, key) ? result[key].push(value) : result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. - _.countBy = function(obj, value, context) { - return group(obj, value, context, function(result, key) { - if (!_.has(result, key)) result[key] = 0; - result[key]++; - }); - }; + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); + iterator = lookupIterator(iterator); var value = iterator.call(context, obj); var low = 0, high = array.length; while (low < high) { @@ -392,7 +408,7 @@ module.exports = { return low; }; - // Safely convert anything iterable into a real, live array. + // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); @@ -414,7 +430,9 @@ module.exports = { // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on @@ -429,11 +447,8 @@ module.exports = { // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. @@ -451,8 +466,11 @@ module.exports = { // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } each(input, function(value) { - if (_.isArray(value)) { + if (_.isArray(value) || _.isArguments(value)) { shallow ? push.apply(output, value) : flatten(value, shallow, output); } else { output.push(value); @@ -461,7 +479,7 @@ module.exports = { return output; }; - // Return a completely flattened version of an array. + // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, []); }; @@ -471,6 +489,16 @@ module.exports = { return _.difference(array, slice.call(arguments, 1)); }; + // Split an array into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(array, predicate) { + var pass = [], fail = []; + each(array, function(elem) { + (predicate(elem) ? pass : fail).push(elem); + }); + return [pass, fail]; + }; + // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. @@ -495,7 +523,7 @@ module.exports = { // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { - return _.uniq(concat.apply(ArrayProto, arguments)); + return _.uniq(_.flatten(arguments, true)); }; // Produce an array that contains every item shared between all the @@ -504,7 +532,7 @@ module.exports = { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; + return _.contains(other, item); }); }); }; @@ -519,11 +547,10 @@ module.exports = { // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { - var args = slice.call(arguments); - var length = _.max(_.pluck(args, 'length')); + var length = _.max(_.pluck(arguments, 'length').concat(0)); var results = new Array(length); for (var i = 0; i < length; i++) { - results[i] = _.pluck(args, "" + i); + results[i] = _.pluck(arguments, '' + i); } return results; }; @@ -534,7 +561,7 @@ module.exports = { _.object = function(list, values) { if (list == null) return {}; var result = {}; - for (var i = 0, l = list.length; i < l; i++) { + for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { @@ -552,17 +579,17 @@ module.exports = { // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; - var i = 0, l = array.length; + var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < l; i++) if (array[i] === item) return i; + for (; i < length; i++) if (array[i] === item) return i; return -1; }; @@ -588,11 +615,11 @@ module.exports = { } step = arguments[2] || 1; - var len = Math.max(Math.ceil((stop - start) / step), 0); + var length = Math.max(Math.ceil((stop - start) / step), 0); var idx = 0; - var range = new Array(len); + var range = new Array(length); - while(idx < len) { + while(idx < length) { range[idx++] = start; start += step; } @@ -603,31 +630,50 @@ module.exports = { // Function (ahem) Functions // ------------------ + // Reusable constructor function for prototype setting. + var ctor = function(){}; + // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { - if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - var args = slice.call(arguments, 2); - return function() { - return func.apply(context, args.concat(slice.call(arguments))); + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; }; }; // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { - var args = slice.call(arguments, 1); + var boundArgs = slice.call(arguments, 1); return function() { - return func.apply(this, args.concat(slice.call(arguments))); + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); }; }; - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. _.bindAll = function(obj) { var funcs = slice.call(arguments, 1); - if (funcs.length === 0) funcs = _.functions(obj); + if (funcs.length === 0) throw new Error('bindAll must be passed function names'); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); return obj; }; @@ -656,17 +702,24 @@ module.exports = { }; // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. - _.throttle = function(func, wait) { - var context, args, timeout, result; + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; var previous = 0; + options || (options = {}); var later = function() { - previous = new Date; + previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); + context = args = null; }; return function() { - var now = new Date; + var now = _.now(); + if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; @@ -675,7 +728,8 @@ module.exports = { timeout = null; previous = now; result = func.apply(context, args); - } else if (!timeout) { + context = args = null; + } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; @@ -687,17 +741,34 @@ module.exports = { // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments; - var later = function() { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { timeout = null; - if (!immediate) result = func.apply(context, args); - }; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) result = func.apply(context, args); + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + return result; }; }; @@ -719,11 +790,7 @@ module.exports = { // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { - return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; + return _.partial(wrapper, func); }; // Returns a function that is the composition of a list of functions, each @@ -741,7 +808,6 @@ module.exports = { // Returns a function that will only be executed after being called N times. _.after = function(times, func) { - if (times <= 0) return func(); return function() { if (--times < 1) { return func.apply(this, arguments); @@ -754,31 +820,43 @@ module.exports = { // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); var keys = []; - for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { - var values = []; - for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { - var pairs = []; - for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; - for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } return result; }; @@ -829,7 +907,7 @@ module.exports = { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { - if (obj[prop] == null) obj[prop] = source[prop]; + if (obj[prop] === void 0) obj[prop] = source[prop]; } } }); @@ -853,7 +931,7 @@ module.exports = { // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a == 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; @@ -895,6 +973,14 @@ module.exports = { // unique nested structures. if (aStack[length] == a) return bStack[length] == b; } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); @@ -911,13 +997,6 @@ module.exports = { } } } else { - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; - } // Deep compare objects. for (var key in a) { if (_.has(a, key)) { @@ -1039,9 +1118,33 @@ module.exports = { return value; }; + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + return function(obj) { + if (obj === attrs) return true; //avoid comparing an object to itself. + for (var key in attrs) { + if (attrs[key] !== obj[key]) + return false; + } + return true; + } + }; + // Run a function **n** times. _.times = function(n, iterator, context) { - var accum = Array(n); + var accum = Array(Math.max(0, n)); for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); return accum; }; @@ -1055,6 +1158,9 @@ module.exports = { return min + Math.floor(Math.random() * (max - min + 1)); }; + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { return new Date().getTime(); }; + // List of HTML entities for escaping. var entityMap = { escape: { @@ -1062,8 +1168,7 @@ module.exports = { '<': '<', '>': '>', '"': '"', - "'": ''', - '/': '/' + "'": ''' } }; entityMap.unescape = _.invert(entityMap.escape); @@ -1084,17 +1189,17 @@ module.exports = { }; }); - // If the value of the named property is a function then invoke it; - // otherwise, return it. + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. _.result = function(object, property) { - if (object == null) return null; + if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? value.call(object) : value; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { - each(_.functions(obj), function(name){ + each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; @@ -1252,6 +1357,18 @@ module.exports = { }); + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } }).call(this); }, @@ -1353,6 +1470,7 @@ var JSHINT = (function () { globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict') immed : true, // if immediate invocations must be wrapped in parens iterator : true, // if the `__iterator__` property should be allowed + jasmine : true, // Jasmine functions should be predefined jquery : true, // if jQuery globals should be predefined lastsemic : true, // if semicolons may be ommitted for the trailing // statements inside of a one-line blocks. @@ -1378,6 +1496,7 @@ var JSHINT = (function () { proto : true, // if the `__proto__` property should be allowed prototypejs : true, // if Prototype and Scriptaculous globals should be // predefined + qunit : true, // if the QUnit environment globals should be predefined rhino : true, // if the Rhino environment globals should be predefined shelljs : true, // if ShellJS globals should be predefined typed : true, // if typed array globals should be predefined @@ -1570,7 +1689,7 @@ var JSHINT = (function () { function combine(dest, src) { Object.keys(src).forEach(function (name) { - if (JSHINT.blacklist.hasOwnProperty(name)) return; + if (_.has(JSHINT.blacklist, name)) return; dest[name] = src[name]; }); } @@ -1584,6 +1703,10 @@ var JSHINT = (function () { combine(predefined, vars.couch); } + if (state.option.qunit) { + combine(predefined, vars.qunit); + } + if (state.option.rhino) { combine(predefined, vars.rhino); } @@ -1626,6 +1749,10 @@ var JSHINT = (function () { combine(predefined, vars.nonstandard); } + if (state.option.jasmine) { + combine(predefined, vars.jasmine); + } + if (state.option.jquery) { combine(predefined, vars.jquery); } @@ -2208,11 +2335,13 @@ var JSHINT = (function () { if (state.tokens.next.id === "(end)") error("E006", state.tokens.curr); - if (state.option.asi && - (state.tokens.curr.id === "[" || - state.tokens.curr.id === "(" || - state.tokens.curr.id === "/") && - state.tokens.prev.line < state.tokens.curr.line) + var isDangerous = + state.option.asi && + state.tokens.prev.line < state.tokens.curr.line && + _.contains(["]", ")"], state.tokens.prev.id) && + _.contains(["[", "("], state.tokens.curr.id); + + if (isDangerous) warning("W014", state.tokens.curr, state.tokens.curr.id); advance(); @@ -2792,6 +2921,21 @@ var JSHINT = (function () { } } + function parseFinalSemicolon() { + if (state.tokens.next.id !== ";") { + if (!state.option.asi) { + // If this is the last statement in a block that ends on + // the same line *and* option lastsemic is on, ignore the warning. + // Otherwise, complain about missing semicolon. + if (!state.option.lastsemic || state.tokens.next.id !== "}" || + state.tokens.next.line !== state.tokens.curr.line) { + warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); + } + } + } else { + advance(";"); + } + } function statement() { var values; @@ -2814,6 +2958,23 @@ var JSHINT = (function () { res = false; } + // detect a module import declaration + if (t.value === "module" && t.type === "(identifier)") { + if (peek().type === "(identifier)") { + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "module"); + } + + advance("module"); + var name = identifier(); + addlabel(name, { type: "unused", token: state.tokens.curr }); + advance("from"); + advance("(string)"); + parseFinalSemicolon(); + return; + } + } + // detect a destructuring assignment if (_.has(["[", "{"], t.value)) { if (lookupBlockType().isDestAssign) { @@ -2879,22 +3040,10 @@ var JSHINT = (function () { } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { warning("W031", t); } - - if (state.tokens.next.id !== ";") { - if (!state.option.asi) { - // If this is the last statement in a block that ends on - // the same line *and* option lastsemic is on, ignore the warning. - // Otherwise, complain about missing semicolon. - if (!state.option.lastsemic || state.tokens.next.id !== "}" || - state.tokens.next.line !== state.tokens.curr.line) { - warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); - } - } - } else { - advance(";"); - } + parseFinalSemicolon(); } + // Restore the indentation. indent = i; @@ -5432,7 +5581,6 @@ var JSHINT = (function () { FutureReservedWord("static", { es5: true, strictOnly: true }); FutureReservedWord("super", { es5: true }); FutureReservedWord("synchronized"); - FutureReservedWord("throws"); FutureReservedWord("transient"); FutureReservedWord("volatile"); @@ -8150,7 +8298,7 @@ exports.starSlash = /\*\//; exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; // JavaScript URL (jx) -exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; +exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; // Catches /* falls through */ comments (ft) exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; @@ -8323,7 +8471,7 @@ exports.register = function (linter) { // Warn about script URLs. linter.on("String", function style_scanJavaScriptURLs(data) { - var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; + var re = /^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i; if (linter.getOption("scripturl")) { return; @@ -8756,6 +8904,27 @@ exports.phantom = { exports : true // v1.7+ }; +exports.qunit = { + asyncTest : false, + deepEqual : false, + equal : false, + expect : false, + module : false, + notDeepEqual : false, + notEqual : false, + notPropEqual : false, + notStrictEqual : false, + ok : false, + propEqual : false, + QUnit : false, + raises : false, + start : false, + stop : false, + strictEqual : false, + test : false, + "throws" : false +}; + exports.rhino = { defineClass : false, deserialize : false, @@ -8875,7 +9044,7 @@ exports.mootools = { Group : false, Hash : false, HtmlTable : false, - Iframe : false, + IFrame : false, IframeShim : false, InputValidator: false, instanceOf : false, @@ -8962,6 +9131,23 @@ exports.mocha = { teardown : false }; +exports.jasmine = { + jasmine : false, + describe : false, + it : false, + xit : false, + beforeEach : false, + afterEach : false, + setFixtures : false, + loadFixtures: false, + spyOn : false, + expect : false, + // Jasmine 1.3 + runs : false, + waitsFor : false, + waits : false +}; + }, {}], 10:[function(_dereq_,module,exports){ diff --git a/lib/ace/mode/ruby.js b/lib/ace/mode/ruby.js index 27bc6c38..76bc2d6f 100644 --- a/lib/ace/mode/ruby.js +++ b/lib/ace/mode/ruby.js @@ -74,14 +74,21 @@ oop.inherits(Mode, TextMode); }; this.checkOutdent = function(state, line, input) { - return /^\s+end$/.test(line + input) || /^\s+}$/.test(line + input) || /^\s+else$/.test(line + input); + return /^\s+(end|else)$/.test(line + input) || this.$outdent.checkOutdent(line, input); }; - this.autoOutdent = function(state, doc, row) { - var indent = this.$getIndent(doc.getLine(row)); - var tab = doc.getTabString(); - if (indent.slice(-tab.length) == tab) - doc.remove(new Range(row, indent.length-tab.length, row, indent.length)); + this.autoOutdent = function(state, session, row) { + var line = session.getLine(row); + if (/}/.test(line)) + return this.$outdent.autoOutdent(session, row); + var indent = this.$getIndent(line); + var prevLine = session.getLine(row - 1); + var prevIndent = this.$getIndent(prevLine); + var tab = session.getTabString(); + if (prevIndent.length <= indent.length) { + if (indent.slice(-tab.length) == tab) + session.remove(new Range(row, indent.length-tab.length, row, indent.length)); + } }; this.$id = "ace/mode/ruby"; diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 34a2a511..f04f1722 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -62,7 +62,7 @@ var Mode = function() { this.getTokenizer = function() { if (!this.$tokenizer) { - this.$highlightRules = new this.HighlightRules(); + this.$highlightRules = this.$highlightRules || new this.HighlightRules(); this.$tokenizer = new Tokenizer(this.$highlightRules.getRules()); } return this.$tokenizer; diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index 8a9adcf7..e7a31540 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -109,12 +109,15 @@ function DefaultHandlers(mouseHandler) { var editor = this.editor; // allow double/triple click handlers to change selection var shiftPressed = this.mousedownEvent.getShiftKey(); - if (shiftPressed) { - editor.selection.selectToPosition(pos); - } - else if (!this.$clickSelection) { - editor.selection.moveToPosition(pos); - } + setTimeout(function(){ + if (shiftPressed) { + editor.selection.selectToPosition(pos); + } + else if (!this.$clickSelection) { + editor.selection.moveToPosition(pos); + } + this.select(); + }.bind(this), 0); if (editor.renderer.scroller.setCapture) { editor.renderer.scroller.setCapture(); } @@ -213,7 +216,6 @@ function DefaultHandlers(mouseHandler) { this.setState("selectByWords"); } this.$clickSelection = range; - this[this.state] && this[this.state](ev); }; this.onTripleClick = function(ev) { @@ -221,8 +223,13 @@ function DefaultHandlers(mouseHandler) { var editor = this.editor; this.setState("selectByLines"); - this.$clickSelection = editor.selection.getLineRange(pos.row); - this[this.state] && this[this.state](ev); + var range = editor.getSelectionRange(); + if (range.isMultiLine() && range.contains(pos.row, pos.column)) { + this.$clickSelection = editor.selection.getLineRange(range.start.row); + this.$clickSelection.end = editor.selection.getLineRange(range.end.row).end; + } else { + this.$clickSelection = editor.selection.getLineRange(pos.row); + } }; this.onQuadClick = function(ev) { diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index 2da72d31..f4b46c34 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -173,6 +173,17 @@ var MouseHandler = function(editor) { var timerId = setInterval(onCaptureInterval, 20); }; this.releaseMouse = null; + this.cancelContextMenu = function() { + var stop = function(e) { + if (e && e.domEvent && e.domEvent.type != "contextmenu") + return; + this.editor.off("nativecontextmenu", stop); + if (e && e.domEvent) + event.stopEvent(e.domEvent); + }.bind(this); + setTimeout(stop, 10); + this.editor.on("nativecontextmenu", stop); + }; }).call(MouseHandler.prototype); config.defineOptions(MouseHandler.prototype, "mouseHandler", { diff --git a/lib/ace/mouse/mouse_handler_test.js b/lib/ace/mouse/mouse_handler_test.js index 96374d71..571acdac 100644 --- a/lib/ace/mouse/mouse_handler_test.js +++ b/lib/ace/mouse/mouse_handler_test.js @@ -58,15 +58,17 @@ module.exports = { next(); }, - "test: double tap. issue #956" : function() { + "test: double tap. issue #956" : function(done) { // mouse up fired immediately after mouse down var target = this.editor.renderer.getMouseEventTarget(); target.dispatchEvent(MouseEvent("down", {x: 1, y: 1})); target.dispatchEvent(MouseEvent("up", {x: 1, y: 1})); target.dispatchEvent(MouseEvent("down", {x: 1, y: 1, detail: 2})); target.dispatchEvent(MouseEvent("up", {x: 1, y: 1, detail: 2})); - - assert.equal(this.editor.getSelectedText(), "Juhu"); + setTimeout(function() { + assert.equal(this.editor.getSelectedText(), "Juhu"); + done(); + }.bind(this)); } }; diff --git a/lib/ace/mouse/multi_select_handler.js b/lib/ace/mouse/multi_select_handler.js index 88dc6668..8d6af6b5 100644 --- a/lib/ace/mouse/multi_select_handler.js +++ b/lib/ace/mouse/multi_select_handler.js @@ -31,6 +31,7 @@ define(function(require, exports, module) { var event = require("../lib/event"); +var useragent = require("../lib/useragent"); // mouse function isSamePoint(p1, p2) { @@ -41,19 +42,26 @@ function onMouseDown(e) { var ev = e.domEvent; var alt = ev.altKey; var shift = ev.shiftKey; - var ctrl = e.getAccelKey(); + var ctrl = ev.ctrlKey; + var accel = e.getAccelKey(); var button = e.getButton(); + + if (ctrl && useragent.isMac) + button = ev.button; if (e.editor.inMultiSelectMode && button == 2) { e.editor.textInput.onContextMenu(e.domEvent); return; } - if (!ctrl && !alt) { + if (!ctrl && !alt && !accel) { if (button === 0 && e.editor.inMultiSelectMode) e.editor.exitMultiSelectMode(); return; } + + if (button !== 0) + return; var editor = e.editor; var selection = editor.selection; @@ -62,38 +70,37 @@ function onMouseDown(e) { var cursor = selection.getCursor(); var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); - var mouseX = e.x, mouseY = e.y; var onMouseSelection = function(e) { mouseX = e.clientX; mouseY = e.clientY; }; - - var blockSelect = function() { - var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); - var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column); - - if (isSamePoint(screenCursor, newCursor) - && isSamePoint(cursor, selection.selectionLead)) - return; - screenCursor = newCursor; - - editor.selection.moveToPosition(cursor); - editor.renderer.scrollCursorIntoView(); - - editor.removeSelectionMarkers(rectSel); - rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); - rectSel.forEach(editor.addSelectionMarker, editor); - editor.updateSelectionMarkers(); - }; var session = editor.session; var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); var screenCursor = screenAnchor; - + var selectionMode; + if (editor.$mouseHandler.$enableJumpToDef) { + if (ctrl && alt || accel && alt) + selectionMode = "add"; + else if (alt) + selectionMode = "block"; + } else { + if (accel && !alt) { + selectionMode = "add"; + if (!isMultiSelect && shift) + return; + } else if (alt) { + selectionMode = "block"; + } + } + + if (selectionMode && useragent.isMac && ev.ctrlKey) { + editor.$mouseHandler.cancelContextMenu(); + } - if (ctrl && !alt && !shift && button === 0) { + if (selectionMode == "add") { if (!isMultiSelect && inSelection) return; // dragging @@ -104,44 +111,86 @@ function onMouseDown(e) { var oldRange = selection.rangeList.rangeAtPoint(pos); + editor.$blockScrolling++; + editor.inVirtualSelectionMode = true; + + if (shift) { + oldRange = null; + range = selection.ranges[0]; + editor.removeSelectionMarker(range); + } editor.once("mouseup", function() { var tmpSel = selection.toOrientedRange(); if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) selection.substractPoint(tmpSel.cursor); else { - if (range) { + if (shift) { + selection.substractPoint(range.cursor); + } else if (range) { editor.removeSelectionMarker(range); selection.addRange(range); } selection.addRange(tmpSel); } editor.$blockScrolling--; + editor.inVirtualSelectionMode = false; }); - } else if (alt && button === 0) { + } else if (selectionMode == "block") { e.stop(); - - if (isMultiSelect && !ctrl) - selection.toSingleRange(); - else if (!isMultiSelect && ctrl) - selection.addRange(); - + editor.inVirtualSelectionMode = true; + var initialRange; var rectSel = []; - if (shift) { - screenAnchor = session.documentToScreenPosition(selection.lead); - blockSelect(); - } else { - selection.moveToPosition(pos); - } + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column); + if (isSamePoint(screenCursor, newCursor) && isSamePoint(cursor, selection.lead)) + return; + screenCursor = newCursor; + + editor.selection.moveToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + if (editor.$mouseHandler.$clickSelection && rectSel.length == 1 && rectSel[0].isEmpty()) + rectSel[0] = editor.$mouseHandler.$clickSelection.clone(); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + }; + + if (isMultiSelect && !accel) { + selection.toSingleRange(); + } else if (!isMultiSelect && accel) { + initialRange = selection.toOrientedRange(); + editor.addSelectionMarker(initialRange); + } + + if (shift) + screenAnchor = session.documentToScreenPosition(selection.lead); + else + selection.moveToPosition(pos); + + screenCursor = {row: -1, column: -1}; var onMouseSelectionEnd = function(e) { clearInterval(timerId); editor.removeSelectionMarkers(rectSel); + if (!rectSel.length) + rectSel = [selection.toOrientedRange()]; + editor.$blockScrolling++; + if (initialRange) { + editor.removeSelectionMarker(initialRange); + selection.toSingleRange(initialRange); + } for (var i = 0; i < rectSel.length; i++) selection.addRange(rectSel[i]); + editor.inVirtualSelectionMode = false; + editor.$mouseHandler.$clickSelection = null; + editor.$blockScrolling--; }; var onSelectionInterval = blockSelect; diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index b079e387..ef0f7a2e 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -76,7 +76,7 @@ var EditSession = require("./edit_session").EditSession; if (!range) return; - if (!this.inMultiSelectMode && this.rangeCount == 0) { + if (!this.inMultiSelectMode && this.rangeCount === 0) { var oldRange = this.toOrientedRange(); this.rangeList.add(oldRange); this.rangeList.add(range); @@ -168,7 +168,7 @@ var EditSession = require("./edit_session").EditSession; this._signal("removeRange", {ranges: removed}); - if (this.rangeCount == 0 && this.inMultiSelectMode) { + if (this.rangeCount === 0 && this.inMultiSelectMode) { this.inMultiSelectMode = false; this._signal("singleSelect"); this.session.$undoSelect = true; @@ -543,6 +543,19 @@ var Editor = require("./editor").Editor; } return text; }; + + this.$checkMultiselectChange = function(e, anchor) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var range = this.multiSelect.ranges[0]; + if (this.multiSelect.isEmpty() && anchor == this.multiSelect.anchor) + return; + var pos = anchor == this.multiSelect.anchor + ? range.cursor == range.start ? range.end : range.start + : range.cursor; + if (!isSamePoint(pos, anchor)) + this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()); + } + }; // todo this should change when paste becomes a command this.onPaste = function(text) { @@ -742,8 +755,15 @@ var Editor = require("./editor").Editor; var session = this.session; var sel = session.multiSelect; var ranges = sel.ranges; - - if (!ranges.length) { + // filter out ranges on same row + var row = -1; + var sameRowRanges = ranges.filter(function(r) { + if (r.cursor.row == row) + return true; + row = r.cursor.row; + }); + + if (!ranges.length || sameRowRanges.length == ranges.length - 1) { var range = this.selection.getRange(); var fr = range.start.row, lr = range.end.row; var guessRange = fr == lr; @@ -769,14 +789,9 @@ var Editor = require("./editor").Editor; } this.selection.setRange(range); } else { - // filter out ranges on same row - var row = -1; - var sameRowRanges = ranges.filter(function(r) { - if (r.cursor.row == row) - return true; - row = r.cursor.row; + sameRowRanges.forEach(function(r) { + sel.substractPoint(r.cursor); }); - sel.$onRemoveRange(sameRowRanges); var maxCol = 0; var minSpace = Infinity; @@ -851,19 +866,19 @@ var Editor = require("./editor").Editor; function alignLeft(m) { return !m[2] ? m[0] : spaces(startW) + m[2] + spaces(textW - m[2].length + endW) - + m[4].replace(/^([=:])\s+/, "$1 ") + + m[4].replace(/^([=:])\s+/, "$1 "); } function alignRight(m) { return !m[2] ? m[0] : spaces(startW + textW - m[2].length) + m[2] + spaces(endW, " ") - + m[4].replace(/^([=:])\s+/, "$1 ") + + m[4].replace(/^([=:])\s+/, "$1 "); } function unAlign(m) { return !m[2] ? m[0] : spaces(startW) + m[2] + spaces(endW) - + m[4].replace(/^([=:])\s+/, "$1 ") + + m[4].replace(/^([=:])\s+/, "$1 "); } - } + }; }).call(Editor.prototype); @@ -884,17 +899,21 @@ exports.onSessionChange = function(e) { var oldSession = e.oldSession; if (oldSession) { - oldSession.multiSelect.removeEventListener("addRange", this.$onAddRange); - oldSession.multiSelect.removeEventListener("removeRange", this.$onRemoveRange); - oldSession.multiSelect.removeEventListener("multiSelect", this.$onMultiSelect); - oldSession.multiSelect.removeEventListener("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.off("addRange", this.$onAddRange); + oldSession.multiSelect.off("removeRange", this.$onRemoveRange); + oldSession.multiSelect.off("multiSelect", this.$onMultiSelect); + oldSession.multiSelect.off("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.lead.off("change", this.$checkMultiselectChange); + oldSession.multiSelect.anchor.off("change", this.$checkMultiselectChange); } session.multiSelect.on("addRange", this.$onAddRange); session.multiSelect.on("removeRange", this.$onRemoveRange); session.multiSelect.on("multiSelect", this.$onMultiSelect); session.multiSelect.on("singleSelect", this.$onSingleSelect); - + session.multiSelect.lead.on("change", this.$checkMultiselectChange); + session.multiSelect.anchor.on("change", this.$checkMultiselectChange); + // this.$onSelectionChange = this.onSelectionChange.bind(this); if (this.inMultiSelectMode != session.selection.inMultiSelectMode) { @@ -916,6 +935,7 @@ function MultiSelect(editor) { editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); editor.$multiselectOnSessionChange = exports.onSessionChange.bind(editor); + editor.$checkMultiselectChange = editor.$checkMultiselectChange.bind(editor); editor.$multiselectOnSessionChange(editor); editor.on("changeSession", editor.$multiselectOnSessionChange); @@ -969,7 +989,7 @@ require("./config").defineOptions(Editor.prototype, "editor", { }, value: true } -}) +}); diff --git a/lib/ace/selection.js b/lib/ace/selection.js index e113d8ad..63bc9668 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -37,7 +37,6 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter; var Range = require("./range").Range; /** - * * Contains the cursor position and the text selection of an edit session. * * The row/columns used in the selection are in document coordinates representing ths coordinates as thez appear in the document before applying soft wrap and folding. @@ -49,22 +48,16 @@ var Range = require("./range").Range; * Emitted when the cursor position changes. * @event changeCursor * - * - * **/ /** * Emitted when the cursor selection changes. + * * @event changeSelection - * - * - * **/ /** * Creates a new `Selection` object. * @param {EditSession} session The session to use - * - * - * + * * @constructor **/ var Selection = function(session) { diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index db56517c..3b7bf5d9 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -44,6 +44,7 @@ var testNames = [ "ace/mode/folding/xml_test", "ace/mode/folding/coffee_test", "ace/multi_select_test", + "ace/mouse/mouse_handler_test", "ace/occur_test", "ace/range_test", "ace/range_list_test", diff --git a/lib/ace/tokenizer.js b/lib/ace/tokenizer.js index 72989766..64bf59e8 100644 --- a/lib/ace/tokenizer.js +++ b/lib/ace/tokenizer.js @@ -116,6 +116,11 @@ var Tokenizer = function(rules) { rule.onMatch = null; } + if (!ruleRegExps.length) { + mapping[0] = 0; + ruleRegExps.push("$"); + } + splitterRurles.forEach(function(rule) { rule.splitRegex = this.createSplitterRegexp(rule.regex, flag); }, this); diff --git a/tool/update_deps.js b/tool/update_deps.js index 99e2a9bd..249083c8 100644 --- a/tool/update_deps.js +++ b/tool/update_deps.js @@ -85,7 +85,7 @@ var deps = { jshint: { path: "mode/javascript/jshint.js", browserify: { - npmModule: "git+https://github.com/nightwing/jshint.git#master", + npmModule: "git+https://github.com/ajaxorg/jshint.git#master", path: "jshint/src/jshint.js", exports: "jshint" },