From e12789ecbbb8694be30cf472406cf1ad7be7b217 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 4 Apr 2017 17:45:43 +0400 Subject: [PATCH 01/15] use c9build instead of .build --- plugins/c9.cli.publish/publish.js | 14 +++++++------- plugins/c9.ide.plugins/manager.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index 5043c490..fa628e94 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -196,9 +196,9 @@ define(function(require, exports, module) { // Write the package.json file var indent = data.match(/{\n\r?^ {4}"/) ? 4 : 2; var newData = JSON.stringify(json, null, indent); - fs.writeFile(cwd + "/.build/package.json", newData, function() { + fs.writeFile(cwd + "/c9build/package.json", newData, function() { if (dryRun) - return next(); // if dry-run is passed only update path in .build + return next(); // if dry-run is passed only update path in c9build fs.writeFile(packagePath, newData, function(err) { if (err) return callback(err); return next(); @@ -448,17 +448,17 @@ define(function(require, exports, module) { }, function(next) { proc.execFile("rm", { - args: ["-rf", ".c9/.build"], + args: ["-rf", "c9build"], cwd: cwd }, function() { - mkdirP(cwd + "/.build"); - fs.writeFile(cwd + "/.build/package." + packageName + ".js", result.code, "utf8", next); + mkdirP(cwd + "/c9build"); + fs.writeFile(cwd + "/c9build/package." + packageName + ".js", result.code, "utf8", next); }); }, function(next) { var copy = require("architect-build/copy"); - var excludeRe = /^\.(\w*ignore|git|c9|hg|build)$/; + var excludeRe = /^\.(\w*ignore|git|c9|hg|build)$|^(c9)?build$/; var excludeMap = Object.create(null); packedFiles.push(cwd + "/package." + packageName + ".js"); @@ -470,7 +470,7 @@ define(function(require, exports, module) { if (json.installer) excludeMap["/" + normalizePath(Path.relative(cwd, json.installer))] = 0; - copy(cwd, cwd + "/.build", { + copy(cwd, cwd + "/c9build", { exclude: function(name, parent) { if (excludeRe.test(name)) return true; diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 6897162f..e96def6a 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -729,7 +729,7 @@ define(function(require, exports, module) { if (!options.name || options.name == "json") options.name = parts[parts.length - 1]; // try parent folder name - if (/^(.?build|master)/.test(options.name)) + if (/^(.?build|master|c9build)/.test(options.name)) options.name = parts[parts.length - 2]; // remove version from the name options.name = options.name.replace(/@.*$/, ""); From 02d465b463604131a9be69e5c9fcd88a13f813d2 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 12 Feb 2017 23:52:52 +0400 Subject: [PATCH 02/15] remove old code --- configs/ide/default.js | 23 +- plugins/c9.ide.plugins/debug.js | 554 -------------------------- plugins/c9.ide.plugins/installer.js | 196 --------- plugins/c9.ide.plugins/loader.js | 340 ---------------- plugins/c9.ide.plugins/packages.js | 202 ---------- plugins/c9.ide.plugins/updater-npm.js | 417 ------------------- 6 files changed, 4 insertions(+), 1728 deletions(-) delete mode 100644 plugins/c9.ide.plugins/debug.js delete mode 100644 plugins/c9.ide.plugins/installer.js delete mode 100644 plugins/c9.ide.plugins/loader.js delete mode 100644 plugins/c9.ide.plugins/packages.js delete mode 100644 plugins/c9.ide.plugins/updater-npm.js diff --git a/configs/ide/default.js b/configs/ide/default.js index 14c381eb..aeb60a7c 100644 --- a/configs/ide/default.js +++ b/configs/ide/default.js @@ -81,30 +81,15 @@ module.exports = function(options) { // baseUrl: options.apiUrl // }, "plugins/c9.core/util", - { - packagePath: "plugins/c9.ide.plugins/loader", - plugins: options.plugins || [], - loadFromDisk: options.standalone - }, - { - packagePath: "plugins/c9.ide.plugins/installer", - updates: options.pluginUpdates || [] - }, { packagePath: "plugins/c9.ide.plugins/manager", staticPrefix: staticPrefix + "/plugins/c9.ide.plugins", devel: devel }, - { - packagePath: "plugins/c9.ide.plugins/debug" - }, - { - packagePath: "plugins/c9.ide.plugins/packages" - }, - { - packagePath: "plugins/c9.ide.plugins/test", - staticPrefix: staticPrefix + "/plugins/c9.ide.plugins" - }, + // { + // packagePath: "plugins/c9.ide.plugins/test", + // staticPrefix: staticPrefix + "/plugins/c9.ide.plugins" + // }, // VFS "plugins/c9.vfs.client/vfs.ping", diff --git a/plugins/c9.ide.plugins/debug.js b/plugins/c9.ide.plugins/debug.js deleted file mode 100644 index 91e150da..00000000 --- a/plugins/c9.ide.plugins/debug.js +++ /dev/null @@ -1,554 +0,0 @@ -/*global requirejs*/ -define(function(require, exports, module) { - main.consumes = [ - "Plugin", "vfs", "fs", "plugin.loader", "c9", "ext", "watcher", - "dialog.notification", "dialog.info", "ui", "menus", "commands", "settings", "auth", - "installer", "find", "util", "preferences.experimental" - ]; - main.provides = ["plugin.debug"]; - return main; - - function main(options, imports, register) { - var Plugin = imports.Plugin; - var vfs = imports.vfs; - var watcher = imports.watcher; - var ext = imports.ext; - var util = imports.util; - var find = imports.find; - var ui = imports.ui; - var menus = imports.menus; - var installer = imports.installer; - var settings = imports.settings; - var commands = imports.commands; - var fs = imports.fs; - var c9 = imports.c9; - var auth = imports.auth; - var loader = imports["plugin.loader"]; - var notify = imports["dialog.notification"].show; - var experimental = imports["preferences.experimental"]; - var showInfo = imports["dialog.info"].show; - - var dirname = require("path").dirname; - var basename = require("path").basename; - var join = require("path").join; - var async = require("async"); - - var architect; - - /***** Initialization *****/ - - var plugin = new Plugin("Ajax.org", main.consumes); - var emit = plugin.getEmitter(); - - var plugins = []; - - var ENABLED = c9.location.indexOf("debug=2") > -1; - var HASSDK = ENABLED || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins"); - - var reParts = /^(builders|keymaps|modes|outline|runners|snippets|themes|templates)\/(.*)/; - var reModule = /(?:_highlight_rules|_test|_worker|_fold|_behaviou?r)\.js$/; - var jsExtRe = /\.js$/; - - var loaded = false; - function load() { - if (loaded) return false; - loaded = true; - - if (!HASSDK) return; - - menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); - menus.addItemByPath("Tools/Developer", null, 100100, plugin); - - if (!ENABLED) { - menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ - onclick: function() { - var url = location.href + (location.href.indexOf("?") > -1 - ? "&debug=2" - : "?debug=2"); - window.open(url); - } - }), 900, plugin); - } - - if (!ENABLED) return; - - notify("
You are in Debug Mode. " - + "Don't forget to open the browser's dev tools to see any errors.", - false); - - // Insert relevant LESS libraries - var theme = settings.get("user/general/@skin"); - - ui.defineLessLibrary(require("text!../c9.ide.layout.classic/themes/default-" + theme + ".less"), plugin); - ui.defineLessLibrary(require("text!../c9.ide.layout.classic/less/lesshat.less"), plugin); - - fs.readdir("~/.c9/plugins", function(err, list) { - if (err) return console.error(err); - - var names = loader.plugins; - var toLoad = []; - - list.forEach(function(stat) { - var name = stat.name; - // If the plugin doesn't exist - if (names.indexOf(name) == -1 && name.charAt(0) != "." && name.charAt(0) != "_") - toLoad.push(name); - }); - - loadPlugins(toLoad); - }); - - commands.addCommand({ - name: "reloadCustomPlugin", - group: "Plugins", - bindKey: { - mac: "Command-Enter", - win: "Ctrl-Enter" - }, - exec: function() { - reloadPluginUI(); - } - }, plugin); - - menus.addItemByPath("Tools/Developer/Reload Custom Plugin", new ui.item({ - command: "reloadCustomPlugin" - }), 1000, plugin); - } - - /***** Methods *****/ - - function loadPlugins(list) { - if (!vfs.connected) { - vfs.once("connect", loadPlugins.bind(this, config)); - return; - } - - if (typeof list == "string") - list = [list]; - - var config = []; - var loadConfig = function() { - architect.loadAdditionalPlugins(config, function(err) { - if (err) console.error(err); - }); - }; - - async.each(list, function(name, next) { - var resourceHolder = new Plugin(); - var resourceVersion = ""; - - resourceHolder.on("load", function() { - if (inited) load(); - }); - - resourceHolder.freezePublicAPI({ - get version() { return resourceVersion; }, - set version(v) { resourceVersion = v; } - }); - - var inited = false; - function load() { - async.parallel([ - function(next) { - // Fetch package.json - fs.readFile("~/.c9/plugins/" + name + "/package.json", function(err, data) { - if (err) - return next(err); - - try { - var options = JSON.parse(data); - if (!options.plugins) - throw new Error("Missing plugins property in package.json of " + name); - } - catch (e) { - return next(err); - } - - var host = vfs.baseUrl + "/"; - var base = join(String(c9.projectId), - "plugins", auth.accessToken); - - // Configure Require.js - var pathConfig = {}; - pathConfig["plugins/" + name] = host + join(base, name); - requirejs.config({ paths: pathConfig }); - - // Add the plugin to the config - Object.keys(options.plugins).forEach(function(path) { - var pluginPath = name + "/" + path; - - // Watch project path - watch("~/.c9/plugins/" + pluginPath); - var cfg = options.plugins[path]; - cfg.packagePath = "plugins/" + name + "/" + path; - cfg.staticPrefix = host + join(base, name); - cfg.apikey = "0000000000000000000000000000="; - - // Set version for package manager - cfg.version = options.version; - - config.push(cfg); - plugins.push(name + "/" + path); - }); - - // Set version for package manager - resourceHolder.version = options.version; - - // Start the installer if one is included - if (options.installer) { - addStaticPlugin("installer", name, options.installer, - null, resourceHolder); - } - - next(); - }); - }, - function(next) { - var path = join("~/.c9/plugins", name); - var rePath = new RegExp("^" + util.escapeRegExp(path.replace(/^~/, c9.home) + "/"), "gm"); - find.getFileList({ - path: path, - nocache: true, - buffer: true - }, function(err, data) { - if (err) - return next(err); - - // Remove the base path - data = data.replace(rePath, ""); - - if (data.match(/^__installed__.js/)) - return next("installed"); - - // Process all the submodules - var parallel = processModules(path, data, resourceHolder); - async.parallel(parallel, function(err, data) { - if (err) - return next(err); - - if (!inited) - resourceHolder.load(name + ".bundle"); - - // Done - next(); - }); - }); - } - ], function(err, results) { - if (err) console.error(err); - - if (!inited) { - next(); - inited = true; - } - }); - } - - load(); - }, function() { - emit.sticky("ready"); - - if (!config.length) return; - - // Load config - if (installer.sessions.length) { - installer.on("stop", function listen(err) { - if (err) - return console.error(err); - - if (!installer.sessions.length) { - loadConfig(); - installer.off("stop", listen); - } - }); - return; - } - - loadConfig(); - }); - } - - function processModules(path, data, plugin) { - var parallel = []; - - data.split("\n").forEach(function(line) { - if (!line.match(reParts)) return; - - var type = RegExp.$1; - var filename = RegExp.$2; - if (filename.indexOf("/") > -1) return; - - if (type == "modes" && (reModule.test(filename) || !jsExtRe.test(filename))) - return; - - if (type == "snippets") { - if (jsExtRe.test(filename)) { - var snippetPath = join("plugins", basename(path), type, filename).replace(jsExtRe, ""); - require([snippetPath], function(m) { - architect.services["language.complete"].addSnippet(m, plugin); - }); - } - if (!/\.snippets$/.test(filename)) - return; - } - - parallel.push(function(next) { - fs.readFile(join(path, type, filename), function(err, data) { - if (err) { - console.error(err); - return next(err); - } - - addStaticPlugin(type, basename(path), filename, data, plugin); - - next(); - }); - }); - }); - - return parallel; - } - - function addStaticPlugin(type, pluginName, filename, data, plugin) { - var services = architect.services; - var path = "plugins/" + pluginName + "/" - + (type == "installer" ? "" : type + "/") - + filename.replace(/\.js$/, ""); - - var bundleName = pluginName + ".bundle"; - if (!services[bundleName] && type !== "installer") { - services[bundleName] = plugin; - architect.lut["~/.c9/plugins/" + pluginName] = { - provides: [] - }; - architect.pluginToPackage[bundleName] = { - path: "~/.c9/plugins/" + pluginName, - package: pluginName, - version: plugin.version, - isAdditionalMode: true - }; - if (!architect.packages[pluginName]) - architect.packages[pluginName] = []; - architect.packages[pluginName].push(name); - } - - switch (type) { - case "builders": - data = util.safeParseJson(data, function() {}); - if (!data) return; - - services.build.addBuilder(filename, data, plugin); - break; - case "keymaps": - data = util.safeParseJson(data, function() {}); - if (!data) return; - - services["preferences.keybindings"].addCustomKeymap(filename, data, plugin); - break; - case "modes": - var mode = {}; - var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); - firstLine.split(";").forEach(function(n) { - if (!n) return; - var info = n.split(":"); - mode[info[0].trim()] = info[1].trim(); - }); - - services.ace.defineSyntax({ - name: path, - caption: mode.caption, - extensions: (mode.extensions || "").trim() - .replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1") - }); - break; - case "outline": - if (!data) return; - - services.outline.addOutlinePlugin(path, data, plugin); - break; - case "runners": - data = util.safeParseJson(data, function() {}); - if (!data) return; - - services.run.addRunner(data.caption || filename, data, plugin); - break; - case "snippets": - services["language.complete"].addSnippet(data, plugin); - break; - case "themes": - services.ace.addTheme(data, plugin); - break; - case "templates": - services.newresource.addFileTemplate(data, plugin); - break; - case "installer": - if (data) { - installer.createSession(pluginName, data, function(v, o) { - require([path], function(fn) { - fn(v, o); - }); - }); - } - else { - require([path], function(fn) { - installer.createSession(pluginName, fn.version, function(v, o) { - fn(v, o); - }); - }); - } - } - } - - // Check if require.s.contexts._ can help watching all dependencies - function watch(path) { - watcher.watch(path); - - watcher.on("change", function(e) { - if (e.path == path) - reloadPackage(path.replace(/^~\/\.c9\//, "")); - }); - watcher.on("delete", function(e) { - if (e.path == path) - reloadPackage(path.replace(/^~\/\.c9\//, "")); - }); - watcher.on("failed", function(e) { - if (e.path == path) { - setTimeout(function() { - watcher.watch(path); // Retries once after 1s - }); - } - }); - } - - function reloadPackage(path) { - var unloaded = []; - - function recurUnload(name) { - var plugin = architect.services[name]; - unloaded.push(name); - - // Find all the dependencies - var deps = ext.getDependencies(plugin.name); - - // Unload all the dependencies (and their deps) - deps.forEach(function(name) { - recurUnload(name); - }); - - // Unload plugin - plugin.unload(); - } - - // Recursively unload plugin - var p = architect.lut[path]; - if (p.provides) { // Plugin might not been initialized all the way - p.provides.forEach(function(name) { - recurUnload(name); - }); - } - - // create reverse lookup table - var rlut = {}; - for (var packagePath in architect.lut) { - var provides = architect.lut[packagePath].provides; - if (provides) { // Plugin might not been initialized all the way - provides.forEach(function(name) { - rlut[name] = packagePath; - }); - } - } - - // Build config of unloaded plugins - var config = [], done = {}; - unloaded.forEach(function(name) { - var packagePath = rlut[name]; - - // Make sure we include each plugin only once - if (done[packagePath]) return; - done[packagePath] = true; - - var options = architect.lut[packagePath]; - delete options.provides; - delete options.consumes; - delete options.setup; - - config.push(options); - - // Clear require cache - requirejs.undef(options.packagePath); // global - }); - - // Load all plugins again - architect.loadAdditionalPlugins(config, function(err) { - if (err) console.error(err); - }); - } - - function reloadPluginUI() { - var list = []; - Object.keys(architect.pluginToPackage).forEach(function(name) { - if (architect.pluginToPackage[name].isAdditionalMode) - list.push(architect.pluginToPackage[name].path); - }); - - var path = list[list.length - 1]; - reloadPackage(path.replace(/^~\/\.c9\//, "")); - - // Avoid confusion with "Reload Last Plugin" - var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); - window.history.replaceState(window.history.state, null, href); - showInfo("Reloaded " + path + ".", 1000); - } - - /***** Lifecycle *****/ - - plugin.on("load", function() { - load(); - }); - plugin.on("unload", function() { - loaded = false; - }); - - /***** Register and define API *****/ - - /** - * - **/ - plugin.freezePublicAPI({ - /** - * - */ - get architect() { throw new Error(); }, - set architect(v) { architect = v; }, - /** - * - */ - get plugins() { return plugins; }, - - _events: [ - /** - * @event ready - */ - "ready" - ], - - /** - * - */ - addStaticPlugin: addStaticPlugin, - - /** - * - */ - reloadPackage: reloadPackage, - /** - * - */ - loadPackage: loadPlugins - }); - - register(null, { - "plugin.debug": plugin - }); - } -}); diff --git a/plugins/c9.ide.plugins/installer.js b/plugins/c9.ide.plugins/installer.js deleted file mode 100644 index 6a6abff8..00000000 --- a/plugins/c9.ide.plugins/installer.js +++ /dev/null @@ -1,196 +0,0 @@ -define(function(require, exports, module) { - main.consumes = [ - "Plugin", "proc", "c9", "pubsub", "auth", "util", "installer", - "preferences.experimental" - ]; - main.provides = ["plugin.installer"]; - return main; - - function main(options, imports, register) { - var Plugin = imports.Plugin; - var c9 = imports.c9; - var util = imports.util; - var proc = imports.proc; - var auth = imports.auth; - var pubsub = imports.pubsub; - var installer = imports.installer; - var experimental = imports["preferences.experimental"]; - - var async = require("async"); - - var escapeShell = util.escapeShell; - var updates = options.updates; - var architect; - - /***** Initialization *****/ - - var plugin = new Plugin("Ajax.org", main.consumes); - var emit = plugin.getEmitter(); - - var DEBUG_MODE = c9.location.indexOf("debug=2") > -1; - var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins"); - - var installing; - - var loaded = false; - function load() { - if (loaded) return false; - loaded = true; - - if (!HASSDK) return; - - pubsub.on("message", function(message) { - if (message.type != "package") - return; - - console.log("PubSub package API message", message); - var action = message.action; - var body = message.body; - - // Only accept packages that are installed for this project - if (body.pid && body.pid != c9.projectId) - return; - - // Only accept packages that are installed for this user - if (body.uid && body.uid != c9.uid) - return; - - if (action == "install") { - installPlugins([body.config], function() {}); - } - else if (action == "uninstall") { - uninstallPlugin(body.name, function() {}); - } - }); - - installPlugins(updates); - } - - /***** Methods *****/ - - function installPlugins(config, callback) { - // if (!vfs.connected) { - // vfs.once("connect", loadPackages.bind(this, config)); - // return; - // } - - if (!config.length) - return callback && callback(); - - // Only run one installer at a time - if (installing) { - return plugin.once("finished", function() { - installPlugins(config, callback); - }); - } - - installing = true; - - var found = {}, packages = []; - config.forEach(function(item) { - if (typeof item === "string") { - item = { name: item, version: null }; - } - - if (found[item.name]) return; - found[item.name] = true; - - packages.push({ name: item.name, version: item.version }); - }); - - async.eachSeries(packages, function(pkg, next) { - installPlugin(pkg.name, pkg.version, next); - }, function(err) { - installing = false; - emit("finished"); - - if (err) { - console.error(err.message); - return callback && callback(err); - } - - architect.loadAdditionalPlugins(config, callback); - }); - } - - function installPlugin(name, version, callback) { - // Headless installation of the plugin - installer.createSession(name, version, function(session, options) { - var cmd = [ - "c9", - "install", - "--local", - "--force", - "--accessToken=" + auth.accessToken, - ]; - - if (version == null) - cmd.push(escapeShell(name)); - else - cmd.push(escapeShell(name + "@" + version)); - - session.install({ - "bash": cmd.join(" ") - }); - - // Force to start immediately - session.start(callback, true); - }, function() {}, 2); // Force to not be administered - } - - function uninstallPlugin(name, callback) { - // Headless uninstallation of the plugin - installer.createSession(name, -1, function(session, options) { - session.install({ - "bash": "c9 remove --local --force --accessToken=" + auth.accessToken - + " " + escapeShell(name) - }); - - // Force to start immediately - session.start(callback, true); - }, function() {}, 2); // Force to not be administered - } - - /***** Lifecycle *****/ - - plugin.on("load", function() { - load(); - }); - plugin.on("unload", function() { - loaded = false; - installing = false; - }); - - /***** Register and define API *****/ - - /** - * - **/ - plugin.freezePublicAPI({ - /** - * - */ - get architect() { throw new Error(); }, - set architect(v) { architect = v; }, - - /** - * - */ - installPlugins: installPlugins, - - /** - * - */ - installPlugin: installPlugin, - - /** - * - */ - uninstallPlugin: uninstallPlugin - }); - - register(null, { - "plugin.installer": plugin - }); - } -}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/loader.js b/plugins/c9.ide.plugins/loader.js deleted file mode 100644 index 08411773..00000000 --- a/plugins/c9.ide.plugins/loader.js +++ /dev/null @@ -1,340 +0,0 @@ -/*global requirejs*/ -define(function(require, exports, module) { - main.consumes = [ - "Plugin", "vfs", "c9", "plugin.installer", "fs", "auth", - "preferences.experimental" - ]; - main.provides = ["plugin.loader"]; - return main; - - function main(options, imports, register) { - var Plugin = imports.Plugin; - var vfs = imports.vfs; - var c9 = imports.c9; - var fs = imports.fs; - var auth = imports.auth; - var installer = imports["plugin.installer"]; - var experimental = imports["preferences.experimental"]; - - var dirname = require("path").dirname; - var join = require("path").join; - var async = require("async"); - var _ = require("lodash"); - - var architect; - - /***** Initialization *****/ - - var plugin = new Plugin("Ajax.org", main.consumes); - // var emit = plugin.getEmitter(); - - var DEBUG_MODE = c9.location.indexOf("debug=2") > -1; - var ENABLED = DEBUG_MODE || experimental.addExperiment("plugins", false, "SDK/Load Plugins From Workspace"); - var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins"); - - var plugins = options.plugins; - var loadFromDisk = options.loadFromDisk; - - var names = []; - - function load() { - if (!HASSDK) return; - if (!ENABLED) return; - - loadPlugins(plugins); - } - - /***** Methods *****/ - - // TODO the resolution alghoritm used here is very inefficient - // ideally we will use a simpler method that doesn't need to scan directories - function loadPlugins(loaderConfig) { - if (!vfs.connected) { - vfs.once("connect", loadPlugins.bind(this, loaderConfig)); - return; - } - - if (!loaderConfig.length && !loadFromDisk) - return; - - listAllPackages(function(err, resolved) { - if (err) return console.error(err); - - var extraPackages = {}; - // convert old format from db to the new one - loaderConfig.forEach(function(p) { - if (!extraPackages[p.packageName]) { - var path = "plugins/" + p.packageName; - extraPackages[path] = { - apikey: p.apikey, - packagePath: path, - version: p.version, - name: p.packageName - }; - } - }); - if (!loadFromDisk) { - // filter packages by config instead of loading - // everything from disk - resolved = resolved.filter(function(config) { - if (extraPackages[config.packagePath]) - return true; - - console.warn("[c9.ide.loader] Not loading package " - + config.path + " because it is not installed, " - + "according to the database"); - return false; - }); - } - resolved.filter(function(config) { - if (extraPackages[config.packagePath]) { - _.assign(config, extraPackages[config.packagePath], function(x, y) { return x || y; }); - delete extraPackages[config.packagePath]; - } - }); - Object.keys(extraPackages).forEach(function(packagePath) { - console.warn("[c9.ide.loader] Package " - + packagePath + " should be installed, according " - + "to the database, but was not found on the filesystem. " - + "Try reinstalling it."); - }); - - async.each(resolved, loadPackage, function(err) { - if (err) console.error(err); - }); - }); - } - - /** - * List all packages on disk by scanning `~/.c9` and resolve the - * detected packages by order of override priority: - * - * - Developer plugins in `~/.c9/dev/plugins` have the highest - * priority to allow local development of new functionality without - * the risk of having your changes overwritten by any update - * mechanism. - * - * - Managed plugins in `~/.c9/managed/plugins` are pre-installed and - * updated by the Cloud9 system and have a higher priority that - * possibly outdated or unknown packages installed by the user. - * - * - User-installed in `~/.c9/plugins` plugins installed plugins are - * the default priority have the lowest priority. - * - * @param {Function} callback - * @param {Error=} callback.err - */ - function listAllPackages(callback) { - async.parallel({ - "plugins": async.apply(listPackages, "~/.c9/plugins"), - "managed": async.apply(listPackages, "~/.c9/managed/plugins"), - "dev": async.apply(listPackages, "~/.c9/dev/plugins"), - }, function(err, packages) { - if (err && err.code === "EDISCONNECT") { - c9.once("connect", function() { - listAllPackages(callback); - }); - return; - } - - if (err) return callback(err); - - var resolved = {}; - - // default: ~/.c9/plugins - packages.plugins.forEach(function(config) { - if (!config) return; - resolved[config.name] = config; - }); - - // high: ~/.c9/managed/plugins - packages.managed.forEach(function(config) { - if (!config) return; - resolved[config.name] = config; - }); - - // higher: ~/.c9/dev/plugins - packages.dev.forEach(function(config) { - if (!config) return; - resolved[config.name] = config; - }); - - callback(null, _.values(resolved)); - }); - } - - /** - * List packages in the given directory - * - * @param {String} dirPath Path to the directory to scan - * - * @param {Function} callback - * @param {Error=} callback.err - * @param {Object[]} callback.packages - */ - function listPackages(dirPath, callback) { - fs.readdir(dirPath, function(err, stats) { - if (err && err.code === "ENOENT") - return callback(null, []); - - if (err) - return callback(err); - - async.map(stats, function(stat, done) { - // check for folder or symlink with folder target - if (stat.mime !== "inode/directory" - && (stat.mime === "inode/symlink" && stat.linkStat.mime !== "inode/directory") - ) { - return done(); - } - - // check folers not prefixed with [._] - if (stat.name[0] === "." || stat.name[0] === "_") { - return done(); - } - - // check and load package.json - var config = { - name: stat.name, - path: absolutePath([dirPath, stat.name].join("/")), - packagePath: ["plugins", stat.name].join("/"), - staticPrefix: vfs.url([dirPath, stat.name].join("/")), - }; - done(null, config); - }, callback); - }); - } - - /** - * Load a the `package.json` metadata of the given package - * - * @param {Object} config The package configuration - * - * @param {Function} callback - * @param {Error=} callback.err - * @param {Object} callback.metadata - */ - function loadPackageMetadata(config, callback) { - fs.readfile([config.path, "package.json" ].join("/"), function(metadataStr) { - var metadata; - - try { - metadata = JSON.parse(metadataStr); - } catch (e) { - return callback(e); - } - - callback(null, metadata); - }, function(err) { - callback(err); - }); - } - - /** - * Load a the `__installed__.js` definition of the given package - * - * @param {Object} config The package configuration - * - * @param {Function} callback - * @param {Error=} callback.err - * @param {Array} callback.installed - */ - function loadPackageInstalledJs(config, callback) { - var paths = {}; - paths[config.packagePath] = config.staticPrefix; - - requirejs.config({ paths: paths }); - requirejs.undef(config.packagePath, true); - - require([[config.packagePath, "__installed__"].join("/")], function(installed) { - callback(null, installed); - }, function(err) { - callback(err); - }); - } - - /** - * Load the given package by checking its `__installed__.js` file or - * its `package.json#plugins`, then call `Architect#loadAdditionalPlugins()`. - * - * @param {Object} config The package configuration - * - * @param {Function} callback - * @param {Error=} callback.err - */ - function loadPackage(config, callback) { - loadPackageInstalledJs(config, function addToArchitect(err, installed) { - var plugins = installed; - if (err) { - return callback(err); - // TODO disbled since this doesn't handle bundles and breaks debug=2 - // loadPackageMetadata(config, function(err, metadata) { - // if (err) return callback(err); - // config.metadata = metadata; - // var plugins = _.map(config.metadata.plugins, function(value, key) { - // return [ "plugins", config.name, key ].join("/"); - // }); - // addToArchitect(err, plugins); - // }); - } - - var architectConfig = plugins.map(function(plugin) { - if (typeof plugin == "string") - plugin = { packagePath: plugin }; - - plugin.staticPrefix = config.staticPrefix; - - plugin.packageName = config.name; - plugin.packageMetadata = config.metadata; - plugin.packageDir = config.path; - - plugin.apikey = config.apikey; - plugin.version = config.version; - - return plugin; - }); - - names.push(config.name); - - architect.loadAdditionalPlugins(architectConfig, function(err) { - callback(err); - }); - }); - } - - function absolutePath(fromPath) { - var toPath = fromPath.replace(/^~/, c9.home); - return toPath; - } - - /***** Lifecycle *****/ - - plugin.on("load", function() { - load(); - }); - plugin.on("unload", function() { - }); - - /***** Register and define API *****/ - - /** - * - **/ - plugin.freezePublicAPI({ - /** - * - */ - get architect() { throw new Error(); }, - set architect(v) { architect = v; }, - - /** - * - */ - get plugins() { return names; } - }); - - register(null, { - "plugin.loader": plugin - }); - } -}); diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js deleted file mode 100644 index 2cfd588b..00000000 --- a/plugins/c9.ide.plugins/packages.js +++ /dev/null @@ -1,202 +0,0 @@ -define(function(require, exports, module) { - main.consumes = [ - "Editor", "editors", "ui", "commands", "menus", "layout", - "tabManager", "util", "settings", "api", "c9", - "preferences.experimental" - ]; - main.provides = ["plugin.packages"]; - return main; - - function main(options, imports, register) { - var Editor = imports.Editor; - var editors = imports.editors; - var tabs = imports.tabManager; - var commands = imports.commands; - var settings = imports.settings; - var api = imports.api; - var c9 = imports.c9; - var menus = imports.menus; - var util = imports.util; - var layout = imports.layout; - var ui = imports.ui; - var experimental = imports["preferences.experimental"]; - - /***** Initialization *****/ - - var extensions = []; - var packages = {}; - - var handle = editors.register("plugin.packages", "Package Browser", - PackageBrowser, extensions); - var emit = handle.getEmitter(); - emit.setMaxListeners(1000); - - var DEBUG_MODE = c9.location.indexOf("debug=2") > -1; - var HASSDK = DEBUG_MODE || experimental.addExperiment("sdk", false, "SDK/Load Custom Plugins"); - - function focusOpenPackages() { - var pages = tabs.getTabs(); - for (var i = 0, tab = pages[i]; tab; tab = pages[i++]) { - if (tab.editorType == "plugin.packages") { - tabs.focusTab(tab); - return true; - } - } - } - - handle.on("load", function() { - if (!HASSDK) return; - - settings.on("read", function() { - settings.setDefaults("user/general", [["animateui", true]]); - }); - - commands.addCommand({ - name: "openpackagebrowser", - hint: "open the package browser", - group: "General", - // bindKey: { mac: "Command-,", win: "Ctrl-," }, - exec: function () { - var tab = tabs.focussedTab; - if (tab && tab.editor.type == "plugin.packages") { - tab.close(); - return; - } - if (focusOpenPackages()) - return; - - tabs.open({ - editorType: "plugin.packages", - active: true - }, function() {}); - } - }, handle); - - // menus.addItemByPath("Cloud9/~", new ui.divider(), 1000, handle); - // menus.addItemByPath("Cloud9/Package Browser", new ui.item({ - // command: "openpackagebrowser" - // }), 1100, handle); - }); - - /***** Methods *****/ - - function installPlugin(name, target, callback) { - var config = packages[name]; - if (!config) - return callback(new Error("Could not find plugin: " + name)); - - if (target != "project" && target != "user") - return callback(new Error("Invalid installation target: " + target)); - - api[target].put("install/" + name, function(err, state) { - callback(err); - }); - } - - function uninstallPlugin(name, target, callback) { - var config = packages[name]; - if (!config) - return callback(new Error("Could not find plugin: " + name)); - - if (target != "project" && target != "user") - return callback(new Error("Invalid installation target: " + target)); - - api[target].put("uninstall/" + name, function(err, state) { - callback(err); - }); - } - - /***** Register and define API *****/ - - /** - * - * @extends Plugin - * @singleton - */ - handle.freezePublicAPI({ - /** - * - */ - installPlugin: installPlugin, - - /** - * - */ - uninstallPlugin: uninstallPlugin - }); - - /***** Editor *****/ - - function PackageBrowser() { - var plugin = new Editor("Ajax.org", main.consumes, extensions); - //var emit = plugin.getEmitter(); - var tab, iframe; - - plugin.on("resize", function(e) { - emit("resize", e); - }); - - plugin.on("draw", function(e) { - tab = e.tab; - var htmlNode = e.htmlNode; - - htmlNode.style.paddingTop = 0; - - iframe = htmlNode.appendChild(document.createElement("iframe")); - iframe.style.width = "100%"; - iframe.style.height = "100%"; - iframe.style.border = 0; - iframe.style.backgroundColor = "#fbfbfb"; - - iframe.src = location.origin.replace("ide.", "") + "/profile/packages?nobar=1&pid=" + c9.projectId; - }); - - plugin.on("getState", function(e) { - }); - plugin.on("setState", function(e) { - }); - - /***** Lifecycle *****/ - - plugin.on("documentLoad", function(e) { - var doc = e.doc; - doc.title = "Package Browser"; - - function setTheme() { - var bg = "#fbfbfb"; - doc.tab.backgroundColor = bg; - - if (util.shadeColor(bg, 1).isLight) - doc.tab.classList.remove("dark"); - else - doc.tab.classList.add("dark"); - } - - layout.on("themeChange", setTheme, e.doc); - setTheme(); - }); - - plugin.on("documentActivate", function(e) { - - }); - - /***** Register and define API *****/ - - /** - * - * @extends Editor - */ - plugin.freezePublicAPI({ - - }); - - plugin.load(null, "plugin.packages"); - - return plugin; - } - - register(null, { - "plugin.packages": handle - }); - } -}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/updater-npm.js b/plugins/c9.ide.plugins/updater-npm.js deleted file mode 100644 index 13d89abc..00000000 --- a/plugins/c9.ide.plugins/updater-npm.js +++ /dev/null @@ -1,417 +0,0 @@ -define(function(require, exports, module) { - "use strict"; - - main.consumes = [ - "Plugin", - "c9", "proc", "Dialog", "ui", - ]; - main.provides = ["plugin.updater.npm"]; - return main; - - function main(options, imports, register) { - var Plugin = imports["Plugin"]; - var c9 = imports["c9"]; - var proc = imports["proc"]; - var Dialog = imports["Dialog"]; - var ui = imports["ui"]; - - var async = require("async"); - var path = require("path"); - var semverCompare = require("semver-compare"); - - var NPM_MIN_VERSION = "2.6.0"; - - /***** Initialization *****/ - - var npmBin = options.npmBin || "/home/ubuntu/.nvm/nvm-exec"; - var managedPath = options.managedPath || "/home/ubuntu/.c9/managed"; - - var managedRcPath = [managedPath, ".npmrc"].join("/"); - var managedNpmPath = [managedPath, "npm"].join("/"); - var managedEtcPath = [managedNpmPath, "etc"].join("/"); - var managedCachePath = [managedPath, "npm", "cache"].join("/"); - var managedPluginsPath = [managedPath, "plugins"].join("/"); - var managedModulesPath = [managedPath, "node_modules"].join("/"); - - var plugin = new Plugin("Ajax.org", main.consumes); - - function load() { - ui.insertCss(require("text!./style.css"), false, plugin); - - var pkgs = options.packages; - - if (!pkgs) { - console.warn("[plugin.updater.npm] no managed packages configured, not loading."); - return; - } - - // TODO: DRY error handling - - fsMkdirs([ managedPath, managedEtcPath, managedModulesPath, managedPluginsPath ], function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - fsWriteNpmrc(function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - npmCheckVersion(function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - // TODO: clean up the flow for detecting and installing missing - // packages. the nested functions are messy. - - async.filter(pkgs, function(pkg, done) { - npmExplorePath(pkg, function(err, path) { - var isMissing = !!err; - - if (isMissing) - debug("missing package:", pkg, err); - - done(isMissing); - }); - }, function(missing) { - if (missing.length) { - console.info("[plugin.updater.npm] Installing missing plugins:", missing); - showUpdateDialog(); - - npmInstall(missing, function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - buildLinks(pkgs, function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - // reload browser - window.location.reload(); - }); - }); - } - else { - npmOutdated(pkgs, function(err, outdated) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - if (!outdated) { - debug("Plugins up-to-date."); - return; - } - - console.info("[plugin.updater.npm] Updating outdated plugins:", outdated); - showUpdateDialog(); - - buildLinks(pkgs, function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - showErrorDialog(err); - return; - } - - npmUpdate(outdated, function(err) { - if (err) { - console.error("[plugin.updater.npm]", err); - alert("[plugin.updater.npm] Error: " + err.message); - } - - // reload browser - window.location.reload(); - }); - }); - }); - } - }); - }); - }); - }); - } - - function unload() { - } - - /* -- Helper Functions ----- */ - - function debug(format) { - if (c9.debug) { - var util = require("util"); - console.info(util.format.apply(null, - Array.prototype.slice.call(arguments) - )); - } - } - - /* -- npm Management ----- */ - - /** - * Check that the installed npm version matches the NPM_MIN_VERSION - * required - * - * @param {Function} callback - * @param {Error=} callback.err An error if the version is lower than required - */ - function npmCheckVersion(callback) { - npmExec("npm", ["-v"], function(err, stdout, stderr) { - if (err) return callback(err, stdout, stderr); - - var version = stdout; - - debug("npm version", version); - - if (semverCompare(version, NPM_MIN_VERSION) === -1) { - var error = new Error("npm version " + NPM_MIN_VERSION + " or greater required"); - return callback(error); - } - - callback(); - }); - } - - function npmExec(command, args, callback) { - debug(npmBin, { args: [ "npm", command ].concat(args) }); - - proc.execFile(npmBin, { - args: [ "npm", command ].concat(args), - cwd: managedPath, - env: { - "npm_config_production": "true", - "npm_config_depth": 0, - "npm_config_userconfig": "/dev/null", - "npm_config_prefix": managedNpmPath, - "npm_config_cache": managedCachePath, - }, - }, function(err, stdout, stderr) { - debug([err, stdout, stderr]); - - if (err) return callback(err, stdout, err.message); - - stdout = stdout.trimRight(); - stderr = stderr.trimRight(); - - callback(null, stdout, stderr); - }); - } - - /* -- Package Management ----- */ - - function npmOutdated(pkgs, callback) { - npmExec("outdated", ["--json"], function(err, stdout, stderr) { - if (err) - return callback(err, null, stdout, stderr); - - if (!stdout) - return callback(null, null, stdout, stderr); - - var outdated = JSON.parse(stdout); - outdated = Object.keys(outdated); - - callback(null, outdated, stdout, stderr); - }); - } - - function npmInstall(pkgs, callback) { - npmExec("install", ["--"].concat(pkgs), function(err, stdout, stderr) { - callback(err, stdout, stderr); - }); - } - - function npmUpdate(pkgs, callback) { - npmExec("update", ["--"].concat(pkgs), function(err, stdout, stderr) { - callback(err, stdout, stderr); - }); - } - - function npmExplorePath(pkg, callback) { - npmExec("explore", [pkg, "--", "pwd"], function(err, stdout, stderr) { - if (err) - return callback(err, null, stderr); - - callback(null, stdout, stderr); - }); - } - - /** - * Build the symbolic links from `~/.c9/plugins` to the managed plugins. - * - * @param {String[]} pkgs A list of package names to link. - * - * @param {Function} callback - * @param {Error=} callback.err - */ - function buildLinks(pkgs, callback) { - async.each(pkgs, function(pkg, done) { - npmExplorePath(pkg, function(err, pkgPath) { - if (err) return done(err); - fsLink(pkgPath, done); - }); - }, callback); - } - - /** - * Removes symbolic links from the `~/.c9/managed/plugins` folder. - */ - function fsRmLinks(callback) { - debug("find", { args: [ managedPluginsPath, "-maxdepth", "1", "-type", "l", "-exec", "rm", "{}", ";" ]}); - - // find . -maxdepth 1 -type l -exec rm {} \; - - proc.execFile("find", { - args: [ - managedPluginsPath, - "-maxdepth", "1", - "-type", "l", - "-exec", "rm", "{}", ";" - ], - }, function(err, stdout, stderr) { - debug([err, stdout, stderr]); - callback(err, stdout, stderr); - }); - } - - /** - * Create a symbolic link in `~/.c9/plugins` pointing to the given - * plugin path. - * - * @param {String} pkgPath Path to the source package folder - */ - function fsLink(pkgPath, callback) { - debug("ln", { args: [ "-s", "-f", pkgPath, [ managedPluginsPath, "." ].join("/") ]}); - - proc.execFile("ln", { - args: [ - "-s", "-f", - pkgPath, - managedPluginsPath + "/.", - ], - }, function(err, stdout, stderr) { - debug([err, stdout, stderr]); - callback(err, stdout, stderr); - }); - } - - function fsMkdirs(dirPaths, callback) { - debug("mkdir", { args: [ "-p", "--" ].concat(dirPaths) }); - - proc.execFile("mkdir", { - args: [ "-p", "--" ].concat(dirPaths), - }, function(err, stdout, stderr) { - callback(err, stdout, stderr); - }); - } - - function fsWriteNpmrc(callback) { - var config = [ - "//registry.npmjs.org/:_authToken = a7c61f6e-5b10-41db-947f-8bc8f1f9468b", - "production = true", - "depth = 0", - "userconfig = /dev/null", - "prefix = " + managedNpmPath, - "cache = " + managedCachePath, - ]; - - // - // HACK: - fs.writeFile() does not always work? we are using echo - // instead - // - // - config is not escaped - // - - debug("sh", { args: [ "-c", "echo '" + config.join("\\n") + "' > " + managedRcPath ]}); - - proc.execFile("sh", { - args: [ - "-c", - "echo '" + config.join("\\n") + "' > " + managedRcPath - ], - }, function(err, stdout, stderr) { - callback(err, stdout, stderr); - }); - } - - /* -- Interface ----- */ - - /** - * Show a modal upgrade progress dialog, blocking the IDE interface - * while we are updating the plugins. - */ - function showUpdateDialog() { - var dialog = new Dialog("Ajax.org", [], { - name: "plugin.updater.npm.dialog", - class: "dialog-updater", - allowClose: false, - modal: true, - elements: [ - ], - }); - - dialog.title = "Installing Updates"; - dialog.heading = ""; - dialog.body = "Your workspace will be updated to the newest version and reload automatically."; - - dialog.show(); - - return dialog; - } - - /** - * Show an upgrade error dialog, requesting to delete and recreate the - * workspace. This is shown for critical update errors. - */ - function showErrorDialog(err) { - var dialog = new Dialog("Ajax.org", [], { - name: "plugin.updater.npm.error_dialog", - allowClose: true, - modal: true, - elements: [ - ], - }); - - var errorMessage = (err && err.message) ? "" + err.message : err; - - dialog.title = "Error installing updates"; - dialog.heading = ""; - dialog.body = "Important updates could not be installed on this workspace.

" - + "Please delete this workspace and create a new one, in order to continue " - + "working in an up-to-date environment.

" - + "
" + errorMessage + "
"; - - dialog.show(); - - return dialog; - } - - /***** Register and define API *****/ - - plugin.on("load", load); - plugin.on("unload", unload); - - /** - * @class salesforc.sync - */ - plugin.freezePublicAPI({ - }); - - register(null, { - "plugin.updater.npm": plugin - }); - } -}); - From ee1495074bc915f2b04242b62530afe043e82265 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 12 Feb 2017 23:59:59 +0400 Subject: [PATCH 03/15] remove plugin manager code from ide.html --- node_modules/architect/architect.js | 33 +++++++------------ plugins/c9.ide.configuration/configure.js | 12 ++----- .../views/standalone.html.ejs | 13 +------- plugins/c9.vfs.standalone/www/ide.html | 13 +------- 4 files changed, 17 insertions(+), 54 deletions(-) diff --git a/node_modules/architect/architect.js b/node_modules/architect/architect.js index ef5b4edd..4e880ca5 100644 --- a/node_modules/architect/architect.js +++ b/node_modules/architect/architect.js @@ -20,10 +20,10 @@ if (typeof module === "object") (function () { // This is assumed to be used at startup and uses sync I/O as well as can // throw exceptions. It loads and parses a config file. function loadConfig(configPath, callback) { - var config = require(configPath); - var base = dirname(configPath); + var config = require(configPath); + var base = dirname(configPath); - return resolveConfig(config, base, callback); + return resolveConfig(config, base, callback); } function resolveConfig(config, base, callback) { @@ -209,7 +209,6 @@ function checkCycles(config, lookup) { if (plugins.length) { var unresolved = {}; plugins.forEach(function(plugin) { - delete plugin.config; plugin.consumes.forEach(function(name) { if (unresolved[name] === false) return; @@ -244,15 +243,16 @@ function checkCycles(config, lookup) { function Architect(config) { var app = this; app.config = config; - app.packages = {}; - app.pluginToPackage = {}; + app.serviceToPlugin = {}; + app.additionalConfigs = []; var isAdditionalMode; var services = app.services = { hub: { on: function (name, callback) { app.on(name, callback); - } + }, + app: app } }; @@ -275,10 +275,6 @@ function Architect(config) { }); } - var m = /^plugins\/([^\/]+)|\/plugins\/[^\/]+\/([^\/]+)/.exec(plugin.packagePath); - var packageName = m && (m[1] || m[2]); - if (!app.packages[packageName]) app.packages[packageName] = []; - if (DEBUG) { recur++; plugin.setup(plugin, imports, register); @@ -315,13 +311,8 @@ function Architect(config) { return app.emit("error", err); } services[name] = provided[name]; - app.pluginToPackage[name] = { - path: plugin.packagePath, - package: packageName, - version: plugin.version, - isAdditionalMode: isAdditionalMode - }; - app.packages[packageName].push(name); + app.serviceToPlugin[name] = plugin; + plugin.__isAdditionalMode = isAdditionalMode; app.emit("service", name, services[name], plugin); }); @@ -344,6 +335,8 @@ function Architect(config) { exports.resolveConfig(additionalConfig, function (err, additionalConfig) { if (err) return callback(err); + app.additionalConfigs.push(additionalConfig); + app.once(ready ? "ready-additional" : "ready", function(app){ callback(null, app); }); // What about error state? @@ -394,7 +387,7 @@ function createApp(config, callback) { var app; try { app = new Architect(config); - } catch(err) { + } catch (err) { if (!callback) throw err; return callback(err, app); } @@ -416,8 +409,6 @@ function createApp(config, callback) { app.removeListener("ready", onReady); callback(err, app); } - - return app; } return exports; diff --git a/plugins/c9.ide.configuration/configure.js b/plugins/c9.ide.configuration/configure.js index ea5f1938..0d370339 100644 --- a/plugins/c9.ide.configuration/configure.js +++ b/plugins/c9.ide.configuration/configure.js @@ -3,7 +3,7 @@ define(function(require, exports, module) { "Plugin", "dialog.error", "ui", "settings", "tabManager", "save", "menus", "preferences.keybindings", "preferences.general", "preferences.project", "c9", "commands", "watcher", "fs", - "tree.favorites", "preferences", "util" + "tree.favorites", "util", "hub" ]; main.provides = ["configure"]; return main; @@ -16,13 +16,13 @@ define(function(require, exports, module) { var menus = imports.menus; var watcher = imports.watcher; var tabManager = imports.tabManager; - var preferences = imports.preferences; var ui = imports.ui; var c9 = imports.c9; var fs = imports.fs; var kbprefs = imports["preferences.keybindings"]; var genprefs = imports["preferences.general"]; var prjprefs = imports["preferences.project"]; + var services = imports.hub.app.services; var showError = imports["dialog.error"].show; var favs = imports["tree.favorites"]; var util = imports.util; @@ -35,7 +35,7 @@ define(function(require, exports, module) { // var emit = plugin.getEmitter(); var cssSession = new Plugin("Ajax.org", main.consumes); - var services, initPlugin; + var initPlugin; var pathFromFavorite = options.pathFromFavorite; @@ -292,20 +292,17 @@ define(function(require, exports, module) { } function editStylesCss() { - // preferences.hide(); var css = settings.get("user/config/styles.css") || ""; openTab("~/.c9/styles.css", css, "css"); } function editProjectSettings() { - // preferences.hide(); var value = JSON.stringify(settings.model.project, 0, " ") .replace(/"true"/g, "true") .replace(/"false"/g, "false"); openTab(settings.paths.project, value, "javascript"); } function editUserSettings() { - // preferences.hide(); var value = JSON.stringify(settings.model.user, 0, " ") .replace(/"true"/g, "true") .replace(/"false"/g, "false"); @@ -353,9 +350,6 @@ define(function(require, exports, module) { * **/ plugin.freezePublicAPI({ - get services() { return services; }, - set services(value) { services = value; }, - /** * */ diff --git a/plugins/c9.vfs.standalone/views/standalone.html.ejs b/plugins/c9.vfs.standalone/views/standalone.html.ejs index 1468c436..35ea0721 100644 --- a/plugins/c9.vfs.standalone/views/standalone.html.ejs +++ b/plugins/c9.vfs.standalone/views/standalone.html.ejs @@ -64,8 +64,6 @@ } }); - app.lut = {}; - app.on("error", function(err){ console.error(err.stack); if (!errored) @@ -73,20 +71,11 @@ }); app.on("service", function(name, plugin, options){ - if (name == "plugin.loader" || name == "plugin.installer" - || name == "plugin.debug" || name == "plugin.manager" - || name == "plugin.test") - plugin.architect = app; if (!plugin.name) plugin.name = name; - if (options) - app.lut[(options.packagePath || "").replace(/^.*\/home\/.c9\//, "")] = options; }); - app.on("ready", function(){ - if (app.services.configure) - app.services.configure.services = app.services; - + app.once("ready", function() { window.app = app.services; window.app.__defineGetter__("_ace", function(){ return this.tabManager.focussedTab.editor.ace; diff --git a/plugins/c9.vfs.standalone/www/ide.html b/plugins/c9.vfs.standalone/www/ide.html index c5479419..e20a98f1 100644 --- a/plugins/c9.vfs.standalone/www/ide.html +++ b/plugins/c9.vfs.standalone/www/ide.html @@ -124,29 +124,18 @@ } }); - app.lut = {}; - app.on("error", function(err){ console.error(err.stack); if (!errored) alert(err); }); - app.on("service", function(name, plugin, options){ - if (name == "plugin.loader" || name == "plugin.installer" - || name == "plugin.debug" || name == "plugin.manager" - || name == "plugin.test") - plugin.architect = app; + app.on("service", function(name, plugin, options) { if (!plugin.name) plugin.name = name; - if (options) - app.lut[(options.packagePath || "").replace(/^.*\/home\/.c9\//, "")] = options; }); app.on("ready", function() { - if (app.services.configure) - app.services.configure.services = app.services; - window.app = app.services; window.app.__defineGetter__("_ace", function(){ return this.tabManager.focussedTab.editor.ace; From 7c18a7e59879f3b98cb3d7300d229cbdcb345a74 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 13 Feb 2017 00:04:05 +0400 Subject: [PATCH 04/15] simplify pluginManager and cleanup --- configs/ide/default.js | 4 +- configs/standalone.js | 5 +- plugins/c9.core/ext.js | 15 +- plugins/c9.ide.editors/tab.js | 2 +- plugins/c9.ide.find.infiles/findinfiles.js | 3 +- plugins/c9.ide.plugins/manager.js | 1326 ++++++++++------- plugins/c9.ide.plugins/managerdp.js | 26 - plugins/c9.ide.plugins/style.css | 7 + .../views/standalone.html.ejs | 2 +- 9 files changed, 830 insertions(+), 560 deletions(-) diff --git a/configs/ide/default.js b/configs/ide/default.js index aeb60a7c..15af4529 100644 --- a/configs/ide/default.js +++ b/configs/ide/default.js @@ -19,6 +19,7 @@ module.exports = function(options) { options.workspaceDir = normalize(options.workspaceDir); options.installPath = normalize(options.installPath); options.home = normalize(options.home); + options.sourceDir = options.sourceDir && normalize(options.sourceDir); var workspaceDir = options.workspaceDir; var debug = options.debug !== undefined ? options.debug : false; @@ -63,7 +64,8 @@ module.exports = function(options) { projectName: options.projectName || "Project", configName: options.configName, standalone: options.standalone, - dashboardUrl: options.dashboardUrl + dashboardUrl: options.dashboardUrl, + sourceDir: options.sourceDir, }, { packagePath: "plugins/c9.core/settings", diff --git a/configs/standalone.js b/configs/standalone.js index 2643e04b..c743906c 100644 --- a/configs/standalone.js +++ b/configs/standalone.js @@ -78,6 +78,9 @@ module.exports = function(config, optimist) { if (argv.hosted) config.client_config = "default-hosted"; + if (!argv.hosted) + config.sourceDir = path.dirname(__dirname); + config.workspaceDir = baseProc; config.settingDir = argv["setting-path"]; config.projectName = path.basename(baseProc); @@ -88,7 +91,7 @@ module.exports = function(config, optimist) { config.startBridge = startBridge; if (testing && argv.k) - require("child_process").exec("tmux -L cloud91.9 kill-server", function(){}); + require("child_process").exec("tmux -L cloud91.9 kill-server", function() {}); var isLocalhost = host == "localhost" || host == "127.0.0.1"; if (!/:/.test(argv.auth) && !isLocalhost) { diff --git a/plugins/c9.core/ext.js b/plugins/c9.core/ext.js index d1ec89ae..9d192960 100644 --- a/plugins/c9.core/ext.js +++ b/plugins/c9.core/ext.js @@ -80,17 +80,8 @@ define(function(require, exports, module) { if (!plugin.registered) return; - if (!ignoreDeps && getDependencies(plugin.name).length) { - //@todo this should be moved to whoever is calling this. - // if (!silent) - // util.alert( - // "Could not disable extension", - // "Extension is still in use", - // "This extension cannot be disabled, because it is still in use by the following plugins:

" - // + " - " + usedBy.join("
- ") - // + "

Please disable those plugins first."); + if (!ignoreDeps && getDependents(plugin.name).length) return false; - } if (!keep) plugins.splice(plugins.indexOf(plugin), 1); @@ -108,7 +99,7 @@ define(function(require, exports, module) { emit("unregister", { plugin: plugin }); } - function getDependencies(pluginName) { + function getDependents(pluginName) { var usedBy = []; // Check for dependencies needing this plugin @@ -284,7 +275,7 @@ define(function(require, exports, module) { /** * */ - getDependencies: getDependencies, + getDependents: getDependents, /** * diff --git a/plugins/c9.ide.editors/tab.js b/plugins/c9.ide.editors/tab.js index afed56c3..732b65e5 100644 --- a/plugins/c9.ide.editors/tab.js +++ b/plugins/c9.ide.editors/tab.js @@ -1,5 +1,5 @@ define(function(require, module, exports) { - main.consumes = ["Plugin", "ui", "Document", "dialog.alert", "settings"]; + main.consumes = ["Plugin", "ui", "Document", "dialog.alert"]; main.provides = ["Tab"]; return main; diff --git a/plugins/c9.ide.find.infiles/findinfiles.js b/plugins/c9.ide.find.infiles/findinfiles.js index 4ddebe1c..cb1e867b 100644 --- a/plugins/c9.ide.find.infiles/findinfiles.js +++ b/plugins/c9.ide.find.infiles/findinfiles.js @@ -1,6 +1,6 @@ define(function(require, exports, module) { main.consumes = [ - "Plugin", "c9", "util", "settings", "ui", "layout", "findreplace", + "Plugin", "c9", "util", "settings", "ui", "layout", "find", "anims", "menus", "tabManager", "commands", "tooltip", "tree", "apf", "console", "preferences", "dialog.question", "tree.favorites", "save" @@ -27,6 +27,7 @@ define(function(require, exports, module) { var find = imports.find; var save = imports.save; var question = imports["dialog.question"].show; + var apf = imports.apf; var markup = require("text!./findinfiles.xml"); var lib = require("plugins/c9.ide.find.replace/libsearch"); diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index e96def6a..e6ce8cd7 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -1,67 +1,39 @@ /*global requirejs*/ define(function(require, exports, module) { main.consumes = [ - "PreferencePanel", "settings", "ui", "util", "Form", "ext", "c9", "apf", + "PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin", "dialog.alert", "dialog.confirm", "layout", "proc", "menus", "commands", - "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", "plugin.debug", - "preferences.experimental" + "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", + "preferences.experimental", "apf", "hub", "dialog.notification" ]; - main.provides = ["plugin.manager", "pluginManager"]; + main.provides = ["pluginManager", "plugin.manager", "plugin.debug"]; return main; - /* - - Show Packages to be updated - - Open Plugin Store - - Open Cloud9 in Debug Mode - - List all installed packages - - Filter - - Core packages - - Name - - Version - - Description - - Load Time - - Plugin profile - - Pre-installed - - * - - Custom packages - - * - - - Actions: - - Uninstall - - Report Issue - - Open README - - Open in Cloud9 - - DataProvider.variableHeightRowMixin.call(this) in datagrid constructor - and set node.height - - harutyun [1:11 PM] - or add a custom getItemHeight function like https://github.com/c9/newclient/blob/master/node_modules/ace_tree/demo/demo.js#L63 (edited) - */ function main(options, imports, register) { var PreferencePanel = imports.PreferencePanel; var settings = imports.settings; var layout = imports.layout; + var commands = imports.commands; + var menus = imports.menus; var ui = imports.ui; var c9 = imports.c9; - var menus = imports.menus; var fs = imports.fs; - var commands = imports.commands; var ext = imports.ext; var tree = imports.tree; var proc = imports.proc; + var Plugin = imports.Plugin; var util = imports.util; var qs = require("querystring"); var apf = imports.apf; - var vfs = imports.vfs; var alert = imports["dialog.alert"].show; var confirm = imports["dialog.confirm"].show; var showError = imports["dialog.error"].show; var showInfo = imports["dialog.info"].show; + var notify = imports["dialog.notification"].show; var favs = imports["tree.favorites"]; - var pluginDebug = imports["plugin.debug"]; var experimental = imports["preferences.experimental"]; + var architectApp = imports.hub.app; var search = require("../c9.ide.navigate/search"); var Tree = require("ace_tree/tree"); @@ -69,27 +41,12 @@ define(function(require, exports, module) { var join = require("path").join; var basename = require("path").basename; var dirname = require("path").dirname; - var async = require("async"); + + var escapeHTML = require("ace/lib/lang").escapeHTML; var staticPrefix = options.staticPrefix; - var architect; - var CORE = { - "c9.core": 1, "c9.fs": 1, "c9.ide.preferences": 1, "c9.ide.panels": 1, - "c9.ide.plugins": 1, "c9.ide.login": 1, "c9.vfs.client": 1, - "c9.ide.console": 1, "c9.ide.editors": 1, "c9.ide.dialog.common": 1, - "c9.ide.dialog.file": 1, "c9.ide.dialog.login": 1, "c9.ide.errorhandler": 1, - "c9.ide.help": 1, "c9.ide.keys": 1, "c9.ide.restore": 1, "c9.ide.watcher": 1, - "c9.ide.tree": 1, "c9.ide.info": 1, - "c9.ide.layout.classic": 1, "c9.ide.terminal": 1, "c9.ide.ace": 1, - "c9.ide.clipboard": 1, "c9.nodeapi": 1 - }; - var GROUPS = { - "custom": "Installed Plugins", - "pre": "Pre-installed plugins", - "core": "Core Plugins", - "runtime": "Plugins created runtime" - }; + var CORE = {}; var TEMPLATES = { "plugin.simple": "Empty Plugin", "plugin.default": "Full Plugin", @@ -97,19 +54,22 @@ define(function(require, exports, module) { "plugin.bundle": "Cloud9 Bundle" }; - // @TODO add sorting + var STATE_ENABLED = 1; + var STATE_PARTIAL = 2; + var STATE_MISSING_DEPS = 3; + var STATE_DISABLED = 4; /***** Initialization *****/ - var ENABLED = c9.location.indexOf("debug=2") > -1 - || experimental.addExperiment( - "plugin-manager", - options.devel, - "SDK/Plugin Manager" - ); + var DEBUG = c9.location.indexOf("debug=2") > -1; + var ENABLED = DEBUG || experimental.addExperiment( + "plugin-manager", + options.devel, + "SDK/Plugin Manager" + ); var plugin = new PreferencePanel("Ajax.org", main.consumes, { - caption: "Plugin Manager", + caption: "Plugin Explorer", className: "plugins", form: false, noscroll: true, @@ -119,28 +79,57 @@ define(function(require, exports, module) { // var emit = plugin.getEmitter(); var model, datagrid, filterbox; - var btnUninstall, btnReport, btnReadme, btnCloud9, btnReload; + var btnUninstall, btnServices, btnReadme, btnReload; + var btnReloadLast; + var localPlugins; + var api; var loaded = false; function load() { if (loaded) return false; loaded = true; - - if (!ENABLED) return; - - // @TODO enable/disable plugins -> move to ext - - // settings.on("read", function(e) { - // updateCommandsFromSettings(); - // }, plugin); - - // commands.on("update", function(){ - // changed = true; - // updateCommandsFromSettings(); - // }, plugin); - if (options.devel) { + menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); + menus.addItemByPath("Tools/Developer", null, 100100, plugin); + + if (!ENABLED) { + menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ + onclick: function() { + var url = location.href + (location.href.indexOf("?") > -1 + ? "&debug=2" + : "?debug=2"); + util.openNewWindow(url); + } + }), 900, plugin); + return; + } + + commands.addCommand({ + name: "openPluginManager", + group: "Plugins", + exec: function() { + commands.exec("openpreferences", null, { panel: plugin }); + } + }, plugin); + + menus.addItemByPath("Tools/Developer/Open Plugin Manager", new ui.item({ + command: "openPluginManager" + }), 1100, plugin); + + if (DEBUG) { + notify("
You are in Debug Mode. " + + "" + + "", + false); + + document.getElementById(".pm_btn1").onclick = function() { commands.exec("openPluginManager") }; + btnReloadLast = document.getElementById(".pm_btn2"); + btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") }; + updateReloadLastButton(); + + readAvailablePlugins(); + commands.addCommand({ name: "reloadLastPlugin", bindKey: { mac: "F4", win: "F4" }, @@ -148,29 +137,31 @@ define(function(require, exports, module) { exec: function() { var name = getLastReloaded(); if (!name) - return commands.exec("reloadPlugin", null, { panel: plugin }); + return commands.exec("openPluginManager", null, { panel: plugin }); reload(name); } }, plugin); - commands.addCommand({ - name: "reloadPlugin", - group: "Plugins", - exec: function() { - commands.exec("openpreferences", null, { panel: plugin }); - } - }, plugin); - menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); - menus.addItemByPath("Tools/Developer", null, 100100, plugin); - - menus.addItemByPath("Tools/Developer/Reload Built-in Plugin...", new ui.item({ - command: "reloadPlugin" - }), 1100, plugin); + menus.addItemByPath("Tools/Developer/Reload All Custom Plugins", new ui.item({ + command: "reloadAllCustomPlugins" + }), 1200, plugin); menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({ command: "reloadLastPlugin", isAvailable: getLastReloaded - }), 1200, plugin); + }), 1300, plugin); + + commands.addCommand({ + name: "reloadAllCustomPlugins", + group: "Plugins", + bindKey: { + mac: "Command-Enter", + win: "Ctrl-Enter" + }, + exec: function() { + reloadAllCustomPlugins(); + } + }, plugin); } menus.addItemByPath("File/New Plugin", null, 210, plugin); @@ -191,6 +182,8 @@ define(function(require, exports, module) { function draw(e) { if (drawn) return; drawn = true; + + ui.insertCss(require("text!./style.css"), plugin); model = new TreeData(); model.emptyMessage = "No plugins found"; @@ -198,21 +191,11 @@ define(function(require, exports, module) { model.columns = [{ caption: "Name", value: "name", - // getText: function(p){ + // getText: function(p) { // return p.name + " (" + p.items.length + ")"; // }, width: "250", type: "tree" - }, { - caption: "Version", - // value: "version", - getText: function(p) { - return p.version || - (p.isPackage - ? p.items.length && p.items[0].version || "" - : ""); - }, - width: "100" }, { caption: "Startup Time", // value: "time", @@ -222,35 +205,21 @@ define(function(require, exports, module) { return (p.time || 0) + "ms"; var total = 0; - if (p.isPackage || p.name == "runtime") { - p.items.forEach(function(item) { total += item.time || 0; }); - } - else { - p.items.forEach(function(p) { - p.items && p.items.forEach(function(item) { total += item.time || 0; }); - }); + function recur(p) { + if (p.time != undefined) + return total += p.time; + if (p.items) + p.items.forEach(recur); } + recur(p); return (p.time = total) + "ms"; } }, { caption: "Enabled", value: "enabled", width: "100" - // }, { - // caption: "Package", - // value: "package", // @todo make a link - // width: "100%" - }, { - caption: "Developer", - // value: "developer", - getText: function(p) { - return p.developer || - (p.isPackage - ? p.items.length && p.items[0].developer || "" - : ""); - }, - width: "150" }]; + model.columns = null; layout.on("eachTheme", function(e) { var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24; @@ -260,26 +229,11 @@ define(function(require, exports, module) { if (e.changed) datagrid.resize(true); }); + architectApp.on("ready-additional", function() { + reloadModel(); + }); reloadModel(); - // type: "custom", - // title: "Introduction", - // position: 1, - // node: intro = new ui.bar({ - // height: 149, - // "class" : "intro", - // style: "padding:12px;position:relative;" - // }) - - // intro.$int.innerHTML = - // '

Keybindings

Change these settings to configure ' - // + 'how Cloud9 responds to your keyboard commands.

' - // + '

You can also manually edit your keymap file.

' - // + '

Hint: Double click on the keystroke cell in the table below to change the keybinding.

'; - - // intro.$int.querySelector("a").onclick = function(){ editUserKeys(); }; - var hbox = new ui.hbox({ htmlNode: e.html, padding: 5, @@ -294,37 +248,39 @@ define(function(require, exports, module) { height: 27, width: 250, singleline: true, + clearbutton: true, "initial-message": "Search installed plugins" }), + btnReadme = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Readme", + class: "serviceButton", + onclick: function() { + mode = "readme"; + window.requestAnimationFrame(renderDetails); + } + }), + btnServices = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "services", + class: "serviceButton", + onclick: function() { + mode = "services"; + window.requestAnimationFrame(renderDetails); + } + }), new ui.filler({}), btnUninstall = new ui.button({ - skin: "btn-default-css3", - caption: "Uninstall", + skin: "c9-toolbarbutton-glossy", + caption: "Disable", class: "btn-red", onclick: function() { var item = datagrid.selection.getCursor(); - if (item.isPackage) - uninstall(item.name, function() {}); - else if (item.enabled == "true") - disable(item.name, function() {}); - else - enable(item.name, function() {}); + unloadPackage({ path: item.path }); } }), - btnReport = new ui.button({ - skin: "btn-default-css3", - caption: "Report Issue" - }), - btnReadme = new ui.button({ - skin: "btn-default-css3", - caption: "Open README" - }), - btnCloud9 = new ui.button({ - skin: "btn-default-css3", - caption: "Open in Cloud9" - }), btnReload = new ui.button({ - skin: "btn-default-css3", + skin: "c9-toolbarbutton-glossy", caption: "Reload", onclick: function() { var item = datagrid.selection.getCursor(); @@ -334,19 +290,36 @@ define(function(require, exports, module) { }) ] }); + var treeBar; + var descriptionBar; + var hboxInner = new ui.hsplitbox({ + anchor: "0 0 0 0", + htmlNode: e.html, + class: "bar-preferences", + splitter: true, + childNodes: [ + treeBar = new ui.bar({ width: 250 }), + descriptionBar = new ui.bar({ textselect: true }), + ] + }); - var div = e.html.appendChild(document.createElement("div")); + var div = treeBar.$ext.appendChild(document.createElement("div")); div.style.position = "absolute"; - div.style.left = "10px"; - div.style.right = "10px"; - div.style.bottom = "10px"; - div.style.top = "50px"; + div.style.left = "0px"; + div.style.right = "0px"; + div.style.bottom = "0px"; + div.style.top = "0px"; + hboxInner.$ext.style.position = "absolute"; + hboxInner.$ext.style.left = "10px"; + hboxInner.$ext.style.right = "10px"; + hboxInner.$ext.style.bottom = "10px"; + hboxInner.$ext.style.top = "50px"; datagrid = new Tree(div); datagrid.setTheme({ cssClass: "blackdg" }); datagrid.setDataProvider(model); - layout.on("resize", function() { datagrid.resize(); }, plugin); + layout.on("resize", function() { datagrid.resize() }, plugin); function setTheme(e) { filterbox.setAttribute("class", @@ -376,106 +349,354 @@ define(function(require, exports, module) { }); datagrid.on("changeSelection", function(e) { - var item = datagrid.selection.getCursor(); - - if (item.isGroup) { - btnUninstall.disable(); - btnReport.disable(); - btnReadme.disable(); - btnCloud9.disable(); - btnReload.disable(); + window.requestAnimationFrame(renderDetails); + }); + model.on("change", function(e) { + window.requestAnimationFrame(renderDetails); + }); + + model.getCheckboxHTML = function(node) { + var enabled = node.enabled; + if (enabled == null || node.isGroup) return ""; + return ""; + }; + + var mode = "services"; + var readmeCache = {}; + function renderDetails() { + var items = datagrid.selection.getSelectedNodes(); + var enabled = items.every(function(x) { + return x.enabled == 1; + }); + if (enabled) { + btnReload.enable(); + btnUninstall.setCaption("Disable"); } else { - if (item.isPackage) { - btnUninstall.setCaption("Uninstall"); - btnUninstall.setAttribute("class", "btn-red"); - } - else { - btnUninstall.setCaption(item.enabled == "true" ? "Disable" : "Enable"); - btnUninstall.setAttribute("class", item.enabled == "true" ? "btn-red" : "btn-green"); - } - - if (CORE[item.name] || item.parent.parent && item.parent.parent.isType == "core") { - btnUninstall.disable(); - } else { - btnUninstall.enable(); - } - - if (item.isPackage || CORE[item.name] || item.parent.parent && item.parent.parent.isType == "core") { - btnReload.disable(); - } else { - btnReload.enable(); - } - - btnReport.enable(); - btnReadme.enable(); - btnCloud9.enable(); + btnReload.disable(); + btnUninstall.setCaption("Enable"); } + if (mode == "services") { + renderServiceDetails(items); + btnServices.$ext.classList.add("serviceButtonActive"); + btnReadme.$ext.classList.remove("serviceButtonActive"); + } + else if (mode == "readme") { + renderReadmeDetails(items); + btnReadme.$ext.classList.add("serviceButtonActive"); + btnServices.$ext.classList.remove("serviceButtonActive"); + } + else { + descriptionBar.$ext.innerHTML = ""; + btnReadme.$ext.classList.remove("serviceButtonActive"); + btnServices.$ext.classList.remove("serviceButtonActive"); + } + } + + function renderReadmeDetails(items) { + if (items.length > 1) { + descriptionBar.$ext.textContent = "Multipe items selected."; + return; + } + var item = items[0]; + while (item && !item.readme) { + item = item.parent; + } + if (!item) { + descriptionBar.$ext.textContent = "Readme is not available."; + return; + } + + if (readmeCache[item.readme]) { + descriptionBar.$ext.textContent = readmeCache[item.readme]; + return; + } + descriptionBar.$ext.textContent = "Loading..."; + // fs.readFile() + } + + function renderServiceDetails(items) { + function addUnique(item, array) { + if (array.indexOf(item) == -1) array.push(item); + } + + var provided = []; + + items.forEach(function addProvides(x) { + if (x.map) { + Object.keys(x.map).forEach(function(n) { + addProvides(x.map[n]); + }); + } + if (x.provides) { + x.provides.forEach(function(p) { + addUnique(p, provided); + }); + } + }); + + var consumedMap = Object.create(null); + provided.forEach(function(service) { + consumedMap[service] = 0; + }); + addAllProviders(consumedMap); + var consumedList = Object.keys(consumedMap); + var consumedGroups = splitToGroups(consumedList, consumedMap); + + var dependents = Object.create(null); + provided.forEach(function(service) { + dependents[service] = 0; + }); + addAllDependents(dependents); + var depList = Object.keys(dependents); + var depGroups = splitToGroups(depList, dependents); + + function splitToGroups(list, map) { + var groups = []; + list.forEach(function(n) { + var level = Math.abs(map[n]); + if (!groups[level]) + groups[level] = []; + groups[level].push(n); + }); + groups = groups.filter(Boolean).slice(1); + return groups; + } + + function formatServices(list, style) { + return "[" + list.map(function(x) { + return '' + escapeHTML(x) + ''; + }).join(", ") + "]"; + } + + descriptionBar.$ext.style.overflow = "auto"; + descriptionBar.$ext.innerHTML = '
\ +

' + (items.length == 1 + ? '' + escapeHTML(items[0].path) + '' + : (items.length || "no") + " plugins selected") + '

\ +
\ +

Provided services [' + provided.length + ']

\ +

' + (provided.length ? formatServices(provided) : "") + '

\ +

Consumed services [' + (consumedList.length - provided.length) + ']

\ +

' + consumedGroups.map(formatServices).join("
") + '

\ +

Dependent services [' + (depList.length - provided.length) + ']

\ +

' + depGroups.map(formatServices).join("
") + '

\ +
\ +
'; + + } + descriptionBar.$ext.addEventListener("click", function(e) { + if (e.button) return; + var el = e.target; + var isButton = el.classList.contains("serviceButton"); + var text = el.textContent; + var serviceToPlugin = architectApp.serviceToPlugin; + if (e.detail == 1 && isButton && text) { + var path = serviceToPlugin[text] ? serviceToPlugin[text].packagePath : text; + var node = function search(node) { + if (node.path == path) + return node; + if (node.items) { + for (var i = 0; i < node.items.length; i++) { + var result = search(node.items[i]); + if (result) + return result; + } + } + }(model.cachedRoot); + + if (node) + datagrid.reveal(node); + } + }); + var hoverDetails = { hovered: "", highlighted: "" }; + descriptionBar.$ext.addEventListener("mousemove", function(e) { + if (e.button) return; + var el = e.target; + var isButton = el.classList.contains("serviceButton"); + var text = el.textContent; + hoverDetails.parent = el.parentNode; + hoverDetails.hovered = isButton ? text : ""; + hoverDetails.type = isButton ? el.parentNode.getAttribute("type") : ""; + if (hoverDetails.hovered != hoverDetails.highlighted) + requestAnimationFrame(updateHighlights); + }); + function updateHighlights() { + if (hoverDetails.hovered == hoverDetails.highlighted) return; + var serviceToPlugin = architectApp.serviceToPlugin; + if (!serviceToPlugin) return; + + var nodes = descriptionBar.$ext.querySelectorAll(".serviceButton"); + + var deps = Object.create(null); + deps[hoverDetails.hovered] = 0; + addAllDependents(deps); + addAllProviders(deps); + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var val = node.textContent; + var highlight1 = false; + var highlight2 = false; + if (hoverDetails.type == "dependent") { + if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { + highlight1 = deps[val] < 0; + highlight2 = deps[val] > 0; + } + } else if (hoverDetails.type == "consumed") { + if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { + highlight1 = deps[val] > 0; + highlight2 = deps[val] < 0; + } + } + + nodes[i].style.borderBottom = highlight1 ? "1px solid" : ""; + if (!highlight1) { + nodes[i].style.borderBottom = highlight2 ? "1px dashed rgba(125, 125, 125, 0.9)" : ""; + } + } + hoverDetails.highlighted = hoverDetails.hovered; + } + window.requestAnimationFrame(renderDetails); + } + + + /***** Methods *****/ + + function readAvailablePlugins(callback) { + fs.readdir("~/.c9/plugins", function(err, list) { + if (err) return callback && callback(err); + + var available = []; + list.forEach(function(stat) { + var name = stat.name; + if (!/(directory|folder)$/.test(stat.mime)) return; + if (!/[._]/.test(name[0])) available.push(name); + }); + localPlugins = available.concat(); + reloadModel(); + callback && callback(null, available); }); } - /***** Methods *****/ - function reloadModel() { if (!model) return; - - var groups = {}; - var packages = {}; - var root = []; - - ["custom", "pre", "core", "runtime"].forEach(function(name) { - root.push(groups[name] = { - items: [], - isOpen: name != "runtime", - className: "group", - isGroup: true, - isType: name, - noSelect: true, - name: GROUPS[name] + + if (!CORE.pluginManager) { + CORE.pluginManager = 1; + addAllProviders(CORE); + Object.keys(CORE).forEach(function(n) { + if (architectApp.serviceToPlugin[n]) + CORE[architectApp.serviceToPlugin[n].packagePath] = 1; }); - }); + } - var lut = ext.named; - - ext.plugins.forEach(function(plugin) { - var info = architect.pluginToPackage[plugin.name]; - var packageName = info && info.package || "runtime"; - - var groupName; - if (CORE[packageName]) groupName = "core"; - else if (info && info.isAdditionalMode) groupName = "custom"; - else groupName = "pre"; - - var package; - if (packageName == "runtime") { - package = groups.runtime; + var GROUPS = { + "changed": "Recently Changed", + "remote": "Remote Plugins", + "vfs": "Locally Installed Plugins", + "pre": "Pre-installed Plugins", + "core": "Core Plugins", + }; + + var groups = model.groups || Object.create(null); + if (!model.groups) { + var root = []; + model.cachedRoot = { items: root }; + model.groups = groups; + Object.keys(GROUPS).forEach(function(name) { + root.push(groups[name] = { + map: Object.create(null), + isOpen: name != "runtime", + className: "group", + isGroup: true, + isType: name, + noSelect: true, + name: GROUPS[name] + }); + }); + } + + architectApp.config.forEach(function(plugin) { + if (plugin.packagePath) { + var parts = plugin.packagePath.split("/"); + + var path = parts.shift(); + var node = CORE[plugin.packagePath] ? groups.core : groups.pre; + parts.forEach(function(p, i) { + path = path + "/" + p; + if (!node.map) + node.map = Object.create(null); + if (!node.map[p]) { + node.map[p] = { + path: path, + parent: node, + }; + } + node.map[p].name = p; + if (i == parts.length - 1) { + var enabled = 1; + node.map[p].provides = plugin.provides; + node.map[p].time = plugin.provides.reduce(function(sum, x) { + var service = architectApp.services[x]; + if (!service || (!service.loaded && service.unload)) { + enabled = 0; + } + return sum + (service && service.time || 0); + }, 0); + node.map[p].enabled = enabled; + } + node = node.map[p]; + }); } - else { - package = packages[packageName]; - if (!package) - groups[groupName].items.push(package = packages[packageName] = { - items: [], - isPackage: true, - className: "package", - parent: groups[groupName], - name: packageName + }); + + if (localPlugins) { + groups.pre.map = groups.pre.map || Object.create(null); + localPlugins.forEach(function(x) { + groups.vfs.map[x] = { + name: x, + enabled: 0 + }; + }); + } + + function flatten(node, index) { + if (node.map) { + node.items = node.children = Object.keys(node.map).map(function(x) { + return node.map[x]; + }); + } + if (node.items) { + node.items.forEach(flatten); + if (node.items.length == 1) { + var other = node.items[0]; + if (node.parent && node.parent.items[index] == node) { + node.parent.items[index] = other; + other.name = node.name + "/" + other.name; + } + } + if (!node.isGroup) { + node.items.some(function(i) { + if (node.enabled == null) { + node.enabled = i.enabled; + } + if (i.enabled != node.enabled) { + node.enabled = -1; + return true; + } }); + } } + } + + flatten(model.cachedRoot); - package.items.push({ - name: plugin.name, - enabled: lut[plugin.name].loaded ? "true" : "false", - time: plugin.time, - version: info && info.version || "N/A", - parent: package, - package: packageName, - developer: plugin.developer == "Ajax.org" - ? "Cloud9" - : plugin.developer - }); - }); - - model.cachedRoot = { items: root }; applyFilter(); } @@ -486,7 +707,7 @@ define(function(require, exports, module) { model.reKeyword = null; model.setRoot(model.cachedRoot); - // model.isOpen = function(node){ return node.isOpen; } + // model.isOpen = function(node) { return node.isOpen; } } else { model.reKeyword = new RegExp("(" @@ -494,106 +715,7 @@ define(function(require, exports, module) { var root = search.treeSearch(model.cachedRoot.items, model.keyword, true); model.setRoot(root); - // model.isOpen = function(node){ return true; }; - } - } - - function uninstall(name) { - btnUninstall.setAttribute("caption", "..."); - btnUninstall.disable(); - - // @TODO first disable the plugin - - proc.spawn("c9", { args: ["uninstall", name]}, function(err, p) { - p.stdout.on("data", function(c) { - - }); - p.stderr.on("data", function(c) { - - }); - p.on("exit", function(code) { - if (code) { - return alert("Could not uninstall plugin", - "Could not uninstall plugin", - "Could not uninstall plugin"); - } - - btnUninstall.setAttribute("caption", "Uninstall"); - btnUninstall.enable(); - }); - }); - } - - function enable(name) { - try { ext.enablePlugin(name); } - catch (e) { - alert("Could not disable plugin", - "Got an error when disabling plugin: " + name, - e.message); - return false; - } - - reloadModel(); - } - - function disable(name, callback) { - var deps = ext.getDependencies(name); - var plugins = ext.named; - - for (var i = 0; i < deps.length; i++) { - ext.getDependencies(deps[i]).forEach(function(name) { - if (deps.indexOf(name) == -1) - deps.push(name); - }); - } - - if (deps.length) { - confirm("Found " + deps.length + " plugins that depend on this plugin.", - "Would you like to disable all the plugins that depend on '" + name + "'?", - "These plugins would also be disabled: " + deps.join(", "), - // Yes - function() { - if (deps.reverse().every(function(name) { - console.log("Disabling", name); - return !recurDisable(name); - })) { - disable(name); - reloadModel(); - } - }, - // No - function() { - callback(new Error("User Cancelled")); - }); - } - else { - var e = disable(name); - if (!e) reloadModel(); - callback(e); - } - - function recurDisable(name) { - var deps = ext.getDependencies(name); - - if (deps.length) { - if (!deps.every(function(name) { - return !recurDisable(name); - })) return false; - } - - return disable(name); - } - - function disable(name) { - if (!plugins[name].loaded) return; - - try { ext.disablePlugin(name); } - catch (e) { - alert("Could not disable plugin", - "Got an error when disabling plugin: " + name, - e.message); - return e; - } + // model.isOpen = function(node) { return true; }; } } @@ -674,13 +796,120 @@ define(function(require, exports, module) { href += (href.match(/\?/) ? "&" : "?") + "reload=" + name; window.history.replaceState(window.history.state, null, href); - for (var plugin in architect.lut) { - if (architect.lut[plugin].provides.indexOf(name) < 0) + for (var plugin in architectApp.lut) { + if (architectApp.lut[plugin].provides.indexOf(name) < 0) continue; - pluginDebug.reloadPackage(plugin); + reloadPackage(plugin); return; } + updateReloadLastButton(); + } + + function updateReloadLastButton() { + var last = getLastReloaded(); + if (last) { + btnReloadLast.visible = true; + btnReloadLast.textContent = "Reload " + last; + } else { + btnReloadLast.visible = false; + } + } + + function loadVFSExtension(callback) { + if (api) + return callback(null, api); + ext.loadRemotePlugin("pluginLoader", { + code: c9.standalone ? undefined : require("text!./vfs.package.reader.js"), + file: c9.standalone ? "c9.ide.plugins/vfs.package.reader.js" : undefined, + redefine: true + }, function(err, remote) { + if (err) + return callback(err); + + api = remote; + return callback(null, api); + }); + } + + function reloadPackage(path) { + var unloaded = []; + + function recurUnload(name) { + var plugin = architectApp.services[name]; + unloaded.push(name); + + // Find all the dependencies + var deps = ext.getDependents(plugin.name); + + // Unload all the dependencies (and their deps) + deps.forEach(function(name) { + recurUnload(name); + }); + + // Unload plugin + plugin.unload(); + } + + // Recursively unload plugin + var p = architectApp.lut[path]; + if (p.provides) { // Plugin might not been initialized all the way + p.provides.forEach(function(name) { + recurUnload(name); + }); + } + + // create reverse lookup table + var rlut = {}; + for (var packagePath in architectApp.lut) { + var provides = architectApp.lut[packagePath].provides; + if (provides) { // Plugin might not been initialized all the way + provides.forEach(function(name) { + rlut[name] = packagePath; + }); + } + } + + // Build config of unloaded plugins + var config = [], done = {}; + unloaded.forEach(function(name) { + var packagePath = rlut[name]; + + // Make sure we include each plugin only once + if (done[packagePath]) return; + done[packagePath] = true; + + var options = architectApp.lut[packagePath]; + delete options.provides; + delete options.consumes; + delete options.setup; + + config.push(options); + + // Clear require cache + requirejs.undef(options.packagePath); // global + }); + + // Load all plugins again + architectApp.loadAdditionalPlugins(config, function(err) { + if (err) console.error(err); + }); + } + + function reloadAllCustomPlugins() { + var list = []; + Object.keys(architectApp.serviceToPlugin).forEach(function(name) { + if (architectApp.serviceToPlugin[name].isAdditionalMode) + list.push(architectApp.serviceToPlugin[name].path); + }); + + var path = list[list.length - 1]; + reloadPackage(path.replace(/^~\/\.c9\//, "")); + + // Avoid confusion with "Reload Last Plugin" + var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); + window.history.replaceState(window.history.state, null, href); + showInfo("Reloaded " + path + ".", 1000); } function showReloadTip(name) { @@ -700,139 +929,216 @@ define(function(require, exports, module) { return qs.parse(document.location.search.substr(1)).reload; } - var packages = {}; - function loadPackage(options, callback) { - if (Array.isArray(options)) - return async.map(options, loadPackage, callback || function() {}); - - if (typeof options == "string") { - if (/^https?:/.test(options)) { - options = { url: options }; - } else if (/^[~\/]/.test(options)) { - options = { path: options }; - } else if (/^[~\/]/.test(options)) { - options = { url: require.toUrl(options) }; - } - } - - if (!options.url && options.path) - options.url = vfs.vfsUrl(options.path); - - var parts = options.url.split("/"); - var root = parts.pop(); - options.url = parts.join("/"); - - if (!options.name) { - // try to find the name from file name - options.name = /^package\.(.*)\.js$|$/.exec(root)[1]; - // try folder name - if (!options.name || options.name == "json") - options.name = parts[parts.length - 1]; - // try parent folder name - if (/^(.?build|master|c9build)/.test(options.name)) - options.name = parts[parts.length - 2]; - // remove version from the name - options.name = options.name.replace(/@.*$/, ""); - } - if (!options.packageName) - options.packageName = root.replace(/\.js$/, ""); - - if (!options.rootDir) - options.rootDir = "plugins"; - - var name = options.name; - var id = options.rootDir + "/" + name; - var pathMappings = {}; - - pathMappings[id] = options.url; - requirejs.config({ paths: pathMappings }); - requirejs.undef(id + "/", true); - - if (/\.js$/.test(root)) { - require([options.url + "/" + root], function(json) { - json = json || require(id + "/" + options.packageName); - getPluginsFromPackage(json, callback); - }, function(err) { - addError("Error loading plugin", err); - }); - } - else if (options.path && /\.json$/.test(root)) { - fs.readFile(options.path, function(err, value) { - if (err) return addError("Error reading " + options.path, err); - try { - var json = JSON.parse(value); - } catch (e) { - return addError("Error parsing package.json", e); - } - json.fromVfs = true; - getPluginsFromPackage(json, callback); - }); - } - else if (options.url && /\.json$/.test(root)) { - require(["text!" + options.id + "/" + root], function(value) { - try { - var json = JSON.parse(value); - } catch (e) { - return addError("Error parsing package.json", e); - } - getPluginsFromPackage(json, callback); - }, function(err) { - addError("Error loading plugin", err); - }); - } - else { - callback && callback(new Error("Missing path and url")); - } - - function addError(message, err) { - if (!packages[name]) - packages[name] = {}; - packages[name].filePath = options.path; - packages[name].url = options.url; - packages[name].__error = new Error(message + "\n" + err.message); - - reloadModel(); - - callback && callback(err); - } - - function getPluginsFromPackage(json, callback) { - var plugins = []; - if (json.name != name) - name = json.name; - var unhandledPlugins = json.c9 && json.c9.plugins || json.plugins; - if (unhandledPlugins) { - Object.keys(unhandledPlugins).forEach(function(name) { - var plugin = unhandledPlugins[name]; - if (typeof plugin == "string") - plugin = { packagePath: plugin }; - if (!plugin.packagePath) - plugin.packagePath = id + "/" + name; - plugin.staticPrefix = options.url; - plugins.push(plugin); + function addAllDependents(packages) { + var config = architectApp.config; + var level = 0; + do { + level++; + var changed = false; + var packageNames = Object.keys(packages); + config.forEach(function(x) { + packageNames.forEach(function(p) { + if (x.consumes.indexOf(p) != -1) { + x.provides.forEach(function(name) { + if (packages[name] == null) { + changed = true; + packages[name] = level; + } + }); + } }); - } - - packages[json.name] = json; - json.filePath = options.path; - json.url = options.url; - - if (!json.c9) - json.c9 = {}; - - json.c9.plugins = plugins; - json.enabled = true; - json.path = id; - - loadPlugins(plugins, callback); - } + }); + } while (changed); + return packages; } - function loadPlugins(plugins, callback) { - architect.loadAdditionalPlugins(plugins, function(err) { - callback && callback && callback(err); + function addAllProviders(packages) { + var serviceToPlugin = architectApp.serviceToPlugin; + var level = 0; + do { + level--; + var changed = false; + var packageNames = Object.keys(packages); + packageNames.forEach(function(p) { + var service = serviceToPlugin[p]; + if (service && service.consumes) { + service.consumes.forEach(function(name) { + if (packages[name] == null) { + changed = true; + packages[name] = level; + } + }); + } + }); + } while (changed); + return packages; + } + + function unloadPackage(options, callback) { + var toUnload = Object.create(null); + var config = architectApp.config; + function addPath(path) { + config.forEach(function(p) { + if (!p.packagePath) return; + if (p.packagePath.startsWith(path)) { + p.provides.forEach(function(name) { + toUnload[name] = 0; + }); + } + }); + } + if (options.path) { + addPath(options.path); + } + if (options.paths) { + options.path.forEach(addPath); + } + if (options.services) { + options.services.forEach(function(name) { + toUnload[name] = 0; + }); + } + + addAllDependents(toUnload); + + var services = architectApp.services; + var serviceToPlugin = architectApp.serviceToPlugin; + Object.keys(toUnload).forEach(function(name) { + recurUnload(name); + }); + + function recurUnload(name) { + var service = services[name]; + + if (!service || !service.loaded) + return; + + // Find all the dependencies + var deps = ext.getDependents(service.name); + + // Unload all the dependencies (and their deps) + deps.forEach(function(name) { + recurUnload(name); + }); + + console.log(name); + + // Unload plugin + service.unload(); + + var pluginConfig = serviceToPlugin[name]; + if (pluginConfig && toUnload[name] == 0) + pluginConfig.__userDisabled = true; + } + + reloadModel(); + } + + function loadPackage(options, callback) { + // 1 {url, } + // 2 + var paths = {}; + paths[options.id] = options.staticPrefix; + + requirejs.config({ paths: paths }); + requirejs.undef(options.id, true); + + require([options.url], function(installed) { + var plugins = require(options.id + "/package.json.js"); + + var architectConfig = plugins.map(function(plugin) { + if (typeof plugin == "string") + plugin = { packagePath: plugin }; + return plugin; + }); + architectApp.loadAdditionalPlugins(architectConfig, function(err) { + callback && callback(err); + }); + + }, function(err) { + callback && callback(err); }); } + + function cleanupCache(packagePath) { + var options = architectApp.pathToPackage[packagePath]; + var url = require.toUrl(options.packagePath, ".js"); + if (!define.fetchedUrls[url]) return false; + + delete options.provides; + delete options.consumes; + delete options.setup; + + requirejs.undef(options.packagePath); + return true; + } + + function addStaticPlugin(type, pluginName, filename, data, plugin) { + var services = architectApp.services; + var path = "plugins/" + pluginName + "/" + + (type == "installer" ? "" : type + "/") + + filename.replace(/\.js$/, ""); + + switch (type) { + case "builders": + data = util.safeParseJson(data, function() {}); + if (!data) return; + if (!services.build.addBuilder) return; + + services.build.addBuilder(filename, data, plugin); + break; + case "keymaps": + data = util.safeParseJson(data, function() {}); + if (!data) return; + if (!services["preferences.keybindings"].addCustomKeymap) return; + + services["preferences.keybindings"].addCustomKeymap(filename, data, plugin); + break; + case "modes": + if (!services.ace) return; + var mode = {}; + var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); + firstLine.split(";").forEach(function(n) { + if (!n) return; + var info = n.split(":"); + mode[info[0].trim()] = info[1].trim(); + }); + + services.ace.defineSyntax({ + name: path, + caption: mode.caption, + extensions: (mode.extensions || "").trim() + .replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1") + }); + break; + case "outline": + if (!data) return; + if (!services.outline.addOutlinePlugin) return; + + services.outline.addOutlinePlugin(path, data, plugin); + break; + case "runners": + data = util.safeParseJson(data, function() {}); + if (!data) return; + + services.run.addRunner(data.caption || filename, data, plugin); + break; + case "snippets": + services["language.complete"].addSnippet(data, plugin); + break; + case "themes": + services.ace.addTheme(data, plugin); + break; + case "templates": + services.newresource.addFileTemplate(data, plugin); + break; + case "installer": + console.error("Installer is not supported."); + default: + console.error("Unsupported type", type); + } + } + /***** Lifecycle *****/ @@ -858,15 +1164,12 @@ define(function(require, exports, module) { loaded = false; drawn = false; - architect = null; model = null; datagrid = null; filterbox = null; btnUninstall = null; - btnReport = null; - btnReadme = null; - btnCloud9 = null; - btnReload = null; + localPlugins = null; + api = null; }); /***** Register and define API *****/ @@ -875,17 +1178,6 @@ define(function(require, exports, module) { * **/ plugin.freezePublicAPI({ - /** - * - */ - get architect() { throw new Error(); }, - set architect(v) { - architect = v; - architect.on("ready-additional", function() { - reloadModel(); - }); - }, - /** * */ @@ -899,27 +1191,27 @@ define(function(require, exports, module) { /** * */ - uninstall: uninstall, + unloadPackage: unloadPackage, + + /** + * + */ + addStaticPlugin: addStaticPlugin, /** * */ - enable: enable, - - /** - * - */ - disable: disable, - - /** - * - */ - reload: reload + reload: reload, + + loadVFSExtension: loadVFSExtension }); + var shim = new Plugin(); + register(null, { "pluginManager": plugin, - "plugin.manager": plugin + "plugin.manager": shim, + "plugin.debug": shim, }); } }); diff --git a/plugins/c9.ide.plugins/managerdp.js b/plugins/c9.ide.plugins/managerdp.js index cf695978..eb638e7d 100644 --- a/plugins/c9.ide.plugins/managerdp.js +++ b/plugins/c9.ide.plugins/managerdp.js @@ -9,32 +9,6 @@ define(function(require, exports, module) { oop.inherits(DataProvider, TreeData); (function() { - - this.getChildren = function(node) { - var children = node.items; - var ch = children && children[0] && children[0]; - if (ch) { - var d = (node.$depth + 1) || 0; - children.forEach(function(n) { - n.$depth = d; - n.parent = node; - }); - } - - if (this.$sortNodes && !node.$sorted) { - children && this.sort(children); - } - return children; - }; - - // this.getRowIndent = function(p){ - // return !p.className ? 2 : p.className == "package" ? 1 : 0; - // } - - this.hasChildren = function(node) { - return node.items && node.items.length; - }; - this.getCaptionHTML = function(node) { if (!node.name) return ""; return node.name.replace(this.reKeyword, "$1"); diff --git a/plugins/c9.ide.plugins/style.css b/plugins/c9.ide.plugins/style.css index ce014210..a439b088 100644 --- a/plugins/c9.ide.plugins/style.css +++ b/plugins/c9.ide.plugins/style.css @@ -4,3 +4,10 @@ .bk-window.dialog-updater .bk-win-footer { display: none; } +.serviceButton { + cursor: default; +} +.serviceButton:hover, .serviceButtonActive { + color: @preferences-intro-link-color; + text-decoration : underline; +} \ No newline at end of file diff --git a/plugins/c9.vfs.standalone/views/standalone.html.ejs b/plugins/c9.vfs.standalone/views/standalone.html.ejs index 35ea0721..d51634aa 100644 --- a/plugins/c9.vfs.standalone/views/standalone.html.ejs +++ b/plugins/c9.vfs.standalone/views/standalone.html.ejs @@ -14,7 +14,7 @@ return obj; } window.isLocalVersion = nRequire ? true : false; - window.require = undefined; + window.require = window.module = undefined; From 4046e3d688f627dada819f7981e1d60a5d6b426b Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 27 Feb 2017 00:21:10 +0400 Subject: [PATCH 05/15] implement loadPackage method --- plugins/c9.ide.behaviors/tabs.js | 2 +- plugins/c9.ide.plugins/manager.js | 301 ++++++++++++++--------------- plugins/c9.ide.ui/lib/menu/menu.js | 2 +- 3 files changed, 152 insertions(+), 153 deletions(-) diff --git a/plugins/c9.ide.behaviors/tabs.js b/plugins/c9.ide.behaviors/tabs.js index 056a4aaa..09fc55c0 100644 --- a/plugins/c9.ide.behaviors/tabs.js +++ b/plugins/c9.ide.behaviors/tabs.js @@ -40,7 +40,7 @@ define(function(require, exports, module) { var cycleKeyPressed, changedTabs, unchangedTabs, dirtyNextTab, dirtyNextPane; var ACTIVEPAGE = function() { return tabs.focussedTab; }; - var ACTIVEPATH = function() { var tab = tabs.focussedTab; return tab && (tab.path || tab.relatedPath || tab.editor.getPathAsync); }; + var ACTIVEPATH = function() { var tab = mnuContext.$tab || tabs.focussedTab; return tab && (tab.path || tab.relatedPath || tab.editor.getPathAsync); }; var MORETABS = function() { return tabs.getTabs().length > 1; }; var MORETABSINPANE = function() { return tabs.focussedTab && tabs.focussedTab.pane.getTabs().length > 1; }; var MOREPANES = function() { return tabs.getPanes().length > 1; }; diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index e6ce8cd7..78689371 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -2,8 +2,8 @@ define(function(require, exports, module) { main.consumes = [ "PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin", - "dialog.alert", "dialog.confirm", "layout", "proc", "menus", "commands", - "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", + "layout", "proc", "menus", "commands", + "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", "preferences.experimental", "apf", "hub", "dialog.notification" ]; main.provides = ["pluginManager", "plugin.manager", "plugin.debug"]; @@ -26,8 +26,7 @@ define(function(require, exports, module) { var util = imports.util; var qs = require("querystring"); var apf = imports.apf; - var alert = imports["dialog.alert"].show; - var confirm = imports["dialog.confirm"].show; + var vfs = imports.vfs; var showError = imports["dialog.error"].show; var showInfo = imports["dialog.info"].show; var notify = imports["dialog.notification"].show; @@ -41,6 +40,7 @@ define(function(require, exports, module) { var join = require("path").join; var basename = require("path").basename; var dirname = require("path").dirname; + var async = require("async"); var escapeHTML = require("ace/lib/lang").escapeHTML; @@ -82,7 +82,7 @@ define(function(require, exports, module) { var btnUninstall, btnServices, btnReadme, btnReload; var btnReloadLast; var localPlugins; - var api; + var disabledPlugins = Object.create(null); var loaded = false; function load() { @@ -93,7 +93,7 @@ define(function(require, exports, module) { menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); menus.addItemByPath("Tools/Developer", null, 100100, plugin); - if (!ENABLED) { + if (!DEBUG) { menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ onclick: function() { var url = location.href + (location.href.indexOf("?") > -1 @@ -102,6 +102,8 @@ define(function(require, exports, module) { util.openNewWindow(url); } }), 900, plugin); + } + if (!ENABLED) { return; } @@ -142,26 +144,10 @@ define(function(require, exports, module) { } }, plugin); - menus.addItemByPath("Tools/Developer/Reload All Custom Plugins", new ui.item({ - command: "reloadAllCustomPlugins" - }), 1200, plugin); - menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({ command: "reloadLastPlugin", isAvailable: getLastReloaded }), 1300, plugin); - - commands.addCommand({ - name: "reloadAllCustomPlugins", - group: "Plugins", - bindKey: { - mac: "Command-Enter", - win: "Ctrl-Enter" - }, - exec: function() { - reloadAllCustomPlugins(); - } - }, plugin); } menus.addItemByPath("File/New Plugin", null, 210, plugin); @@ -276,7 +262,10 @@ define(function(require, exports, module) { class: "btn-red", onclick: function() { var item = datagrid.selection.getCursor(); - unloadPackage({ path: item.path }); + if (item.enabled) + unloadPackage({ path: item.path }); + else + loadPackage({ path: item.filePath || item.path }); } }), btnReload = new ui.button({ @@ -284,7 +273,7 @@ define(function(require, exports, module) { caption: "Reload", onclick: function() { var item = datagrid.selection.getCursor(); - if (item.enabled && item.name) + if (item.name) reload(item.name); } }) @@ -403,7 +392,7 @@ define(function(require, exports, module) { return; } var item = items[0]; - while (item && !item.readme) { + while (item && !item.packageOptions) { item = item.parent; } if (!item) { @@ -411,12 +400,24 @@ define(function(require, exports, module) { return; } - if (readmeCache[item.readme]) { - descriptionBar.$ext.textContent = readmeCache[item.readme]; + if (readmeCache[item.readmePath]) { + descriptionBar.$ext.textContent = readmeCache[item.readmePath]; return; } descriptionBar.$ext.textContent = "Loading..."; - // fs.readFile() + var parts = item.filePath.split("/"); + parts.pop(); + parts.push("README.md"); + var path = parts.join("/"); + fs.readFile(path, function(e, v) { + if (e) + return descriptionBar.$ext.textContent = "Readme is not available."; + var div = document.createElement("div"); + div.textContent = v; + div.style.cssText = "padding: 5px; white-space: pre-wrap"; + descriptionBar.$ext.textContent = ""; + descriptionBar.$ext.appendChild(div); + }); } function renderServiceDetails(items) { @@ -563,6 +564,28 @@ define(function(require, exports, module) { hoverDetails.highlighted = hoverDetails.hovered; } window.requestAnimationFrame(renderDetails); + + var mnuCtxTree = new ui.menu({ + id: "mnuChat", + }, plugin); + menus.decorate(mnuCtxTree); + plugin.addElement(mnuCtxTree); + menus.addItemByPath("context/pluginManager/", mnuCtxTree, 0, plugin); + menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.filePath; + + }, + onclick: function() { + var selected = datagrid.selection.getCursor(); + var tabbehavior = architectApp.services.tabbehavior; + if (selected.filePath) { + tabbehavior.revealtab({ path: selected.filePath }); + } + }, + }), plugin); + treeBar.setAttribute("contextmenu", mnuCtxTree); } @@ -659,9 +682,15 @@ define(function(require, exports, module) { if (localPlugins) { groups.pre.map = groups.pre.map || Object.create(null); localPlugins.forEach(function(x) { - groups.vfs.map[x] = { + groups.vfs.map[x] = groups.vfs.map[x] || { + filePath: "~/.c9/plugins/" + x + "/package.json", + path: "plugins/" + x, name: x, - enabled: 0 + enabled: 0, + parent: groups.vfs, + packageOptions: { + name: x, + } }; }); } @@ -674,7 +703,7 @@ define(function(require, exports, module) { } if (node.items) { node.items.forEach(flatten); - if (node.items.length == 1) { + if (node.items.length == 1 && !node.isGroup) { var other = node.items[0]; if (node.parent && node.parent.items[index] == node) { node.parent.items[index] = other; @@ -722,8 +751,9 @@ define(function(require, exports, module) { function createNewPlugin(template) { if (!template) template = "c9.ide.default"; - - var url = staticPrefix + "/" + join("templates", template + ".tar.gz"); + + var tarSourcePath = join("templates", template + ".tar.gz") + var url = staticPrefix + "/" + tarSourcePath; if (!url.match(/^http/)) url = location.origin + url; @@ -755,8 +785,12 @@ define(function(require, exports, module) { args: ["-c", [ // using mkdirp since "--create-dirs" is broken on windows "mkdir", "-p", util.escapeShell(dirname(tarPathAbsolute)), ";", - "curl", "-L", util.escapeShell(url), "-o", util.escapeShell(tarPathAbsolute)].join(" ") - ] + ].concat( + c9.sourceDir + ? [ "cp", util.escapeShell(c9.sourceDir + "/plugins/c9.ide.plugins/" + tarSourcePath), + util.escapeShell(tarPathAbsolute) ] + : [ "curl", "-L", util.escapeShell(url), "-o", util.escapeShell(tarPathAbsolute) ] + ).join(" ")] }, function(err, stderr, stdout) { if (err) return handleError(err); @@ -782,6 +816,8 @@ define(function(require, exports, module) { // Select and expand the folder of the plugin tree.expandAndSelect(path); + + readAvailablePlugins(reloadModel); }); }); }); @@ -816,102 +852,6 @@ define(function(require, exports, module) { } } - function loadVFSExtension(callback) { - if (api) - return callback(null, api); - ext.loadRemotePlugin("pluginLoader", { - code: c9.standalone ? undefined : require("text!./vfs.package.reader.js"), - file: c9.standalone ? "c9.ide.plugins/vfs.package.reader.js" : undefined, - redefine: true - }, function(err, remote) { - if (err) - return callback(err); - - api = remote; - return callback(null, api); - }); - } - - function reloadPackage(path) { - var unloaded = []; - - function recurUnload(name) { - var plugin = architectApp.services[name]; - unloaded.push(name); - - // Find all the dependencies - var deps = ext.getDependents(plugin.name); - - // Unload all the dependencies (and their deps) - deps.forEach(function(name) { - recurUnload(name); - }); - - // Unload plugin - plugin.unload(); - } - - // Recursively unload plugin - var p = architectApp.lut[path]; - if (p.provides) { // Plugin might not been initialized all the way - p.provides.forEach(function(name) { - recurUnload(name); - }); - } - - // create reverse lookup table - var rlut = {}; - for (var packagePath in architectApp.lut) { - var provides = architectApp.lut[packagePath].provides; - if (provides) { // Plugin might not been initialized all the way - provides.forEach(function(name) { - rlut[name] = packagePath; - }); - } - } - - // Build config of unloaded plugins - var config = [], done = {}; - unloaded.forEach(function(name) { - var packagePath = rlut[name]; - - // Make sure we include each plugin only once - if (done[packagePath]) return; - done[packagePath] = true; - - var options = architectApp.lut[packagePath]; - delete options.provides; - delete options.consumes; - delete options.setup; - - config.push(options); - - // Clear require cache - requirejs.undef(options.packagePath); // global - }); - - // Load all plugins again - architectApp.loadAdditionalPlugins(config, function(err) { - if (err) console.error(err); - }); - } - - function reloadAllCustomPlugins() { - var list = []; - Object.keys(architectApp.serviceToPlugin).forEach(function(name) { - if (architectApp.serviceToPlugin[name].isAdditionalMode) - list.push(architectApp.serviceToPlugin[name].path); - }); - - var path = list[list.length - 1]; - reloadPackage(path.replace(/^~\/\.c9\//, "")); - - // Avoid confusion with "Reload Last Plugin" - var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); - window.history.replaceState(window.history.state, null, href); - showInfo("Reloaded " + path + ".", 1000); - } - function showReloadTip(name) { if (options.devel) { var key = commands.getHotkey("reloadLastPlugin"); @@ -1037,25 +977,86 @@ define(function(require, exports, module) { function loadPackage(options, callback) { // 1 {url, } // 2 - var paths = {}; - paths[options.id] = options.staticPrefix; - - requirejs.config({ paths: paths }); - requirejs.undef(options.id, true); - - require([options.url], function(installed) { - var plugins = require(options.id + "/package.json.js"); - - var architectConfig = plugins.map(function(plugin) { - if (typeof plugin == "string") - plugin = { packagePath: plugin }; - return plugin; - }); - architectApp.loadAdditionalPlugins(architectConfig, function(err) { + if (Array.isArray(options)) + return async.map(options, loadPackage, callback); + + if (typeof options == "string") { + options = /^https?:/.test(options) + ? { path: options } + : { url: options }; + } + + if (!options.url && options.path) + options.url = vfs.url(options.path); + + var parts = options.url.split("/"); + var root = parts.pop(); + options.url = parts.join("/"); + + if (!options.name) { + options.name = parts[parts.length - 1]; + if (options.name == "build") + options.name = parts[parts.length - 2]; + options.name = options.name.replace(/@.*$/, ""); + } + if (!options.packageName) + options.packageName = root; + + if (!options.rootDir) + options.rootDir = "plugins"; + + var id = options.rootDir + "/" + options.name; + var pathMappings = {}; + + pathMappings[id] = options.url; + requirejs.config({ paths: pathMappings }); + requirejs.undef(id + "/", true); + + if (/\.js$/.test(root)) { + require([options.url + "/" + root], function(json) { + json = json || require(options.id + "/" + options.packageName); + getPluginsFromPackage(json, callback); + }, function(err) { callback && callback(err); }); - - }, function(err) { + } + else if (options.path && /\.json$/.test(root)) { + fs.readFile(options.path, function(e, value) { + try { + var json = JSON.parse(value); + } catch (e) { + return callback(e); + } + getPluginsFromPackage(json); + }); + } + else { + callback(new Error("Missing path and url")); + } + + function getPluginsFromPackage(json, callback) { + var plugins = []; + if (json.name != options.name) + json.name = options.name; + + if (json.plugins) { + Object.keys(json.plugins).forEach(function(name) { + var plugin = json.plugins[name]; + if (typeof plugin == "string") + plugin = { packagePath: plugin }; + if (!plugin.packagePath) + plugin.packagePath = id + "/" + name; + plugin.staticPrefix = options.url; + plugins.push(plugin); + }); + } + enablePlugins(plugins, callback); + } + } + + function enablePlugins(plugins, callback) { + + architectApp.loadAdditionalPlugins(plugins, function(err) { callback && callback(err); }); } @@ -1169,7 +1170,7 @@ define(function(require, exports, module) { filterbox = null; btnUninstall = null; localPlugins = null; - api = null; + disabledPlugins = null; }); /***** Register and define API *****/ @@ -1202,8 +1203,6 @@ define(function(require, exports, module) { * */ reload: reload, - - loadVFSExtension: loadVFSExtension }); var shim = new Plugin(); diff --git a/plugins/c9.ide.ui/lib/menu/menu.js b/plugins/c9.ide.ui/lib/menu/menu.js index d5e2e405..1118e70e 100644 --- a/plugins/c9.ide.ui/lib/menu/menu.js +++ b/plugins/c9.ide.ui/lib/menu/menu.js @@ -1520,7 +1520,7 @@ apf.item = function(struct, tagName) { return; var opener = this.parentNode.opener; - if (opener && opener.parentNode.$showingSubMenu) + if (opener && opener.parentNode && opener.parentNode.$showingSubMenu) selectItem(opener); if (!force && (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget) From 542746be50991e75879be9710863eec38e7595df Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 14 Mar 2017 16:17:29 +0400 Subject: [PATCH 06/15] show load errors in plugin manager --- node_modules/architect/architect.js | 4 +- plugins/c9.core/util.js | 2 +- plugins/c9.ide.plugins/manager.js | 131 ++++++++++++++++++++-------- plugins/c9.ide.plugins/style.css | 5 +- 4 files changed, 101 insertions(+), 41 deletions(-) diff --git a/node_modules/architect/architect.js b/node_modules/architect/architect.js index 4e880ca5..8628c9ca 100644 --- a/node_modules/architect/architect.js +++ b/node_modules/architect/architect.js @@ -134,7 +134,9 @@ else (function () { plugin.consumes = module.consumes || []; }); callback(null, config); - }, errback); + }, errback || function(err) { + callback(err); + }); } }()); diff --git a/plugins/c9.core/util.js b/plugins/c9.core/util.js index d0e01c03..da1fba99 100644 --- a/plugins/c9.core/util.js +++ b/plugins/c9.core/util.js @@ -153,7 +153,7 @@ define(function(require, exports, module) { var data = strJson.replace(/(^|\n)\s*\/\/.*/g, ""); try { return JSON.parse(data); } - catch (e) { cb(e); return false; } + catch (e) { cb && cb(e); return false; } }; /** diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 78689371..074fe4e5 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -76,20 +76,20 @@ define(function(require, exports, module) { index: 200, visible: ENABLED, }); - // var emit = plugin.getEmitter(); + var emit = plugin.getEmitter(); var model, datagrid, filterbox; var btnUninstall, btnServices, btnReadme, btnReload; var btnReloadLast; var localPlugins; var disabledPlugins = Object.create(null); + var packages = Object.create(null); var loaded = false; function load() { if (loaded) return false; loaded = true; - menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); menus.addItemByPath("Tools/Developer", null, 100100, plugin); @@ -162,6 +162,12 @@ define(function(require, exports, module) { ext.on("register", function() { setTimeout(reloadModel); }); + + if (DEBUG && sessionStorage.localPackages) { + loadPackage( + util.safeParseJson(sessionStorage.localPackages) || [] + ); + } } var drawn; @@ -234,7 +240,6 @@ define(function(require, exports, module) { height: 27, width: 250, singleline: true, - clearbutton: true, "initial-message": "Search installed plugins" }), btnReadme = new ui.button({ @@ -243,7 +248,7 @@ define(function(require, exports, module) { class: "serviceButton", onclick: function() { mode = "readme"; - window.requestAnimationFrame(renderDetails); + scheduleRedraw(); } }), btnServices = new ui.button({ @@ -252,7 +257,7 @@ define(function(require, exports, module) { class: "serviceButton", onclick: function() { mode = "services"; - window.requestAnimationFrame(renderDetails); + scheduleRedraw(); } }), new ui.filler({}), @@ -262,10 +267,18 @@ define(function(require, exports, module) { class: "btn-red", onclick: function() { var item = datagrid.selection.getCursor(); - if (item.enabled) - unloadPackage({ path: item.path }); - else - loadPackage({ path: item.filePath || item.path }); + if (item.packageConfig) { + if (item.enabled) + unloadPackage(item.packageConfig); + else + loadPackage(item.packageConfig); + } + else { + if (item.enabled) + unloadPlugins(item); + else + loadPlugins(item); + } } }), btnReload = new ui.button({ @@ -337,12 +350,10 @@ define(function(require, exports, module) { datagrid.resize(true); }); - datagrid.on("changeSelection", function(e) { - window.requestAnimationFrame(renderDetails); - }); - model.on("change", function(e) { - window.requestAnimationFrame(renderDetails); - }); + datagrid.on("changeSelection", scheduleRedraw); + model.on("change", scheduleRedraw); + + plugin.on("reloadModel", scheduleRedraw); model.getCheckboxHTML = function(node) { var enabled = node.enabled; @@ -563,7 +574,10 @@ define(function(require, exports, module) { } hoverDetails.highlighted = hoverDetails.hovered; } - window.requestAnimationFrame(renderDetails); + + function scheduleRedraw() { + window.requestAnimationFrame(renderDetails); + } var mnuCtxTree = new ui.menu({ id: "mnuChat", @@ -645,12 +659,13 @@ define(function(require, exports, module) { }); } - architectApp.config.forEach(function(plugin) { + function addPlugin(plugin, node) { + if (!plugin.provides || !plugin.consumes) + return console.error("Broken plugin", plugin); if (plugin.packagePath) { var parts = plugin.packagePath.split("/"); var path = parts.shift(); - var node = CORE[plugin.packagePath] ? groups.core : groups.pre; parts.forEach(function(p, i) { path = path + "/" + p; if (!node.map) @@ -665,22 +680,26 @@ define(function(require, exports, module) { if (i == parts.length - 1) { var enabled = 1; node.map[p].provides = plugin.provides; - node.map[p].time = plugin.provides.reduce(function(sum, x) { + plugin.provides.forEach(function(x) { var service = architectApp.services[x]; if (!service || (!service.loaded && service.unload)) { enabled = 0; } - return sum + (service && service.time || 0); - }, 0); + }); node.map[p].enabled = enabled; } node = node.map[p]; + node.className = plugin.__error ? "load-error" : ""; }); } + } + + architectApp.config.forEach(function(plugin) { + var node = CORE[plugin.packagePath] ? groups.core : groups.pre; + addPlugin(plugin, node); }); if (localPlugins) { - groups.pre.map = groups.pre.map || Object.create(null); localPlugins.forEach(function(x) { groups.vfs.map[x] = groups.vfs.map[x] || { filePath: "~/.c9/plugins/" + x + "/package.json", @@ -695,6 +714,17 @@ define(function(require, exports, module) { }); } + Object.keys(packages).forEach(function(n) { + var node = groups.vfs; + var pkg = packages[n]; + + if (pkg.c9.plugins) { + pkg.c9.plugins.forEach(function(plugin) { + addPlugin(plugin, node); + }); + } + }); + function flatten(node, index) { if (node.map) { node.items = node.children = Object.keys(node.map).map(function(x) { @@ -708,6 +738,9 @@ define(function(require, exports, module) { if (node.parent && node.parent.items[index] == node) { node.parent.items[index] = other; other.name = node.name + "/" + other.name; + other.packageOptions = node.packageOptions; + other.filePath = node.filePath; + other.url = node.url; } } if (!node.isGroup) { @@ -727,6 +760,8 @@ define(function(require, exports, module) { flatten(model.cachedRoot); applyFilter(); + + emit("reloadModel"); } function applyFilter() { @@ -752,7 +787,7 @@ define(function(require, exports, module) { if (!template) template = "c9.ide.default"; - var tarSourcePath = join("templates", template + ".tar.gz") + var tarSourcePath = join("templates", template + ".tar.gz"); var url = staticPrefix + "/" + tarSourcePath; if (!url.match(/^http/)) url = location.origin + url; @@ -869,54 +904,65 @@ define(function(require, exports, module) { return qs.parse(document.location.search.substr(1)).reload; } - function addAllDependents(packages) { + function getAllPlugins() { var config = architectApp.config; + Object.keys(packages).forEach(function(n) { + if (packages[n] && packages[n].c9 && packages[n].c9.plugins) + config = config.concat(packages[n].c9.plugins); + }); + return config.filter(function(x) { + return x.consumes && x.provides; + }); + } + + function addAllDependents(plugins) { + var config = getAllPlugins(); var level = 0; do { level++; var changed = false; - var packageNames = Object.keys(packages); + var packageNames = Object.keys(plugins); config.forEach(function(x) { packageNames.forEach(function(p) { if (x.consumes.indexOf(p) != -1) { x.provides.forEach(function(name) { - if (packages[name] == null) { + if (plugins[name] == null) { changed = true; - packages[name] = level; + plugins[name] = level; } }); } }); }); } while (changed); - return packages; + return plugins; } - function addAllProviders(packages) { + function addAllProviders(plugins) { var serviceToPlugin = architectApp.serviceToPlugin; var level = 0; do { level--; var changed = false; - var packageNames = Object.keys(packages); + var packageNames = Object.keys(plugins); packageNames.forEach(function(p) { var service = serviceToPlugin[p]; if (service && service.consumes) { service.consumes.forEach(function(name) { - if (packages[name] == null) { + if (plugins[name] == null) { changed = true; - packages[name] = level; + plugins[name] = level; } }); } }); } while (changed); - return packages; + return plugins; } function unloadPackage(options, callback) { var toUnload = Object.create(null); - var config = architectApp.config; + var config = getAllPlugins(); function addPath(path) { config.forEach(function(p) { if (!p.packagePath) return; @@ -1027,7 +1073,7 @@ define(function(require, exports, module) { } catch (e) { return callback(e); } - getPluginsFromPackage(json); + getPluginsFromPackage(json, callback); }); } else { @@ -1050,17 +1096,26 @@ define(function(require, exports, module) { plugins.push(plugin); }); } - enablePlugins(plugins, callback); + + packages[json.name] = json; + if (!json.c9) + json.c9 = {}; + json.c9.plugins = plugins; + + loadPlugins(plugins, callback); } } - function enablePlugins(plugins, callback) { - + function loadPlugins(plugins, callback) { architectApp.loadAdditionalPlugins(plugins, function(err) { callback && callback(err); }); } + function unloadPlugins() { + + } + function cleanupCache(packagePath) { var options = architectApp.pathToPackage[packagePath]; var url = require.toUrl(options.packagePath, ".js"); diff --git a/plugins/c9.ide.plugins/style.css b/plugins/c9.ide.plugins/style.css index a439b088..aefe7261 100644 --- a/plugins/c9.ide.plugins/style.css +++ b/plugins/c9.ide.plugins/style.css @@ -10,4 +10,7 @@ .serviceButton:hover, .serviceButtonActive { color: @preferences-intro-link-color; text-decoration : underline; -} \ No newline at end of file +} +.tree-row.load-error { + color: #e44; +} From 21755657a52246ec54c177e9bcabd6ea5decb463 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 27 Mar 2017 00:29:41 +0400 Subject: [PATCH 07/15] got package load/unload to work --- plugins/c9.ide.plugins/manager.js | 446 ++++++++++++++++++------------ plugins/c9.ide.plugins/style.css | 2 +- 2 files changed, 274 insertions(+), 174 deletions(-) diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 074fe4e5..39220834 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -79,7 +79,7 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); var model, datagrid, filterbox; - var btnUninstall, btnServices, btnReadme, btnReload; + var btnInstall, btnUninstall, btnServices, btnReadme, btnReload; var btnReloadLast; var localPlugins; var disabledPlugins = Object.create(null); @@ -115,13 +115,13 @@ define(function(require, exports, module) { } }, plugin); - menus.addItemByPath("Tools/Developer/Open Plugin Manager", new ui.item({ + menus.addItemByPath("Tools/Developer/Open Plugin Explorer", new ui.item({ command: "openPluginManager" }), 1100, plugin); if (DEBUG) { notify("
You are in Debug Mode. " - + "" + + "" + "", false); @@ -163,10 +163,20 @@ define(function(require, exports, module) { setTimeout(reloadModel); }); - if (DEBUG && sessionStorage.localPackages) { - loadPackage( - util.safeParseJson(sessionStorage.localPackages) || [] - ); + if (DEBUG) { + if (sessionStorage.localPackages) { + loadPackage( + util.safeParseJson(sessionStorage.localPackages) || [] + ); + } + var updateSessionStorage = function() { + sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) { + if (packages[x] && packages[x].fromVfs && packages[x].enabled) + return packages[x].filePath; + }).filter(Boolean)); + }; + plugin.on("enablePackage", updateSessionStorage); + plugin.on("disablePackage", updateSessionStorage); } } @@ -183,9 +193,6 @@ define(function(require, exports, module) { model.columns = [{ caption: "Name", value: "name", - // getText: function(p) { - // return p.name + " (" + p.items.length + ")"; - // }, width: "250", type: "tree" }, { @@ -261,33 +268,27 @@ define(function(require, exports, module) { } }), new ui.filler({}), + btnInstall = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Enable", + class: "btn-red", + onclick: function() { + reloadGridSelection(true); + } + }), btnUninstall = new ui.button({ skin: "c9-toolbarbutton-glossy", caption: "Disable", class: "btn-red", onclick: function() { - var item = datagrid.selection.getCursor(); - if (item.packageConfig) { - if (item.enabled) - unloadPackage(item.packageConfig); - else - loadPackage(item.packageConfig); - } - else { - if (item.enabled) - unloadPlugins(item); - else - loadPlugins(item); - } + reloadGridSelection(false); } }), btnReload = new ui.button({ skin: "c9-toolbarbutton-glossy", caption: "Reload", onclick: function() { - var item = datagrid.selection.getCursor(); - if (item.name) - reload(item.name); + reloadGridSelection(); } }) ] @@ -369,17 +370,18 @@ define(function(require, exports, module) { var readmeCache = {}; function renderDetails() { var items = datagrid.selection.getSelectedNodes(); - var enabled = items.every(function(x) { - return x.enabled == 1; + var hasEnabled = 0; + var hasDisabled = 0; + items.forEach(function(x) { + if (x.enabled) { + hasEnabled = true; + } else { + hasDisabled = true; + } }); - if (enabled) { - btnReload.enable(); - btnUninstall.setCaption("Disable"); - } - else { - btnReload.disable(); - btnUninstall.setCaption("Enable"); - } + btnUninstall.setProperty("visible", hasEnabled); + btnInstall.setProperty("visible", hasDisabled); + if (mode == "services") { renderServiceDetails(items); btnServices.$ext.classList.add("serviceButtonActive"); @@ -395,6 +397,13 @@ define(function(require, exports, module) { btnReadme.$ext.classList.remove("serviceButtonActive"); btnServices.$ext.classList.remove("serviceButtonActive"); } + + if (items.length == 1 && items[0].__error) { + var errorContainer = document.createElement("div"); + errorContainer.style.cssText = "padding: 5px; white-space: pre-wrap"; + errorContainer.textContent = items[0].__error.message; + descriptionBar.$ext.insertBefore(errorContainer, descriptionBar.$ext.firstChild); + } } function renderReadmeDetails(items) { @@ -403,7 +412,7 @@ define(function(require, exports, module) { return; } var item = items[0]; - while (item && !item.packageOptions) { + while (item && !item.packageConfig) { item = item.parent; } if (!item) { @@ -411,24 +420,27 @@ define(function(require, exports, module) { return; } - if (readmeCache[item.readmePath]) { - descriptionBar.$ext.textContent = readmeCache[item.readmePath]; - return; - } - descriptionBar.$ext.textContent = "Loading..."; - var parts = item.filePath.split("/"); - parts.pop(); - parts.push("README.md"); - var path = parts.join("/"); - fs.readFile(path, function(e, v) { - if (e) - return descriptionBar.$ext.textContent = "Readme is not available."; + if (readmeCache[item.name]) { var div = document.createElement("div"); - div.textContent = v; + div.textContent = readmeCache[item.name]; div.style.cssText = "padding: 5px; white-space: pre-wrap"; descriptionBar.$ext.textContent = ""; descriptionBar.$ext.appendChild(div); - }); + return; + } + descriptionBar.$ext.textContent = "Loading..."; + if (item.packageConfig.filePath) { + var parts = item.packageConfig.filePath.split("/"); + parts.pop(); + parts.push("README.md"); + var path = parts.join("/"); + fs.readFile(path, function(e, v) { + if (e) + return readmeCache[item.name] = "Readme is not available."; + readmeCache[item.name] = v; + renderDetails(); + }); + } } function renderServiceDetails(items) { @@ -486,20 +498,21 @@ define(function(require, exports, module) { } descriptionBar.$ext.style.overflow = "auto"; - descriptionBar.$ext.innerHTML = '
\ -

' + (items.length == 1 - ? '' + escapeHTML(items[0].path) + '' - : (items.length || "no") + " plugins selected") + '

\ -
\ -

Provided services [' + provided.length + ']

\ + var serviceDesc = '

Provided services [' + provided.length + ']

\

' + (provided.length ? formatServices(provided) : "") + '

\

Consumed services [' + (consumedList.length - provided.length) + ']

\

' + consumedGroups.map(formatServices).join("
") + '

\

Dependent services [' + (depList.length - provided.length) + ']

\

' + depGroups.map(formatServices).join("
") + '

\ -
\ -
'; +
'; + descriptionBar.$ext.innerHTML = '
\ +

' + (items.length == 1 + ? '' + escapeHTML(items[0].path) + '' + : (items.length || "no") + " plugins selected") + '

\ +
' + + (items.length ? serviceDesc : "") + + '
'; } descriptionBar.$ext.addEventListener("click", function(e) { @@ -588,14 +601,15 @@ define(function(require, exports, module) { menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({ isAvailable: function() { var selected = datagrid.selection.getCursor(); - return selected && selected.filePath; + return selected && selected.packageConfig && selected.packageConfig.filePath; }, onclick: function() { var selected = datagrid.selection.getCursor(); var tabbehavior = architectApp.services.tabbehavior; - if (selected.filePath) { - tabbehavior.revealtab({ path: selected.filePath }); + var filePath = selected.packageConfig && selected.packageConfig.filePath; + if (filePath) { + tabbehavior.revealtab({ path: filePath }); } }, }), plugin); @@ -634,7 +648,7 @@ define(function(require, exports, module) { } var GROUPS = { - "changed": "Recently Changed", + // "changed": "Recently Changed", "remote": "Remote Plugins", "vfs": "Locally Installed Plugins", "pre": "Pre-installed Plugins", @@ -690,35 +704,51 @@ define(function(require, exports, module) { } node = node.map[p]; node.className = plugin.__error ? "load-error" : ""; + node.__error = plugin.__error; }); } } + function addPackage(name) { + var pkg = packages[name]; + var parent = pkg.filePath ? groups.vfs : groups.remote; + + var node = parent.map[name] = parent.map[name] || { + path: "plugins/" + name, + name: name, + enabled: 0, + parent: parent, + }; + + node.packageConfig = pkg; + node.className = pkg.__error ? "load-error" : ""; + node.__error = pkg.__error; + } + architectApp.config.forEach(function(plugin) { var node = CORE[plugin.packagePath] ? groups.core : groups.pre; addPlugin(plugin, node); }); if (localPlugins) { - localPlugins.forEach(function(x) { - groups.vfs.map[x] = groups.vfs.map[x] || { - filePath: "~/.c9/plugins/" + x + "/package.json", - path: "plugins/" + x, - name: x, - enabled: 0, - parent: groups.vfs, - packageOptions: { - name: x, + localPlugins.forEach(function(name) { + if (!packages[name]) { + packages[name] = { + name: name, + filePath: "~/.c9/plugins/" + name + "/package.json", } - }; + } + }); } Object.keys(packages).forEach(function(n) { - var node = groups.vfs; var pkg = packages[n]; + var node = pkg.filePath ? groups.vfs : groups.remote; - if (pkg.c9.plugins) { + addPackage(n); + + if (pkg.c9 && pkg.c9.plugins) { pkg.c9.plugins.forEach(function(plugin) { addPlugin(plugin, node); }); @@ -738,12 +768,13 @@ define(function(require, exports, module) { if (node.parent && node.parent.items[index] == node) { node.parent.items[index] = other; other.name = node.name + "/" + other.name; - other.packageOptions = node.packageOptions; + other.packageConfig = node.packageConfig; other.filePath = node.filePath; other.url = node.url; } } if (!node.isGroup) { + node.enabled = null; node.items.some(function(i) { if (node.enabled == null) { node.enabled = i.enabled; @@ -877,6 +908,25 @@ define(function(require, exports, module) { updateReloadLastButton(); } + function reloadGridSelection(mode) { + var nodes = datagrid.selection.getSelectedNodes(); + console.log(nodes, mode); + nodes.forEach(function(node) { + if (node.packageConfig) { + if (!mode) + unloadPackage(node.packageConfig.name); + if (mode != false) + loadPackage(node.packageConfig.filePath); + } + else { + if (!mode) + unloadPlugins({ path: node.path }); + if (mode != false) + loadPlugins({ path: node.path }); + } + }); + } + function updateReloadLastButton() { var last = getLastReloaded(); if (last) { @@ -961,6 +1011,142 @@ define(function(require, exports, module) { } function unloadPackage(options, callback) { + if (Array.isArray(options)) + return async.map(options, unloadPackage, callback || function() {}); + var name = typeof options == "object" ? options.name : options; + if (packages[name]) { + packages[name].enabled = false; + unloadPlugins(packages[name].path); + emit("disablePackage"); + } + } + + function loadPackage(options, callback) { + if (Array.isArray(options)) + return async.map(options, loadPackage, callback || function() {}); + + if (typeof options == "string") { + options = /^https?:/.test(options) + ? { url: options } + : { path: options }; + } + + if (!options.url && options.path) + options.url = vfs.url(options.path); + + var parts = options.url.split("/"); + var root = parts.pop(); + options.url = parts.join("/"); + + if (!options.name) { + options.name = parts[parts.length - 1]; + if (options.name == "build") + options.name = parts[parts.length - 2]; + options.name = options.name.replace(/@.*$/, ""); + } + if (!options.packageName) + options.packageName = root; + + if (!options.rootDir) + options.rootDir = "plugins"; + + var name = options.name; + var id = options.rootDir + "/" + name; + var pathMappings = {}; + + unloadPlugins("plugins/" + options.name); + + pathMappings[id] = options.url; + requirejs.config({ paths: pathMappings }); + requirejs.undef(id + "/", true); + + if (/\.js$/.test(root)) { + require([options.url + "/" + root], function(json) { + json = json || require(options.id + "/" + options.packageName); + getPluginsFromPackage(json, callback); + }, function(err) { + addError("Error loading plugin", err); + }); + } + else if (options.path && /\.json$/.test(root)) { + fs.readFile(options.path, function(err, value) { + if (err) return addError("Error reading " + options.path, err); + try { + var json = JSON.parse(value); + } catch (e) { + return addError("Error parsing package.json", e); + } + json.fromVfs = true; + getPluginsFromPackage(json, callback); + }); + } + else if (options.url && /\.json$/.test(root)) { + require(["text!" + options.id + "/" + root], function(value) { + try { + var json = JSON.parse(value); + } catch (e) { + return addError("Error parsing package.json", e); + } + getPluginsFromPackage(json, callback); + }, function(err) { + addError("Error loading plugin", err); + }); + } + else { + callback && callback(new Error("Missing path and url")); + } + + function addError(message, err) { + if (!packages[name]) + packages[name] = {}; + packages[name].filePath = options.path; + packages[name].url = options.url; + packages[name].__error = new Error(message + "\n" + err.message); + + reloadModel(); + + callback && callback(err); + } + + function getPluginsFromPackage(json, callback) { + var plugins = []; + if (json.name != name) + json.name = name; + + if (json.plugins) { + Object.keys(json.plugins).forEach(function(name) { + var plugin = json.plugins[name]; + if (typeof plugin == "string") + plugin = { packagePath: plugin }; + if (!plugin.packagePath) + plugin.packagePath = id + "/" + name; + plugin.staticPrefix = options.url; + plugins.push(plugin); + }); + } + + packages[json.name] = json; + json.filePath = options.path; + json.url = options.url; + + if (!json.c9) + json.c9 = {}; + json.c9.plugins = plugins; + json.enabled = true; + json.path = id; + + emit("enablePackage", json); + loadPlugins(plugins, callback); + } + } + + function loadPlugins(plugins, callback) { + architectApp.loadAdditionalPlugins(plugins, function(err) { + callback && callback && callback(err); + }); + } + + function unloadPlugins(options, callback) { var toUnload = Object.create(null); var config = getAllPlugins(); function addPath(path) { @@ -973,6 +1159,9 @@ define(function(require, exports, module) { } }); } + if (typeof options == "string") { + addPath(options); + } if (options.path) { addPath(options.path); } @@ -1020,102 +1209,6 @@ define(function(require, exports, module) { reloadModel(); } - function loadPackage(options, callback) { - // 1 {url, } - // 2 - if (Array.isArray(options)) - return async.map(options, loadPackage, callback); - - if (typeof options == "string") { - options = /^https?:/.test(options) - ? { path: options } - : { url: options }; - } - - if (!options.url && options.path) - options.url = vfs.url(options.path); - - var parts = options.url.split("/"); - var root = parts.pop(); - options.url = parts.join("/"); - - if (!options.name) { - options.name = parts[parts.length - 1]; - if (options.name == "build") - options.name = parts[parts.length - 2]; - options.name = options.name.replace(/@.*$/, ""); - } - if (!options.packageName) - options.packageName = root; - - if (!options.rootDir) - options.rootDir = "plugins"; - - var id = options.rootDir + "/" + options.name; - var pathMappings = {}; - - pathMappings[id] = options.url; - requirejs.config({ paths: pathMappings }); - requirejs.undef(id + "/", true); - - if (/\.js$/.test(root)) { - require([options.url + "/" + root], function(json) { - json = json || require(options.id + "/" + options.packageName); - getPluginsFromPackage(json, callback); - }, function(err) { - callback && callback(err); - }); - } - else if (options.path && /\.json$/.test(root)) { - fs.readFile(options.path, function(e, value) { - try { - var json = JSON.parse(value); - } catch (e) { - return callback(e); - } - getPluginsFromPackage(json, callback); - }); - } - else { - callback(new Error("Missing path and url")); - } - - function getPluginsFromPackage(json, callback) { - var plugins = []; - if (json.name != options.name) - json.name = options.name; - - if (json.plugins) { - Object.keys(json.plugins).forEach(function(name) { - var plugin = json.plugins[name]; - if (typeof plugin == "string") - plugin = { packagePath: plugin }; - if (!plugin.packagePath) - plugin.packagePath = id + "/" + name; - plugin.staticPrefix = options.url; - plugins.push(plugin); - }); - } - - packages[json.name] = json; - if (!json.c9) - json.c9 = {}; - json.c9.plugins = plugins; - - loadPlugins(plugins, callback); - } - } - - function loadPlugins(plugins, callback) { - architectApp.loadAdditionalPlugins(plugins, function(err) { - callback && callback(err); - }); - } - - function unloadPlugins() { - - } - function cleanupCache(packagePath) { var options = architectApp.pathToPackage[packagePath]; var url = require.toUrl(options.packagePath, ".js"); @@ -1223,6 +1316,7 @@ define(function(require, exports, module) { model = null; datagrid = null; filterbox = null; + btnInstall = null; btnUninstall = null; localPlugins = null; disabledPlugins = null; @@ -1258,6 +1352,12 @@ define(function(require, exports, module) { * */ reload: reload, + + /* + * @ignore + */ + get datagrid() { return datagrid; }, + get packages() { return packages; }, }); var shim = new Plugin(); diff --git a/plugins/c9.ide.plugins/style.css b/plugins/c9.ide.plugins/style.css index aefe7261..764a247f 100644 --- a/plugins/c9.ide.plugins/style.css +++ b/plugins/c9.ide.plugins/style.css @@ -12,5 +12,5 @@ text-decoration : underline; } .tree-row.load-error { - color: #e44; + color: #f99145; } From eea9f673316c061372b271ef6e9025485dc0faf8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 8 Apr 2017 14:22:30 +0400 Subject: [PATCH 08/15] add loading state to the tree checkbox --- plugins/c9.ide.plugins/manager.js | 56 ++++++++++++++++++++----------- plugins/c9.ide.ui/widgets.less | 16 +++++++++ 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 39220834..de424c9a 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -130,8 +130,22 @@ define(function(require, exports, module) { btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") }; updateReloadLastButton(); - readAvailablePlugins(); - + readAvailablePlugins(function() { + if (sessionStorage.localPackages) { + loadPackage( + util.safeParseJson(sessionStorage.localPackages) || [] + ); + } + var updateSessionStorage = function() { + sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) { + if (packages[x] && packages[x].fromVfs && packages[x].enabled) + return packages[x].filePath; + }).filter(Boolean)); + }; + plugin.on("enablePackage", updateSessionStorage); + plugin.on("disablePackage", updateSessionStorage); + }); + commands.addCommand({ name: "reloadLastPlugin", bindKey: { mac: "F4", win: "F4" }, @@ -162,22 +176,6 @@ define(function(require, exports, module) { ext.on("register", function() { setTimeout(reloadModel); }); - - if (DEBUG) { - if (sessionStorage.localPackages) { - loadPackage( - util.safeParseJson(sessionStorage.localPackages) || [] - ); - } - var updateSessionStorage = function() { - sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) { - if (packages[x] && packages[x].fromVfs && packages[x].enabled) - return packages[x].filePath; - }).filter(Boolean)); - }; - plugin.on("enablePackage", updateSessionStorage); - plugin.on("disablePackage", updateSessionStorage); - } } var drawn; @@ -360,6 +358,7 @@ define(function(require, exports, module) { var enabled = node.enabled; if (enabled == null || node.isGroup) return ""; return " Date: Tue, 4 Apr 2017 20:35:49 +0400 Subject: [PATCH 09/15] polish --- node_modules/architect/architect.js | 15 +- plugins/c9.cli.publish/publish.js | 126 +++++------- plugins/c9.core/c9.js | 4 +- plugins/c9.core/ext.js | 13 +- plugins/c9.ide.collab/workspace.js | 4 +- plugins/c9.ide.plugins/manager.js | 287 +++++++++++++++++++++++----- plugins/c9.ide.save/save.js | 1 - 7 files changed, 295 insertions(+), 155 deletions(-) diff --git a/node_modules/architect/architect.js b/node_modules/architect/architect.js index 8628c9ca..2f0e671d 100644 --- a/node_modules/architect/architect.js +++ b/node_modules/architect/architect.js @@ -126,13 +126,17 @@ else (function () { // Mass-Load path-based plugins using amd's require require(paths, function () { var args = arguments; + var err = []; paths.forEach(function (name, i) { var module = args[i]; var plugin = config[pluginIndexes[name]]; + if (!module || !plugin) return err.push(name); plugin.setup = module; plugin.provides = module.provides || []; plugin.consumes = module.consumes || []; }); + if (err.length) + return callback(new Error("Missing plugins: " + err)); callback(null, config); }, errback || function(err) { callback(err); @@ -246,7 +250,6 @@ function Architect(config) { var app = this; app.config = config; app.serviceToPlugin = {}; - app.additionalConfigs = []; var isAdditionalMode; var services = app.services = { @@ -331,20 +334,18 @@ function Architect(config) { // Give createApp some time to subscribe to our "ready" event (typeof process === "object" ? process.nextTick : setTimeout)(startPlugins); - this.loadAdditionalPlugins = function(additionalConfig, callback){ + this.loadAdditionalPlugins = function(additionalConfig, callback) { isAdditionalMode = true; exports.resolveConfig(additionalConfig, function (err, additionalConfig) { if (err) return callback(err); - app.additionalConfigs.push(additionalConfig); - - app.once(ready ? "ready-additional" : "ready", function(app){ + app.once(ready ? "ready-additional" : "ready", function(app) { callback(null, app); }); // What about error state? // Check the config - hopefully this works - var _sortedPlugins = checkConfig(additionalConfig, function(name){ + var _sortedPlugins = checkConfig(additionalConfig, function(name) { return services[name]; }); @@ -354,7 +355,7 @@ function Architect(config) { startPlugins(true); } else { - _sortedPlugins.forEach(function(item){ + _sortedPlugins.forEach(function(item) { sortedPlugins.push(item); }); } diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index fa628e94..2c5be13b 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -230,97 +230,59 @@ define(function(require, exports, module) { } } - if (files.indexOf("builders") != -1) { - forEachFile(cwd + "/builders", function(filename, data) { - packedFiles.push(cwd + "/builders/" + filename); + function parseHeader(data, filename) { + var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); + var info = {}; + firstLine.split(";").forEach(function(n) { + var key = n.split(":"); + if (key.length != 2) + return console.error("Ignoring invalid key " + n + " in " + filename); + info[key[0].trim()] = key[1].trim(); + }); + info.data = firstLine; + return info; + } + + function addResource(type) { + forEachFile(cwd + "/" + type, function(filename, data) { + packedFiles.push(cwd + "/"+ type + "/" + filename); extraCode.push({ - type: "builders", + type: type, filename: filename, data: data }); }); } - if (files.indexOf("keymaps") != -1) { - forEachFile(cwd + "/keymaps", function(filename, data) { - packedFiles.push(cwd + "/keymaps/" + filename); - extraCode.push({ - type: "keymaps", - filename: filename, - data: data - }); - }); - } - if (files.indexOf("modes") != -1) { - forEachFile(cwd + "/modes", function(filename, data) { + function addMode(type) { + forEachFile(cwd + "/modes", function(filename, data) { if (/(?:_highlight_rules|_test|_worker|_fold|_behaviou?r)\.js$/.test(filename)) return; if (!/\.js$/.test(filename)) return; - var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); + var info = parseHeader(data, cwd + "/modes/" + filename); - if (!/caption\s*:[^;]+/i.test(firstLine)) { - packedFiles.push(cwd + "/modes/" + filename); - console.error("Ignoring mode with invalid header: ", firstLine); - console.error(" at " + cwd + "/modes/" + filename); - return; - } - extraCode.push({ - type: "modes", - filename: filename, - data: firstLine - }); - }); - } - if (files.indexOf("outline") != -1) { - forEachFile(cwd + "/outline", function(filename, data) { - packedFiles.push(cwd + "/outline/" + filename); - extraCode.push({ - type: "outline", - filename: filename, - data: data - }); - }); - } - if (files.indexOf("runners") != -1) { - forEachFile(cwd + "/runners", function(filename, data) { - packedFiles.push(cwd + "/runners/" + filename); - extraCode.push({ - type: "runners", - filename: filename, - data: data - }); - }); - } - if (files.indexOf("snippets") != -1) { - forEachFile(cwd + "/snippets", function(filename, data) { - packedFiles.push(cwd + "/snippets/" + filename); - extraCode.push({ - type: "snippets", - filename: filename, - data: data - }); - }); - } - if (files.indexOf("themes") != -1) { - forEachFile(cwd + "/themes", function(filename, data) { - packedFiles.push(cwd + "/themes/" + filename); - extraCode.push({ - type: "themes", - filename: filename, - data: data - }); - }); - } - if (files.indexOf("templates") != -1) { - forEachFile(cwd + "/templates", function(filename, data) { - packedFiles.push(cwd + "/templates/" + filename); - extraCode.push({ - type: "templates", - filename: filename, - data: data - }); + if (!info.caption) info.caption = filename; + + info.type = "modes"; + info.filename = filename; + extraCode.push(info); }); } + var handlers = { + templates: addResource, + snippets: addResource, + builders: addResource, + keymaps: addResource, + outline: addResource, + runners: addResource, + themes: addResource, + modes: addMode, + }; + files.forEach(function(type) { + if (handlers.hasOwnProperty(type)) + handlers[type](type); + }); + packedFiles.push(cwd + "/package." + packageName + ".js"); @@ -348,7 +310,7 @@ define(function(require, exports, module) { main.consumes = [ "Plugin", "plugin.debug" ]; - main.provides = []; + main.provides = ["PACKAGE_NAME.bundle"]; return main; function main(options, imports, register) { var debug = imports["plugin.debug"]; @@ -363,7 +325,7 @@ define(function(require, exports, module) { plugin.load("PACKAGE_NAME.bundle"); - register(null, {}); + register(null, {"PACKAGE_NAME.bundle": plugin}); } }); }).toString(); @@ -382,7 +344,7 @@ define(function(require, exports, module) { staticPlugin = { source: code, - id: "plugins/" + packageName + "/__static__", + id: "plugins/" + packageName + "/" + packageName + ".bundle", path: "" }; next(); @@ -458,7 +420,7 @@ define(function(require, exports, module) { function(next) { var copy = require("architect-build/copy"); - var excludeRe = /^\.(\w*ignore|git|c9|hg|build)$|^(c9)?build$/; + var excludeRe = /^\.(\w*ignore|git|c9|hg|build)$|^(c9)?build$|_test\.js$/; var excludeMap = Object.create(null); packedFiles.push(cwd + "/package." + packageName + ".js"); diff --git a/plugins/c9.core/c9.js b/plugins/c9.core/c9.js index fe8c1597..0de92bf4 100644 --- a/plugins/c9.core/c9.js +++ b/plugins/c9.core/c9.js @@ -6,7 +6,7 @@ * @main c9.core */ define(function(require, module, exports) { - main.consumes = ["Plugin", "ext", "vfs"]; + main.consumes = ["Plugin", "vfs"]; main.provides = ["c9"]; return main; @@ -20,8 +20,6 @@ define(function(require, module, exports) { var emit = plugin.getEmitter(); emit.setMaxListeners(500); - imports.ext.vfs = imports.vfs; - var loaded = false; var loggedIn = false; var isReady = false; diff --git a/plugins/c9.core/ext.js b/plugins/c9.core/ext.js index 9d192960..07116c3b 100644 --- a/plugins/c9.core/ext.js +++ b/plugins/c9.core/ext.js @@ -1,10 +1,12 @@ define(function(require, exports, module) { - main.consumes = []; + main.consumes = ["hub"]; main.provides = ["ext", "Plugin"]; return main; function main(options, imports, register) { var Emitter = require("events").EventEmitter; + var architectApp = imports.hub.app; + var plugins = []; var lut = {}; @@ -18,12 +20,7 @@ define(function(require, exports, module) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); - var vfs, settings, api; - - plugin.__defineSetter__("vfs", function(remote) { - vfs = remote; - delete plugin.vfs; - }); + var settings, api; plugin.__defineSetter__("settings", function(remote) { settings = remote; @@ -146,12 +143,14 @@ define(function(require, exports, module) { } function loadRemotePlugin(id, options, callback) { + var vfs = architectApp.services.vfs; vfs.extend(id, options, function(err, meta) { callback(err, meta && meta.api); }); } function fetchRemoteApi(id, callback) { + var vfs = architectApp.services.vfs; vfs.use(id, {}, function(err, meta) { callback(err, meta && meta.api); }); diff --git a/plugins/c9.ide.collab/workspace.js b/plugins/c9.ide.collab/workspace.js index d290e08a..ea67e4e7 100644 --- a/plugins/c9.ide.collab/workspace.js +++ b/plugins/c9.ide.collab/workspace.js @@ -380,9 +380,9 @@ define(function(require, exports, module) { get myUserId() { return myUserId; }, /** * Specifies wether the collab workspace was previously loaded and collab was connected - or not - * @property {Boolean} loaded + * @property {Boolean} isReady */ - get loaded() { return loadedWorkspace; }, + get isReady() { return loadedWorkspace; }, /** * Gets my filesystem access to this workspace: * Values can be either "r" or "rw" diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index de424c9a..ab22de35 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -151,10 +151,10 @@ define(function(require, exports, module) { bindKey: { mac: "F4", win: "F4" }, hint: "reload plugin last reloaded in plugin manager", exec: function() { - var name = getLastReloaded(); - if (!name) + var names = getLastReloaded(); + if (!names) return commands.exec("openPluginManager", null, { panel: plugin }); - reload(name); + reload(names); } }, plugin); @@ -372,11 +372,10 @@ define(function(require, exports, module) { var hasEnabled = 0; var hasDisabled = 0; items.forEach(function(x) { - if (x.enabled) { + if (x.enabled != 0) hasEnabled = true; - } else { + if (x.enabled != 1) hasDisabled = true; - } }); btnUninstall.setProperty("visible", hasEnabled); btnInstall.setProperty("visible", hasDisabled); @@ -601,7 +600,6 @@ define(function(require, exports, module) { isAvailable: function() { var selected = datagrid.selection.getCursor(); return selected && selected.packageConfig && selected.packageConfig.filePath; - }, onclick: function() { var selected = datagrid.selection.getCursor(); @@ -612,6 +610,23 @@ define(function(require, exports, module) { } }, }), plugin); + menus.addItemByPath("context/pluginManager/Disable", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.enabled != 0; + }, + onclick: function() { reloadGridSelection(false); }, + }), plugin); + menus.addItemByPath("context/pluginManager/Enable", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.enabled != 1; + }, + onclick: function() { reloadGridSelection(true); }, + }), plugin); + menus.addItemByPath("context/pluginManager/Reload", new ui.item({ + onclick: function() { reloadGridSelection(); }, + }), plugin); treeBar.setAttribute("contextmenu", mnuCtxTree); } @@ -892,39 +907,78 @@ define(function(require, exports, module) { }); } - function reload(name) { - showReloadTip(name); + function reload(names) { + var nodes = names.split(/\s*,\s*/).map(function(name) { + if (packages[name]) + return { packageConfig: packages[name] }; + return { path: name }; + }); - var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); - href += (href.match(/\?/) ? "&" : "?") + "reload=" + name; - window.history.replaceState(window.history.state, null, href); - - for (var plugin in architectApp.lut) { - if (architectApp.lut[plugin].provides.indexOf(name) < 0) - continue; - - reloadPackage(plugin); - return; - } - updateReloadLastButton(); + reloadGridSelection(null, nodes); } - function reloadGridSelection(mode) { - var nodes = datagrid.selection.getSelectedNodes(); - console.log(nodes, mode); + function reloadGridSelection(mode, nodes) { + if (!nodes) + nodes = datagrid.selection.getSelectedNodes(); + var reloadLast = []; nodes.forEach(function(node) { + var id; if (node.packageConfig) { + var config = node.packageConfig; if (!mode) - unloadPackage(node.packageConfig.name); + unloadPackage(config.name); if (mode != false) - loadPackage(node.packageConfig.filePath); + loadPackage(config.filePath || config.url); + id = config.name; } else { if (!mode) unloadPlugins({ path: node.path }); if (mode != false) loadPlugins({ path: node.path }); + id = node.path; } + + reloadLast.push(id); + if (mode == false) + disabledPlugins[id] = true; + else + delete disabledPlugins[id]; + }); + if (reloadLast.length && mode == null) { + var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); + href += (href.match(/\?/) ? "&" : "?") + "reload=" + reloadLast.join(","); + window.history.replaceState(window.history.state, null, href); + + showReloadTip(); + updateReloadLastButton(); + } + } + + function checkPluginsWithMissingDependencies() { + var services = architectApp.services; + var plugins = []; + getAllPlugins().forEach(function(p) { + if (!p.provides.length) return; + var packagePath = p.packagePath; + var isDisabled = p.provides.every(function(name) { + if (!services[name]) return true; + if (!services[name].loaded && typeof services[name].load == "function") + return true; + }); + if (isDisabled && packagePath) { + var packageName = packagePath.split("/")[1]; + if (disabledPlugins[packageName]) return; + while (packagePath && !disabledPlugins[packagePath]) { + var i = packagePath.lastIndexOf("/"); + packagePath = packagePath.slice(0, i > 0 ? i : 0); + } + if (!packagePath) plugins.push(p); + } + }); + if (!plugins.length) return; + architectApp.loadAdditionalPlugins(plugins, function(err) { + if (err) return showError(err); }); } @@ -938,7 +992,7 @@ define(function(require, exports, module) { } } - function showReloadTip(name) { + function showReloadTip() { if (options.devel) { var key = commands.getHotkey("reloadLastPlugin"); if (commands.platform == "mac") @@ -955,13 +1009,13 @@ define(function(require, exports, module) { return qs.parse(document.location.search.substr(1)).reload; } - function getAllPlugins() { + function getAllPlugins(includeDisabled) { var config = architectApp.config; Object.keys(packages).forEach(function(n) { if (packages[n] && packages[n].c9 && packages[n].c9.plugins) config = config.concat(packages[n].c9.plugins); }); - return config.filter(function(x) { + return includeDisabled ? config : config.filter(function(x) { return x.consumes && x.provides; }); } @@ -1027,11 +1081,17 @@ define(function(require, exports, module) { return async.map(options, loadPackage, callback || function() {}); if (typeof options == "string") { - options = /^https?:/.test(options) - ? { url: options } - : { path: options }; + if (/^https?:/.test(options)) { + options = { url: options }; + } else if (/^[~\/]/.test(options)) { + options = { path: options }; + } else if (/^[~\/]/.test(options)) { + options = { url: require.toUrl(options) }; + } } + var url = options.url; + if (!options.url && options.path) options.url = vfs.url(options.path); @@ -1040,13 +1100,19 @@ define(function(require, exports, module) { options.url = parts.join("/"); if (!options.name) { - options.name = parts[parts.length - 1]; - if (options.name == "build") + // try to find the name from file name + options.name = /^package\.(.*)\.js$|$/.exec(root)[1]; + // try folder name + if (!options.name || options.name == "json") + options.name = parts[parts.length - 1]; + // try parent folder name + if (/^(.?build|master|c9build)/.test(options.name)) options.name = parts[parts.length - 2]; + // remove version from the name options.name = options.name.replace(/@.*$/, ""); } if (!options.packageName) - options.packageName = root; + options.packageName = root.replace(/\.js$/, ""); if (!options.rootDir) options.rootDir = "plugins"; @@ -1086,6 +1152,8 @@ define(function(require, exports, module) { return addError("Error parsing package.json", e); } json.fromVfs = true; + // handle the old format + if (!json.c9) loadBundleFiles(json, options); getPluginsFromPackage(json, callback); }); } @@ -1109,7 +1177,7 @@ define(function(require, exports, module) { if (!packages[name]) packages[name] = {}; packages[name].filePath = options.path; - packages[name].url = options.url; + packages[name].url = url; packages[name].__error = new Error(message + "\n" + err.message); packages[name].loading = false; @@ -1122,10 +1190,10 @@ define(function(require, exports, module) { var plugins = []; if (json.name != name) json.name = name; - - if (json.plugins) { - Object.keys(json.plugins).forEach(function(name) { - var plugin = json.plugins[name]; + var unhandledPlugins = json.c9 && json.c9.plugins || json.plugins; + if (unhandledPlugins) { + Object.keys(unhandledPlugins).forEach(function(name) { + var plugin = unhandledPlugins[name]; if (typeof plugin == "string") plugin = { packagePath: plugin }; if (!plugin.packagePath) @@ -1137,10 +1205,11 @@ define(function(require, exports, module) { packages[json.name] = json; json.filePath = options.path; - json.url = options.url; + json.url = url; if (!json.c9) json.c9 = {}; + json.c9.plugins = plugins; json.enabled = true; json.path = id; @@ -1154,15 +1223,109 @@ define(function(require, exports, module) { callback && callback(err, result); }); } + + function loadBundleFiles(json, options, callback) { + var cwd = dirname(options.path); + var resourceHolder = new Plugin(); + fs.readdir(cwd, function(err, files) { + if (err) return callback && callback(err); + function forEachFile(dir, fn) { + fs.readdir(dir, function(err, files) { + if (err) return callback && callback(err); + files.forEach(function(stat) { + fs.readFile(dir + "/" + stat.name, function(err, value) { + if (err) return callback && callback(err); + fn(stat.name, value); + }); + }); + }); + } + function parseHeader(data, filename) { + var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); + var info = {}; + firstLine.split(";").forEach(function(n) { + var key = n.split(":"); + if (key.length != 2) + return console.error("Ignoring invalid key " + n + " in " + filename); + info[key[0].trim()] = key[1].trim(); + }); + info.data = firstLine; + return info; + } + function addResource(type) { + forEachFile(cwd + "/" + type, function(filename, data) { + addStaticPlugin(type, options.name, filename, data, plugin); + + }); + } + function addMode(type) { + forEachFile(cwd + "/modes", function(filename, data) { + if (/(?:_highlight_rules|_test|_worker|_fold|_behaviou?r)\.js$/.test(filename)) + return; + if (!/\.js$/.test(filename)) + return; + var info = parseHeader(data, cwd + "/modes/" + filename); + + if (!info.caption) info.caption = filename; + + info.type = "modes"; + info.filename = filename; + addStaticPlugin(type, options.name, filename, data, plugin); + }); + } + var handlers = { + templates: addResource, + snippets: addResource, + builders: addResource, + keymaps: addResource, + outline: addResource, + runners: addResource, + themes: addResource, + modes: addMode, + }; + files.forEach(function(stat) { + var type = stat.name; + if (handlers.hasOwnProperty(type)) + handlers[type](type); + }); + + var name = options.name + ".bundle"; + var bundle = { + packagePath: id + "/" + name, + consumes: [], + provides: [name], + setup: function(imports, options, register) { + var ret = {}; + ret[name] = resourceHolder; + register(null, ret); + } + }; + json.c9.plugins.push(bundle); + architectApp.loadAdditionalPlugins([bundle], function() {}); + }); + } } function loadPlugins(plugins, callback) { + if (!Array.isArray(plugins)) { + options = plugins; + plugins = []; + Object.keys(getServiceNamesByPath(options)).forEach(function(n) { + var plugin = architectApp.serviceToPlugin[n]; + if (plugin && plugin.packagePath) { + unloadPluginConfig(plugin); + plugins.push(plugin); + } + }); + } + architectApp.loadAdditionalPlugins(plugins, function(err) { + setTimeout(checkPluginsWithMissingDependencies); callback && callback && callback(err); }); } - function unloadPlugins(options, callback) { + function getServiceNamesByPath(options) { var toUnload = Object.create(null); var config = getAllPlugins(); function addPath(path) { @@ -1189,7 +1352,11 @@ define(function(require, exports, module) { toUnload[name] = 0; }); } - + return toUnload; + } + + function unloadPlugins(options, callback) { + var toUnload = getServiceNamesByPath(options); addAllDependents(toUnload); var services = architectApp.services; @@ -1225,19 +1392,19 @@ define(function(require, exports, module) { reloadModel(); } - function cleanupCache(packagePath) { - var options = architectApp.pathToPackage[packagePath]; - var url = require.toUrl(options.packagePath, ".js"); + function unloadPluginConfig(plugin) { + var url = requirejs.toUrl(plugin.packagePath, ".js"); if (!define.fetchedUrls[url]) return false; - delete options.provides; - delete options.consumes; - delete options.setup; + delete plugin.provides; + delete plugin.consumes; + delete plugin.setup; - requirejs.undef(options.packagePath); + requirejs.undef(plugin.packagePath); return true; } + // TODO optimize this function addStaticPlugin(type, pluginName, filename, data, plugin) { var services = architectApp.services; var path = "plugins/" + pluginName + "/" @@ -1264,14 +1431,14 @@ define(function(require, exports, module) { var mode = {}; var firstLine = data.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); firstLine.split(";").forEach(function(n) { - if (!n) return; var info = n.split(":"); + if (info.length != 2) return; mode[info[0].trim()] = info[1].trim(); }); services.ace.defineSyntax({ name: path, - caption: mode.caption, + caption: mode.caption || filename, extensions: (mode.extensions || "").trim() .replace(/\s*,\s*/g, "|").replace(/(^|\|)\./g, "$1") }); @@ -1298,7 +1465,20 @@ define(function(require, exports, module) { services.newresource.addFileTemplate(data, plugin); break; case "installer": - console.error("Installer is not supported."); + if (data) { + services.installer.createSession(pluginName, data, function(v, o) { + require([path], function(fn) { + fn(v, o); + }); + }); + } + else { + require([path], function(fn) { + services.installer.createSession(pluginName, fn.version, function(v, o) { + fn(v, o); + }); + }); + } default: console.error("Unsupported type", type); } @@ -1377,6 +1557,7 @@ define(function(require, exports, module) { }); var shim = new Plugin(); + shim.addStaticPlugin = addStaticPlugin; register(null, { "pluginManager": plugin, diff --git a/plugins/c9.ide.save/save.js b/plugins/c9.ide.save/save.js index c8eed027..c52361af 100644 --- a/plugins/c9.ide.save/save.js +++ b/plugins/c9.ide.save/save.js @@ -98,7 +98,6 @@ define(function(require, exports, module) { name: "reverttosavedall", hint: "downgrade the all open tabs to the last saved version", bindKey: { mac: "Option-Shift-Q", win: "Alt-Shift-Q" }, - isAvailable: available, exec: function () { revertToSavedAll(); } From 5acd862c43ae4371b43d6c9fc95c76df1ccce85a Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 1 May 2017 18:46:15 +0400 Subject: [PATCH 10/15] move ui code for pluginManager into separate plugin --- configs/ide/default.js | 1 + plugins/c9.ide.plugins/gui.js | 923 +++++++++++++++++++++++++++++ plugins/c9.ide.plugins/manager.js | 925 +++--------------------------- 3 files changed, 988 insertions(+), 861 deletions(-) create mode 100644 plugins/c9.ide.plugins/gui.js diff --git a/configs/ide/default.js b/configs/ide/default.js index 15af4529..cb53d374 100644 --- a/configs/ide/default.js +++ b/configs/ide/default.js @@ -88,6 +88,7 @@ module.exports = function(options) { staticPrefix: staticPrefix + "/plugins/c9.ide.plugins", devel: devel }, + "plugins/c9.ide.plugins/gui", // { // packagePath: "plugins/c9.ide.plugins/test", // staticPrefix: staticPrefix + "/plugins/c9.ide.plugins" diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js new file mode 100644 index 00000000..d35aae9c --- /dev/null +++ b/plugins/c9.ide.plugins/gui.js @@ -0,0 +1,923 @@ +define(function(require, exports, module) { + main.consumes = [ + "PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin", + "layout", "proc", "menus", "commands", "pluginManager", + "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", + "preferences.experimental", "apf", "hub", "dialog.notification" + ]; + main.provides = ["pluginManagerUi"]; + return main; + + + function main(options, imports, register) { + var PreferencePanel = imports.PreferencePanel; + var settings = imports.settings; + var layout = imports.layout; + var commands = imports.commands; + var menus = imports.menus; + var ui = imports.ui; + var c9 = imports.c9; + var fs = imports.fs; + var ext = imports.ext; + var util = imports.util; + var apf = imports.apf; + var showError = imports["dialog.error"].show; + var showInfo = imports["dialog.info"].show; + var notify = imports["dialog.notification"].show; + var experimental = imports["preferences.experimental"]; + var pluginManager = imports.pluginManager; + var architectApp = imports.hub.app; + + var search = require("../c9.ide.navigate/search"); + var Tree = require("ace_tree/tree"); + var TreeData = require("./managerdp"); + var qs = require("querystring"); + + var escapeHTML = require("ace/lib/lang").escapeHTML; + + var CORE = {}; + var TEMPLATES = { + "plugin.simple": "Empty Plugin", + "plugin.default": "Full Plugin", + "plugin.installer": "Installer Plugin", + "plugin.bundle": "Cloud9 Bundle" + }; + + /***** Initialization *****/ + + var DEBUG = c9.location.indexOf("debug=2") > -1; + var ENABLED = DEBUG || experimental.addExperiment( + "plugin-manager", + options.devel, + "SDK/Plugin Manager" + ); + + var plugin = new PreferencePanel("Ajax.org", main.consumes, { + caption: "Plugin Explorer", + className: "plugins", + form: false, + noscroll: true, + index: 200, + visible: ENABLED, + }); + var emit = plugin.getEmitter(); + + var model; + var datagrid; + var filterbox; + var btnInstall; + var btnUninstall; + var btnServices; + var btnReadme; + var btnReloadLast; + var localPlugins; + + var loaded = false; + function load() { + if (loaded) return false; + loaded = true; + + menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); + menus.addItemByPath("Tools/Developer", null, 100100, plugin); + + if (!DEBUG) { + menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ + onclick: function() { + var url = location.href + (location.href.indexOf("?") > -1 + ? "&debug=2" + : "?debug=2"); + util.openNewWindow(url); + } + }), 900, plugin); + } + if (!ENABLED) { + return; + } + + commands.addCommand({ + name: "openPluginManager", + group: "Plugins", + exec: function() { + commands.exec("openpreferences", null, { panel: plugin }); + } + }, plugin); + + menus.addItemByPath("Tools/Developer/Open Plugin Explorer", new ui.item({ + command: "openPluginManager" + }), 1100, plugin); + + if (DEBUG) { + notify("
You are in Debug Mode. " + + "" + + "", + false); + + document.getElementById(".pm_btn1").onclick = function() { commands.exec("openPluginManager") }; + btnReloadLast = document.getElementById(".pm_btn2"); + btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") }; + updateReloadLastButton(); + + pluginManager.readAvailablePlugins(function(err, available) { + if (err) return console.error(err); + localPlugins = available; + reloadModel(); + if (sessionStorage.localPackages) { + pluginManager.loadPackage( + util.safeParseJson(sessionStorage.localPackages) || [] + ); + } + var updateSessionStorage = function() { + var packages = pluginManager.packages; + sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) { + if (packages[x] && packages[x].fromVfs && packages[x].enabled) + return packages[x].filePath; + }).filter(Boolean)); + }; + pluginManager.on("enablePackage", updateSessionStorage); + pluginManager.on("disablePackage", updateSessionStorage); + }); + + commands.addCommand({ + name: "reloadLastPlugin", + bindKey: { mac: "F4", win: "F4" }, + hint: "reload plugin last reloaded in plugin manager", + exec: function() { + var names = getLastReloaded(); + if (!names) + return commands.exec("openPluginManager", null, { panel: plugin }); + reload(names); + } + }, plugin); + + menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({ + command: "reloadLastPlugin", + isAvailable: getLastReloaded + }), 1300, plugin); + } + + menus.addItemByPath("File/New Plugin", null, 210, plugin); + Object.keys(TEMPLATES).forEach(function(name) { + menus.addItemByPath("File/New Plugin/" + TEMPLATES[name], new ui.item({ + onclick: function() { + pluginManager.createNewPlugin(name); + } + }), 210, plugin); + }); + } + + var drawn; + function draw(e) { + if (drawn) return; + drawn = true; + + function scheduleReloadModel() { + if (scheduleReloadModel.timer) return; + scheduleReloadModel.timer = setTimeout(function() { + scheduleReloadModel.timer = null; + reloadModel(); + }); + } + ext.on("register", scheduleReloadModel); + pluginManager.on("change", scheduleReloadModel); + pluginManager.on("enablePackage", scheduleReloadModel); + pluginManager.on("disablePackage", scheduleReloadModel); + + ui.insertCss(require("text!./style.css"), plugin); + + model = new TreeData(); + model.emptyMessage = "No plugins found"; + + model.columns = [{ + caption: "Name", + value: "name", + width: "250", + type: "tree" + }, { + caption: "Startup Time", + // value: "time", + width: "100", + getText: function(p) { + if (p.time !== undefined) + return (p.time || 0) + "ms"; + + var total = 0; + function recur(p) { + if (p.time != undefined) + return total += p.time; + if (p.items) + p.items.forEach(recur); + } + recur(p); + return (p.time = total) + "ms"; + } + }, { + caption: "Enabled", + value: "enabled", + width: "100" + }]; + model.columns = null; + + layout.on("eachTheme", function(e) { + var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24; + model.rowHeightInner = height; + model.rowHeight = height; + + if (e.changed) datagrid.resize(true); + }); + + architectApp.on("ready-additional", function() { + reloadModel(); + }); + reloadModel(); + + var hbox = new ui.hbox({ + htmlNode: e.html, + padding: 5, + edge: "10 10 0 10", + childNodes: [ + filterbox = new apf.codebox({ + realtime: true, + skin: "codebox", + "class": "dark", + clearbutton: true, + focusselect: true, + height: 27, + width: 250, + singleline: true, + "initial-message": "Search installed plugins" + }), + btnReadme = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Readme", + class: "serviceButton", + onclick: function() { + mode = "readme"; + scheduleRedraw(); + } + }), + btnServices = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "services", + class: "serviceButton", + onclick: function() { + mode = "services"; + scheduleRedraw(); + } + }), + new ui.filler({}), + btnInstall = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Enable", + class: "btn-red", + onclick: function() { + reloadGridSelection(true); + } + }), + btnUninstall = new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Disable", + class: "btn-red", + onclick: function() { + reloadGridSelection(false); + } + }), + new ui.button({ + skin: "c9-toolbarbutton-glossy", + caption: "Reload", + onclick: function() { + reloadGridSelection(); + } + }) + ] + }); + var treeBar; + var descriptionBar; + var hboxInner = new ui.hsplitbox({ + anchor: "0 0 0 0", + htmlNode: e.html, + class: "bar-preferences", + splitter: true, + childNodes: [ + treeBar = new ui.bar({ width: 250 }), + descriptionBar = new ui.bar({ textselect: true }), + ] + }); + + var div = treeBar.$ext.appendChild(document.createElement("div")); + div.style.position = "absolute"; + div.style.left = "0px"; + div.style.right = "0px"; + div.style.bottom = "0px"; + div.style.top = "0px"; + hboxInner.$ext.style.position = "absolute"; + hboxInner.$ext.style.left = "10px"; + hboxInner.$ext.style.right = "10px"; + hboxInner.$ext.style.bottom = "10px"; + hboxInner.$ext.style.top = "50px"; + + datagrid = new Tree(div); + datagrid.setTheme({ cssClass: "blackdg" }); + datagrid.setDataProvider(model); + + layout.on("resize", function() { datagrid.resize() }, plugin); + + function setTheme(e) { + filterbox.setAttribute("class", + e.theme.indexOf("dark") > -1 ? "dark" : ""); + } + layout.on("themeChange", setTheme); + setTheme({ theme: settings.get("user/general/@skin") }); + + filterbox.ace.commands.addCommands([ + { + bindKey: "Enter", + exec: function() { } + }, { + bindKey: "Esc", + exec: function(ace) { ace.setValue(""); } + } + ]); + + filterbox.ace.on("input", function(e) { + applyFilter(); + }); + + // when tab is restored datagrids size might be wrong + // todo: remove this when apf bug is fixed + datagrid.once("mousemove", function() { + datagrid.resize(true); + }); + + datagrid.on("changeSelection", scheduleRedraw); + model.on("change", scheduleRedraw); + + plugin.on("reloadModel", scheduleRedraw); + + model.getCheckboxHTML = function(node) { + var enabled = node.enabled; + if (enabled == null || node.isGroup) return ""; + return ""; + }; + + var mode = "services"; + var readmeCache = {}; + function renderDetails() { + var items = datagrid.selection.getSelectedNodes(); + var hasEnabled = 0; + var hasDisabled = 0; + items.forEach(function(x) { + if (x.enabled != 0) + hasEnabled = true; + if (x.enabled != 1) + hasDisabled = true; + }); + btnUninstall.setProperty("visible", hasEnabled); + btnInstall.setProperty("visible", hasDisabled); + + if (mode == "services") { + renderServiceDetails(items); + btnServices.$ext.classList.add("serviceButtonActive"); + btnReadme.$ext.classList.remove("serviceButtonActive"); + } + else if (mode == "readme") { + renderReadmeDetails(items); + btnReadme.$ext.classList.add("serviceButtonActive"); + btnServices.$ext.classList.remove("serviceButtonActive"); + } + else { + descriptionBar.$ext.innerHTML = ""; + btnReadme.$ext.classList.remove("serviceButtonActive"); + btnServices.$ext.classList.remove("serviceButtonActive"); + } + + if (items.length == 1 && items[0].__error) { + var errorContainer = document.createElement("div"); + errorContainer.style.cssText = "padding: 5px; white-space: pre-wrap"; + errorContainer.textContent = items[0].__error.message; + descriptionBar.$ext.insertBefore(errorContainer, descriptionBar.$ext.firstChild); + } + } + + function renderReadmeDetails(items) { + if (items.length > 1) { + descriptionBar.$ext.textContent = "Multipe items selected."; + return; + } + var item = items[0]; + while (item && !item.packageConfig) { + item = item.parent; + } + if (!item) { + descriptionBar.$ext.textContent = "Readme is not available."; + return; + } + + if (readmeCache[item.name]) { + var div = document.createElement("div"); + div.textContent = readmeCache[item.name]; + div.style.cssText = "padding: 5px; white-space: pre-wrap"; + descriptionBar.$ext.textContent = ""; + descriptionBar.$ext.appendChild(div); + return; + } + descriptionBar.$ext.textContent = "Loading..."; + if (item.packageConfig.filePath) { + var parts = item.packageConfig.filePath.split("/"); + parts.pop(); + parts.push("README.md"); + var path = parts.join("/"); + fs.readFile(path, function(e, v) { + if (e) + return readmeCache[item.name] = "Readme is not available."; + readmeCache[item.name] = v; + renderDetails(); + }); + } + } + + function renderServiceDetails(items) { + function addUnique(item, array) { + if (array.indexOf(item) == -1) array.push(item); + } + + var provided = []; + + items.forEach(function addProvides(x) { + if (x.map) { + Object.keys(x.map).forEach(function(n) { + addProvides(x.map[n]); + }); + } + if (x.provides) { + x.provides.forEach(function(p) { + addUnique(p, provided); + }); + } + }); + + var consumedMap = Object.create(null); + provided.forEach(function(service) { + consumedMap[service] = 0; + }); + pluginManager.addAllProviders(consumedMap); + var consumedList = Object.keys(consumedMap); + var consumedGroups = splitToGroups(consumedList, consumedMap); + + var dependents = Object.create(null); + provided.forEach(function(service) { + dependents[service] = 0; + }); + pluginManager.addAllDependents(dependents); + var depList = Object.keys(dependents); + var depGroups = splitToGroups(depList, dependents); + + function splitToGroups(list, map) { + var groups = []; + list.forEach(function(n) { + var level = Math.abs(map[n]); + if (!groups[level]) + groups[level] = []; + groups[level].push(n); + }); + groups = groups.filter(Boolean).slice(1); + return groups; + } + + function formatServices(list, style) { + return "[" + list.map(function(x) { + return '' + escapeHTML(x) + ''; + }).join(", ") + "]"; + } + + descriptionBar.$ext.style.overflow = "auto"; + var serviceDesc = '

Provided services [' + provided.length + ']

\ +

' + (provided.length ? formatServices(provided) : "") + '

\ +

Consumed services [' + (consumedList.length - provided.length) + ']

\ +

' + consumedGroups.map(formatServices).join("
") + '

\ +

Dependent services [' + (depList.length - provided.length) + ']

\ +

' + depGroups.map(formatServices).join("
") + '

\ +
'; + descriptionBar.$ext.innerHTML = '
\ +

' + (items.length == 1 + ? '' + escapeHTML(items[0].path) + '' + : (items.length || "no") + " plugins selected") + '

\ +
' + + (items.length ? serviceDesc : "") + + '
'; + + } + descriptionBar.$ext.addEventListener("click", function(e) { + if (e.button) return; + var el = e.target; + var isButton = el.classList.contains("serviceButton"); + var text = el.textContent; + var serviceToPlugin = architectApp.serviceToPlugin; + if (e.detail == 1 && isButton && text) { + var path = serviceToPlugin[text] ? serviceToPlugin[text].packagePath : text; + var node = function search(node) { + if (node.path == path) + return node; + if (node.items) { + for (var i = 0; i < node.items.length; i++) { + var result = search(node.items[i]); + if (result) + return result; + } + } + }(model.cachedRoot); + + if (node) + datagrid.reveal(node); + } + }); + var hoverDetails = { hovered: "", highlighted: "" }; + descriptionBar.$ext.addEventListener("mousemove", function(e) { + if (e.button) return; + var el = e.target; + var isButton = el.classList.contains("serviceButton"); + var text = el.textContent; + hoverDetails.parent = el.parentNode; + hoverDetails.hovered = isButton ? text : ""; + hoverDetails.type = isButton ? el.parentNode.getAttribute("type") : ""; + if (hoverDetails.hovered != hoverDetails.highlighted) + requestAnimationFrame(updateHighlights); + }); + function updateHighlights() { + if (hoverDetails.hovered == hoverDetails.highlighted) return; + var serviceToPlugin = architectApp.serviceToPlugin; + if (!serviceToPlugin) return; + + var nodes = descriptionBar.$ext.querySelectorAll(".serviceButton"); + + var deps = Object.create(null); + deps[hoverDetails.hovered] = 0; + pluginManager.addAllDependents(deps); + pluginManager.addAllProviders(deps); + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var val = node.textContent; + var highlight1 = false; + var highlight2 = false; + if (hoverDetails.type == "dependent") { + if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { + highlight1 = deps[val] < 0; + highlight2 = deps[val] > 0; + } + } else if (hoverDetails.type == "consumed") { + if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { + highlight1 = deps[val] > 0; + highlight2 = deps[val] < 0; + } + } + + nodes[i].style.borderBottom = highlight1 ? "1px solid" : ""; + if (!highlight1) { + nodes[i].style.borderBottom = highlight2 ? "1px dashed rgba(125, 125, 125, 0.9)" : ""; + } + } + hoverDetails.highlighted = hoverDetails.hovered; + } + + function scheduleRedraw() { + window.requestAnimationFrame(renderDetails); + } + + var mnuCtxTree = new ui.menu({ + id: "mnuChat", + }, plugin); + menus.decorate(mnuCtxTree); + plugin.addElement(mnuCtxTree); + menus.addItemByPath("context/pluginManager/", mnuCtxTree, 0, plugin); + menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.packageConfig && selected.packageConfig.filePath; + }, + onclick: function() { + var selected = datagrid.selection.getCursor(); + var tabbehavior = architectApp.services.tabbehavior; + var filePath = selected.packageConfig && selected.packageConfig.filePath; + if (filePath) { + tabbehavior.revealtab({ path: filePath }); + } + }, + }), plugin); + menus.addItemByPath("context/pluginManager/Disable", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.enabled != 0; + }, + onclick: function() { reloadGridSelection(false); }, + }), plugin); + menus.addItemByPath("context/pluginManager/Enable", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected && selected.enabled != 1; + }, + onclick: function() { reloadGridSelection(true); }, + }), plugin); + menus.addItemByPath("context/pluginManager/Reload", new ui.item({ + onclick: function() { reloadGridSelection(); }, + }), plugin); + treeBar.setAttribute("contextmenu", mnuCtxTree); + } + + + /***** Methods *****/ + + function reloadModel() { + if (!model) return; + + var packages = pluginManager.packages; + + if (!CORE.pluginManager) { + CORE.pluginManager = 1; + pluginManager.addAllProviders(CORE); + Object.keys(CORE).forEach(function(n) { + if (architectApp.serviceToPlugin[n]) + CORE[architectApp.serviceToPlugin[n].packagePath] = 1; + }); + } + + var GROUPS = { + // "changed": "Recently Changed", + "remote": "Remote Plugins", + "vfs": "Locally Installed Plugins", + "pre": "Pre-installed Plugins", + "core": "Core Plugins", + }; + + var groups = model.groups || Object.create(null); + if (!model.groups) { + var root = []; + model.cachedRoot = { items: root }; + model.groups = groups; + Object.keys(GROUPS).forEach(function(name) { + root.push(groups[name] = { + map: Object.create(null), + isOpen: name != "runtime", + className: "group", + isGroup: true, + isType: name, + noSelect: true, + name: GROUPS[name] + }); + }); + } + + function addPlugin(plugin, node) { + if (!plugin.provides || !plugin.consumes) + return; + if (plugin.packagePath) { + var parts = plugin.packagePath.split("/"); + + var path = parts.shift(); + parts.forEach(function(p, i) { + path = path + "/" + p; + if (!node.map) + node.map = Object.create(null); + if (!node.map[p]) { + node.map[p] = { + path: path, + parent: node, + }; + } + node.map[p].name = p; + if (i == parts.length - 1) { + var enabled = 1; + node.map[p].provides = plugin.provides; + plugin.provides.forEach(function(x) { + var service = architectApp.services[x]; + if (!service || (!service.loaded && service.unload)) { + enabled = 0; + } + }); + node.map[p].enabled = enabled; + } + node = node.map[p]; + node.className = plugin.__error ? "load-error" : ""; + node.__error = plugin.__error; + }); + } + } + + function addPackage(name) { + var pkg = packages[name]; + var parent = pkg.filePath ? groups.vfs : groups.remote; + + var node = parent.map[name] = parent.map[name] || { + path: "plugins/" + name, + name: name, + enabled: 0, + parent: parent, + }; + + node.packageConfig = pkg; + node.className = pkg.__error ? "load-error" : ""; + node.__error = pkg.__error; + node.loading = pkg.loading; + } + + architectApp.config.forEach(function(plugin) { + var node = CORE[plugin.packagePath] ? groups.core : groups.pre; + addPlugin(plugin, node); + }); + + if (localPlugins) { + localPlugins.forEach(function(name) { + if (!packages[name]) { + packages[name] = { + name: name, + filePath: "~/.c9/plugins/" + name + "/package.json", + }; + } + + }); + } + + Object.keys(packages).forEach(function(n) { + var pkg = packages[n]; + var node = pkg.filePath ? groups.vfs : groups.remote; + + addPackage(n); + + if (pkg.c9 && pkg.c9.plugins) { + pkg.c9.plugins.forEach(function(plugin) { + addPlugin(plugin, node); + }); + } + }); + + function flatten(node, index) { + if (node.map) { + node.items = node.children = Object.keys(node.map).map(function(x) { + return node.map[x]; + }); + } + if (node.items) { + node.items.forEach(flatten); + if (node.items.length == 1 && !node.isGroup) { + var other = node.items[0]; + if (node.parent && node.parent.items[index] == node) { + node.parent.items[index] = other; + other.name = node.name + "/" + other.name; + other.packageConfig = node.packageConfig; + other.filePath = node.filePath; + other.url = node.url; + other.loading = node.loading; + } + } + if (!node.isGroup) { + node.enabled = null; + node.items.some(function(i) { + if (node.enabled == null) { + node.enabled = i.enabled; + } + if (i.enabled != node.enabled) { + node.enabled = -1; + return true; + } + }); + } + } + } + + flatten(model.cachedRoot); + + applyFilter(); + + emit("reloadModel"); + } + + function applyFilter() { + model.keyword = filterbox && filterbox.getValue(); + + if (!model.keyword) { + model.reKeyword = null; + model.setRoot(model.cachedRoot); + + // model.isOpen = function(node) { return node.isOpen; } + } + else { + model.reKeyword = new RegExp("(" + + util.escapeRegExp(model.keyword) + ")", 'i'); + var root = search.treeSearch(model.cachedRoot.items, model.keyword, true); + model.setRoot(root); + + // model.isOpen = function(node) { return true; }; + } + } + + function reload(names) { + var nodes = names.split(/\s*,\s*/).map(function(name) { + if (pluginManager.packages[name]) + return { packageConfig: pluginManager.packages[name] }; + return { path: name }; + }); + + reloadGridSelection(null, nodes); + } + + function reloadGridSelection(mode, nodes) { + if (!nodes) + nodes = datagrid.selection.getSelectedNodes(); + + var reloadLast = pluginManager.reload(nodes, mode); + if (reloadLast.length && mode == null) { + var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); + href += (href.match(/\?/) ? "&" : "?") + "reload=" + reloadLast.join(","); + window.history.replaceState(window.history.state, null, href); + + showReloadTip(); + updateReloadLastButton(); + } + } + + + function updateReloadLastButton() { + var last = getLastReloaded(); + if (last) { + btnReloadLast.visible = true; + btnReloadLast.textContent = "Reload " + last; + } else { + btnReloadLast.visible = false; + } + } + + function showReloadTip(name) { + if (options.devel) { + var key = commands.getHotkey("reloadLastPlugin"); + if (commands.platform == "mac") + key = apf.hotkeys.toMacNotation(key); + if (!getLastReloaded()) { + showInfo("Reloaded " + name + ". Press " + key + " to reload again.", 3000); + return; + } + } + showInfo("Reloaded " + name + ".", 1000); + } + + function getLastReloaded() { + return qs.parse(document.location.search.substr(1)).reload; + } + + + /***** Lifecycle *****/ + + plugin.on("load", function() { + load(); + }); + plugin.on("draw", function(e) { + draw(e); + }); + plugin.on("activate", function(e) { + datagrid && datagrid.resize(); + }); + plugin.on("resize", function(e) { + datagrid && datagrid.resize(); + }); + plugin.on("enable", function() { + + }); + plugin.on("disable", function() { + + }); + plugin.on("unload", function() { + loaded = false; + drawn = false; + btnServices = null; + btnReadme = null; + btnReloadLast = null; + model = null; + datagrid = null; + filterbox = null; + btnInstall = null; + btnUninstall = null; + localPlugins = null; + }); + + /***** Register and define API *****/ + + /** + * + **/ + plugin.freezePublicAPI({ + + /* + * @ignore + */ + get datagrid() { return datagrid; }, + }); + + register(null, { + "pluginManagerUi": plugin, + }); + } +}); diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index ab22de35..99dc0c9c 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -1,22 +1,14 @@ /*global requirejs*/ define(function(require, exports, module) { main.consumes = [ - "PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin", - "layout", "proc", "menus", "commands", - "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", - "preferences.experimental", "apf", "hub", "dialog.notification" + "hub", "ext", "c9", "Plugin", "proc", "fs", "tree", "vfs", + "dialog.error", "tree.favorites" ]; main.provides = ["pluginManager", "plugin.manager", "plugin.debug"]; return main; function main(options, imports, register) { - var PreferencePanel = imports.PreferencePanel; - var settings = imports.settings; - var layout = imports.layout; - var commands = imports.commands; - var menus = imports.menus; - var ui = imports.ui; var c9 = imports.c9; var fs = imports.fs; var ext = imports.ext; @@ -24,29 +16,18 @@ define(function(require, exports, module) { var proc = imports.proc; var Plugin = imports.Plugin; var util = imports.util; - var qs = require("querystring"); - var apf = imports.apf; var vfs = imports.vfs; var showError = imports["dialog.error"].show; - var showInfo = imports["dialog.info"].show; - var notify = imports["dialog.notification"].show; var favs = imports["tree.favorites"]; - var experimental = imports["preferences.experimental"]; var architectApp = imports.hub.app; - var search = require("../c9.ide.navigate/search"); - var Tree = require("ace_tree/tree"); - var TreeData = require("./managerdp"); var join = require("path").join; var basename = require("path").basename; var dirname = require("path").dirname; var async = require("async"); - var escapeHTML = require("ace/lib/lang").escapeHTML; - var staticPrefix = options.staticPrefix; - var CORE = {}; var TEMPLATES = { "plugin.simple": "Empty Plugin", "plugin.default": "Full Plugin", @@ -54,583 +35,14 @@ define(function(require, exports, module) { "plugin.bundle": "Cloud9 Bundle" }; - var STATE_ENABLED = 1; - var STATE_PARTIAL = 2; - var STATE_MISSING_DEPS = 3; - var STATE_DISABLED = 4; - /***** Initialization *****/ - var DEBUG = c9.location.indexOf("debug=2") > -1; - var ENABLED = DEBUG || experimental.addExperiment( - "plugin-manager", - options.devel, - "SDK/Plugin Manager" - ); - - var plugin = new PreferencePanel("Ajax.org", main.consumes, { - caption: "Plugin Explorer", - className: "plugins", - form: false, - noscroll: true, - index: 200, - visible: ENABLED, - }); + var plugin = new Plugin(); var emit = plugin.getEmitter(); - var model, datagrid, filterbox; - var btnInstall, btnUninstall, btnServices, btnReadme, btnReload; - var btnReloadLast; - var localPlugins; var disabledPlugins = Object.create(null); var packages = Object.create(null); - var loaded = false; - function load() { - if (loaded) return false; - loaded = true; - - menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin); - menus.addItemByPath("Tools/Developer", null, 100100, plugin); - - if (!DEBUG) { - menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ - onclick: function() { - var url = location.href + (location.href.indexOf("?") > -1 - ? "&debug=2" - : "?debug=2"); - util.openNewWindow(url); - } - }), 900, plugin); - } - if (!ENABLED) { - return; - } - - commands.addCommand({ - name: "openPluginManager", - group: "Plugins", - exec: function() { - commands.exec("openpreferences", null, { panel: plugin }); - } - }, plugin); - - menus.addItemByPath("Tools/Developer/Open Plugin Explorer", new ui.item({ - command: "openPluginManager" - }), 1100, plugin); - - if (DEBUG) { - notify("
You are in Debug Mode. " - + "" - + "", - false); - - document.getElementById(".pm_btn1").onclick = function() { commands.exec("openPluginManager") }; - btnReloadLast = document.getElementById(".pm_btn2"); - btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") }; - updateReloadLastButton(); - - readAvailablePlugins(function() { - if (sessionStorage.localPackages) { - loadPackage( - util.safeParseJson(sessionStorage.localPackages) || [] - ); - } - var updateSessionStorage = function() { - sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) { - if (packages[x] && packages[x].fromVfs && packages[x].enabled) - return packages[x].filePath; - }).filter(Boolean)); - }; - plugin.on("enablePackage", updateSessionStorage); - plugin.on("disablePackage", updateSessionStorage); - }); - - commands.addCommand({ - name: "reloadLastPlugin", - bindKey: { mac: "F4", win: "F4" }, - hint: "reload plugin last reloaded in plugin manager", - exec: function() { - var names = getLastReloaded(); - if (!names) - return commands.exec("openPluginManager", null, { panel: plugin }); - reload(names); - } - }, plugin); - - menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({ - command: "reloadLastPlugin", - isAvailable: getLastReloaded - }), 1300, plugin); - } - - menus.addItemByPath("File/New Plugin", null, 210, plugin); - Object.keys(TEMPLATES).forEach(function(name) { - menus.addItemByPath("File/New Plugin/" + TEMPLATES[name], new ui.item({ - onclick: function() { - createNewPlugin(name); - } - }), 210, plugin); - }); - - ext.on("register", function() { - setTimeout(reloadModel); - }); - } - - var drawn; - function draw(e) { - if (drawn) return; - drawn = true; - - ui.insertCss(require("text!./style.css"), plugin); - - model = new TreeData(); - model.emptyMessage = "No plugins found"; - - model.columns = [{ - caption: "Name", - value: "name", - width: "250", - type: "tree" - }, { - caption: "Startup Time", - // value: "time", - width: "100", - getText: function(p) { - if (p.time !== undefined) - return (p.time || 0) + "ms"; - - var total = 0; - function recur(p) { - if (p.time != undefined) - return total += p.time; - if (p.items) - p.items.forEach(recur); - } - recur(p); - return (p.time = total) + "ms"; - } - }, { - caption: "Enabled", - value: "enabled", - width: "100" - }]; - model.columns = null; - - layout.on("eachTheme", function(e) { - var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24; - model.rowHeightInner = height; - model.rowHeight = height; - - if (e.changed) datagrid.resize(true); - }); - - architectApp.on("ready-additional", function() { - reloadModel(); - }); - reloadModel(); - - var hbox = new ui.hbox({ - htmlNode: e.html, - padding: 5, - edge: "10 10 0 10", - childNodes: [ - filterbox = new apf.codebox({ - realtime: true, - skin: "codebox", - "class": "dark", - clearbutton: true, - focusselect: true, - height: 27, - width: 250, - singleline: true, - "initial-message": "Search installed plugins" - }), - btnReadme = new ui.button({ - skin: "c9-toolbarbutton-glossy", - caption: "Readme", - class: "serviceButton", - onclick: function() { - mode = "readme"; - scheduleRedraw(); - } - }), - btnServices = new ui.button({ - skin: "c9-toolbarbutton-glossy", - caption: "services", - class: "serviceButton", - onclick: function() { - mode = "services"; - scheduleRedraw(); - } - }), - new ui.filler({}), - btnInstall = new ui.button({ - skin: "c9-toolbarbutton-glossy", - caption: "Enable", - class: "btn-red", - onclick: function() { - reloadGridSelection(true); - } - }), - btnUninstall = new ui.button({ - skin: "c9-toolbarbutton-glossy", - caption: "Disable", - class: "btn-red", - onclick: function() { - reloadGridSelection(false); - } - }), - btnReload = new ui.button({ - skin: "c9-toolbarbutton-glossy", - caption: "Reload", - onclick: function() { - reloadGridSelection(); - } - }) - ] - }); - var treeBar; - var descriptionBar; - var hboxInner = new ui.hsplitbox({ - anchor: "0 0 0 0", - htmlNode: e.html, - class: "bar-preferences", - splitter: true, - childNodes: [ - treeBar = new ui.bar({ width: 250 }), - descriptionBar = new ui.bar({ textselect: true }), - ] - }); - - var div = treeBar.$ext.appendChild(document.createElement("div")); - div.style.position = "absolute"; - div.style.left = "0px"; - div.style.right = "0px"; - div.style.bottom = "0px"; - div.style.top = "0px"; - hboxInner.$ext.style.position = "absolute"; - hboxInner.$ext.style.left = "10px"; - hboxInner.$ext.style.right = "10px"; - hboxInner.$ext.style.bottom = "10px"; - hboxInner.$ext.style.top = "50px"; - - datagrid = new Tree(div); - datagrid.setTheme({ cssClass: "blackdg" }); - datagrid.setDataProvider(model); - - layout.on("resize", function() { datagrid.resize() }, plugin); - - function setTheme(e) { - filterbox.setAttribute("class", - e.theme.indexOf("dark") > -1 ? "dark" : ""); - } - layout.on("themeChange", setTheme); - setTheme({ theme: settings.get("user/general/@skin") }); - - filterbox.ace.commands.addCommands([ - { - bindKey: "Enter", - exec: function() { } - }, { - bindKey: "Esc", - exec: function(ace) { ace.setValue(""); } - } - ]); - - filterbox.ace.on("input", function(e) { - applyFilter(); - }); - - // when tab is restored datagrids size might be wrong - // todo: remove this when apf bug is fixed - datagrid.once("mousemove", function() { - datagrid.resize(true); - }); - - datagrid.on("changeSelection", scheduleRedraw); - model.on("change", scheduleRedraw); - - plugin.on("reloadModel", scheduleRedraw); - - model.getCheckboxHTML = function(node) { - var enabled = node.enabled; - if (enabled == null || node.isGroup) return ""; - return ""; - }; - - var mode = "services"; - var readmeCache = {}; - function renderDetails() { - var items = datagrid.selection.getSelectedNodes(); - var hasEnabled = 0; - var hasDisabled = 0; - items.forEach(function(x) { - if (x.enabled != 0) - hasEnabled = true; - if (x.enabled != 1) - hasDisabled = true; - }); - btnUninstall.setProperty("visible", hasEnabled); - btnInstall.setProperty("visible", hasDisabled); - - if (mode == "services") { - renderServiceDetails(items); - btnServices.$ext.classList.add("serviceButtonActive"); - btnReadme.$ext.classList.remove("serviceButtonActive"); - } - else if (mode == "readme") { - renderReadmeDetails(items); - btnReadme.$ext.classList.add("serviceButtonActive"); - btnServices.$ext.classList.remove("serviceButtonActive"); - } - else { - descriptionBar.$ext.innerHTML = ""; - btnReadme.$ext.classList.remove("serviceButtonActive"); - btnServices.$ext.classList.remove("serviceButtonActive"); - } - - if (items.length == 1 && items[0].__error) { - var errorContainer = document.createElement("div"); - errorContainer.style.cssText = "padding: 5px; white-space: pre-wrap"; - errorContainer.textContent = items[0].__error.message; - descriptionBar.$ext.insertBefore(errorContainer, descriptionBar.$ext.firstChild); - } - } - - function renderReadmeDetails(items) { - if (items.length > 1) { - descriptionBar.$ext.textContent = "Multipe items selected."; - return; - } - var item = items[0]; - while (item && !item.packageConfig) { - item = item.parent; - } - if (!item) { - descriptionBar.$ext.textContent = "Readme is not available."; - return; - } - - if (readmeCache[item.name]) { - var div = document.createElement("div"); - div.textContent = readmeCache[item.name]; - div.style.cssText = "padding: 5px; white-space: pre-wrap"; - descriptionBar.$ext.textContent = ""; - descriptionBar.$ext.appendChild(div); - return; - } - descriptionBar.$ext.textContent = "Loading..."; - if (item.packageConfig.filePath) { - var parts = item.packageConfig.filePath.split("/"); - parts.pop(); - parts.push("README.md"); - var path = parts.join("/"); - fs.readFile(path, function(e, v) { - if (e) - return readmeCache[item.name] = "Readme is not available."; - readmeCache[item.name] = v; - renderDetails(); - }); - } - } - - function renderServiceDetails(items) { - function addUnique(item, array) { - if (array.indexOf(item) == -1) array.push(item); - } - - var provided = []; - - items.forEach(function addProvides(x) { - if (x.map) { - Object.keys(x.map).forEach(function(n) { - addProvides(x.map[n]); - }); - } - if (x.provides) { - x.provides.forEach(function(p) { - addUnique(p, provided); - }); - } - }); - - var consumedMap = Object.create(null); - provided.forEach(function(service) { - consumedMap[service] = 0; - }); - addAllProviders(consumedMap); - var consumedList = Object.keys(consumedMap); - var consumedGroups = splitToGroups(consumedList, consumedMap); - - var dependents = Object.create(null); - provided.forEach(function(service) { - dependents[service] = 0; - }); - addAllDependents(dependents); - var depList = Object.keys(dependents); - var depGroups = splitToGroups(depList, dependents); - - function splitToGroups(list, map) { - var groups = []; - list.forEach(function(n) { - var level = Math.abs(map[n]); - if (!groups[level]) - groups[level] = []; - groups[level].push(n); - }); - groups = groups.filter(Boolean).slice(1); - return groups; - } - - function formatServices(list, style) { - return "[" + list.map(function(x) { - return '' + escapeHTML(x) + ''; - }).join(", ") + "]"; - } - - descriptionBar.$ext.style.overflow = "auto"; - var serviceDesc = '

Provided services [' + provided.length + ']

\ -

' + (provided.length ? formatServices(provided) : "") + '

\ -

Consumed services [' + (consumedList.length - provided.length) + ']

\ -

' + consumedGroups.map(formatServices).join("
") + '

\ -

Dependent services [' + (depList.length - provided.length) + ']

\ -

' + depGroups.map(formatServices).join("
") + '

\ -
'; - descriptionBar.$ext.innerHTML = '
\ -

' + (items.length == 1 - ? '' + escapeHTML(items[0].path) + '' - : (items.length || "no") + " plugins selected") + '

\ -
' - + (items.length ? serviceDesc : "") - + '
'; - - } - descriptionBar.$ext.addEventListener("click", function(e) { - if (e.button) return; - var el = e.target; - var isButton = el.classList.contains("serviceButton"); - var text = el.textContent; - var serviceToPlugin = architectApp.serviceToPlugin; - if (e.detail == 1 && isButton && text) { - var path = serviceToPlugin[text] ? serviceToPlugin[text].packagePath : text; - var node = function search(node) { - if (node.path == path) - return node; - if (node.items) { - for (var i = 0; i < node.items.length; i++) { - var result = search(node.items[i]); - if (result) - return result; - } - } - }(model.cachedRoot); - - if (node) - datagrid.reveal(node); - } - }); - var hoverDetails = { hovered: "", highlighted: "" }; - descriptionBar.$ext.addEventListener("mousemove", function(e) { - if (e.button) return; - var el = e.target; - var isButton = el.classList.contains("serviceButton"); - var text = el.textContent; - hoverDetails.parent = el.parentNode; - hoverDetails.hovered = isButton ? text : ""; - hoverDetails.type = isButton ? el.parentNode.getAttribute("type") : ""; - if (hoverDetails.hovered != hoverDetails.highlighted) - requestAnimationFrame(updateHighlights); - }); - function updateHighlights() { - if (hoverDetails.hovered == hoverDetails.highlighted) return; - var serviceToPlugin = architectApp.serviceToPlugin; - if (!serviceToPlugin) return; - - var nodes = descriptionBar.$ext.querySelectorAll(".serviceButton"); - - var deps = Object.create(null); - deps[hoverDetails.hovered] = 0; - addAllDependents(deps); - addAllProviders(deps); - - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var val = node.textContent; - var highlight1 = false; - var highlight2 = false; - if (hoverDetails.type == "dependent") { - if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { - highlight1 = deps[val] < 0; - highlight2 = deps[val] > 0; - } - } else if (hoverDetails.type == "consumed") { - if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") { - highlight1 = deps[val] > 0; - highlight2 = deps[val] < 0; - } - } - - nodes[i].style.borderBottom = highlight1 ? "1px solid" : ""; - if (!highlight1) { - nodes[i].style.borderBottom = highlight2 ? "1px dashed rgba(125, 125, 125, 0.9)" : ""; - } - } - hoverDetails.highlighted = hoverDetails.hovered; - } - - function scheduleRedraw() { - window.requestAnimationFrame(renderDetails); - } - - var mnuCtxTree = new ui.menu({ - id: "mnuChat", - }, plugin); - menus.decorate(mnuCtxTree); - plugin.addElement(mnuCtxTree); - menus.addItemByPath("context/pluginManager/", mnuCtxTree, 0, plugin); - menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({ - isAvailable: function() { - var selected = datagrid.selection.getCursor(); - return selected && selected.packageConfig && selected.packageConfig.filePath; - }, - onclick: function() { - var selected = datagrid.selection.getCursor(); - var tabbehavior = architectApp.services.tabbehavior; - var filePath = selected.packageConfig && selected.packageConfig.filePath; - if (filePath) { - tabbehavior.revealtab({ path: filePath }); - } - }, - }), plugin); - menus.addItemByPath("context/pluginManager/Disable", new ui.item({ - isAvailable: function() { - var selected = datagrid.selection.getCursor(); - return selected && selected.enabled != 0; - }, - onclick: function() { reloadGridSelection(false); }, - }), plugin); - menus.addItemByPath("context/pluginManager/Enable", new ui.item({ - isAvailable: function() { - var selected = datagrid.selection.getCursor(); - return selected && selected.enabled != 1; - }, - onclick: function() { reloadGridSelection(true); }, - }), plugin); - menus.addItemByPath("context/pluginManager/Reload", new ui.item({ - onclick: function() { reloadGridSelection(); }, - }), plugin); - treeBar.setAttribute("contextmenu", mnuCtxTree); - } - - /***** Methods *****/ function readAvailablePlugins(callback) { @@ -643,193 +55,10 @@ define(function(require, exports, module) { if (!/(directory|folder)$/.test(stat.mime)) return; if (!/[._]/.test(name[0])) available.push(name); }); - localPlugins = available.concat(); - reloadModel(); callback && callback(null, available); }); } - function reloadModel() { - if (!model) return; - - if (!CORE.pluginManager) { - CORE.pluginManager = 1; - addAllProviders(CORE); - Object.keys(CORE).forEach(function(n) { - if (architectApp.serviceToPlugin[n]) - CORE[architectApp.serviceToPlugin[n].packagePath] = 1; - }); - } - - var GROUPS = { - // "changed": "Recently Changed", - "remote": "Remote Plugins", - "vfs": "Locally Installed Plugins", - "pre": "Pre-installed Plugins", - "core": "Core Plugins", - }; - - var groups = model.groups || Object.create(null); - if (!model.groups) { - var root = []; - model.cachedRoot = { items: root }; - model.groups = groups; - Object.keys(GROUPS).forEach(function(name) { - root.push(groups[name] = { - map: Object.create(null), - isOpen: name != "runtime", - className: "group", - isGroup: true, - isType: name, - noSelect: true, - name: GROUPS[name] - }); - }); - } - - function addPlugin(plugin, node) { - if (!plugin.provides || !plugin.consumes) - return console.error("Broken plugin", plugin); - if (plugin.packagePath) { - var parts = plugin.packagePath.split("/"); - - var path = parts.shift(); - parts.forEach(function(p, i) { - path = path + "/" + p; - if (!node.map) - node.map = Object.create(null); - if (!node.map[p]) { - node.map[p] = { - path: path, - parent: node, - }; - } - node.map[p].name = p; - if (i == parts.length - 1) { - var enabled = 1; - node.map[p].provides = plugin.provides; - plugin.provides.forEach(function(x) { - var service = architectApp.services[x]; - if (!service || (!service.loaded && service.unload)) { - enabled = 0; - } - }); - node.map[p].enabled = enabled; - } - node = node.map[p]; - node.className = plugin.__error ? "load-error" : ""; - node.__error = plugin.__error; - }); - } - } - - function addPackage(name) { - var pkg = packages[name]; - var parent = pkg.filePath ? groups.vfs : groups.remote; - - var node = parent.map[name] = parent.map[name] || { - path: "plugins/" + name, - name: name, - enabled: 0, - parent: parent, - }; - - node.packageConfig = pkg; - node.className = pkg.__error ? "load-error" : ""; - node.__error = pkg.__error; - node.loading = pkg.loading; - } - - architectApp.config.forEach(function(plugin) { - var node = CORE[plugin.packagePath] ? groups.core : groups.pre; - addPlugin(plugin, node); - }); - - if (localPlugins) { - localPlugins.forEach(function(name) { - if (!packages[name]) { - packages[name] = { - name: name, - filePath: "~/.c9/plugins/" + name + "/package.json", - } - } - - }); - } - - Object.keys(packages).forEach(function(n) { - var pkg = packages[n]; - var node = pkg.filePath ? groups.vfs : groups.remote; - - addPackage(n); - - if (pkg.c9 && pkg.c9.plugins) { - pkg.c9.plugins.forEach(function(plugin) { - addPlugin(plugin, node); - }); - } - }); - - function flatten(node, index) { - if (node.map) { - node.items = node.children = Object.keys(node.map).map(function(x) { - return node.map[x]; - }); - } - if (node.items) { - node.items.forEach(flatten); - if (node.items.length == 1 && !node.isGroup) { - var other = node.items[0]; - if (node.parent && node.parent.items[index] == node) { - node.parent.items[index] = other; - other.name = node.name + "/" + other.name; - other.packageConfig = node.packageConfig; - other.filePath = node.filePath; - other.url = node.url; - other.loading = node.loading; - } - } - if (!node.isGroup) { - node.enabled = null; - node.items.some(function(i) { - if (node.enabled == null) { - node.enabled = i.enabled; - } - if (i.enabled != node.enabled) { - node.enabled = -1; - return true; - } - }); - } - } - } - - flatten(model.cachedRoot); - - applyFilter(); - - emit("reloadModel"); - } - - function applyFilter() { - model.keyword = filterbox && filterbox.getValue(); - - if (!model.keyword) { - model.reKeyword = null; - model.setRoot(model.cachedRoot); - - // model.isOpen = function(node) { return node.isOpen; } - } - else { - model.reKeyword = new RegExp("(" - + util.escapeRegExp(model.keyword) + ")", 'i'); - var root = search.treeSearch(model.cachedRoot.items, model.keyword, true); - model.setRoot(root); - - // model.isOpen = function(node) { return true; }; - } - } - function createNewPlugin(template) { if (!template) template = "c9.ide.default"; @@ -871,7 +100,11 @@ define(function(require, exports, module) { c9.sourceDir ? [ "cp", util.escapeShell(c9.sourceDir + "/plugins/c9.ide.plugins/" + tarSourcePath), util.escapeShell(tarPathAbsolute) ] - : [ "curl", "-L", util.escapeShell(url), "-o", util.escapeShell(tarPathAbsolute) ] + : [ "curl", "-L", + (architectApp.services.onlinedev_helper ? "-k" : ""), // ignore certificate errors in dev mode + util.escapeShell(url), + "-o", util.escapeShell(tarPathAbsolute) + ].filter(Boolean) ).join(" ")] }, function(err, stderr, stdout) { if (err) @@ -899,7 +132,9 @@ define(function(require, exports, module) { // Select and expand the folder of the plugin tree.expandAndSelect(path); - readAvailablePlugins(reloadModel); + readAvailablePlugins(function(e) { + emit("change"); + }); }); }); }); @@ -907,19 +142,7 @@ define(function(require, exports, module) { }); } - function reload(names) { - var nodes = names.split(/\s*,\s*/).map(function(name) { - if (packages[name]) - return { packageConfig: packages[name] }; - return { path: name }; - }); - - reloadGridSelection(null, nodes); - } - - function reloadGridSelection(mode, nodes) { - if (!nodes) - nodes = datagrid.selection.getSelectedNodes(); + function reload(nodes, mode) { var reloadLast = []; nodes.forEach(function(node) { var id; @@ -945,16 +168,9 @@ define(function(require, exports, module) { else delete disabledPlugins[id]; }); - if (reloadLast.length && mode == null) { - var href = document.location.href.replace(/[?&]reload=[^&]+/, ""); - href += (href.match(/\?/) ? "&" : "?") + "reload=" + reloadLast.join(","); - window.history.replaceState(window.history.state, null, href); - - showReloadTip(); - updateReloadLastButton(); - } + return reloadLast; } - + function checkPluginsWithMissingDependencies() { var services = architectApp.services; var plugins = []; @@ -982,33 +198,6 @@ define(function(require, exports, module) { }); } - function updateReloadLastButton() { - var last = getLastReloaded(); - if (last) { - btnReloadLast.visible = true; - btnReloadLast.textContent = "Reload " + last; - } else { - btnReloadLast.visible = false; - } - } - - function showReloadTip() { - if (options.devel) { - var key = commands.getHotkey("reloadLastPlugin"); - if (commands.platform == "mac") - key = apf.hotkeys.toMacNotation(key); - if (!getLastReloaded()) { - showInfo("Reloaded " + name + ". Press " + key + " to reload again.", 3000); - return; - } - } - showInfo("Reloaded " + name + ".", 1000); - } - - function getLastReloaded() { - return qs.parse(document.location.search.substr(1)).reload; - } - function getAllPlugins(includeDisabled) { var config = architectApp.config; Object.keys(packages).forEach(function(n) { @@ -1181,7 +370,7 @@ define(function(require, exports, module) { packages[name].__error = new Error(message + "\n" + err.message); packages[name].loading = false; - reloadModel(); + emit("change"); callback && callback(err); } @@ -1219,7 +408,7 @@ define(function(require, exports, module) { if (err) return addError("Error loading plugins", err); if (packages[name]) packages[name].loading = false; - reloadModel(); + emit("change"); callback && callback(err, result); }); } @@ -1389,7 +578,7 @@ define(function(require, exports, module) { pluginConfig.__userDisabled = true; } - reloadModel(); + emit("change"); } function unloadPluginConfig(plugin) { @@ -1488,34 +677,10 @@ define(function(require, exports, module) { /***** Lifecycle *****/ plugin.on("load", function() { - load(); - }); - plugin.on("draw", function(e) { - draw(e); - }); - plugin.on("activate", function(e) { - datagrid && datagrid.resize(); - }); - plugin.on("resize", function(e) { - datagrid && datagrid.resize(); - }); - plugin.on("enable", function() { - - }); - plugin.on("disable", function() { - }); plugin.on("unload", function() { - loaded = false; - drawn = false; - - model = null; - datagrid = null; - filterbox = null; - btnInstall = null; - btnUninstall = null; - localPlugins = null; disabledPlugins = null; + plugin = null; }); /***** Register and define API *****/ @@ -1527,12 +692,27 @@ define(function(require, exports, module) { /** * */ - createNewPlugin: createNewPlugin, - + readAvailablePlugins: readAvailablePlugins, + /** * */ - loadPackage: loadPackage, + createNewPlugin: createNewPlugin, + + /** + * + */ + getAllPlugins: getAllPlugins, + + /** + * + */ + addAllDependents: addAllDependents, + + /** + * + */ + addAllProviders: addAllProviders, /** * @@ -1542,18 +722,41 @@ define(function(require, exports, module) { /** * */ - addStaticPlugin: addStaticPlugin, - + loadPackage: loadPackage, + + /** + * + */ + loadPlugins: loadPlugins, + /** * */ reload: reload, - /* - * @ignore + /** + * */ - get datagrid() { return datagrid; }, + getServiceNamesByPath: getServiceNamesByPath, + + /** + * + */ + unloadPlugins: unloadPlugins, + + /** + * + */ + unloadPluginConfig: unloadPluginConfig, + + /** + * + */ + addStaticPlugin: addStaticPlugin, + + get packages() { return packages; }, + get disabledPlugins() { return disabledPlugins; }, }); var shim = new Plugin(); From 2e268550806c5e7b29f947e55d1eaa29866c2078 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 2 May 2017 21:06:22 +0400 Subject: [PATCH 11/15] add note about plugin installation to init script --- plugins/c9.cli.bridge/bridge.js | 3 ++- plugins/c9.ide.configuration/configure.js | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge.js b/plugins/c9.cli.bridge/bridge.js index 8589f630..e291c098 100644 --- a/plugins/c9.cli.bridge/bridge.js +++ b/plugins/c9.cli.bridge/bridge.js @@ -81,7 +81,8 @@ define(function(require, exports, module) { /***** Methods *****/ plugin.on("load", function() { - c9.on("connect", load, plugin); + if (c9.connected) load(); + else c9.on("connect", load, plugin); }); plugin.on("unload", function() { diff --git a/plugins/c9.ide.configuration/configure.js b/plugins/c9.ide.configuration/configure.js index 0d370339..3c26590a 100644 --- a/plugins/c9.ide.configuration/configure.js +++ b/plugins/c9.ide.configuration/configure.js @@ -288,7 +288,13 @@ define(function(require, exports, module) { var script = settings.get("user/config/init.js") || ""; openTab("~/.c9/init.js", script, "javascript", "// You can access plugins via the 'services' global variable\n" + - "/*global services, plugin*/\n"); + "/*global services, plugin*/\n" + + "\n" + + "// to load plugins use\n" + + "// services.pluginManager.loadPackage([\n" + + "// \"https://.github.io//build/package..js\",\n" + + "// \"~/.c9/plugins//package.json\",\n" + + "// ]);\n"); } function editStylesCss() { From 1ef011719c168b012b095ba1c38dab547fa4d0b3 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 2 May 2017 21:29:09 +0400 Subject: [PATCH 12/15] rename hub to app --- configs/client-config_test.js | 2 +- node_modules/architect/architect.js | 11 ++++------- plugins/c9.core/ext.js | 4 ++-- plugins/c9.ide.configuration/configure.js | 4 ++-- plugins/c9.ide.plugins/gui.js | 8 ++++---- plugins/c9.ide.plugins/manager.js | 4 ++-- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/configs/client-config_test.js b/configs/client-config_test.js index c26942dd..45f74285 100755 --- a/configs/client-config_test.js +++ b/configs/client-config_test.js @@ -99,7 +99,7 @@ describe("client config consistency", function() { } }); - var provides = {"auth.bootstrap": 1, "hub": 1}; + var provides = { "auth.bootstrap": 1, "hub": 1, "app": 1 }; var paths = {}; clientPlugins.forEach(function(p) { if (paths[p.packagePath]) { diff --git a/node_modules/architect/architect.js b/node_modules/architect/architect.js index 2f0e671d..5b9c188d 100644 --- a/node_modules/architect/architect.js +++ b/node_modules/architect/architect.js @@ -179,7 +179,8 @@ function checkCycles(config, lookup) { }); var resolved = { - hub: true + hub: true, + app: true }; var changed = true; var sorted = []; @@ -253,12 +254,8 @@ function Architect(config) { var isAdditionalMode; var services = app.services = { - hub: { - on: function (name, callback) { - app.on(name, callback); - }, - app: app - } + hub: app, + app: app }; // Check the config diff --git a/plugins/c9.core/ext.js b/plugins/c9.core/ext.js index 07116c3b..8e07a19f 100644 --- a/plugins/c9.core/ext.js +++ b/plugins/c9.core/ext.js @@ -1,11 +1,11 @@ define(function(require, exports, module) { - main.consumes = ["hub"]; + main.consumes = ["app"]; main.provides = ["ext", "Plugin"]; return main; function main(options, imports, register) { var Emitter = require("events").EventEmitter; - var architectApp = imports.hub.app; + var architectApp = imports.app; var plugins = []; diff --git a/plugins/c9.ide.configuration/configure.js b/plugins/c9.ide.configuration/configure.js index 3c26590a..2b066a5c 100644 --- a/plugins/c9.ide.configuration/configure.js +++ b/plugins/c9.ide.configuration/configure.js @@ -3,7 +3,7 @@ define(function(require, exports, module) { "Plugin", "dialog.error", "ui", "settings", "tabManager", "save", "menus", "preferences.keybindings", "preferences.general", "preferences.project", "c9", "commands", "watcher", "fs", - "tree.favorites", "util", "hub" + "tree.favorites", "util", "app" ]; main.provides = ["configure"]; return main; @@ -22,7 +22,7 @@ define(function(require, exports, module) { var kbprefs = imports["preferences.keybindings"]; var genprefs = imports["preferences.general"]; var prjprefs = imports["preferences.project"]; - var services = imports.hub.app.services; + var services = imports.app.services; var showError = imports["dialog.error"].show; var favs = imports["tree.favorites"]; var util = imports.util; diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js index d35aae9c..c5d44207 100644 --- a/plugins/c9.ide.plugins/gui.js +++ b/plugins/c9.ide.plugins/gui.js @@ -1,9 +1,9 @@ define(function(require, exports, module) { main.consumes = [ - "PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin", - "layout", "proc", "menus", "commands", "pluginManager", + "app", "ext", "c9", "PreferencePanel", "settings", "ui", "util", + "layout", "menus", "commands", "pluginManager", "dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs", - "preferences.experimental", "apf", "hub", "dialog.notification" + "preferences.experimental", "apf","dialog.notification" ]; main.provides = ["pluginManagerUi"]; return main; @@ -26,7 +26,7 @@ define(function(require, exports, module) { var notify = imports["dialog.notification"].show; var experimental = imports["preferences.experimental"]; var pluginManager = imports.pluginManager; - var architectApp = imports.hub.app; + var architectApp = imports.app; var search = require("../c9.ide.navigate/search"); var Tree = require("ace_tree/tree"); diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 99dc0c9c..16196b63 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -1,7 +1,7 @@ /*global requirejs*/ define(function(require, exports, module) { main.consumes = [ - "hub", "ext", "c9", "Plugin", "proc", "fs", "tree", "vfs", + "app", "ext", "c9", "Plugin", "proc", "fs", "tree", "vfs", "dialog.error", "tree.favorites" ]; main.provides = ["pluginManager", "plugin.manager", "plugin.debug"]; @@ -19,7 +19,7 @@ define(function(require, exports, module) { var vfs = imports.vfs; var showError = imports["dialog.error"].show; var favs = imports["tree.favorites"]; - var architectApp = imports.hub.app; + var architectApp = imports.app; var join = require("path").join; var basename = require("path").basename; From c8a81787e3efbf6842de52b877511a249d09db95 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 2 May 2017 22:10:38 +0400 Subject: [PATCH 13/15] tweak ui --- plugins/c9.ide.plugins/gui.js | 55 +++++++++++++++++++++---------- plugins/c9.ide.plugins/manager.js | 3 ++ plugins/c9.ide.plugins/style.css | 4 +++ 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js index c5d44207..a2bcb846 100644 --- a/plugins/c9.ide.plugins/gui.js +++ b/plugins/c9.ide.plugins/gui.js @@ -71,6 +71,7 @@ define(function(require, exports, module) { var btnReadme; var btnReloadLast; var localPlugins; + var btnSettings; var loaded = false; function load() { @@ -495,11 +496,11 @@ define(function(require, exports, module) { } descriptionBar.$ext.style.overflow = "auto"; - var serviceDesc = '

Provided services [' + provided.length + ']

\ + var serviceDesc = '

Provided services [' + provided.length + ']

\

' + (provided.length ? formatServices(provided) : "") + '

\ -

Consumed services [' + (consumedList.length - provided.length) + ']

\ +

Consumed services [' + (consumedList.length - provided.length) + ']

\

' + consumedGroups.map(formatServices).join("
") + '

\ -

Dependent services [' + (depList.length - provided.length) + ']

\ +

Dependent services [' + (depList.length - provided.length) + ']

\

' + depGroups.map(formatServices).join("
") + '

\
'; descriptionBar.$ext.innerHTML = '
Date: Thu, 4 May 2017 00:55:20 +0400 Subject: [PATCH 14/15] allow using checkboxes to enable plugins --- .../lib/ace_tree/mouse/default_handlers.js | 12 ++++++----- plugins/c9.ide.plugins/gui.js | 21 +++++++++++++++++-- plugins/c9.ide.plugins/manager.js | 8 +++---- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js b/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js index b568a458..54247f61 100644 --- a/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js +++ b/node_modules/ace_tree/lib/ace_tree/mouse/default_handlers.js @@ -137,13 +137,15 @@ function DefaultHandlers(mouseHandler) { } this.$clickNode = null; } else if (dom.hasCssClass(target, "checkbox")) { + var nodes = inSelection && editor.selection.getSelectedNodes(); + provider._signal("toggleCheckbox", { target: node, selectedNodes: nodes }); + // consider deprecating this node.isChecked = !node.isChecked; - if (inSelection) { - var nodes = editor.selection.getSelectedNodes(); - nodes.forEach(function(n){ n.isChecked = node.isChecked }); + if (nodes) { + nodes.forEach(function(n) { n.isChecked = node.isChecked }); } - provider._signal(node.isChecked ? "check" : "uncheck", inSelection ? nodes : [node]); - provider._signal("change") + provider._signal(node.isChecked ? "check" : "uncheck", nodes || [node]); + provider._signal("change"); } else if (dom.hasCssClass(target, "icon-ok")) { if (ev.getShiftKey()) { editor.selection.expandSelection(node, null, true); diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js index a2bcb846..fce943db 100644 --- a/plugins/c9.ide.plugins/gui.js +++ b/plugins/c9.ide.plugins/gui.js @@ -629,6 +629,16 @@ define(function(require, exports, module) { showInfo("Path is not available."); }, }), plugin); + menus.addItemByPath("context/pluginManager/Refresh List", new ui.item({ + onclick: function() { + pluginManager.readAvailablePlugins(function(err, available) { + if (err) return console.error(err); + localPlugins = available; + reloadModel(); + }); + }, + }), plugin); + menus.addItemByPath("context/pluginManager/~", new ui.divider({}), plugin); menus.addItemByPath("context/pluginManager/Disable", new ui.item({ isAvailable: function() { var selected = datagrid.selection.getCursor(); @@ -644,9 +654,17 @@ define(function(require, exports, module) { onclick: function() { reloadGridSelection(true); }, }), plugin); menus.addItemByPath("context/pluginManager/Reload", new ui.item({ + isAvailable: function() { + var selected = datagrid.selection.getCursor(); + return selected; + }, onclick: function() { reloadGridSelection(); }, }), plugin); treeBar.setAttribute("contextmenu", mnuCtxTree); + + model.on("toggleCheckbox", function(e) { + reloadGridSelection(!e.target.enabled, e.selectedNodes || [e.target]); + }); } @@ -861,7 +879,6 @@ define(function(require, exports, module) { } } - function updateReloadLastButton() { var last = getLastReloaded(); if (last) { @@ -880,7 +897,7 @@ define(function(require, exports, module) { return; } } - showInfo("Loaded " + name + " for the duration of current browser session.", 1000); + showInfo("Loaded " + name + " for the duration of current browser session.", 3000); } function getLastReloaded() { diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 5ea0d9a0..266fcc4b 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -1,8 +1,7 @@ /*global requirejs*/ define(function(require, exports, module) { main.consumes = [ - "app", "ext", "c9", "Plugin", "proc", "fs", "tree", "vfs", - "dialog.error", "tree.favorites" + "app", "ext", "c9", "Plugin", "proc", "fs", "vfs", "dialog.error" ]; main.provides = ["pluginManager", "plugin.manager", "plugin.debug"]; return main; @@ -12,13 +11,11 @@ define(function(require, exports, module) { var c9 = imports.c9; var fs = imports.fs; var ext = imports.ext; - var tree = imports.tree; var proc = imports.proc; var Plugin = imports.Plugin; var util = imports.util; var vfs = imports.vfs; var showError = imports["dialog.error"].show; - var favs = imports["tree.favorites"]; var architectApp = imports.app; var join = require("path").join; @@ -125,6 +122,9 @@ define(function(require, exports, module) { // Remove .tar.gz fs.unlink(tarPath, function() { + // using this to allow reloading the tree + var tree = architectApp.services["tree"]; + var favs = architectApp.services["tree.favorites"]; // Add plugin to favorites favs.addFavorite(dirname(pluginsDir), "plugins"); From a9273255700cbbc2948a3eecb785019d322f6db7 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 4 May 2017 18:01:13 +0400 Subject: [PATCH 15/15] do not allow to disable plugin manager ui --- plugins/c9.ide.plugins/gui.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/c9.ide.plugins/gui.js b/plugins/c9.ide.plugins/gui.js index fce943db..cc0bf006 100644 --- a/plugins/c9.ide.plugins/gui.js +++ b/plugins/c9.ide.plugins/gui.js @@ -675,8 +675,8 @@ define(function(require, exports, module) { var packages = pluginManager.packages; - if (!CORE.pluginManager) { - CORE.pluginManager = 1; + if (!CORE.pluginManagerUi) { + CORE.pluginManagerUi = 1; pluginManager.addAllProviders(CORE); Object.keys(CORE).forEach(function(n) { if (architectApp.serviceToPlugin[n])