diff --git a/package.json b/package.json index 350a299e..625bcc7d 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "licenses": [], "c9plugins": { - "c9.ide.language": "#afda452919", + "c9.ide.language": "#b201fe581a", "c9.ide.language.css": "#ef8a28943e", "c9.ide.language.generic": "#8a3be4533a", "c9.ide.language.html": "#bbe81afed1", @@ -62,14 +62,14 @@ "c9.ide.language.jsonalyzer": "#7261f47b26", "c9.ide.collab": "#7b09419b5c", "c9.ide.local": "#cf624506cc", - "c9.ide.find": "#ef82bc4f0d", + "c9.ide.find": "#e0ec892635", "c9.ide.find.infiles": "#1b83cf12f1", "c9.ide.find.replace": "#e4daf722b8", "c9.ide.run.debug": "#638e6b00b3", "c9.automate": "#86bf1ee1ca", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", - "c9.ide.ace.keymaps": "#6c4bb65b1f", + "c9.ide.ace.keymaps": "#43445d6306", "c9.ide.ace.repl": "#864dc3aea1", "c9.ide.ace.split": "#0ae0151c78", "c9.ide.ace.statusbar": "#d7b45bb7c3", @@ -95,8 +95,8 @@ "c9.ide.readonly": "#f6f07bbe42", "c9.ide.recentfiles": "#7c099abf40", "c9.ide.remote": "#cd45e81d2f", - "c9.ide.run": "#f3ac81cc10", - "c9.ide.run.build": "#fd57b3341d", + "c9.ide.run": "#71c5562e42", + "c9.ide.run.build": "#ad45874c88", "c9.ide.run.debug.xdebug": "#b91d23f48b", "c9.ide.save": "#b876d87d55", "c9.ide.terminal.monitor": "#b0b4d03280", diff --git a/plugins/c9.core/util.js b/plugins/c9.core/util.js index fd78dff6..e73001ed 100644 --- a/plugins/c9.core/util.js +++ b/plugins/c9.core/util.js @@ -209,6 +209,14 @@ define(function(require, exports, module) { return JSON.stringify(sortByKeys(obj), replacer, spaces); }; + plugin.safeParseJson = function(strJson, cb){ + // Remove comments + var data = strJson.replace(/(^|\n)\s*\/\/.*/g, ""); + + try { return JSON.parse(data); } + catch (e) { cb(e); return false; } + } + /** * */ diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 43d2ea37..4a3cfa19 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -90,7 +90,8 @@ define(function(require, exports, module) { var isMinimal = options.minimal; var themeLoaded = {}; - var lastTheme, grpSyntax; + var themeCounter = 100; + var lastTheme, grpSyntax, grpThemes; var theme; var skin = settings.get("user/general/@skin"); @@ -140,8 +141,10 @@ define(function(require, exports, module) { function setTheme(path, isPreview, fromServer, $err) { // Get Theme or wait for theme to load - try{ - theme = fromServer || require(path); + try { + theme = typeof path == "object" + ? path + : fromServer || require(path); // fixes a problem with Ace architect loading /lib/ace // creating a conflict with themes @@ -1089,69 +1092,83 @@ define(function(require, exports, module) { /**** Themes ****/ - var grpThemes = new ui.group(); - var mnuThemes = new ui.menu({ + grpThemes = new ui.group(); + + menus.addItemByPath("View/Themes/", new ui.menu({ "onprop.visible" : function(e) { if (e.value) grpThemes.setValue(settings.get("user/ace/@theme")); } - }); - menus.addItemByPath("View/Themes/", mnuThemes, 350000, handle); + }), 350000, handle); - var preview; - var setMenuThemeDelayed = lang.delayedCall(function(){ - setMenuTheme(preview, true); - }, 150); - function setMenuTheme(path, isPreview) { - setTheme(path || settings.get("user/ace/@theme"), isPreview); - } - - function addThemeMenu(name, path, index) { - menus.addItemByPath("View/Themes/" + name, new ui.item({ - type: "radio", - value: path || themes[name], - group: grpThemes, - - onmouseover: function(e) { - preview = this.value; - setMenuThemeDelayed.schedule(); - }, - - onmouseout: function(e) { - preview = null; - setMenuThemeDelayed.schedule(); - }, - - onclick: function(e) { - setMenuTheme(e.currentTarget.value); - } - }), index, handle); - } - // Create Theme Menus - var mainCounter = 100; for (var name in themes) { if (themes[name] instanceof Array) { // Add Menu Item (for submenu) - menus.addItemByPath("View/Themes/" + name + "/", null, mainCounter++, handle); + menus.addItemByPath("View/Themes/" + name + "/", null, themeCounter++, handle); themes[name].forEach(function (n) { // Add Menu Item var themeprop = Object.keys(n)[0]; - addThemeMenu(name + "/" + themeprop, n[themeprop]); + addThemeMenu(name + "/" + themeprop, n[themeprop], -1); }); } else { // Add Menu Item - addThemeMenu(name, null, mainCounter++); + addThemeMenu(name, null, themeCounter++); } } + /**** Syntax ****/ + grpSyntax = new ui.group(); handle.addElement(grpNewline, grpSyntax, grpThemes); } + var preview; + var setMenuThemeDelayed = lang.delayedCall(function(){ + setMenuTheme(preview, true); + }, 150); + function setMenuTheme(path, isPreview) { + setTheme(path || settings.get("user/ace/@theme"), isPreview); + } + function addThemeMenu(name, path, index, plugin) { + menus.addItemByPath("View/Themes/" + name, new ui.item({ + type: "radio", + value: path || themes[name], + group: grpThemes, + + onmouseover: function(e) { + preview = this.value; + setMenuThemeDelayed.schedule(); + }, + + onmouseout: function(e) { + preview = null; + setMenuThemeDelayed.schedule(); + }, + + onclick: function(e) { + setMenuTheme(e.currentTarget.value); + } + }), index == -1 ? undefined : index || themeCounter++, plugin || handle); + } + function addTheme(css, plugin){ + var theme = { cssText: css }; + var firstLine = css.split("\n", 1)[0].replace(/\/\*|\*\//g, "").trim(); + firstLine.split(";").forEach(function(n){ + if (!n) return; + var info = n.split(":");console.log(info) + theme[info[0].trim()] = info[1].trim(); + }); + theme.isDark = theme.isDark == "true"; + + themes[theme.name] = theme; + + ui.insertCss(exports.cssText, plugin); + addThemeMenu(theme.name, theme, null, plugin); + } function rebuildSyntaxMenu() { menus.remove("View/Syntax/"); @@ -1500,7 +1517,7 @@ define(function(require, exports, module) { /** * Set the theme for ace. * - * Here's a list of known themes: + * Here's a list of default themes: * * * ace/theme/ambiance * * ace/theme/chrome @@ -1559,6 +1576,13 @@ define(function(require, exports, module) { return mode && mode.caption || "Text"; }, + /** + * Adds a menu item for a new theme + * @param {String} css + * @param {Plugin} plugin + */ + addTheme: addTheme, + /** * @ignore */ diff --git a/plugins/c9.ide.ace/themes.js b/plugins/c9.ide.ace/themes.js index 802bd111..66c8000e 100644 --- a/plugins/c9.ide.ace/themes.js +++ b/plugins/c9.ide.ace/themes.js @@ -16,7 +16,7 @@ define(function(require, exports, module) { var plugin = new PreferencePanel("Ajax.org", main.consumes, { caption: "Themes", - className: "keybindings", + className: "flatform", form: true, noscroll: true, colwidth: 150, @@ -183,12 +183,6 @@ define(function(require, exports, module) { }); plugin.on("draw", function(e) { draw(e); - }); - plugin.on("enable", function() { - - }); - plugin.on("disable", function() { - }); plugin.on("unload", function() { loaded = false; diff --git a/plugins/c9.ide.keys/editor.js b/plugins/c9.ide.keys/editor.js index 970e4e08..9e802965 100644 --- a/plugins/c9.ide.keys/editor.js +++ b/plugins/c9.ide.keys/editor.js @@ -38,6 +38,8 @@ define(function(require, exports, module) { }); // var emit = plugin.getEmitter(); + var customKeymaps = {}; + var model, datagrid, changed, container, filterbox; var appliedCustomSets, intro, reloading; @@ -57,6 +59,15 @@ define(function(require, exports, module) { } }, plugin); + settings.on("user/ace/@keyboardmode", function(){ + var mode = settings.getJson("user/ace/@keyboardmode"); + if (customKeymaps[mode]) { + settings.set("user/ace/@keyboardmode", "default"); + settings.setJson("user/key-bindings", customKeymaps[mode]); + updateCommandsFromSettings(); + } + }); + settings.on("read", function(e) { updateCommandsFromSettings(); }, plugin); @@ -184,6 +195,7 @@ define(function(require, exports, module) { title: "Keyboard Mode", type: "dropdown", path: "user/ace/@keyboardmode", + name: "kbmode", items: [ { caption: "Default", value: "default" }, { caption: "Vim", value: "vim" }, @@ -495,6 +507,47 @@ define(function(require, exports, module) { } } + function addCustomKeymap(name, keymap, plugin){ + customKeymaps[name] = keymap; + + if (!Object.keys(customKeymaps).length) { + menus.addItemByPath("Edit/Keyboard Mode/~", + new ui.divider(), 10000, plugin); + } + + menus.addItemByPath("Edit/Keyboard Mode/" + name, new ui.item({ + type: "radio", + value: name.toLowerCase(), + onclick: function(e) { + settings.set("user/ace/@keyboardmode", name); + } + }), 10000 + Object.keys(customKeymaps).length, plugin); + + plugin.addOther(function(){ delete customKeymaps[name]; }); + + if (plugin.visible) + updateKeymaps(); + } + + function updateKeymaps(){ + var items = [ + { caption: "Default", value: "default" }, + { caption: "Vim", value: "vim" }, + { caption: "Emacs", value: "emacs" }, + { caption: "Sublime", value: "sublime" } + ]; + + for (var name in customKeymaps) { + items.push({ caption: name, value: name }); + } + + plugin.form.update([{ + type: "dropdown", + name: "kbmode", + items: items + }]) + } + /***** Lifecycle *****/ plugin.on("load", function() { @@ -504,7 +557,10 @@ define(function(require, exports, module) { draw(e); }); plugin.on("activate", function(e) { - datagrid && datagrid.resize(); + if (!drawn) return; + + datagrid.resize(); + updateKeymaps(); }); plugin.on("resize", function(e) { datagrid && datagrid.resize(); @@ -532,7 +588,12 @@ define(function(require, exports, module) { /** * */ - editUserKeys: editUserKeys + editUserKeys: editUserKeys, + + /** + * + */ + addCustomKeymap: addCustomKeymap }); register(null, { diff --git a/plugins/c9.ide.plugins/debug.js b/plugins/c9.ide.plugins/debug.js index bbd1980d..9b990eca 100644 --- a/plugins/c9.ide.plugins/debug.js +++ b/plugins/c9.ide.plugins/debug.js @@ -3,7 +3,7 @@ define(function(require, exports, module) { main.consumes = [ "Plugin", "vfs", "fs", "plugin.loader", "c9", "ext", "watcher", "dialog.notification", "ui", "menus", "commands", "settings", "auth", - "installer" + "installer", "find", "util" ]; main.provides = ["plugin.debug"]; return main; @@ -13,6 +13,8 @@ define(function(require, exports, module) { 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; @@ -26,6 +28,7 @@ define(function(require, exports, module) { var dirname = require("path").dirname; var join = require("path").join; + var async = require("async"); var architect; @@ -37,6 +40,8 @@ define(function(require, exports, module) { var ENABLED = c9.location.indexOf("debug=2") > -1; var HASSDK = c9.location.indexOf("sdk=0") === -1; + var reParts = /^(builders|keymaps|modes|outline|runners|snippets|themes)\/(.*)/ + var loaded = false; function load() { if (loaded) return false; @@ -118,52 +123,81 @@ define(function(require, exports, module) { } // Fetch package.json - fs.readFile("~/.c9/plugins/" + name + "/package.json", function(err, data){ - if (err) { - console.error(err); - return next(); - } - - try { - var options = JSON.parse(data); - if (!options.plugins) - throw new Error("Missing plugins property in package.json of " + name); - } - catch(e){ - console.error(err); - return next(); - } - - var host = vfs.baseUrl + "/"; - var base = join(String(c9.projectId), - "plugins", auth.accessToken); - - // Start the installer if one is included - if (options.installer) { - var version = options.installer.version; - var url = host + join(base, name, options.installer.main); - installer.createVersion(name, version, function(v, o){ - require([url], function(fn){ - fn(v, o); + async.parallel([ + function(next){ + 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); + + // Start the installer if one is included + if (options.installer) { + var version = options.installer.version; + var url = host + join(base, name, options.installer.main); + installer.createVersion(name, version, function(v, o){ + require([url], function(fn){ + fn(v, o); + }); + }); + } + + // Add the plugin to the config + Object.keys(options.plugins).forEach(function(path){ + var pluginPath = name + "/" + path + ".js"; + + // Watch project path + watch("~/.c9/plugins/" + pluginPath); + + var cfg = options.plugins[path]; + cfg.packagePath = host + join(base, pluginPath.replace(/^plugins\//, "")); + cfg.staticPrefix = host + join(base, name); + cfg.apikey = "0000000000000000000000000000="; + + config.push(cfg); + }); + + next(); + }); + }, + function(next){ + var path = join(c9.home, "plugins", + name); + var rePath = new RegExp("^" + util.escapeRegExp(path), "g"); + find.getFileList({ + path: path, + nocache: true, + buffer: true + }, function(err, data){ + if (err) + return next(err); + + // Remove the base path + data = data.replace(rePath, ""); + + // Process all the submodules + var parallel = processModules(path, data); + async.parallel(parallel, function(err, data){ + if (err) + return next(err); + + // Done + next(); }); }); } - - // Add the plugin to the config - Object.keys(options.plugins).forEach(function(path){ - var pluginPath = name + "/" + path + ".js"; - - // Watch project path - watch("~/.c9/plugins/" + pluginPath); - - var cfg = options.plugins[path]; - cfg.packagePath = host + join(base, pluginPath.replace(/^plugins\//, "")); - cfg.staticPrefix = host + join(base, name); - cfg.apikey = "0000000000000000000000000000="; - - config.push(cfg); - }); - + ], function(err, results){ + if (err) console.error(err); next(); }); } @@ -189,6 +223,113 @@ define(function(require, exports, module) { list.forEach(next); } + function processModules(path, data){ + var parallel = []; + var services = architect.services; + + var placeholder = new Plugin(); + + data.split("\n").forEach(function(line){ + if (!line.match(reParts)) return; + + var type = RegExp.$1; + var filename = RegExp.$2; + if (filename.indexOf("/") > -1) return; + + switch (type) { + case "builders": + parallel.push(function(next){ + fs.readFile(join(path, filename), function(err, data){ + if (err) { + console.error(err); + return next(err); + } + + data = util.safeParseJson(data, next); + if (!data) return; + + services.build.addBuilder(filename, data, placeholder); + next(); + }); + }); + break; + case "keymaps": + parallel.push(function(next){ + fs.readFile(join(path, filename), function(err, data){ + if (err) { + console.error(err); + return next(err); + } + + data = util.safeParseJson(data, next); + if (!data) return; + + services["preferences.keybindings"].addCustomKeymap(filename, data, placeholder); + next(); + }); + }); + break; + case "modes": + parallel.push(function(next){ + + }); + break; + case "outline": + parallel.push(function(next){ + fs.readFile(join(path, filename), function(err, data){ + if (err) { + console.error(err); + return next(err); + } + + data = util.safeParseJson(data, next); + if (!data) return; + + services.outline.addOutlinePlugin(filename, data, placeholder); + next(); + }); + }); + break; + case "runners": + parallel.push(function(next){ + fs.readFile(join(path, filename), function(err, data){ + if (err) { + console.error(err); + return next(err); + } + + data = util.safeParseJson(data, next); + if (!data) return; + + services.run.addRunner(filename, data, placeholder); + next(); + }); + }); + break; + case "snippets": + parallel.push(function(next){ + + }); + break; + case "themes": + parallel.push(function(next){ + fs.readFile(join(path, filename), function(err, theme){ + if (err) { + console.error(err); + return next(err); + } + + services.ace.addTheme(theme, placeholder); + next(); + }); + }); + break; + } + }); + + return parallel; + } + // Check if require.s.contexts._ can help watching all dependencies function watch(path){ watcher.watch(path); diff --git a/plugins/c9.ide.preferences/preferencepanel.js b/plugins/c9.ide.preferences/preferencepanel.js index 66acbaa4..d6917fd1 100644 --- a/plugins/c9.ide.preferences/preferencepanel.js +++ b/plugins/c9.ide.preferences/preferencepanel.js @@ -352,6 +352,13 @@ define(function(require, module, exports) { */ get form(){ return form }, + /** + * Whether this panel is active + * @property {Boolean} active + * @readonly + */ + get active(){ return amlBar.visible; }, + _events: [ /** * Fired when the panel container is drawn. @@ -361,7 +368,19 @@ define(function(require, module, exports) { * @param {AMLElement} e.aml The aml container. * @param {AMLElement} e.navHtml The html element that represents the navigation. */ - "draw" + "draw", + /** + * @event activate + */ + "activate", + /** + * @event deactivate + */ + "deactivate", + /** + * @event resize + */ + "resize" ], /**