diff --git a/Makefile b/Makefile index 54312a73..579259d1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY : doc build clean dist -build: +pre_build: git rev-parse HEAD > .git-ref mkdir -p build/src mkdir -p build/demo/kitchen-sink @@ -9,7 +9,8 @@ build: cp -r demo/kitchen-sink/styles.css build/demo/kitchen-sink/styles.css cp demo/kitchen-sink/logo.png build/demo/kitchen-sink/logo.png cp -r doc/site/images build/textarea - + +build: pre_build ./Makefile.dryice.js normal ./Makefile.dryice.js demo ./Makefile.dryice.js bm diff --git a/Makefile.dryice.js b/Makefile.dryice.js index da119177..547c5b1a 100755 --- a/Makefile.dryice.js +++ b/Makefile.dryice.js @@ -43,56 +43,61 @@ if (!fs.existsSync) var copy = require('dryice').copy; var ACE_HOME = __dirname; +var BUILD_DIR = "build"; function main(args) { - var target = "minimal"; - if (args.length == 3) { - target = args[2]; - // Check if 'target' contains some allowed value. - if (!/^(normal|bm|demo|minimal)$/.test(target)) - target = "help"; - } + var type = "minimal"; + args = args.map(function(x) { + if (x[0] == "-" && x[1] != "-") + return "-" + x; + return x; + }); + + if (args[2] && (args[2][0] != "-" || args[2].indexOf("h") != -1)) + type = args[2]; - if (target == "help") { - console.log("--- Ace Dryice Build Tool ---"); - console.log(""); - console.log("Options:"); - console.log(" minimal Runs minimal build of Ace"); - console.log(" normal Runs embedded build of Ace"); - console.log(" demo Runs demo build of Ace"); - console.log(" bm Runs bookmarklet build of Ace"); - process.exit(0); - } + var i = args.indexOf("--target"); + if (i != -1 && args[i+1]) + BUILD_DIR = args[i+1]; - var aceProject = { - roots: [ - ACE_HOME + '/lib', - ACE_HOME + '/demo' - ], - textPluginPattern: /^ace\/requirejs\/text!/ - }; - - if (target == "minimal") { - buildAce(aceProject, { - compress: false, - noconflict: false, - suffix: "", - name: "ace" + if (type == "minimal") { + buildAce({ + compress: args.indexOf("-m") != -1, + noconflict: args.indexOf("-nc") != -1 }); + } else if (type == "normal") { + ace(); + } else if (type == "demo") { + demo(); + } else if (type == "bm") { + bookmarklet(); + } else if (type == "full") { + ace(); + demo(); + bookmarklet(); } - if (target == "normal") { - ace(aceProject); - } - else if (target == "demo") { - demo(aceProject); - } - else if (target == "bm") { - bookmarklet(aceProject); - } + + console.log("--- Ace Dryice Build Tool ---"); + console.log(""); + console.log("Options:"); + console.log(" normal Runs embedded build of Ace"); + console.log(" demo Runs demo build of Ace"); + console.log(" bm Runs bookmarklet build of Ace"); + console.log(" full all of above"); + console.log("flags:"); + console.log(" -m minify"); + console.log(" -nc namespace require"); + console.log(" --target ./path path to build folder"); + console.log(""); + if (BUILD_DIR) + console.log(" output generated in " + type + __dirname + "/" + BUILD_DIR) + + process.exit(0); + } -function bookmarklet(aceProject) { - var targetDir = "build/textarea"; +function bookmarklet() { + var targetDir = BUILD_DIR + "/textarea"; copy({ source: "build_support/editor_textarea.html", dest: targetDir + '/editor.html' @@ -102,7 +107,7 @@ function bookmarklet(aceProject) { dest: targetDir + '/style.css' }); - buildAce(aceProject, { + buildAce({ targetDir: targetDir + "/src", ns: "__ace_shadowed__", exportModule: "ace/ext/textarea", @@ -115,38 +120,27 @@ function bookmarklet(aceProject) { }); } -function ace(aceProject) { +function ace() { console.log('# ace ---------'); // uncompressed - buildAce(aceProject, { + buildAce({ compress: false, - noconflict: false, - suffix: "", - name: "ace" + noconflict: false }); - buildAce(aceProject, { + buildAce({ compress: false, - noconflict: true, - suffix: "-noconflict", - name: "ace", - workers: [] + noconflict: true }); // compressed - buildAce(aceProject, { + buildAce({ compress: true, - noconflict: false, - suffix: "-min", - name: "ace", - workers: [] + noconflict: false }); - buildAce(aceProject, { + buildAce({ compress: true, - noconflict: true, - suffix: "-min-noconflict", - name: "ace", - workers: [] + noconflict: true }); console.log('# ace License | Readme | Changelog ---------'); @@ -169,7 +163,7 @@ function ace(aceProject) { }); } -function demo(aceProject) { +function demo() { console.log('# kitchen sink ---------'); var version, ref; @@ -182,14 +176,10 @@ function demo(aceProject) { } var changeComments = function(data) { return (data - .replace("DEVEL-->", "") - .replace("", "") - .replace(" + + + + + + - + + +
@@ -216,8 +226,7 @@
- - + - + ", - next : "start" - }, { - token : "comment", - regex : ".+" - } + { + token : "comment", + regex : ".*?-->", + next : "start" + }, { + token : "comment", + regex : ".+" + } ], "htmltag" : [ { diff --git a/lib/ace/mode/scala_highlight_rules.js b/lib/ace/mode/scala_highlight_rules.js index 67f8e1b2..c39962db 100644 --- a/lib/ace/mode/scala_highlight_rules.js +++ b/lib/ace/mode/scala_highlight_rules.js @@ -75,11 +75,16 @@ var ScalaHighlightRules = function() { token : "string.regexp", regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)" }, { - token : "string", // single line - regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' + token : "string", + regex : '"""', + next : "tstring" }, { - token : "string", // single line - regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" + token : "string", + regex : '"(?=.)', // " strings can't span multiple lines + next : "string" + }, { + token : "symbol.constant", // single line + regex : "'[\\w\\d_]+" }, { token : "constant.numeric", // hex regex : "0[xX][0-9a-fA-F]+\\b" @@ -131,6 +136,36 @@ var ScalaHighlightRules = function() { merge : true, regex : ".+" } + ], + "string" : [ + { + token : "escape", + regex : '\\\\"', + }, { + token : "string", + merge : true, + regex : '"', + next : "start" + }, { + token : "string.invalid", + regex : '[^"\\\\]*$', + next : "start" + }, { + token : "string", + regex : '[^"\\\\]+', + merge : true + } + ], + "tstring" : [ + { + token : "string", // closing comment + regex : '"{3,5}', + next : "start" + }, { + token : "string", // comment spanning whole line + merge : true, + regex : ".+?" + } ] }; diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index 49de33e7..094432bf 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -98,68 +98,6 @@ var Mode = function() { return null; }; - this.highlightSelection = function(editor) { - var session = editor.session; - if (!session.$selectionOccurrences) - session.$selectionOccurrences = []; - - if (session.$selectionOccurrences.length) - this.clearSelectionHighlight(editor); - - var selection = editor.getSelectionRange(); - if (selection.isEmpty() || selection.isMultiLine()) - return; - - var startOuter = selection.start.column - 1; - var endOuter = selection.end.column + 1; - var line = session.getLine(selection.start.row); - var lineCols = line.length; - var needle = line.substring(Math.max(startOuter, 0), - Math.min(endOuter, lineCols)); - - // Make sure the outer characters are not part of the word. - if ((startOuter >= 0 && /^[\w\d]/.test(needle)) || - (endOuter <= lineCols && /[\w\d]$/.test(needle))) - return; - - needle = line.substring(selection.start.column, selection.end.column); - if (!/^[\w\d]+$/.test(needle)) - return; - - var cursor = editor.getCursorPosition(); - - var newOptions = { - wrap: true, - wholeWord: true, - caseSensitive: true, - needle: needle - }; - - var currentOptions = editor.$search.getOptions(); - editor.$search.set(newOptions); - - var ranges = editor.$search.findAll(session); - ranges.forEach(function(range) { - if (!range.contains(cursor.row, cursor.column)) { - var marker = session.addMarker(range, "ace_selected_word", "text"); - session.$selectionOccurrences.push(marker); - } - }); - - editor.$search.set(currentOptions); - }; - - this.clearSelectionHighlight = function(editor) { - if (!editor.session.$selectionOccurrences) - return; - - editor.session.$selectionOccurrences.forEach(function(marker) { - editor.session.removeMarker(marker); - }); - - editor.session.$selectionOccurrences = []; - }; - this.createModeDelegates = function (mapping) { if (!this.$embeds) { return; diff --git a/lib/ace/mouse/default_gutter_handler.js b/lib/ace/mouse/default_gutter_handler.js index 30a5e6f5..fc9b012b 100644 --- a/lib/ace/mouse/default_gutter_handler.js +++ b/lib/ace/mouse/default_gutter_handler.js @@ -67,6 +67,7 @@ function GutterHandler(mouseHandler) { } mouseHandler.captureMouse(e, "selectByLines"); + return e.preventDefault(); }); } diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index 48fe88d7..e5568881 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -42,16 +42,12 @@ define(function(require, exports, module) { "use strict"; var dom = require("../lib/dom"); -var BrowserFocus = require("../lib/browser_focus").BrowserFocus; - +var useragent = require("../lib/useragent"); var DRAG_OFFSET = 5; // pixels - - function DefaultHandlers(mouseHandler) { mouseHandler.$clickSelection = null; - mouseHandler.browserFocus = new BrowserFocus(); var editor = mouseHandler.editor; editor.setDefaultHandler("mousedown", this.onMouseDown.bind(mouseHandler)); @@ -61,7 +57,7 @@ function DefaultHandlers(mouseHandler) { editor.setDefaultHandler("mousewheel", this.onScroll.bind(mouseHandler)); var exports = ["select", "startSelect", "drag", "dragEnd", "dragWait", - "dragWaitEnd", "startDrag"]; + "dragWaitEnd", "startDrag", "focusWait"]; exports.forEach(function(x) { mouseHandler[x] = this[x]; @@ -69,44 +65,43 @@ function DefaultHandlers(mouseHandler) { mouseHandler.selectByLines = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); mouseHandler.selectByWords = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); + + mouseHandler.$focusWaitTimout = 250; } (function() { this.onMouseDown = function(ev) { - this.mousedownEvent = ev; var inSelection = ev.inSelection(); var pos = ev.getDocumentPosition(); + this.mousedownEvent = ev; var editor = this.editor; var _self = this; - this.ev = ev - var selectionRange = editor.getSelectionRange(); - var selectionEmpty = selectionRange.isEmpty(); - var button = ev.getButton(); if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + if (selectionEmpty) { editor.moveCursorToPosition(pos); editor.selection.clearSelection(); } - // 2: contextmenu, 1: linux paste - this.moveTextarea = function() { - editor.textInput.onContextMenu({x: _self.x, y: _self.y}); - }; - this.moveTextareaEnd = editor.textInput.onContextMenuClose; - - editor.textInput.onContextMenu({x: this.x, y: this.y}, selectionEmpty); - this.captureMouse(ev, "moveTextarea"); - return; + // 2: contextmenu, 1: linux paste + editor.textInput.onContextMenu(ev.domEvent); + return ev.stop(); } // if this click caused the editor to be focused should not clear the // selection if (inSelection && !editor.isFocused()) { editor.focus(); - return; + if (this.$focusWaitTimout && !this.$clickSelection) { + this.setState("focusWait"); + this.captureMouse(ev); + return ev.preventDefault(); + } } if (!inSelection || this.$clickSelection || ev.getShiftKey()) { @@ -123,7 +118,8 @@ function DefaultHandlers(mouseHandler) { } } - this.captureMouse(ev) + this.captureMouse(ev); + return ev.preventDefault(); }; this.startSelect = function(pos) { @@ -217,6 +213,14 @@ function DefaultHandlers(mouseHandler) { editor.keyBinding.addKeyboardHandler(this.$dragKeybinding); }; + this.focusWait = function() { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + var time = (new Date()).getTime(); + + if (distance > DRAG_OFFSET ||time - this.mousedownEvent.time > this.$focusWaitTimout) + this.startSelect(); + }; + this.dragWait = function() { var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); var time = (new Date()).getTime(); @@ -224,7 +228,7 @@ function DefaultHandlers(mouseHandler) { if (distance > DRAG_OFFSET) { this.startSelect(); - } else if ((time - this.mousedownEvent.time) > editor.getDragDelay()) { + } else if (time - this.mousedownEvent.time > editor.getDragDelay()) { this.startDrag() } }; diff --git a/lib/ace/mouse/dragdrop.js b/lib/ace/mouse/dragdrop.js index e69de29b..8fe5cb5e 100644 --- a/lib/ace/mouse/dragdrop.js +++ b/lib/ace/mouse/dragdrop.js @@ -0,0 +1,105 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var event = require("../lib/event"); + +var DragdropHandler = function(mouseHandler) { + var editor = mouseHandler.editor; + var dragSelectionMarker, x, y; + var timerId, range, isBackwards; + var dragCursor, counter = 0; + + var mouseTarget = editor.container; + event.addListener(mouseTarget, "dragenter", function(e) {console.log(e.type, counter,e.target); + counter++; + if (!dragSelectionMarker) { + range = editor.getSelectionRange(); + isBackwards = editor.selection.isBackwards(); + var style = editor.getSelectionStyle(); + dragSelectionMarker = editor.session.addMarker(range, "ace_selection", style); + editor.clearSelection(); + clearInterval(timerId); + timerId = setInterval(onDragInterval, 20); + } + return event.preventDefault(e); + }); + + event.addListener(mouseTarget, "dragover", function(e) { + x = e.clientX; + y = e.clientY; + return event.preventDefault(e); + }); + + var onDragInterval = function() { + dragCursor = editor.renderer.screenToTextCoordinates(x, y); + editor.moveCursorToPosition(dragCursor); + editor.renderer.scrollCursorIntoView(); + }; + + event.addListener(mouseTarget, "dragleave", function(e) {console.log(e.type, counter,e.target); + counter--; + if (counter > 0) + return; + console.log(e.type, counter,e.target); + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + editor.selection.setSelectionRange(range, isBackwards); + return event.preventDefault(e); + }); + + event.addListener(mouseTarget, "drop", function(e) { + console.log(e.type, counter,e.target); + counter = 0; + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + + range.end = editor.session.insert(dragCursor, e.dataTransfer.getData('Text')); + range.start = dragCursor; + editor.focus(); + editor.selection.setSelectionRange(range); + return event.preventDefault(e); + }); + +}; + +exports.DragdropHandler = DragdropHandler; +}); diff --git a/lib/ace/mouse/mouse_handler.js b/lib/ace/mouse/mouse_handler.js index a85e9672..e1e7a2a1 100644 --- a/lib/ace/mouse/mouse_handler.js +++ b/lib/ace/mouse/mouse_handler.js @@ -44,28 +44,24 @@ var event = require("../lib/event"); var DefaultHandlers = require("./default_handlers").DefaultHandlers; var DefaultGutterHandler = require("./default_gutter_handler").GutterHandler; var MouseEvent = require("./mouse_event").MouseEvent; +var DragdropHandler = require("./dragdrop").DragdropHandler; var MouseHandler = function(editor) { this.editor = editor; new DefaultHandlers(this); new DefaultGutterHandler(this); + new DragdropHandler(this); event.addListener(editor.container, "mousedown", function(e) { editor.focus(); return event.preventDefault(e); }); - event.addListener(editor.container, "selectstart", function(e) { - return event.preventDefault(e); - }); var mouseTarget = editor.renderer.getMouseEventTarget(); - event.addListener(mouseTarget, "mousedown", this.onMouseEvent.bind(this, "mousedown")); event.addListener(mouseTarget, "click", this.onMouseEvent.bind(this, "click")); event.addListener(mouseTarget, "mousemove", this.onMouseMove.bind(this, "mousemove")); - event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseEvent.bind(this, "dblclick")); - event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseEvent.bind(this, "tripleclick")); - event.addMultiMouseDownListener(mouseTarget, 0, 4, 600, this.onMouseEvent.bind(this, "quadclick")); + event.addMultiMouseDownListener(mouseTarget, [300, 300, 250], this, "onMouseEvent"); event.addMouseWheelListener(editor.container, this.onMouseWheel.bind(this, "mousewheel")); var gutterEl = editor.renderer.$gutter; @@ -129,8 +125,9 @@ var MouseHandler = function(editor) { this.y = ev.y; // do not move textarea during selection - var kt = this.editor.renderer.$keepTextAreaAtCursor; - this.editor.renderer.$keepTextAreaAtCursor = false; + var renderer = this.editor.renderer; + if (renderer.$keepTextAreaAtCursor) + renderer.$keepTextAreaAtCursor = null; var self = this; var onMouseSelection = function(e) { @@ -142,8 +139,10 @@ var MouseHandler = function(editor) { clearInterval(timerId); self[self.state + "End"] && self[self.state + "End"](e); self.$clickSelection = null; - self.editor.renderer.$keepTextAreaAtCursor = kt; - self.editor.renderer.$moveTextAreaToCursor(); + if (renderer.$keepTextAreaAtCursor == null) { + renderer.$keepTextAreaAtCursor = true; + renderer.$moveTextAreaToCursor(); + } }; var onSelectionInterval = function() { @@ -152,8 +151,6 @@ var MouseHandler = function(editor) { event.capture(this.editor.container, onMouseSelection, onMouseSelectionEnd); var timerId = setInterval(onSelectionInterval, 20); - - ev.preventDefault(); }; }).call(MouseHandler.prototype); diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index 3560bbb3..9cbc5417 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -43,7 +43,8 @@ var Range = require("./range").Range; var Selection = require("./selection").Selection; var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; var event = require("./lib/event"); -exports.commands = require("./commands/multi_select_commands"); +var commands = require("./commands/multi_select_commands"); +exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands); // Todo: session.find or editor.findVolatile that returns range var Search = require("./search").Search; @@ -397,7 +398,7 @@ var Editor = require("./editor").Editor; this.inMultiSelectMode = true; this.setStyle("multiselect"); - this.keyBinding.addKeyboardHandler(exports.commands.keyboardHandler); + this.keyBinding.addKeyboardHandler(commands.keyboardHandler); this.commands.on("exec", this.$onMultiSelectExec); this.renderer.updateCursor(); @@ -410,7 +411,7 @@ var Editor = require("./editor").Editor; this.inMultiSelectMode = false; this.unsetStyle("multiselect"); - this.keyBinding.removeKeyboardHandler(exports.commands.keyboardHandler); + this.keyBinding.removeKeyboardHandler(commands.keyboardHandler); this.commands.removeEventListener("exec", this.$onMultiSelectExec); this.renderer.updateCursor(); @@ -420,6 +421,8 @@ var Editor = require("./editor").Editor; this.$onMultiSelectExec = function(e) { var command = e.command; var editor = e.editor; + if (!editor.multiSelect) + return; if (!command.multiSelectAction) { command.exec(editor, e.args || {}); editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); @@ -724,7 +727,7 @@ function MultiSelect(editor) { editor.on("changeSession", exports.onSessionChange.bind(editor)); editor.on("mousedown", onMouseDown); - editor.commands.addCommands(exports.commands.defaultCommands); + editor.commands.addCommands(commands.defaultCommands); addAltCursorListeners(editor); } diff --git a/lib/ace/range_list.js b/lib/ace/range_list.js index 185ca5a6..8f6f34f6 100644 --- a/lib/ace/range_list.js +++ b/lib/ace/range_list.js @@ -196,10 +196,9 @@ var RangeList = function() { var lineDif = endRow - startRow; var colDiff = -start.column + end.column; - var ranges = this.ranges; - for (var i=0, n = ranges.length; i < n; i++) { + for (var i = 0, n = ranges.length; i < n; i++) { var r = ranges[i]; if (r.end.row < startRow) continue; diff --git a/lib/ace/search.js b/lib/ace/search.js index dedcadea..c03391e2 100644 --- a/lib/ace/search.js +++ b/lib/ace/search.js @@ -54,35 +54,25 @@ var Range = require("./range").Range; /** * new Search() * - * Creates a new `Search` object. The search options contain the following defaults: + * Creates a new `Search` object. The following search options are avaliable: * - * * `needle`: `""` - * * `backwards`: `false` - * * `wrap`: `false` - * * `caseSensitive`: `false` - * * `wholeWord`: `false` - * * `scope`: `ALL` - * * `regExp`: `false` + * * needle: string or regular expression + * * backwards: false + * * wrap: false + * * caseSensitive: false + * * wholeWord: false + * * range: Range or null for whole document + * * regExp: false + * * start: Range or position + * * skipCurrent: false * **/ var Search = function() { - this.$options = { - needle: "", - backwards: false, - wrap: false, - caseSensitive: false, - wholeWord: false, - scope: Search.ALL, - regExp: false - }; + this.$options = {}; }; -Search.ALL = 1; -Search.SELECTION = 2; - (function() { - /** * Search.set(options) -> Search * - options (Object): An object containing all the new search properties @@ -105,6 +95,9 @@ Search.SELECTION = 2; return lang.copyObject(this.$options); }; + this.setOptions = function(options) { + this.$options = options; + }; /** * Search.find(session) -> Range * - session (EditSession): The session to search with @@ -113,17 +106,18 @@ Search.SELECTION = 2; * **/ this.find = function(session) { - if (!this.$options.needle) - return null; - - var iterator = this.$matchIterator(session); + var iterator = this.$matchIterator(session, this.$options); if (!iterator) return false; var firstRange = null; - iterator.forEach(function(range) { - firstRange = range; + iterator.forEach(function(range, row, offset) { + if (!range.start) { + var column = range.offset + (offset || 0); + firstRange = new Range(row, column, row, column+range.length); + } else + firstRange = range; return true; }); @@ -141,23 +135,53 @@ Search.SELECTION = 2; var options = this.$options; if (!options.needle) return []; + this.$assembleRegExp(options); - var iterator = this.$matchIterator(session); - - if (!iterator) - return false; - - var ignoreCursor = !options.start && options.wrap && options.scope == Search.ALL; - if (ignoreCursor) - options.start = {row: 0, column: 0}; + if (options.range) { + var range = options.range; + var lines = session.getLines(range.start.row, range.end.row); + } else + var lines = session.doc.getAllLines(); var ranges = []; - iterator.forEach(function(range) { - ranges.push(range); - }); + var re = options.re; + if (options.$isMultiLine) { + var len = re.length; + var maxRow = lines.length - len; + for (var row = re.offset || 0; row < maxRow; row++) { + for (var j = 0; j < re.length; j++) + if (lines[row + j].search(re[j]) == -1) + break; - if (ignoreCursor) - options.start = null; + var startIndex = lines[row + j].match(re[0])[0].length; + var endIndex = line.match(re[len - 1])[0].length; + + ranges.push(new Range( + row, startLine.length - startIndex, + row + len - 1, endIndex + )); + } + } else { + for (var i = 0; i < lines.length; i++) { + var matches = lang.getMatchOffsets(lines[i], re); + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + ranges.push(new Range(i, match.offset, i, match.offset + match.length)); + }; + } + } + + if (options.range) { + var startColumn = range.start.column; + var endColumn = range.start.column; + var i = 0, j = ranges.length - 1; + while (i < j && ranges[i].start.column < startColumn && ranges[i].start.row == range.start.row) + i++; + + while (i < j && ranges[j].end.column > endColumn && ranges[j].end.row == range.end.row) + j--; + return ranges.slice(i, j + 1); + } return ranges; }; @@ -173,252 +197,195 @@ Search.SELECTION = 2; * **/ this.replace = function(input, replacement) { - if (!this.$options.regExp) - return input == this.$options.needle ? replacement : null; - - var re = this.$assembleRegExp(); + var options = this.$options; + + var re = this.$assembleRegExp(options); + if (options.$isMultiLine) + return replacement; + if (!re) return; var match = re.exec(input); - if (match && match[0].length == input.length) { - return input.replace(re, replacement); - } - else { + if (!match || match[0].length != input.length) return null; + + replacement = input.replace(re, replacement) + if (options.preserveCase) { + replacement = replacement.split(""); + for (var i = Math.min(input.length, input.length); i--; ) { + var ch = input[i]; + if (ch && ch.toLowerCase() != ch) + replacement[i] = replacement[i].toUpperCase(); + else + replacement[i] = replacement[i].toLowerCase(); + } + replacement = replacement.join(""); } + + return replacement; }; /** internal, hide * Search.$matchIterator(session) -> String | Boolean * - session (EditSession): The session to search with * - * - * **/ - this.$matchIterator = function(session) { - var re = this.$assembleRegExp(); + this.$matchIterator = function(session, options) { + var re = this.$assembleRegExp(options); if (!re) return false; - var self = this, callback, backwards = this.$options.backwards; + var self = this, callback, backwards = options.backwards; - if (this.$options.$isMultiLine) { - var matchIterator = function(line, startIndex, row) { - var startLine = line; - if (startIndex) - line = line.substring(startIndex); - - var len = re.length; - var part = re[0]; - if (line.slice(-part.length) != part) + if (options.$isMultiLine) { + var len = re.length; + var matchIterator = function(line, row, offset) { + var startIndex = line.search(re[0]); + if (startIndex == -1) return; - - for (var i = 1; i < len - 1; i++) - if (re[i] != session.getLine(row + i)) + for (var i = 1; i < len; i++) { + line = session.getLine(row + i); + if (line.search(re[i]) == -1) return; + } - part = re[len - 1]; - if (session.getLine(row + len - 1).slice(0, part.length) != part) - return; + var endIndex = line.match(re[len - 1])[0].length; + + var range = new Range(row, startIndex, row + len - 1, endIndex); + if (re.offset == 1) { + range.start.row--; + range.start.column = Number.MAX_VALUE; + } else if (offset) + range.start.column += offset; - var range = new Range( - row, startLine.length - re[0].length, - row + len - 1, re[len - 1].length - ); if (callback(range)) return true; } } else if (backwards) { - var matchIterator = function(line, startIndex, row) { - if (startIndex) - line = line.substring(startIndex); - - var matches = []; - - line.replace(re, function(str) { - var offset = arguments[arguments.length-2]; - matches.push({ - str: str, - offset: startIndex + offset - }); - return str; - }); - - for (var i=matches.length-1; i>= 0; i--) { - var match = matches[i]; - var range = self.$rangeFromMatch(row, match.offset, match.str.length); - if (callback(range)) + var matchIterator = function(line, row, startIndex) { + var matches = lang.getMatchOffsets(line, re); + for (var i = matches.length-1; i >= 0; i--) + if (callback(matches[i], row, startIndex)) return true; - } } } else { - var matchIterator = function(line, startIndex, row) { - if (startIndex) - line = line.substring(startIndex); - - var matches = []; - - line.replace(re, function(str) { - var offset = arguments[arguments.length-2]; - matches.push({ - str: str, - offset: startIndex + offset - }); - return str; - }); - - for (var i=0; i lastRow) { - if (wrap) { - row = firstRow; - startIndex = firstColumn; - inWrap = true; - } else { - return; - } - } - - if (row == start.row) - stop = true; - - line = getLine(row); - } + for (row = firstRow, lastRow = start.row; row <= lastRow; row++) + if (callback(session.getLine(row), row)) + return; } } else { var forEach = function(callback) { var row = start.row; var line = session.getLine(row).substring(0, start.column); - var startIndex = 0; - var stop = false; - var inWrap = false; + if (callback(line, row)) + return; - while (!callback(line, startIndex, row)) { - if (stop) + for (row--; row >= firstRow; row--) + if (callback(session.getLine(row), row)) return; - row--; - startIndex = 0; + if (options.wrap == false) + return; - if (row < firstRow) { - if (wrap) { - row = lastRow; - inWrap = true; - } else { - return; - } - } - - if (row == start.row) - stop = true; - - line = session.getLine(row); - if (searchSelection) { - if (row == firstRow) - startIndex = firstColumn; - else if (row == lastRow) - line = line.substring(0, range.end.column); - } - - if (inWrap && row == start.row) - startIndex = start.column; - } + for (row = lastRow, firstRow = start.row; row >= firstRow; row--) + if (callback(session.getLine(row), row)) + return; } } diff --git a/lib/ace/search_highlight.js b/lib/ace/search_highlight.js new file mode 100644 index 00000000..b2df7792 --- /dev/null +++ b/lib/ace/search_highlight.js @@ -0,0 +1,87 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Harutyun Amirjanyan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var lang = require("./lib/lang"); +var oop = require("./lib/oop"); +var Range = require("./range").Range; + +var SearchHighlight = function(regExp, clazz, type) { + this.setRegexp(regExp); + this.clazz = clazz; + this.type = type || "text"; +}; + +(function() { + this.setRegexp = function(regExp) { + if (this.regExp+"" == regExp+"") + return; + this.regExp = regExp; + this.cache = []; + }; + + this.update = function(html, markerLayer, session, config) { + if (!this.regExp) + return; + var start = config.firstRow, end = config.lastRow; + + for (var i = start; i <= end; i++) { + var ranges = this.cache[i]; + if (ranges == null) { + ranges = lang.getMatchOffsets(session.getLine(i), this.regExp); + ranges = ranges.map(function(match) { + return new Range(i, match.offset, i, match.offset + match.length); + }); + this.cache[i] = ranges.length ? ranges : ""; + } + + for (var j = ranges.length; j --; ) { + markerLayer.drawSingleLineMarker( + html, ranges[j].toScreenRange(session), this.clazz, config, + null, this.type + ); + } + } + }; + +}).call(SearchHighlight.prototype); + +exports.SearchHighlight = SearchHighlight; +}); diff --git a/lib/ace/search_test.js b/lib/ace/search_test.js index e1f02c46..7a28d89c 100644 --- a/lib/ace/search_test.js +++ b/lib/ace/search_test.js @@ -52,7 +52,6 @@ module.exports = { var search = new Search(); search.set({ needle: "juhu", - scope: Search.ALL }); }, @@ -90,7 +89,7 @@ module.exports = { assert.position(range.end, 1, 12); }, - "test: wrap search is off by default" : function() { + "test: wrap search is on by default" : function() { var session = new EditSession(["abc", "juhu kinners 123", "456"]); session.getSelection().moveCursorTo(2, 1); @@ -98,7 +97,7 @@ module.exports = { needle: "kinners" }); - assert.equal(search.find(session), null); + assert.notEqual(search.find(session), null); }, "test: wrap search should wrap at file end" : function() { @@ -115,6 +114,20 @@ module.exports = { assert.position(range.end, 1, 12); }, + "test: wrap search should find needle even if it starts inside it" : function() { + var session = new EditSession(["abc", "juhu kinners 123", "456"]); + session.getSelection().moveCursorTo(6, 1); + + var search = new Search().set({ + needle: "kinners", + wrap: true + }); + + var range = search.find(session); + assert.position(range.start, 1, 5); + assert.position(range.end, 1, 12); + }, + "test: wrap search with no match should return 'null'": function() { var session = new EditSession(["abc", "juhu kinners 123", "456"]); session.getSelection().moveCursorTo(2, 1); @@ -183,13 +196,19 @@ module.exports = { var search = new Search().set({ needle: "juhu", wrap: true, - scope: Search.SELECTION + range: session.getSelection().getRange() }); var range = search.find(session); assert.position(range.start, 1, 0); assert.position(range.end, 1, 4); + search = new Search().set({ + needle: "juhu", + wrap: true, + range: session.getSelection().getRange() + }); + session.getSelection().setSelectionAnchor(0, 2); session.getSelection().selectTo(3, 2); @@ -201,24 +220,32 @@ module.exports = { "test: find backwards in selection": function() { var session = new EditSession(["juhu", "juhu", "juhu", "juhu"]); + session.getSelection().setSelectionAnchor(0, 2); + session.getSelection().selectTo(3, 2); + var search = new Search().set({ needle: "juhu", wrap: true, backwards: true, - scope: Search.SELECTION + range: session.getSelection().getRange() }); - session.getSelection().setSelectionAnchor(0, 2); - session.getSelection().selectTo(3, 2); - var range = search.find(session); assert.position(range.start, 2, 0); assert.position(range.end, 2, 4); + search = new Search().set({ + needle: "juhu", + wrap: true, + range: session.getSelection().getRange() + }); + session.getSelection().setSelectionAnchor(0, 2); session.getSelection().selectTo(1, 2); - assert.equal(search.find(session), null); + var range = search.find(session); + assert.position(range.start, 1, 0); + assert.position(range.end, 1, 4); }, "test: edge case - match directly before the cursor" : function() { @@ -295,15 +322,15 @@ module.exports = { "test: find all matches in selection" : function() { var session = new EditSession(["juhu", "juhu", "juhu", "juhu"]); + session.getSelection().setSelectionAnchor(0, 2); + session.getSelection().selectTo(3, 2); + var search = new Search().set({ needle: "uh", wrap: true, - scope: Search.SELECTION + range: session.getSelection().getRange() }); - session.getSelection().setSelectionAnchor(0, 2); - session.getSelection().selectTo(3, 2); - var ranges = search.findAll(session); assert.equal(ranges.length, 2); @@ -322,6 +349,11 @@ module.exports = { assert.equal(search.replace("", "kinners"), null); assert.equal(search.replace(" juhu", "kinners"), null); + // case sensitivity + assert.equal(search.replace("Juhu", "kinners"), "kinners"); + search.set({caseSensitive: true}); + assert.equal(search.replace("Juhu", "kinners"), null); + // regexp replacement }, @@ -356,7 +388,6 @@ module.exports = { needle: "[ ]+$", regExp: true, wrap: true, - scope: Search.ALL }); session.getSelection().moveCursorTo(1, 2); @@ -404,12 +435,12 @@ module.exports = { var ranges = search.findAll(session); assert.equal(ranges.length, 3); - assert.position(ranges[0].start, 0, 23); - assert.position(ranges[0].end, 0, 26); + assert.position(ranges[2].start, 0, 23); + assert.position(ranges[2].end, 0, 26); assert.position(ranges[1].start, 0, 8); assert.position(ranges[1].end, 0, 11); - assert.position(ranges[2].start, 0, 0); - assert.position(ranges[2].end, 0, 3); + assert.position(ranges[0].start, 0, 0); + assert.position(ranges[0].end, 0, 3); }, }; diff --git a/lib/ace/selection.js b/lib/ace/selection.js index 5def4061..8f6e3143 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -55,7 +55,7 @@ var Range = require("./range").Range; /** * new Selection(session) * - session (EditSession): The session to use - * + * * Creates a new `Selection` object. * **/ @@ -88,7 +88,7 @@ var Selection = function(session) { /** * Selection.isEmpty() -> Boolean - * + * * Returns `true` if the selection is empty. **/ this.isEmpty = function() { @@ -100,7 +100,7 @@ var Selection = function(session) { /** * Selection.isMultiLine() -> Boolean - * + * * Returns `true` if the selection is a multi-line. **/ this.isMultiLine = function() { @@ -113,7 +113,7 @@ var Selection = function(session) { /** * Selection.getCursor() -> Number - * + * * Gets the current position of the cursor. **/ this.getCursor = function() { @@ -121,7 +121,7 @@ var Selection = function(session) { }; /** - * Selection.setSelectionAnchor(row, column) + * Selection.setSelectionAnchor(row, column) * - row (Number): The new row * - column (Number): The new column * @@ -138,7 +138,7 @@ var Selection = function(session) { /** related to: Anchor.getPosition * Selection.getSelectionAnchor() -> Object - * + * * Returns an object containing the `row` and `column` of the calling selection anchor. * **/ @@ -149,9 +149,9 @@ var Selection = function(session) { return this.anchor.getPosition(); }; - /** + /** * Selection.getSelectionLead() -> Object - * + * * Returns an object containing the `row` and `column` of the calling selection lead. **/ this.getSelectionLead = function() { @@ -159,9 +159,9 @@ var Selection = function(session) { }; /** - * Selection.shiftSelection(columns) + * Selection.shiftSelection(columns) * - columns (Number): The number of columns to shift by - * + * * Shifts the selection up (or down, if [[Selection.isBackwards `isBackwards()`]] is true) the given number of columns. * **/ @@ -188,7 +188,7 @@ var Selection = function(session) { /** * Selection.isBackwards() -> Boolean - * + * * Returns `true` if the selection is going backwards in the document. **/ this.isBackwards = function() { @@ -199,7 +199,7 @@ var Selection = function(session) { /** * Selection.getRange() -> Range - * + * * [Returns the [[Range `Range`]] for the selected text.]{: #Selection.getRange} **/ this.getRange = function() { @@ -218,8 +218,8 @@ var Selection = function(session) { }; /** - * Selection.clearSelection() - * + * Selection.clearSelection() + * * [Empties the selection (by de-selecting it). This function also emits the `'changeSelection'` event.]{: #Selection.clearSelection} **/ this.clearSelection = function() { @@ -230,24 +230,25 @@ var Selection = function(session) { }; /** - * Selection.selectAll() - * + * Selection.selectAll() + * * Selects all the text in the document. **/ this.selectAll = function() { var lastRow = this.doc.getLength() - 1; - this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length); - this.moveCursorTo(0, 0); + this.setSelectionAnchor(0, 0); + this.moveCursorTo(lastRow, this.doc.getLine(lastRow).length); }; /** - * Selection.setSelectionRange(range, reverse) + * Selection.setSelectionRange(range, reverse) * - range (Range): The range of text to select * - reverse (Boolean): Indicates if the range should go backwards (`true`) or not * * Sets the selection to the provided range. * **/ + this.setRange = this.setSelectionRange = function(range, reverse) { if (reverse) { this.setSelectionAnchor(range.end.row, range.end.column); @@ -268,7 +269,7 @@ var Selection = function(session) { }; /** - * Selection.selectTo(row, column) + * Selection.selectTo(row, column) * - row (Number): The row to select to * - column (Number): The column to select to * @@ -282,9 +283,9 @@ var Selection = function(session) { }; /** - * Selection.selectToPosition(pos) + * Selection.selectToPosition(pos) * - pos (Object): An object containing the row and column - * + * * Moves the selection cursor to the row and column indicated by `pos`. * **/ @@ -295,8 +296,8 @@ var Selection = function(session) { }; /** - * Selection.selectUp() - * + * Selection.selectUp() + * * Moves the selection up one row. **/ this.selectUp = function() { @@ -304,8 +305,8 @@ var Selection = function(session) { }; /** - * Selection.selectDown() - * + * Selection.selectDown() + * * Moves the selection down one row. **/ this.selectDown = function() { @@ -313,8 +314,8 @@ var Selection = function(session) { }; /** - * Selection.selectRight() - * + * Selection.selectRight() + * * Moves the selection right one column. **/ this.selectRight = function() { @@ -322,8 +323,8 @@ var Selection = function(session) { }; /** - * Selection.selectLeft() - * + * Selection.selectLeft() + * * Moves the selection left one column. **/ this.selectLeft = function() { @@ -331,8 +332,8 @@ var Selection = function(session) { }; /** - * Selection.selectLineStart() - * + * Selection.selectLineStart() + * * Moves the selection to the beginning of the current line. **/ this.selectLineStart = function() { @@ -340,8 +341,8 @@ var Selection = function(session) { }; /** - * Selection.selectLineEnd() - * + * Selection.selectLineEnd() + * * Moves the selection to the end of the current line. **/ this.selectLineEnd = function() { @@ -349,8 +350,8 @@ var Selection = function(session) { }; /** - * Selection.selectFileEnd() - * + * Selection.selectFileEnd() + * * Moves the selection to the end of the file. **/ this.selectFileEnd = function() { @@ -358,8 +359,8 @@ var Selection = function(session) { }; /** - * Selection.selectFileStart() - * + * Selection.selectFileStart() + * * Moves the selection to the start of the file. **/ this.selectFileStart = function() { @@ -367,8 +368,8 @@ var Selection = function(session) { }; /** - * Selection.selectWordRight() - * + * Selection.selectWordRight() + * * Moves the selection to the first word on the right. **/ this.selectWordRight = function() { @@ -376,8 +377,8 @@ var Selection = function(session) { }; /** - * Selection.selectWordLeft() - * + * Selection.selectWordLeft() + * * Moves the selection to the first word on the left. **/ this.selectWordLeft = function() { @@ -385,8 +386,8 @@ var Selection = function(session) { }; /** related to: EditSession.getWordRange - * Selection.selectWord() - * + * Selection.selectWord() + * * Moves the selection to highlight the entire word. **/ this.getWordRange = function(row, column) { @@ -403,8 +404,8 @@ var Selection = function(session) { }; /** related to: EditSession.getAWordRange - * Selection.selectAWord() - * + * Selection.selectAWord() + * * Selects a word, including its right whitespace. **/ this.selectAWord = function() { @@ -430,9 +431,9 @@ var Selection = function(session) { return new Range(rowStart, 0, rowEnd + 1, 0); }; - /** - * Selection.selectLine() - * + /** + * Selection.selectLine() + * * Selects the entire line. **/ this.selectLine = function() { @@ -440,8 +441,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorUp() - * + * Selection.moveCursorUp() + * * Moves the cursor up one row. **/ this.moveCursorUp = function() { @@ -449,8 +450,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorDown() - * + * Selection.moveCursorDown() + * * Moves the cursor down one row. **/ this.moveCursorDown = function() { @@ -458,8 +459,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorLeft() - * + * Selection.moveCursorLeft() + * * Moves the cursor left one column. **/ this.moveCursorLeft = function() { @@ -484,8 +485,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorRight() - * + * Selection.moveCursorRight() + * * Moves the cursor right one column. **/ this.moveCursorRight = function() { @@ -510,8 +511,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorLineStart() - * + * Selection.moveCursorLineStart() + * * Moves the cursor to the start of the line. **/ this.moveCursorLineStart = function() { @@ -543,8 +544,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorLineEnd() - * + * Selection.moveCursorLineEnd() + * * Moves the cursor to the end of the line. **/ this.moveCursorLineEnd = function() { @@ -558,8 +559,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorFileEnd() - * + * Selection.moveCursorFileEnd() + * * Moves the cursor to the end of the file. **/ this.moveCursorFileEnd = function() { @@ -569,8 +570,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorFileStart() - * + * Selection.moveCursorFileStart() + * * Moves the cursor to the start of the file. **/ this.moveCursorFileStart = function() { @@ -578,8 +579,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorLongWordRight() - * + * Selection.moveCursorLongWordRight() + * * Moves the cursor to the word on the right. **/ this.moveCursorLongWordRight = function() { @@ -598,14 +599,14 @@ var Selection = function(session) { this.moveCursorTo(fold.end.row, fold.end.column); return; } - + // first skip space if (match = this.session.nonTokenRe.exec(rightOfCursor)) { column += this.session.nonTokenRe.lastIndex; this.session.nonTokenRe.lastIndex = 0; rightOfCursor = line.substring(column); } - + // if at line end proceed with next line if (column >= line.length) { this.moveCursorTo(row, line.length); @@ -614,7 +615,7 @@ var Selection = function(session) { this.moveCursorWordRight(); return; } - + // advance to the end of the next token if (match = this.session.tokenRe.exec(rightOfCursor)) { column += this.session.tokenRe.lastIndex; @@ -625,8 +626,8 @@ var Selection = function(session) { }; /** - * Selection.moveCursorLongWordLeft() - * + * Selection.moveCursorLongWordLeft() + * * Moves the cursor to the word on the left. **/ this.moveCursorLongWordLeft = function() { @@ -644,19 +645,19 @@ var Selection = function(session) { if (str == null) { str = this.doc.getLine(row).substring(0, column) } - + var leftOfCursor = lang.stringReverse(str); var match; this.session.nonTokenRe.lastIndex = 0; this.session.tokenRe.lastIndex = 0; - + // skip whitespace if (match = this.session.nonTokenRe.exec(leftOfCursor)) { column -= this.session.nonTokenRe.lastIndex; leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); this.session.nonTokenRe.lastIndex = 0; } - + // if at begin of the line proceed in line above if (column <= 0) { this.moveCursorTo(row, 0); @@ -721,8 +722,17 @@ var Selection = function(session) { if (fold) return this.moveCursorTo(fold.end.row, fold.end.column); - if (column == line.length) - return this.moveCursorRight(); + if (column == line.length) { + var l = this.doc.getLength(); + do { + row++; + rightOfCursor = this.doc.getLine(row) + } while (row < l && /^\s*$/.test(rightOfCursor)) + + if (!/^\s+/.test(rightOfCursor)) + rightOfCursor = "" + column = 0; + } var index = this.$shortWordEndIndex(rightOfCursor); @@ -737,11 +747,19 @@ var Selection = function(session) { if (fold = this.session.getFoldAt(row, column, -1)) return this.moveCursorTo(fold.start.row, fold.start.column); - if (column == 0) - return this.moveCursorLeft(); + var line = this.session.getLine(row).substring(0, column); + if (column == 0) { + do { + row--; + line = this.doc.getLine(row); + } while (row > 0 && /^\s*$/.test(line)) + + column = line.length; + if (!/\s+$/.test(line)) + line = "" + } - var str = this.session.getLine(row).substring(0, column); - var leftOfCursor = lang.stringReverse(str); + var leftOfCursor = lang.stringReverse(line); var index = this.$shortWordEndIndex(leftOfCursor); return this.moveCursorTo(row, column - index); @@ -762,7 +780,7 @@ var Selection = function(session) { }; /** related to: EditSession.documentToScreenPosition - * Selection.moveCursorBy(rows, chars) + * Selection.moveCursorBy(rows, chars) * - rows (Number): The number of rows to move by * - chars (Number): The number of characters to move by * @@ -788,9 +806,9 @@ var Selection = function(session) { }; /** - * Selection.moveCursorToPosition(position) + * Selection.moveCursorToPosition(position) * - position (Object): The position to move to - * + * * Moves the selection to the position indicated by its `row` and `column`. **/ this.moveCursorToPosition = function(position) { @@ -798,7 +816,7 @@ var Selection = function(session) { }; /** - * Selection.moveCursorTo(row, column, keepDesiredColumn) + * Selection.moveCursorTo(row, column, keepDesiredColumn) * - row (Number): The row to move to * - column (Number): The column to move to * - keepDesiredColumn (Boolean): [If `true`, the cursor move does not respect the previous column]{: #preventUpdateBool} @@ -822,7 +840,7 @@ var Selection = function(session) { }; /** - * Selection.moveCursorToScreen(row, column, keepDesiredColumn) + * Selection.moveCursorToScreen(row, column, keepDesiredColumn) * - row (Number): The row to move to * - column (Number): The column to move to * - keepDesiredColumn (Boolean): {:preventUpdateBool} diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index d6c349f7..934149e4 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -11,6 +11,7 @@ var log = document.getElementById("log") var testNames = [ "ace/anchor_test", + "ace/background_tokenizer_test", "ace/commands/command_manager_test", "ace/document_test", "ace/edit_session_test", @@ -65,7 +66,11 @@ if (location.search) testNames = location.search.substr(1).split(",") require(testNames, function() { - var tests = testNames.map(require); + var tests = testNames.map(function(x) { + var module = require(x); + module.href = x; + return module; + }); async.list(tests) .expand(function(test) { @@ -73,6 +78,12 @@ require(testNames, function() { }, AsyncTest.TestGenerator) .run() .each(function(test, next) { + if (test.index == 1 && test.context.href) { + var href = test.context.href; + var node = document.createElement("div"); + node.innerHTML = "" + href.replace(/^ace\//, "") + ""; + log.appendChild(node); + } var node = document.createElement("div"); node.className = test.passed ? "passed" : "failed"; diff --git a/lib/ace/test/mockrenderer.js b/lib/ace/test/mockrenderer.js index 381a7918..4e5c515d 100644 --- a/lib/ace/test/mockrenderer.js +++ b/lib/ace/test/mockrenderer.js @@ -163,7 +163,7 @@ MockRenderer.prototype.updateBackMarkers = function() { MockRenderer.prototype.updateFrontMarkers = function() { }; -MockRenderer.prototype.setBreakpoints = function() { +MockRenderer.prototype.updateBreakpoints = function() { }; MockRenderer.prototype.onResize = function() { diff --git a/lib/ace/theme/clouds.css b/lib/ace/theme/clouds.css index e666fa8b..e490c386 100644 --- a/lib/ace/theme/clouds.css +++ b/lib/ace/theme/clouds.css @@ -57,6 +57,10 @@ background: #FFFBD1; } +.ace-clouds .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-clouds .ace_marker-layer .ace_selected_word { border: 1px solid #BDD5FC; } diff --git a/lib/ace/theme/clouds_midnight.css b/lib/ace/theme/clouds_midnight.css index 0a9f3382..69ed51db 100644 --- a/lib/ace/theme/clouds_midnight.css +++ b/lib/ace/theme/clouds_midnight.css @@ -57,6 +57,10 @@ background: rgba(215, 215, 215, 0.031); } +.ace-clouds-midnight .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-clouds-midnight .ace_marker-layer .ace_selected_word { border: 1px solid #000000; } diff --git a/lib/ace/theme/cobalt.css b/lib/ace/theme/cobalt.css index fc4c8118..5c9a7328 100644 --- a/lib/ace/theme/cobalt.css +++ b/lib/ace/theme/cobalt.css @@ -57,6 +57,10 @@ background: rgba(0, 0, 0, 0.35); } +.ace-cobalt .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-cobalt .ace_marker-layer .ace_selected_word { border: 1px solid rgba(179, 101, 57, 0.75); } diff --git a/lib/ace/theme/dawn.css b/lib/ace/theme/dawn.css index 86fe47dd..85d48623 100644 --- a/lib/ace/theme/dawn.css +++ b/lib/ace/theme/dawn.css @@ -57,6 +57,10 @@ background: rgba(36, 99, 180, 0.12); } +.ace-dawn .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-dawn .ace_marker-layer .ace_selected_word { border: 1px solid rgba(39, 95, 255, 0.30); } diff --git a/lib/ace/theme/idle_fingers.css b/lib/ace/theme/idle_fingers.css index a7737f6d..4ad6ff23 100644 --- a/lib/ace/theme/idle_fingers.css +++ b/lib/ace/theme/idle_fingers.css @@ -57,6 +57,10 @@ background: #353637; } +.ace-idle-fingers .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-idle-fingers .ace_marker-layer .ace_selected_word { border: 1px solid rgba(90, 100, 126, 0.88); } diff --git a/lib/ace/theme/kr_theme.css b/lib/ace/theme/kr_theme.css index 57ad4be1..aa6c2e37 100644 --- a/lib/ace/theme/kr_theme.css +++ b/lib/ace/theme/kr_theme.css @@ -57,6 +57,10 @@ background: #38403D; } +.ace-kr-theme .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-kr-theme .ace_marker-layer .ace_selected_word { border: 1px solid rgba(170, 0, 255, 0.45); } diff --git a/lib/ace/theme/merbivore.css b/lib/ace/theme/merbivore.css index 7d255603..fe957fba 100644 --- a/lib/ace/theme/merbivore.css +++ b/lib/ace/theme/merbivore.css @@ -57,6 +57,10 @@ background: #333435; } +.ace-merbivore .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-merbivore .ace_marker-layer .ace_selected_word { border: 1px solid #454545; } diff --git a/lib/ace/theme/merbivore_soft.css b/lib/ace/theme/merbivore_soft.css index 6ab63324..4b7e91f2 100644 --- a/lib/ace/theme/merbivore_soft.css +++ b/lib/ace/theme/merbivore_soft.css @@ -57,6 +57,10 @@ background: #333435; } +.ace-merbivore-soft .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-merbivore-soft .ace_marker-layer .ace_selected_word { border: 1px solid #494949; } diff --git a/lib/ace/theme/mono_industrial.css b/lib/ace/theme/mono_industrial.css index 3eaf1cae..1e2d06ba 100644 --- a/lib/ace/theme/mono_industrial.css +++ b/lib/ace/theme/mono_industrial.css @@ -57,6 +57,10 @@ background: rgba(12, 13, 12, 0.25); } +.ace-mono-industrial .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-mono-industrial .ace_marker-layer .ace_selected_word { border: 1px solid rgba(145, 153, 148, 0.40); } diff --git a/lib/ace/theme/monokai.css b/lib/ace/theme/monokai.css index 36b0a23c..5597ab01 100644 --- a/lib/ace/theme/monokai.css +++ b/lib/ace/theme/monokai.css @@ -53,11 +53,11 @@ border: 1px solid #49483E; } -.ace-monokai .ace_marker-layer .ace_active_line{ +.ace-monokai .ace_marker-layer .ace_active_line { background: #49483E; } -.ace-monokai .ace_gutter_active_line{ - background: #191916; +.ace-monokai .ace_gutter_active_line { + background-color: #191916; } .ace-monokai .ace_marker-layer .ace_selected_word { diff --git a/lib/ace/theme/pastel_on_dark.css b/lib/ace/theme/pastel_on_dark.css index 31460fb7..fc5155a4 100644 --- a/lib/ace/theme/pastel_on_dark.css +++ b/lib/ace/theme/pastel_on_dark.css @@ -57,6 +57,10 @@ background: rgba(255, 255, 255, 0.031); } +.ace-pastel-on-dark .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-pastel-on-dark .ace_marker-layer .ace_selected_word { border: 1px solid rgba(221, 240, 255, 0.20); } diff --git a/lib/ace/theme/solarized_dark.css b/lib/ace/theme/solarized_dark.css index 392afe54..31eed2bd 100644 --- a/lib/ace/theme/solarized_dark.css +++ b/lib/ace/theme/solarized_dark.css @@ -56,8 +56,9 @@ .ace-solarized-dark .ace_marker-layer .ace_active_line { background: #073642; } -.ace-solarized-dark .ace_gutter_active_line{ - background: #0d3440; + +.ace-solarized-dark .ace_gutter_active_line { + background-color: #0d3440; } .ace-solarized-dark .ace_marker-layer .ace_selected_word { diff --git a/lib/ace/theme/solarized_light.css b/lib/ace/theme/solarized_light.css index cdf01956..cc1c8699 100644 --- a/lib/ace/theme/solarized_light.css +++ b/lib/ace/theme/solarized_light.css @@ -57,6 +57,10 @@ background: #EEE8D5; } +.ace-solarized-light .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-solarized-light .ace_marker-layer .ace_selected_word { border: 1px solid #073642; } diff --git a/lib/ace/theme/tomorrow.css b/lib/ace/theme/tomorrow.css index 27bc1e87..95c9c0d4 100644 --- a/lib/ace/theme/tomorrow.css +++ b/lib/ace/theme/tomorrow.css @@ -57,6 +57,10 @@ background: #EFEFEF; } +.ace-tomorrow .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-tomorrow .ace_marker-layer .ace_selected_word { border: 1px solid #D6D6D6; } diff --git a/lib/ace/theme/tomorrow_night.css b/lib/ace/theme/tomorrow_night.css index eb09c39c..2e74d864 100644 --- a/lib/ace/theme/tomorrow_night.css +++ b/lib/ace/theme/tomorrow_night.css @@ -57,6 +57,10 @@ background: #282A2E; } +.ace-tomorrow-night .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-tomorrow-night .ace_marker-layer .ace_selected_word { border: 1px solid #373B41; } diff --git a/lib/ace/theme/tomorrow_night_blue.css b/lib/ace/theme/tomorrow_night_blue.css index 23b197bf..10c58165 100644 --- a/lib/ace/theme/tomorrow_night_blue.css +++ b/lib/ace/theme/tomorrow_night_blue.css @@ -53,11 +53,12 @@ border: 1px solid #404F7D; } -.ace-tomorrow-night-blue .ace_marker-layer .ace_active_line{ +.ace-tomorrow-night-blue .ace_marker-layer .ace_active_line { background: #00346E; } -.ace-tomorrow-night-blue .ace_gutter_active_line{ - background: #022040; + +.ace-tomorrow-night-blue .ace_gutter_active_line { + background-color: #022040; } .ace-tomorrow-night-blue .ace_marker-layer .ace_selected_word { diff --git a/lib/ace/theme/tomorrow_night_bright.css b/lib/ace/theme/tomorrow_night_bright.css index c7b35f8b..21a928be 100644 --- a/lib/ace/theme/tomorrow_night_bright.css +++ b/lib/ace/theme/tomorrow_night_bright.css @@ -57,6 +57,10 @@ background: #2A2A2A; } +.ace-tomorrow-night-bright .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-tomorrow-night-bright .ace_marker-layer .ace_selected_word { border: 1px solid #424242; } diff --git a/lib/ace/theme/tomorrow_night_eighties.css b/lib/ace/theme/tomorrow_night_eighties.css index 8f0244f4..93f2d3b6 100644 --- a/lib/ace/theme/tomorrow_night_eighties.css +++ b/lib/ace/theme/tomorrow_night_eighties.css @@ -57,6 +57,10 @@ background: #393939; } +.ace-tomorrow-night-eighties .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-tomorrow-night-eighties .ace_marker-layer .ace_selected_word { border: 1px solid #515151; } diff --git a/lib/ace/theme/twilight.css b/lib/ace/theme/twilight.css index a9637b09..6d81e4a5 100644 --- a/lib/ace/theme/twilight.css +++ b/lib/ace/theme/twilight.css @@ -57,6 +57,10 @@ background: rgba(255, 255, 255, 0.031); } +.ace-twilight .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-twilight .ace_marker-layer .ace_selected_word { border: 1px solid rgba(221, 240, 255, 0.20); } diff --git a/lib/ace/theme/vibrant_ink.css b/lib/ace/theme/vibrant_ink.css index 9144a275..81c54d71 100644 --- a/lib/ace/theme/vibrant_ink.css +++ b/lib/ace/theme/vibrant_ink.css @@ -57,6 +57,10 @@ background: #333333; } +.ace-vibrant-ink .ace_gutter_active_line { + background-color : #dcdcdc; +} + .ace-vibrant-ink .ace_marker-layer .ace_selected_word { border: 1px solid #6699CC; } diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 363f3cd8..88719079 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -67,7 +67,7 @@ dom.importCssString(editorCss, "ace_editor"); /** * new VirtualRenderer(container, theme) - * - container (DOMElement): The root element of the editor + * - container (DOMElement): The root element of the editor * - theme (String): The starting theme * * Constructs a new `VirtualRenderer` within the `container` specified, applying the given `theme`. @@ -82,7 +82,7 @@ var VirtualRenderer = function(container, theme) { // TODO: this breaks rendering in Cloud9 with multiple ace instances // // Imports CSS once per DOM document ('ace_editor' serves as an identifier). // dom.importCssString(editorCss, "ace_editor", container.ownerDocument); - + // in IE <= 9 the native cursor always shines through this.$keepTextAreaAtCursor = !useragent.isIE; @@ -102,7 +102,6 @@ var VirtualRenderer = function(container, theme) { this.content.className = "ace_content"; this.scroller.appendChild(this.content); - this.setHighlightGutterLine(true); this.$gutterLayer = new GutterLayer(this.$gutter); this.$gutterLayer.on("changeGutterWidth", this.onResize.bind(this, true)); this.setFadeFoldWidgets(true); @@ -449,31 +448,38 @@ var VirtualRenderer = function(container, theme) { dom.removeCssClass(this.$gutter, "ace_fade-fold-widgets"); }; - this.$highlightGutterLine = false; + this.$highlightGutterLine = true; this.setHighlightGutterLine = function(shouldHighlight) { if (this.$highlightGutterLine == shouldHighlight) return; this.$highlightGutterLine = shouldHighlight; - - - if (!this.$gutterLineHighlight) { - this.$gutterLineHighlight = dom.createElement("div"); - this.$gutterLineHighlight.className = "ace_gutter_active_line"; - this.$gutter.appendChild(this.$gutterLineHighlight); - return; - } - this.$gutterLineHighlight.style.display = shouldHighlight ? "" : "none"; - this.$updateGutterLineHighlight(); + this.$loop.schedule(this.CHANGE_GUTTER); }; this.getHighlightGutterLine = function() { return this.$highlightGutterLine; }; - this.$updateGutterLineHighlight = function() { - this.$gutterLineHighlight.style.top = this.$cursorLayer.$pixelPos.top - this.layerConfig.offset + "px"; - this.$gutterLineHighlight.style.height = this.layerConfig.lineHeight + "px"; + this.$updateGutterLineHighlight = function(gutterReady) { + var i = this.session.selection.lead.row; + if (i == this.$gutterLineHighlight) + return; + + if (!gutterReady) { + var ch = this.$gutterLayer.element.children + var oldEl = ch[this.$gutterLineHighlight - this.layerConfig.firstRow]; + if (oldEl) + dom.removeCssClass(oldEl, "ace_gutter_active_line"); + + var newEl = ch[i - this.layerConfig.firstRow]; + if (newEl) + dom.addCssClass(newEl, "ace_gutter_active_line"); + } + + this.$gutterLayer.removeGutterDecoration(this.$gutterLineHighlight, "ace_gutter_active_line"); + this.$gutterLayer.addGutterDecoration(i, "ace_gutter_active_line"); + this.$gutterLineHighlight = i; }; this.$updatePrintMargin = function() { @@ -533,13 +539,23 @@ var VirtualRenderer = function(container, theme) { var posLeft = this.$cursorLayer.$pixelPos.left; posTop -= this.layerConfig.offset; - if (posTop < 0 || posTop > this.layerConfig.height) + if (posTop < 0 || posTop > this.layerConfig.height - this.lineHeight) return; - posLeft += (this.showGutter ? this.$gutterLayer.gutterWidth : 0) - this.scrollLeft; - var bounds = this.container.getBoundingClientRect(); - this.textarea.style.left = (bounds.left + posLeft) + "px"; - this.textarea.style.top = (bounds.top + posTop) + "px"; + var w = this.characterWidth; + if (this.$composition) + w += this.textarea.scrollWidth; + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + + if (this.showGutter) + posLeft += this.$gutterLayer.gutterWidth; + + this.textarea.style.height = this.lineHeight + "px"; + this.textarea.style.width = w + "px"; + this.textarea.style.left = posLeft + "px"; + this.textarea.style.top = posTop - 1 + "px"; }; /** @@ -657,13 +673,15 @@ var VirtualRenderer = function(container, theme) { // update scrollbar first to not lose scroll position when gutter calls resize this.$updateScrollBar(); this.$textLayer.update(this.layerConfig); - if (this.showGutter) + if (this.showGutter) { + if (this.$highlightGutterLine) + this.$updateGutterLineHighlight(true); this.$gutterLayer.update(this.layerConfig); + } this.$markerBack.update(this.layerConfig); this.$markerFront.update(this.layerConfig); this.$cursorLayer.update(this.layerConfig); this.$moveTextAreaToCursor(); - this.$highlightGutterLine && this.$updateGutterLineHighlight(); return; } @@ -675,13 +693,15 @@ var VirtualRenderer = function(container, theme) { else this.$textLayer.scrollLines(this.layerConfig); - if (this.showGutter) + if (this.showGutter) { + if (this.$highlightGutterLine) + this.$updateGutterLineHighlight(true); this.$gutterLayer.update(this.layerConfig); + } this.$markerBack.update(this.layerConfig); this.$markerFront.update(this.layerConfig); this.$cursorLayer.update(this.layerConfig); this.$moveTextAreaToCursor(); - this.$highlightGutterLine && this.$updateGutterLineHighlight(); return; } @@ -704,7 +724,7 @@ var VirtualRenderer = function(container, theme) { if (changes & this.CHANGE_CURSOR) { this.$cursorLayer.update(this.layerConfig); this.$moveTextAreaToCursor(); - this.$highlightGutterLine && this.$updateGutterLineHighlight(); + this.$highlightGutterLine && this.$updateGutterLineHighlight(false); } if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) { @@ -800,10 +820,6 @@ var VirtualRenderer = function(container, theme) { var layerConfig = this.layerConfig; - // if the update changes the width of the document do a full redraw - if (layerConfig.width != this.$getLongestLine()) - return this.$textLayer.update(layerConfig); - if (firstRow > layerConfig.lastRow + 1) { return; } if (lastRow < layerConfig.firstRow) { return; } @@ -873,13 +889,11 @@ var VirtualRenderer = function(container, theme) { }; /** - * VirtualRenderer.setBreakpoints(rows) -> Void - * - rows (Array): An array containg row numbers + * VirtualRenderer.updateBreakpoints() -> Void * - * Sets a breakpoint for every row number indicated on `rows`. + * Redraw breakpoints. **/ - this.setBreakpoints = function(rows) { - this.$gutterLayer.setBreakpoints(rows); + this.updateBreakpoints = function(rows) { this.$loop.schedule(this.CHANGE_GUTTER); }; @@ -1035,13 +1049,13 @@ var VirtualRenderer = function(container, theme) { return steps; }; - /** + /** * VirtualRenderer.scrollToLine(line, center, animate, callback) -> Void * - line (Number): A line number * - center (Boolean): If `true`, centers the editor the to indicated line * - animate (Boolean): If `true` animates scrolling * - callback (Function): Function to be called after the animation has finished - * + * * Gracefully scrolls the editor to the row indicated. **/ this.scrollToLine = function(line, center, animate, callback) { @@ -1062,7 +1076,7 @@ var VirtualRenderer = function(container, theme) { var _self = this; var steps = _self.$calcSteps(fromValue, toValue); this.$inScrollAnimation = true; - + clearInterval(this.$timer); _self.session.setScrollTop(steps.shift()); @@ -1071,18 +1085,20 @@ var VirtualRenderer = function(container, theme) { _self.session.setScrollTop(steps.shift()); // trick session to think it's already scrolled to not loose toValue _self.session.$scrollTop = toValue; - } else { - _self.$inScrollAnimation = false; - clearInterval(_self.$timer); - + } else if (toValue != null) { _self.session.$scrollTop = -1; _self.session.setScrollTop(toValue); + toValue = null; + } else { + // do this on separate step to not get spurious scroll event from scrollbar + _self.$timer = clearInterval(_self.$timer); + _self.$inScrollAnimation = false; callback && callback(); } }, 10); } }; - + /** * VirtualRenderer.scrollToY(scrollTop) -> Number * - scrollTop (Number): The position to scroll to @@ -1211,21 +1227,16 @@ var VirtualRenderer = function(container, theme) { * **/ this.showComposition = function(position) { - if (!this.$composition) { - this.$composition = dom.createElement("div"); - this.$composition.className = "ace_composition"; - this.content.appendChild(this.$composition); - } + if (!this.$composition) + this.$composition = { + keepTextAreaAtCursor: this.$keepTextAreaAtCursor, + cssText: this.textarea.style.cssText + }; - this.$composition.innerHTML = " "; - - var pos = this.$cursorLayer.getPixelPosition(); - var style = this.$composition.style; - style.top = pos.top + "px"; - style.left = (pos.left + this.$padding) + "px"; - style.height = this.lineHeight + "px"; - - this.hideCursor(); + this.$keepTextAreaAtCursor = true; + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); }; /** @@ -1235,7 +1246,7 @@ var VirtualRenderer = function(container, theme) { * Sets the inner text of the current composition to `text`. **/ this.setCompositionText = function(text) { - dom.setInnerText(this.$composition, text); + this.$moveTextAreaToCursor(); }; /** @@ -1244,14 +1255,13 @@ var VirtualRenderer = function(container, theme) { * Hides the current composition. **/ this.hideComposition = function() { - this.showCursor(); - if (!this.$composition) return; - var style = this.$composition.style; - style.top = "-10000px"; - style.left = "-10000px"; + dom.removeCssClass(this.textarea, "ace_composition"); + this.$keepTextAreaAtCursor = this.$composition.keepTextAreaAtCursor; + this.textarea.style.cssText = this.$composition.cssText; + this.$composition = null; }; this._loadTheme = function(name, callback) { diff --git a/tool/Theme.tmpl.css b/tool/Theme.tmpl.css index 1d6aca07..5d55386a 100644 --- a/tool/Theme.tmpl.css +++ b/tool/Theme.tmpl.css @@ -57,6 +57,10 @@ background: %active_line%; } +.%cssClass% .ace_gutter_active_line { + background-color : #dcdcdc; +} + .%cssClass% .ace_marker-layer .ace_selected_word { %selected_word_highlight% }