Merge branch 'multiCursor'
Conflicts: demo/kitchen-sink/demo.js lib/ace/test/all_browser.js
This commit is contained in:
commit
aed7ca654c
26 changed files with 1902 additions and 386 deletions
|
|
@ -56,6 +56,10 @@ var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
||||||
|
|
||||||
var modesByName;
|
var modesByName;
|
||||||
|
|
||||||
|
// workers do not work for file:
|
||||||
|
if (location.protocol == "file:")
|
||||||
|
EditSession.prototype.$useWorker = false;
|
||||||
|
|
||||||
var Doc = function(name, desc, file) {
|
var Doc = function(name, desc, file) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.desc = desc;
|
this.desc = desc;
|
||||||
|
|
@ -82,6 +86,7 @@ Mode.prototype.supportsFile = function(filename) {
|
||||||
return filename.match(this.extRe);
|
return filename.match(this.extRe);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//{
|
||||||
var modes = [
|
var modes = [
|
||||||
new Mode("c_cpp", "C/C++", ["c", "cpp", "cxx", "h", "hpp"]),
|
new Mode("c_cpp", "C/C++", ["c", "cpp", "cxx", "h", "hpp"]),
|
||||||
new Mode("clojure", "Clojure", ["clj"]),
|
new Mode("clojure", "Clojure", ["clj"]),
|
||||||
|
|
@ -260,6 +265,9 @@ var docs = [
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
var docsByName = {};
|
var docsByName = {};
|
||||||
docs.forEach(function(d) {
|
docs.forEach(function(d) {
|
||||||
docsByName[d.name] = d;
|
docsByName[d.name] = d;
|
||||||
|
|
@ -324,6 +332,11 @@ modes.forEach(function(mode) {
|
||||||
modeEl.appendChild(option);
|
modeEl.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bindDropdown("mode", function(value) {
|
||||||
|
env.editor.getSession().setMode(modesByName[value].mode || modesByName.text.mode);
|
||||||
|
env.editor.getSession().modeName = value;
|
||||||
|
});
|
||||||
|
|
||||||
bindDropdown("doc", function(value) {
|
bindDropdown("doc", function(value) {
|
||||||
var doc = docsByName[value].doc;
|
var doc = docsByName[value].doc;
|
||||||
|
|
||||||
|
|
@ -344,40 +357,43 @@ function updateUIEditorOptions() {
|
||||||
var editor = env.editor;
|
var editor = env.editor;
|
||||||
var session = editor.session;
|
var session = editor.session;
|
||||||
|
|
||||||
docEl.value = session.name;
|
|
||||||
modeEl.value = session.modeName || "text";
|
|
||||||
|
|
||||||
session.setFoldStyle(foldingEl.value);
|
session.setFoldStyle(foldingEl.value);
|
||||||
|
|
||||||
if (!session.getUseWrapMode()) {
|
saveOption(docEl, session.name);
|
||||||
wrapModeEl.value = "off";
|
saveOption(modeEl, session.modeName || "text");
|
||||||
} else {
|
saveOption(wrapModeEl, session.getUseWrapMode() ? session.getWrapLimitRange().min || "free" : "off");
|
||||||
wrapModeEl.value = session.getWrapLimitRange().min || "free";
|
|
||||||
}
|
|
||||||
|
|
||||||
selectStyleEl.checked = editor.getSelectionStyle() == "line";
|
saveOption(selectStyleEl, editor.getSelectionStyle() == "line");
|
||||||
themeEl.value = editor.getTheme();
|
saveOption(themeEl, editor.getTheme());
|
||||||
highlightActiveEl.checked = editor.getHighlightActiveLine();
|
saveOption(highlightActiveEl, editor.getHighlightActiveLine());
|
||||||
showHiddenEl.checked = editor.getShowInvisibles();
|
saveOption(showHiddenEl, editor.getShowInvisibles());
|
||||||
showGutterEl.checked = editor.renderer.getShowGutter();
|
saveOption(showGutterEl, editor.renderer.getShowGutter());
|
||||||
showPrintMarginEl.checked = editor.renderer.getShowPrintMargin();
|
saveOption(showPrintMarginEl, editor.renderer.getShowPrintMargin());
|
||||||
highlightSelectedWordE.checked = editor.getHighlightSelectedWord();
|
saveOption(highlightSelectedWordE, editor.getHighlightSelectedWord());
|
||||||
showHScrollEl.checked = editor.renderer.getHScrollBarAlwaysVisible();
|
saveOption(showHScrollEl, editor.renderer.getHScrollBarAlwaysVisible());
|
||||||
animateScrollEl.checked = editor.getAnimatedScroll();
|
saveOption(showHScrollEl, editor.getAnimatedScroll());
|
||||||
softTabEl.checked = session.getUseSoftTabs();
|
saveOption(softTabEl, session.getUseSoftTabs());
|
||||||
behavioursEl.checked = editor.getBehavioursEnabled();
|
saveOption(behavioursEl, editor.getBehavioursEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
bindDropdown("mode", function(value) {
|
function saveOption(el, val) {
|
||||||
env.editor.getSession().setMode(modesByName[value].mode || modesByName.text.mode);
|
if (!el.onchange || el.onclick)
|
||||||
env.editor.getSession().modeName = value;
|
return;
|
||||||
});
|
if ("checked" in el) {
|
||||||
|
if (val !== undefined)
|
||||||
|
el.checked = val;
|
||||||
|
localStorage && localStorage.setItem(el.id, el.checked ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
if (val !== undefined)
|
||||||
|
el.value = val;
|
||||||
|
localStorage && localStorage.setItem(el.id, el.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bindDropdown("theme", function(value) {
|
bindDropdown("theme", function(value) {
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
|
env.editor.setTheme(value);
|
||||||
env.editor.setTheme(value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bindDropdown("keybinding", function(value) {
|
bindDropdown("keybinding", function(value) {
|
||||||
|
|
@ -459,6 +475,7 @@ bindCheckbox("enable_behaviours", function(checked) {
|
||||||
env.editor.setBehavioursEnabled(checked);
|
env.editor.setBehavioursEnabled(checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var secondSession = null;
|
var secondSession = null;
|
||||||
bindDropdown("split", function(value) {
|
bindDropdown("split", function(value) {
|
||||||
var sp = env.split;
|
var sp = env.split;
|
||||||
|
|
@ -486,8 +503,12 @@ bindDropdown("split", function(value) {
|
||||||
|
|
||||||
function bindCheckbox(id, callback) {
|
function bindCheckbox(id, callback) {
|
||||||
var el = document.getElementById(id);
|
var el = document.getElementById(id);
|
||||||
|
if (localStorage && localStorage.getItem(id))
|
||||||
|
el.checked = localStorage.getItem(id) == "1";
|
||||||
|
|
||||||
var onCheck = function() {
|
var onCheck = function() {
|
||||||
callback(!!el.checked);
|
callback(!!el.checked);
|
||||||
|
saveOption(el);
|
||||||
};
|
};
|
||||||
el.onclick = onCheck;
|
el.onclick = onCheck;
|
||||||
onCheck();
|
onCheck();
|
||||||
|
|
@ -495,9 +516,14 @@ function bindCheckbox(id, callback) {
|
||||||
|
|
||||||
function bindDropdown(id, callback) {
|
function bindDropdown(id, callback) {
|
||||||
var el = document.getElementById(id);
|
var el = document.getElementById(id);
|
||||||
|
if (localStorage && localStorage.getItem(id))
|
||||||
|
el.value = localStorage.getItem(id);
|
||||||
|
|
||||||
var onChange = function() {
|
var onChange = function() {
|
||||||
callback(el.value);
|
callback(el.value);
|
||||||
|
saveOption(el);
|
||||||
};
|
};
|
||||||
|
|
||||||
el.onchange = onChange;
|
el.onchange = onChange;
|
||||||
onChange();
|
onChange();
|
||||||
}
|
}
|
||||||
|
|
@ -589,4 +615,7 @@ commands.addCommand({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add multiple cursor support to editor
|
||||||
|
require("ace/multi_select").MultiSelect(env.editor);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
function foo (items, nada) {
|
function foo(items, nada) {
|
||||||
for (var i=0; i<items.length; i++) {
|
for (var i=0; i<items.length; i++) {
|
||||||
alert(items[i] + "juhu\n");
|
alert(items[i] + "juhu\n");
|
||||||
} // Real Tab.
|
} // Real Tab.
|
||||||
|
|
|
||||||
2
doc/wiki
2
doc/wiki
|
|
@ -1 +1 @@
|
||||||
Subproject commit fadf80529983183e3e4bf01f90e014c1c16f223b
|
Subproject commit d2a65d0addc2e5ab922bbff9cb6022a4652b4f13
|
||||||
|
|
@ -1,131 +1,27 @@
|
||||||
define(function(require, exports, module) {
|
define(function(require, exports, module) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var keyUtil = require("../lib/keys");
|
var oop = require("../lib/oop");
|
||||||
|
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||||
|
var EventEmitter = require("../lib/event_emitter").EventEmitter;
|
||||||
|
|
||||||
var CommandManager = function(platform, commands) {
|
var CommandManager = function(platform, commands) {
|
||||||
if (typeof platform !== "string")
|
|
||||||
throw new TypeError("'platform' argument must be either 'mac' or 'win'");
|
|
||||||
|
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
this.commands = {};
|
this.commands = {};
|
||||||
this.commmandKeyBinding = {};
|
this.commmandKeyBinding = {};
|
||||||
|
|
||||||
if (commands)
|
this.addCommands(commands);
|
||||||
commands.forEach(this.addCommand, this);
|
|
||||||
|
this.setDefaultHandler("exec", function(e) {
|
||||||
|
e.command.exec(e.editor, e.args || {});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oop.inherits(CommandManager, HashHandler);
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
this.addCommand = function(command) {
|
oop.implement(this, EventEmitter);
|
||||||
if (this.commands[command.name])
|
|
||||||
this.removeCommand(command);
|
|
||||||
|
|
||||||
this.commands[command.name] = command;
|
|
||||||
|
|
||||||
if (command.bindKey) {
|
|
||||||
this._buildKeyHash(command);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.removeCommand = function(command) {
|
|
||||||
var name = (typeof command === 'string' ? command : command.name);
|
|
||||||
command = this.commands[name];
|
|
||||||
delete this.commands[name];
|
|
||||||
|
|
||||||
// exaustive search is brute force but since removeCommand is
|
|
||||||
// not a performance critical operation this should be OK
|
|
||||||
var ckb = this.commmandKeyBinding;
|
|
||||||
for (var hashId in ckb) {
|
|
||||||
for (var key in ckb[hashId]) {
|
|
||||||
if (ckb[hashId][key] == command)
|
|
||||||
delete ckb[hashId][key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addCommands = function(commands) {
|
|
||||||
Object.keys(commands).forEach(function(name) {
|
|
||||||
var command = commands[name];
|
|
||||||
if (typeof command === "string")
|
|
||||||
return this.bindKey(command, name);
|
|
||||||
|
|
||||||
if (typeof command === "function")
|
|
||||||
command = { exec: command };
|
|
||||||
|
|
||||||
if (!command.name)
|
|
||||||
command.name = name;
|
|
||||||
|
|
||||||
this.addCommand(command);
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.removeCommands = function(commands) {
|
|
||||||
Object.keys(commands).forEach(function(name) {
|
|
||||||
this.removeCommand(commands[name]);
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.bindKey = function(key, command) {
|
|
||||||
if(!key)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var ckb = this.commmandKeyBinding;
|
|
||||||
key.split("|").forEach(function(keyPart) {
|
|
||||||
var binding = parseKeys(keyPart, command);
|
|
||||||
var hashId = binding.hashId;
|
|
||||||
(ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.bindKeys = function(keyList) {
|
|
||||||
Object.keys(keyList).forEach(function(key) {
|
|
||||||
this.bindKey(key, keyList[key]);
|
|
||||||
}, this);
|
|
||||||
};
|
|
||||||
|
|
||||||
this._buildKeyHash = function(command) {
|
|
||||||
var binding = command.bindKey;
|
|
||||||
if (!binding)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var key = typeof binding == "string" ? binding: binding[this.platform];
|
|
||||||
this.bindKey(key, command);
|
|
||||||
};
|
|
||||||
|
|
||||||
function parseKeys(keys, val, ret) {
|
|
||||||
var key;
|
|
||||||
var hashId = 0;
|
|
||||||
var parts = splitSafe(keys);
|
|
||||||
|
|
||||||
for (var i=0, l = parts.length; i < l; i++) {
|
|
||||||
if (keyUtil.KEY_MODS[parts[i]])
|
|
||||||
hashId = hashId | keyUtil.KEY_MODS[parts[i]];
|
|
||||||
else
|
|
||||||
key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
key: key,
|
|
||||||
hashId: hashId
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function splitSafe(s) {
|
|
||||||
return (s.toLowerCase()
|
|
||||||
.trim()
|
|
||||||
.split(new RegExp("[\\s ]*\\-[\\s ]*", "g"), 999));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.findKeyCommand = function findKeyCommand(hashId, textOrKey) {
|
|
||||||
// Convert keyCode to the string representation.
|
|
||||||
if (typeof textOrKey == "number") {
|
|
||||||
textOrKey = keyUtil.keyCodeToString(textOrKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ckbr = this.commmandKeyBinding;
|
|
||||||
return ckbr[hashId] && ckbr[hashId][textOrKey.toLowerCase()];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.exec = function(command, editor, args) {
|
this.exec = function(command, editor, args) {
|
||||||
if (typeof command === 'string')
|
if (typeof command === 'string')
|
||||||
|
|
@ -137,7 +33,7 @@ var CommandManager = function(platform, commands) {
|
||||||
if (editor && editor.$readOnly && !command.readOnly)
|
if (editor && editor.$readOnly && !command.readOnly)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
command.exec(editor, args || {});
|
this._emit("exec", {editor: editor, command: command, args: args});
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -146,20 +42,22 @@ var CommandManager = function(platform, commands) {
|
||||||
return;
|
return;
|
||||||
if (this.recording) {
|
if (this.recording) {
|
||||||
this.macro.pop();
|
this.macro.pop();
|
||||||
this.exec = this.normal_exec;
|
this.removeEventListener("exec", this.$addCommandToMacro);
|
||||||
|
|
||||||
if (!this.macro.length)
|
if (!this.macro.length)
|
||||||
this.macro = this.oldMacro;
|
this.macro = this.oldMacro;
|
||||||
|
|
||||||
return this.recording = false;
|
return this.recording = false;
|
||||||
}
|
}
|
||||||
|
if (!this.$addCommandToMacro) {
|
||||||
|
this.$addCommandToMacro = function(e) {
|
||||||
|
this.macro.push([e.command, e.args]);
|
||||||
|
}.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
this.oldMacro = this.macro;
|
this.oldMacro = this.macro;
|
||||||
this.macro = [];
|
this.macro = [];
|
||||||
this.normal_exec = this.exec;
|
this.on("exec", this.$addCommandToMacro);
|
||||||
this.exec = function(command, editor, args) {
|
|
||||||
this.macro.push([command, args]);
|
|
||||||
return this.normal_exec(command, editor, args);
|
|
||||||
};
|
|
||||||
return this.recording = true;
|
return this.recording = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
121
lib/ace/commands/multi_select_commands.js
Normal file
121
lib/ace/commands/multi_select_commands.js
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* 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 <amirjanyan AT gmail DOT com>
|
||||||
|
*
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
// add multiSelectAction annotations to default commands
|
||||||
|
require("./default_commands").commands.forEach(function(command) {
|
||||||
|
var single = RegExp(["selectall"].join("|"), "");
|
||||||
|
var mapOver = RegExp(["backspace", "del",
|
||||||
|
"golinedown", "golineup", "gotoend", "gotoleft", "gotolineend", "gotolinestart",
|
||||||
|
"gotoright", "gotostart", "gotowordleft", "gotowordright",
|
||||||
|
"indent", "insertstring", "inserttext", "jumptomatching", "outdent",
|
||||||
|
"removetolineend", "removetolinestart", "removewordleft", "removewordright", "removeline",
|
||||||
|
"selectdown", "selectleft", "selectlineend", "selectlinestart", "selectright",
|
||||||
|
"selecttoend", "selecttolineend", "selecttolinestart", "selecttostart",
|
||||||
|
"selectup", "selectwordleft", "selectwordright",
|
||||||
|
"splitline", "tolowercase", "touppercase"].join("|"), "");
|
||||||
|
|
||||||
|
if (single.test(command.name))
|
||||||
|
command.multiSelectAction = "single";
|
||||||
|
else if (mapOver.test(command.name))
|
||||||
|
command.multiSelectAction = "forEach";
|
||||||
|
else if (command.name == "transposeletters")
|
||||||
|
command.multiSelectAction = function(editor) {editor.transposeSelections(1); };
|
||||||
|
});
|
||||||
|
|
||||||
|
// commands to enter multiselect mode
|
||||||
|
exports.defaultCommands = [{
|
||||||
|
name: "addCursorAbove",
|
||||||
|
exec: function(editor) { editor.selectMoreLines(-1); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "addCursorBelow",
|
||||||
|
exec: function(editor) { editor.selectMoreLines(1); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "addCursorAboveSkipCurrent",
|
||||||
|
exec: function(editor) { editor.selectMoreLines(-1, true); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "addCursorBelowSkipCurrent",
|
||||||
|
exec: function(editor) { editor.selectMoreLines(1, true); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "selectMoreBefore",
|
||||||
|
exec: function(editor) { editor.selectMore(-1); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "selectMoreAfter",
|
||||||
|
exec: function(editor) { editor.selectMore(1); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "selectNextBefore",
|
||||||
|
exec: function(editor) { editor.selectMore(-1, true); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "selectNextAfter",
|
||||||
|
exec: function(editor) { editor.selectMore(1, true); },
|
||||||
|
bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"},
|
||||||
|
readonly: true
|
||||||
|
}, {
|
||||||
|
name: "splitIntoLines",
|
||||||
|
exec: function(editor) { editor.multiSelect.splitIntoLines(); },
|
||||||
|
bindKey: {win: "Ctrl-Shift-L", mac: "Ctrl-Shift-L"},
|
||||||
|
readonly: true
|
||||||
|
}];
|
||||||
|
|
||||||
|
// commands active in multiselect mode
|
||||||
|
exports.multiEditCommands = [{
|
||||||
|
name: "singleSelection",
|
||||||
|
bindKey: "esc",
|
||||||
|
exec: function(editor) { editor.exitMultiSelectMode(); },
|
||||||
|
readonly: true
|
||||||
|
}];
|
||||||
|
|
||||||
|
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||||
|
exports.keyboardHandler = new HashHandler(exports.multiEditCommands);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -162,12 +162,12 @@
|
||||||
|
|
||||||
.ace_marker-layer .ace_selection {
|
.ace_marker-layer .ace_selection {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 4;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ace_marker-layer .ace_bracket {
|
.ace_marker-layer .ace_bracket {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 5;
|
z-index: 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ace_marker-layer .ace_active_line {
|
.ace_marker-layer .ace_active_line {
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
|
|
||||||
.ace_marker-layer .ace_selected_word {
|
.ace_marker-layer .ace_selected_word {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 6;
|
z-index: 4;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
|
|
|
||||||
|
|
@ -1443,8 +1443,8 @@ var EditSession = function(text, mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getScreenLastRowColumn = function(screenRow) {
|
this.getScreenLastRowColumn = function(screenRow) {
|
||||||
//return this.screenToDocumentColumn(screenRow, Number.MAX_VALUE / 10)
|
var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE)
|
||||||
return this.documentToScreenColumn(screenRow, this.doc.getLine(screenRow).length);
|
return this.documentToScreenColumn(pos.row, pos.column);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getDocumentLastRowColumn = function(docRow, docColumn) {
|
this.getDocumentLastRowColumn = function(docRow, docColumn) {
|
||||||
|
|
@ -1559,16 +1559,10 @@ var EditSession = function(text, mode) {
|
||||||
|
|
||||||
docColumn += this.$getStringScreenWidth(line, screenColumn)[1];
|
docColumn += this.$getStringScreenWidth(line, screenColumn)[1];
|
||||||
|
|
||||||
// Need to do some clamping action here.
|
// We remove one character at the end so that the docColumn
|
||||||
if (this.$useWrapMode) {
|
// position returned is not associated to the next row on the screen.
|
||||||
if (docColumn >= column) {
|
if (this.$useWrapMode && docColumn >= column) {
|
||||||
// We remove one character at the end such that the docColumn
|
docColumn = column - 1;
|
||||||
// position returned is not associated to the next row on the
|
|
||||||
// screen.
|
|
||||||
docColumn = column - 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
docColumn = Math.min(docColumn, line.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foldLine) {
|
if (foldLine) {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ var Fold = exports.Fold = function(range, placeholder) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addSubFold = function(fold) {
|
this.addSubFold = function(fold) {
|
||||||
if (this.range.isEequal(fold))
|
if (this.range.isEqual(fold))
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
if (!this.range.containsRange(fold))
|
if (!this.range.containsRange(fold))
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ function Folding() {
|
||||||
// sometimes singleline folds can be missed by the code above
|
// sometimes singleline folds can be missed by the code above
|
||||||
if (!range.isMultiLine()) {
|
if (!range.isMultiLine()) {
|
||||||
fold = this.getFoldAt(range.start.row, range.start.column, 1);
|
fold = this.getFoldAt(range.start.row, range.start.column, 1);
|
||||||
if (fold && range.isEequal(fold.range)) {
|
if (fold && range.isEqual(fold.range)) {
|
||||||
this.removeFold(fold);
|
this.removeFold(fold);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,76 +41,126 @@ define(function(require, exports, module) {
|
||||||
|
|
||||||
var keyUtil = require("../lib/keys");
|
var keyUtil = require("../lib/keys");
|
||||||
|
|
||||||
function HashHandler(config) {
|
function HashHandler(config, platform) {
|
||||||
this.setConfig(config);
|
this.platform = platform;
|
||||||
}
|
this.commands = {};
|
||||||
|
this.commmandKeyBinding = {};
|
||||||
|
|
||||||
|
this.addCommands(config);
|
||||||
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
function splitSafe(s, separator, limit, bLowerCase) {
|
|
||||||
return (bLowerCase && s.toLowerCase() || s)
|
this.addCommand = function(command) {
|
||||||
.replace(/(?:^\s+|\n|\s+$)/g, "")
|
if (this.commands[command.name])
|
||||||
.split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999);
|
this.removeCommand(command);
|
||||||
}
|
|
||||||
|
this.commands[command.name] = command;
|
||||||
|
|
||||||
|
if (command.bindKey) {
|
||||||
|
this._buildKeyHash(command);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeCommand = function(command) {
|
||||||
|
var name = (typeof command === 'string' ? command : command.name);
|
||||||
|
command = this.commands[name];
|
||||||
|
delete this.commands[name];
|
||||||
|
|
||||||
|
// exhaustive search is brute force but since removeCommand is
|
||||||
|
// not a performance critical operation this should be OK
|
||||||
|
var ckb = this.commmandKeyBinding;
|
||||||
|
for (var hashId in ckb) {
|
||||||
|
for (var key in ckb[hashId]) {
|
||||||
|
if (ckb[hashId][key] == command)
|
||||||
|
delete ckb[hashId][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addCommands = function(commands) {
|
||||||
|
commands && Object.keys(commands).forEach(function(name) {
|
||||||
|
var command = commands[name];
|
||||||
|
if (typeof command === "string")
|
||||||
|
return this.bindKey(command, name);
|
||||||
|
|
||||||
|
if (typeof command === "function")
|
||||||
|
command = { exec: command };
|
||||||
|
|
||||||
|
if (!command.name)
|
||||||
|
command.name = name;
|
||||||
|
|
||||||
|
this.addCommand(command);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeCommands = function(commands) {
|
||||||
|
Object.keys(commands).forEach(function(name) {
|
||||||
|
this.removeCommand(commands[name]);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.bindKey = function(key, command) {
|
||||||
|
if(!key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ckb = this.commmandKeyBinding;
|
||||||
|
key.split("|").forEach(function(keyPart) {
|
||||||
|
var binding = parseKeys(keyPart, command);
|
||||||
|
var hashId = binding.hashId;
|
||||||
|
(ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.bindKeys = function(keyList) {
|
||||||
|
Object.keys(keyList).forEach(function(key) {
|
||||||
|
this.bindKey(key, keyList[key]);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
this._buildKeyHash = function(command) {
|
||||||
|
var binding = command.bindKey;
|
||||||
|
if (!binding)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var key = typeof binding == "string" ? binding: binding[this.platform];
|
||||||
|
this.bindKey(key, command);
|
||||||
|
};
|
||||||
|
|
||||||
function parseKeys(keys, val, ret) {
|
function parseKeys(keys, val, ret) {
|
||||||
var key,
|
var key;
|
||||||
hashId = 0,
|
var hashId = 0;
|
||||||
parts = splitSafe(keys, "\\-", null, true),
|
var parts = splitSafe(keys.toLowerCase());
|
||||||
i = 0,
|
|
||||||
l = parts.length;
|
|
||||||
|
|
||||||
for (; i < l; ++i) {
|
for (var i = 0, l = parts.length; i < l; i++) {
|
||||||
if (keyUtil.KEY_MODS[parts[i]])
|
if (keyUtil.KEY_MODS[parts[i]])
|
||||||
hashId = hashId | keyUtil.KEY_MODS[parts[i]];
|
hashId = hashId | keyUtil.KEY_MODS[parts[i]];
|
||||||
else
|
else
|
||||||
key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
|
key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
(ret[hashId] || (ret[hashId] = {}))[key] = val;
|
return {
|
||||||
return ret;
|
key: key,
|
||||||
|
hashId: hashId
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function objectReverse(obj, keySplit) {
|
function splitSafe(s) {
|
||||||
var i, j, l, key,
|
return (s.trim()
|
||||||
ret = {};
|
.split(new RegExp("[\\s ]*\\-[\\s ]*", "g"), 999));
|
||||||
for (i in obj) {
|
|
||||||
key = obj[i];
|
|
||||||
if (keySplit && typeof key == "string") {
|
|
||||||
key = key.split(keySplit);
|
|
||||||
for (j = 0, l = key.length; j < l; ++j)
|
|
||||||
parseKeys.call(this, key[j], i, ret);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
parseKeys.call(this, key, i, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setConfig = function(config) {
|
this.findKeyCommand = function findKeyCommand(hashId, keyString) {
|
||||||
this.$config = config;
|
var ckbr = this.commmandKeyBinding;
|
||||||
if (typeof this.$config.reverse == "undefined")
|
return ckbr[hashId] && ckbr[hashId][keyString.toLowerCase()];
|
||||||
this.$config.reverse = objectReverse.call(this, this.$config, "|");
|
}
|
||||||
|
|
||||||
|
this.handleKeyboard = function(data, hashId, keyString, keyCode) {
|
||||||
|
return {
|
||||||
|
command: this.findKeyCommand(hashId, keyString)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called by keyBinding.
|
|
||||||
*/
|
|
||||||
this.handleKeyboard = function(data, hashId, textOrKey, keyCode) {
|
|
||||||
// Figure out if a commandKey was pressed or just some text was insert.
|
|
||||||
if (hashId != 0 || keyCode != 0) {
|
|
||||||
return {
|
|
||||||
command: (this.$config.reverse[hashId] || {})[textOrKey]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
command: "inserttext",
|
|
||||||
args: {
|
|
||||||
text: textOrKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).call(HashHandler.prototype)
|
}).call(HashHandler.prototype)
|
||||||
|
|
||||||
exports.HashHandler = HashHandler;
|
exports.HashHandler = HashHandler;
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,10 @@ var Cursor = function(parentEl) {
|
||||||
this.element.className = "ace_layer ace_cursor-layer";
|
this.element.className = "ace_layer ace_cursor-layer";
|
||||||
parentEl.appendChild(this.element);
|
parentEl.appendChild(this.element);
|
||||||
|
|
||||||
this.cursor = dom.createElement("div");
|
|
||||||
this.cursor.className = "ace_cursor ace_hidden";
|
|
||||||
this.element.appendChild(this.cursor);
|
|
||||||
|
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
|
|
||||||
|
this.cursors = [];
|
||||||
|
this.cursor = this.addCursor();
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
@ -65,30 +64,54 @@ var Cursor = function(parentEl) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.addCursor = function() {
|
||||||
|
var el = dom.createElement("div");
|
||||||
|
var className = "ace_cursor";
|
||||||
|
if (!this.isVisible)
|
||||||
|
className += " ace_hidden";
|
||||||
|
if (this.overwrite)
|
||||||
|
className += " ace_overwrite";
|
||||||
|
|
||||||
|
el.className = className;
|
||||||
|
this.element.appendChild(el);
|
||||||
|
this.cursors.push(el);
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeCursor = function() {
|
||||||
|
if (this.cursors.length > 1) {
|
||||||
|
var el = this.cursors.pop();
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.hideCursor = function() {
|
this.hideCursor = function() {
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
dom.addCssClass(this.cursor, "ace_hidden");
|
for (var i = this.cursors.length; i--; )
|
||||||
|
dom.addCssClass(this.cursors[i], "ace_hidden");
|
||||||
clearInterval(this.blinkId);
|
clearInterval(this.blinkId);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.showCursor = function() {
|
this.showCursor = function() {
|
||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
dom.removeCssClass(this.cursor, "ace_hidden");
|
for (var i = this.cursors.length; i--; )
|
||||||
this.cursor.style.visibility = "visible";
|
dom.removeCssClass(this.cursors[i], "ace_hidden");
|
||||||
|
|
||||||
|
this.element.style.visibility = "";
|
||||||
this.restartTimer();
|
this.restartTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.restartTimer = function() {
|
this.restartTimer = function() {
|
||||||
clearInterval(this.blinkId);
|
clearInterval(this.blinkId);
|
||||||
if (!this.isVisible) {
|
if (!this.isVisible)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
var cursor = this.cursor;
|
var element = this.element;
|
||||||
this.blinkId = setInterval(function() {
|
this.blinkId = setInterval(function() {
|
||||||
cursor.style.visibility = "hidden";
|
element.style.visibility = "hidden";
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
cursor.style.visibility = "visible";
|
element.style.visibility = "visible";
|
||||||
}, 400);
|
}, 400);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
@ -118,25 +141,53 @@ var Cursor = function(parentEl) {
|
||||||
this.update = function(config) {
|
this.update = function(config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
this.pixelPos = this.getPixelPosition(null, true);
|
if (this.session.selectionMarkerCount > 1) {
|
||||||
|
var selections = this.session.$selectionMarkers;
|
||||||
|
var i = 0, sel, cursorIndex = 0;
|
||||||
|
|
||||||
this.cursor.style.left = this.pixelPos.left + "px";
|
for (var i = selections.length; i--; ) {
|
||||||
this.cursor.style.top = this.pixelPos.top + "px";
|
sel = selections[i];
|
||||||
this.cursor.style.width = config.characterWidth + "px";
|
var pixelPos = this.getPixelPosition(sel.cursor, true);
|
||||||
this.cursor.style.height = config.lineHeight + "px";
|
|
||||||
|
|
||||||
var overwrite = this.session.getOverwrite()
|
var style = (this.cursors[cursorIndex++] || this.addCursor()).style;
|
||||||
if (overwrite != this.overwrite) {
|
|
||||||
this.overwrite = overwrite;
|
style.left = pixelPos.left + "px";
|
||||||
if (overwrite)
|
style.top = pixelPos.top + "px";
|
||||||
dom.addCssClass(this.cursor, "ace_overwrite");
|
style.width = config.characterWidth + "px";
|
||||||
else
|
style.height = config.lineHeight + "px";
|
||||||
dom.removeCssClass(this.cursor, "ace_overwrite");
|
}
|
||||||
|
if (cursorIndex > 1)
|
||||||
|
while (this.cursors.length > cursorIndex)
|
||||||
|
this.removeCursor();
|
||||||
|
} else {
|
||||||
|
var pixelPos = this.getPixelPosition(null, true);
|
||||||
|
var style = this.cursor.style;
|
||||||
|
style.left = pixelPos.left + "px";
|
||||||
|
style.top = pixelPos.top + "px";
|
||||||
|
style.width = config.characterWidth + "px";
|
||||||
|
style.height = config.lineHeight + "px";
|
||||||
|
|
||||||
|
while (this.cursors.length > 1)
|
||||||
|
this.removeCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var overwrite = this.session.getOverwrite();
|
||||||
|
if (overwrite != this.overwrite)
|
||||||
|
this.$setOverite(overwrite);
|
||||||
|
|
||||||
this.restartTimer();
|
this.restartTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.$setOverite = function(overwrite) {
|
||||||
|
this.overwrite = overwrite;
|
||||||
|
for (var i = this.cursors.length; i--; ) {
|
||||||
|
if (overwrite)
|
||||||
|
dom.addCssClass(this.cursors[i], "ace_overwrite");
|
||||||
|
else
|
||||||
|
dom.removeCssClass(this.cursors[i], "ace_overwrite");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.destroy = function() {
|
this.destroy = function() {
|
||||||
clearInterval(this.blinkId);
|
clearInterval(this.blinkId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ var Marker = function(parentEl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a marker, which spans a range of text in a single line
|
* Draws a marker, which spans a range of text on multiple lines
|
||||||
*/
|
*/
|
||||||
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
|
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
|
||||||
// selection start
|
// selection start
|
||||||
|
|
@ -187,7 +187,7 @@ var Marker = function(parentEl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a marker which covers one single full line
|
* Draws a marker which covers part or whole width of a single screen line
|
||||||
*/
|
*/
|
||||||
this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength, type) {
|
this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength, type) {
|
||||||
var padding = type === "background" ? 0 : this.$padding;
|
var padding = type === "background" ? 0 : this.$padding;
|
||||||
|
|
|
||||||
|
|
@ -302,16 +302,16 @@ var Text = function(parentEl) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$renderLinesFragment = function(config, firstRow, lastRow) {
|
this.$renderLinesFragment = function(config, firstRow, lastRow) {
|
||||||
var fragment = this.element.ownerDocument.createDocumentFragment(),
|
var fragment = this.element.ownerDocument.createDocumentFragment();
|
||||||
row = firstRow,
|
var row = firstRow;
|
||||||
fold = this.session.getNextFoldLine(row),
|
var foldLine = this.session.getNextFoldLine(row);
|
||||||
foldStart = fold ?fold.start.row :Infinity;
|
var foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (row > foldStart) {
|
if (row > foldStart) {
|
||||||
row = fold.end.row+1;
|
row = foldLine.end.row+1;
|
||||||
fold = this.session.getNextFoldLine(row, fold);
|
foldLine = this.session.getNextFoldLine(row, foldLine);
|
||||||
foldStart = fold ?fold.start.row :Infinity;
|
foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||||
}
|
}
|
||||||
if (row > lastRow)
|
if (row > lastRow)
|
||||||
break;
|
break;
|
||||||
|
|
@ -350,15 +350,15 @@ var Text = function(parentEl) {
|
||||||
var html = [];
|
var html = [];
|
||||||
var firstRow = config.firstRow, lastRow = config.lastRow;
|
var firstRow = config.firstRow, lastRow = config.lastRow;
|
||||||
|
|
||||||
var row = firstRow,
|
var row = firstRow;
|
||||||
fold = this.session.getNextFoldLine(row),
|
var foldLine = this.session.getNextFoldLine(row);
|
||||||
foldStart = fold ?fold.start.row :Infinity;
|
var foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (row > foldStart) {
|
if (row > foldStart) {
|
||||||
row = fold.end.row+1;
|
row = foldLine.end.row+1;
|
||||||
fold = this.session.getNextFoldLine(row, fold);
|
foldLine = this.session.getNextFoldLine(row, foldLine);
|
||||||
foldStart = fold ?fold.start.row :Infinity;
|
foldStart = foldLine ? foldLine.start.row :Infinity;
|
||||||
}
|
}
|
||||||
if (row > lastRow)
|
if (row > lastRow)
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ function DefaultHandlers(editor) {
|
||||||
if (!editor.$mouseHandler.$clickSelection) {
|
if (!editor.$mouseHandler.$clickSelection) {
|
||||||
if (!dragCursor) {
|
if (!dragCursor) {
|
||||||
editor.moveCursorToPosition(pos);
|
editor.moveCursorToPosition(pos);
|
||||||
editor.selection.clearSelection(pos.row, pos.column);
|
editor.selection.clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +169,6 @@ function DefaultHandlers(editor) {
|
||||||
if (distance > DRAG_OFFSET) {
|
if (distance > DRAG_OFFSET) {
|
||||||
state = STATE_SELECT;
|
state = STATE_SELECT;
|
||||||
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
||||||
cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
|
|
||||||
onStartSelect(cursor);
|
onStartSelect(cursor);
|
||||||
}
|
}
|
||||||
else if ((time - mousedownTime) > editor.getDragDelay()) {
|
else if ((time - mousedownTime) > editor.getDragDelay()) {
|
||||||
|
|
@ -196,7 +195,7 @@ function DefaultHandlers(editor) {
|
||||||
else {
|
else {
|
||||||
if (!_self.$clickSelection) {
|
if (!_self.$clickSelection) {
|
||||||
editor.moveCursorToPosition(pos);
|
editor.moveCursorToPosition(pos);
|
||||||
editor.selection.clearSelection(pos.row, pos.column);
|
editor.selection.clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state = STATE_SELECT;
|
state = STATE_SELECT;
|
||||||
|
|
@ -205,7 +204,6 @@ function DefaultHandlers(editor) {
|
||||||
var onUpdateSelectionInterval = function() {
|
var onUpdateSelectionInterval = function() {
|
||||||
var anchor;
|
var anchor;
|
||||||
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
||||||
cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
|
|
||||||
|
|
||||||
if (_self.$clickSelection) {
|
if (_self.$clickSelection) {
|
||||||
if (_self.$clickSelection.contains(cursor.row, cursor.column)) {
|
if (_self.$clickSelection.contains(cursor.row, cursor.column)) {
|
||||||
|
|
@ -231,8 +229,6 @@ function DefaultHandlers(editor) {
|
||||||
|
|
||||||
var onDragSelectionInterval = function() {
|
var onDragSelectionInterval = function() {
|
||||||
dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
||||||
dragCursor.row = Math.max(0, Math.min(dragCursor.row, editor.session.getLength() - 1));
|
|
||||||
|
|
||||||
editor.moveCursorToPosition(dragCursor);
|
editor.moveCursorToPosition(dragCursor);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ function FoldHandler(editor) {
|
||||||
var position = e.getDocumentPosition();
|
var position = e.getDocumentPosition();
|
||||||
var session = editor.session;
|
var session = editor.session;
|
||||||
|
|
||||||
// If the user dclicked on a fold, then expand it.
|
// If the user clicked on a fold, then expand it.
|
||||||
var fold = session.getFoldAt(position.row, position.column, 1);
|
var fold = session.getFoldAt(position.row, position.column, 1);
|
||||||
if (fold) {
|
if (fold) {
|
||||||
if (e.getAccelKey())
|
if (e.getAccelKey())
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) {
|
||||||
var pageX = event.getDocumentX(this.domEvent);
|
var pageX = event.getDocumentX(this.domEvent);
|
||||||
var pageY = event.getDocumentY(this.domEvent);
|
var pageY = event.getDocumentY(this.domEvent);
|
||||||
this.$pos = this.editor.renderer.screenToTextCoordinates(pageX, pageY);
|
this.$pos = this.editor.renderer.screenToTextCoordinates(pageX, pageY);
|
||||||
this.$pos.row = Math.max(0, Math.min(this.$pos.row, this.editor.session.getLength()-1));
|
|
||||||
return this.$pos;
|
return this.$pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
164
lib/ace/mouse/multi_select_handler.js
Normal file
164
lib/ace/mouse/multi_select_handler.js
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* 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 <amirjanyan AT gmail DOT com>
|
||||||
|
*
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
var event = require("../lib/event");
|
||||||
|
|
||||||
|
|
||||||
|
// mouse
|
||||||
|
function isSamePoint(p1, p2) {
|
||||||
|
return p1.row == p2.row && p1.column == p2.column
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseDown(e) {
|
||||||
|
var ev = e.domEvent;
|
||||||
|
var alt = ev.altKey;
|
||||||
|
var shift = ev.shiftKey;
|
||||||
|
var ctrl = e.getAccelKey();
|
||||||
|
var button = e.getButton();
|
||||||
|
|
||||||
|
if (!ctrl && !alt) {
|
||||||
|
if (e.editor.inMultiSelectMode) {
|
||||||
|
if (button == 0) {
|
||||||
|
e.editor.exitMultiSelectMode();
|
||||||
|
} else if (button == 2) {
|
||||||
|
var editor = e.editor;
|
||||||
|
var selectionEmpty = editor.selection.isEmpty();
|
||||||
|
editor.textInput.onContextMenu({x: e.clientX, y: e.clientY}, selectionEmpty);
|
||||||
|
event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose);
|
||||||
|
e.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var editor = e.editor;
|
||||||
|
var selection = editor.selection;
|
||||||
|
var isMultiSelect = editor.inMultiSelectMode;
|
||||||
|
var pos = e.getDocumentPosition();
|
||||||
|
var cursor = selection.getCursor()
|
||||||
|
var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor));
|
||||||
|
|
||||||
|
|
||||||
|
var mouseX = e.pageX, mouseY = e.pageY;
|
||||||
|
var clickX = mouseX, clickY = mouseY;
|
||||||
|
var onMouseSelection = function(e) {
|
||||||
|
mouseX = event.getDocumentX(e);
|
||||||
|
mouseY = event.getDocumentY(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
var blockSelect = function() {
|
||||||
|
var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY);
|
||||||
|
var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column);
|
||||||
|
|
||||||
|
if (isSamePoint(screenCursor, newCursor)
|
||||||
|
&& isSamePoint(cursor, selection.selectionLead))
|
||||||
|
return;
|
||||||
|
screenCursor = newCursor;
|
||||||
|
|
||||||
|
editor.selection.moveCursorToPosition(cursor);
|
||||||
|
editor.selection.clearSelection();
|
||||||
|
editor.renderer.scrollCursorIntoView();
|
||||||
|
|
||||||
|
editor.removeSelectionMarkers(rectSel);
|
||||||
|
rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor);
|
||||||
|
rectSel.forEach(editor.addSelectionMarker, editor);
|
||||||
|
editor.updateSelectionMarkers()
|
||||||
|
};
|
||||||
|
|
||||||
|
var session = editor.session;
|
||||||
|
var anchor = selection.getCursor();
|
||||||
|
var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY);
|
||||||
|
var clippedAnchor = session.documentToScreenPosition(anchor);
|
||||||
|
var screenCursor = screenAnchor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (ctrl && !shift && !alt && button == 0) {
|
||||||
|
if (!isMultiSelect && inSelection)
|
||||||
|
return // dragging
|
||||||
|
|
||||||
|
if (!isMultiSelect)
|
||||||
|
selection.addRange(selection.toOrientedRange());
|
||||||
|
|
||||||
|
|
||||||
|
var oldRange = selection.rangeList.rangeAtPoint(pos);
|
||||||
|
|
||||||
|
event.capture(editor.container, function(){}, function() {
|
||||||
|
var tmpSel = selection.toOrientedRange();
|
||||||
|
|
||||||
|
if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor))
|
||||||
|
selection.substractPoint(tmpSel.cursor);
|
||||||
|
else
|
||||||
|
selection.addRange(tmpSel);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (!shift && alt && button == 0) {
|
||||||
|
e.stop()
|
||||||
|
|
||||||
|
if (isMultiSelect && !ctrl)
|
||||||
|
selection.toSingleRange();
|
||||||
|
else if (!isMultiSelect && ctrl)
|
||||||
|
selection.addRange();
|
||||||
|
|
||||||
|
selection.moveCursorToPosition(pos);
|
||||||
|
selection.clearSelection();
|
||||||
|
|
||||||
|
var rectSel = [];
|
||||||
|
|
||||||
|
var onMouseSelectionEnd = function(e) {
|
||||||
|
clearInterval(timerId);
|
||||||
|
editor.removeSelectionMarkers(rectSel);
|
||||||
|
for (var i = 0; i < rectSel.length; i++)
|
||||||
|
selection.addRange(rectSel[i])
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelectionInterval = blockSelect;
|
||||||
|
|
||||||
|
event.capture(editor.container, onMouseSelection, onMouseSelectionEnd);
|
||||||
|
var timerId = setInterval(function() {onSelectionInterval()}, 20);
|
||||||
|
|
||||||
|
return e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.onMouseDown = onMouseDown;
|
||||||
|
|
||||||
|
});
|
||||||
581
lib/ace/multi_select.js
Normal file
581
lib/ace/multi_select.js
Normal file
|
|
@ -0,0 +1,581 @@
|
||||||
|
/* 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 <amirjanyan AT gmail DOT com>
|
||||||
|
*
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
var RangeList = require("./range_list").RangeList;
|
||||||
|
var Range = require("./range").Range;
|
||||||
|
var Selection = require("./selection").Selection;
|
||||||
|
var onMouseDown = require("./mouse/multi_select_handler").onMouseDown;
|
||||||
|
exports.commands = require("./commands/multi_select_commands");
|
||||||
|
|
||||||
|
// Todo: session.find or editor.findVolatile that returns range
|
||||||
|
var Search = require("./search").Search;
|
||||||
|
var search = new Search();
|
||||||
|
|
||||||
|
function find(session, needle, dir) {
|
||||||
|
search.$options.wrap = true;
|
||||||
|
search.$options.needle = needle;
|
||||||
|
search.$options.backwards = dir == -1;
|
||||||
|
return search.find(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend EditSession
|
||||||
|
var EditSession = require("./edit_session").EditSession;
|
||||||
|
;(function() {
|
||||||
|
this.getSelectionMarkers = function() {
|
||||||
|
return this.$selectionMarkers;
|
||||||
|
};
|
||||||
|
}).call(EditSession.prototype);
|
||||||
|
|
||||||
|
// extend Selection
|
||||||
|
;(function() {
|
||||||
|
// list of ranges in reverse addition order
|
||||||
|
this.ranges = null;
|
||||||
|
|
||||||
|
// automatically sorted list of ranges
|
||||||
|
this.rangeList = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a range to selection entering multiselect mode if necessary
|
||||||
|
*/
|
||||||
|
this.addRange = function(range) {
|
||||||
|
if (!this.inMultiSelectMode && this.rangeCount == 0) {
|
||||||
|
var oldRange = this.toOrientedRange();
|
||||||
|
if (!range || !range.isEqual(oldRange)) {
|
||||||
|
this.rangeList.add(oldRange);
|
||||||
|
this.$onAddRange(oldRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!range.cursor)
|
||||||
|
range.cursor = range.end;
|
||||||
|
|
||||||
|
var removed = this.rangeList.add(range);
|
||||||
|
|
||||||
|
this.$onAddRange(range);
|
||||||
|
|
||||||
|
if (removed.length)
|
||||||
|
this.$onRemoveRange(removed);
|
||||||
|
|
||||||
|
if (this.rangeCount > 0 && !this.inMultiSelectMode) {
|
||||||
|
this._emit("multiSelect");
|
||||||
|
this.inMultiSelectMode = true;
|
||||||
|
this.session.$undoSelect = false;
|
||||||
|
this.rangeList.attach(this.session);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.toSingleRange = function(range) {
|
||||||
|
range = range || this.ranges[0];
|
||||||
|
var removed = this.rangeList.removeAll();
|
||||||
|
if (removed.length)
|
||||||
|
this.$onRemoveRange(removed);
|
||||||
|
|
||||||
|
range && this.fromOrientedRange(range);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.substractPoint = function(pos) {
|
||||||
|
var removed = this.rangeList.substractPoint(pos);
|
||||||
|
if (removed) {
|
||||||
|
this.$onRemoveRange(removed);
|
||||||
|
return removed[0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.mergeOverlappingRanges = function() {
|
||||||
|
var removed = this.rangeList.merge();
|
||||||
|
if (removed.length)
|
||||||
|
this.$onRemoveRange(removed);
|
||||||
|
else if(this.ranges[0])
|
||||||
|
this.fromOrientedRange(this.ranges[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onAddRange = function(range) {
|
||||||
|
this.rangeCount = this.rangeList.ranges.length;
|
||||||
|
this.ranges.unshift(range);
|
||||||
|
this.fromOrientedRange(range);
|
||||||
|
this._emit("addRange", {range: range});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onRemoveRange = function(removed) {
|
||||||
|
this.rangeCount = this.rangeList.ranges.length;
|
||||||
|
if (this.rangeCount == 1 && this.inMultiSelectMode) {
|
||||||
|
var lastRange = this.rangeList.ranges.pop()
|
||||||
|
removed.push(lastRange);
|
||||||
|
this.rangeCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = removed.length; i--; ) {
|
||||||
|
var index = this.ranges.indexOf(removed[i]);
|
||||||
|
this.ranges.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._emit("removeRange", {ranges: removed});
|
||||||
|
|
||||||
|
if (this.rangeCount == 0 && this.inMultiSelectMode) {
|
||||||
|
this.inMultiSelectMode = false;
|
||||||
|
this._emit("singleSelect");
|
||||||
|
this.session.$undoSelect = true;
|
||||||
|
this.rangeList.detach(this.session);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastRange = lastRange || this.ranges[0];
|
||||||
|
if (lastRange && !lastRange.isEqual(this.getRange()))
|
||||||
|
this.fromOrientedRange(lastRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
// adds multicursor support to selection
|
||||||
|
this.$initRangeList = function() {
|
||||||
|
if (this.rangeList)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.rangeList = new RangeList();
|
||||||
|
this.ranges = [];
|
||||||
|
this.rangeCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getAllRanges = function() {
|
||||||
|
return this.rangeList.ranges.concat();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.splitIntoLines = function () {
|
||||||
|
if (this.rangeCount > 1) {
|
||||||
|
var ranges = this.rangeList.ranges;
|
||||||
|
var lastRange = ranges[ranges.length - 1];
|
||||||
|
var range = Range.fromPoints(ranges[0].start, lastRange.end);
|
||||||
|
|
||||||
|
this.toSingleRange();
|
||||||
|
this.setSelectionRange(range, lastRange.cursor == lastRange.start);
|
||||||
|
} else {
|
||||||
|
var cursor = this.session.documentToScreenPosition(this.selectionLead);
|
||||||
|
var anchor = this.session.documentToScreenPosition(this.selectionAnchor);
|
||||||
|
|
||||||
|
var rectSel = this.rectangularRangeBlock(cursor, anchor);
|
||||||
|
rectSel.forEach(this.addRange, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets list of ranges composing rectangular block on the screen
|
||||||
|
* @includeEmptyLines if true includes ranges inside the block which
|
||||||
|
* are empty becuase of the clipping
|
||||||
|
*/
|
||||||
|
this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) {
|
||||||
|
var rectSel = [];
|
||||||
|
|
||||||
|
var xBackwards = screenCursor.column < screenAnchor.column;
|
||||||
|
if (xBackwards) {
|
||||||
|
var startColumn = screenCursor.column;
|
||||||
|
var endColumn = screenAnchor.column;
|
||||||
|
} else {
|
||||||
|
var startColumn = screenAnchor.column;
|
||||||
|
var endColumn = screenCursor.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
var yBackwards = screenCursor.row < screenAnchor.row;
|
||||||
|
if (yBackwards) {
|
||||||
|
var startRow = screenCursor.row;
|
||||||
|
var endRow = screenAnchor.row;
|
||||||
|
} else {
|
||||||
|
var startRow = screenAnchor.row;
|
||||||
|
var endRow = screenCursor.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startColumn < 0)
|
||||||
|
startColumn = 0;
|
||||||
|
if (startRow < 0)
|
||||||
|
startRow = 0;
|
||||||
|
|
||||||
|
if (startRow == endRow)
|
||||||
|
includeEmptyLines = true;
|
||||||
|
|
||||||
|
for (var row = startRow; row <= endRow; row++) {
|
||||||
|
var range = Range.fromPoints(
|
||||||
|
this.session.screenToDocumentPosition(row, startColumn),
|
||||||
|
this.session.screenToDocumentPosition(row, endColumn)
|
||||||
|
);
|
||||||
|
if (range.isEmpty()) {
|
||||||
|
if (docEnd && isSamePoint(range.end, docEnd))
|
||||||
|
break;
|
||||||
|
var docEnd = range.end;
|
||||||
|
}
|
||||||
|
range.cursor = xBackwards ? range.start : range.end;
|
||||||
|
rectSel.push(range);
|
||||||
|
}
|
||||||
|
if (yBackwards)
|
||||||
|
rectSel.reverse();
|
||||||
|
|
||||||
|
if (!includeEmptyLines) {
|
||||||
|
var end = rectSel.length - 1;
|
||||||
|
while (rectSel[end].isEmpty() && end > 0)
|
||||||
|
end--;
|
||||||
|
if (end > 0) {
|
||||||
|
var start = 0;
|
||||||
|
while (rectSel[start].isEmpty())
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
for (var i = end; i >= start; i--) {
|
||||||
|
if (rectSel[i].isEmpty())
|
||||||
|
rectSel.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rectSel;
|
||||||
|
};
|
||||||
|
}).call(Selection.prototype);
|
||||||
|
|
||||||
|
// extend Editor
|
||||||
|
var Editor = require("./editor").Editor;
|
||||||
|
;(function() {
|
||||||
|
this.updateSelectionMarkers = function() {
|
||||||
|
this.renderer.updateCursor();
|
||||||
|
this.renderer.updateBackMarkers();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addSelectionMarker = function(orientedRange) {
|
||||||
|
if (!orientedRange.cursor)
|
||||||
|
orientedRange.cursor = orientedRange.end;
|
||||||
|
|
||||||
|
var style = this.getSelectionStyle();
|
||||||
|
orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style);
|
||||||
|
|
||||||
|
this.session.$selectionMarkers.push(orientedRange);
|
||||||
|
this.session.selectionMarkerCount = this.session.$selectionMarkers.length;
|
||||||
|
return orientedRange;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeSelectionMarkers = function(ranges) {
|
||||||
|
for (var i = ranges.length; i--; ) {
|
||||||
|
var range = ranges[i];
|
||||||
|
if (!range.marker)
|
||||||
|
continue;
|
||||||
|
this.session.removeMarker(range.marker);
|
||||||
|
var index = this.session.$selectionMarkers.indexOf(range);
|
||||||
|
if (index != -1)
|
||||||
|
this.session.$selectionMarkers.splice(index, 1)
|
||||||
|
}
|
||||||
|
this.session.selectionMarkerCount = this.session.$selectionMarkers.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onAddRange = function(e) {
|
||||||
|
this.addSelectionMarker(e.range);
|
||||||
|
this.renderer.updateCursor();
|
||||||
|
this.renderer.updateBackMarkers();
|
||||||
|
};
|
||||||
|
this.$onRemoveRange = function(e) {
|
||||||
|
this.removeSelectionMarkers(e.ranges);
|
||||||
|
this.renderer.updateCursor();
|
||||||
|
this.renderer.updateBackMarkers();
|
||||||
|
};
|
||||||
|
this.$onMultiSelect = function(e) {
|
||||||
|
if (this.inMultiSelectMode)
|
||||||
|
return;
|
||||||
|
this.inMultiSelectMode = true;
|
||||||
|
|
||||||
|
this.setStyle("multiselect");
|
||||||
|
this.keyBinding.addKeyboardHandler(exports.commands.keyboardHandler);
|
||||||
|
this.commands.on("exec", this.$onMultiSelectExec);
|
||||||
|
|
||||||
|
this.renderer.updateCursor();
|
||||||
|
this.renderer.updateBackMarkers();
|
||||||
|
};
|
||||||
|
this.$onSingleSelect = function(e) {
|
||||||
|
if (this.session.multiSelect.inVirtualMode)
|
||||||
|
return;
|
||||||
|
this.inMultiSelectMode = false;
|
||||||
|
|
||||||
|
this.unsetStyle("multiselect");
|
||||||
|
this.keyBinding.removeKeyboardHandler(exports.commands.keyboardHandler);
|
||||||
|
|
||||||
|
this.commands.removeEventListener("exec", this.$onMultiSelectExec);
|
||||||
|
this.renderer.updateCursor();
|
||||||
|
this.renderer.updateBackMarkers();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onMultiSelectExec = function(e) {
|
||||||
|
var command = e.command;
|
||||||
|
var editor = e.editor;
|
||||||
|
if (!command.multiSelectAction) {
|
||||||
|
command.exec(editor, e.args || {});
|
||||||
|
editor.multiSelect.addRange(editor.multiSelect.toOrientedRange());
|
||||||
|
editor.multiSelect.mergeOverlappingRanges();
|
||||||
|
} else if (command.multiSelectAction == "forEach") {
|
||||||
|
editor.forEachSelection(command, e.args);
|
||||||
|
} else if (command.multiSelectAction == "single") {
|
||||||
|
editor.exitMultiSelectMode();
|
||||||
|
command.exec(editor, e.args || {});
|
||||||
|
} else {
|
||||||
|
command.multiSelectAction(editor, e.args || {});
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.forEachSelection = function(cmd, args) {
|
||||||
|
if (this.inVirtualSelectionMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var session = this.session;
|
||||||
|
var selection = this.selection;
|
||||||
|
var rangeList = selection.rangeList;
|
||||||
|
|
||||||
|
var reg = selection._eventRegistry;
|
||||||
|
selection._eventRegistry = {};
|
||||||
|
|
||||||
|
var tmpSel = new Selection(session);
|
||||||
|
this.inVirtualSelectionMode = true;
|
||||||
|
for (var i = rangeList.ranges.length; i--;) {
|
||||||
|
tmpSel.fromOrientedRange(rangeList.ranges[i]);
|
||||||
|
this.selection = session.selection = tmpSel;
|
||||||
|
cmd.exec(this, args || {});
|
||||||
|
tmpSel.toOrientedRange(rangeList.ranges[i]);
|
||||||
|
}
|
||||||
|
tmpSel.detach();
|
||||||
|
|
||||||
|
this.selection = session.selection = selection;
|
||||||
|
this.inVirtualSelectionMode = false;
|
||||||
|
selection._eventRegistry = reg;
|
||||||
|
selection.mergeOverlappingRanges();
|
||||||
|
|
||||||
|
this.onCursorChange();
|
||||||
|
this.onSelectionChange();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.exitMultiSelectMode = function() {
|
||||||
|
if (this.inVirtualSelectionMode)
|
||||||
|
return;
|
||||||
|
this.multiSelect.toSingleRange();
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo route copy/cut/paste through commandmanager
|
||||||
|
this.getCopyText = function() {
|
||||||
|
var text = "";
|
||||||
|
if (this.inMultiSelectMode) {
|
||||||
|
var ranges = this.multiSelect.rangeList.ranges;
|
||||||
|
text = [];
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
text.push(this.session.getTextRange(ranges[i]));
|
||||||
|
}
|
||||||
|
text = text.join(this.session.getDocument().getNewLineCharacter());
|
||||||
|
} else if (!this.selection.isEmpty()) {
|
||||||
|
text = this.session.getTextRange(this.getSelectionRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onCut = function() {
|
||||||
|
var cmd = {
|
||||||
|
name: "cut",
|
||||||
|
exec: function(editor) {
|
||||||
|
var range = editor.getSelectionRange();
|
||||||
|
editor._emit("cut", range);
|
||||||
|
|
||||||
|
if (!editor.selection.isEmpty()) {
|
||||||
|
editor.session.remove(range);
|
||||||
|
editor.clearSelection();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
readonly: true,
|
||||||
|
multiSelectAction: "forEach"
|
||||||
|
};
|
||||||
|
this.commands.exec(cmd, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
// commands
|
||||||
|
|
||||||
|
this.selectMoreLines = function(dir, skip) {
|
||||||
|
var range = this.selection.toOrientedRange();
|
||||||
|
var isBackwards = range.cursor == range.end;
|
||||||
|
|
||||||
|
var screenLead = this.session.documentToScreenPosition(range.cursor);
|
||||||
|
if (this.selection.$desiredColumn)
|
||||||
|
screenLead.column = this.selection.$desiredColumn;
|
||||||
|
|
||||||
|
var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column);
|
||||||
|
|
||||||
|
if (!range.isEmpty()) {
|
||||||
|
var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start);
|
||||||
|
var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column);
|
||||||
|
} else {
|
||||||
|
var anchor = lead;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBackwards) {
|
||||||
|
var newRange = Range.fromPoints(lead, anchor);
|
||||||
|
newRange.cursor = newRange.start;
|
||||||
|
} else {
|
||||||
|
var newRange = Range.fromPoints(anchor, lead);
|
||||||
|
newRange.cursor = newRange.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
newRange.desiredColumn = screenLead.column;
|
||||||
|
if (!this.selection.inMultiSelectMode) {
|
||||||
|
this.selection.addRange(range);
|
||||||
|
} else {
|
||||||
|
var allRanges = this.selection.rangeList.ranges;
|
||||||
|
// remove range if at end
|
||||||
|
// var atEdge = range.isEqual(allRanges[dir == 1 ? 0 : allRanges.length - 1]);
|
||||||
|
if (skip)
|
||||||
|
var toRemove = range.cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selection.addRange(newRange);
|
||||||
|
if (toRemove)
|
||||||
|
this.selection.substractPoint(toRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transposeSelections = function(dir) {
|
||||||
|
var session = this.session;
|
||||||
|
var sel = session.multiSelect;
|
||||||
|
var all = sel.ranges;
|
||||||
|
|
||||||
|
var words = [];
|
||||||
|
for (var i = all.length; i--; ) {
|
||||||
|
var range = all[i];
|
||||||
|
if (range.isEmpty()) {
|
||||||
|
var tmp = session.getWordRange(range.start.row, range.start.column);
|
||||||
|
range.start.row = tmp.start.row;
|
||||||
|
range.start.column = tmp.start.column;
|
||||||
|
range.end.row = tmp.end.row;
|
||||||
|
range.end.column = tmp.end.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
words.unshift(this.session.getTextRange(range));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir < 0)
|
||||||
|
words.unshift(words.pop());
|
||||||
|
else
|
||||||
|
words.push(words.shift());
|
||||||
|
|
||||||
|
for (var i = all.length; i--; ) {
|
||||||
|
var range = all[i];
|
||||||
|
var tmp = range.clone();
|
||||||
|
session.replace(range, words[i]);
|
||||||
|
range.start.row = tmp.start.row;
|
||||||
|
range.start.column = tmp.start.column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectMore = function (dir, skip) {
|
||||||
|
var session = this.session;
|
||||||
|
var sel = session.multiSelect;
|
||||||
|
|
||||||
|
var range = sel.toOrientedRange();
|
||||||
|
if (range.isEmpty()) {
|
||||||
|
var range = session.getWordRange(range.start.row, range.start.column);
|
||||||
|
range.cursor = range.end;
|
||||||
|
this.multiSelect.addRange(range);
|
||||||
|
}
|
||||||
|
var needle = session.getTextRange(range);
|
||||||
|
|
||||||
|
var newRange = find(session, needle, dir);
|
||||||
|
if (newRange) {
|
||||||
|
newRange.cursor = dir == -1 ? newRange.start : newRange.end;
|
||||||
|
this.multiSelect.addRange(newRange);
|
||||||
|
}
|
||||||
|
if (skip)
|
||||||
|
this.multiSelect.substractPoint(range.cursor);
|
||||||
|
}
|
||||||
|
}).call(Editor.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
function isSamePoint(p1, p2) {
|
||||||
|
return p1.row == p2.row && p1.column == p2.column
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch
|
||||||
|
// adds multicursor support to a session
|
||||||
|
exports.onSessionChange = function(e) {
|
||||||
|
var session = e.session;
|
||||||
|
if (!session.multiSelect) {
|
||||||
|
session.$selectionMarkers = [];
|
||||||
|
session.selection.$initRangeList();
|
||||||
|
session.multiSelect = session.selection;
|
||||||
|
}
|
||||||
|
this.multiSelect = session.multiSelect;
|
||||||
|
|
||||||
|
var oldSession = e.oldSession;
|
||||||
|
if (oldSession) {
|
||||||
|
// todo use events
|
||||||
|
if (oldSession.multiSelect && oldSession.multiSelect.editor == this)
|
||||||
|
oldSession.multiSelect.editor = null;
|
||||||
|
|
||||||
|
session.multiSelect.removeEventListener("addRange", this.$onAddRange);
|
||||||
|
session.multiSelect.removeEventListener("removeRange", this.$onRemoveRange);
|
||||||
|
session.multiSelect.removeEventListener("multiSelect", this.$onMultiSelect);
|
||||||
|
session.multiSelect.removeEventListener("singleSelect", this.$onSingleSelect);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.multiSelect.on("addRange", this.$onAddRange);
|
||||||
|
session.multiSelect.on("removeRange", this.$onRemoveRange);
|
||||||
|
session.multiSelect.on("multiSelect", this.$onMultiSelect);
|
||||||
|
session.multiSelect.on("singleSelect", this.$onSingleSelect);
|
||||||
|
|
||||||
|
// this.$onSelectionChange = this.onSelectionChange.bind(this);
|
||||||
|
|
||||||
|
if (this.inMultiSelectMode != session.selection.inMultiSelectMode) {
|
||||||
|
if (session.selection.inMultiSelectMode)
|
||||||
|
this.$onMultiSelect();
|
||||||
|
else
|
||||||
|
this.$onSingleSelect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// adds multicursor support to editor instance
|
||||||
|
function MultiSelect(editor) {
|
||||||
|
editor.$onAddRange = editor.$onAddRange.bind(editor);
|
||||||
|
editor.$onRemoveRange = editor.$onRemoveRange.bind(editor);
|
||||||
|
editor.$onMultiSelect = editor.$onMultiSelect.bind(editor);
|
||||||
|
editor.$onSingleSelect = editor.$onSingleSelect.bind(editor);
|
||||||
|
|
||||||
|
exports.onSessionChange.call(editor, editor);
|
||||||
|
editor.on("changeSession", exports.onSessionChange.bind(editor));
|
||||||
|
|
||||||
|
editor.on("mousedown", onMouseDown);
|
||||||
|
editor.commands.addCommands(exports.commands.defaultCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.MultiSelect = MultiSelect;
|
||||||
|
|
||||||
|
});
|
||||||
144
lib/ace/multi_select_test.js
Normal file
144
lib/ace/multi_select_test.js
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
/* 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 <amirjanyan AT gmail DOT com>
|
||||||
|
*
|
||||||
|
* 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 ***** */
|
||||||
|
|
||||||
|
if (typeof process !== "undefined") {
|
||||||
|
require("amd-loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EditSession = require("./edit_session").EditSession;
|
||||||
|
var Editor = require("./editor").Editor;
|
||||||
|
var MockRenderer = require("./test/mockrenderer").MockRenderer;
|
||||||
|
var Range = require("./range").Range;
|
||||||
|
var assert = require("./test/assertions");
|
||||||
|
var MultiSelect = require("./multi_select").MultiSelect;
|
||||||
|
|
||||||
|
var editor
|
||||||
|
var exec = function(name, times, args) {
|
||||||
|
do {
|
||||||
|
editor.commands.exec(name, editor, args);
|
||||||
|
} while(times --> 1)
|
||||||
|
};
|
||||||
|
var testRanges = function(str) {
|
||||||
|
assert.equal(editor.selection.getAllRanges()+"", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
name: "ACE multi_select.js",
|
||||||
|
|
||||||
|
"test: multiselect editing": function() {
|
||||||
|
var doc = new EditSession([
|
||||||
|
"w1.w2",
|
||||||
|
" wtt.w",
|
||||||
|
" wtt.w"
|
||||||
|
]);
|
||||||
|
editor = new Editor(new MockRenderer(), doc);
|
||||||
|
MultiSelect(editor);
|
||||||
|
|
||||||
|
editor.navigateFileEnd();
|
||||||
|
exec("selectMoreBefore", 3);
|
||||||
|
assert.ok(editor.inMultiSelectMode);
|
||||||
|
assert.equal(editor.selection.getAllRanges().length, 4);
|
||||||
|
|
||||||
|
assert.equal(editor.getCopyText(), "wwww");
|
||||||
|
exec("insertstring", 1, "a");
|
||||||
|
exec("backspace", 2);
|
||||||
|
assert.equal(editor.session.getValue(), "w1.w2\ntt\ntt");
|
||||||
|
assert.equal(editor.selection.getAllRanges().length, 4);
|
||||||
|
|
||||||
|
exec("selectall");
|
||||||
|
assert.ok(!editor.inMultiSelectMode);
|
||||||
|
//assert.equal(editor.selection.getAllRanges().length, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: multiselect navigation": function() {
|
||||||
|
var doc = new EditSession([
|
||||||
|
"w1.w2",
|
||||||
|
" wtt.w",
|
||||||
|
" wtt.we"
|
||||||
|
]);
|
||||||
|
editor = new Editor(new MockRenderer(), doc);
|
||||||
|
MultiSelect(editor);
|
||||||
|
|
||||||
|
editor.selectMoreLines(1);
|
||||||
|
testRanges("Range: [0/0] -> [0/0],Range: [1/0] -> [1/0]");
|
||||||
|
assert.ok(editor.inMultiSelectMode);
|
||||||
|
|
||||||
|
exec("golinedown");
|
||||||
|
exec("gotolineend");
|
||||||
|
testRanges("Range: [1/9] -> [1/9],Range: [2/10] -> [2/10]");
|
||||||
|
exec("selectwordleft");
|
||||||
|
|
||||||
|
testRanges("Range: [1/8] -> [1/9],Range: [2/8] -> [2/10]");
|
||||||
|
exec("golinedown", 2);
|
||||||
|
assert.ok(!editor.inMultiSelectMode);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: multiselect session change": function() {
|
||||||
|
var doc = new EditSession([
|
||||||
|
"w1.w2",
|
||||||
|
" wtt.w",
|
||||||
|
" wtt.w"
|
||||||
|
]);
|
||||||
|
var editor = new Editor(new MockRenderer(), doc);
|
||||||
|
MultiSelect(editor);
|
||||||
|
|
||||||
|
editor.selectMoreLines(1)
|
||||||
|
assert.equal(
|
||||||
|
editor.selection.getAllRanges()+"",
|
||||||
|
"Range: [0/0] -> [0/0],Range: [1/0] -> [1/0]"
|
||||||
|
);
|
||||||
|
assert.ok(editor.inMultiSelectMode);
|
||||||
|
|
||||||
|
var doc2 = new EditSession(["w1"]);
|
||||||
|
editor.setSession(doc2);
|
||||||
|
assert.ok(!editor.inMultiSelectMode);
|
||||||
|
|
||||||
|
editor.setSession(doc);
|
||||||
|
assert.ok(editor.inMultiSelectMode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof module !== "undefined" && module === require.main) {
|
||||||
|
require("asyncjs").test.testcase(module.exports).exec()
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
this.isEequal = function(range) {
|
this.isEqual = function(range) {
|
||||||
return this.start.row == range.start.row &&
|
return this.start.row == range.start.row &&
|
||||||
this.end.row == range.end.row &&
|
this.end.row == range.end.row &&
|
||||||
this.start.column == range.start.column &&
|
this.start.column == range.start.column &&
|
||||||
|
|
@ -117,6 +117,11 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
|
||||||
return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
|
return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.intersectsRange = function(range) {
|
||||||
|
var cmp = this.compareRange(range);
|
||||||
|
return (cmp == -1 || cmp == 0 || cmp == 1);
|
||||||
|
}
|
||||||
|
|
||||||
this.isEnd = function(row, column) {
|
this.isEnd = function(row, column) {
|
||||||
return this.end.row == row && this.end.column == column;
|
return this.end.row == row && this.end.column == column;
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +281,21 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
|
||||||
return Range.fromPoints(start || this.start, end || this.end);
|
return Range.fromPoints(start || this.start, end || this.end);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.fixOrientation = function() {
|
||||||
|
if (
|
||||||
|
this.start.row < this.end.row
|
||||||
|
|| (this.start.row == this.end.row && this.start.column < this.end.column)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp = this.start;
|
||||||
|
this.end = this.start;
|
||||||
|
this.start = temp;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.isEmpty = function() {
|
this.isEmpty = function() {
|
||||||
return (this.start.row == this.end.row && this.start.column == this.end.column);
|
return (this.start.row == this.end.row && this.start.column == this.end.column);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
233
lib/ace/range_list.js
Normal file
233
lib/ace/range_list.js
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
/* ***** 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 <amirjanyan AT gmail DOT com>
|
||||||
|
*
|
||||||
|
* 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 oop = require("./lib/oop");
|
||||||
|
var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
||||||
|
|
||||||
|
var RangeList = function(startRow, startColumn, endRow, endColumn) {
|
||||||
|
this.ranges = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
this.comparePoints = function(p1, p2) {
|
||||||
|
return p1.row - p2.row || p1.column - p2.column
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pointIndex = function(pos, startIndex) {
|
||||||
|
var list = this.ranges;
|
||||||
|
|
||||||
|
for (var i = startIndex || 0; i < list.length; i++) {
|
||||||
|
var range = list[i];
|
||||||
|
var cmp = this.comparePoints(pos, range.end);
|
||||||
|
|
||||||
|
if (cmp > 0)
|
||||||
|
continue;
|
||||||
|
if (cmp == 0)
|
||||||
|
return i;
|
||||||
|
cmp = this.comparePoints(pos, range.start);
|
||||||
|
if (cmp >= 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -i-1;
|
||||||
|
}
|
||||||
|
return -i - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
this.add = function(range) {
|
||||||
|
var startIndex = this.pointIndex(range.start);
|
||||||
|
if (startIndex < 0)
|
||||||
|
startIndex = -startIndex - 1;
|
||||||
|
|
||||||
|
var endIndex = this.pointIndex(range.end, startIndex);
|
||||||
|
|
||||||
|
if (endIndex < 0)
|
||||||
|
endIndex = -endIndex - 1;
|
||||||
|
else
|
||||||
|
endIndex++;
|
||||||
|
|
||||||
|
return this.ranges.splice(startIndex, endIndex - startIndex, range);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addList = function(list) {
|
||||||
|
var removed = [];
|
||||||
|
for (var i = list.length; i--; ) {
|
||||||
|
removed.push.call(removed, this.add(list[i]))
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.substractPoint = function(pos) {
|
||||||
|
var i = this.pointIndex(pos);
|
||||||
|
|
||||||
|
if (i >= 0)
|
||||||
|
return this.ranges.splice(i, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// merge overlapping ranges
|
||||||
|
this.merge = function() {
|
||||||
|
var removed = [];
|
||||||
|
var list = this.ranges;
|
||||||
|
var next = list[0], range;
|
||||||
|
for (var i = 1; i < list.length; i++) {
|
||||||
|
range = next;
|
||||||
|
next = list[i];
|
||||||
|
var cmp = this.comparePoints(range.end, next.start)
|
||||||
|
if (cmp < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cmp == 0 && !(range.isEmpty() || next.isEmpty()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this.comparePoints(range.end, next.end) < 0) {
|
||||||
|
range.end.row = next.end.row;
|
||||||
|
range.end.column = next.end.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.splice(i, 1);
|
||||||
|
removed.push(next);
|
||||||
|
next = range;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.contains = function(row, column) {
|
||||||
|
return this.pointIndex({row: row, column: column}) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.containsPoint = function(pos) {
|
||||||
|
return this.pointIndex(pos) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.rangeAtPoint = function(pos) {
|
||||||
|
var i = this.pointIndex(pos);
|
||||||
|
if (i >= 0)
|
||||||
|
return this.ranges[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.clipRows = function(startRow, endRow) {
|
||||||
|
var list = this.ranges;
|
||||||
|
if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var startIndex = this.pointIndex({row: startRow, column: 0});
|
||||||
|
if (startIndex < 0)
|
||||||
|
startIndex = -startIndex - 1;
|
||||||
|
var endIndex = this.pointIndex({row: endRow, column: 0}, startIndex);
|
||||||
|
if (endIndex < 0)
|
||||||
|
endIndex = -endIndex - 1;
|
||||||
|
|
||||||
|
var clipped = [];
|
||||||
|
for (var i = startIndex; i < endIndex; i++) {
|
||||||
|
clipped.push(list[i]);
|
||||||
|
}
|
||||||
|
return clipped;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeAll = function() {
|
||||||
|
return this.ranges.splice(0, this.ranges.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.attach = function(session) {
|
||||||
|
if (this.session)
|
||||||
|
this.detach();
|
||||||
|
|
||||||
|
this.session = session;
|
||||||
|
this.onChange = this.$onChange.bind(this);
|
||||||
|
|
||||||
|
this.session.on('change', this.onChange);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.detach = function() {
|
||||||
|
if (!this.session)
|
||||||
|
return;
|
||||||
|
this.session.removeListener('change', this.onChange)
|
||||||
|
this.session = null
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$onChange = function(e) {
|
||||||
|
var changeRange = e.data.range;
|
||||||
|
if (e.data.action[0] == "i"){
|
||||||
|
var start = changeRange.start;
|
||||||
|
var end = changeRange.end;
|
||||||
|
} else {
|
||||||
|
var end = changeRange.start;
|
||||||
|
var start = changeRange.end;
|
||||||
|
}
|
||||||
|
var startRow = start.row;
|
||||||
|
var endRow = end.row;
|
||||||
|
var lineDif = endRow - startRow;
|
||||||
|
|
||||||
|
var colDiff = -start.column + end.column;
|
||||||
|
|
||||||
|
var ranges = this.ranges;
|
||||||
|
|
||||||
|
for (var i=0, n = ranges.length; i < n; i++) {
|
||||||
|
var r = ranges[i];
|
||||||
|
if (r.end.row < startRow)
|
||||||
|
continue;
|
||||||
|
if (r.start.row > startRow)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (r.start.row == startRow && r.start.column >= start.column ) {
|
||||||
|
r.start.column += colDiff;
|
||||||
|
r.start.row += lineDif;
|
||||||
|
}
|
||||||
|
if (r.end.row == startRow && r.end.column >= start.column) {
|
||||||
|
r.end.column += colDiff;
|
||||||
|
r.end.row += lineDif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineDif != 0 && i < n) {
|
||||||
|
for (; i < n; i++) {
|
||||||
|
var r = ranges[i];
|
||||||
|
r.start.row += lineDif;
|
||||||
|
r.end.row += lineDif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(RangeList.prototype);
|
||||||
|
|
||||||
|
exports.RangeList = RangeList;
|
||||||
|
});
|
||||||
170
lib/ace/range_list_test.js
Normal file
170
lib/ace/range_list_test.js
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
/* ***** 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):
|
||||||
|
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||||
|
*
|
||||||
|
* 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 ***** */
|
||||||
|
|
||||||
|
if (typeof process !== "undefined") {
|
||||||
|
require("amd-loader");
|
||||||
|
}
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Range = require("./range").Range;
|
||||||
|
var RangeList = require("./range_list").RangeList;
|
||||||
|
var EditSession = require("./edit_session").EditSession;
|
||||||
|
var assert = require("./test/assertions");
|
||||||
|
|
||||||
|
function flatten(rangeList) {
|
||||||
|
var points = [];
|
||||||
|
rangeList.ranges.forEach(function(r) {
|
||||||
|
points.push(r.start.row, r.start.column, r.end.row, r.end.column)
|
||||||
|
})
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
function testRangeList(rangeList, points) {
|
||||||
|
assert.equal("" + flatten(rangeList), "" + points);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
name: "ACE range_list.js",
|
||||||
|
|
||||||
|
"test: rangeList pointIndex": function() {
|
||||||
|
var rangeList = new RangeList();
|
||||||
|
rangeList.ranges = [
|
||||||
|
new Range(1,2,3,4),
|
||||||
|
new Range(4,2,5,4),
|
||||||
|
new Range(8,8,9,9)
|
||||||
|
];
|
||||||
|
|
||||||
|
assert.equal(rangeList.pointIndex({row: 0, column: 1}), -1);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 1, column: 2}), 0);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 1, column: 3}), 0);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 3, column: 4}), 0);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 4, column: 1}), -2);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 5, column: 1}), 1);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 8, column: 9}), 2);
|
||||||
|
assert.equal(rangeList.pointIndex({row: 18, column: 9}), -4);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: rangeList add": function() {
|
||||||
|
var rangeList = new RangeList();
|
||||||
|
rangeList.addList([
|
||||||
|
new Range(9,0,9,1),
|
||||||
|
new Range(1,2,3,4),
|
||||||
|
new Range(8,8,9,9),
|
||||||
|
new Range(4,2,5,4),
|
||||||
|
new Range(3,20,3,24),
|
||||||
|
new Range(6,6,7,7)
|
||||||
|
]);
|
||||||
|
assert.equal(rangeList.ranges.length, 5);
|
||||||
|
|
||||||
|
rangeList.add(new Range(1,2,3,5));
|
||||||
|
assert.range(rangeList.ranges[0], 1,2,3,5);
|
||||||
|
assert.equal(rangeList.ranges.length, 5);
|
||||||
|
|
||||||
|
rangeList.add(new Range(7,7,7,7));
|
||||||
|
assert.range(rangeList.ranges[3], 7,7,7,7);
|
||||||
|
rangeList.add(new Range(7,8,7,8));
|
||||||
|
assert.range(rangeList.ranges[4], 7,8,7,8);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: rangeList add empty": function() {
|
||||||
|
var rangeList = new RangeList();
|
||||||
|
rangeList.addList([
|
||||||
|
new Range(7,10,7,10),
|
||||||
|
new Range(9,10,9,10),
|
||||||
|
new Range(8,10,8,10)
|
||||||
|
]);
|
||||||
|
assert.equal(rangeList.ranges.length, 3);
|
||||||
|
|
||||||
|
rangeList.add(new Range(9,10,9,10));
|
||||||
|
testRangeList(rangeList, [7,10,7,10,8,10,8,10,9,10,9,10]);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: rangeList merge": function() {
|
||||||
|
var rangeList = new RangeList();
|
||||||
|
rangeList.addList([
|
||||||
|
new Range(1,2,3,4),
|
||||||
|
new Range(4,2,5,4),
|
||||||
|
new Range(6,6,7,7),
|
||||||
|
new Range(8,8,9,9)
|
||||||
|
]);
|
||||||
|
var removed = [];
|
||||||
|
|
||||||
|
assert.equal(rangeList.ranges.length, 4);
|
||||||
|
|
||||||
|
rangeList.ranges[1].end.row = 7;
|
||||||
|
removed = rangeList.merge();
|
||||||
|
assert.equal(removed.length, 1);
|
||||||
|
assert.range(rangeList.ranges[1], 4,2,7,7);
|
||||||
|
assert.equal(rangeList.ranges.length, 3);
|
||||||
|
|
||||||
|
rangeList.ranges[0].end.row = 10;
|
||||||
|
removed = rangeList.merge();
|
||||||
|
assert.range(rangeList.ranges[0], 1,2,10,4);
|
||||||
|
assert.equal(removed.length, 2);
|
||||||
|
assert.equal(rangeList.ranges.length, 1);
|
||||||
|
|
||||||
|
rangeList.ranges.push(new Range(10,10,10,10));
|
||||||
|
rangeList.ranges.push(new Range(10,10,10,10));
|
||||||
|
removed = rangeList.merge();
|
||||||
|
assert.equal(rangeList.ranges.length, 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
"test: rangeList remove": function() {
|
||||||
|
var rangeList = new RangeList();
|
||||||
|
var list = [
|
||||||
|
new Range(1,2,3,4),
|
||||||
|
new Range(4,2,5,4),
|
||||||
|
new Range(6,6,7,7),
|
||||||
|
new Range(8,8,9,9)
|
||||||
|
];
|
||||||
|
rangeList.addList(list);
|
||||||
|
assert.equal(rangeList.ranges.length, 4);
|
||||||
|
rangeList.substractPoint({row: 1, column: 2});
|
||||||
|
assert.equal(rangeList.ranges.length, 3);
|
||||||
|
rangeList.substractPoint({row: 6, column: 7});
|
||||||
|
assert.equal(rangeList.ranges.length, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof module !== "undefined" && module === require.main) {
|
||||||
|
require("asyncjs").test.testcase(module.exports).exec()
|
||||||
|
}
|
||||||
|
|
@ -59,18 +59,18 @@ var Selection = function(session) {
|
||||||
this.selectionLead = this.doc.createAnchor(0, 0);
|
this.selectionLead = this.doc.createAnchor(0, 0);
|
||||||
this.selectionAnchor = this.doc.createAnchor(0, 0);
|
this.selectionAnchor = this.doc.createAnchor(0, 0);
|
||||||
|
|
||||||
var _self = this;
|
var self = this;
|
||||||
this.selectionLead.on("change", function(e) {
|
this.selectionLead.on("change", function(e) {
|
||||||
_self._emit("changeCursor");
|
self._emit("changeCursor");
|
||||||
if (!_self.$isEmpty)
|
if (!self.$isEmpty)
|
||||||
_self._emit("changeSelection");
|
self._emit("changeSelection");
|
||||||
if (!_self.$preventUpdateDesiredColumnOnChange && e.old.column != e.value.column)
|
if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column)
|
||||||
_self.$updateDesiredColumn();
|
self.$desiredColumn = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.selectionAnchor.on("change", function() {
|
this.selectionAnchor.on("change", function() {
|
||||||
if (!_self.$isEmpty)
|
if (!self.$isEmpty)
|
||||||
_self._emit("changeSelection");
|
self._emit("changeSelection");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -180,12 +180,7 @@ var Selection = function(session) {
|
||||||
this.setSelectionAnchor(range.start.row, range.start.column);
|
this.setSelectionAnchor(range.start.row, range.start.column);
|
||||||
this.selectTo(range.end.row, range.end.column);
|
this.selectTo(range.end.row, range.end.column);
|
||||||
}
|
}
|
||||||
this.$updateDesiredColumn();
|
this.$desiredColumn = null;
|
||||||
};
|
|
||||||
|
|
||||||
this.$updateDesiredColumn = function() {
|
|
||||||
var cursor = this.getCursor();
|
|
||||||
this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$moveSelection = function(mover) {
|
this.$moveSelection = function(mover) {
|
||||||
|
|
@ -392,14 +387,14 @@ var Selection = function(session) {
|
||||||
this.moveCursorTo(fold.end.row, fold.end.column);
|
this.moveCursorTo(fold.end.row, fold.end.column);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first skip space
|
// first skip space
|
||||||
if (match = this.session.nonTokenRe.exec(rightOfCursor)) {
|
if (match = this.session.nonTokenRe.exec(rightOfCursor)) {
|
||||||
column += this.session.nonTokenRe.lastIndex;
|
column += this.session.nonTokenRe.lastIndex;
|
||||||
this.session.nonTokenRe.lastIndex = 0;
|
this.session.nonTokenRe.lastIndex = 0;
|
||||||
rightOfCursor = line.substring(column);
|
rightOfCursor = line.substring(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if at line end proceed with next line
|
// if at line end proceed with next line
|
||||||
if (column >= line.length) {
|
if (column >= line.length) {
|
||||||
this.moveCursorTo(row, line.length);
|
this.moveCursorTo(row, line.length);
|
||||||
|
|
@ -408,7 +403,7 @@ var Selection = function(session) {
|
||||||
this.moveCursorWordRight();
|
this.moveCursorWordRight();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance to the end of the next token
|
// advance to the end of the next token
|
||||||
if (match = this.session.tokenRe.exec(rightOfCursor)) {
|
if (match = this.session.tokenRe.exec(rightOfCursor)) {
|
||||||
column += this.session.tokenRe.lastIndex;
|
column += this.session.tokenRe.lastIndex;
|
||||||
|
|
@ -433,19 +428,19 @@ var Selection = function(session) {
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
str = this.doc.getLine(row).substring(0, column)
|
str = this.doc.getLine(row).substring(0, column)
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftOfCursor = lang.stringReverse(str);
|
var leftOfCursor = lang.stringReverse(str);
|
||||||
var match;
|
var match;
|
||||||
this.session.nonTokenRe.lastIndex = 0;
|
this.session.nonTokenRe.lastIndex = 0;
|
||||||
this.session.tokenRe.lastIndex = 0;
|
this.session.tokenRe.lastIndex = 0;
|
||||||
|
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
if (match = this.session.nonTokenRe.exec(leftOfCursor)) {
|
if (match = this.session.nonTokenRe.exec(leftOfCursor)) {
|
||||||
column -= this.session.nonTokenRe.lastIndex;
|
column -= this.session.nonTokenRe.lastIndex;
|
||||||
leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex);
|
leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex);
|
||||||
this.session.nonTokenRe.lastIndex = 0;
|
this.session.nonTokenRe.lastIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if at begin of the line proceed in line above
|
// if at begin of the line proceed in line above
|
||||||
if (column <= 0) {
|
if (column <= 0) {
|
||||||
this.moveCursorTo(row, 0);
|
this.moveCursorTo(row, 0);
|
||||||
|
|
@ -470,8 +465,14 @@ var Selection = function(session) {
|
||||||
this.selectionLead.column
|
this.selectionLead.column
|
||||||
);
|
);
|
||||||
|
|
||||||
var screenCol = (chars === 0 && this.$desiredColumn) || screenPos.column;
|
if (chars === 0) {
|
||||||
var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol);
|
if (this.$desiredColumn)
|
||||||
|
screenPos.column = this.$desiredColumn;
|
||||||
|
else
|
||||||
|
this.$desiredColumn = screenPos.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenPos.column);
|
||||||
|
|
||||||
// move the cursor and update the desired column
|
// move the cursor and update the desired column
|
||||||
this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0);
|
this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0);
|
||||||
|
|
@ -481,7 +482,7 @@ var Selection = function(session) {
|
||||||
this.moveCursorTo(position.row, position.column);
|
this.moveCursorTo(position.row, position.column);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.moveCursorTo = function(row, column, preventUpdateDesiredColumn) {
|
this.moveCursorTo = function(row, column, keepDesiredColumn) {
|
||||||
// Ensure the row/column is not inside of a fold.
|
// Ensure the row/column is not inside of a fold.
|
||||||
var fold = this.session.getFoldAt(row, column, 1);
|
var fold = this.session.getFoldAt(row, column, 1);
|
||||||
if (fold) {
|
if (fold) {
|
||||||
|
|
@ -489,21 +490,47 @@ var Selection = function(session) {
|
||||||
column = fold.start.column;
|
column = fold.start.column;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$preventUpdateDesiredColumnOnChange = true;
|
this.$keepDesiredColumnOnChange = true;
|
||||||
this.selectionLead.setPosition(row, column);
|
this.selectionLead.setPosition(row, column);
|
||||||
this.$preventUpdateDesiredColumnOnChange = false;
|
this.$keepDesiredColumnOnChange = false;
|
||||||
|
|
||||||
if (!preventUpdateDesiredColumn)
|
if (!keepDesiredColumn)
|
||||||
this.$updateDesiredColumn(this.selectionLead.column);
|
this.$desiredColumn = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) {
|
this.moveCursorToScreen = function(row, column, keepDesiredColumn) {
|
||||||
var pos = this.session.screenToDocumentPosition(row, column);
|
var pos = this.session.screenToDocumentPosition(row, column);
|
||||||
row = pos.row;
|
this.moveCursorTo(pos.row, pos.column, keepDesiredColumn);
|
||||||
column = pos.column;
|
|
||||||
this.moveCursorTo(row, column, preventUpdateDesiredColumn);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// remove listeners from document
|
||||||
|
this.detach = function() {
|
||||||
|
this.selectionLead.detach();
|
||||||
|
this.selectionAnchor.detach();
|
||||||
|
this.session = this.doc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fromOrientedRange = function(range) {
|
||||||
|
this.setSelectionRange(range, range.cursor == range.start);
|
||||||
|
this.$desiredColumn = range.desiredColumn || this.$desiredColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toOrientedRange = function(range) {
|
||||||
|
var r = this.getRange();
|
||||||
|
if (range) {
|
||||||
|
range.start.column = r.start.column;
|
||||||
|
range.start.row = r.start.row;
|
||||||
|
range.end.column = r.end.column;
|
||||||
|
range.end.row = r.end.row;
|
||||||
|
} else {
|
||||||
|
range = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
range.cursor = this.isBackwards() ? range.start : range.end;
|
||||||
|
range.desiredColumn = this.$desiredColumn;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
}).call(Selection.prototype);
|
}).call(Selection.prototype);
|
||||||
|
|
||||||
exports.Selection = Selection;
|
exports.Selection = Selection;
|
||||||
|
|
|
||||||
|
|
@ -9,96 +9,116 @@ var passed = 0
|
||||||
var failed = 0
|
var failed = 0
|
||||||
var log = document.getElementById("log")
|
var log = document.getElementById("log")
|
||||||
|
|
||||||
var tests = [
|
var testNames = [
|
||||||
require("ace/anchor_test"),
|
"ace/anchor_test",
|
||||||
require("ace/commands/command_manager_test"),
|
"ace/commands/command_manager_test",
|
||||||
require("ace/document_test"),
|
"ace/document_test",
|
||||||
require("ace/edit_session_test"),
|
"ace/edit_session_test",
|
||||||
require("ace/editor_change_document_test"),
|
"ace/editor_change_document_test",
|
||||||
require("ace/editor_highlight_selected_word_test"),
|
"ace/editor_highlight_selected_word_test",
|
||||||
require("ace/editor_navigation_test"),
|
"ace/editor_navigation_test",
|
||||||
require("ace/editor_text_edit_test"),
|
"ace/editor_text_edit_test",
|
||||||
require("ace/ext/static_highlight_test"),
|
"ace/ext/static_highlight_test",
|
||||||
require("ace/layer/text_test"),
|
"ace/layer/text_test",
|
||||||
require("ace/lib/event_emitter_test"),
|
"ace/lib/event_emitter_test",
|
||||||
require("ace/mode/coffee/parser_test"),
|
"ace/mode/coffee/parser_test",
|
||||||
require("ace/mode/coffee_highlight_rules_test"),
|
"ace/mode/coffee_highlight_rules_test",
|
||||||
require("ace/mode/coldfusion_test"),
|
"ace/mode/coldfusion_test",
|
||||||
require("ace/mode/css_test"),
|
"ace/mode/css_test",
|
||||||
require("ace/mode/css_highlight_rules_test"),
|
"ace/mode/css_highlight_rules_test",
|
||||||
require("ace/mode/css_worker"),
|
"ace/mode/css_worker",
|
||||||
require("ace/mode/html_test"),
|
"ace/mode/html_test",
|
||||||
require("ace/mode/html_highlight_rules_test"),
|
"ace/mode/html_highlight_rules_test",
|
||||||
require("ace/mode/javascript_test"),
|
"ace/mode/javascript_test",
|
||||||
require("ace/mode/javascript_highlight_rules_test"),
|
"ace/mode/javascript_highlight_rules_test",
|
||||||
require("ace/mode/javascript_worker_test"),
|
"ace/mode/javascript_worker_test",
|
||||||
require("ace/mode/python_test"),
|
"ace/mode/python_test",
|
||||||
require("ace/mode/ruby_highlight_rules_test"),
|
"ace/mode/ruby_highlight_rules_test",
|
||||||
require("ace/mode/text_test"),
|
"ace/mode/text_test",
|
||||||
require("ace/mode/xml_test"),
|
"ace/mode/xml_test",
|
||||||
require("ace/mode/xml_highlight_rules_test"),
|
"ace/mode/xml_highlight_rules_test",
|
||||||
require("ace/mode/folding/cstyle_test"),
|
"ace/mode/folding/cstyle_test",
|
||||||
require("ace/mode/folding/html_test"),
|
"ace/mode/folding/html_test",
|
||||||
require("ace/mode/folding/pythonic_test"),
|
"ace/mode/folding/pythonic_test",
|
||||||
require("ace/mode/folding/xml_test"),
|
"ace/mode/folding/xml_test",
|
||||||
require("ace/range_test"),
|
"ace/multi_select_test",
|
||||||
require("ace/search_test"),
|
"ace/range_test",
|
||||||
require("ace/selection_test"),
|
"ace/range_list_test",
|
||||||
require("ace/token_iterator_test"),
|
"ace/search_test",
|
||||||
require("ace/virtual_renderer_test")
|
"ace/selection_test",
|
||||||
]
|
"ace/token_iterator_test",
|
||||||
|
"ace/virtual_renderer_test"
|
||||||
|
];
|
||||||
|
|
||||||
async.list(tests)
|
var html = ["<a href='?'>all tests</a><br>"];
|
||||||
.expand(function(test) {
|
for (var i in testNames) {
|
||||||
return AsyncTest.testcase(test)
|
var href = testNames[i];
|
||||||
}, AsyncTest.TestGenerator)
|
html.push("<a href='?", href, "'>", href.replace(/^ace\//, "") ,"</a><br>");
|
||||||
.run()
|
}
|
||||||
.each(function(test, next) {
|
|
||||||
var node = document.createElement("div");
|
|
||||||
node.className = test.passed ? "passed" : "failed";
|
|
||||||
|
|
||||||
var name = test.name
|
var nav = document.createElement("div");
|
||||||
if (test.suiteName)
|
nav.innerHTML = html.join("");
|
||||||
name = test.suiteName + ": " + test.name
|
nav.style.cssText = "position:absolute;right:0;top:0";
|
||||||
|
document.body.appendChild(nav);
|
||||||
|
|
||||||
var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL")
|
if (location.search)
|
||||||
if (!test.passed) {
|
testNames = location.search.substr(1).split(",")
|
||||||
if (test.err.stack)
|
|
||||||
var err = test.err.stack
|
require(testNames, function() {
|
||||||
|
var tests = testNames.map(require);
|
||||||
|
|
||||||
|
async.list(tests)
|
||||||
|
.expand(function(test) {
|
||||||
|
return AsyncTest.testcase(test)
|
||||||
|
}, AsyncTest.TestGenerator)
|
||||||
|
.run()
|
||||||
|
.each(function(test, next) {
|
||||||
|
var node = document.createElement("div");
|
||||||
|
node.className = test.passed ? "passed" : "failed";
|
||||||
|
|
||||||
|
var name = test.name
|
||||||
|
if (test.suiteName)
|
||||||
|
name = test.suiteName + ": " + test.name
|
||||||
|
|
||||||
|
var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL")
|
||||||
|
if (!test.passed) {
|
||||||
|
if (test.err.stack)
|
||||||
|
var err = test.err.stack
|
||||||
|
else
|
||||||
|
var err = test.err
|
||||||
|
|
||||||
|
console.error(msg);
|
||||||
|
console.error(err);
|
||||||
|
msg += "<pre class='error'>" + err + "</pre>";
|
||||||
|
} else {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.innerHTML = msg;
|
||||||
|
log.appendChild(node);
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
.each(function(test) {
|
||||||
|
if (test.passed)
|
||||||
|
passed += 1
|
||||||
else
|
else
|
||||||
var err = test.err
|
failed += 1
|
||||||
|
})
|
||||||
console.error(msg);
|
.end(function() {
|
||||||
console.error(err);
|
log.innerHTML += [
|
||||||
msg += "<pre class='error'>" + err + "</pre>";
|
"<div class='summary'>",
|
||||||
} else {
|
"<br>",
|
||||||
console.log(msg);
|
"Summary: <br>",
|
||||||
}
|
"<br>",
|
||||||
|
"Total number of tests: " + (passed + failed) + "<br>",
|
||||||
node.innerHTML = msg;
|
(passed ? "Passed tests: " + passed + "<br>" : ""),
|
||||||
log.appendChild(node);
|
(failed ? "Failed tests: " + failed + "<br>" : "")
|
||||||
|
].join("")
|
||||||
next()
|
console.log("Total number of tests: " + (passed + failed));
|
||||||
})
|
console.log("Passed tests: " + passed);
|
||||||
.each(function(test) {
|
console.log("Failed tests: " + failed);
|
||||||
if (test.passed)
|
})
|
||||||
passed += 1
|
});
|
||||||
else
|
|
||||||
failed += 1
|
|
||||||
})
|
|
||||||
.end(function() {
|
|
||||||
log.innerHTML += [
|
|
||||||
"<div class='summary'>",
|
|
||||||
"<br>",
|
|
||||||
"Summary: <br>",
|
|
||||||
"<br>",
|
|
||||||
"Total number of tests: " + (passed + failed) + "<br>",
|
|
||||||
(passed ? "Passed tests: " + passed + "<br>" : ""),
|
|
||||||
(failed ? "Failed tests: " + failed + "<br>" : "")
|
|
||||||
].join("")
|
|
||||||
console.log("Total number of tests: " + (passed + failed));
|
|
||||||
console.log("Passed tests: " + passed);
|
|
||||||
console.log("Failed tests: " + failed);
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,12 @@ MockRenderer.prototype.visualizeFocus = function() {
|
||||||
MockRenderer.prototype.setAnnotations = function() {
|
MockRenderer.prototype.setAnnotations = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MockRenderer.prototype.setStyle = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
MockRenderer.prototype.unsetStyle = function() {
|
||||||
|
};
|
||||||
|
|
||||||
MockRenderer.prototype.textToScreenCoordinates = function() {
|
MockRenderer.prototype.textToScreenCoordinates = function() {
|
||||||
return {
|
return {
|
||||||
pageX: 0,
|
pageX: 0,
|
||||||
|
|
|
||||||
|
|
@ -794,6 +794,19 @@ var VirtualRenderer = function(container, theme) {
|
||||||
// todo: handle horizontal scrolling
|
// todo: handle horizontal scrolling
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.pixelToScreenCoordinates = function(pageX, pageY) {
|
||||||
|
var canvasPos = this.scroller.getBoundingClientRect();
|
||||||
|
|
||||||
|
var col = Math.round(
|
||||||
|
(pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth
|
||||||
|
);
|
||||||
|
var row = Math.floor(
|
||||||
|
(pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
return {row: row, column: col};
|
||||||
|
};
|
||||||
|
|
||||||
this.screenToTextCoordinates = function(pageX, pageY) {
|
this.screenToTextCoordinates = function(pageX, pageY) {
|
||||||
var canvasPos = this.scroller.getBoundingClientRect();
|
var canvasPos = this.scroller.getBoundingClientRect();
|
||||||
|
|
||||||
|
|
@ -885,7 +898,7 @@ var VirtualRenderer = function(container, theme) {
|
||||||
return afterLoad(module);
|
return afterLoad(module);
|
||||||
|
|
||||||
_self._loadTheme(moduleName, function() {
|
_self._loadTheme(moduleName, function() {
|
||||||
require([theme], function(module) {
|
require([moduleName], function(module) {
|
||||||
if (_self.$themeValue !== theme)
|
if (_self.$themeValue !== theme)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue