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;
|
||||
|
||||
// workers do not work for file:
|
||||
if (location.protocol == "file:")
|
||||
EditSession.prototype.$useWorker = false;
|
||||
|
||||
var Doc = function(name, desc, file) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
|
|
@ -82,6 +86,7 @@ Mode.prototype.supportsFile = function(filename) {
|
|||
return filename.match(this.extRe);
|
||||
};
|
||||
|
||||
//{
|
||||
var modes = [
|
||||
new Mode("c_cpp", "C/C++", ["c", "cpp", "cxx", "h", "hpp"]),
|
||||
new Mode("clojure", "Clojure", ["clj"]),
|
||||
|
|
@ -260,6 +265,9 @@ var docs = [
|
|||
)
|
||||
];
|
||||
|
||||
//}
|
||||
|
||||
|
||||
var docsByName = {};
|
||||
docs.forEach(function(d) {
|
||||
docsByName[d.name] = d;
|
||||
|
|
@ -324,6 +332,11 @@ modes.forEach(function(mode) {
|
|||
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) {
|
||||
var doc = docsByName[value].doc;
|
||||
|
||||
|
|
@ -344,40 +357,43 @@ function updateUIEditorOptions() {
|
|||
var editor = env.editor;
|
||||
var session = editor.session;
|
||||
|
||||
docEl.value = session.name;
|
||||
modeEl.value = session.modeName || "text";
|
||||
|
||||
session.setFoldStyle(foldingEl.value);
|
||||
|
||||
if (!session.getUseWrapMode()) {
|
||||
wrapModeEl.value = "off";
|
||||
} else {
|
||||
wrapModeEl.value = session.getWrapLimitRange().min || "free";
|
||||
}
|
||||
saveOption(docEl, session.name);
|
||||
saveOption(modeEl, session.modeName || "text");
|
||||
saveOption(wrapModeEl, session.getUseWrapMode() ? session.getWrapLimitRange().min || "free" : "off");
|
||||
|
||||
selectStyleEl.checked = editor.getSelectionStyle() == "line";
|
||||
themeEl.value = editor.getTheme();
|
||||
highlightActiveEl.checked = editor.getHighlightActiveLine();
|
||||
showHiddenEl.checked = editor.getShowInvisibles();
|
||||
showGutterEl.checked = editor.renderer.getShowGutter();
|
||||
showPrintMarginEl.checked = editor.renderer.getShowPrintMargin();
|
||||
highlightSelectedWordE.checked = editor.getHighlightSelectedWord();
|
||||
showHScrollEl.checked = editor.renderer.getHScrollBarAlwaysVisible();
|
||||
animateScrollEl.checked = editor.getAnimatedScroll();
|
||||
softTabEl.checked = session.getUseSoftTabs();
|
||||
behavioursEl.checked = editor.getBehavioursEnabled();
|
||||
saveOption(selectStyleEl, editor.getSelectionStyle() == "line");
|
||||
saveOption(themeEl, editor.getTheme());
|
||||
saveOption(highlightActiveEl, editor.getHighlightActiveLine());
|
||||
saveOption(showHiddenEl, editor.getShowInvisibles());
|
||||
saveOption(showGutterEl, editor.renderer.getShowGutter());
|
||||
saveOption(showPrintMarginEl, editor.renderer.getShowPrintMargin());
|
||||
saveOption(highlightSelectedWordE, editor.getHighlightSelectedWord());
|
||||
saveOption(showHScrollEl, editor.renderer.getHScrollBarAlwaysVisible());
|
||||
saveOption(showHScrollEl, editor.getAnimatedScroll());
|
||||
saveOption(softTabEl, session.getUseSoftTabs());
|
||||
saveOption(behavioursEl, editor.getBehavioursEnabled());
|
||||
}
|
||||
|
||||
bindDropdown("mode", function(value) {
|
||||
env.editor.getSession().setMode(modesByName[value].mode || modesByName.text.mode);
|
||||
env.editor.getSession().modeName = value;
|
||||
});
|
||||
function saveOption(el, val) {
|
||||
if (!el.onchange || el.onclick)
|
||||
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) {
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
env.editor.setTheme(value);
|
||||
env.editor.setTheme(value);
|
||||
});
|
||||
|
||||
bindDropdown("keybinding", function(value) {
|
||||
|
|
@ -459,6 +475,7 @@ bindCheckbox("enable_behaviours", function(checked) {
|
|||
env.editor.setBehavioursEnabled(checked);
|
||||
});
|
||||
|
||||
|
||||
var secondSession = null;
|
||||
bindDropdown("split", function(value) {
|
||||
var sp = env.split;
|
||||
|
|
@ -486,8 +503,12 @@ bindDropdown("split", function(value) {
|
|||
|
||||
function bindCheckbox(id, callback) {
|
||||
var el = document.getElementById(id);
|
||||
if (localStorage && localStorage.getItem(id))
|
||||
el.checked = localStorage.getItem(id) == "1";
|
||||
|
||||
var onCheck = function() {
|
||||
callback(!!el.checked);
|
||||
saveOption(el);
|
||||
};
|
||||
el.onclick = onCheck;
|
||||
onCheck();
|
||||
|
|
@ -495,9 +516,14 @@ function bindCheckbox(id, callback) {
|
|||
|
||||
function bindDropdown(id, callback) {
|
||||
var el = document.getElementById(id);
|
||||
if (localStorage && localStorage.getItem(id))
|
||||
el.value = localStorage.getItem(id);
|
||||
|
||||
var onChange = function() {
|
||||
callback(el.value);
|
||||
saveOption(el);
|
||||
};
|
||||
|
||||
el.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++) {
|
||||
alert(items[i] + "juhu\n");
|
||||
} // 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) {
|
||||
"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) {
|
||||
if (typeof platform !== "string")
|
||||
throw new TypeError("'platform' argument must be either 'mac' or 'win'");
|
||||
|
||||
this.platform = platform;
|
||||
this.commands = {};
|
||||
this.commmandKeyBinding = {};
|
||||
|
||||
if (commands)
|
||||
commands.forEach(this.addCommand, this);
|
||||
this.addCommands(commands);
|
||||
|
||||
this.setDefaultHandler("exec", function(e) {
|
||||
e.command.exec(e.editor, e.args || {});
|
||||
});
|
||||
};
|
||||
|
||||
oop.inherits(CommandManager, HashHandler);
|
||||
|
||||
(function() {
|
||||
|
||||
this.addCommand = function(command) {
|
||||
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()];
|
||||
};
|
||||
oop.implement(this, EventEmitter);
|
||||
|
||||
this.exec = function(command, editor, args) {
|
||||
if (typeof command === 'string')
|
||||
|
|
@ -137,7 +33,7 @@ var CommandManager = function(platform, commands) {
|
|||
if (editor && editor.$readOnly && !command.readOnly)
|
||||
return false;
|
||||
|
||||
command.exec(editor, args || {});
|
||||
this._emit("exec", {editor: editor, command: command, args: args});
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -146,20 +42,22 @@ var CommandManager = function(platform, commands) {
|
|||
return;
|
||||
if (this.recording) {
|
||||
this.macro.pop();
|
||||
this.exec = this.normal_exec;
|
||||
this.removeEventListener("exec", this.$addCommandToMacro);
|
||||
|
||||
if (!this.macro.length)
|
||||
this.macro = this.oldMacro;
|
||||
|
||||
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.macro = [];
|
||||
this.normal_exec = this.exec;
|
||||
this.exec = function(command, editor, args) {
|
||||
this.macro.push([command, args]);
|
||||
return this.normal_exec(command, editor, args);
|
||||
};
|
||||
this.on("exec", this.$addCommandToMacro);
|
||||
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 {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.ace_marker-layer .ace_bracket {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.ace_marker-layer .ace_active_line {
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
|
||||
.ace_marker-layer .ace_selected_word {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
z-index: 4;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
|
|
|
|||
|
|
@ -1443,8 +1443,8 @@ var EditSession = function(text, mode) {
|
|||
}
|
||||
|
||||
this.getScreenLastRowColumn = function(screenRow) {
|
||||
//return this.screenToDocumentColumn(screenRow, Number.MAX_VALUE / 10)
|
||||
return this.documentToScreenColumn(screenRow, this.doc.getLine(screenRow).length);
|
||||
var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE)
|
||||
return this.documentToScreenColumn(pos.row, pos.column);
|
||||
};
|
||||
|
||||
this.getDocumentLastRowColumn = function(docRow, docColumn) {
|
||||
|
|
@ -1559,16 +1559,10 @@ var EditSession = function(text, mode) {
|
|||
|
||||
docColumn += this.$getStringScreenWidth(line, screenColumn)[1];
|
||||
|
||||
// Need to do some clamping action here.
|
||||
if (this.$useWrapMode) {
|
||||
if (docColumn >= column) {
|
||||
// We remove one character at the end such that the docColumn
|
||||
// position returned is not associated to the next row on the
|
||||
// screen.
|
||||
docColumn = column - 1;
|
||||
}
|
||||
} else {
|
||||
docColumn = Math.min(docColumn, line.length);
|
||||
// We remove one character at the end so that the docColumn
|
||||
// position returned is not associated to the next row on the screen.
|
||||
if (this.$useWrapMode && docColumn >= column) {
|
||||
docColumn = column - 1;
|
||||
}
|
||||
|
||||
if (foldLine) {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ var Fold = exports.Fold = function(range, placeholder) {
|
|||
};
|
||||
|
||||
this.addSubFold = function(fold) {
|
||||
if (this.range.isEequal(fold))
|
||||
if (this.range.isEqual(fold))
|
||||
return this;
|
||||
|
||||
if (!this.range.containsRange(fold))
|
||||
|
|
|
|||
|
|
@ -714,7 +714,7 @@ function Folding() {
|
|||
// sometimes singleline folds can be missed by the code above
|
||||
if (!range.isMultiLine()) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,76 +41,126 @@ define(function(require, exports, module) {
|
|||
|
||||
var keyUtil = require("../lib/keys");
|
||||
|
||||
function HashHandler(config) {
|
||||
this.setConfig(config);
|
||||
}
|
||||
function HashHandler(config, platform) {
|
||||
this.platform = platform;
|
||||
this.commands = {};
|
||||
this.commmandKeyBinding = {};
|
||||
|
||||
this.addCommands(config);
|
||||
};
|
||||
|
||||
(function() {
|
||||
function splitSafe(s, separator, limit, bLowerCase) {
|
||||
return (bLowerCase && s.toLowerCase() || s)
|
||||
.replace(/(?:^\s+|\n|\s+$)/g, "")
|
||||
.split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999);
|
||||
}
|
||||
|
||||
this.addCommand = function(command) {
|
||||
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];
|
||||
|
||||
// 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) {
|
||||
var key,
|
||||
hashId = 0,
|
||||
parts = splitSafe(keys, "\\-", null, true),
|
||||
i = 0,
|
||||
l = parts.length;
|
||||
var key;
|
||||
var hashId = 0;
|
||||
var parts = splitSafe(keys.toLowerCase());
|
||||
|
||||
for (; i < l; ++i) {
|
||||
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 '-'
|
||||
}
|
||||
|
||||
(ret[hashId] || (ret[hashId] = {}))[key] = val;
|
||||
return ret;
|
||||
return {
|
||||
key: key,
|
||||
hashId: hashId
|
||||
};
|
||||
}
|
||||
|
||||
function objectReverse(obj, keySplit) {
|
||||
var i, j, l, key,
|
||||
ret = {};
|
||||
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;
|
||||
function splitSafe(s) {
|
||||
return (s.trim()
|
||||
.split(new RegExp("[\\s ]*\\-[\\s ]*", "g"), 999));
|
||||
}
|
||||
|
||||
this.setConfig = function(config) {
|
||||
this.$config = config;
|
||||
if (typeof this.$config.reverse == "undefined")
|
||||
this.$config.reverse = objectReverse.call(this, this.$config, "|");
|
||||
this.findKeyCommand = function findKeyCommand(hashId, keyString) {
|
||||
var ckbr = this.commmandKeyBinding;
|
||||
return ckbr[hashId] && ckbr[hashId][keyString.toLowerCase()];
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
exports.HashHandler = HashHandler;
|
||||
|
|
|
|||
|
|
@ -47,11 +47,10 @@ var Cursor = function(parentEl) {
|
|||
this.element.className = "ace_layer ace_cursor-layer";
|
||||
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.cursors = [];
|
||||
this.cursor = this.addCursor();
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
|
@ -65,30 +64,54 @@ var Cursor = function(parentEl) {
|
|||
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.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);
|
||||
};
|
||||
|
||||
this.showCursor = function() {
|
||||
this.isVisible = true;
|
||||
dom.removeCssClass(this.cursor, "ace_hidden");
|
||||
this.cursor.style.visibility = "visible";
|
||||
for (var i = this.cursors.length; i--; )
|
||||
dom.removeCssClass(this.cursors[i], "ace_hidden");
|
||||
|
||||
this.element.style.visibility = "";
|
||||
this.restartTimer();
|
||||
};
|
||||
|
||||
this.restartTimer = function() {
|
||||
clearInterval(this.blinkId);
|
||||
if (!this.isVisible) {
|
||||
if (!this.isVisible)
|
||||
return;
|
||||
}
|
||||
|
||||
var cursor = this.cursor;
|
||||
var element = this.element;
|
||||
this.blinkId = setInterval(function() {
|
||||
cursor.style.visibility = "hidden";
|
||||
element.style.visibility = "hidden";
|
||||
setTimeout(function() {
|
||||
cursor.style.visibility = "visible";
|
||||
element.style.visibility = "visible";
|
||||
}, 400);
|
||||
}, 1000);
|
||||
};
|
||||
|
|
@ -118,25 +141,53 @@ var Cursor = function(parentEl) {
|
|||
this.update = function(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";
|
||||
this.cursor.style.top = this.pixelPos.top + "px";
|
||||
this.cursor.style.width = config.characterWidth + "px";
|
||||
this.cursor.style.height = config.lineHeight + "px";
|
||||
for (var i = selections.length; i--; ) {
|
||||
sel = selections[i];
|
||||
var pixelPos = this.getPixelPosition(sel.cursor, true);
|
||||
|
||||
var overwrite = this.session.getOverwrite()
|
||||
if (overwrite != this.overwrite) {
|
||||
this.overwrite = overwrite;
|
||||
if (overwrite)
|
||||
dom.addCssClass(this.cursor, "ace_overwrite");
|
||||
else
|
||||
dom.removeCssClass(this.cursor, "ace_overwrite");
|
||||
var style = (this.cursors[cursorIndex++] || this.addCursor()).style;
|
||||
|
||||
style.left = pixelPos.left + "px";
|
||||
style.top = pixelPos.top + "px";
|
||||
style.width = config.characterWidth + "px";
|
||||
style.height = config.lineHeight + "px";
|
||||
}
|
||||
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.$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() {
|
||||
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) {
|
||||
// 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) {
|
||||
var padding = type === "background" ? 0 : this.$padding;
|
||||
|
|
|
|||
|
|
@ -302,16 +302,16 @@ var Text = function(parentEl) {
|
|||
};
|
||||
|
||||
this.$renderLinesFragment = function(config, firstRow, lastRow) {
|
||||
var fragment = this.element.ownerDocument.createDocumentFragment(),
|
||||
row = firstRow,
|
||||
fold = this.session.getNextFoldLine(row),
|
||||
foldStart = fold ?fold.start.row :Infinity;
|
||||
var fragment = this.element.ownerDocument.createDocumentFragment();
|
||||
var row = firstRow;
|
||||
var foldLine = this.session.getNextFoldLine(row);
|
||||
var foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||
|
||||
while (true) {
|
||||
if (row > foldStart) {
|
||||
row = fold.end.row+1;
|
||||
fold = this.session.getNextFoldLine(row, fold);
|
||||
foldStart = fold ?fold.start.row :Infinity;
|
||||
row = foldLine.end.row+1;
|
||||
foldLine = this.session.getNextFoldLine(row, foldLine);
|
||||
foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||
}
|
||||
if (row > lastRow)
|
||||
break;
|
||||
|
|
@ -350,15 +350,15 @@ var Text = function(parentEl) {
|
|||
var html = [];
|
||||
var firstRow = config.firstRow, lastRow = config.lastRow;
|
||||
|
||||
var row = firstRow,
|
||||
fold = this.session.getNextFoldLine(row),
|
||||
foldStart = fold ?fold.start.row :Infinity;
|
||||
var row = firstRow;
|
||||
var foldLine = this.session.getNextFoldLine(row);
|
||||
var foldStart = foldLine ? foldLine.start.row : Infinity;
|
||||
|
||||
while (true) {
|
||||
if (row > foldStart) {
|
||||
row = fold.end.row+1;
|
||||
fold = this.session.getNextFoldLine(row, fold);
|
||||
foldStart = fold ?fold.start.row :Infinity;
|
||||
row = foldLine.end.row+1;
|
||||
foldLine = this.session.getNextFoldLine(row, foldLine);
|
||||
foldStart = foldLine ? foldLine.start.row :Infinity;
|
||||
}
|
||||
if (row > lastRow)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ function DefaultHandlers(editor) {
|
|||
if (!editor.$mouseHandler.$clickSelection) {
|
||||
if (!dragCursor) {
|
||||
editor.moveCursorToPosition(pos);
|
||||
editor.selection.clearSelection(pos.row, pos.column);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +169,6 @@ function DefaultHandlers(editor) {
|
|||
if (distance > DRAG_OFFSET) {
|
||||
state = STATE_SELECT;
|
||||
var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
||||
cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
|
||||
onStartSelect(cursor);
|
||||
}
|
||||
else if ((time - mousedownTime) > editor.getDragDelay()) {
|
||||
|
|
@ -196,7 +195,7 @@ function DefaultHandlers(editor) {
|
|||
else {
|
||||
if (!_self.$clickSelection) {
|
||||
editor.moveCursorToPosition(pos);
|
||||
editor.selection.clearSelection(pos.row, pos.column);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
}
|
||||
state = STATE_SELECT;
|
||||
|
|
@ -205,7 +204,6 @@ function DefaultHandlers(editor) {
|
|||
var onUpdateSelectionInterval = function() {
|
||||
var anchor;
|
||||
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.contains(cursor.row, cursor.column)) {
|
||||
|
|
@ -231,8 +229,6 @@ function DefaultHandlers(editor) {
|
|||
|
||||
var onDragSelectionInterval = function() {
|
||||
dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
|
||||
dragCursor.row = Math.max(0, Math.min(dragCursor.row, editor.session.getLength() - 1));
|
||||
|
||||
editor.moveCursorToPosition(dragCursor);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function FoldHandler(editor) {
|
|||
var position = e.getDocumentPosition();
|
||||
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);
|
||||
if (fold) {
|
||||
if (e.getAccelKey())
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) {
|
|||
var pageX = event.getDocumentX(this.domEvent);
|
||||
var pageY = event.getDocumentY(this.domEvent);
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
|||
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() {
|
||||
this.isEequal = function(range) {
|
||||
this.isEqual = function(range) {
|
||||
return this.start.row == range.start.row &&
|
||||
this.end.row == range.end.row &&
|
||||
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;
|
||||
}
|
||||
|
||||
this.intersectsRange = function(range) {
|
||||
var cmp = this.compareRange(range);
|
||||
return (cmp == -1 || cmp == 0 || cmp == 1);
|
||||
}
|
||||
|
||||
this.isEnd = function(row, 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);
|
||||
};
|
||||
|
||||
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() {
|
||||
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.selectionAnchor = this.doc.createAnchor(0, 0);
|
||||
|
||||
var _self = this;
|
||||
var self = this;
|
||||
this.selectionLead.on("change", function(e) {
|
||||
_self._emit("changeCursor");
|
||||
if (!_self.$isEmpty)
|
||||
_self._emit("changeSelection");
|
||||
if (!_self.$preventUpdateDesiredColumnOnChange && e.old.column != e.value.column)
|
||||
_self.$updateDesiredColumn();
|
||||
self._emit("changeCursor");
|
||||
if (!self.$isEmpty)
|
||||
self._emit("changeSelection");
|
||||
if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column)
|
||||
self.$desiredColumn = null;
|
||||
});
|
||||
|
||||
this.selectionAnchor.on("change", function() {
|
||||
if (!_self.$isEmpty)
|
||||
_self._emit("changeSelection");
|
||||
if (!self.$isEmpty)
|
||||
self._emit("changeSelection");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -180,12 +180,7 @@ var Selection = function(session) {
|
|||
this.setSelectionAnchor(range.start.row, range.start.column);
|
||||
this.selectTo(range.end.row, range.end.column);
|
||||
}
|
||||
this.$updateDesiredColumn();
|
||||
};
|
||||
|
||||
this.$updateDesiredColumn = function() {
|
||||
var cursor = this.getCursor();
|
||||
this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);
|
||||
this.$desiredColumn = null;
|
||||
};
|
||||
|
||||
this.$moveSelection = function(mover) {
|
||||
|
|
@ -392,14 +387,14 @@ var Selection = function(session) {
|
|||
this.moveCursorTo(fold.end.row, fold.end.column);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// first skip space
|
||||
if (match = this.session.nonTokenRe.exec(rightOfCursor)) {
|
||||
column += this.session.nonTokenRe.lastIndex;
|
||||
this.session.nonTokenRe.lastIndex = 0;
|
||||
rightOfCursor = line.substring(column);
|
||||
}
|
||||
|
||||
|
||||
// if at line end proceed with next line
|
||||
if (column >= line.length) {
|
||||
this.moveCursorTo(row, line.length);
|
||||
|
|
@ -408,7 +403,7 @@ var Selection = function(session) {
|
|||
this.moveCursorWordRight();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// advance to the end of the next token
|
||||
if (match = this.session.tokenRe.exec(rightOfCursor)) {
|
||||
column += this.session.tokenRe.lastIndex;
|
||||
|
|
@ -433,19 +428,19 @@ var Selection = function(session) {
|
|||
if (str == null) {
|
||||
str = this.doc.getLine(row).substring(0, column)
|
||||
}
|
||||
|
||||
|
||||
var leftOfCursor = lang.stringReverse(str);
|
||||
var match;
|
||||
this.session.nonTokenRe.lastIndex = 0;
|
||||
this.session.tokenRe.lastIndex = 0;
|
||||
|
||||
|
||||
// skip whitespace
|
||||
if (match = this.session.nonTokenRe.exec(leftOfCursor)) {
|
||||
column -= this.session.nonTokenRe.lastIndex;
|
||||
leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex);
|
||||
this.session.nonTokenRe.lastIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
// if at begin of the line proceed in line above
|
||||
if (column <= 0) {
|
||||
this.moveCursorTo(row, 0);
|
||||
|
|
@ -470,8 +465,14 @@ var Selection = function(session) {
|
|||
this.selectionLead.column
|
||||
);
|
||||
|
||||
var screenCol = (chars === 0 && this.$desiredColumn) || screenPos.column;
|
||||
var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol);
|
||||
if (chars === 0) {
|
||||
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
|
||||
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 = function(row, column, preventUpdateDesiredColumn) {
|
||||
this.moveCursorTo = function(row, column, keepDesiredColumn) {
|
||||
// Ensure the row/column is not inside of a fold.
|
||||
var fold = this.session.getFoldAt(row, column, 1);
|
||||
if (fold) {
|
||||
|
|
@ -489,21 +490,47 @@ var Selection = function(session) {
|
|||
column = fold.start.column;
|
||||
}
|
||||
|
||||
this.$preventUpdateDesiredColumnOnChange = true;
|
||||
this.$keepDesiredColumnOnChange = true;
|
||||
this.selectionLead.setPosition(row, column);
|
||||
this.$preventUpdateDesiredColumnOnChange = false;
|
||||
this.$keepDesiredColumnOnChange = false;
|
||||
|
||||
if (!preventUpdateDesiredColumn)
|
||||
this.$updateDesiredColumn(this.selectionLead.column);
|
||||
if (!keepDesiredColumn)
|
||||
this.$desiredColumn = null;
|
||||
};
|
||||
|
||||
this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) {
|
||||
this.moveCursorToScreen = function(row, column, keepDesiredColumn) {
|
||||
var pos = this.session.screenToDocumentPosition(row, column);
|
||||
row = pos.row;
|
||||
column = pos.column;
|
||||
this.moveCursorTo(row, column, preventUpdateDesiredColumn);
|
||||
this.moveCursorTo(pos.row, pos.column, keepDesiredColumn);
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
exports.Selection = Selection;
|
||||
|
|
|
|||
|
|
@ -9,96 +9,116 @@ var passed = 0
|
|||
var failed = 0
|
||||
var log = document.getElementById("log")
|
||||
|
||||
var tests = [
|
||||
require("ace/anchor_test"),
|
||||
require("ace/commands/command_manager_test"),
|
||||
require("ace/document_test"),
|
||||
require("ace/edit_session_test"),
|
||||
require("ace/editor_change_document_test"),
|
||||
require("ace/editor_highlight_selected_word_test"),
|
||||
require("ace/editor_navigation_test"),
|
||||
require("ace/editor_text_edit_test"),
|
||||
require("ace/ext/static_highlight_test"),
|
||||
require("ace/layer/text_test"),
|
||||
require("ace/lib/event_emitter_test"),
|
||||
require("ace/mode/coffee/parser_test"),
|
||||
require("ace/mode/coffee_highlight_rules_test"),
|
||||
require("ace/mode/coldfusion_test"),
|
||||
require("ace/mode/css_test"),
|
||||
require("ace/mode/css_highlight_rules_test"),
|
||||
require("ace/mode/css_worker"),
|
||||
require("ace/mode/html_test"),
|
||||
require("ace/mode/html_highlight_rules_test"),
|
||||
require("ace/mode/javascript_test"),
|
||||
require("ace/mode/javascript_highlight_rules_test"),
|
||||
require("ace/mode/javascript_worker_test"),
|
||||
require("ace/mode/python_test"),
|
||||
require("ace/mode/ruby_highlight_rules_test"),
|
||||
require("ace/mode/text_test"),
|
||||
require("ace/mode/xml_test"),
|
||||
require("ace/mode/xml_highlight_rules_test"),
|
||||
require("ace/mode/folding/cstyle_test"),
|
||||
require("ace/mode/folding/html_test"),
|
||||
require("ace/mode/folding/pythonic_test"),
|
||||
require("ace/mode/folding/xml_test"),
|
||||
require("ace/range_test"),
|
||||
require("ace/search_test"),
|
||||
require("ace/selection_test"),
|
||||
require("ace/token_iterator_test"),
|
||||
require("ace/virtual_renderer_test")
|
||||
]
|
||||
var testNames = [
|
||||
"ace/anchor_test",
|
||||
"ace/commands/command_manager_test",
|
||||
"ace/document_test",
|
||||
"ace/edit_session_test",
|
||||
"ace/editor_change_document_test",
|
||||
"ace/editor_highlight_selected_word_test",
|
||||
"ace/editor_navigation_test",
|
||||
"ace/editor_text_edit_test",
|
||||
"ace/ext/static_highlight_test",
|
||||
"ace/layer/text_test",
|
||||
"ace/lib/event_emitter_test",
|
||||
"ace/mode/coffee/parser_test",
|
||||
"ace/mode/coffee_highlight_rules_test",
|
||||
"ace/mode/coldfusion_test",
|
||||
"ace/mode/css_test",
|
||||
"ace/mode/css_highlight_rules_test",
|
||||
"ace/mode/css_worker",
|
||||
"ace/mode/html_test",
|
||||
"ace/mode/html_highlight_rules_test",
|
||||
"ace/mode/javascript_test",
|
||||
"ace/mode/javascript_highlight_rules_test",
|
||||
"ace/mode/javascript_worker_test",
|
||||
"ace/mode/python_test",
|
||||
"ace/mode/ruby_highlight_rules_test",
|
||||
"ace/mode/text_test",
|
||||
"ace/mode/xml_test",
|
||||
"ace/mode/xml_highlight_rules_test",
|
||||
"ace/mode/folding/cstyle_test",
|
||||
"ace/mode/folding/html_test",
|
||||
"ace/mode/folding/pythonic_test",
|
||||
"ace/mode/folding/xml_test",
|
||||
"ace/multi_select_test",
|
||||
"ace/range_test",
|
||||
"ace/range_list_test",
|
||||
"ace/search_test",
|
||||
"ace/selection_test",
|
||||
"ace/token_iterator_test",
|
||||
"ace/virtual_renderer_test"
|
||||
];
|
||||
|
||||
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 html = ["<a href='?'>all tests</a><br>"];
|
||||
for (var i in testNames) {
|
||||
var href = testNames[i];
|
||||
html.push("<a href='?", href, "'>", href.replace(/^ace\//, "") ,"</a><br>");
|
||||
}
|
||||
|
||||
var name = test.name
|
||||
if (test.suiteName)
|
||||
name = test.suiteName + ": " + test.name
|
||||
var nav = document.createElement("div");
|
||||
nav.innerHTML = html.join("");
|
||||
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 (!test.passed) {
|
||||
if (test.err.stack)
|
||||
var err = test.err.stack
|
||||
if (location.search)
|
||||
testNames = location.search.substr(1).split(",")
|
||||
|
||||
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
|
||||
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
|
||||
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);
|
||||
})
|
||||
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.setStyle = function() {
|
||||
};
|
||||
|
||||
MockRenderer.prototype.unsetStyle = function() {
|
||||
};
|
||||
|
||||
MockRenderer.prototype.textToScreenCoordinates = function() {
|
||||
return {
|
||||
pageX: 0,
|
||||
|
|
|
|||
|
|
@ -794,6 +794,19 @@ var VirtualRenderer = function(container, theme) {
|
|||
// 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) {
|
||||
var canvasPos = this.scroller.getBoundingClientRect();
|
||||
|
||||
|
|
@ -885,7 +898,7 @@ var VirtualRenderer = function(container, theme) {
|
|||
return afterLoad(module);
|
||||
|
||||
_self._loadTheme(moduleName, function() {
|
||||
require([theme], function(module) {
|
||||
require([moduleName], function(module) {
|
||||
if (_self.$themeValue !== theme)
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue