From 8443991f137a1b9fb9deccfdbeb9a705a464eb56 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 5 Nov 2013 20:54:38 +0400 Subject: [PATCH] miscelaneous fixes from c9 --- lib/ace/autocomplete/popup.js | 14 ++- lib/ace/css/editor.css | 2 +- lib/ace/keyboard/hash_handler.js | 3 + lib/ace/layer/gutter.js | 45 +++++---- lib/ace/lib/lang.js | 16 +-- lib/ace/mode/c9search_highlight_rules.js | 123 ++++++++++++++++++++++- lib/ace/theme/tomorrow_night_bright.css | 15 ++- lib/ace/tokenizer.js | 2 +- lib/ace/undomanager.js | 74 +++++++------- lib/ace/worker/mirror.js | 8 +- lib/ace/worker/worker.js | 4 + lib/ace/worker/worker_client.js | 9 +- 12 files changed, 234 insertions(+), 81 deletions(-) diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index 891cf1e8..76deb274 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -209,6 +209,9 @@ var AcePopup = function(parentNode) { } // public + popup.isOpen = false; + popup.isTopdown = false; + popup.data = []; popup.setData = function(list) { popup.data = list || []; @@ -233,26 +236,33 @@ var AcePopup = function(parentNode) { popup._signal("select"); } }; + + popup.on("changeSelection", function() { + if (popup.isOpen) + popup.setRow(popup.selection.lead.row); + }); popup.hide = function() { this.container.style.display = "none"; this._signal("hide"); popup.isOpen = false; }; - popup.show = function(pos, lineHeight) { + popup.show = function(pos, lineHeight, topdownOnly) { var el = this.container; var screenHeight = window.innerHeight; var renderer = this.renderer; // var maxLines = Math.min(renderer.$maxLines, this.session.getLength()); var maxH = renderer.$maxLines * lineHeight * 1.4; var top = pos.top + this.$borderSize; - if (top + maxH > screenHeight - lineHeight) { + if (top + maxH > screenHeight - lineHeight && !topdownOnly) { el.style.top = ""; el.style.bottom = screenHeight - top + "px"; + popup.isTopdown = false; } else { top += lineHeight; el.style.top = top + "px"; el.style.bottom = ""; + popup.isTopdown = true; } el.style.left = pos.left + "px"; diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index b291c11f..65d80d3d 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -158,7 +158,7 @@ z-index: 1; position: absolute; overflow: hidden; - white-space: nowrap; + white-space: pre; height: 100%; width: 100%; -moz-box-sizing: border-box; diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index baa88158..cb02f170 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -119,6 +119,9 @@ function HashHandler(config, platform) { if (typeof command === "function") command = { exec: command }; + if (typeof command !== "object") + return; + if (!command.name) command.name = name; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index c1130d8f..cd8622d8 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -75,8 +75,7 @@ var Gutter = function(parentEl) { this.setAnnotations = function(annotations) { // iterate over sparse array - this.$annotations = [] - var rowInfo, row; + this.$annotations = []; for (var i = 0; i < annotations.length; i++) { var annotation = annotations[i]; var row = annotation.row; @@ -112,7 +111,7 @@ var Gutter = function(parentEl) { } else if (delta.action == "removeText" || delta.action == "removeLines") { this.$annotations.splice(firstRow, len + 1, null); } else { - var args = Array(len + 1); + var args = new Array(len + 1); args.unshift(firstRow, 1); this.$annotations.splice.apply(this.$annotations, args); } @@ -121,13 +120,16 @@ var Gutter = function(parentEl) { this.update = function(config) { var firstRow = config.firstRow; var lastRow = config.lastRow; - var fold = this.session.getNextFoldLine(firstRow); + var session = this.session; + var fold = session.getNextFoldLine(firstRow); var foldStart = fold ? fold.start.row : Infinity; - var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets; - var breakpoints = this.session.$breakpoints; - var decorations = this.session.$decorations; - var firstLineNumber = this.session.$firstLineNumber; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var firstLineNumber = session.$firstLineNumber; var lastLineNumber = 0; + + var gutterRenderer = session.gutterRenderer; var cell = null; var index = -1; @@ -135,7 +137,7 @@ var Gutter = function(parentEl) { while (true) { if (row > foldStart) { row = fold.end.row + 1; - fold = this.session.getNextFoldLine(row, fold); + fold = session.getNextFoldLine(row, fold); foldStart = fold ? fold.start.row : Infinity; } if (row > lastRow) { @@ -166,19 +168,15 @@ var Gutter = function(parentEl) { if (cell.element.className != className) cell.element.className = className; - var height = this.session.getRowLength(row) * config.lineHeight + "px"; + var height = session.getRowLength(row) * config.lineHeight + "px"; if (height != cell.element.style.height) cell.element.style.height = height; - var text = lastLineNumber = row + firstLineNumber; - if (text != cell.textNode.data) - cell.textNode.data = text; - if (foldWidgets) { var c = foldWidgets[row]; // check if cached value is invalidated and we need to recompute if (c == null) - c = foldWidgets[row] = this.session.getFoldWidget(row); + c = foldWidgets[row] = session.getFoldWidget(row); } if (c) { @@ -198,21 +196,30 @@ var Gutter = function(parentEl) { if (cell.foldWidget.style.height != height) cell.foldWidget.style.height = height; } else { - if (cell.foldWidget != null) { + if (cell.foldWidget) { cell.element.removeChild(cell.foldWidget); cell.foldWidget = null; } } + + var text = lastLineNumber = gutterRenderer + ? gutterRenderer.getText(session, row) + : row + firstLineNumber; + if (text != cell.textNode.data) + cell.textNode.data = text; row++; } this.element.style.height = config.minHeight + "px"; - if (this.$fixedWidth || this.session.$useWrapMode) - lastLineNumber = this.session.getLength(); + if (this.$fixedWidth || session.$useWrapMode) + lastLineNumber = session.getLength(); - var gutterWidth = lastLineNumber.toString().length * config.characterWidth; + var gutterWidth = gutterRenderer + ? gutterRenderer.getWidth(session, lastLineNumber, config) + : lastLineNumber.toString().length * config.characterWidth; + var padding = this.$padding || this.$computePadding(); gutterWidth += padding.left + padding.right; if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index 0a0690e8..4405b5c2 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -172,6 +172,10 @@ exports.deferredCall = function(fcn) { timer = null; return deferred; }; + + deferred.isPending = function() { + return timer; + }; return deferred; }; @@ -185,15 +189,15 @@ exports.delayedCall = function(fcn, defaultTimeout) { }; var _self = function(timeout) { + if (timer == null) + timer = setTimeout(callback, timeout || defaultTimeout); + }; + + _self.delay = function(timeout) { timer && clearTimeout(timer); timer = setTimeout(callback, timeout || defaultTimeout); }; - - _self.delay = _self; - _self.schedule = function(timeout) { - if (timer == null) - timer = setTimeout(callback, timeout || 0); - }; + _self.schedule = _self; _self.call = function() { this.cancel(); diff --git a/lib/ace/mode/c9search_highlight_rules.js b/lib/ace/mode/c9search_highlight_rules.js index df31112b..07273bbb 100644 --- a/lib/ace/mode/c9search_highlight_rules.js +++ b/lib/ace/mode/c9search_highlight_rules.js @@ -32,8 +32,15 @@ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); +var lang = require("../lib/lang"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; +function safeCreateRegexp(source, flag) { + try { + return new RegExp(source, flag); + } catch(e) {} +} + var C9SearchHighlightRules = function() { // regexp must not have capturing parentheses. Use (?:) instead. @@ -41,12 +48,120 @@ var C9SearchHighlightRules = function() { this.$rules = { "start" : [ { - token : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text"], - regex : "(^\\s+[0-9]+)(:\\s*)(.+)" + tokenNames : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text", "c9searchresults.keyword"], + regex : "(^\\s+[0-9]+)(:\\s)(.+)", + onMatch : function(val, state, stack) { + var values = this.splitRegex.exec(val); + var types = this.tokenNames; + var tokens = [{ + type: types[0], + value: values[1] + },{ + type: types[1], + value: values[2] + }]; + + var regex = stack[1]; + var str = values[3]; + if (regex && str) + values = str.split(regex); // this doesn't work on ie8 but we don't care:) + else + values = [str]; + + for (var i = 0, l = values.length; i < l; i+=2) { + if (values[i]) + tokens.push({ + type: types[2], + value: values[i] + }); + if (values[i+1]) + tokens.push({ + type: types[3], + value: values[i + 1] + }); + } + return tokens; + } }, { token : ["string", "text"], // single line - regex : "(.+)(:$)" + regex : "(\\S.*)(:$)" + }, + { + regex : "Searching for .*$", + onMatch: function(val, state, stack) { + var parts = val.split("\x01"); + var search = parts[1]; + if (parts.length < 3) + return "text"; + var options = parts[2] == " in" ? parts[5] : parts[6]; + + if (!/regex/.test(options)) + search = lang.escapeRegExp(search); + if (/whole/.test(options)) + search = "\\b" + search + "\\b"; + var regex = safeCreateRegexp( + "(" + search + ")", + / sensitive/.test(options) ? "" : "i" + ); + if (regex) { + stack[0] = state; + stack[1] = regex; + } + + var i = 0; + var tokens = [ + { + value: parts[i++] + "'", + type: "text" + }, + { + value: parts[i++], + type: "text" // "c9searchresults.keyword" + }, + { + value: "'" + parts[i++], + type: "text" + } + ]; + + // replaced + if (parts[2] !== " in") { + tokens.push({ + value: "'" + parts[i++] + "'", + type: "text" + }, { + value: parts[i++], + type: "text" + }); + } + // path + tokens.push({ + value: " " + parts[i++] + " ", + type: "text" + }); + // options + if (parts[i+1]) { + tokens.push({ + value: "(" + parts[i+1] + ")", + type: "text" + }); + i += 1; + } else { + i -= 1; + } + while (i++ < parts.length) + parts[i] && tokens.push({ + value: parts[i], + type: "text" + }); + + return tokens; + } + }, + { + regex : "\\d+", + token: "constant.numeric" } ] }; @@ -56,4 +171,4 @@ oop.inherits(C9SearchHighlightRules, TextHighlightRules); exports.C9SearchHighlightRules = C9SearchHighlightRules; -}); +}); \ No newline at end of file diff --git a/lib/ace/theme/tomorrow_night_bright.css b/lib/ace/theme/tomorrow_night_bright.css index f092a44f..76c9a760 100644 --- a/lib/ace/theme/tomorrow_night_bright.css +++ b/lib/ace/theme/tomorrow_night_bright.css @@ -32,7 +32,14 @@ .ace-tomorrow-night-bright .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; - border: 1px solid #343434 + border: 1px solid #888888 +} +.ace-tomorrow-night-bright .ace_marker-layer .ace_highlight { + border: 1px solid rgb(110, 119, 0); + border-bottom: 0; + box-shadow: inset 0 -1px rgb(110, 119, 0); + margin: -1px 0 0 -1px; + background: rgba(255, 235, 0, 0.1); } .ace-tomorrow-night-bright .ace_marker-layer .ace_active-line { @@ -48,7 +55,7 @@ } .ace-tomorrow-night-bright .ace_marker-layer .ace_selected-word { - border: 1px solid #424242 + border: 1px solid #888888 } .ace-tomorrow-night-bright .ace_invisible { @@ -123,6 +130,10 @@ color: #969896 } +.ace-tomorrow-night-bright .ace_c9searchresults.ace_keyword { + color: #C2C280; +} + .ace-tomorrow-night-bright .ace_indent-guide { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYFBXV/8PAAJoAXX4kT2EAAAAAElFTkSuQmCC) right repeat-y; } \ No newline at end of file diff --git a/lib/ace/tokenizer.js b/lib/ace/tokenizer.js index 6d63b075..5a9aaad4 100644 --- a/lib/ace/tokenizer.js +++ b/lib/ace/tokenizer.js @@ -310,7 +310,7 @@ var Tokenizer = function(rules) { token = { value: line.substring(lastIndex, lastIndex += 2000), type: "overflow" - } + }; } currentState = "start"; stack = []; diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 12aaa01d..304dac23 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -52,14 +52,14 @@ var UndoManager = function() { (function() { /** - * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements: - * - * - `args[0]` is an array of deltas - * - `args[1]` is the document to associate with - * - * @param {Object} options Contains additional properties - * - **/ + * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements: + * + * - `args[0]` is an array of deltas + * - `args[1]` is the document to associate with + * + * @param {Object} options Contains additional properties + * + **/ this.execute = function(options) { var deltas = options.args[0]; this.$doc = options.args[1]; @@ -78,12 +78,12 @@ var UndoManager = function() { }; /** - * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} - * @param {Boolean} dontSelect {:dontSelect} - * - * - * @returns {Range} The range of the undo. - **/ + * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} + * @param {Boolean} dontSelect {:dontSelect} + * + * + * @returns {Range} The range of the undo. + **/ this.undo = function(dontSelect) { var deltas = this.$undoStack.pop(); var undoSelectionRange = null; @@ -98,11 +98,11 @@ var UndoManager = function() { }; /** - * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} - * @param {Boolean} dontSelect {:dontSelect} - * - * - **/ + * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} + * @param {Boolean} dontSelect {:dontSelect} + * + * + **/ this.redo = function(dontSelect) { var deltas = this.$redoStack.pop(); var redoSelectionRange = null; @@ -117,9 +117,9 @@ var UndoManager = function() { }; /** - * - * Destroys the stack of undo and redo redo operations. - **/ + * + * Destroys the stack of undo and redo redo operations. + **/ this.reset = function() { this.$undoStack = []; this.$redoStack = []; @@ -127,36 +127,36 @@ var UndoManager = function() { }; /** - * - * Returns `true` if there are undo operations left to perform. - * @returns {Boolean} - **/ + * + * Returns `true` if there are undo operations left to perform. + * @returns {Boolean} + **/ this.hasUndo = function() { return this.$undoStack.length > 0; }; /** - * - * Returns `true` if there are redo operations left to perform. - * @returns {Boolean} - **/ + * + * Returns `true` if there are redo operations left to perform. + * @returns {Boolean} + **/ this.hasRedo = function() { return this.$redoStack.length > 0; }; /** - * - * Marks the current status clean - **/ + * + * Marks the current status clean + **/ this.markClean = function() { this.dirtyCounter = 0; }; /** - * - * Returns if the current status is clean - * @returns {Boolean} - **/ + * + * Returns if the current status is clean + * @returns {Boolean} + **/ this.isClean = function() { return this.dirtyCounter === 0; }; diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index c521f8fd..7a3318fb 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -13,7 +13,9 @@ var Mirror = exports.Mirror = function(sender) { var _self = this; sender.on("change", function(e) { doc.applyDeltas(e.data); - deferredUpdate.schedule(_self.$timeout); + if (_self.$timeout) + return deferredUpdate.schedule(_self.$timeout); + _self.onUpdate(); }); }; @@ -38,6 +40,10 @@ var Mirror = exports.Mirror = function(sender) { // abstract method }; + this.isPending = function() { + return this.deferredUpdate.isPending(); + }; + }).call(Mirror.prototype); }); diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js index 2f250afc..04296156 100644 --- a/lib/ace/worker/worker.js +++ b/lib/ace/worker/worker.js @@ -16,6 +16,10 @@ window.console.trace = window.console; window.window = window; window.ace = window; +window.onerror = function(message, file, line, col, err) { + console.error("Worker " + err.stack); +}; + window.normalizeModule = function(parentId, moduleName) { // normalize plugin requires if (moduleName.indexOf("!") !== -1) { diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 3411d509..798f1b4b 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -39,7 +39,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); this.changeListener = this.changeListener.bind(this); this.onMessage = this.onMessage.bind(this); - this.onError = this.onError.bind(this); // nameToUrl is renamed to toUrl in requirejs 2 if (require.nameToUrl && !require.toUrl) @@ -69,7 +68,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.callbackId = 1; this.callbacks = {}; - this.$worker.onerror = this.onError; this.$worker.onmessage = this.onMessage; }; @@ -77,11 +75,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { oop.implement(this, EventEmitter); - this.onError = function(e) { - window.console && console.log && console.log(e); - throw e; - }; - this.onMessage = function(e) { var msg = e.data; switch(msg.type) { @@ -157,7 +150,7 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.changeListener = function(e) { if (!this.deltaQueue) { this.deltaQueue = [e.data]; - setTimeout(this.$sendDeltaQueue, 1); + setTimeout(this.$sendDeltaQueue, 0); } else this.deltaQueue.push(e.data); };