From 5f892d475a3270bb60b24e7aae70c42add0550ba Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 24 Apr 2015 04:06:08 +0000 Subject: [PATCH 01/61] Move disk to be the lowest stat just like the popup --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index fa35e43b..3de71e74 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", "c9.ide.find.replace": "#e4daf722b8", - "c9.ide.run.debug": "#638e6b00b3", + "c9.ide.run.debug": "#995b71b833", "c9.automate": "#47e2c429c9", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", @@ -95,6 +95,7 @@ "c9.ide.readonly": "#f6f07bbe42", "c9.ide.recentfiles": "#7c099abf40", "c9.ide.remote": "#cd45e81d2f", + "c9.ide.processlist": "#bc11818bb5", "c9.ide.run": "#71c5562e42", "c9.ide.run.build": "#ad45874c88", "c9.ide.run.debug.xdebug": "#b91d23f48b", From 79f56d5702ace3a286110acb5ba3921189cc7e69 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Wed, 22 Apr 2015 19:12:20 +0000 Subject: [PATCH 02/61] stuff --- .../mock/c9.ide.example3/plugin.html | 1 + .../mock/c9.ide.example3/plugin.js | 155 ++++++++++++++++++ .../mock/c9.ide.example3/plugin_test.js | 41 +++++ 3 files changed, 197 insertions(+) create mode 100644 plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.html create mode 100644 plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.js create mode 100644 plugins/c9.ide.plugins/mock/c9.ide.example3/plugin_test.js diff --git a/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.html b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.html new file mode 100644 index 00000000..d8688f81 --- /dev/null +++ b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.html @@ -0,0 +1 @@ +
Hello World
\ No newline at end of file diff --git a/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.js b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.js new file mode 100644 index 00000000..3dca2f71 --- /dev/null +++ b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin.js @@ -0,0 +1,155 @@ +define(function(require, exports, module) { + main.consumes = [ + "Plugin", "ui", "commands", "menus", "preferences", "settings" + ]; + main.provides = ["myplugin"]; + return main; + + function main(options, imports, register) { + var Plugin = imports.Plugin; + var ui = imports.ui; + var menus = imports.menus; + var commands = imports.commands; + var settings = imports.settings; + var prefs = imports.preferences; + + /***** Initialization *****/ + + var plugin = new Plugin("Ajax.org", main.consumes); + var emit = plugin.getEmitter(); + + var showing; + function load() { + commands.addCommand({ + name: "mycommand", + bindKey: { mac: "Command-I", win: "Ctrl-I" }, + isAvailable: function(){ return true; }, + exec: function() { + showing ? hide() : show(); + } + }, plugin); + + menus.addItemByPath("Tools/My Menu Item", new ui.item({ + command: "mycommand" + }), 300, plugin); + + settings.on("read", function(e){ + settings.setDefaults("user/my-plugin", [ + ["first", "1"], + ["second", "all"] + ]); + }); + + prefs.add({ + "Example" : { + position: 450, + "My Plugin" : { + position: 100, + "First Setting": { + type: "checkbox", + path: "user/my-plugin/@first", + position: 100 + }, + "Second Setting": { + type: "dropdown", + path: "user/my-plugin/@second", + width: "185", + position: 200, + items: [ + { value: "you", caption: "You" }, + { value: "me", caption: "Me" }, + { value: "all", caption: "All" } + ] + } + } + } + }, plugin); + } + + var drawn = false; + function draw() { + if (drawn) return; + drawn = true; + + // Insert HTML + var markup = require("text!./plugin.html"); + ui.insertHtml(document.body, markup, plugin); + + // Insert CSS + ui.insertCss(require("text!./style.css"), options.staticPrefix, plugin); + + emit("draw"); + } + + /***** Methods *****/ + + function show() { + draw(); + + var div = document.querySelector(".helloworld"); + div.style.display = "block"; + div.innerHTML = settings.get("user/my-plugin/@second"); + + emit("show"); + showing = true; + } + + function hide() { + if (!drawn) return; + + document.querySelector(".helloworld").style.display = "none"; + + emit("hide"); + showing = false; + } + + /***** Lifecycle *****/ + + plugin.on("load", function() { + load(); + }); + plugin.on("unload", function() { + drawn = false; + showing = false; + }); + + /***** Register and define API *****/ + + /** + * This is an example of an implementation of a plugin. + * @singleton + */ + plugin.freezePublicAPI({ + /** + * @property showing whether this plugin is being shown + */ + get showing(){ return showing; }, + + _events: [ + /** + * @event show The plugin is shown + */ + "show", + + /** + * @event hide The plugin is hidden + */ + "hide" + ], + + /** + * Show the plugin + */ + show: show, + + /** + * Hide the plugin + */ + hide: hide, + }); + + register(null, { + "myplugin": plugin + }); + } +}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin_test.js b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin_test.js new file mode 100644 index 00000000..3ae2262d --- /dev/null +++ b/plugins/c9.ide.plugins/mock/c9.ide.example3/plugin_test.js @@ -0,0 +1,41 @@ +"use client"; +"use mocha"; + +define(function(require, exports, module) { + main.consumes = ["plugin.test", "myplugin"]; + main.provides = []; + return main; + + function main(options, imports, register) { + var test = imports["plugin.test"]; + var myplugin = imports.myplugin; + + var describe = test.describe; + var it = test.it; + var before = test.before; + var after = test.after; + var beforeEach = test.beforeEach; + var afterEach = test.afterEach; + var assert = test.assert; + var expect = test.expect; + + /***** Initialization *****/ + + describe(myplugin.name, function(){ + this.timeout(2000); + + it("shows a helloworld div", function() { + myplugin.show(); + expect(document.querySelector(".helloworld")).to.ok; + expect(document.querySelector(".helloworld").innerText).to.equal("all"); + }); + + it("hides the div", function() { + myplugin.hide(); + expect(document.querySelector(".helloworld").offsetHeight).to.not.ok; + }); + }); + + register(null, {}); + } +}); \ No newline at end of file From 5db984340f9f8b62ce44750fc5550556c6a8d11d Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 23 Apr 2015 18:07:28 +0000 Subject: [PATCH 03/61] Cleanup --- configs/client-default.js | 2 +- .../c9.ide.plugins/{market.js => packages.js} | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) rename plugins/c9.ide.plugins/{market.js => packages.js} (90%) diff --git a/configs/client-default.js b/configs/client-default.js index 0e403b4e..12cdf67d 100644 --- a/configs/client-default.js +++ b/configs/client-default.js @@ -98,7 +98,7 @@ module.exports = function(options) { packagePath: "plugins/c9.ide.plugins/debug" }, { - packagePath: "plugins/c9.ide.plugins/market" + packagePath: "plugins/c9.ide.plugins/packages" }, { packagePath: "plugins/c9.ide.plugins/test", diff --git a/plugins/c9.ide.plugins/market.js b/plugins/c9.ide.plugins/packages.js similarity index 90% rename from plugins/c9.ide.plugins/market.js rename to plugins/c9.ide.plugins/packages.js index 592f9bc6..2438f50b 100644 --- a/plugins/c9.ide.plugins/market.js +++ b/plugins/c9.ide.plugins/packages.js @@ -3,7 +3,7 @@ define(function(require, exports, module) { "Editor", "editors", "ui", "commands", "menus", "layout", "tabManager", "util", "settings", "api", "c9" ]; - main.provides = ["plugin.market"]; + main.provides = ["plugin.packages"]; return main; function main(options, imports, register) { @@ -24,17 +24,17 @@ define(function(require, exports, module) { var extensions = []; var packages = {}; - var handle = editors.register("plugin.market", "Market Place", - MarketPlace, extensions); + var handle = editors.register("plugin.packages", "Packages Browser", + PackagesBrowser, extensions); var emit = handle.getEmitter(); emit.setMaxListeners(1000); var HASSDK = c9.location.indexOf("sdk=1") > -1; - function focusOpenMarket(){ + function focusOpenPackages(){ var pages = tabs.getTabs(); for (var i = 0, tab = pages[i]; tab; tab = pages[i++]) { - if (tab.editorType == "plugin.market") { + if (tab.editorType == "plugin.packages") { tabs.focusTab(tab); return true; } @@ -49,28 +49,28 @@ define(function(require, exports, module) { }); commands.addCommand({ - name: "openmarketplace", - hint: "open the market place", + name: "openpackagesbrowser", + hint: "open the packages browser", group: "General", // bindKey: { mac: "Command-,", win: "Ctrl-," }, exec: function () { var tab = tabs.focussedTab; - if (tab && tab.editor.type == "plugin.market") { + if (tab && tab.editor.type == "plugin.packages") { tab.close(); return; } - if (focusOpenMarket()) + if (focusOpenPackages()) return; tabs.open({ - editorType: "plugin.market", + editorType: "plugin.packages", active: true }, function(){}); } }, handle); menus.addItemByPath("Cloud9/Plugin Store", new ui.item({ - command: "openmarketplace" + command: "openpackagesbrowser" }), 301, handle); }); @@ -123,7 +123,7 @@ define(function(require, exports, module) { /***** Editor *****/ - function MarketPlace(){ + function PackagesBrowser(){ var plugin = new Editor("Ajax.org", main.consumes, extensions); //var emit = plugin.getEmitter(); var tab; @@ -212,13 +212,13 @@ define(function(require, exports, module) { }); - plugin.load(null, "plugin.market"); + plugin.load(null, "plugin.packages"); return plugin; } register(null, { - "plugin.market": handle + "plugin.packages": handle }); } }); \ No newline at end of file From f40b6fa368b426293e380440ed8b70bef5f27be5 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 23 Apr 2015 22:26:16 +0000 Subject: [PATCH 04/61] Enable the store in the IDE and load it in an iframe --- plugins/c9.ide.plugins/packages.js | 57 +++++++++++------------------- 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js index 2438f50b..368ed356 100644 --- a/plugins/c9.ide.plugins/packages.js +++ b/plugins/c9.ide.plugins/packages.js @@ -24,12 +24,12 @@ define(function(require, exports, module) { var extensions = []; var packages = {}; - var handle = editors.register("plugin.packages", "Packages Browser", - PackagesBrowser, extensions); + var handle = editors.register("plugin.packages", "Package Browser", + PackageBrowser, extensions); var emit = handle.getEmitter(); emit.setMaxListeners(1000); - var HASSDK = c9.location.indexOf("sdk=1") > -1; + var HASSDK = c9.location.indexOf("sdk=0") === -1; function focusOpenPackages(){ var pages = tabs.getTabs(); @@ -49,8 +49,8 @@ define(function(require, exports, module) { }); commands.addCommand({ - name: "openpackagesbrowser", - hint: "open the packages browser", + name: "openpackagebrowser", + hint: "open the package browser", group: "General", // bindKey: { mac: "Command-,", win: "Ctrl-," }, exec: function () { @@ -69,9 +69,10 @@ define(function(require, exports, module) { } }, handle); - menus.addItemByPath("Cloud9/Plugin Store", new ui.item({ - command: "openpackagesbrowser" - }), 301, handle); + menus.addItemByPath("Cloud9/~", new ui.divider(), 1000, handle); + menus.addItemByPath("Cloud9/Package Browser", new ui.item({ + command: "openpackagebrowser" + }), 1100, handle); }); /***** Methods *****/ @@ -123,10 +124,10 @@ define(function(require, exports, module) { /***** Editor *****/ - function PackagesBrowser(){ + function PackageBrowser(){ var plugin = new Editor("Ajax.org", main.consumes, extensions); //var emit = plugin.getEmitter(); - var tab; + var tab, iframe; plugin.on("resize", function(e) { emit("resize", e); @@ -136,33 +137,15 @@ define(function(require, exports, module) { tab = e.tab; var htmlNode = e.htmlNode; - api.packages.get("", function(err, list){ - if (c9.standalone) { - err = null; - list = [{ name: "example", apikey:"0000000000000000000000000000=", packagePath: "plugins/c9.example/example" }]; - } - - if (err) return; - - var sHtml = ""; - list.forEach(function(plugin){ // @todo use react instead in an iframe - packages[plugin.name] = plugin; - - sHtml += "
" - + "" + plugin.name + " | " - + "Install In Workspace | " - + "Install To User" - + "
"; - }); - - htmlNode.innerHTML = sHtml; - htmlNode.addEventListener("click", function(e){ - if (e.target.tagName == "A") { - installPlugin(e.target.getAttribute("plugin-name"), - e.target.getAttribute("target"), function(){}); - } - }); - }); + iframe = htmlNode.appendChild(document.createElement("iframe")); + iframe.style.position = "absolute"; + iframe.style.top = 0; + iframe.style.left = 0; + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.border = 0; + + iframe.src = location.origin + "/profile/packages?nobar=1&pid=" + c9.projectId; }); plugin.on("getState", function(e) { From 717116006501c8e9db027c8120fc900c378b930b Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 23 Apr 2015 22:27:36 +0000 Subject: [PATCH 05/61] Correct tab title --- plugins/c9.ide.plugins/packages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js index 368ed356..e040f432 100644 --- a/plugins/c9.ide.plugins/packages.js +++ b/plugins/c9.ide.plugins/packages.js @@ -157,7 +157,7 @@ define(function(require, exports, module) { plugin.on("documentLoad", function(e) { var doc = e.doc; - doc.title = "Plugin Store"; + doc.title = "Package Browser"; function setTheme(){ // var bg = ui.getStyleRule(".bar-preferences .container .header", "backgroundColor") || "#F0F0F0"; From 9264fd831002d5bc45d16a592198d6b6c2b38be9 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 23 Apr 2015 22:28:43 +0000 Subject: [PATCH 06/61] updated tab color --- plugins/c9.ide.plugins/packages.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js index e040f432..dd56ff87 100644 --- a/plugins/c9.ide.plugins/packages.js +++ b/plugins/c9.ide.plugins/packages.js @@ -160,9 +160,8 @@ define(function(require, exports, module) { doc.title = "Package Browser"; function setTheme(){ - // var bg = ui.getStyleRule(".bar-preferences .container .header", "backgroundColor") || "#F0F0F0"; - var bg = "#FFF"; - doc.tab.backgroundColor = bg; //"#2d2d2d"; + var bg = "#ededed"; + doc.tab.backgroundColor = bg; if (util.shadeColor(bg, 1).isLight) doc.tab.classList.remove("dark"); From 1a5dbfb5ec74156f556ffbcab7e772fa6b6142ba Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 24 Apr 2015 00:34:07 +0000 Subject: [PATCH 07/61] Loads the package browser in the IDE and many tweaks --- plugins/c9.ide.plugins/installer.js | 73 +++++++++++++++-------------- plugins/c9.ide.plugins/packages.js | 17 ++----- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/plugins/c9.ide.plugins/installer.js b/plugins/c9.ide.plugins/installer.js index 2e102bae..754788c0 100644 --- a/plugins/c9.ide.plugins/installer.js +++ b/plugins/c9.ide.plugins/installer.js @@ -13,6 +13,8 @@ define(function(require, exports, module) { var auth = imports.auth; var pubsub = imports.pubsub; + var async = require("async"); + var escapeShell = util.escapeShell; var updates = options.updates; var architect; @@ -20,11 +22,10 @@ define(function(require, exports, module) { /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); - // var emit = plugin.getEmitter(); + var emit = plugin.getEmitter(); var HASSDK = c9.location.indexOf("sdk=0") === -1; - var queue = []; var installing; var loaded = false; @@ -69,38 +70,40 @@ define(function(require, exports, module) { // return; // } - if (!config.length) return; + if (!config.length) + return callback && callback(); - var found = {}; - config.forEach(function(item){ - if (!found[item.packageName]) - found[item.packageName] = true; - else return; - - queue.push({ name: item.packageName, version: item.version }); - - if (installing) - installing.push(item); - }); - - if (installing) return; - installing = config; - - var i = 0; - function next(err){ - if (err) console.log(err); - - if (!queue[i]) { - installing = false; queue = []; - architect.loadAdditionalPlugins(config, callback); - return; - } - - installPlugin(queue[i].name, queue[i].version, next); - i++; + // Only run one installer at a time + if (installing) { + return plugin.once("finished", function(){ + installPlugins(config, callback); + }); } - next(); + installing = true; + + var found = {}, packages = []; + config.forEach(function(item){ + if (!found[item.name]) + found[item.name] = true; + else return; + + 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){ @@ -109,16 +112,17 @@ define(function(require, exports, module) { }, function(err, process){ if (err) return callback(err); + var output = ""; process.stdout.on("data", function(c){ - console.log(c); + output += c; }); process.stderr.on("data", function(c){ - console.error(c); + output += c; }); process.on("exit", function(code){ if (code) { - var error = new Error(err); + var error = new Error(output); error.code = code; return callback(error); } @@ -160,7 +164,6 @@ define(function(require, exports, module) { plugin.on("unload", function() { loaded = false; installing = false; - queue = []; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.plugins/packages.js b/plugins/c9.ide.plugins/packages.js index dd56ff87..4b466aef 100644 --- a/plugins/c9.ide.plugins/packages.js +++ b/plugins/c9.ide.plugins/packages.js @@ -137,15 +137,15 @@ define(function(require, exports, module) { tab = e.tab; var htmlNode = e.htmlNode; + htmlNode.style.paddingTop = 0; + iframe = htmlNode.appendChild(document.createElement("iframe")); - iframe.style.position = "absolute"; - iframe.style.top = 0; - iframe.style.left = 0; iframe.style.width = "100%"; iframe.style.height = "100%"; iframe.style.border = 0; + iframe.style.backgroundColor = "#fbfbfb"; - iframe.src = location.origin + "/profile/packages?nobar=1&pid=" + c9.projectId; + iframe.src = location.origin.replace("ide.", "") + "/profile/packages?nobar=1&pid=" + c9.projectId; }); plugin.on("getState", function(e) { @@ -160,7 +160,7 @@ define(function(require, exports, module) { doc.title = "Package Browser"; function setTheme(){ - var bg = "#ededed"; + var bg = "#fbfbfb"; doc.tab.backgroundColor = bg; if (util.shadeColor(bg, 1).isLight) @@ -174,14 +174,7 @@ define(function(require, exports, module) { }); plugin.on("documentActivate", function(e) { - e.doc.tab.on("unload", function(){ - if (parent.parentNode == tab) - tab.removeChild(parent); - }); - tab.appendChild(parent); - - emit("show"); }); /***** Register and define API *****/ From 52980d2c3ae559066364dbc90135cfdc310b10f3 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 24 Apr 2015 19:50:11 +0000 Subject: [PATCH 08/61] Enable new plugin --- plugins/c9.ide.plugins/manager.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 77294880..243779ce 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -102,7 +102,6 @@ define(function(require, exports, module) { // var emit = plugin.getEmitter(); var HASSDK = c9.location.indexOf("sdk=0") === -1; - var ENABLED = c9.location.indexOf("sdk=1") > -1; var model, datagrid, filterbox; var btnUninstall, btnReport, btnReadme, btnCloud9, btnReload; @@ -125,20 +124,18 @@ define(function(require, exports, module) { // updateCommandsFromSettings(); // }, plugin); - if (ENABLED) { - 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); - }); - } + 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; From 7fc4048213e3f9dae1fb94ed72e6b5041a9bf866 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 24 Apr 2015 22:44:32 +0000 Subject: [PATCH 09/61] Use installer for installing plugins (and removing failure). --- plugins/c9.ide.plugins/installer.js | 64 +++++++++++------------------ plugins/c9.nodeapi/events.js | 2 +- 2 files changed, 24 insertions(+), 42 deletions(-) diff --git a/plugins/c9.ide.plugins/installer.js b/plugins/c9.ide.plugins/installer.js index 754788c0..152e1824 100644 --- a/plugins/c9.ide.plugins/installer.js +++ b/plugins/c9.ide.plugins/installer.js @@ -1,6 +1,6 @@ define(function(require, exports, module) { main.consumes = [ - "Plugin", "proc", "c9", "pubsub", "auth", "util" + "Plugin", "proc", "c9", "pubsub", "auth", "util", "installer" ]; main.provides = ["plugin.installer"]; return main; @@ -12,6 +12,7 @@ define(function(require, exports, module) { var proc = imports.proc; var auth = imports.auth; var pubsub = imports.pubsub; + var installer = imports.installer; var async = require("async"); @@ -107,53 +108,29 @@ define(function(require, exports, module) { } function installPlugin(name, version, callback){ - proc.spawn("bash", { - args: ["-c", ["c9", "install", "--local", "--force", "--accessToken=" + auth.accessToken, escapeShell(name) + "@" + escapeShell(version)].join(" ")] - }, function(err, process){ - if (err) return callback(err); - - var output = ""; - process.stdout.on("data", function(c){ - output += c; - }); - process.stderr.on("data", function(c){ - output += c; + // Headless installation of the plugin + installer.createSession(name, version, function(session, options){ + session.install({ + "bash": "c9 install --local --force --accessToken=" + auth.accessToken + + " " + escapeShell(name) + "@" + escapeShell(version) }); - process.on("exit", function(code){ - if (code) { - var error = new Error(output); - error.code = code; - return callback(error); - } - callback(); - }); - }); + // Force to start immediately + session.start(callback, true); + }, function(){}, 2); // Force to not be administered } function uninstallPlugin(name, callback){ - proc.spawn("c9", { - args: ["remove", "--local", "--force", "--accessToken=" + auth.accessToken, escapeShell(name)] - }, function(err, process){ - if (err) return callback(err); - - var res = null; - process.stdout.on("data", function(c){ - res = c.toString("utf8"); - }); - process.stderr.on("data", function(c){ - err = c.toString("utf8"); + // Headless uninstallation of the plugin + installer.createSession(name, -1, function(session, options){ + session.install({ + "bash": "c9 remove --local --force --accessToken=" + auth.accessToken + + " " + escapeShell(name) }); - process.on("exit", function(code){ - if (code) { - var error = new Error(err); - error.code = code; - return callback(error); - } - callback(null, res); - }); - }); + // Force to start immediately + session.start(callback, true); + }, function(){}, 2); // Force to not be administered } /***** Lifecycle *****/ @@ -183,6 +160,11 @@ define(function(require, exports, module) { */ installPlugins: installPlugins, + /** + * + */ + installPlugin: installPlugin, + /** * */ diff --git a/plugins/c9.nodeapi/events.js b/plugins/c9.nodeapi/events.js index 5bf4e755..027cace5 100644 --- a/plugins/c9.nodeapi/events.js +++ b/plugins/c9.nodeapi/events.js @@ -146,7 +146,7 @@ EventEmitter.prototype.addListener = function(type, listener, plugin) { if (m && m > 0 && eventList.length > m) { eventList.warned = true; - console.error('(node) warning: possible EventEmitter memory ' + console.error('warning: possible EventEmitter memory ' + 'leak detected. " + eventList.length + " listeners of type "' + type + '" added. ' + 'Use emitter.setMaxListeners() to increase limit.' ); From 68de616dab93fef0f55ad9ee3f706a4e8ea2ecb1 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Mon, 27 Apr 2015 16:25:37 +0000 Subject: [PATCH 10/61] added some more templates --- plugins/c9.ide.plugins/manager.js | 4 +- .../templates/plugin.bundle/README.md | 1 + .../templates/plugin.bundle/package.json | 21 +++++++ .../templates/plugin.default/README.md | 2 - .../templates/plugin.default/package.json | 2 +- .../templates/plugin.installer/README.md | 1 + .../templates/plugin.installer/install.js | 57 +++++++++++++++++++ .../templates/plugin.installer/package.json | 22 +++++++ .../templates/plugin.simple/README.md | 2 - .../templates/plugin.simple/package.json | 2 +- 10 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 plugins/c9.ide.plugins/templates/plugin.bundle/README.md create mode 100644 plugins/c9.ide.plugins/templates/plugin.bundle/package.json create mode 100644 plugins/c9.ide.plugins/templates/plugin.installer/README.md create mode 100644 plugins/c9.ide.plugins/templates/plugin.installer/install.js create mode 100644 plugins/c9.ide.plugins/templates/plugin.installer/package.json diff --git a/plugins/c9.ide.plugins/manager.js b/plugins/c9.ide.plugins/manager.js index 243779ce..63c96f60 100644 --- a/plugins/c9.ide.plugins/manager.js +++ b/plugins/c9.ide.plugins/manager.js @@ -85,7 +85,9 @@ define(function(require, exports, module) { }; var TEMPLATES = { "plugin.simple": "Empty Plugin", - "plugin.default": "Full Plugin" + "plugin.default": "Full Plugin", + "plugin.installer": "Installer Plugin", + "plugin.bundle": "Cloud9 Bundle" }; // @TODO add sorting diff --git a/plugins/c9.ide.plugins/templates/plugin.bundle/README.md b/plugins/c9.ide.plugins/templates/plugin.bundle/README.md new file mode 100644 index 00000000..143f51ff --- /dev/null +++ b/plugins/c9.ide.plugins/templates/plugin.bundle/README.md @@ -0,0 +1 @@ +This is the Cloud9 bundle example \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.bundle/package.json b/plugins/c9.ide.plugins/templates/plugin.bundle/package.json new file mode 100644 index 00000000..dd39f32a --- /dev/null +++ b/plugins/c9.ide.plugins/templates/plugin.bundle/package.json @@ -0,0 +1,21 @@ +{ + "name": "", + "description": "", + "version": "0.0.1", + "author": "", + "contributors": [ + { + "name": "", + "email": "" + } + ], + "repository": { + "type": "git", + "url": "" + }, + "plugins": {}, + "categories": [ + "misc" + ], + "licenses": [] +} \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.default/README.md b/plugins/c9.ide.plugins/templates/plugin.default/README.md index 6d7f5b94..600c4e28 100644 --- a/plugins/c9.ide.plugins/templates/plugin.default/README.md +++ b/plugins/c9.ide.plugins/templates/plugin.default/README.md @@ -1,3 +1 @@ -# c9.ide.example - This is the Cloud9 default plugin example \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.default/package.json b/plugins/c9.ide.plugins/templates/plugin.default/package.json index 174c0262..3364c37a 100644 --- a/plugins/c9.ide.plugins/templates/plugin.default/package.json +++ b/plugins/c9.ide.plugins/templates/plugin.default/package.json @@ -1,5 +1,5 @@ { - "name": "c9.ide.default", + "name": "", "description": "", "version": "0.0.1", "author": "", diff --git a/plugins/c9.ide.plugins/templates/plugin.installer/README.md b/plugins/c9.ide.plugins/templates/plugin.installer/README.md new file mode 100644 index 00000000..9de9f3a9 --- /dev/null +++ b/plugins/c9.ide.plugins/templates/plugin.installer/README.md @@ -0,0 +1 @@ +This is the Cloud9 installer plugin example \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.installer/install.js b/plugins/c9.ide.plugins/templates/plugin.installer/install.js new file mode 100644 index 00000000..f87bdec5 --- /dev/null +++ b/plugins/c9.ide.plugins/templates/plugin.installer/install.js @@ -0,0 +1,57 @@ +define(function(require, exports, module) { + +module.exports = function(session, options){ + // Dependencies for the collaboration features of Cloud9 + + session.install({ + "name": "SQLite", + "description": "SQLite Database and NPM module", + "cwd": "~/.c9", + "optional": true + }, [ + { + "npm": ["sqlite3@3.0.5"] + }, + { + "tar.gz": { + "url": "https://raw.githubusercontent.com/c9/install/master/packages/sqlite3/linux/sqlite3.tar.gz", + "target": "~/.c9/lib/sqlite3", + "dir": "sqlite3" + } + }, + { + "symlink": { + "source": "~/.c9/lib/sqlite3/sqlite3", + "target": "~/.c9/bin/sqlite3" + } + } + ]); + + session.install({ + "name": "Sequalize", + "description": "Sequalize NPM module", + "cwd": "~/.c9", + "optional": true + }, { + "npm": ["sequelize@2.0.0-beta.0"] + }); + + session.install({ + "name": "Collab Server", + "description": "A small Node.js collaboration server", + "cwd": "~/.c9", + "optional": true + }, { + "tar.gz": { + "url": "https://raw.githubusercontent.com/c9/install/master/packages/extend/c9-vfs-extend.tar.gz", + "target": "~/.c9" + } + }); + + // Show the installation screen + session.start(); +}; + +module.exports.version = 1; + +}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.installer/package.json b/plugins/c9.ide.plugins/templates/plugin.installer/package.json new file mode 100644 index 00000000..9013bf77 --- /dev/null +++ b/plugins/c9.ide.plugins/templates/plugin.installer/package.json @@ -0,0 +1,22 @@ +{ + "name": "", + "description": "", + "version": "0.0.1", + "author": "", + "contributors": [ + { + "name": "", + "email": "" + } + ], + "repository": { + "type": "git", + "url": "" + }, + "plugins": {}, + "installer": "install.js", + "categories": [ + "misc" + ], + "licenses": [] +} \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.simple/README.md b/plugins/c9.ide.plugins/templates/plugin.simple/README.md index 566c1196..8ac7218b 100644 --- a/plugins/c9.ide.plugins/templates/plugin.simple/README.md +++ b/plugins/c9.ide.plugins/templates/plugin.simple/README.md @@ -1,3 +1 @@ -# c9.ide.simple - This is the Cloud9 simple plugin example \ No newline at end of file diff --git a/plugins/c9.ide.plugins/templates/plugin.simple/package.json b/plugins/c9.ide.plugins/templates/plugin.simple/package.json index f782d88e..3364c37a 100644 --- a/plugins/c9.ide.plugins/templates/plugin.simple/package.json +++ b/plugins/c9.ide.plugins/templates/plugin.simple/package.json @@ -1,5 +1,5 @@ { - "name": "c9.ide.simple", + "name": "", "description": "", "version": "0.0.1", "author": "", From 0a86081af0b8eb1d385aed7512b61a91cb00966b Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 30 Apr 2015 04:00:04 +0000 Subject: [PATCH 11/61] Refactored bridge to have bi-directional communication and request/response --- package.json | 2 +- plugins/c9.cli.bridge/json-stream.js | 47 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 plugins/c9.cli.bridge/json-stream.js diff --git a/package.json b/package.json index 354ffdc5..60d3a7ac 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", "c9.ide.find.replace": "#e4daf722b8", - "c9.ide.run.debug": "#638e6b00b3", + "c9.ide.run.debug": "#8b903a06d0", "c9.automate": "#47e2c429c9", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", diff --git a/plugins/c9.cli.bridge/json-stream.js b/plugins/c9.cli.bridge/json-stream.js new file mode 100644 index 00000000..3043020f --- /dev/null +++ b/plugins/c9.cli.bridge/json-stream.js @@ -0,0 +1,47 @@ +define(function(require, exports, module) { + +var EventEmitter = require("events").EventEmitter; + +module.exports = function(stream) { + var emit = this.emit; + + var buffer = ""; + stream.on("data", function(chunk) { + buffer += chunk; + + var parts = buffer.split("\n"); + while (parts.length) { + try { + var message = JSON.parse(buffer[0]); + emit("data", { message: message }); + buffer.shift(); + } + catch (e) { + if (parts.length !== 1) { + emit("error", e); + buffer.shift(); + } + else { + break; + } + } + } + buffer = parts.join("\n"); + }); + + stream.on("error", function(err){ + emit("error", err) + }); + + stream.on("close", function(data){ + emit("close", data); + }); + + this.write = function(data){ + stream.write(JSON.stringify(data)); + } +}; + +module.exports.prototype = new EventEmitter(); + +}); \ No newline at end of file From c78aba36b7ecb9431ef1a84213cbf8e3a1c3d79c Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 30 Apr 2015 04:01:13 +0000 Subject: [PATCH 12/61] Added missing files --- configs/client-default.js | 1 - plugins/c9.cli.bridge/bridge-client.js | 42 +++++++- plugins/c9.cli.bridge/bridge-service.js | 119 +++++++++++++++++++---- plugins/c9.cli.bridge/bridge.js | 75 +++++++------- plugins/c9.cli.bridge/bridge_commands.js | 14 ++- plugins/c9.cli.open/open.js | 10 +- 6 files changed, 184 insertions(+), 77 deletions(-) diff --git a/configs/client-default.js b/configs/client-default.js index 789b64f7..8274dc08 100644 --- a/configs/client-default.js +++ b/configs/client-default.js @@ -618,7 +618,6 @@ module.exports = function(options) { }, { packagePath: "plugins/c9.cli.bridge/bridge", - port: 17123, startBridge: options.startBridge }, { diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index a283cf0c..edf8bb29 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -13,27 +13,59 @@ define(function(require, exports, module) { var c9 = imports.c9; var net = imports.net; + var JSONStream = require("./json-stream"); + /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); // var emit = plugin.getEmitter(); - var PORT = options.port || 17123; + var counter = 0; + var SOCKET = c9.home + "/.c9/bridge.socket"; /***** Methods *****/ function send(message, callback) { - net.connect(PORT, {}, function(err, stream) { + net.connect(SOCKET, {}, function(err, stream) { if (err) return callback(err); - stream.write(JSON.stringify(message)); - stream.end(); + var jstream = new JSONStream(stream); + var msgId = generateMessageId(); + var done; - callback(); + stream.write(JSON.stringify({ + id: msgId, + message: message + })); + + jstream.on("data", function(payload){ + if (payload.id == msgId && !done) { + done = true; + callback(null, payload.message); + stream.end(); + } + }); + + jstream.on("error", function(err){ + if (done) return; + callback(err); + done = true; + }); + + jstream.on("close", function(){ + if (done) return; + callback(new Error("No Response")); + done = true; + }) }); } + function generateMessageId(){ + // Use vfs token + return Math.random() + "-" + ++counter; + } + /***** Lifecycle *****/ plugin.on("load", function(){ diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index fd6543b9..f65bd0b8 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -1,37 +1,114 @@ module.exports = function (vfs, options, register) { + var stream; + + var net = require("net"); var Stream = require('stream'); - var stream, server; + + var SOCKET = process.env.HOME + "/.c9/bridge.socket"; + + function createListenClient(api){ + var client = net.connect(SOCKET, function(data){ + if (data) api.onData(data); + }); + client.setEncoding("utf8"); + client.unref(); + + client.on("data", function(data){ + if (data) api.onData(data); + }); + + client.on("error", function(err){ + if (err.code == "ECONNREFUSED") { + require("fs").unlink(SOCKET, function(){ + createListenServer(api); + }); + } + else + api.onError(err); + }); + + client.on("end", function(){ + createListenServer(api); + }); + + api.onConnect(client); + + api.disconnect = function(){ + client.end(); + }; + + return client; + } + + function createListenServer(api){ + var unixServer = net.createServer(function(client) { + client.setEncoding("utf8"); + + client.on("data", function(data){ + if (data) api.onData(data); + }); + + client.on("error", function(data){ + // console.error("ERROR", api.id, data); + }); + + api.onConnect(client); + }); + unixServer.listen(SOCKET); + + unixServer.on("error", function(err){ + if (err.code == "EADDRINUSE") { + createListenClient(api); + } + else + api.onError(err); + }); + + api.disconnect = function(){ + unixServer.close(); + }; + } register(null, { - connect: function (port, callback) { + connect: function (callback) { if (stream) return callback(null, { stream: stream }); - server = require('net').createServer(function(c) { - var buffer = ""; - c.on("data", function(chunk) { - buffer += chunk; - }); - c.on("end", function(){ - stream.emit("data", buffer); - }); - }); - server.on("error", function(err) { - callback(err); - }); - server.listen(port, process.env.OPENSHIFT_DIY_IP || "localhost", function(err) { - if (err) return callback(err); - callback(null, { stream: stream }); - }); - stream = new Stream(); stream.readable = true; + stream.writable = true; + + var client; + var sent = false; + var api = this.api = { + id: Math.random(), + onConnect: function(c){ + client = c; + if (sent) return; + + callback(null, { stream: stream }); + sent = true; + }, + onData: function(data){ + stream.emit("data", data); + }, + onError: function(err){ + stream.emit("error", err); + } + }; + + createListenServer(api); + + stream.on("data", function(data){ + if (client) client.write(data); + }); }, disconnect: function(){ - try { server && server.close(); } + try { this.api && this.api.disconnect(); } catch (e) {} + stream = null; - server = null; + delete this.api; } }); }; \ No newline at end of file diff --git a/plugins/c9.cli.bridge/bridge.js b/plugins/c9.cli.bridge/bridge.js index bf39c7bc..bd7baa47 100644 --- a/plugins/c9.cli.bridge/bridge.js +++ b/plugins/c9.cli.bridge/bridge.js @@ -1,4 +1,3 @@ - define(function(require, exports, module) { main.consumes = ["c9", "Plugin", "ext"]; main.provides = ["bridge"]; @@ -8,6 +7,8 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var c9 = imports.c9; var ext = imports.ext; + + var JSONStream = require("./json-stream"); /***** Initialization *****/ @@ -15,15 +16,10 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); var ENABLED = options.startBridge !== false; - var PORT = options.port || 17123; var stream, api; - var loaded = false; function load(){ - if (loaded) return; - loaded = true; - if (!ENABLED) return; ext.loadRemotePlugin("bridge", { @@ -35,58 +31,52 @@ define(function(require, exports, module) { api = remote; - api.connect(PORT, function(err, meta) { - if (err) { - loaded = false; + api.connect(function(err, meta) { + if (err) + return console.error(err); // this should never happen - if (err.code == "EADDRINUSE") { - console.warn("Another Application is using port " - + PORT + ". CLI client interface disabled. Restart Cloud9 to retry connecting."); - } - else - console.error(err); - - return; - } - - stream = meta.stream; - - stream.on("data", function(chunk) { - try { var message = JSON.parse(chunk); } - catch (e) { - setTimeout(function(){ - loaded = false; - load(); - }, 60000); - return; - } - emit("message", { message: message }); + stream = new JSONStream(meta.stream); + + stream.on("error", function(err) { + console.error(err); + }); + + stream.on("data", function(payload) { + var response = emit("message", { message: payload.message }); + + stream.write({ + id: payload.id, + message: response + }); }); stream.on("close", function(){ - loaded = false; + load(); }); }); }); - window.addEventListener("unload", unload); + window.addEventListener("unload", function(){ + api && api.disconnect(); + }); } - function unload() { - api && api.disconnect(); - api = stream = null; - loaded = false; + function write(json){ + if (!stream) return false; + stream.write(JSON.stringify(json)); + return true; } - + /***** Methods *****/ plugin.on("load", function(){ c9.on("connect", load, plugin); - c9.on("disconnect", unload, plugin); }); plugin.on("unload", function(){ api && api.disconnect(); + stream = null; + api = null; }); /***** Register and define API *****/ @@ -94,7 +84,12 @@ define(function(require, exports, module) { /** * Bridge To Communicate from CLI to IDE **/ - plugin.freezePublicAPI({ }); + plugin.freezePublicAPI({ + /** + * + */ + write: write + }); register(null, { bridge: plugin diff --git a/plugins/c9.cli.bridge/bridge_commands.js b/plugins/c9.cli.bridge/bridge_commands.js index 496af20a..723e2f6c 100644 --- a/plugins/c9.cli.bridge/bridge_commands.js +++ b/plugins/c9.cli.bridge/bridge_commands.js @@ -23,22 +23,20 @@ define(function(require, exports, module) { var BASEPATH = options.basePath; - var loaded = false; function load(){ - if (loaded) return; - loaded = true; - bridge.on("message", function(e) { var message = e.message; switch (message.type) { case "open": open(message); - break; + return true; case "ping": - break; + return true; + default: + return false; } - }); + }, plugin); } /***** Methods *****/ @@ -61,7 +59,7 @@ define(function(require, exports, module) { var node = favs.addFavorite(path); - tree.expand(path, function(err) { + tree.expand(path, function() { tree.select(node); //path || "/"); tree.scrollToSelection(); }); diff --git a/plugins/c9.cli.open/open.js b/plugins/c9.cli.open/open.js index 0ef32d02..3a22ca45 100755 --- a/plugins/c9.cli.open/open.js +++ b/plugins/c9.cli.open/open.js @@ -90,7 +90,7 @@ define(function(require, exports, module) { paths: paths }; - bridge.send(message, function cb(err) { + bridge.send(message, function cb(err, response) { if (err) { if (err.code == "ECONNREFUSED") { // Seems Cloud9 is not running, lets start it up @@ -111,6 +111,9 @@ define(function(require, exports, module) { console.log(err.message); } + if (response !== true) + console.log("Could not open ", paths); + process.exit(); // I don't get why this is needed }); } @@ -129,13 +132,16 @@ define(function(require, exports, module) { var timed = Date.now(); (function retry(){ - bridge.send({ type: "ping" }, function(err) { + bridge.send({ type: "ping" }, function(err, message) { if (!err) return callback(true); if (Date.now() - timed > 10000) return callback(false); + if (message !== true) + return callback(false); + setTimeout(retry, 100); }); })(); From 5fcf82f3e0302a62caba570edf4904a8ef13736d Mon Sep 17 00:00:00 2001 From: Lennart kats Date: Thu, 30 Apr 2015 08:59:41 +0000 Subject: [PATCH 13/61] Add some more error metrics --- plugins/c9.ide.dialog.common/alert_internal.js | 5 ++++- plugins/c9.ide.dialog.common/error.js | 5 ++++- plugins/c9.ide.errorhandler/raygun_error_handler.js | 10 ++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/c9.ide.dialog.common/alert_internal.js b/plugins/c9.ide.dialog.common/alert_internal.js index 2b527375..c76edc94 100644 --- a/plugins/c9.ide.dialog.common/alert_internal.js +++ b/plugins/c9.ide.dialog.common/alert_internal.js @@ -1,11 +1,12 @@ define(function(require, module, exports) { - main.consumes = ["Dialog", "util", "dialog.alert"]; + main.consumes = ["Dialog", "util", "dialog.alert", "metrics"]; main.provides = ["dialog.alert_internal"]; return main; function main(options, imports, register) { var Dialog = imports.Dialog; var util = imports.util; + var metrics = imports.metrics; var alertWrapper = imports["dialog.alert"]; /***** Initialization *****/ @@ -25,6 +26,8 @@ define(function(require, module, exports) { /***** Methods *****/ function show(title, header, msg, onhide, options) { + metrics.increment("dialog.error"); + return plugin.queue(function(){ if (header === undefined) { plugin.title = "Notice"; diff --git a/plugins/c9.ide.dialog.common/error.js b/plugins/c9.ide.dialog.common/error.js index 7a6cfde5..4b4142e6 100644 --- a/plugins/c9.ide.dialog.common/error.js +++ b/plugins/c9.ide.dialog.common/error.js @@ -1,13 +1,14 @@ define(function(require, exports, module) { "use strict"; - main.consumes = ["Plugin", "ui"]; + main.consumes = ["Plugin", "ui", "metrics"]; main.provides = ["dialog.error"]; return main; function main(options, imports, register) { var Plugin = imports.Plugin; var ui = imports.ui; + var metrics = imports.metrics; /***** Initialization *****/ @@ -84,6 +85,8 @@ define(function(require, exports, module) { } function show(message, timeout) { + metrics.increment("dialog.error"); + // Error message container if (!error) { error = document.body.appendChild(document.createElement("div")); diff --git a/plugins/c9.ide.errorhandler/raygun_error_handler.js b/plugins/c9.ide.errorhandler/raygun_error_handler.js index 3c3dcca8..82ddc557 100644 --- a/plugins/c9.ide.errorhandler/raygun_error_handler.js +++ b/plugins/c9.ide.errorhandler/raygun_error_handler.js @@ -8,7 +8,7 @@ define(function(require, exports, module) { "use strict"; main.consumes = [ - "Plugin", "info" + "Plugin", "info", "metrics" ]; main.provides = ["error_handler"]; return main; @@ -16,6 +16,7 @@ define(function(require, exports, module) { function main(options, imports, register) { var Plugin = imports.Plugin; var info = imports.info; + var metrics = imports.metrics; /***** Initialization *****/ @@ -57,7 +58,8 @@ define(function(require, exports, module) { Raygun.setVersion(version + ".0"); } - function reportError(exception, customData, tags) { + function log(exception, customData, tags) { + metrics.increment("errorhandler.log"); if (typeof exception === "string") exception = new Error(exception); if (!exception) @@ -78,8 +80,8 @@ define(function(require, exports, module) { plugin.freezePublicAPI({ /** @deprecated Use log() instead. */ - reportError: reportError, - log: reportError + reportError: log, + log: log }); register(null, { "error_handler" : plugin }); From 02ffdd4e499efab3a164fc5ff0cc592780c4e087 Mon Sep 17 00:00:00 2001 From: Lennart Kats Date: Thu, 30 Apr 2015 11:25:41 +0200 Subject: [PATCH 14/61] Fix tests --- plugins/c9.vfs.standalone/www/test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/c9.vfs.standalone/www/test.js b/plugins/c9.vfs.standalone/www/test.js index cce8b53b..20e36d56 100644 --- a/plugins/c9.vfs.standalone/www/test.js +++ b/plugins/c9.vfs.standalone/www/test.js @@ -414,6 +414,8 @@ require([ "metrics": { getLastPing: function() { throw Error("Not implemented"); }, getLastest: function() { throw Error("Not implemented"); }, + log: function() {}, + increment: function() {} }, error_handler: {reportError: function(){}}, proc: { From b4e129dd42eeb53b1866e56beecaf52179e9cfb9 Mon Sep 17 00:00:00 2001 From: Lennart Kats Date: Thu, 30 Apr 2015 11:42:55 +0200 Subject: [PATCH 15/61] Add mock error.logger --- plugins/c9.vfs.standalone/www/test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/c9.vfs.standalone/www/test.js b/plugins/c9.vfs.standalone/www/test.js index 20e36d56..fe20012b 100644 --- a/plugins/c9.vfs.standalone/www/test.js +++ b/plugins/c9.vfs.standalone/www/test.js @@ -417,6 +417,9 @@ require([ log: function() {}, increment: function() {} }, + "error.logger": { + log: function() {}, + }, error_handler: {reportError: function(){}}, proc: { execFile: function() {}, From bd832b8cb29e556e963f95f42d6a29219c7f4b3c Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 30 Apr 2015 15:43:34 +0000 Subject: [PATCH 16/61] explicitly add files missed by build-server --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f47d5d3..6ab5a9b8 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", "c9.ide.find.replace": "#e4daf722b8", - "c9.ide.run.debug": "#06a7bd9615", + "c9.ide.run.debug": "#ed9d2ba07e", "c9.automate": "#47e2c429c9", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", From 290a9bf71d164b79c83d9cae2a0d165b8951b867 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Thu, 30 Apr 2015 22:30:29 +0000 Subject: [PATCH 17/61] Almost working --- plugins/c9.cli.bridge/bridge-client.js | 4 +- plugins/c9.cli.bridge/bridge-service.js | 4 +- plugins/c9.cli.bridge/bridge.js | 17 ++++--- plugins/c9.cli.bridge/bridge_commands.js | 63 +++++++++++++++++------- plugins/c9.cli.bridge/json-stream.js | 18 +++---- plugins/c9.cli.open/open.js | 18 +++++-- settings/standalone.js | 1 + 7 files changed, 86 insertions(+), 39 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index edf8bb29..2c8f9f3d 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -34,10 +34,10 @@ define(function(require, exports, module) { var msgId = generateMessageId(); var done; - stream.write(JSON.stringify({ + jstream.write({ id: msgId, message: message - })); + }); jstream.on("data", function(payload){ if (payload.id == msgId && !done) { diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index f65bd0b8..652ea360 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -89,10 +89,10 @@ module.exports = function (vfs, options, register) { sent = true; }, onData: function(data){ - stream.emit("data", data); + stream && stream.emit("data", data); }, onError: function(err){ - stream.emit("error", err); + stream && stream.emit("error", err); } }; diff --git a/plugins/c9.cli.bridge/bridge.js b/plugins/c9.cli.bridge/bridge.js index bd7baa47..db8b1c6d 100644 --- a/plugins/c9.cli.bridge/bridge.js +++ b/plugins/c9.cli.bridge/bridge.js @@ -42,12 +42,17 @@ define(function(require, exports, module) { }); stream.on("data", function(payload) { - var response = emit("message", { message: payload.message }); - - stream.write({ - id: payload.id, - message: response + emit("message", { + message: payload.message, + respond: function(err, message){ + stream.write({ + id: payload.id, + message: message, + error: err + }); + } }); + }); stream.on("close", function(){ @@ -63,7 +68,7 @@ define(function(require, exports, module) { function write(json){ if (!stream) return false; - stream.write(JSON.stringify(json)); + stream.write(json); return true; } diff --git a/plugins/c9.cli.bridge/bridge_commands.js b/plugins/c9.cli.bridge/bridge_commands.js index 723e2f6c..52a29f0f 100644 --- a/plugins/c9.cli.bridge/bridge_commands.js +++ b/plugins/c9.cli.bridge/bridge_commands.js @@ -4,18 +4,20 @@ define(function(require, exports, module) { "Plugin", "bridge", "tabManager", "panels", "tree.favorites", "tree", "fs" ]; - main.provides = ["bridge_commands"]; + main.provides = ["bridge.commands"]; return main; function main(options, imports, register) { var Plugin = imports.Plugin; var bridge = imports.bridge; - var tabs = imports.tabManager; + var tabManager = imports.tabManager; var panels = imports.panels; var tree = imports.tree; var favs = imports["tree.favorites"]; var fs = imports.fs; + var async = require("async"); + /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); @@ -29,28 +31,36 @@ define(function(require, exports, module) { switch (message.type) { case "open": - open(message); - return true; + open(message, e.respond); + break; case "ping": - return true; + e.respond(null, true); + break; default: - return false; + console.error("Unknown Bridge Command: ", message.type); + break; } }, plugin); } /***** Methods *****/ - function open(message) { - message.paths.forEach(function(info, i) { + function open(message, callback) { + var i = -1; + var tabs = []; + + async.each(message.paths, function(info, next) { var path = info.path; + i++; // Make sure file is inside workspace - if (path.substr(0, BASEPATH.length) !== BASEPATH) - return; - - // Remove base path - path = path.substr(BASEPATH.length); + if (path.charAt(0) !== "~") { + if (path.substr(0, BASEPATH.length) !== BASEPATH) + return; // Dont' call callback. Perhaps another client will pick this up. + + // Remove base path + path = path.substr(BASEPATH.length); + } if (info.type == "directory") { path = path.replace(/\/$/, ""); @@ -62,23 +72,42 @@ define(function(require, exports, module) { tree.expand(path, function() { tree.select(node); //path || "/"); tree.scrollToSelection(); + next(); }); tree.focus(); } else { - tabs.once("ready", function(){ + tabManager.once("ready", function(){ fs.exists(path, function(existing) { - tabs.open({ + var tab = tabManager.open({ path: path, active: i === 0, document: existing ? undefined : { meta : { newfile: true } } - }, function(){}); + }, function(){ + next(); + }); + + if (message.wait) { + tab.on("close", function(){ + tabs.splice(tabs.indexOf(tab), 1); + if (!tabs.length) + callback(null, true); + }); + } + + tabs.push(tab); }); }); } + }, function(err){ + if (err) + return callback(err); + + if (!message.wait || !tabs.length) + callback(null, true); }); } @@ -100,7 +129,7 @@ define(function(require, exports, module) { plugin.freezePublicAPI({}); register(null, { - "bridge_commands": plugin + "bridge.commands": plugin }); } }); diff --git a/plugins/c9.cli.bridge/json-stream.js b/plugins/c9.cli.bridge/json-stream.js index 3043020f..1f48ed05 100644 --- a/plugins/c9.cli.bridge/json-stream.js +++ b/plugins/c9.cli.bridge/json-stream.js @@ -3,7 +3,7 @@ define(function(require, exports, module) { var EventEmitter = require("events").EventEmitter; module.exports = function(stream) { - var emit = this.emit; + var emit = this.emit.bind(this); var buffer = ""; stream.on("data", function(chunk) { @@ -12,14 +12,14 @@ module.exports = function(stream) { var parts = buffer.split("\n"); while (parts.length) { try { - var message = JSON.parse(buffer[0]); - emit("data", { message: message }); - buffer.shift(); + var message = JSON.parse(parts[0]); + emit("data", message); + parts.shift(); } catch (e) { if (parts.length !== 1) { emit("error", e); - buffer.shift(); + parts.shift(); } else { break; @@ -30,16 +30,16 @@ module.exports = function(stream) { }); stream.on("error", function(err){ - emit("error", err) + emit("error", err); }); stream.on("close", function(data){ emit("close", data); }); - this.write = function(data){ - stream.write(JSON.stringify(data)); - } + this.write = function(data) { + stream.write(JSON.stringify(data) + "\n"); + }; }; module.exports.prototype = new EventEmitter(); diff --git a/plugins/c9.cli.open/open.js b/plugins/c9.cli.open/open.js index 3a22ca45..492ac3b7 100755 --- a/plugins/c9.cli.open/open.js +++ b/plugins/c9.cli.open/open.js @@ -25,8 +25,13 @@ define(function(require, exports, module) { cmd.addCommand({ name: "open", info: " Opens a file or directory.", - usage: "", + usage: "[--wait] ", options: { + "wait": { + description: "Wait until the file(s) are closed", + "default": false, + "boolean": true + }, "path" : { description: "Specify the path that will be opened", default: false @@ -39,6 +44,7 @@ define(function(require, exports, module) { exec: function(argv) { open( argv._.slice(1), // Remove "open" from the paths + argv.wait, function(){}); } }); @@ -46,12 +52,16 @@ define(function(require, exports, module) { /***** Methods *****/ - function open(paths, callback) { + function open(paths, wait, callback) { try { paths = paths.map(function(path) { var isDir = fs.existsSync(path) && fs.statSync(path).isDirectory(); + path = PATH.resolve(path); + if (path.substr(0, process.env.HOME.length) == process.env.HOME) + path = "~" + path.substr(process.env.HOME.length); + return { - path: "/" + PATH.resolve(path), + path: path, type: isDir ? "directory" : "file" }; }); @@ -65,6 +75,7 @@ define(function(require, exports, module) { paths.forEach(function(info) { var path = info.type == "directory" ? info.path : PATH.dirname(info.path); + if (!last) { last = path; } @@ -86,6 +97,7 @@ define(function(require, exports, module) { var message = { type: "open", workspace: "local", + wait: wait, // cwd : cwd, paths: paths }; diff --git a/settings/standalone.js b/settings/standalone.js index c65482db..4fd9ac74 100644 --- a/settings/standalone.js +++ b/settings/standalone.js @@ -28,6 +28,7 @@ module.exports = function(manifest, installPath) { var config = { standalone: true, + startBridge: true, manifest: manifest, workspaceDir: workspaceDir, projectName: path.basename(workspaceDir), From 69888da84289699052d5e380be407af48582f3c0 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 01:20:58 +0000 Subject: [PATCH 18/61] Added --wait and added to the terminal --- plugins/c9.cli.bridge/bridge-service.js | 4 ++-- plugins/c9.ide.terminal/tmux_connection.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index 652ea360..d1108dea 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -98,9 +98,9 @@ module.exports = function (vfs, options, register) { createListenServer(api); - stream.on("data", function(data){ + stream.write = function(data){ if (client) client.write(data); - }); + }; }, disconnect: function(){ diff --git a/plugins/c9.ide.terminal/tmux_connection.js b/plugins/c9.ide.terminal/tmux_connection.js index fafcfadb..3cd642fc 100644 --- a/plugins/c9.ide.terminal/tmux_connection.js +++ b/plugins/c9.ide.terminal/tmux_connection.js @@ -157,6 +157,9 @@ module.exports = function(c9, proc, installPath, shell) { options.output = false; options.terminal = true; options.detachOthers = !session.hasConnected; + options.env = { + "EDITOR": "`which c9` open --wait" + }; } // Connect to backend and start tmux session From 21b75cb86bab74d39643f9c829d8217ddf18e8d4 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 01:44:12 +0000 Subject: [PATCH 19/61] Add editor to the terminal --- node_modules/vfs-local/localfs.js | 2 +- plugins/c9.ide.terminal/tmux_connection.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/node_modules/vfs-local/localfs.js b/node_modules/vfs-local/localfs.js index 2ecf079e..ca64fc3c 100644 --- a/node_modules/vfs-local/localfs.js +++ b/node_modules/vfs-local/localfs.js @@ -1829,7 +1829,7 @@ module.exports = function setup(fsOptions) { options.command = "echo '[Idle]'"; if (options.terminal) - args.push("export ISOUTPUTPANE=0;" + BASH + " -l"); + args.push("export ISOUTPUTPANE=0; export EDITOR='`which c9` open --wait'; " + BASH + " -l"); else if (options.command) args.push((BASH + " -l -c '" diff --git a/plugins/c9.ide.terminal/tmux_connection.js b/plugins/c9.ide.terminal/tmux_connection.js index 3cd642fc..fafcfadb 100644 --- a/plugins/c9.ide.terminal/tmux_connection.js +++ b/plugins/c9.ide.terminal/tmux_connection.js @@ -157,9 +157,6 @@ module.exports = function(c9, proc, installPath, shell) { options.output = false; options.terminal = true; options.detachOthers = !session.hasConnected; - options.env = { - "EDITOR": "`which c9` open --wait" - }; } // Connect to backend and start tmux session From dd8ed52b4f18e3504b2cb96cee8a0b1a09f771ea Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 01:53:12 +0000 Subject: [PATCH 20/61] Add setting for the default editor --- node_modules/vfs-local/localfs.js | 9 ++++++-- plugins/c9.cli.bridge/bridge_commands.js | 24 ++++++++++++++++++++-- plugins/c9.ide.terminal/terminal.js | 3 +++ plugins/c9.ide.terminal/tmux_connection.js | 1 + 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/node_modules/vfs-local/localfs.js b/node_modules/vfs-local/localfs.js index ca64fc3c..6aefc38f 100644 --- a/node_modules/vfs-local/localfs.js +++ b/node_modules/vfs-local/localfs.js @@ -1828,8 +1828,13 @@ module.exports = function setup(fsOptions) { if (options.idle) options.command = "echo '[Idle]'"; - if (options.terminal) - args.push("export ISOUTPUTPANE=0; export EDITOR='`which c9` open --wait'; " + BASH + " -l"); + if (options.terminal) { + args.push("export ISOUTPUTPANE=0;" + + (options.defaultEditor + ? " export EDITOR='`which c9` open --wait'; " + : "") + + BASH + " -l"); + } else if (options.command) args.push((BASH + " -l -c '" diff --git a/plugins/c9.cli.bridge/bridge_commands.js b/plugins/c9.cli.bridge/bridge_commands.js index 52a29f0f..f21b5934 100644 --- a/plugins/c9.cli.bridge/bridge_commands.js +++ b/plugins/c9.cli.bridge/bridge_commands.js @@ -1,8 +1,8 @@ define(function(require, exports, module) { main.consumes = [ - "Plugin", "bridge", "tabManager", "panels", - "tree.favorites", "tree", "fs" + "Plugin", "bridge", "tabManager", "panels", "tree.favorites", "tree", + "fs", "preferences", "settings" ]; main.provides = ["bridge.commands"]; return main; @@ -13,8 +13,10 @@ define(function(require, exports, module) { var tabManager = imports.tabManager; var panels = imports.panels; var tree = imports.tree; + var settings = imports.settings; var favs = imports["tree.favorites"]; var fs = imports.fs; + var prefs = imports.preferences; var async = require("async"); @@ -41,6 +43,24 @@ define(function(require, exports, module) { break; } }, plugin); + + settings.on("read", function(e) { + settings.setDefaults("user/terminal", [ + ["defaultEditor", "true"] + ]); + }, plugin); + + prefs.add({ + "Editors" : { + "Terminal" : { + "Use Cloud9 as the Default Editor" : { + type: "checkbox", + path: "user/terminal/@defaultEditor", + position: 14000 + } + } + } + }, plugin); } /***** Methods *****/ diff --git a/plugins/c9.ide.terminal/terminal.js b/plugins/c9.ide.terminal/terminal.js index dcc8e9a0..05954eb6 100644 --- a/plugins/c9.ide.terminal/terminal.js +++ b/plugins/c9.ide.terminal/terminal.js @@ -750,6 +750,9 @@ define(function(require, exports, module) { session.__defineGetter__("tab", function(){ return doc.tab }); session.__defineGetter__("doc", function(){ return doc }); + session.__defineGetter__("defaultEditor", function(){ + return settings.getBool("user/terminal/@defaultEditor"); + }); session.attach = function(){ if (session.aceSession && aceterm) { diff --git a/plugins/c9.ide.terminal/tmux_connection.js b/plugins/c9.ide.terminal/tmux_connection.js index fafcfadb..1576ee80 100644 --- a/plugins/c9.ide.terminal/tmux_connection.js +++ b/plugins/c9.ide.terminal/tmux_connection.js @@ -157,6 +157,7 @@ module.exports = function(c9, proc, installPath, shell) { options.output = false; options.terminal = true; options.detachOthers = !session.hasConnected; + options.defaultEditor = session.defaultEditor; } // Connect to backend and start tmux session From a5104cab08bc0b29a33aaf674b3dcdcfed212e53 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 02:34:40 +0000 Subject: [PATCH 21/61] a better way to default to open --- plugins/c9.cli.open/open.js | 4 ---- plugins/c9.cli/cli.js | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/c9.cli.open/open.js b/plugins/c9.cli.open/open.js index 492ac3b7..7ff5cdcc 100755 --- a/plugins/c9.cli.open/open.js +++ b/plugins/c9.cli.open/open.js @@ -31,10 +31,6 @@ define(function(require, exports, module) { description: "Wait until the file(s) are closed", "default": false, "boolean": true - }, - "path" : { - description: "Specify the path that will be opened", - default: false } }, check: function(argv) { diff --git a/plugins/c9.cli/cli.js b/plugins/c9.cli/cli.js index 4a5923fc..91067b16 100755 --- a/plugins/c9.cli/cli.js +++ b/plugins/c9.cli/cli.js @@ -7,6 +7,9 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var cmd = imports.cli_commands; + var fs = require("fs"); + var resolve = require("path").resolve; + var optimist; /***** Initialization *****/ @@ -21,7 +24,7 @@ define(function(require, exports, module) { var module; var argv; - process.argv.slice(2).some(function(n){ + process.argv.slice(2).some(function(n) { if (!n.match(/^[-\/]/) && n != "node") { module = n; return true; @@ -29,6 +32,18 @@ define(function(require, exports, module) { return false; }); + if (!commands[module] && process.argv.length > 2) { + for (var i = 2; i < process.argv.length; i++) { + if (process.argv[i].charAt(0) == "-") continue; + var path = resolve(process.argv[i]); + if (fs.existsSync(path)) { + process.argv.splice(2, 0, "open"); + module = "open"; + } + break; + } + } + optimist = require('optimist'); if (!module || !commands[module]) { From e2f087d41b3cb388430fe5a64ad17f8577a02c49 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 12:13:47 +0400 Subject: [PATCH 22/61] fix cli-bridge on windows --- plugins/c9.cli.bridge/bridge-client.js | 4 +++- plugins/c9.cli.bridge/bridge-service.js | 6 ++++-- plugins/c9.cli.bridge/bridge_commands.js | 7 +++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index 2c8f9f3d..a7a6eab1 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -21,7 +21,9 @@ define(function(require, exports, module) { // var emit = plugin.getEmitter(); var counter = 0; - var SOCKET = c9.home + "/.c9/bridge.socket"; + var SOCKET = process.platform == "win32" + ? "\\\\.\\pipe\\"+ process.env.HOME +"\\.c9\\bridge.socket" + : process.env.HOME + "/.c9/bridge.socket"; /***** Methods *****/ diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index d1108dea..6acda88e 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -4,8 +4,10 @@ module.exports = function (vfs, options, register) { var net = require("net"); var Stream = require('stream'); - var SOCKET = process.env.HOME + "/.c9/bridge.socket"; - + var SOCKET = process.platform == "win32" + ? "\\\\.\\pipe\\"+ process.env.HOME +"\\.c9\\bridge.socket" + : process.env.HOME + "/.c9/bridge.socket"; + function createListenClient(api){ var client = net.connect(SOCKET, function(data){ if (data) api.onData(data); diff --git a/plugins/c9.cli.bridge/bridge_commands.js b/plugins/c9.cli.bridge/bridge_commands.js index f21b5934..bf64a756 100644 --- a/plugins/c9.cli.bridge/bridge_commands.js +++ b/plugins/c9.cli.bridge/bridge_commands.js @@ -2,7 +2,7 @@ define(function(require, exports, module) { main.consumes = [ "Plugin", "bridge", "tabManager", "panels", "tree.favorites", "tree", - "fs", "preferences", "settings" + "fs", "preferences", "settings", "c9" ]; main.provides = ["bridge.commands"]; return main; @@ -16,6 +16,7 @@ define(function(require, exports, module) { var settings = imports.settings; var favs = imports["tree.favorites"]; var fs = imports.fs; + var c9 = imports.c9; var prefs = imports.preferences; var async = require("async"); @@ -68,11 +69,13 @@ define(function(require, exports, module) { function open(message, callback) { var i = -1; var tabs = []; - + BASEPATH = c9.toInternalPath(BASEPATH); + async.each(message.paths, function(info, next) { var path = info.path; i++; + path = c9.toInternalPath(path); // Make sure file is inside workspace if (path.charAt(0) !== "~") { if (path.substr(0, BASEPATH.length) !== BASEPATH) From 0075a1df3ecd6ecd99904556539ab831b0346c02 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 12:14:25 +0400 Subject: [PATCH 23/61] fix node 0.12 warning about customFds --- node_modules/vfs-child/parent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_modules/vfs-child/parent.js b/node_modules/vfs-child/parent.js index 38474005..733570c6 100644 --- a/node_modules/vfs-child/parent.js +++ b/node_modules/vfs-child/parent.js @@ -15,7 +15,7 @@ function Parent(fsOptions) { options.uid = fsOptions.uid; delete fsOptions.uid; } - options.customFds = [-1, -1, 2]; + options.stdio = options.customFds = [-1, -1, 2]; var args = [require.resolve('./child.js'), JSON.stringify(fsOptions)]; var executablePath = process.execPath; var child; From 2f47c750dd2fa835ccf6ffa399829e3468c7fd15 Mon Sep 17 00:00:00 2001 From: Ivar Pruijn Date: Fri, 1 May 2015 10:20:18 +0000 Subject: [PATCH 24/61] theme experiment --- configs/client-default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/client-default.js b/configs/client-default.js index 789b64f7..5aa0cf6b 100644 --- a/configs/client-default.js +++ b/configs/client-default.js @@ -456,7 +456,7 @@ module.exports = function(options) { { packagePath: "plugins/c9.ide.layout.classic/preload", themePrefix: options.themePrefix, - defaultTheme: "dark" + defaultTheme: options.defaultTheme || "dark" }, { packagePath: "plugins/c9.ide.tree/tree", From ae5547b28d3ab91a1f1a8177ea9ab9276fd859bd Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 11:01:54 +0000 Subject: [PATCH 25/61] fix typo --- plugins/c9.vfs.standalone/www/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/c9.vfs.standalone/www/test.js b/plugins/c9.vfs.standalone/www/test.js index fe20012b..9ef583e1 100644 --- a/plugins/c9.vfs.standalone/www/test.js +++ b/plugins/c9.vfs.standalone/www/test.js @@ -417,10 +417,10 @@ require([ log: function() {}, increment: function() {} }, - "error.logger": { + error_handler: { log: function() {}, + reportError: function(){} }, - error_handler: {reportError: function(){}}, proc: { execFile: function() {}, spawn: function() {} From b413c310c476ab159f3363c39944b32adfff9cc6 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 13:02:28 +0000 Subject: [PATCH 26/61] Working on test. Doesn't work yet --- plugins/c9.cli.bridge/bridge-client.js | 4 +- plugins/c9.cli.bridge/bridge.js | 9 ++++- plugins/c9.cli.bridge/bridge_test.js | 52 ++++++++++++++++++-------- plugins/c9.cli.open/open.js | 4 +- plugins/c9.vfs.standalone/www/test.js | 1 + 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index 2c8f9f3d..d576ccc0 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -5,7 +5,7 @@ */ define(function(require, exports, module) { main.consumes = ["c9", "Plugin", "net"]; - main.provides = ["bridge-client"]; + main.provides = ["bridge.client"]; return main; function main(options, imports, register) { @@ -87,7 +87,7 @@ define(function(require, exports, module) { }); register(null, { - "bridge-client": plugin + "bridge.client": plugin }); } }); \ No newline at end of file diff --git a/plugins/c9.cli.bridge/bridge.js b/plugins/c9.cli.bridge/bridge.js index db8b1c6d..d1396622 100644 --- a/plugins/c9.cli.bridge/bridge.js +++ b/plugins/c9.cli.bridge/bridge.js @@ -58,6 +58,8 @@ define(function(require, exports, module) { stream.on("close", function(){ load(); }); + + emit.sticky("ready"); }); }); @@ -67,9 +69,12 @@ define(function(require, exports, module) { } function write(json){ - if (!stream) return false; + if (!stream) { + plugin.once("ready", function(){ write(json); }); + return; + } + stream.write(json); - return true; } /***** Methods *****/ diff --git a/plugins/c9.cli.bridge/bridge_test.js b/plugins/c9.cli.bridge/bridge_test.js index 253d9cca..3131db40 100644 --- a/plugins/c9.cli.bridge/bridge_test.js +++ b/plugins/c9.cli.bridge/bridge_test.js @@ -2,7 +2,7 @@ "use client"; -require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) { +require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"], function (architect, chai, basePath) { var expect = chai.expect; var Assert = chai.assert; @@ -14,46 +14,68 @@ require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) debug: true, hosted: true, local: false, - davPrefix: "/" + davPrefix: "/", + home: "/home/ubuntu" }, "plugins/c9.core/ext", "plugins/c9.core/http-xhr", "plugins/c9.core/util", - "plugins/c9.ide.ui/ui", "plugins/c9.core/settings", - //"plugins/c9.ide.collab/collab", "plugins/c9.vfs.client/vfs_client", "plugins/c9.vfs.client/endpoint", "plugins/c9.ide.auth/auth", "plugins/c9.fs/fs", + "plugins/c9.fs/net", + + { + packagePath: "plugins/c9.cli.bridge/bridge", + startBridge: true + }, + { + packagePath: "plugins/c9.cli.bridge/bridge_commands", + basePath: basePath + }, + "plugins/c9.cli.bridge/bridge-client", // Mock plugins { - consumes: ["ui"], + consumes: [], provides: [ - "preferences", "dialog.error" + "preferences", "ui" ], setup: expect.html.mocked }, { - consumes: ["collab"], + consumes: ["bridge", "bridge.client"], provides: [], setup: main } ], architect); function main(options, imports, register) { - var collab = imports.collab; + var bridge = imports.bridge; + var client = imports["bridge.client"]; - describe('collab', function() { - this.timeout(10000); + describe('bridge', function() { + // this.timeout(10000); - describe("connect", function(){ - it('should connect', function(done) { - collab.connect(null, function(err, stream) { - if (err) throw err.message; - }); + before(function(done){ + bridge.on("ready", function(){ + done(); + }); + }); + + it('send and receive messages', function(done) { + bridge.on("message", function(e){ + if (e.message.hello) { + e.respond({ "hi": true }); + } + }) + client.send({ "hello": true }, function(err, message){ + if (err) throw err.message; + expect(message).property("hi").to.be.ok; + done(); }); }); }); diff --git a/plugins/c9.cli.open/open.js b/plugins/c9.cli.open/open.js index 7ff5cdcc..d186f500 100755 --- a/plugins/c9.cli.open/open.js +++ b/plugins/c9.cli.open/open.js @@ -1,5 +1,5 @@ define(function(require, exports, module) { - main.consumes = ["Plugin", "cli_commands", "proc", "bridge-client"]; + main.consumes = ["Plugin", "cli_commands", "proc", "bridge.client"]; main.provides = ["open"]; return main; @@ -7,7 +7,7 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var cmd = imports.cli_commands; var proc = imports.proc; - var bridge = imports["bridge-client"]; + var bridge = imports["bridge.client"]; var fs = require("fs"); var PATH = require("path"); diff --git a/plugins/c9.vfs.standalone/www/test.js b/plugins/c9.vfs.standalone/www/test.js index cce8b53b..4c5d17af 100644 --- a/plugins/c9.vfs.standalone/www/test.js +++ b/plugins/c9.vfs.standalone/www/test.js @@ -195,6 +195,7 @@ require([ })(), log: {}, http: {}, + ui: {}, api: { stats: { post: function(type, message, cb) { From e150dc8a5aa5cb390f0359fdfc0b4281e5404fb9 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 17:36:36 +0400 Subject: [PATCH 27/61] fix typo --- plugins/c9.cli.bridge/bridge-client.js | 6 +++--- plugins/c9.cli.bridge/bridge_test.js | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index 4dc27dad..fbc3c05c 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -21,9 +21,9 @@ define(function(require, exports, module) { // var emit = plugin.getEmitter(); var counter = 0; - var SOCKET = process.platform == "win32" - ? "\\\\.\\pipe\\"+ process.env.HOME +"\\.c9\\bridge.socket" - : process.env.HOME + "/.c9/bridge.socket"; + var SOCKET = c9.platform == "win32" + ? "\\\\.\\pipe\\"+ c9.home +"\\.c9\\bridge.socket" + : c9.home + "/.c9/bridge.socket"; /***** Methods *****/ diff --git a/plugins/c9.cli.bridge/bridge_test.js b/plugins/c9.cli.bridge/bridge_test.js index 3131db40..54db6dbc 100644 --- a/plugins/c9.cli.bridge/bridge_test.js +++ b/plugins/c9.cli.bridge/bridge_test.js @@ -21,6 +21,8 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"], function (arc "plugins/c9.core/ext", "plugins/c9.core/http-xhr", "plugins/c9.core/util", + "plugins/c9.ide.ui/lib_apf", + "plugins/c9.ide.ui/ui", "plugins/c9.core/settings", "plugins/c9.vfs.client/vfs_client", "plugins/c9.vfs.client/endpoint", From 836fd9222f5d5bb58c71e7ca3404aa068a508769 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 14:19:15 +0000 Subject: [PATCH 28/61] fix vfs bug when sending streams/processes more than once. add fetching home for tests --- node_modules/vfs-socket/worker.js | 6 ++++++ plugins/c9.cli.bridge/bridge_test.js | 4 ++-- plugins/c9.vfs.standalone/standalone.js | 8 ++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/node_modules/vfs-socket/worker.js b/node_modules/vfs-socket/worker.js index 1d8547d6..54bd7ae7 100644 --- a/node_modules/vfs-socket/worker.js +++ b/node_modules/vfs-socket/worker.js @@ -162,6 +162,9 @@ function Worker(vfs) { var nextStreamID = 1; function storeStream(stream) { + if (stream.id) + return { id: stream.id }; + nextStreamID = (nextStreamID + 1) % 10000; while (streams.hasOwnProperty(nextStreamID)) { nextStreamID = (nextStreamID + 1) % 10000; } var id = nextStreamID; @@ -195,6 +198,9 @@ function Worker(vfs) { function storeProcess(process, onlyPid) { var pid = process.pid; + if (processes[pid]) + return onlyPid ? process.pid : { pid: process.pid }; + processes[pid] = process; process.on("exit", function (code, signal) { delete processes[pid]; diff --git a/plugins/c9.cli.bridge/bridge_test.js b/plugins/c9.cli.bridge/bridge_test.js index 54db6dbc..eae084be 100644 --- a/plugins/c9.cli.bridge/bridge_test.js +++ b/plugins/c9.cli.bridge/bridge_test.js @@ -2,7 +2,7 @@ "use client"; -require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"], function (architect, chai, basePath) { +require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "/vfs-home"], function (architect, chai, basePath, homePath) { var expect = chai.expect; var Assert = chai.assert; @@ -15,7 +15,7 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"], function (arc hosted: true, local: false, davPrefix: "/", - home: "/home/ubuntu" + home: homePath }, "plugins/c9.core/ext", diff --git a/plugins/c9.vfs.standalone/standalone.js b/plugins/c9.vfs.standalone/standalone.js index 137f8680..5779888e 100644 --- a/plugins/c9.vfs.standalone/standalone.js +++ b/plugins/c9.vfs.standalone/standalone.js @@ -156,6 +156,14 @@ function plugin(options, imports, register) { res.end("define(function(require, exports, module) { return '" + options.workspaceDir + "'; });"); }); + api.get("/vfs-home", function(req, res, next) { + if (!options.options.testing) + return next(); + + res.writeHead(200, {"Content-Type": "application/javascript"}); + res.end("define(function(require, exports, module) { return '" + + process.env.HOME + "'; });"); + }); api.get("/update", function(req, res, next) { res.writeHead(200, { From 6f34dd7a03af42a931c1fdb3d57c564c58c9a4b0 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 14:54:11 +0000 Subject: [PATCH 29/61] updated installer to install the cli --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60d3a7ac..aa6c990d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#a1e01c07a3", + "c9.ide.installer": "#f781a5f0e3", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From bde9054f84d2777ff7fda703e14b1b58d4a16ee2 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 15:17:29 +0000 Subject: [PATCH 30/61] Fixes adding c9 cli to the installer --- node_modules/vfs-socket/worker.js | 13 ++++++++----- package.json | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/node_modules/vfs-socket/worker.js b/node_modules/vfs-socket/worker.js index 54bd7ae7..56e7add8 100644 --- a/node_modules/vfs-socket/worker.js +++ b/node_modules/vfs-socket/worker.js @@ -162,8 +162,8 @@ function Worker(vfs) { var nextStreamID = 1; function storeStream(stream) { - if (stream.id) - return { id: stream.id }; + if (stream.token) + return stream.token; nextStreamID = (nextStreamID + 1) % 10000; while (streams.hasOwnProperty(nextStreamID)) { nextStreamID = (nextStreamID + 1) % 10000; } @@ -191,6 +191,7 @@ function Worker(vfs) { remote.onClose(id); }); var token = {id: id}; + stream.token = token; if (stream.hasOwnProperty("readable")) token.readable = stream.readable; if (stream.hasOwnProperty("writable")) token.writable = stream.writable; return token; @@ -198,8 +199,8 @@ function Worker(vfs) { function storeProcess(process, onlyPid) { var pid = process.pid; - if (processes[pid]) - return onlyPid ? process.pid : { pid: process.pid }; + if (processes.token) + return onlyPid ? process.pid : process.token; processes[pid] = process; process.on("exit", function (code, signal) { @@ -221,11 +222,13 @@ function Worker(vfs) { code: code }, callback || function() {}); }; + + var token = {pid: pid}; + process.token = token; if (onlyPid) return pid; - var token = {pid: pid}; token.stdin = storeStream(process.stdin); token.stdout = storeStream(process.stdout); token.stderr = storeStream(process.stderr); diff --git a/package.json b/package.json index aa6c990d..a6e1eb0c 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#f781a5f0e3", + "c9.ide.installer": "#2dcd0acc76", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From bfb93fb3167b098c4e85a2e3fb2b97270bb9392e Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 20:00:22 +0400 Subject: [PATCH 31/61] fix version detection in cli publish --- plugins/c9.cli.publish/publish.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index 8bb04348..99768d06 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -438,9 +438,9 @@ define(function(require, exports, module) { var path = join(cwd, json.installer); var installerCode = fs.readFileSync(path, "utf8"); - var m = installerCode.match(/\.version\s*=\s*(\d+)/g); + var m = installerCode.match(/\.version\s*=\s*(\d+)/); - var installerVersion = m && m[0]; + var installerVersion = m && m[1]; if (!installerVersion) return callback(new Error("ERROR: missing installer version in " + json.installer)); extraCode.push({ From 71f2d769d9318d760869a4d7c655cf438583a7ec Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 16:31:49 +0000 Subject: [PATCH 32/61] Fixed tests --- node_modules/vfs-local/localfs.js | 6 ++++++ package.json | 2 +- plugins/c9.cli.bridge/bridge-service.js | 18 +++++++++++++++--- plugins/c9.cli.bridge/bridge_test.js | 4 ++-- plugins/c9.cli.publish/install.js | 1 + 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/node_modules/vfs-local/localfs.js b/node_modules/vfs-local/localfs.js index 6aefc38f..c076aeaf 100644 --- a/node_modules/vfs-local/localfs.js +++ b/node_modules/vfs-local/localfs.js @@ -2339,6 +2339,9 @@ module.exports = function setup(fsOptions) { err.code = "EEXIST"; return callback(err, { api: apis[name] }); } + + if (options.redefine && apis[name] && apis[name].destroy) + apis[name].destroy(); var fn; @@ -2399,6 +2402,9 @@ module.exports = function setup(fsOptions) { } function unextend(name, options, callback) { + if (apis[name] && apis[name].destroy) + apis[name].destroy(); + delete apis[name]; callback(null, {}); } diff --git a/package.json b/package.json index a6e1eb0c..36ece324 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#2dcd0acc76", + "c9.ide.installer": "#312b28d5e8", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index 6acda88e..05a86853 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -21,10 +21,13 @@ module.exports = function (vfs, options, register) { client.on("error", function(err){ if (err.code == "ECONNREFUSED") { - require("fs").unlink(SOCKET, function(){ + require("fs").unlink(SOCKET, function(){ createListenServer(api); }); } + else if (err.code == "ENOENT") { + createListenServer(api); + } else api.onError(err); }); @@ -42,7 +45,11 @@ module.exports = function (vfs, options, register) { return client; } - function createListenServer(api){ + function createListenServer(api){ + // var timeout = setTimeout(function(){ + // unixServer.close(); + // }, 500); + var unixServer = net.createServer(function(client) { client.setEncoding("utf8"); @@ -98,7 +105,8 @@ module.exports = function (vfs, options, register) { } }; - createListenServer(api); + // createListenServer + createListenClient(api); stream.write = function(data){ if (client) client.write(data); @@ -111,6 +119,10 @@ module.exports = function (vfs, options, register) { stream = null; delete this.api; + }, + + destroy: function(){ + this.disconnect(); } }); }; \ No newline at end of file diff --git a/plugins/c9.cli.bridge/bridge_test.js b/plugins/c9.cli.bridge/bridge_test.js index eae084be..ccb93842 100644 --- a/plugins/c9.cli.bridge/bridge_test.js +++ b/plugins/c9.cli.bridge/bridge_test.js @@ -71,9 +71,9 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "/vfs-home"], it('send and receive messages', function(done) { bridge.on("message", function(e){ if (e.message.hello) { - e.respond({ "hi": true }); + e.respond(null, { "hi": true }); } - }) + }); client.send({ "hello": true }, function(err, message){ if (err) throw err.message; expect(message).property("hi").to.be.ok; diff --git a/plugins/c9.cli.publish/install.js b/plugins/c9.cli.publish/install.js index b1643e22..10be9751 100644 --- a/plugins/c9.cli.publish/install.js +++ b/plugins/c9.cli.publish/install.js @@ -140,6 +140,7 @@ define(function(require, exports, module) { cmd.addCommand({ name: "remove", + alias: "uninstall", info: " Removes a cloud9 package.", usage: "[--verbose] [--global] [--local] ", // @TODO --global options: { From 8ce9aa9d6153af057cad7010b581a433476f040d Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Fri, 1 May 2015 16:43:15 +0000 Subject: [PATCH 33/61] Fix pty processes --- node_modules/vfs-socket/worker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node_modules/vfs-socket/worker.js b/node_modules/vfs-socket/worker.js index 56e7add8..9512eb96 100644 --- a/node_modules/vfs-socket/worker.js +++ b/node_modules/vfs-socket/worker.js @@ -239,8 +239,8 @@ function Worker(vfs) { if (!pty || processes[pty.pid] == pty) // Pty is returned twice return pty && pty.token; - var pid = storeProcess(pty, true); - var token = storeStream(pty); + var pid = storeProcess(pty, true); delete pty.token; + var token = storeStream(pty); delete pty.token; token.pid = pid; pty.token = token; From 3294e0fba3e57b89be908e8f6f12976fd4b82f66 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 1 May 2015 22:08:46 +0400 Subject: [PATCH 34/61] better fix for windows --- configs/cli.js | 3 ++- package.json | 2 +- plugins/c9.cli.bridge/bridge-client.js | 2 +- plugins/c9.cli.bridge/bridge-service.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/configs/cli.js b/configs/cli.js index 8d95eff0..2a4be963 100644 --- a/configs/cli.js +++ b/configs/cli.js @@ -119,7 +119,8 @@ return [ local: true, home: process.env.HOME, setStatus: function(){}, - location: "" + location: "", + platform: process.platform, }, error_handler: { log: function(){} diff --git a/package.json b/package.json index a500a719..3021b59a 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#312b28d5e8", + "c9.ide.installer": "#591a1e6408", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", diff --git a/plugins/c9.cli.bridge/bridge-client.js b/plugins/c9.cli.bridge/bridge-client.js index fbc3c05c..f27353be 100644 --- a/plugins/c9.cli.bridge/bridge-client.js +++ b/plugins/c9.cli.bridge/bridge-client.js @@ -22,7 +22,7 @@ define(function(require, exports, module) { var counter = 0; var SOCKET = c9.platform == "win32" - ? "\\\\.\\pipe\\"+ c9.home +"\\.c9\\bridge.socket" + ? "\\\\.\\pipe\\.c9\\bridge.socket" : c9.home + "/.c9/bridge.socket"; /***** Methods *****/ diff --git a/plugins/c9.cli.bridge/bridge-service.js b/plugins/c9.cli.bridge/bridge-service.js index 05a86853..77e900c7 100644 --- a/plugins/c9.cli.bridge/bridge-service.js +++ b/plugins/c9.cli.bridge/bridge-service.js @@ -5,7 +5,7 @@ module.exports = function (vfs, options, register) { var Stream = require('stream'); var SOCKET = process.platform == "win32" - ? "\\\\.\\pipe\\"+ process.env.HOME +"\\.c9\\bridge.socket" + ? "\\\\.\\pipe\\.c9\\bridge.socket" : process.env.HOME + "/.c9/bridge.socket"; function createListenClient(api){ From d2c6ec3c621cbf74421fd106fc8be07f6775fbb5 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sat, 2 May 2015 01:51:30 +0000 Subject: [PATCH 35/61] Split install --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3021b59a..fa5a7a28 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#591a1e6408", + "c9.ide.installer": "#6f8f44c9e1", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From d8d798d6dac5a845dcc9bbe7e8b9175b53668f38 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sat, 2 May 2015 23:35:04 +0000 Subject: [PATCH 36/61] Added help to the cli --- plugins/c9.cli.publish/publish.js | 3 +-- plugins/c9.cli/cli.js | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index 99768d06..be5acac7 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -88,8 +88,7 @@ define(function(require, exports, module) { } }, check: function(argv) { - // if (argv._.length < 2 && !argv["newversion"] && !argv["dry-run"]) - // throw new Error("Missing version"); + }, exec: function(argv) { verbose = argv["verbose"]; diff --git a/plugins/c9.cli/cli.js b/plugins/c9.cli/cli.js index 91067b16..80e10d73 100755 --- a/plugins/c9.cli/cli.js +++ b/plugins/c9.cli/cli.js @@ -77,6 +77,11 @@ define(function(require, exports, module) { argv = optimist .usage("The Cloud9 CLI.\nUsage: c9 " + module + " [--help] " + def.usage) .options(def.options); + if (argv.help) + argv = argv.check(function(){ + if (argv.help) + throw new Error("Help Requested"); + }); if (def.check) argv = argv.check(def.check); argv = argv.argv; From 608a86c05c25aac4e01cffea8542754da0a5a352 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 00:18:05 +0000 Subject: [PATCH 37/61] A proper fix for help --- plugins/c9.cli/cli.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/c9.cli/cli.js b/plugins/c9.cli/cli.js index 80e10d73..6c0ec08a 100755 --- a/plugins/c9.cli/cli.js +++ b/plugins/c9.cli/cli.js @@ -77,7 +77,8 @@ define(function(require, exports, module) { argv = optimist .usage("The Cloud9 CLI.\nUsage: c9 " + module + " [--help] " + def.usage) .options(def.options); - if (argv.help) + + if (argv.argv.help) argv = argv.check(function(){ if (argv.help) throw new Error("Help Requested"); From 04fec89a2f78f23916246b0ed0b7e80aea4c13fc Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 03:08:43 +0000 Subject: [PATCH 38/61] Fixes typo in debug --- plugins/c9.cli.publish/install.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/c9.cli.publish/install.js b/plugins/c9.cli.publish/install.js index 10be9751..1ea7e584 100644 --- a/plugins/c9.cli.publish/install.js +++ b/plugins/c9.cli.publish/install.js @@ -388,8 +388,8 @@ define(function(require, exports, module) { if (verbose) console.log("Installing debug version of package"); - if (!options.test) - return callback(new Error("Dry run is not supported for debug installations")); + if (options.test) + return callback(new Error("Test is not supported for debug installations")); prepareDirectory(function(err, packagePath){ if (err) return callback(err); From 68a6a14d909f58e84b58199f6c8a267f49883a21 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 3 May 2015 14:55:08 +0400 Subject: [PATCH 39/61] fix clearUndo --- plugins/c9.ide.editors/undomanager.js | 15 +++++++-------- plugins/c9.ide.editors/undomanager_test.js | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/c9.ide.editors/undomanager.js b/plugins/c9.ide.editors/undomanager.js index 6ab1adc9..6560de05 100644 --- a/plugins/c9.ide.editors/undomanager.js +++ b/plugins/c9.ide.editors/undomanager.js @@ -10,7 +10,7 @@ define(function(require, module, exports) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); - var position = -1, mark = null, stack = []; + var position = -1, mark = -2, stack = []; if (options) setState(options); @@ -55,7 +55,7 @@ define(function(require, module, exports) { position = 0; if (mark < position) - mark = -1; + mark = -2; emit("change"); } @@ -64,7 +64,7 @@ define(function(require, module, exports) { stack = stack.slice(0, position + 1); if (mark > position) - mark = -1; + mark = -2; if (!noEvent) emit("change"); @@ -91,7 +91,7 @@ define(function(require, module, exports) { position--; if (mark == idx) - mark = -1; + mark = -2; else if (mark > idx) mark--; @@ -105,8 +105,7 @@ define(function(require, module, exports) { } function isAtBookmark(){ - return mark !== null && mark == position - || mark === null && position == -1; + return mark == position; } function item(idx) { @@ -133,7 +132,7 @@ define(function(require, module, exports) { return; // guard against broken stack stack = state.stack; - emit("change"); //If you remove this again, change the test + emit("change"); // If you remove this again, change the test } function findItem(compressedItem) { @@ -146,7 +145,7 @@ define(function(require, module, exports) { position = -1; stack = []; - mark = null; + mark = -1; emit("change"); } diff --git a/plugins/c9.ide.editors/undomanager_test.js b/plugins/c9.ide.editors/undomanager_test.js index b7624924..a3c57ada 100644 --- a/plugins/c9.ide.editors/undomanager_test.js +++ b/plugins/c9.ide.editors/undomanager_test.js @@ -133,7 +133,7 @@ require(["lib/architect/architect", "lib/chai/chai"], function (architect, chai) expect(undo.position).to.equal(4); undo.undo(); check++; - undo.setState({ position : -1, stack : stack, mark : null }); check++; + undo.setState({ position : -1, stack : stack, mark : -1 }); check++; expect(undo.isAtBookmark()).to.equal(true); expect(data).to.deep.equal(["a", "q"]); checkCount(); From ed19e9c37b66e366d808634f5728c0f51d503066 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 15:27:16 +0000 Subject: [PATCH 40/61] Fix typo and error reporting --- plugins/c9.cli.publish/install.js | 11 ++--------- plugins/c9.cli.publish/publish.js | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/plugins/c9.cli.publish/install.js b/plugins/c9.cli.publish/install.js index 1ea7e584..ce82d09c 100644 --- a/plugins/c9.cli.publish/install.js +++ b/plugins/c9.cli.publish/install.js @@ -77,10 +77,6 @@ define(function(require, exports, module) { "default": false, "boolean": true }, - "package" : { - description: "", - "default": false - }, "verbose" : { "description": "Output more information", "alias": "v", @@ -131,7 +127,7 @@ define(function(require, exports, module) { process.exit(1); } else { - console.log("Succesfully installed", name + (argv.debug ? "" : "@" + data.version)); + console.log("Successfully installed", name + (argv.debug ? "" : "@" + data.version)); process.exit(0); } }); @@ -154,9 +150,6 @@ define(function(require, exports, module) { "default": false, "boolean": true }, - "package" : { - description: "" - }, "verbose" : { "description": "Output more information", "alias": "v", @@ -187,7 +180,7 @@ define(function(require, exports, module) { process.exit(1); } else { - console.log("Succesfully removed", name); + console.log("Successfully removed", name); process.exit(0); } }); diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index be5acac7..e79214bd 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -723,7 +723,7 @@ define(function(require, exports, module) { request.on('response', function(res) { // TODO better handle version exists error - if (res.statusCode == 412 && !version) + if (res.statusCode == 412) console.error("ERROR: most likely version " + json.version + " already exisits, try increasing version"); if (res.statusCode != 200) return callback(new Error("ERROR: Unknown Error:" + res.statusCode)); From a7033da9751bb909a5831a4a56bdbfda0cdb997b Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 21 Dec 2014 23:01:01 +0400 Subject: [PATCH 41/61] convert paste into command --- .../ace/lib/ace/commands/default_commands.js | 12 ++++++++++++ node_modules/ace/lib/ace/editor.js | 9 ++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/node_modules/ace/lib/ace/commands/default_commands.js b/node_modules/ace/lib/ace/commands/default_commands.js index f1e267c1..dddce931 100644 --- a/node_modules/ace/lib/ace/commands/default_commands.js +++ b/node_modules/ace/lib/ace/commands/default_commands.js @@ -423,6 +423,12 @@ exports.commands = [{ exec: function() {}, passEvent: true, readOnly: true +}, { + name: "copy", + exec: function(editor) { + // placeholder for replay macro + }, + readOnly: true }, // commands disabled in readOnly mode @@ -439,6 +445,12 @@ exports.commands = [{ }, scrollIntoView: "cursor", multiSelectAction: "forEach" +}, { + name: "paste", + exec: function(editor, text) { + editor.$handlePaste(text); + }, + scrollIntoView: "cursor" }, { name: "removeline", bindKey: bindKey("Ctrl-D", "Command-D"), diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index 500cfc45..0602e7fd 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -903,10 +903,10 @@ var Editor = function(renderer, session) { * **/ this.onPaste = function(text) { - // todo this should change when paste becomes a command - if (this.$readOnly) - return; - + this.commands.exec("paste", this, text); + }; + + this.$handlePaste = function(text) { var e = {text: text}; this._signal("paste", e); text = e.text; @@ -927,7 +927,6 @@ var Editor = function(renderer, session) { this.session.insert(range.start, lines[i]); } } - this.renderer.scrollCursorIntoView(); }; this.execCommand = function(command, args) { From d614198e350e4915ff17960458afb687d83e0fb9 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 2 May 2015 05:09:14 +0400 Subject: [PATCH 42/61] fix collab showAuthorInfo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa5a7a28..4c3ea492 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "c9.ide.language.javascript.tern": "#7aab8b0b6a", "c9.ide.language.javascript.infer": "#cfec494a3c", "c9.ide.language.jsonalyzer": "#21b64e5820", - "c9.ide.collab": "#edef363853", + "c9.ide.collab": "#0b8d47b8b7", "c9.ide.local": "#2bfd7ff051", "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", From 333dee97da73da7febcbadd667f6478545e60b74 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 3 May 2015 20:43:36 +0400 Subject: [PATCH 43/61] fix +7113 Image width shows as 0px on image preview --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c3ea492..c9ebba40 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "c9.ide.fontawesome": "#781602c5d8", "c9.ide.format": "#f51451ac57", "c9.ide.help.support": "#60e88f5680", - "c9.ide.imgeditor": "#08bbc53578", + "c9.ide.imgeditor": "#ed89162aa7", "c9.ide.immediate": "#6845a93705", "c9.ide.installer": "#6f8f44c9e1", "c9.ide.mount": "#32e79866ee", From d3c66bee4c13ec22012bba0699deece46d0a64b0 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 3 May 2015 20:28:09 +0400 Subject: [PATCH 44/61] fix installer on windows --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c9ebba40..46b73fc0 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#ed89162aa7", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#6f8f44c9e1", + "c9.ide.installer": "#91b3cc12f8", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From 90397090803a0770a554d3e95e8442afa21f23ce Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 17:08:40 +0000 Subject: [PATCH 45/61] Some small cli fixes --- configs/cli.js | 3 ++- plugins/c9.cli/auth.bootstrap.js | 1 - plugins/c9.fs/fs.js | 2 +- plugins/c9.ide.auth/auth.js | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/configs/cli.js b/configs/cli.js index 2a4be963..d6f247e1 100644 --- a/configs/cli.js +++ b/configs/cli.js @@ -56,7 +56,8 @@ return [ packagePath: "./c9.ide.auth/auth", accessToken: "token", ideBaseUrl: "", - apiUrl: APIURL + apiUrl: APIURL, + cli: true // userId: process.env.C9_USER }, { diff --git a/plugins/c9.cli/auth.bootstrap.js b/plugins/c9.cli/auth.bootstrap.js index a24fd471..684225f1 100644 --- a/plugins/c9.cli/auth.bootstrap.js +++ b/plugins/c9.cli/auth.bootstrap.js @@ -66,7 +66,6 @@ define(function(require, exports, module) { }, function(err, token) { if (err) return callback(err); - fs.writeFile(AUTHPATH, token, function(err){ if (err) return callback(err); callback(null, lastToken = token); diff --git a/plugins/c9.fs/fs.js b/plugins/c9.fs/fs.js index a3145076..376f68fb 100644 --- a/plugins/c9.fs/fs.js +++ b/plugins/c9.fs/fs.js @@ -55,7 +55,7 @@ define(function(require, exports, module) { loaded = true; if (options.cli) - plugin.on("error", function(e){ console.error(e.error); }); + plugin.on("error", function(e){ }); // Prevent exception } function wrap(name, fn) { diff --git a/plugins/c9.ide.auth/auth.js b/plugins/c9.ide.auth/auth.js index 8508ba0f..54fd2633 100644 --- a/plugins/c9.ide.auth/auth.js +++ b/plugins/c9.ide.auth/auth.js @@ -80,7 +80,10 @@ define(function(require, exports, module) { request(apiUrl + "/user", function(err, user) { if (err || !user) { - console.warn("LOGIN: API /user err", err); + if (options.cli) + console.warn("Invalid username or password. Please try again."); + else + console.warn("LOGIN: API /user err", err); return setTimeout(login, 1000); } From fd0cecaa52e471e67bef8e81eb3ccd61392ab095 Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 18:51:55 +0000 Subject: [PATCH 46/61] Run apt-get update when needed --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa5a7a28..6f1f1bdb 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#6f8f44c9e1", + "c9.ide.installer": "#43bb327ca8", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From 0fa1dae1f3944472f1b017a83b7bdbc5dddb7f3b Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 19:19:20 +0000 Subject: [PATCH 47/61] run apt-get update when install fails --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f1f1bdb..844778b2 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "c9.ide.help.support": "#60e88f5680", "c9.ide.imgeditor": "#08bbc53578", "c9.ide.immediate": "#6845a93705", - "c9.ide.installer": "#43bb327ca8", + "c9.ide.installer": "#948cf86302", "c9.ide.mount": "#32e79866ee", "c9.ide.navigate": "#64156c7f4a", "c9.ide.newresource": "#f1f0624768", From 47a52d8250772bebd8681c0b4a403c7ee8828e1a Mon Sep 17 00:00:00 2001 From: Ruben Daniels Date: Sun, 3 May 2015 21:07:14 +0000 Subject: [PATCH 48/61] Fix error reporting of live update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14054eca..e4202933 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", "c9.ide.find.replace": "#e4daf722b8", - "c9.ide.run.debug": "#ed9d2ba07e", + "c9.ide.run.debug": "#23a188b91a", "c9.automate": "#47e2c429c9", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", From cc252235b0f792c58f0ad523a0fea4472a5bdb17 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 1 Dec 2014 19:17:34 +0400 Subject: [PATCH 49/61] do not undo edits of other users --- node_modules/ace/lib/ace/edit_session.js | 317 +++++++++-------------- node_modules/ace/lib/ace/editor.js | 10 +- node_modules/ace/lib/ace/undomanager.js | 241 +++++++++++------ package.json | 2 +- plugins/c9.ide.ace/ace.js | 58 ++++- 5 files changed, 340 insertions(+), 288 deletions(-) diff --git a/node_modules/ace/lib/ace/edit_session.js b/node_modules/ace/lib/ace/edit_session.js index bd984221..110d8c46 100644 --- a/node_modules/ace/lib/ace/edit_session.js +++ b/node_modules/ace/lib/ace/edit_session.js @@ -256,14 +256,23 @@ var EditSession = function(text, mode) { var removedFolds = this.$updateInternalDataOnChange(delta); if (!this.$fromUndo && this.$undoManager && !delta.ignore) { - this.$deltasDoc.push(delta); - if (removedFolds && removedFolds.length != 0) { - this.$deltasFold.push({ + if (removedFolds && removedFolds.length) { + this.$undoManager.add({ action: "removeFolds", folds: removedFolds - }); + }, this); + this.mergeUndoDeltas = true; } - + this.$undoManager.add(delta, this); + this.mergeUndoDeltas = true; + + var g = this.$undoManager.lastDeltas; + if (g && this.curOp && !this.curOp.group) { + this.curOp.group = g; + if (!g.selectionBefore) + g.selectionBefore = this.curOp.selectionBefore; + } + this.$informUndoManager.schedule(); } @@ -281,9 +290,6 @@ var EditSession = function(text, mode) { this.selection.moveTo(0, 0); this.$resetRowCache(0); - this.$deltas = []; - this.$deltasDoc = []; - this.$deltasFold = []; this.setUndoManager(this.$undoManager); this.getUndoManager().reset(); }; @@ -364,53 +370,26 @@ var EditSession = function(text, mode) { }; /** - * Sets the undo manager. - * @param {UndoManager} undoManager The new undo manager - * - * - **/ + * Sets the undo manager. + * @param {UndoManager} undoManager The new undo manager + * + * + **/ this.setUndoManager = function(undoManager) { this.$undoManager = undoManager; - this.$deltas = []; - this.$deltasDoc = []; - this.$deltasFold = []; - + if (this.$informUndoManager) this.$informUndoManager.cancel(); - + if (undoManager) { var self = this; - this.$syncInformUndoManager = function() { self.$informUndoManager.cancel(); - - if (self.$deltasFold.length) { - self.$deltas.push({ - group: "fold", - deltas: self.$deltasFold - }); - self.$deltasFold = []; - } - - if (self.$deltasDoc.length) { - self.$deltas.push({ - group: "doc", - deltas: self.$deltasDoc - }); - self.$deltasDoc = []; - } - - if (self.$deltas.length > 0) { - undoManager.execute({ - action: "aceupdate", - args: [self.$deltas, self], - merge: self.mergeUndoDeltas - }); - } self.mergeUndoDeltas = false; - self.$deltas = []; }; this.$informUndoManager = lang.delayedCall(this.$syncInformUndoManager); + } else { + this.$syncInformUndoManager = function() {}; } }; @@ -425,7 +404,9 @@ var EditSession = function(text, mode) { this.$defaultUndoManager = { undo: function() {}, redo: function() {}, - reset: function() {} + reset: function() {}, + add: function() {}, + startGroup: function() {}, }; /** @@ -1147,14 +1128,14 @@ var EditSession = function(text, mode) { }; /** - * Removes a range of full lines. This method also triggers the `'change'` event. - * @param {Number} firstRow The first row to be removed - * @param {Number} lastRow The last row to be removed - * @returns {[String]} Returns all the removed lines. - * - * @related Document.removeFullLines - * - **/ + * Removes a range of full lines. This method also triggers the `'change'` event. + * @param {Number} firstRow The first row to be removed + * @param {Number} lastRow The last row to be removed + * @returns {[String]} Returns all the removed lines. + * + * @related Document.removeFullLines + * + **/ this.removeFullLines = function(firstRow, lastRow){ return this.doc.removeFullLines(firstRow, lastRow); }; @@ -1163,34 +1144,27 @@ var EditSession = function(text, mode) { * Reverts previous changes to your document. * @param {Array} deltas An array of previous changes * @param {Boolean} dontSelect [If `true`, doesn't select the range of where the change occured]{: #dontSelect} - * * * @returns {Range} - **/ + **/ this.undoChanges = function(deltas, dontSelect) { if (!deltas.length) return; this.$fromUndo = true; - var lastUndoRange = null; for (var i = deltas.length - 1; i != -1; i--) { var delta = deltas[i]; - if (delta.group == "doc") { - this.doc.revertDeltas(delta.deltas); - lastUndoRange = - this.$getUndoSelection(delta.deltas, true, lastUndoRange); + if (delta.action == "insert" || delta.action == "remove") { + this.doc.revertDelta(delta); } else { - delta.deltas.forEach(function(foldDelta) { - this.addFolds(foldDelta.folds); - }, this); + this.addFolds(delta.folds); } } this.$fromUndo = false; - lastUndoRange && - this.$undoSelect && - !dontSelect && - this.selection.setSelectionRange(lastUndoRange); - return lastUndoRange; + if (deltas.selectionBefore) + this.selection.fromJSON(deltas.selectionBefore); + else + this.selection.setRange(this.$getUndoSelection(deltas, true)); }; /** @@ -1198,7 +1172,6 @@ var EditSession = function(text, mode) { * @param {Array} deltas An array of previous changes * @param {Boolean} dontSelect {:dontSelect} * - * * @returns {Range} **/ this.redoChanges = function(deltas, dontSelect) { @@ -1206,28 +1179,24 @@ var EditSession = function(text, mode) { return; this.$fromUndo = true; - var lastUndoRange = null; for (var i = 0; i < deltas.length; i++) { var delta = deltas[i]; - if (delta.group == "doc") { - this.doc.applyDeltas(delta.deltas); - lastUndoRange = - this.$getUndoSelection(delta.deltas, false, lastUndoRange); + if (delta.action == "insert" || delta.action == "remove") { + this.doc.applyDelta(delta); } } this.$fromUndo = false; - lastUndoRange && - this.$undoSelect && - !dontSelect && - this.selection.setSelectionRange(lastUndoRange); - return lastUndoRange; + if (deltas.selectionAfter) + this.selection.fromJSON(deltas.selectionAfter); + else + this.selection.setRange(this.$getUndoSelection(deltas, false)); }; /** * Enables or disables highlighting of the range where an undo occured. * @param {Boolean} enable If `true`, selects the range of the reinserted change - * - **/ + * + **/ this.setUndoSelect = function(enable) { this.$undoSelect = enable; }; @@ -1236,92 +1205,47 @@ var EditSession = function(text, mode) { function isInsert(delta) { return isUndo ? delta.action !== "insert" : delta.action === "insert"; } - - var delta = deltas[0]; - var range, point; - var lastDeltaIsInsert = false; - if (isInsert(delta)) { - range = Range.fromPoints(delta.start, delta.end); - lastDeltaIsInsert = true; + var i; + if (isUndo) { + i = 0; + while (!deltas[i].start && i < deltas.length) + i++; } else { - range = Range.fromPoints(delta.start, delta.start); - lastDeltaIsInsert = false; - } - - for (var i = 1; i < deltas.length; i++) { - delta = deltas[i]; - if (isInsert(delta)) { - point = delta.start; - if (range.compare(point.row, point.column) == -1) { - range.setStart(point); - } - point = delta.end; - if (range.compare(point.row, point.column) == 1) { - range.setEnd(point); - } - lastDeltaIsInsert = true; - } else { - point = delta.start; - if (range.compare(point.row, point.column) == -1) { - range = Range.fromPoints(delta.start, delta.start); - } - lastDeltaIsInsert = false; - } - } - - // Check if this range and the last undo range has something in common. - // If true, merge the ranges. - if (lastUndoRange != null) { - if (Range.comparePoints(lastUndoRange.start, range.start) === 0) { - lastUndoRange.start.column += range.end.column - range.start.column; - lastUndoRange.end.column += range.end.column - range.start.column; - } - - var cmp = lastUndoRange.compareRange(range); - if (cmp == 1) { - range.setStart(lastUndoRange.start); - } else if (cmp == -1) { - range.setEnd(lastUndoRange.end); - } - } - - return range; + i = deltas.length - 1; + while (!deltas[i].end && i >= 0) + i--; + } + var pos = isInsert(deltas[i]) ? deltas[i].end : deltas[i].start; + return Range.fromPoints(pos, pos); }; /** - * Replaces a range in the document with the new `text`. - * - * @param {Range} range A specified Range to replace - * @param {String} text The new text to use as a replacement - * @returns {Object} An object containing the final row and column, like this: - * ``` - * {row: endRow, column: 0} - * ``` - * If the text and range are empty, this function returns an object containing the current `range.start` value. - * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. - * - * - * - * @related Document.replace - * - * - **/ + * Replaces a range in the document with the new `text`. + * + * @param {Range} range A specified Range to replace + * @param {String} text The new text to use as a replacement + * @returns {Object} An object containing the final row and column, like this: + * ``` + * {row: endRow, column: 0} + * ``` + * If the text and range are empty, this function returns an object containing the current `range.start` value. + * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value. + * + * @related Document.replace + **/ this.replace = function(range, text) { return this.doc.replace(range, text); }; /** - * Moves a range of text from the given range to the given position. `toPosition` is an object that looks like this: + * Moves a range of text from the given range to the given position. `toPosition` is an object that looks like this: * ```json - * { row: newRowLocation, column: newColumnLocation } + * { row: newRowLocation, column: newColumnLocation } * ``` * @param {Range} fromRange The range of text you want moved within the document * @param {Object} toPosition The location (row and column) where you want to move the text to * @returns {Range} The new range where the text was moved to. - * - * - * - **/ + **/ this.moveText = function(fromRange, toPosition, copy) { var text = this.getTextRange(fromRange); var folds = this.getFoldsInRange(fromRange); @@ -1365,15 +1289,15 @@ var EditSession = function(text, mode) { }; /** - * Indents all the rows, from `startRow` to `endRow` (inclusive), by prefixing each row with the token in `indentString`. - * - * If `indentString` contains the `'\t'` character, it's replaced by whatever is defined by [[EditSession.getTabString `getTabString()`]]. - * @param {Number} startRow Starting row - * @param {Number} endRow Ending row - * @param {String} indentString The indent token - * - * - **/ + * Indents all the rows, from `startRow` to `endRow` (inclusive), by prefixing each row with the token in `indentString`. + * + * If `indentString` contains the `'\t'` character, it's replaced by whatever is defined by [[EditSession.getTabString `getTabString()`]]. + * @param {Number} startRow Starting row + * @param {Number} endRow Ending row + * @param {String} indentString The indent token + * + * + **/ this.indentRows = function(startRow, endRow, indentString) { indentString = indentString.replace(/\t/g, this.getTabString()); for (var row=startRow; row<=endRow; row++) @@ -1381,11 +1305,10 @@ var EditSession = function(text, mode) { }; /** - * Outdents all the rows defined by the `start` and `end` properties of `range`. - * @param {Range} range A range of rows - * - * - **/ + * Outdents all the rows defined by the `start` and `end` properties of `range`. + * @param {Range} range A range of rows + * + **/ this.outdentRows = function (range) { var rowRange = range.collapseRows(); var deleteRange = new Range(0, 0, 0, 0); @@ -1443,34 +1366,34 @@ var EditSession = function(text, mode) { return diff; }; /** - * Shifts all the lines in the document up one, starting from `firstRow` and ending at `lastRow`. - * @param {Number} firstRow The starting row to move up - * @param {Number} lastRow The final row to move up - * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. - * - **/ + * Shifts all the lines in the document up one, starting from `firstRow` and ending at `lastRow`. + * @param {Number} firstRow The starting row to move up + * @param {Number} lastRow The final row to move up + * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. + * + **/ this.moveLinesUp = function(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, -1); }; /** - * Shifts all the lines in the document down one, starting from `firstRow` and ending at `lastRow`. - * @param {Number} firstRow The starting row to move down - * @param {Number} lastRow The final row to move down - * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. - **/ + * Shifts all the lines in the document down one, starting from `firstRow` and ending at `lastRow`. + * @param {Number} firstRow The starting row to move down + * @param {Number} lastRow The final row to move down + * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1. + **/ this.moveLinesDown = function(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, 1); }; /** - * Duplicates all the text between `firstRow` and `lastRow`. - * @param {Number} firstRow The starting row to duplicate - * @param {Number} lastRow The final row to duplicate - * @returns {Number} Returns the number of new rows added; in other words, `lastRow - firstRow + 1`. - * - * - **/ + * Duplicates all the text between `firstRow` and `lastRow`. + * @param {Number} firstRow The starting row to duplicate + * @param {Number} lastRow The final row to duplicate + * @returns {Number} Returns the number of new rows added; in other words, `lastRow - firstRow + 1`. + * + * + **/ this.duplicateLines = function(firstRow, lastRow) { return this.$moveLines(firstRow, lastRow, 0); }; @@ -1545,8 +1468,7 @@ var EditSession = function(text, mode) { * Sets whether or not line wrapping is enabled. If `useWrapMode` is different than the current value, the `'changeWrapMode'` event is emitted. * @param {Boolean} useWrapMode Enable (or disable) wrap mode * - * - **/ + **/ this.setUseWrapMode = function(useWrapMode) { if (useWrapMode != this.$useWrapMode) { this.$useWrapMode = useWrapMode; @@ -1565,9 +1487,9 @@ var EditSession = function(text, mode) { }; /** - * Returns `true` if wrap mode is being used; `false` otherwise. - * @returns {Boolean} - **/ + * Returns `true` if wrap mode is being used; `false` otherwise. + * @returns {Boolean} + **/ this.getUseWrapMode = function() { return this.$useWrapMode; }; @@ -1581,8 +1503,7 @@ var EditSession = function(text, mode) { * @param {Number} min The minimum wrap value (the left side wrap) * @param {Number} max The maximum wrap value (the right side wrap) * - * - **/ + **/ this.setWrapLimitRange = function(min, max) { if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { this.$wrapLimitRange = { min: min, max: max }; @@ -1594,12 +1515,12 @@ var EditSession = function(text, mode) { }; /** - * This should generally only be called by the renderer when a resize is detected. - * @param {Number} desiredLimit The new wrap limit - * @returns {Boolean} - * - * @private - **/ + * This should generally only be called by the renderer when a resize is detected. + * @param {Number} desiredLimit The new wrap limit + * @returns {Boolean} + * + * @private + **/ this.adjustWrapLimit = function(desiredLimit, $printMargin) { var limits = this.$wrapLimitRange; if (limits.max < 0) diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index 0602e7fd..60359ddd 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -144,13 +144,13 @@ var Editor = function(renderer, session) { } this.$opResetTimer.schedule(); - this.curOp = { + this.curOp = this.session.curOp = { command: commadEvent.command || {}, args: commadEvent.args, scrollTop: this.renderer.scrollTop }; - - // this.selections.push(this.selection.toJSON()); + this.curOp.selectionBefore = this.selection.toJSON(); + // this.selections.push(); }; this.endOperation = function(e) { @@ -185,7 +185,9 @@ var Editor = function(renderer, session) { if (scrollIntoView == "animate") this.renderer.animateScrolling(this.curOp.scrollTop); } - + this.curOp.selectionAfter = this.selection.toJSON(); + if (this.curOp.group) + this.curOp.group.selectionAfter = this.curOp.selectionAfter; this.prevOp = this.curOp; this.curOp = null; } diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index 6da50a8b..5a778191 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -32,15 +32,11 @@ define(function(require, exports, module) { "use strict"; /** - * - * * This object maintains the undo stack for an [[EditSession `EditSession`]]. * @class UndoManager **/ /** - * - * * Resets the current undo state and creates a new `UndoManager`. * * @constructor @@ -60,17 +56,21 @@ var UndoManager = function() { * @param {Object} options Contains additional properties * **/ - this.execute = function(options) { + this.add = function(delta, doc) { // Normalize deltas for storage. // var deltaSets = this.$serializeDeltas(options.args[0]); - var deltaSets = options.args[0]; + // Add deltas to undo stack. - this.$doc = options.args[1]; - if (options.merge && this.hasUndo()){ - this.dirtyCounter--; - deltaSets = this.$undoStack.pop().concat(deltaSets); + this.$doc = doc; + // if (options.merge && this.hasUndo()){ + // this.dirtyCounter--; + // deltaSets = this.$undoStack.pop().concat(deltaSets); + // } + if (doc.mergeUndoDeltas === false || !this.lastDeltas) { + this.lastDeltas = []; + this.$undoStack.push(this.lastDeltas); } - this.$undoStack.push(deltaSets); + this.lastDeltas.push(delta); // Reset redo stack. this.$redoStack = []; @@ -81,46 +81,50 @@ var UndoManager = function() { } this.dirtyCounter++; }; + + this.startGroup = function() { + this.lastDeltas = null; + }; /** * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} * @param {Boolean} dontSelect {:dontSelect} * - * * @returns {Range} The range of the undo. **/ - this.undo = function(dontSelect) { - var deltaSets = this.$undoStack.pop(); + this.undo = function() { + this.lastDeltas = null; + var stack = this.$undoStack; + UndoManager.rebase(stack, stack.length); + var deltaSet = stack.pop(); var undoSelectionRange = null; - if (deltaSets) { - undoSelectionRange = this.$doc.undoChanges(this.$deserializeDeltas(deltaSets), dontSelect); - this.$redoStack.push(deltaSets); + if (deltaSet) { + undoSelectionRange = this.$doc.undoChanges(deltaSet); + this.$redoStack.push(deltaSet); this.dirtyCounter--; } return undoSelectionRange; }; - + /** * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} * @param {Boolean} dontSelect {:dontSelect} * - * **/ this.redo = function(dontSelect) { - var deltaSets = this.$redoStack.pop(); + this.lastDeltas = null; + var deltaSet = this.$redoStack.pop(); var redoSelectionRange = null; - if (deltaSets) { - redoSelectionRange = - this.$doc.redoChanges(this.$deserializeDeltas(deltaSets), dontSelect); - this.$undoStack.push(deltaSets); + if (deltaSet) { + redoSelectionRange = this.$doc.redoChanges(deltaSet); + this.$undoStack.push(deltaSet); this.dirtyCounter++; } return redoSelectionRange; }; /** - * * Destroys the stack of undo and redo redo operations. **/ this.reset = function() { @@ -130,7 +134,6 @@ var UndoManager = function() { }; /** - * * Returns `true` if there are undo operations left to perform. * @returns {Boolean} **/ @@ -139,7 +142,6 @@ var UndoManager = function() { }; /** - * * Returns `true` if there are redo operations left to perform. * @returns {Boolean} **/ @@ -148,7 +150,6 @@ var UndoManager = function() { }; /** - * * Marks the current status clean **/ this.markClean = function() { @@ -156,7 +157,6 @@ var UndoManager = function() { }; /** - * * Returns if the current status is clean * @returns {Boolean} **/ @@ -164,52 +164,147 @@ var UndoManager = function() { return this.dirtyCounter === 0; }; - // Serializes deltaSets to reduce memory usage. - this.$serializeDeltas = function(deltaSets) { - return cloneDeltaSetsObj(deltaSets, $serializeDelta); - }; - - // Deserializes deltaSets to allow application to the document. - this.$deserializeDeltas = function(deltaSets) { - return cloneDeltaSetsObj(deltaSets, $deserializeDelta); - }; - - function $serializeDelta(delta){ - return { - action: delta.action, - start: delta.start, - end: delta.end, - lines: delta.lines.length == 1 ? null : delta.lines, - text: delta.lines.length == 1 ? delta.lines[0] : null, - }; - } - - function $deserializeDelta(delta) { - return { - action: delta.action, - start: delta.start, - end: delta.end, - lines: delta.lines || [delta.text] - }; - } - - function cloneDeltaSetsObj(deltaSets_old, fnGetModifiedDelta) { - var deltaSets_new = new Array(deltaSets_old.length); - for (var i = 0; i < deltaSets_old.length; i++) { - var deltaSet_old = deltaSets_old[i]; - var deltaSet_new = { group: deltaSet_old.group, deltas: new Array(deltaSet_old.length)}; - - for (var j = 0; j < deltaSet_old.deltas.length; j++) { - var delta_old = deltaSet_old.deltas[j]; - deltaSet_new.deltas[j] = fnGetModifiedDelta(delta_old); - } - - deltaSets_new[i] = deltaSet_new; - } - return deltaSets_new; - } + // // Serializes deltaSets to reduce memory usage. + // this.$serializeDeltas = function(deltaSets) { + // return cloneDeltaSetsObj(deltaSets, $serializeDelta); + // }; + // // Deserializes deltaSets to allow application to the document. + // this.$deserializeDeltas = function(deltaSets) { + // return cloneDeltaSetsObj(deltaSets, $deserializeDelta); + // }; + }).call(UndoManager.prototype); +UndoManager.rebase = function(stack, pos) { + for (var i = pos; i--; ) { + var deltaSet = stack[i]; + if (deltaSet && !deltaSet[0].disabled) { + while(i < pos - 1) { + var swapped = swapGroups(stack[i], stack[i + 1]); + stack[i] = swapped[0]; + stack[i + 1] = swapped[1]; + i++; + } + break; + } + } +}; + + +/* + * i i d1 d2 + * |/ |/ d2.s >= d1.e shift(d2, d1, -1) + * d2.s <= d1.s shift(d1, d2, +1) + * d1.s < d2.s < d1.e // can split + * + * i r d1 d2 + * |/ |\ d2.s >= d1.e shift(d2, d1, -1) + * d2.e <= d1.s shift(d1, d2, -1) + * else // can't swap + * + * r i d1 d2 + * |\ |/ d2.s >= d1.s shift(d2, d1, +1) + * d2.s <= d1.s shift(d1, d2, +1) + * // no else + * + * r r d1 d2 + * |\ |\ d2.s >= d1.s shift(d2, d1, +1) + * d2.e <= d1.s shift(d1, d2, -1) + * d2.s < d1.s < d2.e // can split + */ +var Range = require("./range").Range; +var cmp = Range.comparePoints; +function swap(d1, d2) { + var i1 = d1.action == "insert"; + var i2 = d2.action == "insert"; + + if (i1 && i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (i1 && !i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } else if (!i1 && i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (!i1 && !i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } + return [d2, d1]; + + function shift(d1, d2, dir) { + shiftPos(d1.start, d2.start, d2.end, dir); + shiftPos(d1.end, d2.start, d2.end, dir); + } + function shiftPos(pos, start, end, dir) { + if (pos.row == (dir == 1 ? start : end).row) { + pos.column += dir * (end.column - start.column); + } + pos.row += dir * (end.row - start.row); + } +} +function clonePos(pos) { + return {row: pos.row,column: pos.column}; +} +function cloneDelta(d) { + return { + start: clonePos(d.start), + end: clonePos(d.end), + action: d.action, + lines: d.lines.slice(), + toString: logDelta + }; +} +function logDelta(d) { + d = d || this; + var type = d.action == "insert" ? "+" : "-"; + return type + "[" + d.lines + "]" + + d.start.row + ":" + d.start.column + "=>" + + d.end.row + ":" + d.end.column; +} +function swapGroups(ds1, ds2) { + for (var i = ds1.length; i--; ) { + for (var j = 0; j < ds2.length; j++) { + if (!swap(ds1[i], ds2[j])) { + // rollback, we have to undo ds2 first + while (i < ds1.length) { + while (j--) { + swap(ds2[j], ds1[i]); + } + j = ds2.length; + i++; + } + return [ds1, ds2]; + } + } + } + ds1.selectionBefore = ds2.selectionBefore = + ds1.selectionAfter = ds2.selectionAfter = null; + return [ds2, ds1]; +} + +UndoManager.swapGroups = swapGroups; exports.UndoManager = UndoManager; }); diff --git a/package.json b/package.json index e4202933..b32c2cda 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "c9.ide.language.javascript.tern": "#7aab8b0b6a", "c9.ide.language.javascript.infer": "#cfec494a3c", "c9.ide.language.jsonalyzer": "#21b64e5820", - "c9.ide.collab": "#0b8d47b8b7", + "c9.ide.collab": "#2f568cc2a7", "c9.ide.local": "#2bfd7ff051", "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 4609d862..6459aad0 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -40,13 +40,14 @@ define(function(require, exports, module) { var lang = require("ace/lib/lang"); var Range = require("ace/range").Range; var config = require("ace/config"); - var AceEditor = require("ace/editor").Editor; var Document = require("ace/document").Document; + var AceEditor = require("ace/editor").Editor; var EditSession = require("ace/edit_session").EditSession; + var UndoManager = require("ace/undomanager").UndoManager; + var whitespaceUtil = require("ace/ext/whitespace"); var defaultCommands = require("ace/commands/default_commands").commands; var VirtualRenderer = require("ace/virtual_renderer").VirtualRenderer; var multiSelectCommands = require("ace/multi_select").commands; - var whitespaceUtil = require("ace/ext/whitespace"); // enable multiselect require("ace/multi_select"); @@ -283,47 +284,80 @@ define(function(require, exports, module) { function AceUndoManager(undoManager, session) { this.$session = session; this.$undo = undoManager; + this.$aceUndo = new UndoManager(); var _self = this; var Item = this.Item; this.$undo.on("itemFind", function(e) { return Item(_self, e.state); }); } + function updateDeltas(deltas) { + if (deltas[0] && deltas[0].deltas) { + var oldDeltas = deltas.slice(); + deltas.length = 0; + oldDeltas.forEach(function(x) { + deltas.push.apply(deltas, x.deltas); + }); + } + } AceUndoManager.prototype = { + rebase: function() { + var pos = this.$undo.position + 2; + var stack = this.$undo.stack; + for (var i = pos; i--; ) { + var deltaSet = stack[i].deltas; + if (deltaSet[0] && !deltaSet[0].disabled) { + while(i < pos - 1) { + var swapped = UndoManager.swapGroups(stack[i].deltas, stack[i + 1].deltas); + stack[i].deltas = swapped[0]; + stack[i + 1].deltas = swapped[1]; + i++; + } + break; + } + } + }, Item: function(_self, deltas) { return { + deltas: deltas, undo: function(){ - _self.$session.session.undoChanges(deltas, _self.dontSelect); + updateDeltas(this.deltas); // change to the new format + _self.rebase(); + _self.$session.session.undoChanges(this.deltas, _self.dontSelect); }, redo: function(){ - _self.$session.session.redoChanges(deltas, _self.dontSelect); + updateDeltas(this.deltas); + _self.$session.session.redoChanges(this.deltas, _self.dontSelect); }, - getState: function(){ - return deltas.filter(function (d) { - return d.group != "fold"; + getState: function(){ + updateDeltas(this.deltas); + return this.deltas.filter(function (d) { + return d.action == "insert" || d.action == "remove"; }); } }; }, - execute: function(options) { - if (options.merge && this.lastDeltas) { - this.lastDeltas.push.apply(this.lastDeltas, options.args[0]); - } else { - this.lastDeltas = options.args[0]; + add: function(delta, doc) { + if (doc.mergeUndoDeltas === false || !this.lastDeltas) { + this.lastDeltas = []; this.$undo.add(this.Item(this, this.lastDeltas)); } + this.lastDeltas.push(delta); }, undo: function(dontSelect) { this.dontSelect = dontSelect; + this.lastDeltas = null; this.$undo.undo(); }, redo: function(dontSelect) { this.dontSelect = dontSelect; + this.lastDeltas = null; this.$undo.redo(); }, reset: function(){ + this.lastDeltas = null; this.$undo.reset(); }, hasUndo: function() { From 0d10299299e3fec8f4ed3bbc010a1f0959ebcfb7 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 2 Dec 2014 15:54:01 +0400 Subject: [PATCH 50/61] update tests to work with sync undomanager events --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b32c2cda..a18d2b36 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "c9.ide.run": "#71c5562e42", "c9.ide.run.build": "#ad45874c88", "c9.ide.run.debug.xdebug": "#b91d23f48b", - "c9.ide.save": "#b876d87d55", + "c9.ide.save": "#3cb206c168", "c9.ide.terminal.monitor": "#b0b4d03280", "c9.ide.theme.flat": "#b1d65fa9bb", "c9.ide.threewaymerge": "#229382aa0b", From 9405b78dced44ea055a42ff38eb92be5eb9bae3e Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 2 Dec 2014 17:56:43 +0400 Subject: [PATCH 51/61] do not group local edits with remote ones --- package.json | 2 +- plugins/c9.ide.ace/ace.js | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a18d2b36..c253ca32 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "c9.ide.language.javascript.tern": "#7aab8b0b6a", "c9.ide.language.javascript.infer": "#cfec494a3c", "c9.ide.language.jsonalyzer": "#21b64e5820", - "c9.ide.collab": "#2f568cc2a7", + "c9.ide.collab": "#749c26b859", "c9.ide.local": "#2bfd7ff051", "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 6459aad0..c7e35fd7 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -313,7 +313,10 @@ define(function(require, exports, module) { stack[i + 1].deltas = swapped[1]; i++; } - break; + deltaSet = stack[pos - 1].deltas; + if (deltaSet[0].disabled) + deltaSet[0].disabled = false; + return deltaSet; } } }, @@ -322,8 +325,10 @@ define(function(require, exports, module) { deltas: deltas, undo: function(){ updateDeltas(this.deltas); // change to the new format - _self.rebase(); - _self.$session.session.undoChanges(this.deltas, _self.dontSelect); + var deltas = _self.rebase(); + if (!deltas) + return false; + _self.$session.session.undoChanges(deltas, _self.dontSelect); }, redo: function(){ updateDeltas(this.deltas); From f04f22076cf0f279917ad8f57a8cb38a6a101e6d Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 22 Dec 2014 00:59:08 +0400 Subject: [PATCH 52/61] allow redoing after changing the document --- node_modules/ace/lib/ace/edit_session.js | 32 +- node_modules/ace/lib/ace/editor.js | 32 +- node_modules/ace/lib/ace/undomanager.js | 380 ++++++++++++++++++----- package.json | 2 +- 4 files changed, 342 insertions(+), 104 deletions(-) diff --git a/node_modules/ace/lib/ace/edit_session.js b/node_modules/ace/lib/ace/edit_session.js index 110d8c46..cfd7b174 100644 --- a/node_modules/ace/lib/ace/edit_session.js +++ b/node_modules/ace/lib/ace/edit_session.js @@ -255,7 +255,7 @@ var EditSession = function(text, mode) { this.$resetRowCache(delta.start.row); var removedFolds = this.$updateInternalDataOnChange(delta); - if (!this.$fromUndo && this.$undoManager && !delta.ignore) { + if (!this.$fromUndo && this.$undoManager) { if (removedFolds && removedFolds.length) { this.$undoManager.add({ action: "removeFolds", @@ -266,13 +266,6 @@ var EditSession = function(text, mode) { this.$undoManager.add(delta, this); this.mergeUndoDeltas = true; - var g = this.$undoManager.lastDeltas; - if (g && this.curOp && !this.curOp.group) { - this.curOp.group = g; - if (!g.selectionBefore) - g.selectionBefore = this.curOp.selectionBefore; - } - this.$informUndoManager.schedule(); } @@ -383,6 +376,7 @@ var EditSession = function(text, mode) { if (undoManager) { var self = this; + undoManager.$session = this; this.$syncInformUndoManager = function() { self.$informUndoManager.cancel(); self.mergeUndoDeltas = false; @@ -406,7 +400,8 @@ var EditSession = function(text, mode) { redo: function() {}, reset: function() {}, add: function() {}, - startGroup: function() {}, + addSelection: function() {}, + startNewGroup: function() {}, }; /** @@ -1156,15 +1151,18 @@ var EditSession = function(text, mode) { var delta = deltas[i]; if (delta.action == "insert" || delta.action == "remove") { this.doc.revertDelta(delta); - } else { + } else if (delta.folds) { this.addFolds(delta.folds); } } + if (!dontSelect) { + // console.log(deltas.selectionBefore + "uuu") + if (deltas.selectionBefore) + this.selection.fromJSON(deltas.selectionBefore); + else + this.selection.setRange(this.$getUndoSelection(deltas, true)); + } this.$fromUndo = false; - if (deltas.selectionBefore) - this.selection.fromJSON(deltas.selectionBefore); - else - this.selection.setRange(this.$getUndoSelection(deltas, true)); }; /** @@ -1173,7 +1171,7 @@ var EditSession = function(text, mode) { * @param {Boolean} dontSelect {:dontSelect} * * @returns {Range} - **/ + **/ this.redoChanges = function(deltas, dontSelect) { if (!deltas.length) return; @@ -1195,7 +1193,7 @@ var EditSession = function(text, mode) { /** * Enables or disables highlighting of the range where an undo occured. * @param {Boolean} enable If `true`, selects the range of the reinserted change - * + * **/ this.setUndoSelect = function(enable) { this.$undoSelect = enable; @@ -1214,7 +1212,7 @@ var EditSession = function(text, mode) { i = deltas.length - 1; while (!deltas[i].end && i >= 0) i--; - } + } var pos = isInsert(deltas[i]) ? deltas[i].end : deltas[i].start; return Range.fromPoints(pos, pos); }; diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index 60359ddd..ce099e62 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -111,21 +111,27 @@ var Editor = function(renderer, session) { oop.implement(this, EventEmitter); this.$initOperationListeners = function() { - function last(a) {return a[a.length - 1]} - - this.selections = []; + this.commands.toggleRecording(); + this.commands.on("exec", this.startOperation.bind(this), true); this.commands.on("afterExec", this.endOperation.bind(this), true); this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this)); - + + // todo: add before change events? this.on("change", function() { - this.curOp || this.startOperation(); + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } this.curOp.docChanged = true; }.bind(this), true); - + this.on("changeSelection", function() { - this.curOp || this.startOperation(); + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } this.curOp.selectionChanged = true; }.bind(this), true); }; @@ -150,13 +156,12 @@ var Editor = function(renderer, session) { scrollTop: this.renderer.scrollTop }; this.curOp.selectionBefore = this.selection.toJSON(); - // this.selections.push(); }; this.endOperation = function(e) { if (this.curOp) { if (e && e.returnValue === false) - return this.curOp = null; + return (this.curOp = null); this._signal("beforeEndOperation"); var command = this.curOp.command; var scrollIntoView = command && command.scrollIntoView; @@ -185,9 +190,12 @@ var Editor = function(renderer, session) { if (scrollIntoView == "animate") this.renderer.animateScrolling(this.curOp.scrollTop); } - this.curOp.selectionAfter = this.selection.toJSON(); - if (this.curOp.group) - this.curOp.group.selectionAfter = this.curOp.selectionAfter; + var sel = this.selection.toJSON(); + this.curOp.selectionAfter = sel; + this.$lastSel = this.selection.toJSON(); + + // console.log(this.$lastSel+" endOP") + this.session.getUndoManager().addSelection(sel); this.prevOp = this.curOp; this.curOp = null; } diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index 5a778191..c8348730 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -56,52 +56,101 @@ var UndoManager = function() { * @param {Object} options Contains additional properties * **/ - this.add = function(delta, doc) { - // Normalize deltas for storage. - // var deltaSets = this.$serializeDeltas(options.args[0]); - - // Add deltas to undo stack. - this.$doc = doc; - // if (options.merge && this.hasUndo()){ - // this.dirtyCounter--; - // deltaSets = this.$undoStack.pop().concat(deltaSets); - // } - if (doc.mergeUndoDeltas === false || !this.lastDeltas) { + this.add = function(delta) { + if (this.$session.mergeUndoDeltas === false || !this.lastDeltas) { this.lastDeltas = []; this.$undoStack.push(this.lastDeltas); + this.$rev++; + delta.id = this.$rev; } this.lastDeltas.push(delta); - - // Reset redo stack. - this.$redoStack = []; - if (this.dirtyCounter < 0) { - // The user has made a change after undoing past the last clean state. - // We can never get back to a clean state now until markClean() is called. - this.dirtyCounter = NaN; - } - this.dirtyCounter++; }; - this.startGroup = function() { + this.addSelection = function(selection, rev) { + this.selections.push({ + value: selection, + rev: rev || this.$rev + }); + }; + + this.startNewGroup = function() { + this.lastDeltas = null; + return this.$rev; + }; + + this.markIgnored = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id < to) + delta.ignore = true; + if (delta.id <= from) + break; + } this.lastDeltas = null; }; - + + this.getSelection = function(rev, after) { + var stack = this.selections; + for (var i = stack.length; i--;) { + var selection = stack[i]; + if (selection.rev < rev) { + if (after) + selection = stack[i + 1]; + return selection; + } + } + }; + + this.getRevision = function() { + return this.$rev; + }; + + this.getDeltas = function(from, to) { + if (to == null) to = this.$rev + 1; + var stack = this.$undoStack; + var end = null, start = 0; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id < to && !end) + end = i+1; + if (delta.id <= from) { + start = i + 1; + break; + } + } + return stack.slice(start, end); + }; + + this.getChangedRanges = function(from, to) { + + }; + + this.getChangedLines = function(from, to) { + + }; + /** * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} * @param {Boolean} dontSelect {:dontSelect} * * @returns {Range} The range of the undo. **/ - this.undo = function() { + this.undo = function(dontSelect) { this.lastDeltas = null; var stack = this.$undoStack; - UndoManager.rebase(stack, stack.length); + findDelta(stack, stack.length); var deltaSet = stack.pop(); var undoSelectionRange = null; - if (deltaSet) { - undoSelectionRange = this.$doc.undoChanges(deltaSet); + if (deltaSet && deltaSet.length) { + var id = deltaSet[0].id; + undoSelectionRange = this.$session.undoChanges(deltaSet, dontSelect); + if (this.$redoStackBaseRev !== id) { + this.$redoStack = []; + } this.$redoStack.push(deltaSet); - this.dirtyCounter--; + this.$syncRev(); } return undoSelectionRange; @@ -114,23 +163,39 @@ var UndoManager = function() { **/ this.redo = function(dontSelect) { this.lastDeltas = null; + if (this.$redoStackBaseRev != this.$rev) { + var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); + rebaseRedoStack(this.$redoStack, diff); + this.$redoStackBaseRev = this.$rev; + } var deltaSet = this.$redoStack.pop(); var redoSelectionRange = null; + if (deltaSet) { - redoSelectionRange = this.$doc.redoChanges(deltaSet); + redoSelectionRange = this.$session.redoChanges(deltaSet, dontSelect); this.$undoStack.push(deltaSet); - this.dirtyCounter++; + this.$syncRev(); } return redoSelectionRange; }; + + this.$syncRev = function() { + var stack = this.$undoStack; + var nextDelta = stack[stack.length - 1]; + var id = nextDelta && nextDelta[0].id || 0; + this.$redoStackBaseRev = id; + this.$rev = id; + }; /** * Destroys the stack of undo and redo redo operations. **/ this.reset = function() { + this.lastDeltas = null; this.$undoStack = []; this.$redoStack = []; - this.dirtyCounter = 0; + this.$rev = 0; + this.selections = []; }; /** @@ -164,22 +229,13 @@ var UndoManager = function() { return this.dirtyCounter === 0; }; - // // Serializes deltaSets to reduce memory usage. - // this.$serializeDeltas = function(deltaSets) { - // return cloneDeltaSetsObj(deltaSets, $serializeDelta); - // }; - - // // Deserializes deltaSets to allow application to the document. - // this.$deserializeDeltas = function(deltaSets) { - // return cloneDeltaSetsObj(deltaSets, $deserializeDelta); - // }; }).call(UndoManager.prototype); -UndoManager.rebase = function(stack, pos) { +function findDelta(stack, pos) { for (var i = pos; i--; ) { var deltaSet = stack[i]; - if (deltaSet && !deltaSet[0].disabled) { + if (deltaSet && !deltaSet[0].ignore) { while(i < pos - 1) { var swapped = swapGroups(stack[i], stack[i + 1]); stack[i] = swapped[0]; @@ -191,7 +247,91 @@ UndoManager.rebase = function(stack, pos) { } }; - +var Range = require("./range").Range; +var cmp = Range.comparePoints; +var comparePoints = Range.comparePoints; + +function $updateMarkers(delta) { + var isInsert = delta.action == "insert"; + var start = delta.start; + var end = delta.end; + var rowShift = (end.row - start.row) * (isInsert ? 1 : -1); + var colShift = (end.column - start.column) * (isInsert ? 1 : -1); + if (isInsert) end = start; + + for (var i in this.marks) { + var point = this.marks[i]; + var cmp = comparePoints(point, start); + if (cmp < 0) { + continue; // delta starts after the range + } + if (cmp === 0) { + if (isInsert) { + if (point.bias == 1) { + cmp = 1; + } + else { + point.bias == -1; + continue; + } + } + } + var cmp2 = isInsert ? cmp : comparePoints(point, end); + if (cmp2 > 0) { + point.row += rowShift; + point.column += point.row == end.row ? colShift : 0; + continue; + } + if (!isInsert && cmp2 <= 0) { + point.row = start.row; + point.column = start.column; + if (cmp2 === 0) + point.bias = 1; + } + } +} + + + +function clonePos(pos) { + return {row: pos.row,column: pos.column}; +} +function cloneDelta(d) { + return { + start: clonePos(d.start), + end: clonePos(d.end), + action: d.action, + lines: d.lines.slice() + }; +} +function stringifyDelta(d) { + d = d || this; + if (Array.isArray(d)) { + return d.map(stringifyDelta).join("\n"); + } + var type = ""; + if (d.action) { + type = d.action == "insert" ? "+" : "-"; + type += "[" + d.lines + "]"; + } else if (d.value) { + if (Array.isArray(d.value)) { + type = d.value.map(stringifyRange).join("\n"); + } else { + type = stringifyRange(d.value); + } + } + if (d.start) { + type += stringifyRange(d); + } + if (d.id || d.rev) { + type += "\t(" + (d.id || d.rev) + ")"; + } + return type; +} +function stringifyRange(r) { + return r.start.row + ":" + r.start.column + + "=>" + r.end.row + ":" + r.end.column; +} /* * i i d1 d2 * |/ |/ d2.s >= d1.e shift(d2, d1, -1) @@ -213,8 +353,7 @@ UndoManager.rebase = function(stack, pos) { * d2.e <= d1.s shift(d1, d2, -1) * d2.s < d1.s < d2.e // can split */ -var Range = require("./range").Range; -var cmp = Range.comparePoints; + function swap(d1, d2) { var i1 = d1.action == "insert"; var i2 = d2.action == "insert"; @@ -253,36 +392,6 @@ function swap(d1, d2) { } } return [d2, d1]; - - function shift(d1, d2, dir) { - shiftPos(d1.start, d2.start, d2.end, dir); - shiftPos(d1.end, d2.start, d2.end, dir); - } - function shiftPos(pos, start, end, dir) { - if (pos.row == (dir == 1 ? start : end).row) { - pos.column += dir * (end.column - start.column); - } - pos.row += dir * (end.row - start.row); - } -} -function clonePos(pos) { - return {row: pos.row,column: pos.column}; -} -function cloneDelta(d) { - return { - start: clonePos(d.start), - end: clonePos(d.end), - action: d.action, - lines: d.lines.slice(), - toString: logDelta - }; -} -function logDelta(d) { - d = d || this; - var type = d.action == "insert" ? "+" : "-"; - return type + "[" + d.lines + "]" - + d.start.row + ":" + d.start.column + "=>" - + d.end.row + ":" + d.end.column; } function swapGroups(ds1, ds2) { for (var i = ds1.length; i--; ) { @@ -305,6 +414,129 @@ function swapGroups(ds1, ds2) { return [ds2, ds1]; } +/* + d2 xform(d1, c1) = [d2, c2] + o<---o xform(c1, d1) = [c2, d2] + c2 | | d1 + o<---o + c1 +*/ +function xform(d1, c1) { + var i1 = d1.action == "insert"; + var i2 = c1.action == "insert"; + + if (i1 && i2) { + if (cmp(d1.start, c1.start) < 0) { + shift(c1, d1, 1); + } else { + shift(d1, c1, 1); + } + } else if (i1 && !i2) { + if (cmp(d1.start, c1.end) >= 0) { + shift(d1, c1, -1); + } else if (cmp(d1.start, c1.start) <= 0) { + shift(c1, d1, +1); + } else { + shift(d1, Range.fromPoints(c1.start, d1.start), -1); + shift(c1, d1, +1); + } + } else if (!i1 && i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.start, d1.start) <= 0) { + shift(d1, c1, +1); + } else { + shift(c1, Range.fromPoints(d1.start, c1.start), -1); + shift(d1, c1, +1); + } + } else if (!i1 && !i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.end, d1.start) <= 0) { + shift(d1, c1, -1); + } else { + var before, after; + if (cmp(d1.start, c1.start) < 0) { + before = d1; + d1 = splitDelta(d1, c1.start); + } + if (cmp(d1.end, c1.end) > 0) { + after = splitDelta(d1, c1.end); + } + + shiftPos(c1.end, d1.start, d1.end, -1); + if (after && !before) { + d1.lines = after.lines; + d1.start = after.start; + d1.end = after.end; + after = d1; + } + + return [c1, before, after].filter(Boolean); + } + } + return [c1, d1]; +} + +function shift(d1, d2, dir) { + shiftPos(d1.start, d2.start, d2.end, dir); + shiftPos(d1.end, d2.start, d2.end, dir); +} +function shiftPos(pos, start, end, dir) { + if (pos.row == (dir == 1 ? start : end).row) { + pos.column += dir * (end.column - start.column); + } + pos.row += dir * (end.row - start.row); +} +function splitDelta(c, pos) { + var lines = c.lines; + var end = c.end; + c.end = clonePos(pos); + var rowsBefore = c.end.row - c.start.row; + var otherLines = lines.splice(rowsBefore, lines.length); + + var col = rowsBefore ? pos.column : pos.column - c.start.column; + lines.push(otherLines[0].substring(0, col)); + otherLines[0] = otherLines[0].substr(col) ; + var rest = { + start: clonePos(pos), + end: end, + lines: otherLines, + action: c.action + }; + return rest; +} + +function moveDeltasByOne(redoStack, d) { + d = cloneDelta(d); + for (var j = redoStack.length; j--;) { + var deltaSet = redoStack[j]; + for (var i = deltaSet.length; i--;) { + var x = deltaSet[i]; + var xformed = xform(x, d); + d = xformed[0]; + if (xformed.length != 2) { + if (xformed[2]) { + redoStack.splice(i + 1, 1, xformed[1], xformed[2]); + i++; + } else if (!xformed[1]) { + redoStack.splice(i, 1); + i--; + } + } + } + } + return redoStack; +} +function rebaseRedoStack(redoStack, deltaSets) { + for (var i = 0; i < deltaSets.length; i++) { + var deltas = deltaSets[i]; + for (var j = 0; j < deltas.length; j++) { + moveDeltasByOne(redoStack, deltas[j]); + } + } +} + UndoManager.swapGroups = swapGroups; exports.UndoManager = UndoManager; }); diff --git a/package.json b/package.json index c253ca32..0a942d39 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "c9.ide.language.javascript.tern": "#7aab8b0b6a", "c9.ide.language.javascript.infer": "#cfec494a3c", "c9.ide.language.jsonalyzer": "#21b64e5820", - "c9.ide.collab": "#749c26b859", + "c9.ide.collab": "#da4d09ae6a", "c9.ide.local": "#2bfd7ff051", "c9.ide.find": "#6cc6d3379d", "c9.ide.find.infiles": "#72582de3cd", From c9f492a916fcc18de6106dc974a365194c65a533 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 27 Dec 2014 12:59:59 +0400 Subject: [PATCH 53/61] make sure edit ids are always unique --- node_modules/ace/lib/ace/undomanager.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index c8348730..b2ae7a8d 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -42,6 +42,8 @@ define(function(require, exports, module) { * @constructor **/ var UndoManager = function() { + this.$maxRev = 0; + this.mark = 0; this.reset(); }; @@ -60,8 +62,7 @@ var UndoManager = function() { if (this.$session.mergeUndoDeltas === false || !this.lastDeltas) { this.lastDeltas = []; this.$undoStack.push(this.lastDeltas); - this.$rev++; - delta.id = this.$rev; + delta.id = this.$rev = ++this.$maxRev; } this.lastDeltas.push(delta); }; @@ -167,6 +168,9 @@ var UndoManager = function() { var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); rebaseRedoStack(this.$redoStack, diff); this.$redoStackBaseRev = this.$rev; + this.$redoStack.forEach(function(x) { + x[0].id = ++this.$maxRev; + }, this); } var deltaSet = this.$redoStack.pop(); var redoSelectionRange = null; @@ -217,8 +221,11 @@ var UndoManager = function() { /** * Marks the current status clean **/ - this.markClean = function() { - this.dirtyCounter = 0; + this.bookmark = + this.markClean = function(rev) { + if (rev == undefined) + rev = this.$rev; + this.mark = rev; }; /** @@ -226,7 +233,7 @@ var UndoManager = function() { * @returns {Boolean} **/ this.isClean = function() { - return this.dirtyCounter === 0; + return this.$rev === this.mark; }; @@ -245,7 +252,7 @@ function findDelta(stack, pos) { break; } } -}; +} var Range = require("./range").Range; var cmp = Range.comparePoints; @@ -537,6 +544,6 @@ function rebaseRedoStack(redoStack, deltaSets) { } } -UndoManager.swapGroups = swapGroups; exports.UndoManager = UndoManager; + }); From caad98e5d21442faa33910f0a6a06c71db7b0d6b Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 30 Dec 2014 00:00:20 +0400 Subject: [PATCH 54/61] use the new undoManager in ace.js --- node_modules/ace/lib/ace/edit_session.js | 7 +- node_modules/ace/lib/ace/undomanager.js | 69 +++++++--- plugins/c9.ide.ace/ace.js | 159 ++++++++++++----------- plugins/c9.ide.editors/undomanager.js | 6 +- 4 files changed, 141 insertions(+), 100 deletions(-) diff --git a/node_modules/ace/lib/ace/edit_session.js b/node_modules/ace/lib/ace/edit_session.js index cfd7b174..e385647a 100644 --- a/node_modules/ace/lib/ace/edit_session.js +++ b/node_modules/ace/lib/ace/edit_session.js @@ -260,10 +260,10 @@ var EditSession = function(text, mode) { this.$undoManager.add({ action: "removeFolds", folds: removedFolds - }, this); + }, this.mergeUndoDeltas); this.mergeUndoDeltas = true; } - this.$undoManager.add(delta, this); + this.$undoManager.add(delta, this.mergeUndoDeltas); this.mergeUndoDeltas = true; this.$informUndoManager.schedule(); @@ -376,7 +376,7 @@ var EditSession = function(text, mode) { if (undoManager) { var self = this; - undoManager.$session = this; + undoManager.setSession(this); this.$syncInformUndoManager = function() { self.$informUndoManager.cancel(); self.mergeUndoDeltas = false; @@ -402,6 +402,7 @@ var EditSession = function(text, mode) { add: function() {}, addSelection: function() {}, startNewGroup: function() {}, + setSession: function() {}, }; /** diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index b2ae7a8d..cd0f1711 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -44,11 +44,15 @@ define(function(require, exports, module) { var UndoManager = function() { this.$maxRev = 0; this.mark = 0; + this.$fromUndo = false; this.reset(); }; (function() { - + + this.setSession = function(session) { + this.$session = session; + }; /** * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements: * @@ -58,8 +62,9 @@ var UndoManager = function() { * @param {Object} options Contains additional properties * **/ - this.add = function(delta) { - if (this.$session.mergeUndoDeltas === false || !this.lastDeltas) { + this.add = function(delta, allowMerge) { + if (this.$fromUndo) return; + if (allowMerge === false || !this.lastDeltas) { this.lastDeltas = []; this.$undoStack.push(this.lastDeltas); delta.id = this.$rev = ++this.$maxRev; @@ -84,10 +89,10 @@ var UndoManager = function() { var stack = this.$undoStack; for (var i = stack.length; i--;) { var delta = stack[i][0]; - if (delta.id < to) - delta.ignore = true; if (delta.id <= from) break; + if (delta.id < to) + delta.ignore = true; } this.lastDeltas = null; }; @@ -125,10 +130,12 @@ var UndoManager = function() { }; this.getChangedRanges = function(from, to) { + if (to == null) to = this.$rev + 1; }; this.getChangedLines = function(from, to) { + if (to == null) to = this.$rev + 1; }; @@ -141,18 +148,24 @@ var UndoManager = function() { this.undo = function(dontSelect) { this.lastDeltas = null; var stack = this.$undoStack; - findDelta(stack, stack.length); + + if (!rearrangeUndoStack(stack, stack.length)) + return; + + if (this.$redoStackBaseRev !== this.$rev && this.$redoStack.length) + this.$redoStack = []; + + this.$fromUndo = true; + var deltaSet = stack.pop(); var undoSelectionRange = null; if (deltaSet && deltaSet.length) { - var id = deltaSet[0].id; undoSelectionRange = this.$session.undoChanges(deltaSet, dontSelect); - if (this.$redoStackBaseRev !== id) { - this.$redoStack = []; - } this.$redoStack.push(deltaSet); this.$syncRev(); } + + this.$fromUndo = false; return undoSelectionRange; }; @@ -164,6 +177,8 @@ var UndoManager = function() { **/ this.redo = function(dontSelect) { this.lastDeltas = null; + + this.$fromUndo = true; if (this.$redoStackBaseRev != this.$rev) { var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); rebaseRedoStack(this.$redoStack, diff); @@ -180,13 +195,15 @@ var UndoManager = function() { this.$undoStack.push(deltaSet); this.$syncRev(); } + this.$fromUndo = false; + return redoSelectionRange; }; this.$syncRev = function() { var stack = this.$undoStack; var nextDelta = stack[stack.length - 1]; - var id = nextDelta && nextDelta[0].id || 0; + var id = nextDelta && nextDelta[0] && nextDelta[0].id || 0; this.$redoStackBaseRev = id; this.$rev = id; }; @@ -202,11 +219,12 @@ var UndoManager = function() { this.selections = []; }; + /** * Returns `true` if there are undo operations left to perform. * @returns {Boolean} **/ - this.hasUndo = function() { + this.canUndo = function() { return this.$undoStack.length > 0; }; @@ -214,15 +232,14 @@ var UndoManager = function() { * Returns `true` if there are redo operations left to perform. * @returns {Boolean} **/ - this.hasRedo = function() { + this.canRedo = function() { return this.$redoStack.length > 0; }; - + /** * Marks the current status clean **/ - this.bookmark = - this.markClean = function(rev) { + this.bookmark = function(rev) { if (rev == undefined) rev = this.$rev; this.mark = rev; @@ -232,14 +249,26 @@ var UndoManager = function() { * Returns if the current status is clean * @returns {Boolean} **/ - this.isClean = function() { + this.isAtBookmark = function() { return this.$rev === this.mark; }; - + this.toJSON = function() { + + }; + + this.fromJSON = function() { + + }; + + this.hasUndo = this.canUndo; + this.hasRedo = this.canRedo; + this.isClean = this.isAtBookmark; + this.markClean = this.bookmark; + }).call(UndoManager.prototype); -function findDelta(stack, pos) { +function rearrangeUndoStack(stack, pos) { for (var i = pos; i--; ) { var deltaSet = stack[i]; if (deltaSet && !deltaSet[0].ignore) { @@ -249,7 +278,7 @@ function findDelta(stack, pos) { stack[i + 1] = swapped[1]; i++; } - break; + return true; } } } diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index c7e35fd7..3f6bca39 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -282,14 +282,24 @@ define(function(require, exports, module) { /***** Undo Manager *****/ function AceUndoManager(undoManager, session) { + var state = undoManager.getState(); this.$session = session; this.$undo = undoManager; this.$aceUndo = new UndoManager(); - var _self = this; - var Item = this.Item; - this.$undo.on("itemFind", function(e) { - return Item(_self, e.state); - }); + this.$aceUndo.c9UndoProxy = undoManager; + undoManager.$aceUndo = this.$aceUndo; + undoManager.add = this.add; + undoManager.addSelection = this.addSelection; + undoManager.undo = this.undo; + undoManager.redo = this.redo; + undoManager.reset = this.reset; + undoManager.canUndo = this.canUndo; + undoManager.canRedo = this.canRedo; + undoManager.getState = this.getState; + undoManager.setState = this.setState; + this._emit = undoManager.getEmitter(); + + undoManager.setState(state); } function updateDeltas(deltas) { if (deltas[0] && deltas[0].deltas) { @@ -299,81 +309,74 @@ define(function(require, exports, module) { deltas.push.apply(deltas, x.deltas); }); } + return deltas; } AceUndoManager.prototype = { - rebase: function() { - var pos = this.$undo.position + 2; - var stack = this.$undo.stack; - for (var i = pos; i--; ) { - var deltaSet = stack[i].deltas; - if (deltaSet[0] && !deltaSet[0].disabled) { - while(i < pos - 1) { - var swapped = UndoManager.swapGroups(stack[i].deltas, stack[i + 1].deltas); - stack[i].deltas = swapped[0]; - stack[i + 1].deltas = swapped[1]; - i++; - } - deltaSet = stack[pos - 1].deltas; - if (deltaSet[0].disabled) - deltaSet[0].disabled = false; - return deltaSet; - } - } - }, - Item: function(_self, deltas) { - return { - deltas: deltas, - undo: function(){ - updateDeltas(this.deltas); // change to the new format - var deltas = _self.rebase(); - if (!deltas) - return false; - _self.$session.session.undoChanges(deltas, _self.dontSelect); - }, - redo: function(){ - updateDeltas(this.deltas); - _self.$session.session.redoChanges(this.deltas, _self.dontSelect); - }, - getState: function(){ - updateDeltas(this.deltas); - return this.deltas.filter(function (d) { - return d.action == "insert" || d.action == "remove"; - }); - } - }; - }, - add: function(delta, doc) { - if (doc.mergeUndoDeltas === false || !this.lastDeltas) { - this.lastDeltas = []; - this.$undo.add(this.Item(this, this.lastDeltas)); - } - this.lastDeltas.push(delta); + this.$aceUndo.add(delta, doc); + this._emit("change"); + }, + addSelection: function(range, rev) { + this.$aceUndo.addSelection(range, rev); }, - undo: function(dontSelect) { - this.dontSelect = dontSelect; - this.lastDeltas = null; - this.$undo.undo(); + this.$aceUndo.undo(dontSelect); + this._emit("change"); }, redo: function(dontSelect) { - this.dontSelect = dontSelect; - this.lastDeltas = null; - this.$undo.redo(); + this.$aceUndo.redo(dontSelect); + this._emit("change"); }, reset: function(){ - this.lastDeltas = null; - this.$undo.reset(); + this.$aceUndo.reset(); + this._emit("change"); }, - hasUndo: function() { - return this.$undo.length > this.$undo.position + 1; + canUndo: function() { + return this.$aceUndo.canUndo(); }, - hasRedo: function() { - return this.$undo.length <= this.$undo.position + 1; + canRedo: function() { + return this.$aceUndo.canRedo(); }, - get $undoStack() { - return this.$undo.stack.slice(0, this.$undo.position + 1) - .map(function(e){ return e.getState ? e.getState() : e }); + clearUndo: function() { + this.$aceUndo.$undoStack = []; + this._emit("change"); + }, + clearRedo: function() { + this.$aceUndo.$redoStack = []; + this._emit("change"); + }, + getState: function() { + console.log("getState()"); + var aceUndo = this.$aceUndo; + var stack = aceUndo.$undoStack.map(function(deltaSet) { + return deltaSet.filter(function (d) { + return d.action == "insert" || d.action == "remove"; + }); + }); + return { + stack: stack, + mark: aceUndo.mark, + position: aceUndo.$rev + }; + }, + setState: function(e) { + console.log("setState()"); + var aceUndo = this.$aceUndo; + aceUndo.$undoStack = (e.stack || []).filter(function(x) { + return x.length; + }); + aceUndo.$rev = e.position; + this.bookmark(e.mark); + }, + isAtBookmark: function() { + return this.$aceUndo.isAtBookmark(); + }, + bookmark: function(index) { + this.$aceUndo.bookmark(index); + this._emit("change"); + }, + setSession: function(session) { + this.$aceUndo.setSession(session); } }; @@ -383,9 +386,19 @@ define(function(require, exports, module) { } (function() { - this.execute = function(options) { - this.$u.execute(options); + this.add = function(delta, doc) { + this.$u.add(delta, doc); }; + + Object.defineProperty(this, "", { + configurable: true, + get: function() { + return this.$u.lastDeltas; + }, + set: function(v) { + this.$u.lastDeltas = v; + } + }); this.undo = function() { var selectionRange = this.$u.undo(true); @@ -1410,7 +1423,7 @@ define(function(require, exports, module) { undoManager = session.getUndoManager(); if (undoManager) { var undoManagerProxy = new UndoManagerProxy(undoManager, s); - s.setUndoManager(undoManagerProxy); + s.setUndoManager(undoManagerProxy.$aceUndo); } // Overwrite the default $informUndoManager function such that new deltas @@ -2211,9 +2224,9 @@ define(function(require, exports, module) { var c9Session = doc.getSession(); // if load starts from another editor type - // tabmanager will show as instantly + // tabmanager will show us instantly // so we need to show progress bar instantly - progress.noFadeIn = !currentDocument; + progress.noFadeIn = !currentDocument || !currentDocument.tab.active; // Value Retrieval doc.on("getValue", function get(e) { diff --git a/plugins/c9.ide.editors/undomanager.js b/plugins/c9.ide.editors/undomanager.js index 6560de05..db1e90a0 100644 --- a/plugins/c9.ide.editors/undomanager.js +++ b/plugins/c9.ide.editors/undomanager.js @@ -150,6 +150,8 @@ define(function(require, module, exports) { emit("change"); } + plugin.freezePublicAPI.baseclass(); + /** * The Undo Manager class of Cloud9. Each {@link Document} * has a single instance of the undo manager that @@ -212,10 +214,6 @@ define(function(require, module, exports) { * **/ plugin.freezePublicAPI({ - /** - * @ignore - */ - get stack() { return stack; }, /** * The number of items on the stack. This number will stay the * same when using {@link UndoManager#undo} and From 873143a72585a515635d4172dd674fd96ea4cc2d Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 5 Jan 2015 16:32:58 +0400 Subject: [PATCH 55/61] fix metadata tests --- node_modules/ace/lib/ace/undomanager.js | 4 ++-- plugins/c9.ide.ace/ace.js | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index cd0f1711..3bf6b5bc 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -43,7 +43,6 @@ define(function(require, exports, module) { **/ var UndoManager = function() { this.$maxRev = 0; - this.mark = 0; this.$fromUndo = false; this.reset(); }; @@ -203,7 +202,7 @@ var UndoManager = function() { this.$syncRev = function() { var stack = this.$undoStack; var nextDelta = stack[stack.length - 1]; - var id = nextDelta && nextDelta[0] && nextDelta[0].id || 0; + var id = nextDelta && nextDelta[0].id || 0; this.$redoStackBaseRev = id; this.$rev = id; }; @@ -216,6 +215,7 @@ var UndoManager = function() { this.$undoStack = []; this.$redoStack = []; this.$rev = 0; + this.mark = 0; this.selections = []; }; diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 3f6bca39..8f94dfc2 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -297,9 +297,13 @@ define(function(require, exports, module) { undoManager.canRedo = this.canRedo; undoManager.getState = this.getState; undoManager.setState = this.setState; - this._emit = undoManager.getEmitter(); + undoManager.bookmark = this.bookmark; + undoManager.isAtBookmark = this.isAtBookmark; + undoManager.__defineGetter__("position", this.getPosition); + undoManager.__defineGetter__("length", this.getLength); + undoManager._emit = this._emit = undoManager.getEmitter(); - undoManager.setState(state); + this.setState(state); } function updateDeltas(deltas) { if (deltas[0] && deltas[0].deltas) { @@ -356,7 +360,7 @@ define(function(require, exports, module) { return { stack: stack, mark: aceUndo.mark, - position: aceUndo.$rev + position: stack.length - 1 }; }, setState: function(e) { @@ -365,7 +369,11 @@ define(function(require, exports, module) { aceUndo.$undoStack = (e.stack || []).filter(function(x) { return x.length; }); - aceUndo.$rev = e.position; + var stack = aceUndo.$undoStack; + var lastDeltaGroup = stack[stack.length] - 1; + var lastRev = lastDeltaGroup && lastDeltaGroup[0].id || 0; + aceUndo.$rev = lastRev; + aceUndo.$maxRev = Math.max(aceUndo.$maxRev, lastRev); this.bookmark(e.mark); }, isAtBookmark: function() { @@ -377,6 +385,14 @@ define(function(require, exports, module) { }, setSession: function(session) { this.$aceUndo.setSession(session); + }, + getPosition: function() { + var aceUndo = this.$aceUndo; + return aceUndo.$undoStack.length - 1; + }, + getLength: function() { + var aceUndo = this.$aceUndo; + return aceUndo.$undoStack.length + aceUndo.$redoStack.length; } }; From 8312d3d5acd0e41022afb3596c3719b8cf4b3a0e Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 6 Jan 2015 18:24:33 +0400 Subject: [PATCH 56/61] fix save test --- plugins/c9.ide.ace/ace.js | 45 ++++++++++++++++++++------ plugins/c9.ide.editors/document.js | 51 ++++++++++++++---------------- plugins/c9.ide.watcher/gui.js | 7 ++-- 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 8f94dfc2..1affd8b2 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -297,12 +297,15 @@ define(function(require, exports, module) { undoManager.canRedo = this.canRedo; undoManager.getState = this.getState; undoManager.setState = this.setState; - undoManager.bookmark = this.bookmark; + undoManager.bookmark = this.bookmarkPosition; undoManager.isAtBookmark = this.isAtBookmark; undoManager.__defineGetter__("position", this.getPosition); undoManager.__defineGetter__("length", this.getLength); undoManager._emit = this._emit = undoManager.getEmitter(); + this.deleyedEmit = lang.delayedCall(this._emit.bind(null, "change")) + .schedule.bind(null, 0); + undoManager.on("changeSync", this.deleyedEmit); this.setState(state); } function updateDeltas(deltas) { @@ -318,22 +321,22 @@ define(function(require, exports, module) { AceUndoManager.prototype = { add: function(delta, doc) { this.$aceUndo.add(delta, doc); - this._emit("change"); + this._emit("changeSync"); }, addSelection: function(range, rev) { this.$aceUndo.addSelection(range, rev); }, undo: function(dontSelect) { this.$aceUndo.undo(dontSelect); - this._emit("change"); + this._emit("changeSync"); }, redo: function(dontSelect) { this.$aceUndo.redo(dontSelect); - this._emit("change"); + this._emit("changeSync"); }, reset: function(){ this.$aceUndo.reset(); - this._emit("change"); + this._emit("changeSync"); }, canUndo: function() { return this.$aceUndo.canUndo(); @@ -343,11 +346,11 @@ define(function(require, exports, module) { }, clearUndo: function() { this.$aceUndo.$undoStack = []; - this._emit("change"); + this._emit("changeSync"); }, clearRedo: function() { this.$aceUndo.$redoStack = []; - this._emit("change"); + this._emit("changeSync"); }, getState: function() { console.log("getState()"); @@ -379,9 +382,27 @@ define(function(require, exports, module) { isAtBookmark: function() { return this.$aceUndo.isAtBookmark(); }, - bookmark: function(index) { - this.$aceUndo.bookmark(index); - this._emit("change"); + bookmark: function(rev) { + this.$aceUndo.bookmark(rev); + this._emit("changeSync"); + }, + bookmarkPosition: function(index) { + if (index > -1) { + var stack = this.$aceUndo.$undoStack; + if (index >= stack.length) { + index -= stack.length; + stack = this.$aceUndo.$redoStack; + index = stack.length - index; + } + var deltaSet = stack[index]; + var rev = deltaSet && deltaSet[0] && deltaSet[0].id; + this.$aceUndo.bookmark(rev); + } else if (index == -1) { + this.$aceUndo.bookmark(0); + } else { + this.$aceUndo.bookmark(index); + } + this._emit("changeSync"); }, setSession: function(session) { this.$aceUndo.setSession(session); @@ -390,6 +411,10 @@ define(function(require, exports, module) { var aceUndo = this.$aceUndo; return aceUndo.$undoStack.length - 1; }, + getMark: function() { + var aceUndo = this.$aceUndo; + return aceUndo.$undoStack.length - 1; + }, getLength: function() { var aceUndo = this.$aceUndo; return aceUndo.$undoStack.length + aceUndo.$redoStack.length; diff --git a/plugins/c9.ide.editors/document.js b/plugins/c9.ide.editors/document.js index c305ad8f..ec70358e 100644 --- a/plugins/c9.ide.editors/document.js +++ b/plugins/c9.ide.editors/document.js @@ -40,16 +40,15 @@ define(function(require, module, exports) { // Listen to changes and detect when the value of the editor // is different from what is on disk - function initUndo(){ - undoManager.on("change", function(e) { - var c = !undoManager.isAtBookmark(); - if (changed !== c || undoManager.position == -1) { - changed = c; - emit("changed", { changed: c }); - } - }); + function updateStatus(e) { + var c = !undoManager.isAtBookmark(); + if (changed !== c) { + changed = c; + emit("changed", { changed: c }); + } } - initUndo(); + undoManager.on("change", updateStatus); + undoManager.on("changeSync", updateStatus); /***** Methods *****/ @@ -60,26 +59,24 @@ define(function(require, module, exports) { } var state = getState(); - undoManager.once("change", function(){ - // Bookmark the undo manager - undoManager.bookmark(); - - // Update state - delete state.changed; - delete state.value; - delete state.meta; - state.undoManager = undoManager.getState(); - - if (cleansed && editor && state[editor.type]) - state[editor.type].cleansed = true; - - // Set new state (preserving original state) - if (emit("mergeState") !== false) - setState(state); - }); - // Record value (which should add an undo stack item) plugin.value = value; + + // Bookmark the undo manager + undoManager.bookmark(); + + // Update state + delete state.changed; + delete state.value; + delete state.meta; + state.undoManager = undoManager.getState(); + + if (cleansed && editor && state[editor.type]) + state[editor.type].cleansed = true; + + // Set new state (preserving original state) + if (emit("mergeState") !== false) + setState(state); } function getState(filter) { diff --git a/plugins/c9.ide.watcher/gui.js b/plugins/c9.ide.watcher/gui.js index a4489de9..6bf4147a 100644 --- a/plugins/c9.ide.watcher/gui.js +++ b/plugins/c9.ide.watcher/gui.js @@ -309,11 +309,8 @@ define(function(require, exports, module) { doc.meta.$mergeRoot = data; // If the value on disk is the same as in the document, set the bookmark - if (mergedValue == data) { - doc.undoManager.once("change", function(){ - doc.undoManager.bookmark(); - }); - } + if (mergedValue == data) + doc.undoManager.bookmark(); return true; } From 3644d203c2db6ffe570bfb1bfe7f3e9aad76af80 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 7 Apr 2015 22:49:55 +0400 Subject: [PATCH 57/61] use same undo manager for multiple sessions --- node_modules/ace/lib/ace/edit_session.js | 10 ++- node_modules/ace/lib/ace/editor.js | 4 +- node_modules/ace/lib/ace/split.js | 49 +---------- node_modules/ace/lib/ace/test/all_browser.js | 48 ++++++---- node_modules/ace/lib/ace/undomanager.js | 16 ++-- node_modules/ace/lib/ace/undomanager_test.js | 92 ++++++++++++++++++++ plugins/c9.ide.ace/ace.js | 68 +++------------ plugins/c9.ide.editors/document.js | 17 ++-- 8 files changed, 164 insertions(+), 140 deletions(-) create mode 100644 node_modules/ace/lib/ace/undomanager_test.js diff --git a/node_modules/ace/lib/ace/edit_session.js b/node_modules/ace/lib/ace/edit_session.js index e385647a..30a8749d 100644 --- a/node_modules/ace/lib/ace/edit_session.js +++ b/node_modules/ace/lib/ace/edit_session.js @@ -132,7 +132,6 @@ var SearchHighlight = require("./search_highlight").SearchHighlight; //} /** - * * Sets up a new `EditSession` and associates it with the given `Document` and `TextMode`. * @param {Document | String} text [If `text` is a `Document`, it associates the `EditSession` with it. Otherwise, a new `Document` is created, with the initial text]{: #textParam} * @param {TextMode} mode [The inital language mode to use for the document]{: #modeParam} @@ -149,9 +148,10 @@ var EditSession = function(text, mode) { this.$undoSelect = true; this.$foldData = []; + this.id = "session" + EditSession.$uid; this.$foldData.toString = function() { return this.join("\n"); - } + }; this.on("changeFold", this.onChangeFold.bind(this)); this.$onChange = this.onChange.bind(this); @@ -167,6 +167,8 @@ var EditSession = function(text, mode) { }; +EditSession.$uid = 0; + (function() { oop.implement(this, EventEmitter); @@ -376,7 +378,7 @@ var EditSession = function(text, mode) { if (undoManager) { var self = this; - undoManager.setSession(this); + undoManager.addSession(this); this.$syncInformUndoManager = function() { self.$informUndoManager.cancel(); self.mergeUndoDeltas = false; @@ -402,7 +404,7 @@ var EditSession = function(text, mode) { add: function() {}, addSelection: function() {}, startNewGroup: function() {}, - setSession: function() {}, + addSession: function() {}, }; /** diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index ce099e62..8d816cbd 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -2496,7 +2496,7 @@ var Editor = function(renderer, session) { * @related UndoManager.undo **/ this.undo = function() { - this.session.getUndoManager().undo(); + this.session.getUndoManager().undo(this.session); this.renderer.scrollCursorIntoView(null, 0.5); }; @@ -2505,7 +2505,7 @@ var Editor = function(renderer, session) { * @related UndoManager.redo **/ this.redo = function() { - this.session.getUndoManager().redo(); + this.session.getUndoManager().redo(this.session); this.renderer.scrollCursorIntoView(null, 0.5); }; diff --git a/node_modules/ace/lib/ace/split.js b/node_modules/ace/lib/ace/split.js index 878b0dc3..e960c128 100644 --- a/node_modules/ace/lib/ace/split.js +++ b/node_modules/ace/lib/ace/split.js @@ -42,8 +42,6 @@ var EditSession = require("./edit_session").EditSession; /** * @class Split * - * - * **/ @@ -217,14 +215,7 @@ var Split = function(container, theme, splits) { var s = new EditSession(session.getDocument(), session.getMode()); var undoManager = session.getUndoManager(); - if (undoManager) { - var undoManagerProxy = new UndoManagerProxy(undoManager, s); - s.setUndoManager(undoManagerProxy); - } - - // Overwrite the default $informUndoManager function such that new delas - // aren't added to the undo manager from the new and the old session. - s.$informUndoManager = lang.delayedCall(function() { s.$deltas = []; }); + s.setUndoManager(undoManager); // Copy over 'settings' from the session. s.setTabSize(session.getTabSize()); @@ -331,43 +322,5 @@ var Split = function(container, theme, splits) { }).call(Split.prototype); - -function UndoManagerProxy(undoManager, session) { - this.$u = undoManager; - this.$doc = session; -} - -(function() { - this.execute = function(options) { - this.$u.execute(options); - }; - - this.undo = function() { - var selectionRange = this.$u.undo(true); - if (selectionRange) { - this.$doc.selection.setSelectionRange(selectionRange); - } - }; - - this.redo = function() { - var selectionRange = this.$u.redo(true); - if (selectionRange) { - this.$doc.selection.setSelectionRange(selectionRange); - } - }; - - this.reset = function() { - this.$u.reset(); - }; - - this.hasUndo = function() { - return this.$u.hasUndo(); - }; - - this.hasRedo = function() { - return this.$u.hasRedo(); - }; -}).call(UndoManagerProxy.prototype); - exports.Split = Split; }); diff --git a/node_modules/ace/lib/ace/test/all_browser.js b/node_modules/ace/lib/ace/test/all_browser.js index 7ac5092e..71e96e2f 100644 --- a/node_modules/ace/lib/ace/test/all_browser.js +++ b/node_modules/ace/lib/ace/test/all_browser.js @@ -5,9 +5,9 @@ require("ace/lib/fixoldbrowsers"); var AsyncTest = require("asyncjs").test; var async = require("asyncjs"); -var passed = 0 -var failed = 0 -var log = document.getElementById("log") +var passed = 0; +var failed = 0; +var log = document.getElementById("log"); var testNames = [ "ace/anchor_test", @@ -55,6 +55,7 @@ var testNames = [ "ace/snippets_test", "ace/token_iterator_test", "ace/tokenizer_test", + "ace/undomanager_test", "ace/virtual_renderer_test" ]; @@ -64,13 +65,30 @@ for (var i in testNames) { html.push("", href.replace(/^ace\//, "") ,"
"); } + +if (location.search.indexOf("show=1") != -1) { + var VirtualRenderer = require("ace/virtual_renderer").VirtualRenderer; + require("ace/test/mockrenderer").MockRenderer = function() { + var el = document.createElement("div"); + el.style.position = "fixed"; + el.style.left = "20px"; + el.style.top = "30px"; + el.style.width = "500px"; + el.style.height = "300px"; + document.body.appendChild(el); + + return new VirtualRenderer(el); + }; +} + + var nav = document.createElement("div"); nav.innerHTML = html.join(""); nav.style.cssText = "position:absolute;right:0;top:0"; document.body.appendChild(nav); if (location.search) - testNames = location.search.substr(1).split(",") + testNames = location.search.substr(1).split(","); var filter = location.hash.substr(1); @@ -89,7 +107,7 @@ require(testNames, function() { test[method] = undefined; }); } - return AsyncTest.testcase(test) + return AsyncTest.testcase(test); }, AsyncTest.TestGenerator) .run() .each(function(test, next) { @@ -102,16 +120,16 @@ require(testNames, function() { var node = document.createElement("div"); node.className = test.passed ? "passed" : "failed"; - var name = test.name + var name = test.name; if (test.suiteName) - name = test.suiteName + ": " + test.name + name = test.suiteName + ": " + test.name; - var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL") + var msg = "[" + test.count + "/" + test.index + "] " + name + " " + (test.passed ? "OK" : "FAIL"); if (!test.passed) { if (test.err.stack) - var err = test.err.stack + var err = test.err.stack; else - var err = test.err + var err = test.err; console.error(msg); console.error(err); @@ -123,13 +141,13 @@ require(testNames, function() { node.innerHTML = msg; log.appendChild(node); - next() + next(); }) .each(function(test) { if (test.passed) - passed += 1 + passed += 1; else - failed += 1 + failed += 1; }) .end(function() { log.innerHTML += [ @@ -140,11 +158,11 @@ require(testNames, function() { "Total number of tests: " + (passed + failed) + "
", (passed ? "Passed tests: " + passed + "
" : ""), (failed ? "Failed tests: " + failed + "
" : "") - ].join("") + ].join(""); console.log("Total number of tests: " + (passed + failed)); console.log("Passed tests: " + passed); console.log("Failed tests: " + failed); - }) + }); }); }); diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index 3bf6b5bc..c23eb4f9 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -49,7 +49,7 @@ var UndoManager = function() { (function() { - this.setSession = function(session) { + this.addSession = function(session) { this.$session = session; }; /** @@ -61,13 +61,16 @@ var UndoManager = function() { * @param {Object} options Contains additional properties * **/ - this.add = function(delta, allowMerge) { + this.add = function(delta, allowMerge, session) { if (this.$fromUndo) return; + if (delta == this.$lastDelta) return; if (allowMerge === false || !this.lastDeltas) { this.lastDeltas = []; this.$undoStack.push(this.lastDeltas); delta.id = this.$rev = ++this.$maxRev; } + if (delta.action == "remove" || delta.action == "insert") + this.$lastDelta = delta; this.lastDeltas.push(delta); }; @@ -144,7 +147,7 @@ var UndoManager = function() { * * @returns {Range} The range of the undo. **/ - this.undo = function(dontSelect) { + this.undo = function(session, dontSelect) { this.lastDeltas = null; var stack = this.$undoStack; @@ -159,7 +162,7 @@ var UndoManager = function() { var deltaSet = stack.pop(); var undoSelectionRange = null; if (deltaSet && deltaSet.length) { - undoSelectionRange = this.$session.undoChanges(deltaSet, dontSelect); + undoSelectionRange = session.undoChanges(deltaSet, dontSelect); this.$redoStack.push(deltaSet); this.$syncRev(); } @@ -174,7 +177,7 @@ var UndoManager = function() { * @param {Boolean} dontSelect {:dontSelect} * **/ - this.redo = function(dontSelect) { + this.redo = function(session, dontSelect) { this.lastDeltas = null; this.$fromUndo = true; @@ -190,7 +193,7 @@ var UndoManager = function() { var redoSelectionRange = null; if (deltaSet) { - redoSelectionRange = this.$session.redoChanges(deltaSet, dontSelect); + redoSelectionRange = session.redoChanges(deltaSet, dontSelect); this.$undoStack.push(deltaSet); this.$syncRev(); } @@ -212,6 +215,7 @@ var UndoManager = function() { **/ this.reset = function() { this.lastDeltas = null; + this.$lastDelta = null; this.$undoStack = []; this.$redoStack = []; this.$rev = 0; diff --git a/node_modules/ace/lib/ace/undomanager_test.js b/node_modules/ace/lib/ace/undomanager_test.js new file mode 100644 index 00000000..6d0dbf58 --- /dev/null +++ b/node_modules/ace/lib/ace/undomanager_test.js @@ -0,0 +1,92 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +if (typeof process !== "undefined") { + require("amd-loader"); + require("./test/mockdom"); +} + +define(function(require, exports, module) { +"use strict"; + +require("./multi_select"); +var assert = require("./test/assertions"); +var Range = require("./range").Range; +var Editor = require("./editor").Editor; +var EditSession = require("./edit_session").EditSession; +var MockRenderer = require("./test/mockrenderer").MockRenderer; +var UndoManager = require("./undomanager").UndoManager; + +var editor; + + +module.exports = { + + name: "ACE undoManager.js", + + "test: reabsing": function() { + var session = new EditSession(""); + var editor = new Editor(new MockRenderer(), session); + var undoManager = new UndoManager(); + session.setUndoManager(undoManager); + + session.setValue("012345-012345-012345"); + session.insert({row: 0, column: 0}, "xx"); + session.markUndoGroup(); + session.remove({start: {row: 0, column: 10}, end: {row: 0, column: 15}}); + session.markUndoGroup(); + session.insert({row: 0, column: 5}, "yy"); + session.markUndoGroup(); + editor.undo(); + editor.undo(); + var rev = session.getUndoManager().startNewGroup(); + session.insert({row: 0, column: 5}, "z\nz"); + session.getUndoManager().markIgnored(rev); + // editor.undo() + editor.redo(); + editor.redo(); + var val1 = editor.getValue(); + editor.undo(); + editor.undo(); + editor.undo(); + + editor.redo(); + editor.redo(); + editor.redo(); + var val2 = editor.getValue(); + assert.equal(val1, val2); + } +}; + +}); + +if (typeof module !== "undefined" && module === require.main) { + require("asyncjs").test.testcase(module.exports).exec(); +} diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 1affd8b2..d453afc0 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -352,6 +352,12 @@ define(function(require, exports, module) { this.$aceUndo.$redoStack = []; this._emit("changeSync"); }, + startNewGroup: function() { + return this.$aceUndo.startNewGroup(); + }, + markIgnored: function(from, to) { + return this.$aceUndo.markIgnored(from, to); + }, getState: function() { console.log("getState()"); var aceUndo = this.$aceUndo; @@ -371,9 +377,9 @@ define(function(require, exports, module) { var aceUndo = this.$aceUndo; aceUndo.$undoStack = (e.stack || []).filter(function(x) { return x.length; - }); + }).map(updateDeltas); var stack = aceUndo.$undoStack; - var lastDeltaGroup = stack[stack.length] - 1; + var lastDeltaGroup = stack[stack.length - 1]; var lastRev = lastDeltaGroup && lastDeltaGroup[0].id || 0; aceUndo.$rev = lastRev; aceUndo.$maxRev = Math.max(aceUndo.$maxRev, lastRev); @@ -404,70 +410,19 @@ define(function(require, exports, module) { } this._emit("changeSync"); }, - setSession: function(session) { - this.$aceUndo.setSession(session); + addSession: function(session) { + this.$aceUndo.addSession(session); }, getPosition: function() { var aceUndo = this.$aceUndo; return aceUndo.$undoStack.length - 1; }, - getMark: function() { - var aceUndo = this.$aceUndo; - return aceUndo.$undoStack.length - 1; - }, getLength: function() { var aceUndo = this.$aceUndo; return aceUndo.$undoStack.length + aceUndo.$redoStack.length; } }; - function UndoManagerProxy(undoManager, session) { - this.$u = undoManager; - this.$doc = session; - } - - (function() { - this.add = function(delta, doc) { - this.$u.add(delta, doc); - }; - - Object.defineProperty(this, "", { - configurable: true, - get: function() { - return this.$u.lastDeltas; - }, - set: function(v) { - this.$u.lastDeltas = v; - } - }); - - this.undo = function() { - var selectionRange = this.$u.undo(true); - if (selectionRange) { - this.$doc.selection.setSelectionRange(selectionRange); - } - }; - - this.redo = function() { - var selectionRange = this.$u.redo(true); - if (selectionRange) { - this.$doc.selection.setSelectionRange(selectionRange); - } - }; - - this.reset = function() { - this.$u.reset(); - }; - - this.hasUndo = function() { - return this.$u.hasUndo(); - }; - - this.hasRedo = function() { - return this.$u.hasRedo(); - }; - }).call(UndoManagerProxy.prototype); - /***** Generic Load *****/ handle.on("load", function(){ @@ -1463,8 +1418,7 @@ define(function(require, exports, module) { if (!undoManager) undoManager = session.getUndoManager(); if (undoManager) { - var undoManagerProxy = new UndoManagerProxy(undoManager, s); - s.setUndoManager(undoManagerProxy.$aceUndo); + s.setUndoManager(undoManager); } // Overwrite the default $informUndoManager function such that new deltas diff --git a/plugins/c9.ide.editors/document.js b/plugins/c9.ide.editors/document.js index ec70358e..6c7174c5 100644 --- a/plugins/c9.ide.editors/document.js +++ b/plugins/c9.ide.editors/document.js @@ -40,15 +40,16 @@ define(function(require, module, exports) { // Listen to changes and detect when the value of the editor // is different from what is on disk - function updateStatus(e) { - var c = !undoManager.isAtBookmark(); - if (changed !== c) { - changed = c; - emit("changed", { changed: c }); - } + function initUndo(){ + undoManager.on("change", function(e) { + var c = !undoManager.isAtBookmark(); + if (changed !== c) { + changed = c; + emit("changed", { changed: c }); + } + }); } - undoManager.on("change", updateStatus); - undoManager.on("changeSync", updateStatus); + initUndo(); /***** Methods *****/ From cef4157eb3e20a60fadd3517642050a5ef7bd824 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 7 Apr 2015 23:43:45 +0400 Subject: [PATCH 58/61] revert changeSync event --- plugins/c9.ide.ace/ace.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index d453afc0..b1073aac 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -305,8 +305,7 @@ define(function(require, exports, module) { this.deleyedEmit = lang.delayedCall(this._emit.bind(null, "change")) .schedule.bind(null, 0); - undoManager.on("changeSync", this.deleyedEmit); - this.setState(state); + this.setState(state, true); } function updateDeltas(deltas) { if (deltas[0] && deltas[0].deltas) { @@ -321,22 +320,22 @@ define(function(require, exports, module) { AceUndoManager.prototype = { add: function(delta, doc) { this.$aceUndo.add(delta, doc); - this._emit("changeSync"); + this._emit("change"); }, addSelection: function(range, rev) { this.$aceUndo.addSelection(range, rev); }, undo: function(dontSelect) { this.$aceUndo.undo(dontSelect); - this._emit("changeSync"); + this._emit("change"); }, redo: function(dontSelect) { this.$aceUndo.redo(dontSelect); - this._emit("changeSync"); + this._emit("change"); }, reset: function(){ this.$aceUndo.reset(); - this._emit("changeSync"); + this._emit("change"); }, canUndo: function() { return this.$aceUndo.canUndo(); @@ -346,11 +345,11 @@ define(function(require, exports, module) { }, clearUndo: function() { this.$aceUndo.$undoStack = []; - this._emit("changeSync"); + this._emit("change"); }, clearRedo: function() { this.$aceUndo.$redoStack = []; - this._emit("changeSync"); + this._emit("change"); }, startNewGroup: function() { return this.$aceUndo.startNewGroup(); @@ -372,7 +371,7 @@ define(function(require, exports, module) { position: stack.length - 1 }; }, - setState: function(e) { + setState: function(e, silent) { console.log("setState()"); var aceUndo = this.$aceUndo; aceUndo.$undoStack = (e.stack || []).filter(function(x) { @@ -383,14 +382,15 @@ define(function(require, exports, module) { var lastRev = lastDeltaGroup && lastDeltaGroup[0].id || 0; aceUndo.$rev = lastRev; aceUndo.$maxRev = Math.max(aceUndo.$maxRev, lastRev); - this.bookmark(e.mark); + this.$aceUndo.bookmark(e.mark); + silent || this._emit("change"); }, isAtBookmark: function() { return this.$aceUndo.isAtBookmark(); }, bookmark: function(rev) { this.$aceUndo.bookmark(rev); - this._emit("changeSync"); + this._emit("change"); }, bookmarkPosition: function(index) { if (index > -1) { @@ -408,7 +408,7 @@ define(function(require, exports, module) { } else { this.$aceUndo.bookmark(index); } - this._emit("changeSync"); + this._emit("change"); }, addSession: function(session) { this.$aceUndo.addSession(session); From ce06798521faccf240eb059a6a1138980da2cf45 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 2 May 2015 03:26:41 +0400 Subject: [PATCH 59/61] fix inconsistency between getState and setState --- node_modules/ace/lib/ace/editor.js | 2 -- node_modules/ace/lib/ace/undomanager.js | 3 ++- plugins/c9.ide.ace/ace.js | 33 +++++++++++++++++++------ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/node_modules/ace/lib/ace/editor.js b/node_modules/ace/lib/ace/editor.js index 8d816cbd..5cc23e95 100644 --- a/node_modules/ace/lib/ace/editor.js +++ b/node_modules/ace/lib/ace/editor.js @@ -111,8 +111,6 @@ var Editor = function(renderer, session) { oop.implement(this, EventEmitter); this.$initOperationListeners = function() { - this.commands.toggleRecording(); - this.commands.on("exec", this.startOperation.bind(this), true); this.commands.on("afterExec", this.endOperation.bind(this), true); diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index c23eb4f9..bdfdfb89 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -219,7 +219,8 @@ var UndoManager = function() { this.$undoStack = []; this.$redoStack = []; this.$rev = 0; - this.mark = 0; + this.mark = -1; + this.$redoStackBaseRev = this.$rev; this.selections = []; }; diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index b1073aac..01d9989c 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -360,29 +360,46 @@ define(function(require, exports, module) { getState: function() { console.log("getState()"); var aceUndo = this.$aceUndo; - var stack = aceUndo.$undoStack.map(function(deltaSet) { - return deltaSet.filter(function (d) { + var mark = -1; + var aceMark = aceUndo.mark; + var stack = []; + function transform(deltaSet) { + var newDelta = deltaSet.filter(function (d) { + if (d.id == aceMark) mark = stack.length; return d.action == "insert" || d.action == "remove"; }); - }); + stack.push(newDelta); + } + aceUndo.$undoStack.forEach(transform); + if (aceUndo.$redoStackBaseRev == aceUndo.$rev) + aceUndo.$redoStack.forEach(transform); return { stack: stack, - mark: aceUndo.mark, - position: stack.length - 1 + mark: mark, + position: aceUndo.$undoStack.length - 1 }; }, setState: function(e, silent) { console.log("setState()"); var aceUndo = this.$aceUndo; - aceUndo.$undoStack = (e.stack || []).filter(function(x) { + var stack = e.stack || []; + var marked = stack[e.mark] && stack[e.mark][0]; + var pos = e.position + 1; + var undo = stack.slice(0, pos); + var redo = stack.slice(pos); + aceUndo.$undoStack = undo.filter(function(x) { return x.length; }).map(updateDeltas); - var stack = aceUndo.$undoStack; + aceUndo.$redoStack = redo.filter(function(x) { + return x.length; + }).map(updateDeltas); + stack = aceUndo.$undoStack; var lastDeltaGroup = stack[stack.length - 1]; var lastRev = lastDeltaGroup && lastDeltaGroup[0].id || 0; aceUndo.$rev = lastRev; + aceUndo.$redoStackBaseRev == aceUndo.$rev; aceUndo.$maxRev = Math.max(aceUndo.$maxRev, lastRev); - this.$aceUndo.bookmark(e.mark); + this.$aceUndo.bookmark(marked && marked.id != null ? marked.id : -1); silent || this._emit("change"); }, isAtBookmark: function() { From e7c56ee45c5afdd43f45a823355389995bebdfa9 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 3 May 2015 14:24:47 +0400 Subject: [PATCH 60/61] fix failing tests --- node_modules/ace/lib/ace/undomanager.js | 4 ++-- node_modules/ace/lib/ace/undomanager_test.js | 21 +++++++++++++++++++- plugins/c9.ide.ace/ace.js | 13 ++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/node_modules/ace/lib/ace/undomanager.js b/node_modules/ace/lib/ace/undomanager.js index bdfdfb89..1c76f4f4 100644 --- a/node_modules/ace/lib/ace/undomanager.js +++ b/node_modules/ace/lib/ace/undomanager.js @@ -219,7 +219,7 @@ var UndoManager = function() { this.$undoStack = []; this.$redoStack = []; this.$rev = 0; - this.mark = -1; + this.mark = 0; this.$redoStackBaseRev = this.$rev; this.selections = []; }; @@ -552,7 +552,7 @@ function moveDeltasByOne(redoStack, d) { d = cloneDelta(d); for (var j = redoStack.length; j--;) { var deltaSet = redoStack[j]; - for (var i = deltaSet.length; i--;) { + for (var i = deltaSet.length; i--> 0;) { var x = deltaSet[i]; var xformed = xform(x, d); d = xformed[0]; diff --git a/node_modules/ace/lib/ace/undomanager_test.js b/node_modules/ace/lib/ace/undomanager_test.js index 6d0dbf58..4a9c1028 100644 --- a/node_modules/ace/lib/ace/undomanager_test.js +++ b/node_modules/ace/lib/ace/undomanager_test.js @@ -60,7 +60,7 @@ module.exports = { session.setValue("012345-012345-012345"); session.insert({row: 0, column: 0}, "xx"); session.markUndoGroup(); - session.remove({start: {row: 0, column: 10}, end: {row: 0, column: 15}}); + session.remove(new Range(0, 10, 0, 15)); session.markUndoGroup(); session.insert({row: 0, column: 5}, "yy"); session.markUndoGroup(); @@ -82,6 +82,25 @@ module.exports = { editor.redo(); var val2 = editor.getValue(); assert.equal(val1, val2); + }, + "test: conflicting deletes": function() { + var session = new EditSession(""); + var editor = new Editor(new MockRenderer(), session); + var undoManager = new UndoManager(); + session.setUndoManager(undoManager); + + session.setValue("012345\nabcdefg\nxyz"); + session.remove(new Range(0, 2, 0, 4)); + assert.equal(session.getLine(0), "0145"); + session.markUndoGroup(); + editor.undo(); + session.remove(new Range(0, 1, 0, 5)); + assert.equal(session.getLine(0), "05"); + session.markUndoGroup(); + editor.redo(); + assert.equal(session.getLine(0), "05"); + editor.undo(); + assert.equal(session.getLine(0), "012345"); } }; diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 01d9989c..a06b159e 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -358,7 +358,6 @@ define(function(require, exports, module) { return this.$aceUndo.markIgnored(from, to); }, getState: function() { - console.log("getState()"); var aceUndo = this.$aceUndo; var mark = -1; var aceMark = aceUndo.mark; @@ -380,7 +379,6 @@ define(function(require, exports, module) { }; }, setState: function(e, silent) { - console.log("setState()"); var aceUndo = this.$aceUndo; var stack = e.stack || []; var marked = stack[e.mark] && stack[e.mark][0]; @@ -397,9 +395,15 @@ define(function(require, exports, module) { var lastDeltaGroup = stack[stack.length - 1]; var lastRev = lastDeltaGroup && lastDeltaGroup[0].id || 0; aceUndo.$rev = lastRev; - aceUndo.$redoStackBaseRev == aceUndo.$rev; + aceUndo.$redoStackBaseRev = aceUndo.$rev; aceUndo.$maxRev = Math.max(aceUndo.$maxRev, lastRev); - this.$aceUndo.bookmark(marked && marked.id != null ? marked.id : -1); + var markedRev = marked && marked.id; + if (markedRev != null) + this.$aceUndo.bookmark(markedRev); + else if (e.mark == e.position) + this.$aceUndo.bookmark(); + else + this.$aceUndo.bookmark(-1); silent || this._emit("change"); }, isAtBookmark: function() { @@ -419,6 +423,7 @@ define(function(require, exports, module) { } var deltaSet = stack[index]; var rev = deltaSet && deltaSet[0] && deltaSet[0].id; + if (rev == null) rev = -1; this.$aceUndo.bookmark(rev); } else if (index == -1) { this.$aceUndo.bookmark(0); From 8a88de165645f4c1ef10660c0f85dfa1c26c942d Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 4 May 2015 06:55:22 +0400 Subject: [PATCH 61/61] revert changes to getUndoSelection for now --- node_modules/ace/lib/ace/edit_session.js | 54 +++++++++++++++++------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/node_modules/ace/lib/ace/edit_session.js b/node_modules/ace/lib/ace/edit_session.js index 30a8749d..56c0f0b5 100644 --- a/node_modules/ace/lib/ace/edit_session.js +++ b/node_modules/ace/lib/ace/edit_session.js @@ -1186,11 +1186,14 @@ EditSession.$uid = 0; this.doc.applyDelta(delta); } } + + if (!dontSelect) { + if (deltas.selectionAfter) + this.selection.fromJSON(deltas.selectionAfter); + else + this.selection.setRange(this.$getUndoSelection(deltas, false)); + } this.$fromUndo = false; - if (deltas.selectionAfter) - this.selection.fromJSON(deltas.selectionAfter); - else - this.selection.setRange(this.$getUndoSelection(deltas, false)); }; /** @@ -1202,22 +1205,43 @@ EditSession.$uid = 0; this.$undoSelect = enable; }; - this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { + this.$getUndoSelection = function(deltas, isUndo) { function isInsert(delta) { return isUndo ? delta.action !== "insert" : delta.action === "insert"; } - var i; - if (isUndo) { - i = 0; - while (!deltas[i].start && i < deltas.length) - i++; + + var delta = deltas[0]; + var range, point; + var lastDeltaIsInsert = false; + if (isInsert(delta)) { + range = Range.fromPoints(delta.start, delta.end); + lastDeltaIsInsert = true; } else { - i = deltas.length - 1; - while (!deltas[i].end && i >= 0) - i--; + range = Range.fromPoints(delta.start, delta.start); + lastDeltaIsInsert = false; } - var pos = isInsert(deltas[i]) ? deltas[i].end : deltas[i].start; - return Range.fromPoints(pos, pos); + + for (var i = 1; i < deltas.length; i++) { + delta = deltas[i]; + if (isInsert(delta)) { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range.setStart(point); + } + point = delta.end; + if (range.compare(point.row, point.column) == 1) { + range.setEnd(point); + } + lastDeltaIsInsert = true; + } else { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range = Range.fromPoints(delta.start, delta.start); + } + lastDeltaIsInsert = false; + } + } + return range; }; /**