kopia lustrzana https://github.com/c9/core
403 wiersze
14 KiB
JavaScript
403 wiersze
14 KiB
JavaScript
define(function(require, exports, module) {
|
|
main.consumes = [
|
|
"Plugin", "c9", "settings", "ui",
|
|
"anims", "menus", "commands", "util", "tabManager"
|
|
];
|
|
main.provides = ["ace.gotoline"];
|
|
return main;
|
|
|
|
// @todo add commands for list navigation and bookmarking
|
|
// @todo fix pasting of line numbers
|
|
|
|
function main(options, imports, register) {
|
|
var c9 = imports.c9;
|
|
var Plugin = imports.Plugin;
|
|
var settings = imports.settings;
|
|
var ui = imports.ui;
|
|
var anims = imports.anims;
|
|
var util = imports.util;
|
|
var menus = imports.menus;
|
|
var commands = imports.commands;
|
|
var tabs = imports.tabManager;
|
|
|
|
var skin = require("text!./skin.xml");
|
|
var markup = require("text!./gotoline.xml");
|
|
|
|
/***** Initialization *****/
|
|
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
var emit = plugin.getEmitter();
|
|
|
|
var originalLine, originalColumn, control, lastLine, lineControl;
|
|
var nohide, originalPath;
|
|
var win, input, list, model; // ui elements
|
|
|
|
var loaded = false, changed = false;
|
|
function load() {
|
|
if (loaded) return false;
|
|
loaded = true;
|
|
|
|
model = new ui.model();
|
|
|
|
menus.addItemByPath("Goto/Goto Line...", new apf.item({
|
|
caption: "Goto Line...",
|
|
hint: "enter a line number and jump to it in the active document",
|
|
command: "gotoline"
|
|
}), 200, plugin);
|
|
|
|
commands.addCommand({
|
|
name: "gotoline",
|
|
bindKey: { mac: "Command-L", win: "Ctrl-G" },
|
|
isAvailable: function(editor) {
|
|
return editor && editor.type == "ace";
|
|
},
|
|
exec: function() {
|
|
gotoline();
|
|
}
|
|
}, plugin);
|
|
|
|
commands.addCommand({
|
|
name: "hideGotoLine",
|
|
group: "ignore",
|
|
bindKey: { mac: "ESC", win: "ESC" },
|
|
isAvailable: function(editor) { return win && win.visible; },
|
|
exec: function() {
|
|
hide();
|
|
var tab = tabs.focussedTab;
|
|
tab && tabs.focusTab(tab);
|
|
|
|
if (originalLine) {
|
|
execGotoLine(originalLine, originalColumn, true);
|
|
originalPath = originalColumn = originalLine = undefined;
|
|
}
|
|
}
|
|
}, plugin);
|
|
|
|
settings.on("read", function() {
|
|
var lines = settings.getJson("state/gotoline") || [];
|
|
var xml = "";
|
|
for (var i = 0, l = lines.length; i < l; i += 2) {
|
|
xml += "<line nr='" + lines[i] + "' />";
|
|
}
|
|
model.load("<lines>" + xml + "</lines>");
|
|
}, plugin);
|
|
|
|
settings.on("write", function() {
|
|
if (changed) {
|
|
var nodes = model.data.childNodes;
|
|
var lines = [];
|
|
for (var i = 0, l = Math.min(20, nodes.length); i < l; i++) {
|
|
lines.push(nodes[i].getAttribute("nr"));
|
|
}
|
|
settings.setJson("state/gotoline", lines);
|
|
}
|
|
}, plugin);
|
|
}
|
|
|
|
var drawn = false;
|
|
function draw() {
|
|
if (drawn) return;
|
|
drawn = true;
|
|
|
|
// Import Skin
|
|
ui.insertSkin({
|
|
name: "gotoline",
|
|
data: skin,
|
|
"media-path": options.staticPrefix + "/images/"
|
|
}, plugin);
|
|
|
|
// Create UI elements
|
|
ui.insertMarkup(null, markup, plugin);
|
|
|
|
win = plugin.getElement("window");
|
|
input = plugin.getElement("input");
|
|
list = plugin.getElement("list");
|
|
|
|
list.setAttribute("model", model);
|
|
|
|
list.addEventListener("afterchoose", function() {
|
|
if (list.selected) {
|
|
execGotoLine(parseInt(list.selected.getAttribute("nr"), 10));
|
|
}
|
|
else {
|
|
execGotoLine();
|
|
}
|
|
});
|
|
list.addEventListener("afterselect", function() {
|
|
if (!list.selected)
|
|
return;
|
|
|
|
var line = list.selected.getAttribute("nr");
|
|
input.setValue(line);
|
|
|
|
// Focus the list
|
|
list.focus();
|
|
|
|
// Go to the right line
|
|
execGotoLine(null, null, true);
|
|
});
|
|
|
|
var restricted = [38, 40, 36, 35];
|
|
list.addEventListener("keydown", function(e) {
|
|
if (e.keyCode == 13 && list.selected) {
|
|
return false;
|
|
}
|
|
else if (e.keyCode == 38) {
|
|
if (list.selected == list.getFirstTraverseNode()) {
|
|
input.focus();
|
|
list.clearSelection();
|
|
}
|
|
}
|
|
else if (restricted.indexOf(e.keyCode) == -1)
|
|
input.focus();
|
|
}, true);
|
|
|
|
input.addEventListener("keydown", function(e) {
|
|
var NotANumber = (e.keyCode > 57 || e.keyCode == 32)
|
|
&& (e.keyCode < 96 || e.keyCode > 105);
|
|
|
|
if (e.keyCode == 13) {
|
|
execGotoLine();
|
|
return false;
|
|
}
|
|
else if (e.keyCode == 40) {
|
|
var first = list.getFirstTraverseNode();
|
|
if (first) {
|
|
list.select(first);
|
|
list.$container.scrollTop = 0;
|
|
list.focus();
|
|
}
|
|
}
|
|
else if (NotANumber && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
return false;
|
|
}
|
|
|
|
// Numbers & Cmd-V / Cmd-C
|
|
if (!NotANumber || (e.metaKey || e.ctrlKey)
|
|
&& (e.keyCode == 86 || e.keyCode == 88)) {
|
|
setTimeout(function() {
|
|
execGotoLine(null, null, true);
|
|
}, 10);
|
|
}
|
|
});
|
|
|
|
win.addEventListener("blur", function(e) {
|
|
if (!ui.isChildOf(win, e.toElement))
|
|
hide();
|
|
});
|
|
|
|
input.addEventListener("blur", function(e) {
|
|
if (!ui.isChildOf(win, e.toElement))
|
|
hide();
|
|
});
|
|
|
|
emit("draw");
|
|
}
|
|
|
|
/***** Methods *****/
|
|
|
|
function show(noanim) {
|
|
var tab = tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace") return;
|
|
|
|
var ace = editor.ace;
|
|
var aceHtml = ace.container;
|
|
var cursor = ace.getCursorPosition();
|
|
|
|
originalLine = cursor.row + 1;
|
|
originalColumn = cursor.column;
|
|
originalPath = tab.path;
|
|
|
|
//Set the current line
|
|
input.setValue(input.getValue() || cursor.row + 1);
|
|
|
|
//Determine the position of the window
|
|
var pos = ace.renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
|
var epos = ui.getAbsolutePosition(aceHtml.parentNode);
|
|
var maxTop = aceHtml.offsetHeight - 100;
|
|
var top = Math.max(0, Math.min(maxTop, pos.pageY - epos[1] - 5));
|
|
var left = 0;
|
|
|
|
ace.container.parentNode.appendChild(win.$ext);
|
|
|
|
win.show();
|
|
win.$ext.style.top = top + "px";
|
|
|
|
//Animate
|
|
if (!noanim && settings.getBool('user/general/@animateui')) {
|
|
win.setWidth(0);
|
|
|
|
anims.animate(win, {
|
|
width: "60px",
|
|
duration: 0.15,
|
|
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
|
}, function() {
|
|
win.$ext.style.left = left + "px";
|
|
});
|
|
}
|
|
else {
|
|
win.setWidth(60);
|
|
}
|
|
input.focus();
|
|
}
|
|
|
|
function hide() {
|
|
if (nohide) return;
|
|
|
|
if (settings.getBool('user/general/@animateui')) {
|
|
anims.animate(win, {
|
|
width: "0px",
|
|
duration: 0.15,
|
|
timingFunction: "cubic-bezier(.10, .10, .25, .90)"
|
|
}, function() {
|
|
win.hide();
|
|
});
|
|
}
|
|
else {
|
|
win.hide();
|
|
}
|
|
}
|
|
|
|
function gotoline(force) {
|
|
draw();
|
|
|
|
if (control && control.stop)
|
|
control.stop();
|
|
|
|
var tab = tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace")
|
|
return;
|
|
|
|
if (force != 2 && !win.visible || force == 1)
|
|
show();
|
|
else
|
|
hide();
|
|
|
|
return false;
|
|
}
|
|
|
|
function execGotoLine(line, column, preview) {
|
|
var tab = tabs.focussedTab && tabs.focussedTab;
|
|
var editor = tab && tab.editor;
|
|
if (!editor || editor.type != "ace") return;
|
|
|
|
var ace = editor.ace;
|
|
var aceHtml = ace.container;
|
|
|
|
if (typeof line != "number")
|
|
line = parseInt(input.getValue(), 10) || 0;
|
|
|
|
// I don't know why this if was here. It caused a bug where if the
|
|
// line target was already in view, it wouldn't jump to it.
|
|
// if (!lastLine || lastLine != line || !ace.isRowFullyVisible(line)) {
|
|
ace.gotoLine(line, column);
|
|
lastLine = line;
|
|
// }
|
|
|
|
if (typeof preview != "undefined") {
|
|
var animate = settings.getBool("user/ace/@animatedScroll");
|
|
if (!animate)
|
|
return;
|
|
|
|
var cursor = ace.getCursorPosition();
|
|
var renderer = ace.renderer;
|
|
var pos = renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
|
var maxTop = renderer.$size.height - win.getHeight() - 10;
|
|
var epos = ui.getAbsolutePosition(aceHtml);
|
|
var sm = renderer.scrollMargin;
|
|
var scrollTop = ace.session.getScrollTop();
|
|
scrollTop = Math.max(-sm.top, Math.min(scrollTop,
|
|
renderer.layerConfig.maxHeight - renderer.$size.scrollerHeight + sm.v));
|
|
var top = Math.min(pos.pageY - epos[1] - 2
|
|
+ renderer.scrollTop - scrollTop, maxTop);
|
|
|
|
if (lineControl)
|
|
lineControl.stop();
|
|
|
|
//Animate
|
|
anims.animate(win, {
|
|
top: top + "px",
|
|
duration: 0.25,
|
|
timingFunction: "cubic-bezier(.11, .93, .84, 1)"
|
|
}, function() {
|
|
win.$ext.style.left = "0px";
|
|
});
|
|
}
|
|
else {
|
|
//win.hide();
|
|
hide();
|
|
|
|
var lineNode = model.queryNode("line[@nr='" + line + "']");
|
|
|
|
if (!lineNode) {
|
|
lineNode = ui.n("<line />")
|
|
.attr("nr", line)
|
|
.node();
|
|
}
|
|
|
|
var pNode = model.data;
|
|
if (lineNode != pNode.firstChild) {
|
|
apf.xmldb.appendChild(pNode, lineNode, pNode.firstChild);
|
|
changed = true;
|
|
settings.save();
|
|
}
|
|
|
|
tabs.focusTab(tab);
|
|
}
|
|
}
|
|
|
|
/***** Lifecycle *****/
|
|
|
|
plugin.on("load", function() {
|
|
load();
|
|
});
|
|
plugin.on("enable", function() {
|
|
|
|
});
|
|
plugin.on("disable", function() {
|
|
|
|
});
|
|
plugin.on("unload", function() {
|
|
loaded = false;
|
|
});
|
|
|
|
/***** Register and define API *****/
|
|
|
|
/**
|
|
* The goto line dialog for ace editors. The goto line dialog allows
|
|
* users to jump to a line in a file. It has a history of all lines
|
|
* that were jumped to before. Users can navigate this list and press
|
|
* ESC to return to their original position.
|
|
*
|
|
* @singleton
|
|
**/
|
|
plugin.freezePublicAPI({
|
|
/**
|
|
* Jump to a line and column in the focussed tab.
|
|
* @param {Number} line The line to jump to.
|
|
* @param {Number} column The column to jump to.
|
|
* @param {Boolean} preview Whether to keep the original location in memory.
|
|
*/
|
|
gotoline: function(line, column, preview) {
|
|
gotoline(1);
|
|
return execGotoLine(line, column, preview);
|
|
},
|
|
|
|
/**
|
|
* Show the goto line dialog
|
|
*/
|
|
show: function() { gotoline(1); },
|
|
|
|
/**
|
|
* Hide the goto line dialog
|
|
*/
|
|
hide: function() { gotoline(2); }
|
|
});
|
|
|
|
register(null, {
|
|
"ace.gotoline": plugin
|
|
});
|
|
}
|
|
}); |