From 23a3ba85f014af153d2733a2f209d8128eb65da0 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 3 Dec 2014 13:56:22 -0500 Subject: [PATCH 01/12] Teach tmlanguage.js to process multiple .tmLanguage files at once You can now pass more than one .tmLanguage file to tmlanguage.js and it will process each one in turn. This can vastly speed up the processing of multiple files as you don't have to pay the node/V8 startup cost for each file. Note that the script's "dev mode" is now controlled by a --dev flag rather than an unnamed second argument. --- tool/tmlanguage.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tool/tmlanguage.js b/tool/tmlanguage.js index 9261a2f1..0e458330 100644 --- a/tool/tmlanguage.js +++ b/tool/tmlanguage.js @@ -684,13 +684,14 @@ function convertTmLanguage(name, langStr) { if (!module.parent) { var args = process.argv.splice(2); - var tmLanguageFile = args[0]; - var devMode = args[1]; - if (tmLanguageFile === undefined) { - console.error("Usage: node tmlanguage.js path/or/url/to/syntax.file"); + var devMode = args[0] == "--dev"; + if (devMode) + args.shift(); + if (args.length < 1) { + console.error("Usage: node tmlanguage.js [--dev] path/or/url/to/syntax.file ..."); process.exit(1); } - fetchAndConvert(tmLanguageFile); + args.forEach(fetchAndConvert); } else { exports.fetchAndConvert = fetchAndConvert; } From 8c1aa5c7723928283236efb358ac45f871ce219a Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 22 Jul 2014 03:27:51 +0400 Subject: [PATCH 02/12] do not use automatic scroll into view prepares for deprecating the feature in the next version of ace --- lib/ace/autocomplete/popup.js | 3 +++ lib/ace/commands/default_commands.js | 17 ++++++++++++-- lib/ace/commands/multi_select_commands.js | 13 ++++++++++- lib/ace/editor.js | 27 +++++++++++++++++++++-- lib/ace/mouse/default_handlers.js | 15 +++++++------ lib/ace/multi_select.js | 27 ----------------------- 6 files changed, 63 insertions(+), 39 deletions(-) diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index f580e391..862a1c5e 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -209,6 +209,8 @@ var AcePopup = function(parentNode) { return this.screenWidth = 0; }; + popup.$blockScrolling = Infinity; + // public popup.isOpen = false; popup.isTopdown = false; @@ -241,6 +243,7 @@ var AcePopup = function(parentNode) { popup.on("changeSelection", function() { if (popup.isOpen) popup.setRow(popup.selection.lead.row); + popup.renderer.scrollCursorIntoView(); }); popup.hide = function() { diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index a150a76f..c5d34aae 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -205,12 +205,14 @@ exports.commands = [{ bindKey: bindKey("Shift-Up", "Shift-Up"), exec: function(editor) { editor.getSelection().selectUp(); }, multiSelectAction: "forEach", + scrollIntoView: "cursor", readOnly: true }, { name: "golineup", bindKey: bindKey("Up", "Up|Ctrl-P"), exec: function(editor, args) { editor.navigateUp(args.times); }, multiSelectAction: "forEach", + scrollIntoView: "cursor", readOnly: true }, { name: "selecttoend", @@ -395,12 +397,21 @@ exports.commands = [{ bindKey: bindKey("Ctrl-P", "Ctrl-P"), exec: function(editor) { editor.jumpToMatching(); }, multiSelectAction: "forEach", + scrollIntoView: "animate", readOnly: true }, { name: "selecttomatching", bindKey: bindKey("Ctrl-Shift-P", "Ctrl-Shift-P"), exec: function(editor) { editor.jumpToMatching(true); }, multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true +}, { + name: "expandToMatching", + bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"), + exec: function(editor) { editor.jumpToMatching(true, true); }, + multiSelectAction: "forEach", + scrollIntoView: "animate", readOnly: true }, { name: "passKeysToBrowser", @@ -458,11 +469,13 @@ exports.commands = [{ name: "modifyNumberUp", bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"), exec: function(editor) { editor.modifyNumber(1); }, + scrollIntoView: "cursor", multiSelectAction: "forEach" }, { name: "modifyNumberDown", bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"), exec: function(editor) { editor.modifyNumber(-1); }, + scrollIntoView: "cursor", multiSelectAction: "forEach" }, { name: "replace", @@ -629,7 +642,7 @@ exports.commands = [{ var isBackwards = editor.selection.isBackwards(); var selectionStart = isBackwards ? editor.selection.getSelectionLead() : editor.selection.getSelectionAnchor(); var selectionEnd = isBackwards ? editor.selection.getSelectionAnchor() : editor.selection.getSelectionLead(); - var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length + var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length; var selectedText = editor.session.doc.getTextRange(editor.selection.getRange()); var selectedCount = selectedText.replace(/\n\s*/, " ").length; var insertLine = editor.session.doc.getLine(selectionStart.row); @@ -640,7 +653,7 @@ exports.commands = [{ curLine = " " + curLine; } insertLine += curLine; - }; + } if (selectionEnd.row + 1 < (editor.session.doc.getLength() - 1)) { // Don't insert a newline at the end of the document diff --git a/lib/ace/commands/multi_select_commands.js b/lib/ace/commands/multi_select_commands.js index 347eb606..ba6392bc 100644 --- a/lib/ace/commands/multi_select_commands.js +++ b/lib/ace/commands/multi_select_commands.js @@ -35,41 +35,49 @@ exports.defaultCommands = [{ name: "addCursorAbove", exec: function(editor) { editor.selectMoreLines(-1); }, bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"}, + scrollIntoView: "cursor", readonly: true }, { name: "addCursorBelow", exec: function(editor) { editor.selectMoreLines(1); }, bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"}, + scrollIntoView: "cursor", readonly: true }, { name: "addCursorAboveSkipCurrent", exec: function(editor) { editor.selectMoreLines(-1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"}, + scrollIntoView: "cursor", readonly: true }, { name: "addCursorBelowSkipCurrent", exec: function(editor) { editor.selectMoreLines(1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"}, + scrollIntoView: "cursor", readonly: true }, { name: "selectMoreBefore", exec: function(editor) { editor.selectMore(-1); }, bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"}, + scrollIntoView: "cursor", readonly: true }, { name: "selectMoreAfter", exec: function(editor) { editor.selectMore(1); }, bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"}, + scrollIntoView: "cursor", readonly: true }, { name: "selectNextBefore", exec: function(editor) { editor.selectMore(-1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"}, + scrollIntoView: "cursor", readonly: true }, { name: "selectNextAfter", exec: function(editor) { editor.selectMore(1, true); }, bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"}, + scrollIntoView: "cursor", readonly: true }, { name: "splitIntoLines", @@ -79,11 +87,13 @@ exports.defaultCommands = [{ }, { name: "alignCursors", exec: function(editor) { editor.alignCursors(); }, - bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"} + bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}, + scrollIntoView: "cursor" }, { name: "findAll", exec: function(editor) { editor.findAll(); }, bindKey: {win: "Ctrl-Alt-K", mac: "Ctrl-Alt-G"}, + scrollIntoView: "cursor", readonly: true }]; @@ -92,6 +102,7 @@ exports.multiSelectCommands = [{ name: "singleSelection", bindKey: "esc", exec: function(editor) { editor.exitMultiSelectMode(); }, + scrollIntoView: "cursor", readonly: true, isAvailable: function(editor) {return editor && editor.inMultiSelectMode} }]; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 59b136b1..a3fdc978 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -150,7 +150,8 @@ var Editor = function(renderer, session) { args: commadEvent.args, scrollTop: this.renderer.scrollTop }; - + if (this.curOp.command.name) + this.$blockScrolling++; // this.selections.push(this.selection.toJSON()); }; @@ -160,6 +161,8 @@ var Editor = function(renderer, session) { return this.curOp = null; this._signal("beforeEndOperation"); var command = this.curOp.command; + if (command.name && this.$blockScrolling) + this.$blockScrolling--; if (command && command.scrollIntoView) { switch (command.scrollIntoView) { case "center": @@ -714,6 +717,7 @@ var Editor = function(renderer, session) { this.$cursorChange(); if (!this.$blockScrolling) { + console.warn("In next version of ace automatic scrolling into view will be disabled"); this.renderer.scrollCursorIntoView(); } @@ -902,9 +906,28 @@ var Editor = function(renderer, session) { // todo this should change when paste becomes a command if (this.$readOnly) return; + var e = {text: text}; this._signal("paste", e); - this.insert(e.text, true); + text = e.text; + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { + this.insert(text); + } else { + var lines = text.split(/\r\n|\r|\n/); + var ranges = this.selection.rangeList.ranges; + + if (lines.length > ranges.length || lines.length < 2 || !lines[1]) + return this.commands.exec("insertstring", this, text); + + for (var i = ranges.length; i--;) { + var range = ranges[i]; + if (!range.isEmpty()) + this.session.remove(range); + + this.session.insert(range.start, lines[i]); + } + } + this.renderer.scrollCursorIntoView(); }; this.execCommand = function(command, args) { diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index bbd85484..a48d1097 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -70,10 +70,10 @@ function DefaultHandlers(mouseHandler) { if (button !== 0) { var selectionRange = editor.getSelectionRange(); var selectionEmpty = selectionRange.isEmpty(); - + editor.$blockScrolling++; if (selectionEmpty) editor.selection.moveToPosition(pos); - + editor.$blockScrolling--; // 2: contextmenu, 1: linux paste editor.textInput.onContextMenu(ev.domEvent); return; // stopping event here breaks contextmenu on ff mac @@ -100,7 +100,7 @@ function DefaultHandlers(mouseHandler) { pos = pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); var editor = this.editor; // allow double/triple click handlers to change selection - + editor.$blockScrolling++; if (this.mousedownEvent.getShiftKey()) editor.selection.selectToPosition(pos); else if (!waitForClickSelection) @@ -112,12 +112,13 @@ function DefaultHandlers(mouseHandler) { } editor.setStyle("ace_selecting"); this.setState("select"); + editor.$blockScrolling--; }; this.select = function() { var anchor, editor = this.editor; var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); - + editor.$blockScrolling++; if (this.$clickSelection) { var cmp = this.$clickSelection.comparePoint(cursor); @@ -133,7 +134,7 @@ function DefaultHandlers(mouseHandler) { editor.selection.setSelectionAnchor(anchor.row, anchor.column); } editor.selection.selectToPosition(cursor); - + editor.$blockScrolling--; editor.renderer.scrollCursorIntoView(); }; @@ -141,7 +142,7 @@ function DefaultHandlers(mouseHandler) { var anchor, editor = this.editor; var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); var range = editor.selection[unitName](cursor.row, cursor.column); - + editor.$blockScrolling++; if (this.$clickSelection) { var cmpStart = this.$clickSelection.comparePoint(range.start); var cmpEnd = this.$clickSelection.comparePoint(range.end); @@ -165,7 +166,7 @@ function DefaultHandlers(mouseHandler) { editor.selection.setSelectionAnchor(anchor.row, anchor.column); } editor.selection.selectToPosition(cursor); - + editor.$blockScrolling--; editor.renderer.scrollCursorIntoView(); }; diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 729d5253..0635afc2 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -558,33 +558,6 @@ var Editor = require("./editor").Editor; } }; - // todo this should change when paste becomes a command - this.onPaste = function(text) { - if (this.$readOnly) - return; - - - var e = {text: text}; - this._signal("paste", e); - text = e.text; - if (!this.inMultiSelectMode || this.inVirtualSelectionMode) - return this.insert(text); - - var lines = text.split(/\r\n|\r|\n/); - var ranges = this.selection.rangeList.ranges; - - if (lines.length > ranges.length || lines.length < 2 || !lines[1]) - return this.commands.exec("insertstring", this, text); - - for (var i = ranges.length; i--;) { - var range = ranges[i]; - if (!range.isEmpty()) - this.session.remove(range); - - this.session.insert(range.start, lines[i]); - } - }; - /** * Finds and selects all the occurences of `needle`. * @param {String} The text to find From a71c17b46f2088d7584b3e9d09d2c83787bc1954 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 23 Nov 2014 15:10:50 +0400 Subject: [PATCH 03/12] add config.warn and refactor config to allow sharing with ace_tree --- lib/ace/config.js | 105 +------------------------ lib/ace/edit_session.js | 3 +- lib/ace/editor.js | 5 +- lib/ace/lib/app_config.js | 158 ++++++++++++++++++++++++++++++++++++++ lib/ace/lib/dom.js | 7 +- lib/ace/tokenizer.js | 10 +-- 6 files changed, 173 insertions(+), 115 deletions(-) create mode 100644 lib/ace/lib/app_config.js diff --git a/lib/ace/config.js b/lib/ace/config.js index 6116da96..34bf50fd 100644 --- a/lib/ace/config.js +++ b/lib/ace/config.js @@ -34,7 +34,9 @@ define(function(require, exports, module) { var lang = require("./lib/lang"); var oop = require("./lib/oop"); var net = require("./lib/net"); -var EventEmitter = require("./lib/event_emitter").EventEmitter; +var AppConfig = require("./lib/app_config").AppConfig; + +module.exports = exports = new AppConfig(); var global = (function() { return this; @@ -69,8 +71,6 @@ exports.all = function() { }; // module loading -oop.implement(exports, EventEmitter); - exports.moduleUrl = function(name, component) { if (options.$moduleUrls[name]) return options.$moduleUrls[name]; @@ -142,7 +142,6 @@ exports.loadModule = function(moduleName, onLoad) { net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); }; - // initialization function init(packaged) { options.packaged = packaged || require.packaged || module.packaged || (global.define && define.packaged); @@ -200,102 +199,4 @@ function deHyphenate(str) { return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); }); } -var optionsProvider = { - setOptions: function(optList) { - Object.keys(optList).forEach(function(key) { - this.setOption(key, optList[key]); - }, this); - }, - getOptions: function(optionNames) { - var result = {}; - if (!optionNames) { - optionNames = Object.keys(this.$options); - } else if (!Array.isArray(optionNames)) { - result = optionNames; - optionNames = Object.keys(result); - } - optionNames.forEach(function(key) { - result[key] = this.getOption(key); - }, this); - return result; - }, - setOption: function(name, value) { - if (this["$" + name] === value) - return; - var opt = this.$options[name]; - if (!opt) { - if (typeof console != "undefined" && console.warn) - console.warn('misspelled option "' + name + '"'); - return undefined; - } - if (opt.forwardTo) - return this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value); - - if (!opt.handlesSet) - this["$" + name] = value; - if (opt && opt.set) - opt.set.call(this, value); - }, - getOption: function(name) { - var opt = this.$options[name]; - if (!opt) { - if (typeof console != "undefined" && console.warn) - console.warn('misspelled option "' + name + '"'); - return undefined; - } - if (opt.forwardTo) - return this[opt.forwardTo] && this[opt.forwardTo].getOption(name); - return opt && opt.get ? opt.get.call(this) : this["$" + name]; - } -}; - -var defaultOptions = {}; -/* - * option {name, value, initialValue, setterName, set, get } - */ -exports.defineOptions = function(obj, path, options) { - if (!obj.$options) - defaultOptions[path] = obj.$options = {}; - - Object.keys(options).forEach(function(key) { - var opt = options[key]; - if (typeof opt == "string") - opt = {forwardTo: opt}; - - opt.name || (opt.name = key); - obj.$options[opt.name] = opt; - if ("initialValue" in opt) - obj["$" + opt.name] = opt.initialValue; - }); - - // implement option provider interface - oop.implement(obj, optionsProvider); - - return this; -}; - -exports.resetOptions = function(obj) { - Object.keys(obj.$options).forEach(function(key) { - var opt = obj.$options[key]; - if ("value" in opt) - obj.setOption(key, opt.value); - }); -}; - -exports.setDefaultValue = function(path, name, value) { - var opts = defaultOptions[path] || (defaultOptions[path] = {}); - if (opts[name]) { - if (opts.forwardTo) - exports.setDefaultValue(opts.forwardTo, name, value); - else - opts[name].value = value; - } -}; - -exports.setDefaultValues = function(path, optionHash) { - Object.keys(optionHash).forEach(function(key) { - exports.setDefaultValue(path, key, optionHash[key]); - }); -}; - }); diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 932a9e3d..347c854a 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -969,8 +969,7 @@ var EditSession = function(text, mode) { try { this.$worker = this.$mode.createWorker(this); } catch (e) { - if (typeof console == "object" && console.error) - console.log("Could not load worker", e); + config.warn("Could not load worker", e); this.$worker = null; } }; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index a3fdc978..78c7c18d 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -717,7 +717,10 @@ var Editor = function(renderer, session) { this.$cursorChange(); if (!this.$blockScrolling) { - console.warn("In next version of ace automatic scrolling into view will be disabled"); + config.warn("Automatically scrolling cursor into view after selection change", + "this will be disabled in the next version", + "set editor.$blockScrolling = Infinity to disable this message" + ); this.renderer.scrollCursorIntoView(); } diff --git a/lib/ace/lib/app_config.js b/lib/ace/lib/app_config.js new file mode 100644 index 00000000..84b9e70c --- /dev/null +++ b/lib/ace/lib/app_config.js @@ -0,0 +1,158 @@ +/* ***** 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) { +"no use strict"; + +var lang = require("./lang"); +var oop = require("./oop"); +var EventEmitter = require("./event_emitter").EventEmitter; + +var optionsProvider = { + setOptions: function(optList) { + Object.keys(optList).forEach(function(key) { + this.setOption(key, optList[key]); + }, this); + }, + getOptions: function(optionNames) { + var result = {}; + if (!optionNames) { + optionNames = Object.keys(this.$options); + } else if (!Array.isArray(optionNames)) { + result = optionNames; + optionNames = Object.keys(result); + } + optionNames.forEach(function(key) { + result[key] = this.getOption(key); + }, this); + return result; + }, + setOption: function(name, value) { + if (this["$" + name] === value) + return; + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value); + + if (!opt.handlesSet) + this["$" + name] = value; + if (opt && opt.set) + opt.set.call(this, value); + }, + getOption: function(name) { + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].getOption(name); + return opt && opt.get ? opt.get.call(this) : this["$" + name]; + } +}; + +function warn(message) { + if (typeof console != "undefined" && console.warn) + console.warn.apply(console, arguments); +} + +function reportError(msg, data) { + var e = new Error(msg); + e.data = data; + if (typeof console == "object" && console.error) + console.error(e); + setTimeout(function() { throw e; }); +}; + +var AppConfig = function() { + this.$defaultOptions = {}; +}; + +(function() { + // module loading + oop.implement(this, EventEmitter); + /* + * option {name, value, initialValue, setterName, set, get } + */ + this.defineOptions = function(obj, path, options) { + if (!obj.$options) + this.$defaultOptions[path] = obj.$options = {}; + + Object.keys(options).forEach(function(key) { + var opt = options[key]; + if (typeof opt == "string") + opt = {forwardTo: opt}; + + opt.name || (opt.name = key); + obj.$options[opt.name] = opt; + if ("initialValue" in opt) + obj["$" + opt.name] = opt.initialValue; + }); + + // implement option provider interface + oop.implement(obj, optionsProvider); + + return this; + }; + + this.resetOptions = function(obj) { + Object.keys(obj.$options).forEach(function(key) { + var opt = obj.$options[key]; + if ("value" in opt) + obj.setOption(key, opt.value); + }); + }; + + this.setDefaultValue = function(path, name, value) { + var opts = this.$defaultOptions[path] || (this.$defaultOptions[path] = {}); + if (opts[name]) { + if (opts.forwardTo) + this.setDefaultValue(opts.forwardTo, name, value); + else + opts[name].value = value; + } + }; + + this.setDefaultValues = function(path, optionHash) { + Object.keys(optionHash).forEach(function(key) { + this.setDefaultValue(path, key, optionHash[key]); + }); + }; + + this.warn = warn; + this.reportError = reportError; + +}).call(AppConfig.prototype); + +exports.AppConfig = AppConfig; + +}); diff --git a/lib/ace/lib/dom.js b/lib/ace/lib/dom.js index 65f20d7d..ef2f7caa 100644 --- a/lib/ace/lib/dom.js +++ b/lib/ace/lib/dom.js @@ -31,9 +31,6 @@ define(function(require, exports, module) { "use strict"; -if (typeof document == "undefined") - return; - var XHTML_NS = "http://www.w3.org/1999/xhtml"; exports.getDocumentHead = function(doc) { @@ -175,6 +172,10 @@ exports.getInnerHeight = function(element) { ); }; + +if (typeof document == "undefined") + return; + if (window.pageYOffset !== undefined) { exports.getPageScrollTop = function() { return window.pageYOffset; diff --git a/lib/ace/tokenizer.js b/lib/ace/tokenizer.js index a0756157..72490fbb 100644 --- a/lib/ace/tokenizer.js +++ b/lib/ace/tokenizer.js @@ -31,6 +31,7 @@ define(function(require, exports, module) { "use strict"; +var config = require("./config"); // tokenizing lines longer than this makes editor very slow var MAX_TOKEN_COUNT = 2000; /** @@ -353,13 +354,8 @@ var Tokenizer = function(rules) { }; }; - this.reportError = function(msg, data) { - var e = new Error(msg); - e.data = data; - if (typeof console == "object" && console.error) - console.error(e); - setTimeout(function() { throw e; }); - }; + this.reportError = config.reportError; + }).call(Tokenizer.prototype); exports.Tokenizer = Tokenizer; From 29597325d97f324442e76b9f2c36ceb013ba332e Mon Sep 17 00:00:00 2001 From: Victorien ELVINGER Date: Sat, 6 Dec 2014 19:50:52 +0100 Subject: [PATCH 04/12] Update Eiffel programming language definition. - Disable comments into strings - Enable concise form for real numbers - Enable juxtaposed digit separators - Check char and string content. --- lib/ace/mode/eiffel_highlight_rules.js | 67 ++++++++++++-------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/lib/ace/mode/eiffel_highlight_rules.js b/lib/ace/mode/eiffel_highlight_rules.js index f2cc8bd6..a95a4f37 100644 --- a/lib/ace/mode/eiffel_highlight_rules.js +++ b/lib/ace/mode/eiffel_highlight_rules.js @@ -39,7 +39,7 @@ var EiffelHighlightRules = function() { "class|convert|create|debug|deferred|detachable|do|else|elseif|end|" + "ensure|expanded|export|external|feature|from|frozen|if|inherit|" + "inspect|invariant|like|local|loop|not|note|obsolete|old|once|" + - "Precursor|redefine|rename|require|rescue|retry|select|separate|" + + "Precursor|redefine|rename|require|rescue|retry|select|separate|" + "some|then|undefine|until|variant|when"; var operatorKeywords = "and|implies|or|xor"; @@ -58,45 +58,44 @@ var EiffelHighlightRules = function() { "keyword": keywords }, "identifier", true); + var simpleString = /(?:[^"%\b\f\v]|%[A-DFHLNQR-V%'"()<>]|%\/(?:0[xX][\da-fA-F](?:_*[\da-fA-F])*|0[cC][0-7](?:_*[0-7])*|0[bB][01](?:_*[01])*|\d(?:_*\d)*)\/)+?/; + this.$rules = { "start": [{ - token : "comment.line.double-dash", - regex : /--.*$/ - }, { - token : "string.quoted.double", - regex : /"(?:%"|[^%])*?"/ - }, { - token : "string.quoted.other", // "[ ]" aligned verbatim string + token : "string.quoted.other", // Aligned-verbatim-strings (verbatim option not supported) regex : /"\[/, next: "aligned_verbatim_string" }, { - token : "string.quoted.other", // "{ }" non-aligned verbatim string + token : "string.quoted.other", // Non-aligned-verbatim-strings (verbatim option not supported) regex : /"\{/, next: "non-aligned_verbatim_string" + }, { + token : "string.quoted.double", + regex : /"(?:[^%\b\f\n\r\v]|%[A-DFHLNQR-V%'"()<>]|%\/(?:0[xX][\da-fA-F](?:_*[\da-fA-F])*|0[cC][0-7](?:_*[0-7])*|0[bB][01](?:_*[01])*|\d(?:_*\d)*)\/)*?"/ + }, { + token : "comment.line.double-dash", + regex : /--.*/ }, { token : "constant.character", - regex : /'(?:%%|%T|%R|%N|%F|%'|[^%])'/ + regex : /'(?:[^%\b\f\n\r\t\v]|%[A-DFHLNQR-V%'"()<>]|%\/(?:0[xX][\da-fA-F](?:_*[\da-fA-F])*|0[cC][0-7](?:_*[0-7])*|0[bB][01](?:_*[01])*|\d(?:_*\d)*)\/)'/ }, { - token : "constant.numeric", // real - regex : /(?:\d(?:_?\d)*\.|\.\d)(?:\d*[eE][+-]?\d+)?\b/ + token : "constant.numeric", // hexa | octal | bin + regex : /\b0(?:[xX][\da-fA-F](?:_*[\da-fA-F])*|[cC][0-7](?:_*[0-7])*|[bB][01](?:_*[01])*)\b/ }, { - token : "constant.numeric", // integer - regex : /\d(?:_?\d)*\b/ + token : "constant.numeric", + regex : /(?:(?:\d(?:_*\d)*)?\.(?:(?:\d(?:_*\d)*)?[eE][+-]?)?\d(?:_*\d)*)|\d(?:_*\d)*\.?/ }, { - token : "constant.numeric", // hex - regex : /0[xX][a-fA-F\d](?:_?[a-fA-F\d])*\b/ + token : "paren.lparen", + regex : /[\[({]|<<|\|\(/ }, { - token : "constant.numeric", // octal - regex : /0[cC][0-7](?:_?[0-7])*\b/ - },{ - token : "constant.numeric", // bin - regex : /0[bB][01](?:_?[01])*\b/ - }, { - token : "keyword.operator", - regex : /\+|\-|\*|\/|\\\\|\/\/|\^|~|\/~|<|>|<=|>=|\/=|=|:=|\|\.\.\||\.\./ + token : "paren.rparen", + regex : /[\])}]|>>|\|\)/ }, { token : "keyword.operator", // punctuation - regex : /\.|:|,|;\b/ + regex : /:=|->|\.(?=\w)|[;,:?]/ + }, { + token : "keyword.operator", + regex : /\\\\|\|\.\.\||\.\.|\/[~\/]?|[><\/]=?|[-+*^=~]/ }, { token : function (v) { var result = keywordMapper (v); @@ -106,33 +105,27 @@ var EiffelHighlightRules = function() { return result; }, regex : /[a-zA-Z][a-zA-Z\d_]*\b/ - }, { - token : "paren.lparen", - regex : /[\[({]/ - }, { - token : "paren.rparen", - regex : /[\])}]/ }, { token : "text", regex : /\s+/ } ], "aligned_verbatim_string" : [{ - token : "string", // closing multi-line comment + token : "string", regex : /]"/, next : "start" }, { - token : "string", // comment spanning whole line - regex : /[^(?:\]")]+/ + token : "string", + regex : simpleString } ], "non-aligned_verbatim_string" : [{ - token : "string.quoted.other", // closing multi-line comment + token : "string.quoted.other", regex : /}"/, next : "start" }, { - token : "string.quoted.other", // comment spanning whole line - regex : /[^(?:\}")]+/ + token : "string.quoted.other", + regex : simpleString } ]}; }; From 354dc37ea604916051024e4d9da368ae1ff46f98 Mon Sep 17 00:00:00 2001 From: Victorien ELVINGER Date: Sat, 6 Dec 2014 20:04:27 +0100 Subject: [PATCH 05/12] Remove unneeded regex groups for Eiffel --- lib/ace/mode/eiffel_highlight_rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/mode/eiffel_highlight_rules.js b/lib/ace/mode/eiffel_highlight_rules.js index a95a4f37..a3ca8896 100644 --- a/lib/ace/mode/eiffel_highlight_rules.js +++ b/lib/ace/mode/eiffel_highlight_rules.js @@ -83,7 +83,7 @@ var EiffelHighlightRules = function() { regex : /\b0(?:[xX][\da-fA-F](?:_*[\da-fA-F])*|[cC][0-7](?:_*[0-7])*|[bB][01](?:_*[01])*)\b/ }, { token : "constant.numeric", - regex : /(?:(?:\d(?:_*\d)*)?\.(?:(?:\d(?:_*\d)*)?[eE][+-]?)?\d(?:_*\d)*)|\d(?:_*\d)*\.?/ + regex : /(?:\d(?:_*\d)*)?\.(?:(?:\d(?:_*\d)*)?[eE][+-]?)?\d(?:_*\d)*|\d(?:_*\d)*\.?/ }, { token : "paren.lparen", regex : /[\[({]|<<|\|\(/ From 119259b7027574052a81e10d9e5947de69b2a86f Mon Sep 17 00:00:00 2001 From: "Matthias S. Benkmann" Date: Sun, 7 Dec 2014 16:54:41 +0100 Subject: [PATCH 06/12] add *~ (backup files created by various editors) to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c6a7ecc9..59ec847b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .DS_Store *.swp *.tmp +*~ # Project files that should not be in the repo .* From cfdb223e7a35f95cd023c5270ce0f6bd8197bb16 Mon Sep 17 00:00:00 2001 From: Adam Jimenez Date: Fri, 12 Dec 2014 10:27:45 +0000 Subject: [PATCH 07/12] Autocomplete exactMatch option Option to show only exact matches in autocomplete --- lib/ace/autocomplete.js | 74 ++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/lib/ace/autocomplete.js b/lib/ace/autocomplete.js index 2544c858..dd04426e 100644 --- a/lib/ace/autocomplete.js +++ b/lib/ace/autocomplete.js @@ -42,6 +42,7 @@ var snippetManager = require("./snippets").snippetManager; var Autocomplete = function() { this.autoInsert = true; this.autoSelect = true; + this.exactMatch = false; this.keyboardHandler = new HashHandler(); this.keyboardHandler.bindKeys(this.commands); @@ -53,7 +54,7 @@ var Autocomplete = function() { this.changeTimer = lang.delayedCall(function() { this.updateCompletions(true); }.bind(this)); - + this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50); }; @@ -72,7 +73,7 @@ var Autocomplete = function() { this.popup.on("changeHoverMarker", this.tooltipTimer.bind(null, null)); return this.popup; }; - + this.getPopup = function() { return this.popup || this.$init(); }; @@ -118,7 +119,7 @@ var Autocomplete = function() { this.gatherCompletionsId += 1; this.popup.hide(); } - + if (this.base) this.base.detach(); this.activated = false; @@ -141,7 +142,7 @@ var Autocomplete = function() { // on IE preventDefault doesn't stop scrollbar from being focussed var el = document.activeElement; var text = this.editor.textInput.getElement() - if (el != text && el.parentNode != this.popup.container + if (el != text && el.parentNode != this.popup.container && el != this.tooltipNode && e.relatedTarget != this.tooltipNode && e.relatedTarget != text ) { @@ -227,7 +228,7 @@ var Autocomplete = function() { this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length); this.base.$insertRight = true; - + var matches = []; var total = editor.completers.length; editor.completers.forEach(function(completer, i) { @@ -297,7 +298,7 @@ var Autocomplete = function() { var prefix = results.prefix; var matches = results && results.matches; - + if (!matches || !matches.length) return detachIfFinished(); @@ -306,6 +307,10 @@ var Autocomplete = function() { return; this.completions = new FilteredList(matches); + + if (this.exactMatch) + this.completions.exactMatch = true; + this.completions.setFilter(prefix); var filtered = this.completions.filtered; @@ -328,7 +333,7 @@ var Autocomplete = function() { this.cancelContextMenu = function() { this.editor.$mouseHandler.cancelContextMenu(); }; - + this.updateDocTooltip = function() { var popup = this.popup; var all = popup.data; @@ -343,14 +348,14 @@ var Autocomplete = function() { }); if (!doc) doc = selected; - + if (typeof doc == "string") doc = {docText: doc} if (!doc || !(doc.docHTML || doc.docText)) return this.hideDocTooltip(); this.showDocTooltip(doc); }; - + this.showDocTooltip = function(item) { if (!this.tooltipNode) { this.tooltipNode = dom.createElement("div"); @@ -360,21 +365,21 @@ var Autocomplete = function() { this.tooltipNode.tabIndex = -1; this.tooltipNode.onblur = this.blurListener.bind(this); } - + var tooltipNode = this.tooltipNode; if (item.docHTML) { tooltipNode.innerHTML = item.docHTML; } else if (item.docText) { tooltipNode.textContent = item.docText; } - + if (!tooltipNode.parentNode) - document.body.appendChild(tooltipNode); + document.body.appendChild(tooltipNode); var popup = this.popup; var rect = popup.container.getBoundingClientRect(); tooltipNode.style.top = popup.container.style.top; tooltipNode.style.bottom = popup.container.style.bottom; - + if (window.innerWidth - rect.right < 320) { tooltipNode.style.right = window.innerWidth - rect.left + "px"; tooltipNode.style.left = ""; @@ -384,7 +389,7 @@ var Autocomplete = function() { } tooltipNode.style.display = "block"; }; - + this.hideDocTooltip = function() { this.tooltipTimer.cancel(); if (!this.tooltipNode) return; @@ -392,7 +397,7 @@ var Autocomplete = function() { if (!this.editor.isFocused() && document.activeElement == el) this.editor.focus(); this.tooltipNode = null; - if (el.parentNode) + if (el.parentNode) el.parentNode.removeChild(el); }; @@ -403,7 +408,7 @@ Autocomplete.startCommand = { exec: function(editor) { if (!editor.completer) editor.completer = new Autocomplete(); - editor.completer.autoInsert = + editor.completer.autoInsert = editor.completer.autoSelect = true; editor.completer.showPopup(editor); // needed for firefox on mac @@ -416,6 +421,7 @@ var FilteredList = function(array, filterText, mutateData) { this.all = array; this.filtered = array; this.filterText = filterText || ""; + this.exactMatch = false; }; (function(){ this.setFilter = function(str) { @@ -452,23 +458,29 @@ var FilteredList = function(array, filterText, mutateData) { var matchMask = 0; var penalty = 0; var index, distance; - // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf - for (var j = 0; j < needle.length; j++) { - // TODO add penalty on case mismatch - var i1 = caption.indexOf(lower[j], lastIndex + 1); - var i2 = caption.indexOf(upper[j], lastIndex + 1); - index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2; - if (index < 0) + + if (this.exactMatch) { + if (needle !== caption.substr(0, needle.length)) continue loop; - distance = index - lastIndex - 1; - if (distance > 0) { - // first char mismatch should be more sensitive - if (lastIndex === -1) - penalty += 10; - penalty += distance; + }else{ + // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf + for (var j = 0; j < needle.length; j++) { + // TODO add penalty on case mismatch + var i1 = caption.indexOf(lower[j], lastIndex + 1); + var i2 = caption.indexOf(upper[j], lastIndex + 1); + index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2; + if (index < 0) + continue loop; + distance = index - lastIndex - 1; + if (distance > 0) { + // first char mismatch should be more sensitive + if (lastIndex === -1) + penalty += 10; + penalty += distance; + } + matchMask = matchMask | (1 << index); + lastIndex = index; } - matchMask = matchMask | (1 << index); - lastIndex = index; } item.matchMask = matchMask; item.exactMatch = penalty ? 0 : 1; From 278f1d1674e002a737c8a74012d1d8160d41d26e Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 21 Nov 2014 00:54:29 +0400 Subject: [PATCH 08/12] add helper for creating tests --- demo/kitchen-sink/dev_util.js | 163 ++++++++++++++++++++++++++++++---- 1 file changed, 147 insertions(+), 16 deletions(-) diff --git a/demo/kitchen-sink/dev_util.js b/demo/kitchen-sink/dev_util.js index 81ab446b..4580d9be 100644 --- a/demo/kitchen-sink/dev_util.js +++ b/demo/kitchen-sink/dev_util.js @@ -29,6 +29,8 @@ * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { +var dom = require("ace/lib/dom"); +var Range = require("ace/range").Range; // allow easy access to ace in console, but not in ace code which uses strict function isStrict() { try { return !arguments.callee.caller.caller.caller} @@ -59,21 +61,150 @@ def(window, "session", function(){ warn(); return window.env.editor.session }); def(window, "split", function(){ warn(); return window.env.split }); -/* for textinput debuggging -dom.importCssString("\ - .ace_text-input {\ - position: absolute;\ - z-index: 10!important;\ - width: 6em!important;\ - height: 1em;\ - opacity: 1!important;\ - background: rgba(0, 92, 255, 0.11);\ - border: none;\ - font: inherit;\ - padding: 0 1px;\ - margin: 0 -1px;\ - text-indent: 0em;\ -}\ -")*/ +def(window, "devUtil", function(){ warn(); return exports }); +exports.showTextArea = function(argument) { + dom.importCssString("\ + .ace_text-input {\ + position: absolute;\ + z-index: 10!important;\ + width: 6em!important;\ + height: 1em;\ + opacity: 1!important;\ + background: rgba(0, 92, 255, 0.11);\ + border: none;\ + font: inherit;\ + padding: 0 1px;\ + margin: 0 -1px;\ + text-indent: 0em;\ + }\ + "); +}; + +exports.addGlobals = function() { + window.oop = require("ace/lib/oop"); + window.dom = require("ace/lib/dom"); + window.Range = require("ace/range").Range; + window.Editor = require("ace/editor").Editor; + window.assert = require("ace/test/asyncjs/assert"); + window.asyncjs = require("ace/test/asyncjs/async"); + window.UndoManager = require("ace/undomanager").UndoManager; + window.EditSession = require("ace/edit_session").EditSession; + window.MockRenderer = require("ace/test/mockrenderer").MockRenderer; + window.EventEmitter = require("ace/lib/event_emitter").EventEmitter; +}; + +exports.recordTestCase = function() { + exports.addGlobals(); + var editor = window.editor; + var testcase = window.testcase = []; + var assert; + function getSelection(editor) { + var data = editor.multiSelect.toJSON(); + if (!data.length) data = [data]; + data = data.map(function(x) { + var a, c; + if (x.isBackwards) { + a = x.end; + c = x.start; + } else { + c = x.end; + a = x.start; + } + return Range.comparePoints(a, c) + ? [a.row, a.column, c.row, c.column] + : [a.row, a.column]; + }); + return data.length > 1 ? data : data[0]; + } + function setSelection(editor, data) { + if (typeof data[0] == "number") + data = [data]; + editor.selection.fromJSON(data.map(function(x) { + var start = {row: x[0], column: x[1]}; + var end = x.length == 2 ? start : {row: x[2], column: x[3]}; + var isBackwards = Range.comparePoints(start, end) > 0; + return isBackwards ? { + start: end, + end: start, + isBackwards: true + } : { + start: start, + end: end, + isBackwards: true + }; + })); + } + function testSelection(editor, data) { + assert.equal(getSelection(editor) + "", data + ""); + } + + testcase.push({ + type: "setValue", + data: editor.getValue() + }, { + type: "setSelection", + data: getSelection(editor) + }); + editor.commands.on("afterExec", function(e) { + testcase.push({ + type: "exec", + data: e + }); + testcase.push({ + type: "value", + data: editor.getValue() + }); + testcase.push({ + type: "selection", + data: getSelection(editor) + }); + }); + editor.on("mouseup", function() { + testcase.push({ + type: "setSelection", + data: getSelection(editor) + }); + }); + + testcase.toString = function() { + var lastValue = ""; + // var lastSelection = "" + var str = this.map(function(x) { + var data = x.data; + switch (x.type) { + case "exec": + return 'editor.execCommand("' + + data.command.name + + (data.args ? '", ' + JSON.stringify(data.args) : '"') + + ')'; + case "setSelection": + return 'setSelection(editor, ' + JSON.stringify(data) + ')'; + case "setValue": + if (lastValue != data) { + lastValue = data; + return 'editor.setValue(' + JSON.stringify(data) + ', -1)'; + } + return; + case "selection": + return 'testSelection(editor, ' + JSON.stringify(data) + ')'; + case "value": + if (lastValue != data) { + lastValue = data; + return 'assert.equal(' + + 'editor.getValue(),' + + JSON.stringify(data) + + ')'; + } + return; + } + }).filter(Boolean).join("\n"); + + return getSelection + "\n" + + testSelection + "\n" + + setSelection + "\n" + + "\n" + str + "\n"; + }; +}; + }); From 852e0673ca4dca473159f848d9b51d5e5bd11e56 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 21 Nov 2014 00:58:25 +0400 Subject: [PATCH 09/12] fix pairing of quotes in cstyle behavior --- lib/ace/mode/behaviour/cstyle.js | 68 ++++++++++++++------------------ 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/lib/ace/mode/behaviour/cstyle.js b/lib/ace/mode/behaviour/cstyle.js index a8a18e72..db8f4a36 100644 --- a/lib/ace/mode/behaviour/cstyle.js +++ b/lib/ace/mode/behaviour/cstyle.js @@ -260,48 +260,38 @@ var CstyleBehaviour = function() { var cursor = editor.getCursorPosition(); var line = session.doc.getLine(cursor.row); var leftChar = line.substring(cursor.column-1, cursor.column); - + var rightChar = line.substring(cursor.column, cursor.column + 1); + + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); // We're escaped. - if (leftChar == '\\') { + if (leftChar == "\\" && token && /escape/.test(token.type)) return null; + + var stringBefore = token && /string/.test(token.type); + var stringAfter = !rightToken || /string/.test(rightToken.type); + + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(leftChar); + if (isWordBefore || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + pair = true; } - - // Find what token we're inside. - var tokens = session.getTokens(selection.start.row); - var col = 0, token; - var quotepos = -1; // Track whether we're inside an open quote. - - for (var x = 0; x < tokens.length; x++) { - token = tokens[x]; - if (token.type == "string") { - quotepos = -1; - } else if (quotepos < 0) { - quotepos = token.value.indexOf(quote); - } - if ((token.value.length + col) > selection.start.column) { - break; - } - col += tokens[x].value.length; - } - - // Try and be smart about when we auto insert. - if (!token || (quotepos < 0 && token.type !== "comment" && (token.type !== "string" || ((selection.start.column !== token.value.length+col-1) && token.value.lastIndexOf(quote) === token.value.length-1)))) { - if (!CstyleBehaviour.isSaneInsertion(editor, session)) - return; - return { - text: quote + quote, - selection: [1,1] - }; - } else if (token && token.type === "string") { - // Ignore input and move right one if we're typing over the closing quote. - var rightChar = line.substring(cursor.column, cursor.column + 1); - if (rightChar == quote) { - return { - text: '', - selection: [1, 1] - }; - } - } + return { + text: pair ? quote + quote : "", + selection: [1,1] + }; } } }); From 3fa54ce97e9d14ebd0b140deb702692c569d67fc Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 21 Nov 2014 01:02:31 +0400 Subject: [PATCH 10/12] fix copyLines* for multiple selections --- demo/kitchen-sink/dev_util.js | 85 ++++++++++++++++++----------------- kitchen-sink.html | 4 +- lib/ace/editor.js | 79 ++++++++++++++++---------------- lib/ace/multi_select_test.js | 70 +++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 79 deletions(-) diff --git a/demo/kitchen-sink/dev_util.js b/demo/kitchen-sink/dev_util.js index 4580d9be..ae9200b7 100644 --- a/demo/kitchen-sink/dev_util.js +++ b/demo/kitchen-sink/dev_util.js @@ -91,53 +91,58 @@ exports.addGlobals = function() { window.EditSession = require("ace/edit_session").EditSession; window.MockRenderer = require("ace/test/mockrenderer").MockRenderer; window.EventEmitter = require("ace/lib/event_emitter").EventEmitter; + + window.getSelection = getSelection; + window.setSelection = setSelection; + window.testSelection = testSelection; }; +function getSelection(editor) { + var data = editor.multiSelect.toJSON(); + if (!data.length) data = [data]; + data = data.map(function(x) { + var a, c; + if (x.isBackwards) { + a = x.end; + c = x.start; + } else { + c = x.end; + a = x.start; + } + return Range.comparePoints(a, c) + ? [a.row, a.column, c.row, c.column] + : [a.row, a.column]; + }); + return data.length > 1 ? data : data[0]; +} +function setSelection(editor, data) { + if (typeof data[0] == "number") + data = [data]; + editor.selection.fromJSON(data.map(function(x) { + var start = {row: x[0], column: x[1]}; + var end = x.length == 2 ? start : {row: x[2], column: x[3]}; + var isBackwards = Range.comparePoints(start, end) > 0; + return isBackwards ? { + start: end, + end: start, + isBackwards: true + } : { + start: start, + end: end, + isBackwards: true + }; + })); +} +function testSelection(editor, data) { + assert.equal(getSelection(editor) + "", data + ""); +} + exports.recordTestCase = function() { exports.addGlobals(); var editor = window.editor; var testcase = window.testcase = []; var assert; - function getSelection(editor) { - var data = editor.multiSelect.toJSON(); - if (!data.length) data = [data]; - data = data.map(function(x) { - var a, c; - if (x.isBackwards) { - a = x.end; - c = x.start; - } else { - c = x.end; - a = x.start; - } - return Range.comparePoints(a, c) - ? [a.row, a.column, c.row, c.column] - : [a.row, a.column]; - }); - return data.length > 1 ? data : data[0]; - } - function setSelection(editor, data) { - if (typeof data[0] == "number") - data = [data]; - editor.selection.fromJSON(data.map(function(x) { - var start = {row: x[0], column: x[1]}; - var end = x.length == 2 ? start : {row: x[2], column: x[3]}; - var isBackwards = Range.comparePoints(start, end) > 0; - return isBackwards ? { - start: end, - end: start, - isBackwards: true - } : { - start: start, - end: end, - isBackwards: true - }; - })); - } - function testSelection(editor, data) { - assert.equal(getSelection(editor) + "", data + ""); - } - + testcase.push({ type: "setValue", data: editor.getValue() diff --git a/kitchen-sink.html b/kitchen-sink.html index 65fe2a2a..a8e1fb7b 100644 --- a/kitchen-sink.html +++ b/kitchen-sink.html @@ -257,7 +257,9 @@ - + diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 78c7c18d..f487b49e 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -1655,9 +1655,7 @@ var Editor = function(renderer, session) { * @related EditSession.moveLinesUp **/ this.moveLinesDown = function() { - this.$moveLines(function(firstRow, lastRow) { - return this.session.moveLinesDown(firstRow, lastRow); - }); + this.$moveLines(1, false); }; /** @@ -1666,9 +1664,7 @@ var Editor = function(renderer, session) { * @related EditSession.moveLinesDown **/ this.moveLinesUp = function() { - this.$moveLines(function(firstRow, lastRow) { - return this.session.moveLinesUp(firstRow, lastRow); - }); + this.$moveLines(-1, false); }; /** @@ -1692,10 +1688,7 @@ var Editor = function(renderer, session) { * **/ this.copyLinesUp = function() { - this.$moveLines(function(firstRow, lastRow) { - this.session.duplicateLines(firstRow, lastRow); - return 0; - }); + this.$moveLines(-1, true); }; /** @@ -1705,51 +1698,61 @@ var Editor = function(renderer, session) { * **/ this.copyLinesDown = function() { - this.$moveLines(function(firstRow, lastRow) { - return this.session.duplicateLines(firstRow, lastRow); - }); + this.$moveLines(1, true); }; /** - * Executes a specific function, which can be anything that manipulates selected lines, such as copying them, duplicating them, or shifting them. - * @param {Function} mover A method to call on each selected row - * + * for internal use + * @ignore * **/ - this.$moveLines = function(mover) { + this.$moveLines = function(dir, copy) { + var rows, moved; var selection = this.selection; if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) { var range = selection.toOrientedRange(); - var rows = this.$getSelectedRows(range); - var linesMoved = mover.call(this, rows.first, rows.last); - range.moveBy(linesMoved, 0); + rows = this.$getSelectedRows(range); + moved = this.session.$moveLines(rows.first, rows.last, copy ? 0 : dir); + if (copy && dir == -1) moved = 0; + range.moveBy(moved, 0); selection.fromOrientedRange(range); } else { var ranges = selection.rangeList.ranges; selection.rangeList.detach(this.session); - - for (var i = ranges.length; i--; ) { + this.inVirtualSelectionMode = true; + + var diff = 0; + var totalDiff = 0; + var l = ranges.length; + for (var i = 0; i < l; i++) { var rangeIndex = i; - var rows = ranges[i].collapseRows(); - var last = rows.end.row; - var first = rows.start.row; - while (i--) { - rows = ranges[i].collapseRows(); - if (first - rows.end.row <= 1) - first = rows.end.row; - else + ranges[i].moveBy(diff, 0); + rows = this.$getSelectedRows(ranges[i]); + var first = rows.first; + var last = rows.last; + while (++i < l) { + if (totalDiff) ranges[i].moveBy(totalDiff, 0); + var subRows = this.$getSelectedRows(ranges[i]); + if (copy && subRows.first != last) break; + else if (!copy && subRows.first > last + 1) + break; + last = subRows.last; } - i++; - - var linesMoved = mover.call(this, first, last); - while (rangeIndex >= i) { - ranges[rangeIndex].moveBy(linesMoved, 0); - rangeIndex--; + i--; + diff = this.session.$moveLines(first, last, copy ? 0 : dir); + if (copy && dir == -1) rangeIndex = i + 1; + while (rangeIndex <= i) { + ranges[rangeIndex].moveBy(diff, 0); + rangeIndex++; } + if (!copy) diff = 0; + totalDiff += diff; } + selection.fromOrientedRange(selection.ranges[0]); selection.rangeList.attach(this.session); + this.inVirtualSelectionMode = false; } }; @@ -1762,8 +1765,8 @@ var Editor = function(renderer, session) { * * @returns {Object} **/ - this.$getSelectedRows = function() { - var range = this.getSelectionRange().collapseRows(); + this.$getSelectedRows = function(range) { + range = (range || this.getSelectionRange()).collapseRows(); return { first: this.session.getRowFoldStart(range.start.row), diff --git a/lib/ace/multi_select_test.js b/lib/ace/multi_select_test.js index 2e71aefa..1eccc4ce 100644 --- a/lib/ace/multi_select_test.js +++ b/lib/ace/multi_select_test.js @@ -51,6 +51,45 @@ var exec = function(name, times, args) { var testRanges = function(str) { assert.equal(editor.selection.getAllRanges() + "", str + ""); }; +function getSelection(editor) { + var data = editor.multiSelect.toJSON(); + if (!data.length) data = [data]; + data = data.map(function(x) { + var a, c; + if (x.isBackwards) { + a = x.end; + c = x.start; + } else { + c = x.end; + a = x.start; + } + return Range.comparePoints(a, c) + ? [a.row, a.column, c.row, c.column] + : [a.row, a.column]; + }); + return data.length > 1 ? data : data[0]; +} +function testSelection(editor, data) { + assert.equal(getSelection(editor) + "", data + ""); +} +function setSelection(editor, data) { + if (typeof data[0] == "number") + data = [data]; + editor.selection.fromJSON(data.map(function(x) { + var start = {row: x[0], column: x[1]}; + var end = x.length == 2 ? start : {row: x[2], column: x[3]}; + var isBackwards = Range.comparePoints(start, end) > 0; + return isBackwards ? { + start: end, + end: start, + isBackwards: true + } : { + start: start, + end: end, + isBackwards: true + }; + })); +} module.exports = { @@ -167,6 +206,37 @@ module.exports = { editor.execCommand('insertfoo'); assert.equal('l1foo\nl2foo', editor.getValue()); }, + + "test multiselect move lines": function() { + editor = new Editor(new MockRenderer()); + + editor.setValue("l1\nl2\nl3\nl4", -1); + setSelection(editor, [[0,2],[1,2],[2,2],[3,2]]); + + exec("copylinesdown"); + assert.equal(editor.getValue(),"l1\nl1\nl2\nl2\nl3\nl3\nl4\nl4"); + testSelection(editor, [[1,2],[3,2],[5,2],[7,2]]); + exec("copylinesup"); + assert.equal(editor.getValue(),"l1\nl1\nl1\nl2\nl2\nl2\nl3\nl3\nl3\nl4\nl4\nl4"); + testSelection(editor, [[1,2],[4,2],[7,2],[10,2]]); + exec("removeline"); + assert.equal(editor.getValue(),"l1\nl1\nl2\nl2\nl3\nl3\nl4\nl4"); + testSelection(editor, [[1,0],[3,0],[5,0],[7,0]]); + + setSelection(editor, [[1,2],[1,0,1,1],[3,0,3,1],[5,0,5,1],[7,0,7,1]]); + exec("copylinesdown"); + exec("copylinesup"); + assert.equal(editor.getValue(),"l1\nl1\nl1\nl1\nl2\nl2\nl2\nl2\nl3\nl3\nl3\nl3\nl4\nl4\nl4\nl4"); + testSelection(editor, [[2,2],[2,0,2,1],[6,0,6,1],[10,0,10,1],[14,0,14,1]]); + + exec("movelinesdown", 12); + assert.equal(editor.getValue(),"l1\nl1\nl1\nl2\nl2\nl2\nl3\nl3\nl3\nl4\nl4\nl4\nl1\nl2\nl3\nl4"); + testSelection(editor, [[12,2],[12,0,12,1],[13,0,13,1],[14,0,14,1],[15,0,15,1]]); + + exec("movelinesup", 12); + assert.equal(editor.getValue(),"l1\nl2\nl3\nl4\nl1\nl1\nl1\nl2\nl2\nl2\nl3\nl3\nl3\nl4\nl4\nl4"); + testSelection(editor, [[0,2],[0,0,0,1],[1,0,1,1],[2,0,2,1],[3,0,3,1]]); + }, "test multiselect fromJSON/toJSON": function() { var doc = new EditSession(["l1", "l2"]); From ec18d9493d89805f702e66854f84c687ac0d6f89 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 21 Nov 2014 01:04:11 +0400 Subject: [PATCH 11/12] fix toggleComment command in php mode --- lib/ace/mode/php.js | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/ace/mode/php.js b/lib/ace/mode/php.js index 2754ef51..c7688d43 100644 --- a/lib/ace/mode/php.js +++ b/lib/ace/mode/php.js @@ -55,6 +55,24 @@ oop.inherits(PhpMode, TextMode); (function() { + this.tokenRe = new RegExp("^[" + + unicode.packages.L + + unicode.packages.Mn + unicode.packages.Mc + + unicode.packages.Nd + + unicode.packages.Pc + "\_]+", "g" + ); + + this.nonTokenRe = new RegExp("^(?:[^" + + unicode.packages.L + + unicode.packages.Mn + unicode.packages.Mc + + unicode.packages.Nd + + unicode.packages.Pc + "\_]|\s])+", "g" + ); + + + this.lineCommentStart = ["//", "#"]; + this.blockComment = {start: "/*", end: "*/"}; + this.getNextLineIndent = function(state, line, tab) { var indent = this.$getIndent(line); @@ -100,8 +118,10 @@ oop.inherits(PhpMode, TextMode); var Mode = function(opts) { if (opts && opts.inline) { - PhpMode.call(this); - return; + var mode = new PhpMode(); + mode.createWorker = this.createWorker; + mode.inlinePhp = true; + return mode; } HtmlMode.call(this); this.HighlightRules = PhpHighlightRules; @@ -116,24 +136,6 @@ oop.inherits(Mode, HtmlMode); (function() { - this.tokenRe = new RegExp("^[" - + unicode.packages.L - + unicode.packages.Mn + unicode.packages.Mc - + unicode.packages.Nd - + unicode.packages.Pc + "\_]+", "g" - ); - - this.nonTokenRe = new RegExp("^(?:[^" - + unicode.packages.L - + unicode.packages.Mn + unicode.packages.Mc - + unicode.packages.Nd - + unicode.packages.Pc + "\_]|\s])+", "g" - ); - - - this.lineCommentStart = ["//", "#"]; - this.blockComment = {start: "/*", end: "*/"}; - this.createWorker = function(session) { var worker = new WorkerClient(["ace"], "ace/mode/php_worker", "PhpWorker"); worker.attachToDocument(session.getDocument()); From 6b13aedf5f45fad451dea7a0bb2431ea9737ce53 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 4 Dec 2014 14:47:17 +0400 Subject: [PATCH 12/12] do not pair quotes inside strings --- lib/ace/mode/behaviour/behaviour_test.js | 9 +++++++++ lib/ace/mode/behaviour/cstyle.js | 2 ++ 2 files changed, 11 insertions(+) diff --git a/lib/ace/mode/behaviour/behaviour_test.js b/lib/ace/mode/behaviour/behaviour_test.js index ded22ccb..ba0a5041 100644 --- a/lib/ace/mode/behaviour/behaviour_test.js +++ b/lib/ace/mode/behaviour/behaviour_test.js @@ -120,6 +120,15 @@ module.exports = { assert.equal(editor.getValue(), "{") exec("insertstring", 1, "\n"); assert.equal(editor.getValue(), "{\n \n}") + + editor.setValue(""); + exec("insertstring", 1, "("); + exec("insertstring", 1, '"'); + exec("insertstring", 1, '"'); + assert.equal(editor.getValue(), '("")'); + exec("backspace", 1); + exec("insertstring", 1, '"'); + assert.equal(editor.getValue(), '("")'); } }; diff --git a/lib/ace/mode/behaviour/cstyle.js b/lib/ace/mode/behaviour/cstyle.js index db8f4a36..5abf08fd 100644 --- a/lib/ace/mode/behaviour/cstyle.js +++ b/lib/ace/mode/behaviour/cstyle.js @@ -277,6 +277,8 @@ var CstyleBehaviour = function() { } else { if (stringBefore && !stringAfter) return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings var wordRe = session.$mode.tokenRe; wordRe.lastIndex = 0; var isWordBefore = wordRe.test(leftChar);