kopia lustrzana https://github.com/c9/core
489 wiersze
16 KiB
JavaScript
489 wiersze
16 KiB
JavaScript
define(function(require, exports, module) {
|
|
main.consumes = [
|
|
"Plugin", "settings", "ui", "watcher", "menus", "tabManager", "find", "fs",
|
|
"panels", "fs.cache", "preferences", "c9", "commands", "tree"
|
|
];
|
|
main.provides = ["navigate"];
|
|
return main;
|
|
|
|
function main(options, imports, register) {
|
|
var Plugin = imports.Plugin;
|
|
var settings = imports.settings;
|
|
var ui = imports.ui;
|
|
var c9 = imports.c9;
|
|
var fs = imports.fs;
|
|
var fsCache = imports["fs.cache"];
|
|
var tabs = imports.tabManager;
|
|
var menus = imports.menus;
|
|
var watcher = imports.watcher;
|
|
var commands = imports.commands;
|
|
var panels = imports.panels;
|
|
var find = imports.find;
|
|
var filetree = imports.tree;
|
|
var prefs = imports.preferences;
|
|
|
|
var markup = require("text!./navigate.xml");
|
|
var search = require('./search');
|
|
var Tree = require("ace_tree/tree");
|
|
var ListData = require("./dataprovider");
|
|
|
|
/***** Initialization *****/
|
|
|
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
|
var emit = plugin.getEmitter();
|
|
|
|
var winGoToFile, txtGoToFile, tree, ldSearch;
|
|
var lastPanel, lastSearch, lastPreviewed;
|
|
|
|
var dirty = true;
|
|
var arrayCache = [];
|
|
var timer;
|
|
|
|
var loaded = false;
|
|
function load(){
|
|
if (loaded) return false;
|
|
loaded = true;
|
|
|
|
// Register this panel on the left-side panels
|
|
panels.register({
|
|
index: 200,
|
|
caption: "Navigate",
|
|
command: "navigate",
|
|
hint: "search for a filename, line or symbol and jump to it",
|
|
bindKey: { mac: "Command-E|Command-P", win: "Ctrl-E|Ctrl-P" },
|
|
panel: plugin,
|
|
elementName: "winGoToFile",
|
|
minWidth: 130,
|
|
autohide: true,
|
|
draw: draw
|
|
});
|
|
|
|
panels.on("showpanelNavigate", function(e) {
|
|
lastPanel = e.lastPanel;
|
|
txtGoToFile.focus();
|
|
txtGoToFile.select();
|
|
tree.resize();
|
|
});
|
|
panels.on("hidepanelNavigate", function(e) {
|
|
tree.clearSelection();
|
|
});
|
|
|
|
// Menus
|
|
var mnuItem = new apf.item({ command : "navigate" });
|
|
menus.addItemByPath("File/Open...", mnuItem, 500, plugin);
|
|
menus.addItemByPath("Goto/Goto File...", mnuItem.cloneNode(false), 100, plugin);
|
|
|
|
// Settings
|
|
settings.on("read", function(){
|
|
settings.setDefaults("user/general", [["preview-navigate", "false"]]);
|
|
}, plugin);
|
|
|
|
// Prefs
|
|
prefs.add({
|
|
"General" : {
|
|
"General" : {
|
|
"Enable Preview on Navigation" : {
|
|
type: "checkbox",
|
|
position: 2000,
|
|
path: "user/general/@preview-navigate"
|
|
}
|
|
}
|
|
}
|
|
}, plugin);
|
|
|
|
// Update when the fs changes
|
|
fs.on("afterWriteFile", function(e) {
|
|
// Only mark dirty if file didn't exist yet
|
|
if (arrayCache.indexOf(e.path) == -1)
|
|
markDirty(e);
|
|
});
|
|
fs.on("afterUnlink", markDirty);
|
|
fs.on("afterRmfile", markDirty);
|
|
fs.on("afterRmdir", markDirty);
|
|
fs.on("afterCopy", markDirty);
|
|
fs.on("afterRename", markDirty);
|
|
fs.on("afterSymlink", markDirty);
|
|
|
|
// Or when a watcher fires
|
|
watcher.on("delete", markDirty);
|
|
watcher.on("directory", markDirty);
|
|
|
|
// Or when the user refreshes the tree
|
|
filetree.on("refresh", markDirty);
|
|
|
|
// Or when we change the visibility of hidden files
|
|
fsCache.on("setShowHidden", markDirty);
|
|
|
|
// Pre-load file list
|
|
updateFileCache();
|
|
}
|
|
|
|
function offlineHandler(e) {
|
|
// Online
|
|
if (e.state & c9.STORAGE) {
|
|
txtGoToFile.enable();
|
|
//@Harutyun This doesn't work
|
|
// tree.enable();
|
|
}
|
|
// Offline
|
|
else {
|
|
txtGoToFile.disable();
|
|
//@Harutyun This doesn't work
|
|
// tree.disable();
|
|
}
|
|
}
|
|
|
|
var drawn = false;
|
|
function draw(options) {
|
|
if (drawn) return;
|
|
drawn = true;
|
|
|
|
// Create UI elements
|
|
ui.insertMarkup(options.panel, markup, plugin);
|
|
|
|
// Import CSS
|
|
ui.insertCss(require("text!./style.css"), plugin);
|
|
|
|
var treeParent = plugin.getElement("tree");
|
|
txtGoToFile = plugin.getElement("txtGoToFile");
|
|
winGoToFile = plugin.getElement("winGoToFile");
|
|
txtGoToFile = plugin.getElement("txtGoToFile");
|
|
|
|
// Create the Ace Tree
|
|
tree = new Tree(treeParent.$int);
|
|
ldSearch = new ListData(arrayCache);
|
|
ldSearch.search = search;
|
|
|
|
// Assign the dataprovider
|
|
tree.setDataProvider(ldSearch);
|
|
|
|
// @TODO this is probably not sufficient
|
|
window.addEventListener("resize", function(){ tree.resize() });
|
|
|
|
tree.textInput = txtGoToFile.ace.textInput;
|
|
|
|
// @Harutyun; is this the right way to do it?
|
|
function available(){ return tree.isFocused() }
|
|
|
|
txtGoToFile.ace.commands.addCommands([
|
|
{
|
|
bindKey: "ESC",
|
|
exec: function(){ hide(); }
|
|
}, {
|
|
bindKey: "Up",
|
|
exec: function(){ tree.execCommand("goUp"); }
|
|
}, {
|
|
bindKey: "Down",
|
|
exec: function(){ tree.execCommand("goDown"); }
|
|
}, {
|
|
bindKey: "Enter",
|
|
exec: function(){ openFile(true); }
|
|
}
|
|
]);
|
|
|
|
tree.on("mousedown", function(e) {
|
|
var pos = e.getDocumentPosition();
|
|
var node = tree.provider.findItemAtOffset(pos.y);
|
|
ldSearch.selectNode(node);
|
|
openFile(true);
|
|
})
|
|
|
|
txtGoToFile.ace.on("input", function(e) {
|
|
var val = txtGoToFile.getValue();
|
|
filter(val);
|
|
|
|
if (dirty && val.length > 0 && ldSearch.loaded) {
|
|
dirty = false;
|
|
updateFileCache(true);
|
|
}
|
|
});
|
|
|
|
// @Harutyun, the select event should be on the tree or the selection object
|
|
// I just hacked it into the dataprovider for now, but that is very wrong.
|
|
// tree.getSelection().on("changeSelection", function(){ previewFile(); });
|
|
// tree.on("select", function(){ previewFile(); });
|
|
ldSearch.on("select", function(){ previewFile(); });
|
|
|
|
function onblur(e) {
|
|
if (!winGoToFile.visible)
|
|
return;
|
|
|
|
var to = e.toEasdasdasdasdaaaasssdddlement;
|
|
if (!to || apf.isChildOf(winGoToFile, to, true)
|
|
|| (lastPreviewed && tabs.previewTab
|
|
&& tabs.previewTab === lastPreviewed
|
|
&& (apf.isChildOf(lastPreviewed.aml.relPage, to, true)
|
|
|| lastPreviewed.aml == to))) {
|
|
return;
|
|
}
|
|
|
|
hide();
|
|
}
|
|
|
|
apf.addEventListener("movefocus", onblur);
|
|
|
|
// Focus the input field
|
|
txtGoToFile.focus();
|
|
|
|
// Offline
|
|
c9.on("stateChange", offlineHandler, plugin);
|
|
offlineHandler({ state: c9.status });
|
|
|
|
emit("draw");
|
|
}
|
|
|
|
/***** Methods *****/
|
|
|
|
function reloadResults(){
|
|
if (!winGoToFile)
|
|
return;
|
|
|
|
// Wait until window is visible
|
|
if (!winGoToFile.visible) {
|
|
winGoToFile.on("prop.visible", function visible(e) {
|
|
if (e.value) {
|
|
reloadResults();
|
|
winGoToFile.off("prop.visible", visible);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
// @Harutyun, how do I get the selection, scrollTop?
|
|
|
|
// var sel = [];
|
|
// dgGoToFile.getSelection().forEach(function(node) {
|
|
// var i = node.firstChild.nodeValue;
|
|
// sel.push(searchResults[i]);
|
|
// });
|
|
|
|
// var state = {
|
|
// sel : sel, //store previous selection
|
|
// caret : dgGoToFile.caret && searchResults[dgGoToFile.caret.firstChild.nodeValue],
|
|
// scrollTop : dgGoToFile.$viewport.getScrollTop()
|
|
// };
|
|
|
|
if (lastSearch)
|
|
filter(lastSearch);//, state.sel.length);
|
|
else
|
|
ldSearch.updateData(arrayCache);
|
|
|
|
// @Harutyun, how do I set the selection, scrollTop?
|
|
|
|
// if (state.sel.length && state.sel.length < 100) {
|
|
// var list = [];
|
|
// sel = state.sel;
|
|
// for (var i = 0, l = sel.length; i < l; i++) {
|
|
// list.push(dgGoToFile.queryNode("//d:href[text()='"
|
|
// + searchResults.indexOf(sel[i]) + "']"));
|
|
// }
|
|
// dgGoToFile.selectList(list);
|
|
// if (state.caret)
|
|
// dgGoToFile.setCaret(dgGoToFile.queryNode("//d:href[text()='"
|
|
// + searchResults.indexOf(state.caret) + "']"));
|
|
// dgGoToFile.$viewport.setScrollTop(state.scrollTop);
|
|
// }
|
|
}
|
|
|
|
function markDirty(options) {
|
|
// Ignore hidden files
|
|
var path = options && options.path || "";
|
|
if (path && !fsCache.showHidden && path.charAt(0) == ".")
|
|
return;
|
|
|
|
dirty = true;
|
|
if (panels.isActive("navigate")) {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(function(){ updateFileCache(true); }, 2000);
|
|
}
|
|
}
|
|
|
|
function updateFileCache(isDirty) {
|
|
clearTimeout(timer);
|
|
|
|
find.getFileList({
|
|
path: "/",
|
|
nocache: isDirty,
|
|
hidden: fsCache.showHidden,
|
|
buffer: true
|
|
}, function(err, data) {
|
|
if (err)
|
|
return;
|
|
|
|
arrayCache = data.trim().split("\n");
|
|
reloadResults();
|
|
});
|
|
|
|
dirty = false;
|
|
}
|
|
|
|
/**
|
|
* Searches through the dataset
|
|
*
|
|
*/
|
|
function filter(keyword, nosel) {
|
|
keyword = keyword.replace(/\*/g, "");
|
|
|
|
if (!ldSearch.loaded) {
|
|
lastSearch = keyword;
|
|
return;
|
|
}
|
|
|
|
// Needed for highlighting
|
|
ldSearch.keyword = keyword;
|
|
|
|
var searchResults;
|
|
if (!keyword || !keyword.length) {
|
|
var result = arrayCache.slice();
|
|
// More prioritization for already open files
|
|
tabs.getTabs().forEach(function (tab) {
|
|
if (!tab.path
|
|
|| pages[i].document.meta.preview) return;
|
|
|
|
var idx = result.indexOf(tab.path);
|
|
if (idx > -1) {
|
|
result.splice(idx, 1);
|
|
result.unshift(tab.path);
|
|
}
|
|
});
|
|
searchResults = result;
|
|
}
|
|
else {
|
|
// @Harutyun, how do I set the scroll position?
|
|
// dgGoToFile.$viewport.setScrollTop(0);
|
|
searchResults = search.fileSearch(arrayCache, keyword);
|
|
}
|
|
|
|
lastSearch = keyword;
|
|
|
|
if (searchResults && searchResults.length)
|
|
ldSearch.updateData(searchResults);
|
|
|
|
if (nosel || !searchResults.length)
|
|
return;
|
|
|
|
// See if there are open files that match the search results
|
|
// and the first if in the displayed results
|
|
var pages = tabs.getTabs(), hash = {};
|
|
for (var i = pages.length - 1; i >= 0; i--) {
|
|
if (!pages[i].document.meta.preview)
|
|
hash[pages[i].path] = true;
|
|
}
|
|
|
|
// loop over all visible items. If we find a visible item
|
|
// that is in the `hash`, select it and return.
|
|
var first = tree.renderer.getFirstVisibleRow();
|
|
var last = tree.renderer.getLastVisibleRow();
|
|
for (var i = first; i < last; i++) {
|
|
if (hash[ldSearch.visibleItems[i]]) {
|
|
tree.select(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// select the first item in the list
|
|
if (keyword) {
|
|
tree.select(0);
|
|
}
|
|
}
|
|
|
|
function openFile(noanim) {
|
|
if (!ldSearch.loaded)
|
|
return false;
|
|
|
|
// @Harutyun, How do I get the selection (multiple)
|
|
// var nodes = dgGoToFile.getSelection();
|
|
|
|
// Temporary
|
|
var nodes = [];
|
|
var row = ldSearch.selectedRow;
|
|
nodes.push(ldSearch.visibleItems[row]);
|
|
|
|
// Cancel Preview and Keep the tab if there's only one
|
|
if (tabs.preview({ cancel: true, keep : nodes.length == 1 }) === true)
|
|
return hide();
|
|
|
|
hide();
|
|
|
|
var fn = function(){};
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
var path = "/" + nodes[i].replace(/^[\/]+/, "");
|
|
|
|
tabs.open({
|
|
path: path,
|
|
noanim: l > 1,
|
|
active: i == l - 1
|
|
}, fn);
|
|
}
|
|
|
|
lastPreviewed = null;
|
|
}
|
|
|
|
function previewFile(noanim) {
|
|
if (!settings.getBool("user/general/@preview-navigate"))
|
|
return;
|
|
|
|
if (!ldSearch.loaded)
|
|
return false;
|
|
|
|
var row = ldSearch.selectedRow;
|
|
var value = ldSearch.visibleItems[row];
|
|
if (!value)
|
|
return;
|
|
|
|
var path = "/" + value.replace(/^[\/]+/, "");
|
|
lastPreviewed = tabs.preview({ path: path }, function(){});
|
|
}
|
|
|
|
function show() {
|
|
panels.activate("navigate");
|
|
}
|
|
|
|
function hide(){
|
|
if (panels.isActive("navigate")) {
|
|
if (lastPanel)
|
|
panels.activate(lastPanel);
|
|
else
|
|
panels.deactivate("navigate");
|
|
|
|
// Cancel Preview
|
|
tabs.preview({ cancel: true });
|
|
|
|
if (tabs.focussedTab)
|
|
tabs.focussedTab.editor.focus();
|
|
}
|
|
}
|
|
|
|
/***** Lifecycle *****/
|
|
|
|
plugin.on("load", function(){
|
|
load();
|
|
});
|
|
plugin.on("enable", function(){
|
|
|
|
});
|
|
plugin.on("disable", function(){
|
|
|
|
});
|
|
plugin.on("unload", function(){
|
|
loaded = false;
|
|
drawn = false;
|
|
});
|
|
|
|
/***** Register and define API *****/
|
|
|
|
/**
|
|
* Navigation panel. Navigates to files, lines and symbols
|
|
**/
|
|
plugin.freezePublicAPI({
|
|
/**
|
|
*
|
|
*/
|
|
show: show
|
|
});
|
|
|
|
register(null, {
|
|
navigate: plugin
|
|
});
|
|
}
|
|
}); |