From b5a1aaebbac57d145d47e3d4dc74e96b39bb0443 Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Mon, 12 May 2014 17:35:41 +0100 Subject: [PATCH 01/58] change mouse cursor when over a selection Change mouse cursor to a pointer when hovered over a selection --- lib/ace/virtual_renderer.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 33f3a906..030cf5e0 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -88,6 +88,18 @@ var VirtualRenderer = function(container, theme) { this.content = dom.createElement("div"); this.content.className = "ace_content"; + + this.content.addEventListener('mousemove', function(e){ + var char = _self.pixelToScreenCoordinates(e.pageX, e.pageY); + var range = _self.session.selection.getRange(); + + if( !range.isEmpty() && range.contains(char.row, char.column) ){ + _self.content.style.cursor = "default"; + }else{ + _self.content.style.cursor = "text"; + } + }); + this.scroller.appendChild(this.content); this.$gutterLayer = new GutterLayer(this.$gutter); From 449226dac92be22b9867da75aa4f32da8907a492 Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Wed, 14 May 2014 10:27:37 +0100 Subject: [PATCH 02/58] Revert "change mouse cursor when over a selection" This reverts commit b5a1aaebbac57d145d47e3d4dc74e96b39bb0443. --- lib/ace/virtual_renderer.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 030cf5e0..33f3a906 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -88,18 +88,6 @@ var VirtualRenderer = function(container, theme) { this.content = dom.createElement("div"); this.content.className = "ace_content"; - - this.content.addEventListener('mousemove', function(e){ - var char = _self.pixelToScreenCoordinates(e.pageX, e.pageY); - var range = _self.session.selection.getRange(); - - if( !range.isEmpty() && range.contains(char.row, char.column) ){ - _self.content.style.cursor = "default"; - }else{ - _self.content.style.cursor = "text"; - } - }); - this.scroller.appendChild(this.content); this.$gutterLayer = new GutterLayer(this.$gutter); From 23680b4f7ed62144076f11e7421296904e78c3ce Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Wed, 14 May 2014 10:30:31 +0100 Subject: [PATCH 03/58] change mouse cursor when over selection moved to mouse handler. don't change when dragging or selecting. --- lib/ace/css/editor.css | 4 ++++ lib/ace/mouse/mouse_handler.js | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index a2b7475e..faa678cc 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -50,6 +50,10 @@ cursor: text !important; } +.ace_selection .ace_content { + cursor: default; +} + .ace_gutter { position: absolute; overflow : hidden; diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index c7573e94..41521120 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -74,6 +74,17 @@ var MouseHandler = function(editor) { editor.focus(); return event.preventDefault(e); }); + + editor.on('mousemove', function(e){ + var char = editor.renderer.pixelToScreenCoordinates(e.x, e.y); + var range = editor.session.selection.getRange(); + + if( !range.isEmpty() && range.contains(char.row, char.column) ){ + editor.setStyle("ace_selection"); + }else{ + editor.unsetStyle("ace_selection"); + } + }); }; (function() { From 8dffbbe200be7a8d871d3b8d72dab8a882875f78 Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Wed, 14 May 2014 14:56:57 +0100 Subject: [PATCH 04/58] selection hover improvements fix issue with wrapped lines and compute only when necessary. --- lib/ace/mouse/mouse_handler.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index 41521120..e3b45abf 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -40,6 +40,7 @@ var DragdropHandler = require("./dragdrop_handler").DragdropHandler; var config = require("../config"); var MouseHandler = function(editor) { + var _self = this; this.editor = editor; new DefaultHandlers(this); @@ -76,12 +77,15 @@ var MouseHandler = function(editor) { }); editor.on('mousemove', function(e){ - var char = editor.renderer.pixelToScreenCoordinates(e.x, e.y); + if (_self.state) + return; + + var char = editor.renderer.screenToTextCoordinates(e.x, e.y); var range = editor.session.selection.getRange(); - if( !range.isEmpty() && range.contains(char.row, char.column) ){ + if (!range.isEmpty() && range.inside(char.row, char.column)) { editor.setStyle("ace_selection"); - }else{ + } else { editor.unsetStyle("ace_selection"); } }); From 39fd0e6c42f35a2c123e241a1f553cf2f916da22 Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Wed, 14 May 2014 15:08:40 +0100 Subject: [PATCH 05/58] fix mouse not always changing on first line --- lib/ace/mouse/mouse_handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index e3b45abf..6a91b172 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -83,7 +83,7 @@ var MouseHandler = function(editor) { var char = editor.renderer.screenToTextCoordinates(e.x, e.y); var range = editor.session.selection.getRange(); - if (!range.isEmpty() && range.inside(char.row, char.column)) { + if (!range.isEmpty() && range.insideStart(char.row, char.column)) { editor.setStyle("ace_selection"); } else { editor.unsetStyle("ace_selection"); From 4e2d7a41f51494706a2c6d78ea28ffbebf6cc0ac Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 18:15:59 +0400 Subject: [PATCH 06/58] update jshint --- lib/ace/mode/javascript/jshint.js | 542 ++++++++++++++++++++---------- tool/update_deps.js | 2 +- 2 files changed, 365 insertions(+), 179 deletions(-) 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/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" }, From 9ab12a6ed37eeec01c46bee8f75cfb02999817d7 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:07:07 +0400 Subject: [PATCH 07/58] fix ctrl-shift-mousedown --- lib/ace/autocomplete.js | 8 +--- lib/ace/lib/event.js | 4 +- lib/ace/mouse/mouse_handler.js | 11 +++++ lib/ace/mouse/multi_select_handler.js | 47 +++++++++++++++++--- lib/ace/multi_select.js | 62 ++++++++++++++++++--------- lib/ace/selection.js | 11 +---- 6 files changed, 99 insertions(+), 44 deletions(-) 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/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/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index c7573e94..a991a849 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -157,6 +157,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/multi_select_handler.js b/lib/ace/mouse/multi_select_handler.js index 88dc6668..0620f8f5 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,8 +42,12 @@ 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); @@ -54,6 +59,9 @@ function onMouseDown(e) { e.editor.exitMultiSelectMode(); return; } + + if (button !== 0) + return; var editor = e.editor; var selection = editor.selection; @@ -90,10 +98,28 @@ function onMouseDown(e) { 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,23 +130,34 @@ 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) 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) { From 88aed652124ed2210e08db358c83dfc03dbf2134 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:26:13 +0400 Subject: [PATCH 08/58] fix home key on folded line --- lib/ace/edit_session/folding.js | 10 +++++----- lib/ace/ext/modelist.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) 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/modelist.js b/lib/ace/ext/modelist.js index 66fe3de8..77b5b127 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"], From ba93cc6dfad98d4a20ddff0ed09c7396cd38c0ee Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:30:47 +0400 Subject: [PATCH 09/58] fix tab detection --- lib/ace/ext/whitespace.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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}; }; From 4872441bc6126665bcbdb507f31a7dac7a72d13a Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:32:08 +0400 Subject: [PATCH 10/58] fix vim cc --- lib/ace/keyboard/vim/maps/operators.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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); From 72a509e5422b39d4712fee46b3b4b2161999f202 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:32:34 +0400 Subject: [PATCH 11/58] improve ruby outdent --- lib/ace/mode/ruby.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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"; From 7e2dda1e185d76d9044afada55a1a6d0585f8448 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 21 May 2014 20:38:07 +0400 Subject: [PATCH 12/58] allow tokenizer states with only defaultToken --- lib/ace/mode/text.js | 2 +- lib/ace/tokenizer.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) 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/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); From d0acc15e67bbe1b9ef51703644b1e30abae75659 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 23 May 2014 00:16:31 +0400 Subject: [PATCH 13/58] fix #1973 custom array of autocompleters never used --- lib/ace/ext/language_tools.js | 39 +++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/ace/ext/language_tools.js b/lib/ace/ext/language_tools.js index 651835d4..f8c00047 100644 --- a/lib/ace/ext/language_tools.js +++ b/lib/ace/ext/language_tools.js @@ -120,40 +120,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 @@ -168,7 +173,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); @@ -183,7 +189,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 { From 66866121e9ae1015148d0ac6ee0af375a46f30c1 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 23 May 2014 14:13:23 +0400 Subject: [PATCH 14/58] fix issue with scrolling into view on focus --- demo/autoresize.html | 40 +++++++++++++++++++--------------- lib/ace/mouse/mouse_handler.js | 6 ++++- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/demo/autoresize.html b/demo/autoresize.html index 2b64d96a..73a87599 100644 --- a/demo/autoresize.html +++ b/demo/autoresize.html @@ -26,28 +26,32 @@

 
-
+
 
 
 
diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js
index c7573e94..a69a672c 100644
--- a/lib/ace/mouse/mouse_handler.js
+++ b/lib/ace/mouse/mouse_handler.js
@@ -46,7 +46,11 @@ var MouseHandler = function(editor) {
     new DefaultGutterHandler(this);
     new DragdropHandler(this);
     
-    var focusEditor = function(e) { editor.focus() };
+    var focusEditor = function(e) { 
+        if (!editor.isFocused() && editor.textInput)
+            editor.textInput.onContextMenu(e);
+        editor.focus() 
+    };
     
     var mouseTarget = editor.renderer.getMouseEventTarget();
     event.addListener(mouseTarget, "click", this.onMouseEvent.bind(this, "click"));

From 1c2bc2942436b2f7ad998bcf7059671a592f8768 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Wed, 28 May 2014 01:25:18 +0400
Subject: [PATCH 15/58] fix selecting with ctrl-alt click

---
 lib/ace/mouse/default_handlers.js     | 24 +++++----
 lib/ace/mouse/multi_select_handler.js | 76 ++++++++++++++++-----------
 2 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js
index 8a9adcf7..7bb701a8 100644
--- a/lib/ace/mouse/default_handlers.js
+++ b/lib/ace/mouse/default_handlers.js
@@ -109,12 +109,14 @@ 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);
+            }
+        }.bind(this), 0);
         if (editor.renderer.scroller.setCapture) {
             editor.renderer.scroller.setCapture();
         }
@@ -213,7 +215,6 @@ function DefaultHandlers(mouseHandler) {
             this.setState("selectByWords");
         }
         this.$clickSelection = range;
-        this[this.state] && this[this.state](ev);
     };
 
     this.onTripleClick = function(ev) {
@@ -221,8 +222,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/multi_select_handler.js b/lib/ace/mouse/multi_select_handler.js
index 0620f8f5..8d6af6b5 100644
--- a/lib/ace/mouse/multi_select_handler.js
+++ b/lib/ace/mouse/multi_select_handler.js
@@ -54,7 +54,7 @@ function onMouseDown(e) {
         return;
     }
     
-    if (!ctrl && !alt) {
+    if (!ctrl && !alt && !accel) {
         if (button === 0 && e.editor.inMultiSelectMode)
             e.editor.exitMultiSelectMode();
         return;
@@ -70,30 +70,11 @@ 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);
@@ -159,26 +140,57 @@ function onMouseDown(e) {
 
     } 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;

From a7cac9466d563ed31bad9e0fa8d13b463ee5f6d9 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Sun, 1 Jun 2014 16:04:07 +0400
Subject: [PATCH 16/58] add vala mode

---
 demo/kitchen-sink/docs/vala.vala     |  21 ++
 lib/ace/ext/language_tools.js        |   5 +-
 lib/ace/ext/modelist.js              |   1 +
 lib/ace/mode/vala.js                 | 105 ++++++
 lib/ace/mode/vala_highlight_rules.js | 457 +++++++++++++++++++++++++++
 lib/ace/snippets/vala.js             | 195 ++++++++++++
 6 files changed, 782 insertions(+), 2 deletions(-)
 create mode 100644 demo/kitchen-sink/docs/vala.vala
 create mode 100644 lib/ace/mode/vala.js
 create mode 100644 lib/ace/mode/vala_highlight_rules.js
 create mode 100644 lib/ace/snippets/vala.js

diff --git a/demo/kitchen-sink/docs/vala.vala b/demo/kitchen-sink/docs/vala.vala
new file mode 100644
index 00000000..f9600286
--- /dev/null
+++ b/demo/kitchen-sink/docs/vala.vala
@@ -0,0 +1,21 @@
+using Gtk;
+ 
+int main (string[] args) {
+    Gtk.init (ref args);
+    var foo = new MyFoo>();
+
+    var window = new Window();
+    window.title = "Hello, World!";
+    window.border_width = 10;
+    window.window_position = WindowPosition.CENTER;
+    window.set_default_size(350, 70);
+    window.destroy.connect(Gtk.main_quit);
+ 
+    var label = new Label("Hello, World!");
+ 
+    window.add(label);
+    window.show_all();
+ 
+    Gtk.main();
+    return 0;
+}
\ No newline at end of file
diff --git a/lib/ace/ext/language_tools.js b/lib/ace/ext/language_tools.js
index 651835d4..17d23bf7 100644
--- a/lib/ace/ext/language_tools.js
+++ b/lib/ace/ext/language_tools.js
@@ -108,8 +108,9 @@ var loadSnippetFile = function(id) {
     config.loadModule(snippetFilePath, function(m) {
         if (m) {
             snippetManager.files[id] = m;
-            m.snippets = snippetManager.parseSnippetFile(m.snippetText);
-            snippetManager.register(m.snippets, m.scope);
+            if (!m.snippets && m.snippetText)
+                m.snippets = snippetManager.parseSnippetFile(m.snippetText);
+            snippetManager.register(m.snippets || [], m.scope);
             if (m.includeScopes) {
                 snippetManager.snippetMap[m.scope].includeScopes = m.includeScopes;
                 m.includeScopes.forEach(function(x) {
diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js
index 66fe3de8..f6436ffb 100644
--- a/lib/ace/ext/modelist.js
+++ b/lib/ace/ext/modelist.js
@@ -143,6 +143,7 @@ var supportedModes = {
     Toml:        ["toml"],
     Twig:        ["twig"],
     Typescript:  ["ts|typescript|str"],
+    Vala:        ["vala"],
     VBScript:    ["vbs"],
     Velocity:    ["vm"],
     Verilog:     ["v|vh|sv|svh"],
diff --git a/lib/ace/mode/vala.js b/lib/ace/mode/vala.js
new file mode 100644
index 00000000..4ff8ab1a
--- /dev/null
+++ b/lib/ace/mode/vala.js
@@ -0,0 +1,105 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2012, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+  THIS FILE WAS AUTOGENERATED BY mode.tmpl.js
+*/
+
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var TextMode = require("./text").Mode;
+var Tokenizer = require("../tokenizer").Tokenizer;
+var ValaHighlightRules = require("./vala_highlight_rules").ValaHighlightRules;
+var FoldMode = require("./folding/cstyle").FoldMode;
+var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
+var CStyleFoldMode = require("./folding/cstyle").FoldMode;
+var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
+
+var Mode = function() {
+    this.HighlightRules = ValaHighlightRules;
+    
+    this.$outdent = new MatchingBraceOutdent();
+    this.$behaviour = new CstyleBehaviour();
+    this.foldingRules = new CStyleFoldMode();
+};
+oop.inherits(Mode, TextMode);
+
+(function() {
+    this.lineCommentStart = "//";
+    this.blockComment = {start: "/*", end: "*/"};
+
+    this.getNextLineIndent = function(state, line, tab) {
+        var indent = this.$getIndent(line);
+
+        var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
+        var tokens = tokenizedLine.tokens;
+        var endState = tokenizedLine.state;
+
+        if (tokens.length && tokens[tokens.length-1].type == "comment") {
+            return indent;
+        }
+
+        if (state == "start" || state == "no_regex") {
+            var match = line.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);
+            if (match) {
+                indent += tab;
+            }
+        } else if (state == "doc-start") {
+            if (endState == "start" || endState == "no_regex") {
+                return "";
+            }
+            var match = line.match(/^\s*(\/?)\*/);
+            if (match) {
+                if (match[1]) {
+                    indent += " ";
+                }
+                indent += "* ";
+            }
+        }
+
+        return indent;
+    };
+
+    this.checkOutdent = function(state, line, input) {
+        return this.$outdent.checkOutdent(line, input);
+    };
+
+    this.autoOutdent = function(state, doc, row) {
+        this.$outdent.autoOutdent(doc, row);
+    };
+
+    // Extra logic goes here.
+    this.$id = "ace/mode/vala"
+}).call(Mode.prototype);
+
+exports.Mode = Mode;
+});
\ No newline at end of file
diff --git a/lib/ace/mode/vala_highlight_rules.js b/lib/ace/mode/vala_highlight_rules.js
new file mode 100644
index 00000000..f199426d
--- /dev/null
+++ b/lib/ace/mode/vala_highlight_rules.js
@@ -0,0 +1,457 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2012, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Ajax.org B.V. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* This file was autogenerated from https://raw.githubusercontent.com/technosophos/Vala-TMBundle/master/Syntaxes/Vala.tmLanguage (uuid: ) */
+/****************************************************************************************
+ * IT MIGHT NOT BE PERFECT ...But it's a good start from an existing *.tmlanguage file. *
+ * fileTypes                                                                            *
+ ****************************************************************************************/
+
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
+
+var ValaHighlightRules = function() {
+    // regexp must not have capturing parentheses. Use (?:) instead.
+    // regexps are ordered -> the first match is used
+
+    this.$rules = { start: 
+       [ { token: 
+            [ 'meta.using.vala',
+              'keyword.other.using.vala',
+              'meta.using.vala',
+              'storage.modifier.using.vala',
+              'meta.using.vala',
+              'punctuation.terminator.vala' ],
+           regex: '^(\\s*)(using)\\b(?:(\\s*)([^ ;$]+)(\\s*)((?:;)?))?' },
+         { include: '#code' } ],
+      '#all-types': 
+       [ { include: '#primitive-arrays' },
+         { include: '#primitive-types' },
+         { include: '#object-types' } ],
+      '#annotations': 
+       [ { token: 
+            [ 'storage.type.annotation.vala',
+              'punctuation.definition.annotation-arguments.begin.vala' ],
+           regex: '(@[^ (]+)(\\()',
+           push: 
+            [ { token: 'punctuation.definition.annotation-arguments.end.vala',
+                regex: '\\)',
+                next: 'pop' },
+              { token: 
+                 [ 'constant.other.key.vala',
+                   'text',
+                   'keyword.operator.assignment.vala' ],
+                regex: '(\\w*)(\\s*)(=)' },
+              { include: '#code' },
+              { token: 'punctuation.seperator.property.vala', regex: ',' },
+              { defaultToken: 'meta.declaration.annotation.vala' } ] },
+         { token: 'storage.type.annotation.vala', regex: '@\\w*' } ],
+      '#anonymous-classes-and-new': 
+       [ { token: 'keyword.control.new.vala',
+           regex: '\\bnew\\b',
+           push_disabled: 
+            [ { token: 'text',
+                regex: '(?<=\\)|\\])(?!\\s*{)|(?<=})|(?=;)',
+                TODO: 'FIXME: regexp doesn\'t have js equivalent',
+                originalRegex: '(?<=\\)|\\])(?!\\s*{)|(?<=})|(?=;)',
+                next: 'pop' },
+              { token: [ 'storage.type.vala', 'text' ],
+                regex: '(\\w+)(\\s*)(?=\\[)',
+                push: 
+                 [ { token: 'text', regex: '}|(?=;|\\))', next: 'pop' },
+                   { token: 'text',
+                     regex: '\\[',
+                     push: 
+                      [ { token: 'text', regex: '\\]', next: 'pop' },
+                        { include: '#code' } ] },
+                   { token: 'text',
+                     regex: '{',
+                     push: 
+                      [ { token: 'text', regex: '(?=})', next: 'pop' },
+                        { include: '#code' } ] } ] },
+              { token: 'text',
+                regex: '(?=\\w.*\\()',
+                push: 
+                 [ { token: 'text',
+                     regex: '(?<=\\))',
+                     TODO: 'FIXME: regexp doesn\'t have js equivalent',
+                     originalRegex: '(?<=\\))',
+                     next: 'pop' },
+                   { include: '#object-types' },
+                   { token: 'text',
+                     regex: '\\(',
+                     push: 
+                      [ { token: 'text', regex: '\\)', next: 'pop' },
+                        { include: '#code' } ] } ] },
+              { token: 'meta.inner-class.vala',
+                regex: '{',
+                push: 
+                 [ { token: 'meta.inner-class.vala', regex: '}', next: 'pop' },
+                   { include: '#class-body' },
+                   { defaultToken: 'meta.inner-class.vala' } ] } ] } ],
+      '#assertions': 
+       [ { token: 
+            [ 'keyword.control.assert.vala',
+              'meta.declaration.assertion.vala' ],
+           regex: '\\b(assert|requires|ensures)(\\s)',
+           push: 
+            [ { token: 'meta.declaration.assertion.vala',
+                regex: '$',
+                next: 'pop' },
+              { token: 'keyword.operator.assert.expression-seperator.vala',
+                regex: ':' },
+              { include: '#code' },
+              { defaultToken: 'meta.declaration.assertion.vala' } ] } ],
+      '#class': 
+       [ { token: 'meta.class.vala',
+           regex: '(?=\\w?[\\w\\s]*(?:class|(?:@)?interface|enum|struct|namespace)\\s+\\w+)',
+           push: 
+            [ { token: 'punctuation.section.class.end.vala',
+                regex: '}',
+                next: 'pop' },
+              { include: '#storage-modifiers' },
+              { include: '#comments' },
+              { token: 
+                 [ 'storage.modifier.vala',
+                   'meta.class.identifier.vala',
+                   'entity.name.type.class.vala' ],
+                regex: '(class|(?:@)?interface|enum|struct|namespace)(\\s+)([\\w\\.]+)' },
+              { token: 'storage.modifier.extends.vala',
+                regex: ':',
+                push: 
+                 [ { token: 'meta.definition.class.inherited.classes.vala',
+                     regex: '(?={|,)',
+                     next: 'pop' },
+                   { include: '#object-types-inherited' },
+                   { include: '#comments' },
+                   { defaultToken: 'meta.definition.class.inherited.classes.vala' } ] },
+              { token: 
+                 [ 'storage.modifier.implements.vala',
+                   'meta.definition.class.implemented.interfaces.vala' ],
+                regex: '(,)(\\s)',
+                push: 
+                 [ { token: 'meta.definition.class.implemented.interfaces.vala',
+                     regex: '(?=\\{)',
+                     next: 'pop' },
+                   { include: '#object-types-inherited' },
+                   { include: '#comments' },
+                   { defaultToken: 'meta.definition.class.implemented.interfaces.vala' } ] },
+              { token: 'meta.class.body.vala',
+                regex: '{',
+                push: 
+                 [ { token: 'meta.class.body.vala', regex: '(?=})', next: 'pop' },
+                   { include: '#class-body' },
+                   { defaultToken: 'meta.class.body.vala' } ] },
+              { defaultToken: 'meta.class.vala' } ],
+           comment: 'attempting to put namespace in here.' } ],
+      '#class-body': 
+       [ { include: '#comments' },
+         { include: '#class' },
+         { include: '#enums' },
+         { include: '#methods' },
+         { include: '#annotations' },
+         { include: '#storage-modifiers' },
+         { include: '#code' } ],
+      '#code': 
+       [ { include: '#comments' },
+         { include: '#class' },
+         { token: 'text',
+           regex: '{',
+           push: 
+            [ { token: 'text', regex: '}', next: 'pop' },
+              { include: '#code' } ] },
+         { include: '#assertions' },
+         { include: '#parens' },
+         { include: '#constants-and-special-vars' },
+         { include: '#anonymous-classes-and-new' },
+         { include: '#keywords' },
+         { include: '#storage-modifiers' },
+         { include: '#strings' },
+         { include: '#all-types' } ],
+      '#comments': 
+       [ { token: 'punctuation.definition.comment.vala',
+           regex: '/\\*\\*/' },
+         { include: 'text.html.javadoc' },
+         { include: '#comments-inline' } ],
+      '#comments-inline': 
+       [ { token: 'punctuation.definition.comment.vala',
+           regex: '/\\*',
+           push: 
+            [ { token: 'punctuation.definition.comment.vala',
+                regex: '\\*/',
+                next: 'pop' },
+              { defaultToken: 'comment.block.vala' } ] },
+         { token: 
+            [ 'text',
+              'punctuation.definition.comment.vala',
+              'comment.line.double-slash.vala' ],
+           regex: '(\\s*)(//)(.*$)' } ],
+      '#constants-and-special-vars': 
+       [ { token: 'constant.language.vala',
+           regex: '\\b(?:true|false|null)\\b' },
+         { token: 'variable.language.vala',
+           regex: '\\b(?:this|base)\\b' },
+         { token: 'constant.numeric.vala',
+           regex: '\\b(?:0(?:x|X)[0-9a-fA-F]*|(?:[0-9]+\\.?[0-9]*|\\.[0-9]+)(?:(?:e|E)(?:\\+|-)?[0-9]+)?)(?:[LlFfUuDd]|UL|ul)?\\b' },
+         { token: [ 'keyword.operator.dereference.vala', 'constant.other.vala' ],
+           regex: '((?:\\.)?)\\b([A-Z][A-Z0-9_]+)(?!<|\\.class|\\s*\\w+\\s*=)\\b' } ],
+      '#enums': 
+       [ { token: 'text',
+           regex: '^(?=\\s*[A-Z0-9_]+\\s*(?:{|\\(|,))',
+           push: 
+            [ { token: 'text', regex: '(?=;|})', next: 'pop' },
+              { token: 'constant.other.enum.vala',
+                regex: '\\w+',
+                push: 
+                 [ { token: 'meta.enum.vala', regex: '(?=,|;|})', next: 'pop' },
+                   { include: '#parens' },
+                   { token: 'text',
+                     regex: '{',
+                     push: 
+                      [ { token: 'text', regex: '}', next: 'pop' },
+                        { include: '#class-body' } ] },
+                   { defaultToken: 'meta.enum.vala' } ] } ] } ],
+      '#keywords': 
+       [ { token: 'keyword.control.catch-exception.vala',
+           regex: '\\b(?:try|catch|finally|throw)\\b' },
+         { token: 'keyword.control.vala', regex: '\\?|:|\\?\\?' },
+         { token: 'keyword.control.vala',
+           regex: '\\b(?:return|break|case|continue|default|do|while|for|foreach|switch|if|else|in|yield|get|set|value)\\b' },
+         { token: 'keyword.operator.vala',
+           regex: '\\b(?:typeof|is|as)\\b' },
+         { token: 'keyword.operator.comparison.vala',
+           regex: '==|!=|<=|>=|<>|<|>' },
+         { token: 'keyword.operator.assignment.vala', regex: '=' },
+         { token: 'keyword.operator.increment-decrement.vala',
+           regex: '\\-\\-|\\+\\+' },
+         { token: 'keyword.operator.arithmetic.vala',
+           regex: '\\-|\\+|\\*|\\/|%' },
+         { token: 'keyword.operator.logical.vala', regex: '!|&&|\\|\\|' },
+         { token: 'keyword.operator.dereference.vala',
+           regex: '\\.(?=\\S)',
+           originalRegex: '(?<=\\S)\\.(?=\\S)' },
+         { token: 'punctuation.terminator.vala', regex: ';' },
+         { token: 'keyword.operator.ownership', regex: 'owned|unowned' } ],
+      '#methods': 
+       [ { token: 'meta.method.vala',
+           regex: '(?!new)(?=\\w.*\\s+)(?=[^=]+\\()',
+           push: 
+            [ { token: 'meta.method.vala', regex: '}|(?=;)', next: 'pop' },
+              { include: '#storage-modifiers' },
+              { token: [ 'entity.name.function.vala', 'meta.method.identifier.vala' ],
+                regex: '([\\~\\w\\.]+)(\\s*\\()',
+                push: 
+                 [ { token: 'meta.method.identifier.vala',
+                     regex: '\\)',
+                     next: 'pop' },
+                   { include: '#parameters' },
+                   { defaultToken: 'meta.method.identifier.vala' } ] },
+              { token: 'meta.method.return-type.vala',
+                regex: '(?=\\w.*\\s+\\w+\\s*\\()',
+                push: 
+                 [ { token: 'meta.method.return-type.vala',
+                     regex: '(?=\\w+\\s*\\()',
+                     next: 'pop' },
+                   { include: '#all-types' },
+                   { defaultToken: 'meta.method.return-type.vala' } ] },
+              { include: '#throws' },
+              { token: 'meta.method.body.vala',
+                regex: '{',
+                push: 
+                 [ { token: 'meta.method.body.vala', regex: '(?=})', next: 'pop' },
+                   { include: '#code' },
+                   { defaultToken: 'meta.method.body.vala' } ] },
+              { defaultToken: 'meta.method.vala' } ] } ],
+      '#namespace': 
+       [ { token: 'text',
+           regex: '^(?=\\s*[A-Z0-9_]+\\s*(?:{|\\(|,))',
+           push: 
+            [ { token: 'text', regex: '(?=;|})', next: 'pop' },
+              { token: 'constant.other.namespace.vala',
+                regex: '\\w+',
+                push: 
+                 [ { token: 'meta.namespace.vala', regex: '(?=,|;|})', next: 'pop' },
+                   { include: '#parens' },
+                   { token: 'text',
+                     regex: '{',
+                     push: 
+                      [ { token: 'text', regex: '}', next: 'pop' },
+                        { include: '#code' } ] },
+                   { defaultToken: 'meta.namespace.vala' } ] } ],
+           comment: 'This is not quite right. See the class grammar right now' } ],
+      '#object-types': 
+       [ { token: 'storage.type.generic.vala',
+           regex: '\\b(?:[a-z]\\w*\\.)*[A-Z]+\\w*<',
+           push: 
+            [ { token: 'storage.type.generic.vala',
+                regex: '>|[^\\w\\s,\\?<\\[()\\]]',
+                TODO: 'FIXME: regexp doesn\'t have js equivalent',
+                originalRegex: '>|[^\\w\\s,\\?<\\[(?:[,]+)\\]]',
+                next: 'pop' },
+              { include: '#object-types' },
+              { token: 'storage.type.generic.vala',
+                regex: '<',
+                push: 
+                 [ { token: 'storage.type.generic.vala',
+                     regex: '>|[^\\w\\s,\\[\\]<]',
+                     next: 'pop' },
+                   { defaultToken: 'storage.type.generic.vala' } ],
+                comment: 'This is just to support <>\'s with no actual type prefix' },
+              { defaultToken: 'storage.type.generic.vala' } ] },
+         { token: 'storage.type.object.array.vala',
+           regex: '\\b(?:[a-z]\\w*\\.)*[A-Z]+\\w*(?=\\[)',
+           push: 
+            [ { token: 'storage.type.object.array.vala',
+                regex: '(?=[^\\]\\s])',
+                next: 'pop' },
+              { token: 'text',
+                regex: '\\[',
+                push: 
+                 [ { token: 'text', regex: '\\]', next: 'pop' },
+                   { include: '#code' } ] },
+              { defaultToken: 'storage.type.object.array.vala' } ] },
+         { token: 
+            [ 'storage.type.vala',
+              'keyword.operator.dereference.vala',
+              'storage.type.vala' ],
+           regex: '\\b(?:([a-z]\\w*)(\\.))*([A-Z]+\\w*\\b)' } ],
+      '#object-types-inherited': 
+       [ { token: 'entity.other.inherited-class.vala',
+           regex: '\\b(?:[a-z]\\w*\\.)*[A-Z]+\\w*<',
+           push: 
+            [ { token: 'entity.other.inherited-class.vala',
+                regex: '>|[^\\w\\s,<]',
+                next: 'pop' },
+              { include: '#object-types' },
+              { token: 'storage.type.generic.vala',
+                regex: '<',
+                push: 
+                 [ { token: 'storage.type.generic.vala',
+                     regex: '>|[^\\w\\s,<]',
+                     next: 'pop' },
+                   { defaultToken: 'storage.type.generic.vala' } ],
+                comment: 'This is just to support <>\'s with no actual type prefix' },
+              { defaultToken: 'entity.other.inherited-class.vala' } ] },
+         { token: 
+            [ 'entity.other.inherited-class.vala',
+              'keyword.operator.dereference.vala',
+              'entity.other.inherited-class.vala' ],
+           regex: '\\b(?:([a-z]\\w*)(\\.))*([A-Z]+\\w*)' } ],
+      '#parameters': 
+       [ { token: 'storage.modifier.vala', regex: 'final' },
+         { include: '#primitive-arrays' },
+         { include: '#primitive-types' },
+         { include: '#object-types' },
+         { token: 'variable.parameter.vala', regex: '\\w+' } ],
+      '#parens': 
+       [ { token: 'text',
+           regex: '\\(',
+           push: 
+            [ { token: 'text', regex: '\\)', next: 'pop' },
+              { include: '#code' } ] } ],
+      '#primitive-arrays': 
+       [ { token: 'storage.type.primitive.array.vala',
+           regex: '\\b(?:bool|byte|sbyte|char|decimal|double|float|int|uint|long|ulong|object|short|ushort|string|void|int8|int16|int32|int64|uint8|uint16|uint32|uint64)(?:\\[\\])*\\b' } ],
+      '#primitive-types': 
+       [ { token: 'storage.type.primitive.vala',
+           regex: '\\b(?:var|bool|byte|sbyte|char|decimal|double|float|int|uint|long|ulong|object|short|ushort|string|void|signal|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b',
+           comment: 'var is not really a primitive, but acts like one in most cases' } ],
+      '#storage-modifiers': 
+       [ { token: 'storage.modifier.vala',
+           regex: '\\b(?:public|private|protected|internal|static|final|sealed|virtual|override|abstract|readonly|volatile|dynamic|async|unsafe|out|ref|weak|owned|unowned|const)\\b',
+           comment: 'Not sure about unsafe and readonly' } ],
+      '#strings': 
+       [ { token: 'punctuation.definition.string.begin.vala',
+           regex: '@"',
+           push: 
+            [ { token: 'punctuation.definition.string.end.vala',
+                regex: '"',
+                next: 'pop' },
+              { token: 'constant.character.escape.vala',
+                regex: '\\\\.|%[\\w\\.\\-]+|\\$(?:\\w+|\\([\\w\\s\\+\\-\\*\\/]+\\))' },
+              { defaultToken: 'string.quoted.interpolated.vala' } ] },
+         { token: 'punctuation.definition.string.begin.vala',
+           regex: '"',
+           push: 
+            [ { token: 'punctuation.definition.string.end.vala',
+                regex: '"',
+                next: 'pop' },
+              { token: 'constant.character.escape.vala', regex: '\\\\.' },
+              { token: 'constant.character.escape.vala',
+                regex: '%[\\w\\.\\-]+' },
+              { defaultToken: 'string.quoted.double.vala' } ] },
+         { token: 'punctuation.definition.string.begin.vala',
+           regex: '\'',
+           push: 
+            [ { token: 'punctuation.definition.string.end.vala',
+                regex: '\'',
+                next: 'pop' },
+              { token: 'constant.character.escape.vala', regex: '\\\\.' },
+              { defaultToken: 'string.quoted.single.vala' } ] },
+         { token: 'punctuation.definition.string.begin.vala',
+           regex: '"""',
+           push: 
+            [ { token: 'punctuation.definition.string.end.vala',
+                regex: '"""',
+                next: 'pop' },
+              { token: 'constant.character.escape.vala',
+                regex: '%[\\w\\.\\-]+' },
+              { defaultToken: 'string.quoted.triple.vala' } ] } ],
+      '#throws': 
+       [ { token: 'storage.modifier.vala',
+           regex: 'throws',
+           push: 
+            [ { token: 'meta.throwables.vala', regex: '(?={|;)', next: 'pop' },
+              { include: '#object-types' },
+              { defaultToken: 'meta.throwables.vala' } ] } ],
+      '#values': 
+       [ { include: '#strings' },
+         { include: '#object-types' },
+         { include: '#constants-and-special-vars' } ] }
+    
+    this.normalizeRules();
+};
+
+ValaHighlightRules.metaData = { 
+    comment: 'Based heavily on the Java bundle\'s language syntax. TODO:\n* Closures\n* Delegates\n* Properties: Better support for properties.\n* Annotations\n* Error domains\n* Named arguments\n* Array slicing, negative indexes, multidimensional\n* construct blocks\n* lock blocks?\n* regex literals\n* DocBlock syntax highlighting. (Currently importing javadoc)\n* Folding rule for comments.\n',
+      fileTypes: [ 'vala' ],
+      foldingStartMarker: '(\\{\\s*(//.*)?$|^\\s*// \\{\\{\\{)',
+      foldingStopMarker: '^\\s*(\\}|// \\}\\}\\}$)',
+      name: 'Vala',
+      scopeName: 'source.vala' }
+
+
+oop.inherits(ValaHighlightRules, TextHighlightRules);
+
+exports.ValaHighlightRules = ValaHighlightRules;
+});
\ No newline at end of file
diff --git a/lib/ace/snippets/vala.js b/lib/ace/snippets/vala.js
new file mode 100644
index 00000000..6092d3c1
--- /dev/null
+++ b/lib/ace/snippets/vala.js
@@ -0,0 +1,195 @@
+define(function(require, exports, module) {
+"use strict";
+
+// exports.snippetText = require("../requirejs/text!./.snippets");
+exports.snippets = [
+    {
+        "content": "case ${1:condition}:\n\t$0\n\tbreak;\n",
+        "name": "case",
+        "scope": "vala",
+        "tabTrigger": "case"
+    },
+    {
+        "content": "/**\n * ${6}\n */\n${1:public} class ${2:MethodName}${3: : GLib.Object} {\n\n\t/**\n\t * ${7}\n\t */\n\tpublic ${2}(${4}) {\n\t\t${5}\n\t}\n\n\t$0\n}",
+        "name": "class",
+        "scope": "vala",
+        "tabTrigger": "class"
+    },
+    {
+        "content": "(${1}) => {\n\t${0}\n}\n",
+        "name": "closure",
+        "scope": "vala",
+        "tabTrigger": "=>"
+    },
+    {
+        "content": "/*\n * $0\n */",
+        "name": "Comment (multiline)",
+        "scope": "vala",
+        "tabTrigger": "/*"
+    },
+    {
+        "content": "Console.WriteLine($1);\n$0",
+        "name": "Console.WriteLine (writeline)",
+        "scope": "vala",
+        "tabTrigger": "writeline"
+    },
+    {
+        "content": "[DBus(name = \"$0\")]",
+        "name": "DBus annotation",
+        "scope": "vala",
+        "tabTrigger": "[DBus"
+    },
+    {
+        "content": "delegate ${1:void} ${2:DelegateName}($0);",
+        "name": "delegate",
+        "scope": "vala",
+        "tabTrigger": "delegate"
+    },
+    {
+        "content": "do {\n\t$0\n} while ($1);\n",
+        "name": "do while",
+        "scope": "vala",
+        "tabTrigger": "dowhile"
+    },
+    {
+        "content": "/**\n * $0\n */",
+        "name": "DocBlock",
+        "scope": "vala",
+        "tabTrigger": "/**"
+    },
+    {
+        "content": "else if ($1) {\n\t$0\n}\n",
+        "name": "else if (elseif)",
+        "scope": "vala",
+        "tabTrigger": "elseif"
+    },
+    {
+        "content": "else {\n\t$0\n}",
+        "name": "else",
+        "scope": "vala",
+        "tabTrigger": "else"
+    },
+    {
+        "content": "enum {$1:EnumName} {\n\t$0\n}",
+        "name": "enum",
+        "scope": "vala",
+        "tabTrigger": "enum"
+    },
+    {
+        "content": "public errordomain ${1:Error} {\n\t$0\n}",
+        "name": "error domain",
+        "scope": "vala",
+        "tabTrigger": "errordomain"
+    },
+    {
+        "content": "for ($1;$2;$3) {\n\t$0\n}",
+        "name": "for",
+        "scope": "vala",
+        "tabTrigger": "for"
+    },
+    {
+        "content": "foreach ($1 in $2) {\n\t$0\n}",
+        "name": "foreach",
+        "scope": "vala",
+        "tabTrigger": "foreach"
+    },
+    {
+        "content": "Gee.ArrayList<${1:G}>($0);",
+        "name": "Gee.ArrayList",
+        "scope": "vala",
+        "tabTrigger": "ArrayList"
+    },
+    {
+        "content": "Gee.HashMap<${1:K},${2:V}>($0);",
+        "name": "Gee.HashMap",
+        "scope": "vala",
+        "tabTrigger": "HashMap"
+    },
+    {
+        "content": "Gee.HashSet<${1:G}>($0);",
+        "name": "Gee.HashSet",
+        "scope": "vala",
+        "tabTrigger": "HashSet"
+    },
+    {
+        "content": "if ($1) {\n\t$0\n}",
+        "name": "if",
+        "scope": "vala",
+        "tabTrigger": "if"
+    },
+    {
+        "content": "interface ${1:InterfaceName}{$2: : SuperInterface} {\n\t$0\n}",
+        "name": "interface",
+        "scope": "vala",
+        "tabTrigger": "interface"
+    },
+    {
+        "content": "public static int main(string [] argv) {\n\t${0}\n\treturn 0;\n}",
+        "name": "Main function",
+        "scope": "vala",
+        "tabTrigger": "main"
+    },
+    {
+        "content": "namespace $1 {\n\t$0\n}\n",
+        "name": "namespace (ns)",
+        "scope": "vala",
+        "tabTrigger": "ns"
+    },
+    {
+        "content": "stdout.printf($0);",
+        "name": "printf",
+        "scope": "vala",
+        "tabTrigger": "printf"
+    },
+    {
+        "content": "${1:public} ${2:Type} ${3:Name} {\n\tset {\n\t\t$0\n\t}\n\tget {\n\n\t}\n}",
+        "name": "property (prop)",
+        "scope": "vala",
+        "tabTrigger": "prop"
+    },
+    {
+        "content": "${1:public} ${2:Type} ${3:Name} {\n\tget {\n\t\t$0\n\t}\n}",
+        "name": "read-only property (roprop)",
+        "scope": "vala",
+        "tabTrigger": "roprop"
+    },
+    {
+        "content": "@\"${1:\\$var}\"",
+        "name": "String template (@)",
+        "scope": "vala",
+        "tabTrigger": "@"
+    },
+    {
+        "content": "struct ${1:StructName} {\n\t$0\n}",
+        "name": "struct",
+        "scope": "vala",
+        "tabTrigger": "struct"
+    },
+    {
+        "content": "switch ($1) {\n\t$0\n}",
+        "name": "switch",
+        "scope": "vala",
+        "tabTrigger": "switch"
+    },
+    {
+        "content": "try {\n\t$2\n} catch (${1:Error} e) {\n\t$0\n}",
+        "name": "try/catch",
+        "scope": "vala",
+        "tabTrigger": "try"
+    },
+    {
+        "content": "\"\"\"$0\"\"\";",
+        "name": "Verbatim string (\"\"\")",
+        "scope": "vala",
+        "tabTrigger": "verbatim"
+    },
+    {
+        "content": "while ($1) {\n\t$0\n}",
+        "name": "while",
+        "scope": "vala",
+        "tabTrigger": "while"
+    }
+];
+exports.scope = "";
+
+});

From bf9eec6b2365f82333dffa385a9105c3825d1921 Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Mon, 2 Jun 2014 23:26:59 +0100
Subject: [PATCH 17/58] fix "{$foo->bar}"."{$foo->bar($a)}"."{$foo->bar(&$a,
 $b)}";

---
 lib/ace/mode/php/php.js | 40 +++++++++++++++++++++++++++++-----------
 1 file changed, 29 insertions(+), 11 deletions(-)

diff --git a/lib/ace/mode/php/php.js b/lib/ace/mode/php/php.js
index ed23f641..74af47c2 100644
--- a/lib/ace/mode/php/php.js
+++ b/lib/ace/mode/php/php.js
@@ -717,34 +717,52 @@ PHP.Lexer = function( src, ini ) {
 
                             var re;
                             if ( curlyOpen > 0) {
-                                re = /^([^\\\$"{}\]\)]|\\.)+/g;
+                                re = /^([^\\\$"{}\]\)\->]|\\.)+/g;
                             } else {
                                 re = /^([^\\\$"{]|\\.|{[^\$]|\$(?=[^a-zA-Z_\x7f-\uffff]))+/g;;
                             }
 
+                            var type, match2;
                             while(( match = result.match( re )) !== null ) {
-
-
                                 if (result.length === 1) {
                                     throw new Error(match);
                                 }
+                                
+                                type = 0;
 
-
-
-                                results.push([
-                                    parseInt(( curlyOpen > 0 ) ? PHP.Constants.T_CONSTANT_ENCAPSED_STRING : PHP.Constants.T_ENCAPSED_AND_WHITESPACE, 10),
-                                    match[ 0 ].replace(/\n/g,"\\n").replace(/\r/g,""),
-                                    line
-                                    ]);
+                                if( curlyOpen > 0 ){
+                                    if( match2 = match[0].match(/^[\[\]\;\:\?\(\)\!\.\,\>\<\=\+\-\/\*\|\&\{\}\@\^\%\"\'\$\~]/) ){
+                                        results.push(match2[0]);
+                                    }else{                                    
+                                        type = PHP.Constants.T_STRING;    
+                                    }
+                                }else{
+                                    type = PHP.Constants.T_ENCAPSED_AND_WHITESPACE;
+                                }
+                                
+                                if( type ){
+                                    results.push([
+                                        parseInt(type, 10),
+                                        match[ 0 ].replace(/\n/g,"\\n").replace(/\r/g,""),
+                                        line
+                                        ]);
+                                }
 
                                 line += match[ 0 ].split('\n').length - 1;
 
                                 result = result.substring( match[ 0 ].length );
+                            }
 
+                            if( curlyOpen > 0 && result.match(/^\->/) !== null ) {
+                                results.push([
+                                    parseInt(PHP.Constants.T_OBJECT_OPERATOR, 10),
+                                    '->',
+                                    line
+                                    ]);
+                                result = result.substring( 2 );
                             }
 
                             if( result.match(/^{\$/) !== null ) {
-
                                 results.push([
                                     parseInt(PHP.Constants.T_CURLY_OPEN, 10),
                                     "{",

From 32e3ad5966e5f199a3b2019d02b7558ef98bfe4c Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Tue, 3 Jun 2014 00:41:47 +0100
Subject: [PATCH 18/58] fix "{$foo['bar']}"

---
 lib/ace/mode/php/php.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/ace/mode/php/php.js b/lib/ace/mode/php/php.js
index 74af47c2..e82a680a 100644
--- a/lib/ace/mode/php/php.js
+++ b/lib/ace/mode/php/php.js
@@ -731,7 +731,7 @@ PHP.Lexer = function( src, ini ) {
                                 type = 0;
 
                                 if( curlyOpen > 0 ){
-                                    if( match2 = match[0].match(/^[\[\]\;\:\?\(\)\!\.\,\>\<\=\+\-\/\*\|\&\{\}\@\^\%\"\'\$\~]/) ){
+                                    if( match2 = match[0].match(/^[\[\]\;\:\?\(\)\!\.\,\>\<\=\+\-\/\*\|\&\{\}\@\^\%\$\~]/) ){
                                         results.push(match2[0]);
                                     }else{                                    
                                         type = PHP.Constants.T_STRING;    

From 617cd3202ae29fa6e1c9b96d90e05c4da0561b0c Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Wed, 4 Jun 2014 10:39:50 +0100
Subject: [PATCH 19/58] fix "{$this->foo->bar('foobar')}"

---
 lib/ace/mode/php/php.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/ace/mode/php/php.js b/lib/ace/mode/php/php.js
index e82a680a..fd703ae2 100644
--- a/lib/ace/mode/php/php.js
+++ b/lib/ace/mode/php/php.js
@@ -717,7 +717,7 @@ PHP.Lexer = function( src, ini ) {
 
                             var re;
                             if ( curlyOpen > 0) {
-                                re = /^([^\\\$"{}\]\)\->]|\\.)+/g;
+                                re = /^([^\\\$"{}\]\(\)\->]|\\.)+/g;
                             } else {
                                 re = /^([^\\\$"{]|\\.|{[^\$]|\$(?=[^a-zA-Z_\x7f-\uffff]))+/g;;
                             }

From 84abfa5ff89ded60a99c8d9809291115c639fd16 Mon Sep 17 00:00:00 2001
From: hitode909 
Date: Thu, 5 Jun 2014 14:10:25 +0900
Subject: [PATCH 20/58] window.controllers was renamed to window.Controllers in
 Firefox 29

useragent.isGecko was false on Firefox 29
---
 lib/ace/lib/useragent.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/ace/lib/useragent.js b/lib/ace/lib/useragent.js
index e539dae3..0d1d9f4d 100644
--- a/lib/ace/lib/useragent.js
+++ b/lib/ace/lib/useragent.js
@@ -82,7 +82,7 @@ exports.isIE =
 exports.isOldIE = exports.isIE && exports.isIE < 9;
 
 // Is this Firefox or related?
-exports.isGecko = exports.isMozilla = window.controllers && window.navigator.product === "Gecko";
+exports.isGecko = exports.isMozilla = (window.Controllers || window.controllers) && window.navigator.product === "Gecko";
 
 // oldGecko == rev < 2.0 
 exports.isOldGecko = exports.isGecko && parseInt((ua.match(/rv\:(\d+)/)||[])[1], 10) < 4;

From 07fb7738db3d581e086fa01d8f6ed7697e181418 Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Thu, 5 Jun 2014 19:56:46 +0100
Subject: [PATCH 21/58] Highlight matching HTML tag

fixes #1308
---
 lib/ace/editor.js | 78 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index ef2d9f1a..57c6adb2 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -48,6 +48,7 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
 var CommandManager = require("./commands/command_manager").CommandManager;
 var defaultCommands = require("./commands/default_commands").commands;
 var config = require("./config");
+var TokenIterator = require("./token_iterator").TokenIterator;
 
 /**
  * The main entry point into the Ace functionality.
@@ -534,6 +535,83 @@ var Editor = function(renderer, session) {
                 self.session.$bracketHighlight = self.session.addMarker(range, "ace_bracket", "text");
         }, 50);
     };
+    
+    this.$highlightTags = function() {
+        var session = this.session;
+
+        if (session.$tagHighlight) {
+            session.removeMarker(session.$tagHighlight);
+            session.$tagHighlight = null;
+        }
+
+        if (this.$highlightTagPending) {
+            return;
+        }
+
+        // perform highlight async to not block the browser during navigation
+        var self = this;
+        this.$highlightTagPending = true;
+        setTimeout(function() {
+            self.$highlightTagPending = false;
+            
+            var pos = editor.getCursorPosition();
+            var iterator = new TokenIterator(self.session, pos.row, pos.column);
+            var token = iterator.getCurrentToken();
+            
+            if( !token || token.type.indexOf('tag-name') === -1 ){
+                return;
+            }
+
+            var tag = token.value;
+            var depth = 0;
+            var prevToken = iterator.stepBackward();
+            
+            if (prevToken.value == '<'){
+                //find closing tag
+                do {
+                    prevToken = token;
+                    token = iterator.stepForward();
+                    
+                    if (token && token.value === tag && token.type.indexOf('tag-name') !== -1) {
+                        if (prevToken.value==='<'){
+                            depth++;
+                        } else if (prevToken.value==='=0);
+            }else{
+                //find opening tag
+                do {
+                    token = prevToken;
+                    prevToken = iterator.stepBackward();
+                    
+                    if(token && token.value === tag && token.type.indexOf('tag-name') !== -1) {
+                        if (prevToken.value==='<') {
+                            depth++;
+                        } else if( prevToken.value==='
Date: Fri, 6 Jun 2014 10:32:18 +0100
Subject: [PATCH 22/58] Jump To Matching Tag

Alt-P: Jump to Matching Tag
Alt-Shift-P: Select to Matching Tag
fixes #1158
---
 lib/ace/commands/default_commands.js | 12 ++++
 lib/ace/editor.js                    | 95 ++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)

diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js
index 77cdfc61..e7cb56ad 100644
--- a/lib/ace/commands/default_commands.js
+++ b/lib/ace/commands/default_commands.js
@@ -402,6 +402,18 @@ exports.commands = [{
     exec: function(editor) { editor.jumpToMatching(true); },
     multiSelectAction: "forEach",
     readOnly: true
+}, {
+    name: "jumptomatchingtag",
+    bindKey: bindKey("Alt-P", "Alt-Shift-P"),
+    exec: function(editor) { editor.jumpToMatchingTag(); },
+    multiSelectAction: "forEach",
+    readOnly: true
+}, {
+    name: "selecttomatchingtag",
+    bindKey: bindKey("Alt-Shift-P", null),
+    exec: function(editor) { editor.jumpToMatchingTag(true); },
+    multiSelectAction: "forEach",
+    readOnly: true
 }, 
 
 // commands disabled in readOnly mode
diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index ef2d9f1a..da77e185 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -48,6 +48,7 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
 var CommandManager = require("./commands/command_manager").CommandManager;
 var defaultCommands = require("./commands/default_commands").commands;
 var config = require("./config");
+var TokenIterator = require("./token_iterator").TokenIterator;
 
 /**
  * The main entry point into the Ace functionality.
@@ -1948,6 +1949,100 @@ var Editor = function(renderer, session) {
         }
     };
 
+    /**
+     * Moves the cursor's row and column to the next matching tag.
+     *
+     **/
+    this.jumpToMatchingTag = function(select) {
+        var tag;
+        var depth = {};
+        var sel = {};
+        var cursor = this.getCursorPosition();
+        var iterator = new TokenIterator(this.session, cursor.row, cursor.column);
+        var prevToken = iterator.getCurrentToken();
+        var token = iterator.stepForward();
+
+        //get next closing tag
+        if (!token || token.type.indexOf('tag-name') === -1) {
+            do {
+                prevToken = token;
+                token = iterator.stepForward();
+                
+                if(token && token.type.indexOf('tag-name') !== -1) {
+                    if (isNaN(depth[token.value])) {
+                        depth[token.value] = 0;
+                    }
+                    
+                    if (prevToken.value==='<' ){
+                        depth[token.value]++;
+                    } else if ( prevToken.value==='=0));
+    	}
+
+		if (token && token.type.indexOf('tag-name') !== -1) {
+			sel.end = {
+				column: iterator.getCurrentTokenColumn()-2,
+				row: iterator.getCurrentTokenRow() 
+			};
+			tag = token.value;
+		}
+		
+		//no tag found
+		if (!tag) {
+			return;
+		}
+
+		//find matching tag
+		do {
+	        token = prevToken;
+            prevToken = iterator.stepBackward();
+            
+            if( prevToken ){
+                if (prevToken.type.indexOf('tag-close') !== -1) {
+                    sel.start = {
+                        column: iterator.getCurrentTokenColumn()+1,
+                        row: iterator.getCurrentTokenRow() 
+                    };
+                }
+    
+                if (token.value===tag && token.type.indexOf('tag-name') !== -1 && prevToken.value==='<') {
+                    depth[tag]++;
+                } else if ( token.value===tag && token.type.indexOf('tag-name') !== -1 && prevToken.value==='
Date: Fri, 6 Jun 2014 10:53:55 +0100
Subject: [PATCH 23/58] matching tag fixes

remove global editor ref, add listener
---
 lib/ace/editor.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index 57c6adb2..643bc217 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -554,7 +554,7 @@ var Editor = function(renderer, session) {
         setTimeout(function() {
             self.$highlightTagPending = false;
             
-            var pos = editor.getCursorPosition();
+            var pos = self.getCursorPosition();
             var iterator = new TokenIterator(self.session, pos.row, pos.column);
             var token = iterator.getCurrentToken();
             
@@ -729,6 +729,7 @@ var Editor = function(renderer, session) {
         }
 
         this.$highlightBrackets();
+        this.$highlightTags();
         this.$updateHighlightActiveLine();
         this._signal("changeSelection");
     };

From 111ec1899fc98cf841cc6e0d879bca46cbe1af59 Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Fri, 6 Jun 2014 13:32:16 +0100
Subject: [PATCH 24/58] fix tag flicker

fix tag highlight flicker when selecting
---
 lib/ace/editor.js | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index 643bc217..f77c5aa3 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -539,11 +539,6 @@ var Editor = function(renderer, session) {
     this.$highlightTags = function() {
         var session = this.session;
 
-        if (session.$tagHighlight) {
-            session.removeMarker(session.$tagHighlight);
-            session.$tagHighlight = null;
-        }
-
         if (this.$highlightTagPending) {
             return;
         }
@@ -558,7 +553,9 @@ var Editor = function(renderer, session) {
             var iterator = new TokenIterator(self.session, pos.row, pos.column);
             var token = iterator.getCurrentToken();
             
-            if( !token || token.type.indexOf('tag-name') === -1 ){
+            if (!token || token.type.indexOf('tag-name') === -1) {
+                session.removeMarker(session.$tagHighlight);
+                session.$tagHighlight = null;
                 return;
             }
 
@@ -601,15 +598,23 @@ var Editor = function(renderer, session) {
             }
             
             if (!token) {
+                session.removeMarker(session.$tagHighlight);
+                session.$tagHighlight = null;
                 return;
             }
-
+            
             var row = iterator.getCurrentTokenRow();
             var column = iterator.getCurrentTokenColumn();
             var range = new Range(row, column, row, column+token.value.length);
-
-            if (range)
-                self.session.$tagHighlight = self.session.addMarker(range, "ace_bracket", "text");
+            
+            //remove range if different
+            if (session.$tagHighlight && range.compareRange(session.$backMarkers[session.$tagHighlight].range)!==0) {
+                session.removeMarker(session.$tagHighlight);
+                session.$tagHighlight = null;
+            }
+            
+            if (range && !session.$tagHighlight)
+                session.$tagHighlight = session.addMarker(range, "ace_bracket", "text");
         }, 50);
     };
 

From c0343a2cbaef639200b479997f6805ba55cc63aa Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Fri, 6 Jun 2014 13:50:29 +0100
Subject: [PATCH 25/58] use setCursorStyle instead of modifying classes

---
 lib/ace/css/editor.css         | 4 ----
 lib/ace/mouse/mouse_handler.js | 7 ++++---
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css
index faa678cc..a2b7475e 100644
--- a/lib/ace/css/editor.css
+++ b/lib/ace/css/editor.css
@@ -50,10 +50,6 @@
     cursor: text !important;
 }
 
-.ace_selection .ace_content {
-    cursor: default;
-}
-
 .ace_gutter {
     position: absolute;
     overflow : hidden;
diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js
index 6a91b172..f755270c 100644
--- a/lib/ace/mouse/mouse_handler.js
+++ b/lib/ace/mouse/mouse_handler.js
@@ -82,11 +82,12 @@ var MouseHandler = function(editor) {
         
         var char = editor.renderer.screenToTextCoordinates(e.x, e.y);
         var range = editor.session.selection.getRange();
-
+		var renderer = editor.renderer;
+		
         if (!range.isEmpty() && range.insideStart(char.row, char.column)) {
-            editor.setStyle("ace_selection");
+            renderer.setCursorStyle("default");
         } else {
-            editor.unsetStyle("ace_selection");
+            renderer.setCursorStyle("");
         }
     });
 };

From 636cc6a96f9ca20fd678a3830a3aa43fc27283a5 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Sat, 7 Jun 2014 15:10:36 +0400
Subject: [PATCH 26/58] do not change mouse over selection if dragging isn't
 enabled

---
 lib/ace/mouse/mouse_handler.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js
index f755270c..2da72d31 100644
--- a/lib/ace/mouse/mouse_handler.js
+++ b/lib/ace/mouse/mouse_handler.js
@@ -76,14 +76,14 @@ var MouseHandler = function(editor) {
         return event.preventDefault(e);
     });
 
-    editor.on('mousemove', function(e){
-        if (_self.state)
+    editor.on("mousemove", function(e){
+        if (_self.state || _self.$dragDelay || !_self.$dragEnabled)
             return;
         
         var char = editor.renderer.screenToTextCoordinates(e.x, e.y);
         var range = editor.session.selection.getRange();
-		var renderer = editor.renderer;
-		
+        var renderer = editor.renderer;
+
         if (!range.isEmpty() && range.insideStart(char.row, char.column)) {
             renderer.setCursorStyle("default");
         } else {

From efcbe1c0103d407602e586e2d9e4e89ec671926d Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Sat, 7 Jun 2014 15:26:07 +0400
Subject: [PATCH 27/58] add todo reminder

---
 lib/ace/editor.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index f77c5aa3..86630d9e 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -535,7 +535,8 @@ var Editor = function(renderer, session) {
                 self.session.$bracketHighlight = self.session.addMarker(range, "ace_bracket", "text");
         }, 50);
     };
-    
+
+    // todo: move to mode.getMatching
     this.$highlightTags = function() {
         var session = this.session;
 

From 76adcc51a0db435d03bd16c013e4a201d52925d2 Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Mon, 9 Jun 2014 14:12:10 +0100
Subject: [PATCH 28/58] Revert "Jump To Matching Tag"

This reverts commit 9a4a387de65d035a49f1398befc3c8541d1c02c3.
---
 lib/ace/commands/default_commands.js | 12 ----
 lib/ace/editor.js                    | 95 ----------------------------
 2 files changed, 107 deletions(-)

diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js
index e7cb56ad..77cdfc61 100644
--- a/lib/ace/commands/default_commands.js
+++ b/lib/ace/commands/default_commands.js
@@ -402,18 +402,6 @@ exports.commands = [{
     exec: function(editor) { editor.jumpToMatching(true); },
     multiSelectAction: "forEach",
     readOnly: true
-}, {
-    name: "jumptomatchingtag",
-    bindKey: bindKey("Alt-P", "Alt-Shift-P"),
-    exec: function(editor) { editor.jumpToMatchingTag(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selecttomatchingtag",
-    bindKey: bindKey("Alt-Shift-P", null),
-    exec: function(editor) { editor.jumpToMatchingTag(true); },
-    multiSelectAction: "forEach",
-    readOnly: true
 }, 
 
 // commands disabled in readOnly mode
diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index da77e185..ef2d9f1a 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -48,7 +48,6 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
 var CommandManager = require("./commands/command_manager").CommandManager;
 var defaultCommands = require("./commands/default_commands").commands;
 var config = require("./config");
-var TokenIterator = require("./token_iterator").TokenIterator;
 
 /**
  * The main entry point into the Ace functionality.
@@ -1949,100 +1948,6 @@ var Editor = function(renderer, session) {
         }
     };
 
-    /**
-     * Moves the cursor's row and column to the next matching tag.
-     *
-     **/
-    this.jumpToMatchingTag = function(select) {
-        var tag;
-        var depth = {};
-        var sel = {};
-        var cursor = this.getCursorPosition();
-        var iterator = new TokenIterator(this.session, cursor.row, cursor.column);
-        var prevToken = iterator.getCurrentToken();
-        var token = iterator.stepForward();
-
-        //get next closing tag
-        if (!token || token.type.indexOf('tag-name') === -1) {
-            do {
-                prevToken = token;
-                token = iterator.stepForward();
-                
-                if(token && token.type.indexOf('tag-name') !== -1) {
-                    if (isNaN(depth[token.value])) {
-                        depth[token.value] = 0;
-                    }
-                    
-                    if (prevToken.value==='<' ){
-                        depth[token.value]++;
-                    } else if ( prevToken.value==='=0));
-    	}
-
-		if (token && token.type.indexOf('tag-name') !== -1) {
-			sel.end = {
-				column: iterator.getCurrentTokenColumn()-2,
-				row: iterator.getCurrentTokenRow() 
-			};
-			tag = token.value;
-		}
-		
-		//no tag found
-		if (!tag) {
-			return;
-		}
-
-		//find matching tag
-		do {
-	        token = prevToken;
-            prevToken = iterator.stepBackward();
-            
-            if( prevToken ){
-                if (prevToken.type.indexOf('tag-close') !== -1) {
-                    sel.start = {
-                        column: iterator.getCurrentTokenColumn()+1,
-                        row: iterator.getCurrentTokenRow() 
-                    };
-                }
-    
-                if (token.value===tag && token.type.indexOf('tag-name') !== -1 && prevToken.value==='<') {
-                    depth[tag]++;
-                } else if ( token.value===tag && token.type.indexOf('tag-name') !== -1 && prevToken.value==='
Date: Mon, 9 Jun 2014 17:09:31 +0100
Subject: [PATCH 29/58] add html tag support to jumpToMatching

---
 lib/ace/editor.js | 153 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 140 insertions(+), 13 deletions(-)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index ef2d9f1a..f57bdc70 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -48,6 +48,7 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
 var CommandManager = require("./commands/command_manager").CommandManager;
 var defaultCommands = require("./commands/default_commands").commands;
 var config = require("./config");
+var TokenIterator = require("./token_iterator").TokenIterator;
 
 /**
  * The main entry point into the Ace functionality.
@@ -1915,26 +1916,152 @@ var Editor = function(renderer, session) {
     };
 
     /**
-     * Moves the cursor's row and column to the next matching bracket.
+     * Moves the cursor's row and column to the next matching bracket or HTML tag.
      *
      **/
     this.jumpToMatching = function(select) {
         var cursor = this.getCursorPosition();
+        var iterator = new TokenIterator(this.session, cursor.row, cursor.column);
+        var prevToken = iterator.getCurrentToken();
+        var token;
 
-        var range = this.session.getBracketRange(cursor);
-        if (!range) {
-            range = this.find({
-                needle: /[{}()\[\]]/g,
-                preventScroll:true,
-                start: {row: cursor.row, column: cursor.column - 1}
-            });
-            if (!range)
-                return;
-            var pos = range.start;
-            if (pos.row == cursor.row && Math.abs(pos.column - cursor.column) < 2)
-                range = this.session.getBracketRange(pos);
+        //handle bracket as first char on line
+        if (prevToken && prevToken.index===0)
+            token = prevToken;
+        else
+            token = iterator.stepForward();
+
+        if (!token)
+            return;
+            
+        //get next closing tag or bracket
+        var matchType;
+        var found = false;
+        var inverse;
+        var depth = {};
+        
+        do {
+            if (token.value.match(/[{}()\[\]]/g)) {
+                for (var i=0; i
Date: Mon, 9 Jun 2014 23:44:27 +0400
Subject: [PATCH 30/58] fix tests

---
 lib/ace/mouse/default_handlers.js   | 1 +
 lib/ace/mouse/mouse_handler_test.js | 8 +++++---
 lib/ace/test/all_browser.js         | 1 +
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js
index 7bb701a8..e7a31540 100644
--- a/lib/ace/mouse/default_handlers.js
+++ b/lib/ace/mouse/default_handlers.js
@@ -116,6 +116,7 @@ function DefaultHandlers(mouseHandler) {
             else if (!this.$clickSelection) {
                 editor.selection.moveToPosition(pos);
             }
+            this.select();
         }.bind(this), 0);
         if (editor.renderer.scroller.setCapture) {
             editor.renderer.scroller.setCapture();
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/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",

From e92b709eb247344e4133bc143bfbdef9dbc6d7cd Mon Sep 17 00:00:00 2001
From: Adam Jimenez 
Date: Mon, 9 Jun 2014 21:30:19 +0100
Subject: [PATCH 31/58] fix jumping to brackets in strings

---
 lib/ace/editor.js | 87 +++++++++++++++++++++++++----------------------
 1 file changed, 47 insertions(+), 40 deletions(-)

diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index f57bdc70..a60e542f 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -1923,48 +1923,54 @@ var Editor = function(renderer, session) {
         var cursor = this.getCursorPosition();
         var iterator = new TokenIterator(this.session, cursor.row, cursor.column);
         var prevToken = iterator.getCurrentToken();
-        var token;
+        var token = prevToken;
 
-        //handle bracket as first char on line
-        if (prevToken && prevToken.index===0)
-            token = prevToken;
-        else
+        if (!token)
             token = iterator.stepForward();
 
         if (!token)
             return;
-            
+
         //get next closing tag or bracket
         var matchType;
         var found = false;
-        var inverse;
         var depth = {};
+        var i = cursor.column - token.start;
+        var bracketType;
+        var brackets = {
+            ")": "(",
+            "(": "(",
+            "]": "[",
+            "[": "[",
+            "{": "{",
+            "}": "{"
+        };
         
         do {
             if (token.value.match(/[{}()\[\]]/g)) {
-                for (var i=0; i
Date: Fri, 13 Jun 2014 01:58:05 +0200
Subject: [PATCH 32/58] Use current document to process ')}
-                    scripts.forEach(function(s) {
-                        s = s.replace(/"/g, "");
-                        if (s == "ace/ace") {
-                            comment("load ace")
-                            script("ace")
-                        } else {
-                            var extName = s.match(/[^/]*$/)[0];
-                            comment("load ace " + extName + " extension");
-                            script("ext-" + extName);
-                        }
-                    });
-                    result.push("')}
+                scripts.forEach(function(s) {
+                    s = s.replace(/"/g, "");
+                    if (s == "ace/ace") {
+                        comment("load ace");
+                        script("ace");
+                    } else {
+                        var extName = s.match(/[^/]*$/)[0];
+                        comment("load ace " + extName + " extension");
+                        script("ext-" + extName);
+                    }
+                });
+                result.push("
-
 
 
 
diff --git a/package.json b/package.json
index cbabb3a0..58aabdf9 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
         "asyncjs": "0.0.x",
         "jsdom": "0.2.x",
         "amd-loader": "~0.0.4",
-        "dryice": "0.4.10"
+        "dryice": "0.4.11"
     },
     "mappings": {
         "ace": "."

From b42108a2c993b684036620e606201f6989ff91b4 Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Fri, 20 Jun 2014 10:37:23 +0400
Subject: [PATCH 38/58] remove unnecessary use of async

---
 Makefile.dryice.js | 104 ++++++++++++++++++++++-----------------------
 1 file changed, 51 insertions(+), 53 deletions(-)

diff --git a/Makefile.dryice.js b/Makefile.dryice.js
index 8bf54562..5f5d69bc 100755
--- a/Makefile.dryice.js
+++ b/Makefile.dryice.js
@@ -33,7 +33,6 @@ var fs = require("fs");
 var path = require("path");
 var copy = require('architect-build/copy');
 var build = require('architect-build/build');
-var async = require("asyncjs");
 
 var ACE_HOME = __dirname;
 var BUILD_DIR = ACE_HOME + "/build";
@@ -346,58 +345,57 @@ function buildSubmodule(options, extra, file, callback) {
 function buildAce(options) {
     var snippetFiles = jsFileList("lib/ace/snippets");
     var modeNames = modeList();
-    async.forEach([
-        buildCore.bind(null, options, {outputFile: "ace.js"}),
-        // modes
-        async.forEach.bind(null, modeNames, function(name, next) {
-            buildSubmodule(options, {
-                projectType: "mode",
-                require: ["ace/mode/" + name]
-            }, "mode-" + name, next);
-        }),
-        // snippets
-        async.forEach.bind(null, modeNames, function(name, next) {
-            if (snippetFiles.indexOf(name + ".js") == -1)
-                addSnippetFile(name);
-            
-            buildSubmodule(options, {
-                require: ["ace/snippets/" + name],
-            }, "snippets/" + name, next);
-        }),
-        // themes
-        async.forEach.bind(null, jsFileList("lib/ace/theme"), function(name, next) {
-            buildSubmodule(options, {
-                projectType: "theme",
-                require: ["ace/theme/" + name]
-            }, "theme-" +  name.replace("_theme", ""), next);
-        }),
-        // keybindings
-        async.forEach.bind(null, ["vim", "emacs"], function(name, next) {
-            buildSubmodule(options, {
-                projectType: "keybinding",
-                require: ["ace/keyboard/" + name ]
-            }, "keybinding-" + name, next);
-        }),
-        // extensions
-        async.forEach.bind(null, jsFileList("lib/ace/ext"), function(name, next) {
-            buildSubmodule(options, {
-                projectType: "ext",
-                require: ["ace/ext/" + name]
-            }, "ext-" + name, next);
-        }),
-        // workers
-        async.forEach.bind(null, workers("lib/ace/mode"), function(name, next) {
-            buildSubmodule(options, {
-                projectType: "worker",
-                require: ["ace/mode/" + name + "_worker"],
-                additional: [{
-                    id: "ace/worker/worker",
-                    loaderModule: true,
-                    order: -1000
-                }],
-            }, "worker-" + name, next);
-        }),
-    ], function(f, next) { f(next); });
+
+    buildCore(options, {outputFile: "ace.js"}),
+    // modes
+    modeNames.forEach(function(name) {
+        buildSubmodule(options, {
+            projectType: "mode",
+            require: ["ace/mode/" + name]
+        }, "mode-" + name);
+    });
+    // snippets
+    modeNames.forEach(function(name) {
+        if (snippetFiles.indexOf(name + ".js") == -1)
+            addSnippetFile(name);
+        
+        buildSubmodule(options, {
+            require: ["ace/snippets/" + name],
+        }, "snippets/" + name);
+    });
+    // themes
+    jsFileList("lib/ace/theme").forEach(function(name) {
+        buildSubmodule(options, {
+            projectType: "theme",
+            require: ["ace/theme/" + name]
+        }, "theme-" +  name.replace("_theme", ""));
+    });
+    // keybindings
+    ["vim", "emacs"].forEach(function(name) {
+        buildSubmodule(options, {
+            projectType: "keybinding",
+            require: ["ace/keyboard/" + name ]
+        }, "keybinding-" + name);
+    });
+    // extensions
+    jsFileList("lib/ace/ext").forEach(function(name) {
+        buildSubmodule(options, {
+            projectType: "ext",
+            require: ["ace/ext/" + name]
+        }, "ext-" + name);
+    });
+    // workers
+    workers("lib/ace/mode").forEach(function(name) {
+        buildSubmodule(options, {
+            projectType: "worker",
+            require: ["ace/mode/" + name + "_worker"],
+            additional: [{
+                id: "ace/worker/worker",
+                loaderModule: true,
+                order: -1000
+            }],
+        }, "worker-" + name);
+    });
 }
 
 function getLoadedFileList(options, callback, result) {

From e52884761d447c85e6a597732abfcfae4874e00a Mon Sep 17 00:00:00 2001
From: nightwing 
Date: Fri, 20 Jun 2014 10:38:05 +0400
Subject: [PATCH 39/58] keep demo/kitchein-sink folders location in the build

---
 Makefile.dryice.js | 15 +++++----------
 kitchen-sink.html  | 10 ++--------
 2 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/Makefile.dryice.js b/Makefile.dryice.js
index 5f5d69bc..a0982577 100755
--- a/Makefile.dryice.js
+++ b/Makefile.dryice.js
@@ -132,25 +132,20 @@ function demo() {
         );
     }
 
-    function fixDocPaths(data) {
-        return data.replace(/"(demo|build)\//g, "\"");
-    }
-
-    copy(ACE_HOME + "/demo/kitchen-sink", BUILD_DIR + "/kitchen-sink", {
+    copy(ACE_HOME + "/demo/kitchen-sink", BUILD_DIR + "/demo/kitchen-sink", {
         shallow: true,
-        replace: [changeComments, fixDocPaths],
+        replace: [changeComments],
         include: /\.(css|html)$/
     });
     
-    copy(ACE_HOME +"/demo/kitchen-sink/docs/", BUILD_DIR + "/kitchen-sink/docs/");
+    copy(ACE_HOME +"/demo/kitchen-sink/docs/", BUILD_DIR + "/demo/kitchen-sink/docs/");
     
-    copy.file(ACE_HOME + "/demo/kitchen-sink/logo.png", BUILD_DIR + "/kitchen-sink/logo.png");
+    copy.file(ACE_HOME + "/demo/kitchen-sink/logo.png", BUILD_DIR + "/demo/kitchen-sink/logo.png");
 
     buildSubmodule({}, {
         require: ["kitchen-sink/demo"],
-        filters: [fixDocPaths],
         projectType: "demo"
-    }, BUILD_DIR + "/kitchen-sink/demo");
+    }, BUILD_DIR + "/demo/kitchen-sink/demo");
 
     copy(ACE_HOME + "/demo/", BUILD_DIR + "/demo/", {
         shallow: true,
diff --git a/kitchen-sink.html b/kitchen-sink.html
index f55cfa14..d32b98b5 100644
--- a/kitchen-sink.html
+++ b/kitchen-sink.html
@@ -11,16 +11,10 @@
       commit  %commit%
     -->
 
-    
-      
-      
-    
+    
+    
 
     
-    
 
 
 
From e5b2eed5c58425c3ae3db35da9328320a57582f2 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 20 Jun 2014 20:54:08 +0400 Subject: [PATCH 40/58] add architect build to package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 58aabdf9..31a4dec9 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "asyncjs": "0.0.x", "jsdom": "0.2.x", "amd-loader": "~0.0.4", - "dryice": "0.4.11" + "dryice": "0.4.11", + "architect-build": "https://github.com/c9/architect-build/tarball/42723e152bb" }, "mappings": { "ace": "." From 67143599981cadec7b9898d0fdd028f73ff8d7ba Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 21 Jun 2014 00:45:50 +0400 Subject: [PATCH 41/58] tweak --- Makefile | 1 - Makefile.dryice.js | 61 +++++++++++++++++----------------------------- kitchen-sink.html | 2 +- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 95dcf964..29cf0495 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ pre_build: build: pre_build ./Makefile.dryice.js normal ./Makefile.dryice.js demo - ./Makefile.dryice.js bm # Minimal build: call Makefile.dryice.js only if our sources changed basic: build/src/ace.js diff --git a/Makefile.dryice.js b/Makefile.dryice.js index a0982577..2bc36f8b 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -112,14 +112,11 @@ function ace() { function demo() { console.log('# kitchen sink ---------'); - var version, ref; + var version = "", ref = ""; try { version = JSON.parse(fs.readFileSync(ACE_HOME + "/package.json")).version; ref = fs.readFileSync(ACE_HOME + "/.git-ref").toString(); - } catch(e) { - ref = ""; - version = ""; - } + } catch(e) {} function changeComments(data) { return (data @@ -131,16 +128,12 @@ function demo() { .replace("%commit%", ref) ); } - - copy(ACE_HOME + "/demo/kitchen-sink", BUILD_DIR + "/demo/kitchen-sink", { - shallow: true, - replace: [changeComments], - include: /\.(css|html)$/ - }); copy(ACE_HOME +"/demo/kitchen-sink/docs/", BUILD_DIR + "/demo/kitchen-sink/docs/"); copy.file(ACE_HOME + "/demo/kitchen-sink/logo.png", BUILD_DIR + "/demo/kitchen-sink/logo.png"); + copy.file(ACE_HOME + "/demo/kitchen-sink/styles.css", BUILD_DIR + "/demo/kitchen-sink/styles.css"); + copy.file(ACE_HOME + "/kitchen-sink.html", BUILD_DIR + "/kitchen-sink.html", changeComments); buildSubmodule({}, { require: ["kitchen-sink/demo"], @@ -214,9 +207,9 @@ function buildAceModule(opts, callback) { buildAceModule.dequeue = function() { if (buildAceModule.running) return; var call = buildAceModule.queue.shift(); + buildAceModule.running = call; if (call) buildAceModuleInternal.apply(null, call); - buildAceModule.running = call; }; } @@ -235,7 +228,7 @@ function buildAceModule(opts, callback) { function buildAceModuleInternal(opts, callback) { var cache = opts.cache == undefined ? CACHE : opts.cache; - var key = opts.require + "|" + opts.compress + "|" + opts.projectType; + var key = opts.require + "|" + opts.projectType; if (cache && cache.configs && cache.configs[key]) return write(null, cache.configs[key]); @@ -249,19 +242,23 @@ function buildAceModuleInternal(opts, callback) { }; function write(err, result) { - if (opts.compress) - result.code = compress(result.code); - - if (cache && key) { + if (cache && key && !(cache.configs && cache.configs[key])) { cache.configs = cache.configs || Object.create(null); cache.configs[key] = result; result.sources = result.sources.map(function(pkg) { return {deps: pkg.deps}; }); - } + } if (!opts.outputFile) return callback(err, result); + + var code = result.code; + if (opts.compress) { + if (!result.codeMin) + result.codeMin = compress(result.code); + code = result.codeMin; + } var targetDir = BUILD_DIR + "/src"; if (opts.compress) @@ -287,7 +284,6 @@ function buildAceModuleInternal(opts, callback) { opts.noconflict ? ns : "", projectType == "ext")); } - var code = result.code; filters.forEach(function(f) { code = f(code); }); build.writeToFile({code: code}, { @@ -331,7 +327,7 @@ function buildSubmodule(options, extra, file, callback) { options = extend(extra, options); getLoadedFileList(options, function(coreFiles) { options.outputFile = file + ".js"; - options.ignore = coreFiles; + options.ignore = options.ignore || coreFiles; options.quiet = true; buildAceModule(options, callback); }); @@ -384,9 +380,10 @@ function buildAce(options) { buildSubmodule(options, { projectType: "worker", require: ["ace/mode/" + name + "_worker"], + ignore: [], additional: [{ id: "ace/worker/worker", - loaderModule: true, + transforms: [], order: -1000 }], }, "worker-" + name); @@ -467,9 +464,7 @@ function namespace(ns) { return function(text) { text = text .toString() - .replace('var ACE_NAMESPACE = "";', 'var ACE_NAMESPACE = "' + ns +'";') - // .replace(/(\.define)|([,;\n]|^\s*?)define\(/g, function(_, a, b) { - // return a || b + ns + ".define("; + .replace(/ACE_NAMESPACE\s*=\s*""/, 'ACE_NAMESPACE = "' + ns +'"') .replace(/(\.define)|\bdefine\(/g, function(_, a) { return a || ns + ".define("; }); @@ -551,20 +546,10 @@ function addSnippetFile(modeName) { } function compress(text) { - return asciify(require("dryice").copy.filter.uglifyjs(text)); - // copy.filter.uglifyjs.options.ascii_only = true; doesn't work with some uglify.js versions - function asciify(text) { - return text.replace(/[\x00-\x08\x0b\x0c\x0e\x19\x80-\uffff]/g, function(c) { - c = c.charCodeAt(0).toString(16); - if (c.length == 1) - return "\\x0" + c; - if (c.length == 2) - return "\\x" + c; - if (c.length == 3) - return "\\u0" + c; - return "\\u" + c; - }); - } + var ujs = require("dryice").copy.filter.uglifyjs; + ujs.options.mangle_toplevel = {except: ["ACE_NAMESPACE", "requirejs"]}; + ujs.options.beautify = {ascii_only: true, inline_script: true} + return ujs(text); } function extend(base, extra) { diff --git a/kitchen-sink.html b/kitchen-sink.html index d32b98b5..65fe2a2a 100644 --- a/kitchen-sink.html +++ b/kitchen-sink.html @@ -281,7 +281,7 @@ - + From 739e86cde7eacd303246afe5a6d80ab49f7d5375 Mon Sep 17 00:00:00 2001 From: rmsmith Date: Sat, 21 Jun 2014 19:15:52 +0300 Subject: [PATCH 42/58] mode/applescript : set the `$id` to the correct value This brings the new applescript mode into alignment with the other modes per #1778 --- lib/ace/mode/applescript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ace/mode/applescript.js b/lib/ace/mode/applescript.js index 121b73eb..81bc3533 100644 --- a/lib/ace/mode/applescript.js +++ b/lib/ace/mode/applescript.js @@ -47,6 +47,7 @@ oop.inherits(Mode, TextMode); (function() { this.lineCommentStart = "--"; this.blockComment = {start: "(*", end: "*)"}; + this.$id = "ace/mode/applescript"; // Extra logic goes here. }).call(Mode.prototype); From 6aaede1a2e15ed246bc75f76e5d74fb90e1c74ce Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 22 Jun 2014 23:33:45 +0400 Subject: [PATCH 43/58] keep mac line movement keybindings (still overridden by jumptomatching) --- lib/ace/commands/default_commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 9d1720e3..b1c0b5f0 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -208,7 +208,7 @@ exports.commands = [{ readOnly: true }, { name: "golineup", - bindKey: bindKey("Up", "Up"), + bindKey: bindKey("Up", "Up|Ctrl-P"), exec: function(editor, args) { editor.navigateUp(args.times); }, multiSelectAction: "forEach", readOnly: true @@ -237,7 +237,7 @@ exports.commands = [{ readOnly: true }, { name: "golinedown", - bindKey: bindKey("Down", "Down"), + bindKey: bindKey("Down", "Down|Ctrl-N"), exec: function(editor, args) { editor.navigateDown(args.times); }, multiSelectAction: "forEach", scrollIntoView: "cursor", From 3ebc2828e608c9c0847d5fa45b9ed4a37a108f38 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 10 Jun 2014 22:11:55 +0400 Subject: [PATCH 44/58] add textarea keybinding (probably fixes #36) --- lib/ace/commands/default_commands.js | 8 +- .../ext/menu_tools/add_editor_menu_options.js | 46 ++++++----- lib/ace/keyboard/textarea.js | 82 +++++++++++++++++++ 3 files changed, 115 insertions(+), 21 deletions(-) create mode 100644 lib/ace/keyboard/textarea.js diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index b1c0b5f0..a150a76f 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -402,7 +402,13 @@ exports.commands = [{ exec: function(editor) { editor.jumpToMatching(true); }, multiSelectAction: "forEach", readOnly: true -}, +}, { + name: "passKeysToBrowser", + bindKey: bindKey("null", "null"), + exec: function() {}, + passEvent: true, + readOnly: true +}, // commands disabled in readOnly mode { diff --git a/lib/ace/ext/menu_tools/add_editor_menu_options.js b/lib/ace/ext/menu_tools/add_editor_menu_options.js index 0c95e790..eb90e31d 100644 --- a/lib/ace/ext/menu_tools/add_editor_menu_options.js +++ b/lib/ace/ext/menu_tools/add_editor_menu_options.js @@ -60,41 +60,47 @@ module.exports.addEditorMenuOptions = function addEditorMenuOptions (editor) { var modelist = require('../modelist'); var themelist = require('../themelist'); editor.menuOptions = { - "setNewLineMode" : [{ - "textContent" : "unix", - "value" : "unix" + setNewLineMode: [{ + textContent: "unix", + value: "unix" }, { - "textContent" : "windows", - "value" : "windows" + textContent: "windows", + value: "windows" }, { - "textContent" : "auto", - "value" : "auto" + textContent: "auto", + value: "auto" }], - "setTheme" : [], - "setMode" : [], - "setKeyboardHandler": [{ - "textContent" : "ace", - "value" : "" + setTheme: [], + setMode: [], + setKeyboardHandler: [{ + textContent: "ace", + value: "" }, { - "textContent" : "vim", - "value" : "ace/keyboard/vim" + textContent: "vim", + value: "ace/keyboard/vim" }, { - "textContent" : "emacs", - "value" : "ace/keyboard/emacs" + textContent: "emacs", + value: "ace/keyboard/emacs" + }, { + textContent: "textarea", + value: "ace/keyboard/textarea" + }, { + textContent: "sublime", + value: "ace/keyboard/sublime" }] }; editor.menuOptions.setTheme = themelist.themes.map(function(theme) { return { - 'textContent' : theme.caption, - 'value' : theme.theme + textContent: theme.caption, + value: theme.theme }; }); editor.menuOptions.setMode = modelist.modes.map(function(mode) { return { - 'textContent' : mode.name, - 'value' : mode.mode + textContent: mode.name, + value: mode.mode }; }); }; diff --git a/lib/ace/keyboard/textarea.js b/lib/ace/keyboard/textarea.js new file mode 100644 index 00000000..470b2940 --- /dev/null +++ b/lib/ace/keyboard/textarea.js @@ -0,0 +1,82 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var HashHandler = require("./hash_handler").HashHandler; +exports.handler = new HashHandler(); + +[{ + bindKey: "Shift-Tab|Tab", + command: "passKeysToBrowser" +}, { + bindKey: {win: "Ctrl-L", mac: "Cmd-L"}, + command: "passKeysToBrowser" +}, { + bindKey: {win: "Ctrl-G", mac: "Cmd-G"}, + command: "gotoline" +}, { + bindKey: {win: "Ctrl-T|Ctrl-Shift-T", mac: "Cmd-T|Cmd-Shift-T"}, + command: "passKeysToBrowser" +}, { + bindKey: {win: "Ctrl-G", mac: "Cmd-G"}, + command: "passKeysToBrowser" +}, { + bindKey: {win: "Ctrl-G", mac: "Cmd-G"}, + command: "passKeysToBrowser" +}, { + name: "golineup", + bindKey: {win: null, mac: "Ctrl-P"}, +}, { + name: "golinedown", + bindKey: {win: null, mac: "Ctrl-N"}, +}, { + name: "gotoleft", + bindKey: {win: null, mac: "Ctrl-B"}, +}, { + name: "gotoright", + bindKey: {win: null, mac: "Ctrl-F"}, +}, { + name: "gotolineend", + bindKey: {win: null, mac: "Ctrl-E"}, +}, { + name: "gotolinestart", + bindKey: {win: null, mac: "Ctrl-A"}, +} +].forEach(function(k) { + var bindKey = k.bindKey; + if (typeof bindKey == "object") + bindKey = bindKey[exports.handler.platform]; + exports.handler.bindKey(bindKey, k.command); +}); +exports.handler.$id = "ace/keyboard/textarea"; + +}); From 1dd41ea75dfa90d2eed246659c7638efc91d7ae2 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Fri, 13 Jun 2014 16:17:17 +0200 Subject: [PATCH 45/58] Make mergeable commands (for undo history) overridable. --- lib/ace/editor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 7552a17a..6a68e5de 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -211,13 +211,14 @@ var Editor = function(renderer, session) { } }; + // TODO use property on commands instead of this + this.$mergeableCommands = ["backspace", "del", "insertstring"]; this.$historyTracker = function(e) { if (!this.$mergeUndoDeltas) return; - var prev = this.prevOp; - var mergeableCommands = ["backspace", "del", "insertstring"]; + var mergeableCommands = this.$mergeableCommands; // previous command was the same var shouldMerge = prev.command && (e.command.name == prev.command.name); if (e.command.name == "insertstring") { From 0450a1a539e5944f2514eb8a6eeca6fdb192c9d8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 25 Jun 2014 19:01:06 +0400 Subject: [PATCH 46/58] remove unneeded setOption call --- lib/ace/line_widgets.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js index 94ad9ddf..9e384e0e 100644 --- a/lib/ace/line_widgets.js +++ b/lib/ace/line_widgets.js @@ -87,7 +87,6 @@ function LineWidgets(session) { editor.widgetManager = this; - editor.setOption("enableLineWidgets", true); editor.renderer.on("beforeRender", this.measureWidgets); editor.renderer.on("afterRender", this.renderWidgets); }; From 2dcc4a3f04c01157b1a56b0703c5bd2ff430f9e5 Mon Sep 17 00:00:00 2001 From: Derk-Jan Hartman Date: Sat, 21 Jun 2014 16:28:10 +0200 Subject: [PATCH 47/58] Match platform specifics for dragDelay of text On Mac we keep it at 150ms. On other platforms it either does not exist or at most it's very small. This fixes issue #1879 --- lib/ace/mouse/mouse_handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index 9897290c..ce88eb81 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -192,7 +192,7 @@ var MouseHandler = function(editor) { config.defineOptions(MouseHandler.prototype, "mouseHandler", { scrollSpeed: {initialValue: 2}, - dragDelay: {initialValue: 150}, + dragDelay: {initialValue: (useragent.isMac ? 150 : 0)}, dragEnabled: {initialValue: true}, focusTimout: {initialValue: 0}, tooltipFollowsMouse: {initialValue: true} From f8b7dce3e05a507e7bf34adc4b97ec6efebf08af Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Sat, 28 Jun 2014 22:21:52 -0700 Subject: [PATCH 48/58] remove the invalid rule from the go syntax file --- lib/ace/mode/golang_highlight_rules.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/ace/mode/golang_highlight_rules.js b/lib/ace/mode/golang_highlight_rules.js index 5bd40b44..2369c8cb 100644 --- a/lib/ace/mode/golang_highlight_rules.js +++ b/lib/ace/mode/golang_highlight_rules.js @@ -72,9 +72,6 @@ define(function(require, exports, module) { }, { token : "paren.rparen", regex : "[\\])}]" - }, { - token: "invalid", - regex: "\\s+$" }, { token : "text", regex : "\\s+" From 299d174dc77c98566dacd547f0c3bfb72c91cbc3 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 22 May 2014 20:26:11 +0400 Subject: [PATCH 49/58] do not use setInnerHTML since it isn't faster on new browsers --- demo/kitchen-sink/demo.js | 2 +- lib/ace/layer/marker.js | 2 +- lib/ace/layer/text.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 0b35ca74..552b1e65 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -592,7 +592,7 @@ env.editSnippets = function() { require("ace/ext/language_tools"); env.editor.setOptions({ enableBasicAutocompletion: true, - enableLiveAutocompletion: true, + enableLiveAutocompletion: false, enableSnippets: true }); diff --git a/lib/ace/layer/marker.js b/lib/ace/layer/marker.js index cd1b992d..37e038b5 100644 --- a/lib/ace/layer/marker.js +++ b/lib/ace/layer/marker.js @@ -93,7 +93,7 @@ var Marker = function(parentEl) { this.drawSingleLineMarker(html, range, marker.clazz + " ace_start", config); } } - this.element = dom.setInnerHtml(this.element, html.join("")); + this.element.innerHTML = html.join(""); }; this.$getTop = function(row, layerConfig) { diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 140700aa..748d2231 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -203,7 +203,7 @@ var Text = function(parentEl) { html, row, !this.$useLineGroups(), row == foldStart ? foldLine : false ); lineElement.style.height = config.lineHeight * this.session.getRowLength(row) + "px"; - dom.setInnerHtml(lineElement, html.join("")); + lineElement.innerHTML = html.join(""); } row++; } @@ -310,7 +310,7 @@ var Text = function(parentEl) { row++; } - this.element = dom.setInnerHtml(this.element, html.join("")); + this.element.innerHTML = html.join(""); }; this.$textToken = { From b5eda22bfc0bf8519ccbb03d1442b6b5b9f05844 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 1 Jul 2014 00:29:18 +0400 Subject: [PATCH 50/58] update changelog --- ChangeLog.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index 2b1f88bb..224f9326 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,13 @@ +2014.07.01 Version 1.1.4 + +* New Features + - Highlight matching tags (Adam Jimenez) + - Improved jump to matching command (Adam Jimenez) + +* new language modes + - AppleScript (Yaogang Lian) + - Vala + 2014.03.08 Version 1.1.3 * New Features From ccfb820418034c5974191451ce71ac5ec59cc845 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 10 Jun 2014 22:12:34 +0400 Subject: [PATCH 51/58] add test for whitespace extension --- build | 2 +- lib/ace/ext/whitespace_test.js | 116 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 lib/ace/ext/whitespace_test.js diff --git a/build b/build index fc9d2cae..b85b38c1 160000 --- a/build +++ b/build @@ -1 +1 @@ -Subproject commit fc9d2cae9fe8e6e95e74c86a31d21caadd8f9f39 +Subproject commit b85b38c1e33e161826f4dd7774ef73aac55e0d14 diff --git a/lib/ace/ext/whitespace_test.js b/lib/ace/ext/whitespace_test.js new file mode 100644 index 00000000..be4f360f --- /dev/null +++ b/lib/ace/ext/whitespace_test.js @@ -0,0 +1,116 @@ +if (typeof process !== "undefined") { + require("amd-loader"); + require("../test/mockdom"); +} + +define(function(require, exports, module) { +"use strict"; + +var assert = require("assert"); +var EditSession = require("../edit_session").EditSession; +var whitespace = require("./whitespace"); + +// Execution ORDER: test.setUpSuite, setUp, testFn, tearDown, test.tearDownSuite +module.exports = { + timeout: 10000, + + "test tab detection": function(next) { + var s = new EditSession([ + "define({", + "\tfoo:1,", + "\tbar:2,", + "\tbaz:{,", + "\t\tx:3", + "\t}", + "})" + ]); + + var indent = whitespace.$detectIndentation(s.doc.$lines); + assert.equal(indent.ch, "\t"); + assert.equal(indent.length, undefined); + + s.insert({row: 0, column: 0}, " "); + indent = whitespace.$detectIndentation(s.doc.$lines); + assert.equal(indent.ch, "\t"); + assert.equal(indent.length, 4); + + s.setValue(""); + indent = whitespace.$detectIndentation(s.doc.$lines); + assert.ok(!indent); + + next(); + }, + + "test empty session": function(next) { + var s = new EditSession([ + "define({", + "foo:1,", + "})" + ]); + var indent = whitespace.$detectIndentation(s.doc.$lines); + assert.ok(!indent); + s.insert({row: 1, column: 0}, " x\n "); + + indent = whitespace.$detectIndentation(s.doc.$lines); + assert.equal(indent.ch, " "); + assert.equal(indent.length, 4); + + next(); + }, + + "!test one line": function(next) { + var s = new EditSession([ + "define({", + " foo:1,", + "})" + ]); + var indent = whitespace.$detectIndentation(s.doc.$lines); + assert.equal(indent.ch, " "); + assert.equal(indent.length, 4); + + next(); + }, + + "test 1 width indents": function(next) { + var s = new EditSession([ + "define({", + " foo:1,", + "})", + "define({", + " bar:1,", + "})", + " t", + " t", + " t", + " t", + " t", + " t", + " t", + " t" + ]); + var indent = whitespace.$detectIndentation(s.doc.$lines); + // assert.equal(indent.ch, " "); + // assert.equal(indent.length, 4); + + s = new EditSession([ + "{", + " foo:1,", + " bar: {", + " baz:2", + " }", + "}" + ]); + indent = whitespace.$detectIndentation(s.doc.$lines); + assert.equal(indent.ch, " "); + assert.equal(indent.length, 1); + + next(); + }, + +}; + +}); + +if (typeof module !== "undefined" && module === require.main) { + require("asyncjs").test.testcase(module.exports).exec(); +} From 2ce7ed0f44e50dc1d89e7599f2767982686fc0cd Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 1 Jul 2014 16:15:45 -0700 Subject: [PATCH 52/58] add the export keyword to dart --- lib/ace/mode/dart_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/dart_highlight_rules.js b/lib/ace/mode/dart_highlight_rules.js index 311acd90..750392f7 100644 --- a/lib/ace/mode/dart_highlight_rules.js +++ b/lib/ace/mode/dart_highlight_rules.js @@ -54,7 +54,7 @@ var DartHighlightRules = function() { }, { token: "keyword.other.import.dart", - regex: "(?:\\b)(?:library|import|part|of)(?:\\b)" + regex: "(?:\\b)(?:library|import|export|part|of)(?:\\b)" }, { token : ["keyword.other.import.dart", "text"], From f9b3655c943dee63b7aba01610bd1ee1d88e6878 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 2 Jul 2014 22:24:20 +0400 Subject: [PATCH 53/58] fix autocomplete popup click regression --- lib/ace/autocomplete/popup.js | 1 + lib/ace/keyboard/textinput.js | 14 +++++++++----- lib/ace/mouse/mouse_handler.js | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index a0ab7604..c8d168a6 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -67,6 +67,7 @@ var AcePopup = function(parentNode) { popup.renderer.setStyle("ace_autocomplete"); popup.setOption("displayIndentGuides", false); + popup.setOption("dragDelay", 150); var noop = function(){}; diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index fbf37786..ed213d70 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -428,13 +428,17 @@ var TextInput = function(parentNode, host) { this.onContextMenu = function(e) { afterContextMenu = true; - if (!tempStyle) - tempStyle = text.style.cssText; - - text.style.cssText = "z-index:100000;" + (useragent.isIE ? "opacity:0.1;" : ""); - resetSelection(host.selection.isEmpty()); host._emit("nativecontextmenu", {target: host, domEvent: e}); + this.moveToMouse(e, true); + }; + + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) + tempStyle = text.style.cssText; + text.style.cssText = (bringToFront ? "z-index:100000;" : "") + + (useragent.isIE ? "opacity:0.1;" : ""); + var rect = host.container.getBoundingClientRect(); var style = dom.computedStyle(host.container); var top = rect.top + (parseInt(style.borderTopWidth) || 0); diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index ce88eb81..9cf26721 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -49,7 +49,7 @@ var MouseHandler = function(editor) { var focusEditor = function(e) { if (!editor.isFocused() && editor.textInput) - editor.textInput.onContextMenu(e); + editor.textInput.moveToMouse(e); editor.focus() }; From f8be5072ac0d3ea6befade28a53c4e4c8fbbab96 Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 2 Jul 2014 23:56:24 +0400 Subject: [PATCH 54/58] allow using name instead of caption --- lib/ace/autocomplete/popup.js | 2 +- lib/ace/autocomplete/text_completer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index c8d168a6..a34ebf09 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -179,7 +179,7 @@ var AcePopup = function(parentNode) { if (typeof data == "string") data = {value: data}; if (!data.caption) - data.caption = data.value; + data.caption = data.value || data.name; var last = -1; var flag, c; diff --git a/lib/ace/autocomplete/text_completer.js b/lib/ace/autocomplete/text_completer.js index 6eebfc61..17a4bdad 100644 --- a/lib/ace/autocomplete/text_completer.js +++ b/lib/ace/autocomplete/text_completer.js @@ -68,7 +68,7 @@ define(function(require, exports, module) { var wordList = Object.keys(wordScore); callback(null, wordList.map(function(word) { return { - name: word, + caption: word, value: word, score: wordScore[word], meta: "local" From 8ed27996207ed25e1e0a4b9ce7394b805ee7ce40 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 3 Jul 2014 00:12:27 +0400 Subject: [PATCH 55/58] do not close the popup if there are matching completions --- lib/ace/autocomplete.js | 13 ++++++------- lib/ace/ext/language_tools.js | 4 ---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index adf9bd73..13d140d9 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -101,12 +101,12 @@ var Autocomplete = function() { this.changeTimer.cancel(); if (this.popup && this.popup.isOpen) { - this.gatherCompletionsId = this.gatherCompletionsId + 1; - } - - if (this.popup) + this.gatherCompletionsId += 1; this.popup.hide(); - + } + + if (this.base) + this.base.detach(); this.activated = false; this.completions = this.base = null; }; @@ -205,8 +205,7 @@ var Autocomplete = function() { var line = session.getLine(pos.row); var prefix = util.retrievePrecedingIdentifier(line, pos.column); - this.base = editor.getCursorPosition(); - this.base.column -= prefix.length; + this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length); var matches = []; var total = editor.completers.length; diff --git a/lib/ace/ext/language_tools.js b/lib/ace/ext/language_tools.js index dc56e6e0..d8b2a1dd 100644 --- a/lib/ace/ext/language_tools.js +++ b/lib/ace/ext/language_tools.js @@ -161,10 +161,6 @@ var doLiveAutocomplete = function(e) { editor.completer.autoSelect = false; editor.completer.autoInsert = false; editor.completer.showPopup(editor); - } else if (!prefix && hasCompleter) { - // When the prefix is empty - // close the autocomplete dialog - editor.completer.detach(); } } }; From bf72097811c2285a4999b527c79b24ef5d3ea070 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 4 Jul 2014 11:25:53 -0700 Subject: [PATCH 56/58] add a gitignore mode --- demo/kitchen-sink/docs/gitignore.gitignore | 8 +++++++ lib/ace/ext/modelist.js | 1 + lib/ace/mode/gitignore.js | 19 +++++++++++++++ lib/ace/mode/gitignore_highlight_rules.js | 28 ++++++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 demo/kitchen-sink/docs/gitignore.gitignore create mode 100644 lib/ace/mode/gitignore.js create mode 100644 lib/ace/mode/gitignore_highlight_rules.js diff --git a/demo/kitchen-sink/docs/gitignore.gitignore b/demo/kitchen-sink/docs/gitignore.gitignore new file mode 100644 index 00000000..80f4f386 --- /dev/null +++ b/demo/kitchen-sink/docs/gitignore.gitignore @@ -0,0 +1,8 @@ +# A sample .gitiignore file. + +.buildlog +.DS_Store +.svn + +# Also ignore user settings... +/.settings diff --git a/lib/ace/ext/modelist.js b/lib/ace/ext/modelist.js index 57470ed0..15a31610 100644 --- a/lib/ace/ext/modelist.js +++ b/lib/ace/ext/modelist.js @@ -70,6 +70,7 @@ var supportedModes = { Forth: ["frt|fs|ldr"], FTL: ["ftl"], Gherkin: ["feature"], + Gitignore: ["^.gitignore"], Glsl: ["glsl|frag|vert"], golang: ["go"], Groovy: ["groovy"], diff --git a/lib/ace/mode/gitignore.js b/lib/ace/mode/gitignore.js new file mode 100644 index 00000000..fd9b04f4 --- /dev/null +++ b/lib/ace/mode/gitignore.js @@ -0,0 +1,19 @@ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextMode = require("./text").Mode; +var GitignoreHighlightRules = require("./gitignore_highlight_rules").GitignoreHighlightRules; + +var Mode = function() { + this.HighlightRules = GitignoreHighlightRules; +}; +oop.inherits(Mode, TextMode); + +(function() { + this.$id = "ace/mode/gitignore"; +}).call(Mode.prototype); + +exports.Mode = Mode; +}); diff --git a/lib/ace/mode/gitignore_highlight_rules.js b/lib/ace/mode/gitignore_highlight_rules.js new file mode 100644 index 00000000..8889511d --- /dev/null +++ b/lib/ace/mode/gitignore_highlight_rules.js @@ -0,0 +1,28 @@ +define(function(require, exports, module) { +"use strict"; + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + +var GitignoreHighlightRules = function() { + this.$rules = { + "start" : [ + { + token : "comment", + regex : /[#].*$/ + } + ] + }; + + this.normalizeRules(); +}; + +GitignoreHighlightRules.metaData = { + fileTypes: ['gitignore'], + name: 'Gitignore' +}; + +oop.inherits(GitignoreHighlightRules, TextHighlightRules); + +exports.GitignoreHighlightRules = GitignoreHighlightRules; +}); From e6f01878d7a6f800bbf62ffc5f3a459bd9d75bbf Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 4 Jul 2014 11:35:07 -0700 Subject: [PATCH 57/58] add snippets; fix the kitchensink demo --- demo/kitchen-sink/docs/{gitignore.gitignore => .gitignore} | 2 +- lib/ace/snippets/gitignore.js | 7 +++++++ lib/ace/snippets/gitignore.snippets | 0 3 files changed, 8 insertions(+), 1 deletion(-) rename demo/kitchen-sink/docs/{gitignore.gitignore => .gitignore} (70%) create mode 100644 lib/ace/snippets/gitignore.js create mode 100644 lib/ace/snippets/gitignore.snippets diff --git a/demo/kitchen-sink/docs/gitignore.gitignore b/demo/kitchen-sink/docs/.gitignore similarity index 70% rename from demo/kitchen-sink/docs/gitignore.gitignore rename to demo/kitchen-sink/docs/.gitignore index 80f4f386..17e256b1 100644 --- a/demo/kitchen-sink/docs/gitignore.gitignore +++ b/demo/kitchen-sink/docs/.gitignore @@ -1,4 +1,4 @@ -# A sample .gitiignore file. +# A sample .gitignore file. .buildlog .DS_Store diff --git a/lib/ace/snippets/gitignore.js b/lib/ace/snippets/gitignore.js new file mode 100644 index 00000000..0a632c99 --- /dev/null +++ b/lib/ace/snippets/gitignore.js @@ -0,0 +1,7 @@ +define(function(require, exports, module) { +"use strict"; + +exports.snippetText = require("../requirejs/text!./gitignore.snippets"); +exports.scope = "gitignore"; + +}); diff --git a/lib/ace/snippets/gitignore.snippets b/lib/ace/snippets/gitignore.snippets new file mode 100644 index 00000000..e69de29b From cc03e77327fc7c83ed4983974b49105628d65039 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 7 Jul 2014 08:17:25 -0700 Subject: [PATCH 58/58] review comments --- demo/kitchen-sink/docs/.gitignore | 3 +++ lib/ace/mode/gitignore_highlight_rules.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/demo/kitchen-sink/docs/.gitignore b/demo/kitchen-sink/docs/.gitignore index 17e256b1..56ec8fd9 100644 --- a/demo/kitchen-sink/docs/.gitignore +++ b/demo/kitchen-sink/docs/.gitignore @@ -4,5 +4,8 @@ .DS_Store .svn +# Negated patterns: +!foo.bar + # Also ignore user settings... /.settings diff --git a/lib/ace/mode/gitignore_highlight_rules.js b/lib/ace/mode/gitignore_highlight_rules.js index 8889511d..cfa42afa 100644 --- a/lib/ace/mode/gitignore_highlight_rules.js +++ b/lib/ace/mode/gitignore_highlight_rules.js @@ -9,7 +9,10 @@ var GitignoreHighlightRules = function() { "start" : [ { token : "comment", - regex : /[#].*$/ + regex : /^\s*#.*$/ + }, { + token : "keyword", // negated patterns + regex : /^\s*!.*$/ } ] };