Merge pull request +15340 from c9/process-list

backport preview changes from v4
pull/198/merge
Harutyun Amirjanyan 2017-04-05 22:28:22 +04:00 zatwierdzone przez GitHub
commit 11ab9610b9
3 zmienionych plików z 269 dodań i 139 usunięć

Wyświetl plik

@ -192,7 +192,7 @@ define(function(require, exports, module) {
var panels = navigation && navigation.$ext && navigation.$ext.children;
if (panels) {
for (var i = 0; i < panels.length; i++) {
if (panels[i].name == panel) {
if (panels[i].name == panel || panels[i].name == "preferences." + panel) {
panel = panels[i].hostPlugin;
break;
}

Wyświetl plik

@ -64,27 +64,8 @@ define(function(require, exports, module) {
if (!options.hideButton) {
var submenu = new ui.menu({
"onprop.visible": function(e) {
var tab = tabs.focussedTab;
var isKnown = false;
if (tab && tab.path) {
var path = tab.path;
for (var name in previewers) {
if (previewers[name].matcher(path)) {
isKnown = true;
break;
}
}
liveMenuItem.setAttribute("caption", isKnown
? "Live Preview File (" + basename(path) + ")"
: "Raw Content of " + basename(path)
);
liveMenuItem.enable();
}
else {
liveMenuItem.disable();
}
if (e.value)
updatePreviewMenu(e, submenu);
}
});
@ -99,7 +80,7 @@ define(function(require, exports, module) {
menus.addItemByPath("Tools/Preview/", submenu, 1000, handle);
liveMenuItem = new ui.item({
onclick: function(e) { commands.exec("preview", { newTab: e && e.button == 1 }); }
onclick: function(e) { commands.exec("preview", null, { newTab: e && e.button == 1 }); }
});
menus.addItemByPath("Tools/Preview/Live Preview Files",
liveMenuItem, 100, handle);
@ -111,6 +92,14 @@ define(function(require, exports, module) {
});
}
}), 200, handle);
menus.addItemByPath("Tools/Preview/~", new ui.divider({}), 2000, handle);
menus.addItemByPath("Tools/Preview/~", new ui.divider({}), 4000, handle);
menus.addItemByPath("Tools/Preview/Configure Preview URL...", new ui.item({
onclick: function(e) { commands.exec("openpreferences", null, {panel: "project", section: "Run & Debug"}); }
}), 4200, handle);
menus.addItemByPath("Tools/Preview/Show Active Servers...", new ui.item({
onclick: function(e) { commands.exec("showprocesslist", null, { mode: "lsof" }); }
}), 4300, handle);
}
settings.on("read", function(e) {
@ -122,18 +111,18 @@ define(function(require, exports, module) {
}, handle);
// Context menu for tree
var itemCtxTreePreview = new ui.item({
match: "file",
caption: "Preview",
isAvailable: function() {
return tree.selectedNode && !tree.selectedNode.isFolder
&& (options.local || util.normalizePath(tree.selectedNode.path).charAt(0) != "~");
},
onclick: function() {
openPreview(tree.selected);
}
});
tree.getElement("mnuCtxTree", function(mnuCtxTree) {
var itemCtxTreePreview = new ui.item({
match: "file",
caption: "Preview",
isAvailable: function() {
return tree.selectedNode && !tree.selectedNode.isFolder
&& (options.local || util.normalizePath(tree.selectedNode.path).charAt(0) != "~");
},
onclick: function() {
openPreview(tree.selected);
}
});
ui.insertByIndex(mnuCtxTree, itemCtxTreePreview, 160, handle);
});
@ -163,70 +152,30 @@ define(function(require, exports, module) {
var nodes = tab.pane.group;
if (!nodes)
pane = tab.pane.hsplit(true);
else {
else
pane = nodes[nodes.indexOf(tab.pane) === 0 ? 1 : 0];
}
}
return pane;
}
if (args.server) {
var hostname = c9.hostname || "localhost:8080";
var openInNewTab = args.newTab;
path = args.url;
var cb = function(err, stderr, stdout) {
if (err && err.code != 1)
showError("Could not check if server is running.");
else if (stderr || !stdout || !stdout.length) {
// Check for project run config
var json = settings.getJson("project/run/configs") || {};
for (var name in json) {
if (json[name]["default"]) {
commands.exec("run", null, {
callback: function(proc) {
proc.on("started", function() {
setTimeout(done, 1000);
});
}
});
return;
}
}
warnNoServer(hostname);
}
done();
};
if (!path)
path = "https://$C9_HOSTNAME";
function done() {
var path = (options.local ? "http" : "https")
+ "://" + hostname;
if (args.newTab)
return util.openNewWindow(path);
// Open Pane
pane = findPane();
// Open Preview
openPreview(path, pane, args && args.active);
}
path = expandUrl(path);
if (args.nocheck)
done();
else if (options.local) {
proc.execFile("lsof", {
args: ["-i", ":8080"]
}, cb);
}
else {
proc.execFile("nc", {
args: ["-zv", hostname, "80"]
}, cb);
}
if (window.location.protocol == "https:" && !path.startsWith("https:"))
openInNewTab = true;
return;
if (openInNewTab)
return util.openNewWindow(path);
pane = findPane();
return openPreview(path, pane, args && args.active);
}
else if (args.path) {
path = args.path;
@ -284,6 +233,20 @@ define(function(require, exports, module) {
var key = commands.getHotkey("reloadpreview");
if (commands.platform == "mac")
key = apf.hotkeys.toMacNotation(key);
prefs.add({
"Project": {
position: 100,
"Run & Debug": {
position: 300,
"Preview URL": {
type: "textbox",
path: "project/preview/@url"
},
}
}
}, handle);
prefs.add({
"Run": {
position: 600,
@ -312,7 +275,7 @@ define(function(require, exports, module) {
{ caption: "Only on " + key, value: "false" },
{ caption: "Always", value: "true" },
]
}
},
}
}
}, handle);
@ -357,6 +320,13 @@ define(function(require, exports, module) {
return pane;
}
function expandUrl(url) {
var hostname = c9.hostname;
if (!c9.hosted && !hostname)
hostname = window.location.hostname;
return url.replace(/\$C9_HOSTNAME\b/, hostname);
}
function registerPlugin(plugin, matcher) {
previewers[plugin.name] = {
plugin: plugin,
@ -409,6 +379,73 @@ define(function(require, exports, module) {
+ "and change the hostname in the location bar.");
}
function updatePreviewMenu(e, submenu) {
var tab = tabs.focussedTab;
var isKnown = false;
var title = "Live Preview File";
if (tab && tab.path) {
var path = tab.path;
for (var name in previewers) {
if (previewers[name].matcher(path)) {
isKnown = true;
break;
}
}
if (!isKnown) {
title = "Raw Content of " + basename(path);
isKnown = true;
}
else {
title += " (" + basename(path) + ")";
}
}
liveMenuItem.setAttribute("caption", title);
if (isKnown)
liveMenuItem.enable();
else
liveMenuItem.disable();
// user configured elements
var url = settings.get("project/preview/@url");
if (!Array.isArray(url)) url = [url];
var added = false;
var index = 0;
var children = submenu.childNodes;
while (children[index] && children[index].localName != "divider") {
index++;
}
var appMenu = children[index - 1];
var firstDivider = children[index];
index++;
for (var i = 0; i < url.length; i++) {
if (typeof url[i] != "string" || !url[i]) continue;
var oldNode = children[index];
if (!oldNode || oldNode.localName != "item") {
oldNode = submenu.insertBefore(new ui.item({
onclick: openUrl
}), oldNode);
}
oldNode.value = url[i];
var caption = "Open " + expandUrl(url[i]);
oldNode.setAttribute("caption", caption);
added = true;
index++;
}
while(index < children.length - 3)
submenu.removeChild(children[index]);
appMenu.setAttribute("visible", !added);
firstDivider.setAttribute("visible", added);
function openUrl(e) {
commands.exec("preview", null, {
newTab: e.button == 1,
url: e.currentTarget.value,
server: true
});
}
}
/**
* The preview handle, responsible for managing preview plugins.
* This is the object you get when you request the preview

Wyświetl plik

@ -1,6 +1,6 @@
define(function(require, exports, module) {
main.consumes = [
"ui", "layout", "commands", "Dialog", "proc", "util", "menus"
"ui", "layout", "commands", "Dialog", "proc", "util", "menus", "dialog.error"
];
main.provides = ["processlist"];
return main;
@ -15,6 +15,7 @@ define(function(require, exports, module) {
var proc = imports.proc;
var util = imports.util;
var menus = imports.menus;
var showError = imports["dialog.error"].show;
var search = require("../c9.ide.navigate/search");
var Tree = require("ace_tree/tree");
@ -47,6 +48,8 @@ define(function(require, exports, module) {
var INTERVAL = 5000;
var model, datagrid, btnKill, btnForceKill, tbFilter, timer;
var mode = "ps";
var loaded = false;
function load() {
if (loaded) return false;
@ -55,8 +58,8 @@ define(function(require, exports, module) {
commands.addCommand({
name: "showprocesslist",
bindKey: { mac: "Command-Option-P", win: "Ctrl-Alt-P" },
exec: function() {
plugin.show();
exec: function(editor, args) {
plugin.show(args.mode || "ps");
}
}, plugin);
@ -82,10 +85,11 @@ define(function(require, exports, module) {
model.$sortNodes = true;
model.$sorted = true;
model.columns = [{
model.columnsPs = [{
caption: "Process Name",
value: "name",
width: "100%"
width: "100%",
type: "tree",
}, {
caption: "CPU",
// value: "cpu",
@ -99,7 +103,7 @@ define(function(require, exports, module) {
}, {
caption: "Process Time",
value: "ptime",
width: "100",
width: "50",
}, {
caption: "PID",
value: "pid",
@ -110,6 +114,30 @@ define(function(require, exports, module) {
width: "80",
}];
model.columnsLsof = [{
caption: "Address",
value: "address",
width: "100%",
}, {
caption: "Process Name",
value: "command",
width: "80",
}, {
caption: "Status",
value: "status",
width: "50",
}, {
caption: "PID",
value: "pid",
width: "50",
}, {
caption: "Type",
value: "type",
width: "50",
}];
model.columns = model.columnsPs;
var datagridDiv = pNode.appendChild(document.createElement("div"));
datagrid = new Tree(datagridDiv);
datagrid.renderer.setTheme({ cssClass: "blackdg" });
@ -148,50 +176,112 @@ define(function(require, exports, module) {
applyFilter();
});
updateProcessList();
update();
emit("draw");
}
/***** Methods *****/
function update() {
if (mode == "ps")
updateProcessList();
else
updateServerList();
}
function updateProcessList() {
var sel = datagrid.selection.getSelectedNodes();
proc.execFile("ps", { args: ["auxc"]}, function(err, stdout, stderr) {
setModel(null, model.columnsPs);
proc.execFile("ps", { args: ["axh", "-ouser,pid:1,ppid:1,pcpu:1,pmem:1,time:1,command:1"] }, function(err, stdout, stderr) {
if (err) return;
var hiddenRegex = /^\[kthreadd|/
var lines = stdout.substr(0, stdout.length - 1).split("\n"); lines.shift();
var oldNodes = model.pidMap;
if (!oldNodes || oldNodes.mode != mode)
oldNodes = { mode: mode };
var pidMap = model.pidMap = { mode: mode };
var lines = stdout.substr(0, stdout.length - 1).split("\n");
var json = lines.map(function(line) {
var item = line.split(/\s+/);
var name = item.splice(10).join(" ");
return {
name: name,
cpu: item[2],
mem: item[3],
ptime: item[9],
pid: item[1],
user: item[0]
};
var name = item.slice(6).join(" ");
var pid = item[1];
var node = oldNodes[pid] || { pid: pid, isOpen: hiddenRegex.test(name) };
pidMap[pid] = node;
node.cpu = item[3];
node.mem = item[4];
node.ppid = item[2];
node.name = name;
node.user = item[0];
node.ptime = item[5];
node.items = node.children = null;
return node;
});
var root = [];
json.forEach(function(node) {
var parent = pidMap[node.ppid];
if (!parent) return root.push(node);
if (!parent.items) parent.items = [];
parent.items.push(node);
});
json = root;
setModel(json, model.columnsPs);
});
}
function updateServerList() {
setModel(null, model.columnsLsof);
proc.execFile("bash", { args: ["-c",
"sudo -n lsof -P -i -F pcnTtu || lsof -P -i -F pcnTtu"
]}, function(err, stdout, stderr) {
if (err) return;
var json = [];
var node;
var oldNodes = model.pidMap;
if (!oldNodes || oldNodes.mode != mode)
oldNodes = { mode: mode };
model.pidMap = { mode: mode };
stdout.split("\n").forEach(function(part) {
if (part[0] == "p") {
if (node) json.push(node);
var pid = part.slice(1);
node = oldNodes[pid] || { pid: pid };
model.pidMap[pid] = node;
}
if (part[0] == "c")
node.command = part.slice(1);
if (part[0] == "n")
node.address = part.slice(1);
if (part[0] == "T" && /^TST=/.test(part))
node.status = part.slice(4);
if (part[0] == "t")
node.type = part.slice(1);
if (part[0] == "u")
node.uid = parseInt(part.slice(1), 10);
});
if (node) json.push(node);
setModel(json, model.columnsLsof);
});
}
function setModel(json, columns) {
if (model.columns != columns) {
model.columns = columns;
datagrid.setDataProvider(model);
if (!json) json = [];
}
if (json) {
model.cachedRoot = json;
model.setRoot(json);
if (model.keyword)
applyFilter();
if (sel) {
var nodes = [];
var pids = sel.map(function(n) { return n.pid; });
model.root.items.forEach(function(item) {
if (pids.indexOf(item.pid) > -1)
nodes.push(item);
});
datagrid.selection.setSelection(nodes);
}
});
}
if (model.keyword)
applyFilter();
}
function forceKill() {
@ -201,22 +291,21 @@ define(function(require, exports, module) {
function kill(force) {
var nodes = datagrid.selection.getSelectedNodes();
if (!nodes.length) return;
var button = force ? btnForceKill : btnKill;
async.each(nodes, function(row, next) {
if (!/^\d+$/.test(row.pid)) return next();
button.disable();
var args = [];
if (force) args.push("-9");
args.push(row.pid);
proc.execFile("kill", { args: args }, function(err, stdout, stderr) {
var args = (force ? "" : "-9 ") + row.pid;
proc.execFile("bash", {
args: ["-c", "sudo -n kill " + args + " || kill " + args]
}, function(err, stdout, stderr) {
next(err);
});
}, function(err) {
button.enable();
if (!err) updateProcessList();
if (err) return showError(err);
update();
});
}
@ -235,29 +324,33 @@ define(function(require, exports, module) {
}
}
function show(reset, options) {
function show(mode, options) {
if (!options)
options = {};
setMode(mode);
return plugin.queue(function() {
// if (reset || current == -1) {
// path = [startPage];
// current = 0;
// activate(startPage);
// }
}, true);
}
function setMode(val) {
if ((val == "ps" || val == "lsof") && mode != val) {
mode = val;
if (timer) update();
}
}
/***** Lifecycle *****/
plugin.on("show", function() {
timer = setInterval(function() {
updateProcessList();
update();
}, INTERVAL);
update();
});
plugin.on("hide", function() {
clearInterval(timer);
timer = clearInterval(timer);
});
plugin.on("draw", function(options) {
draw(options);