diff --git a/README.md b/README.md index f298696509d..2f8d4c3131f 100644 --- a/README.md +++ b/README.md @@ -34,33 +34,23 @@ We support the newer versions of Chrome, Firefox and Safari. ## Installation and Usage +If installing on Windows, please refer to [Installation on Windows](##installation-on-windows-experimental). + Requirements: * NodeJS `>= 0.6.16` * NPM `>= 1.1.16` + * libxml2-dev Install: - # Be sure you have sourcemint installed: - - npm install -g sm - - # Then: - - sm clone --dev https://github.com/ajaxorg/cloud9/tree/master cloud9 - - # or - - git clone https://github.com/ajaxorg/cloud9.git cloud9 + git clone https://github.com/ajaxorg/cloud9.git cd cloud9 - sm install - + npm install +The above install steps create a `cloud9` directory with a `bin/cloud9.sh` +script that can be used to start Cloud9: -The above install steps create a `cloud9` directory in your current directory. Just `cd` into it -and run `bin/cloud9.sh` to start: - - cd cloud9 bin/cloud9.sh Optionally, you may specify the directory you'd like to edit: @@ -86,59 +76,34 @@ Cloud9 is compatible with all connect authentication layers, to implement your own, please see the `plugins-server/cloud9.connect.basic-auth` plugin on how we added basic authentication. -## Updating - -To update to the latest version (if this doesn't work, just make a fresh clone): +## Installation on Windows (experimental) - git pull - sm update +If you're running Cloud9 on Windows you'll have to follow these steps as well: -`sm update` does not currently install missing npm dependencies. To do so use: + * Install [Grep for Windows](http://gnuwin32.sourceforge.net/downlinks/grep.php) + * Add `C:\Program Files (x86)\GnuWin32\bin` to your [PATH](http://www.computerhope.com/issues/ch000549.htm) + * Open a new instance of `cmd` with elevated rights (right click 'Run as adminstrator') + * Now follow the steps under 'Install' + * *Please note that the `npm install` fails due to a libxml error, but you can ignore that for now.* - sm install +To start Cloud9, please don't start through `bin/cloud9.sh` but rather via: -## Development + node server.js [args] -To work on a subcomponent that is copied into node_modules, you can use `sm edit`. -For instance, to work on ACE, run the following from the checkout root: +Please note that there will be errors displayed regarding the `find` command, +and that some features might not work. +Feel free to improve the Windows experience and open a pull request. - sm edit ace - -This is somewhat equivalent to `npm link` but instead of linking to a system wide -shared package it clones the source into the node_modules/ directory. -The idea is to only "edit" when you need to make changes and when done issue -"sm save " (not yet implemented) which will pull up sourcetree to commit, -push code and switch package back to read mode (frozen). The status page - - sm status - -shows problematic and improvement oriented action steps to improve the state of -the program. These relate to git status and dependency changes that need to be -made to bring the dependencies up to date and ready to publish which leads to deployment. - -The line on the status page will have a (W) if it is setup for editing. - -To launch Sourcetree for all dirty/ahead repositories in the dependency -tree use (need to have Sourcetree command-line tools installed (`stree`)): - - sm fix - -The sourcemint package manager works alongside NPM so to link in a -(system-wide shared) NPM package use: - - rm -R node_modules/architect - npm link architect - -`sm` always works on your program sub-tree other than pulling things in -from the cache. +## Updating -To view help info for cloud9 use: +To update to the latest version (if this doesn't work, just make a fresh clone): - sm help + git pull + npm update -To view usage info for `sm` use: +`npm update` does not currently install missing dependencies. To do so use: - sm -h + npm install ## Open Source Projects Used diff --git a/bin/cloud9.sh b/bin/cloud9.sh index 633e9f79767..717ca823455 100755 --- a/bin/cloud9.sh +++ b/bin/cloud9.sh @@ -1,7 +1,6 @@ #!/bin/sh -CMD="$(readlink $0)" -CMD_DIR=`dirname "$CMD"` +CMD_DIR="$( cd "$( dirname "$0" )" && pwd )" cd "$CMD_DIR/.." make update diff --git a/configs/default.js b/configs/default.js index 96f861afa2b..f3f38a0b6b1 100644 --- a/configs/default.js +++ b/configs/default.js @@ -5,14 +5,14 @@ var argv = require('optimist').argv; var path = require("path"); var clientExtensions = {}; -var clientDirs = fs.readdirSync(__dirname + "/../plugins-client"); +var clientDirs = fs.readdirSync(path.join(__dirname, "/../plugins-client")); for (var i = 0; i < clientDirs.length; i++) { var dir = clientDirs[i]; if (dir.indexOf("ext.") !== 0) continue; var name = dir.split(".")[1]; - clientExtensions[name] = __dirname + "/../plugins-client/" + dir; + clientExtensions[name] = path.join(__dirname, "../plugins-client/", dir); } var projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd(); @@ -21,6 +21,7 @@ var vfsUrl = "/vfs"; var port = argv.p || process.env.PORT || 3131; var host = argv.l || process.env.IP || "localhost"; +var debugPort = argv.b || process.env.DEBUG_PORT || 5858; var useAuth = argv.username && argv.password; @@ -29,10 +30,6 @@ var config = [ packagePath: "connect-architect/connect", port: port, host: host - }, { - packagePath: "./cloud9.sourcemint", - prefix: "/static/bundles", - plugins: clientExtensions }, { packagePath: "connect-architect/connect.static", prefix: "/static" @@ -186,7 +183,8 @@ var config = [ }, { packagePath: "./cloud9.run.node-debug", - listenHint: "Important: in your scripts, use 'process.env.PORT' as port and '0.0.0.0' as host." + listenHint: "Important: in your scripts, use 'process.env.PORT' as port and '0.0.0.0' as host.", + debugPort: debugPort }, "./cloud9.run.npm", "./cloud9.run.npmnode", diff --git a/package.json b/package.json index 00131825e1d..6925263c0de 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,9 @@ "cli": "./scripts/help.js" }, - "pm": "sm", "dependencies": { "connect": "1.8.7", - "sourcemint-loader-js": "0.x", - "sourcemint-platform-nodejs": "0.x", "http-error": "0.0.1", "simple-template": "0.0.1", @@ -32,7 +29,14 @@ "smith": "0.1.9", "smith.io": "0.0.36", "vfs-architect": "0.0.2", - "jsDAV": "0.2.4" + "jsDAV": "https://github.com/ajaxorg/jsDAV/tarball/209289355d69adde11dbcd0c83e6dd24a6d9184a", + + "ace": "https://github.com/ajaxorg/ace/tarball/cc2a2c9e056c6d4049f8849ec3ad72974de37177", + "apf": "https://github.com/ajaxorg/apf/tarball/145f65bd24ae83f041418dd01a9c51d278045f3f", + "asyncjs": "https://github.com/ajaxorg/async.js/tarball/0b4ed5608a32c280a7690b5f52883d8d1fc0de08", + "v8debug": "https://github.com/ajaxorg/lib-v8debug/tarball/d2fac9b09724cb2e75e2807191eace9bc27d3b52", + "packager": "https://github.com/ajaxorg/packager/tarball/bee2a82c6d5e2875024bfdc83937e98092ccf04f", + "treehugger": "https://github.com/ajaxorg/treehugger/tarball/b22d7ad712f76a7e7b1070ab339d567dfbf059b1" }, "devDependencies": { @@ -43,16 +47,6 @@ "vfs-nodefs-adapter": "~0.3.0" }, - "mappings": { - "ace": ["npm", "https://github.com/ajaxorg/ace/tarball/cc2a2c9e056c6d4049f8849ec3ad72974de37177"], - "apf": ["npm", "https://github.com/ajaxorg/apf/tarball/145f65bd24ae83f041418dd01a9c51d278045f3f"], - "asyncjs": ["npm", "https://github.com/ajaxorg/async.js/tarball/0b4ed5608a32c280a7690b5f52883d8d1fc0de08"], - "v8debug": ["npm", "https://github.com/ajaxorg/lib-v8debug/tarball/d2fac9b09724cb2e75e2807191eace9bc27d3b52"], - "packager": ["npm", "https://github.com/ajaxorg/packager/tarball/bee2a82c6d5e2875024bfdc83937e98092ccf04f"], - "treehugger": ["npm", "https://github.com/ajaxorg/treehugger/tarball/b22d7ad712f76a7e7b1070ab339d567dfbf059b1"], - "runjs": ["npm", "https://github.com/c9/runjs/tarball/347ad9627fb4a1d63458bc66ea40f0bf1efbf05c"] - }, - "repository" : { "type" : "git", "url" : "git://github.com/ajaxorg/cloud9.git" diff --git a/plugins-client/ext.jslanguage/JSResolver.js b/plugins-client/ext.jslanguage/JSResolver.js new file mode 100644 index 00000000000..232323b9f03 --- /dev/null +++ b/plugins-client/ext.jslanguage/JSResolver.js @@ -0,0 +1,84 @@ +define(function(require, exports, module) { + "use strict"; + + var markerResolution = require('ext/language/MarkerResolution').MarkerResolution; + var Range = require("ace/range").Range; + + var JSResolver = function(value, ast){ + this.addResolutions = function(markers){ + var _self = this; + markers.forEach(function(curMarker) { + curMarker.resolutions = _self.getResolutions(curMarker); + }); + }; + + this.getResolutions = function(marker){ + var type = this.getType(marker); + if (type){ + if (typeof this[type] === 'function'){ + return this[type](marker); + } + } + return []; + }; + + this.getType = function(marker){ + var msg = marker.message; + if (msg.indexOf("Missing semicolon") !== -1){ + return "missingSemicolon"; + } else if (msg.indexOf("Unnecessary semicolon") !== -1){ + return "unnecessarySemicolon"; + } + }; + + this.missingSemicolon = function(marker){ + console.log(marker.pos); + var label = "Add semicolon"; + var image = ""; + var row = marker.pos.sl; + var column = marker.pos.sc; + + var lines = value.split("\n"); + var before = lines[row].substring(0, column); + var after = lines[row].substring(column); + var preview = "Add semicolon

" + before + "; " + after + "

"; + + var insert = ";"; + if (after.length){ + insert += " "; + } + + var delta = { + action: "insertText", + range: new Range(row, column, row, column + insert.length), + text: insert + }; + + return [markerResolution(label, image, preview, [delta])]; + }; + + this.unnecessarySemicolon = function(marker){ + console.log(marker.pos); + var label = "Remove semicolon"; + var image = ""; + var row = marker.pos.sl; + var column = marker.pos.sc; + + var lines = value.split("\n"); + var before = lines[row].substring(0, column); + var after = lines[row].substring(column + 1); + var preview = "Remove semicolon

" + before + ";" + after + "

"; + + var delta = { + action: "removeText", + range: new Range(row, column, row, column + 1) + }; + + return [markerResolution(label, image, preview, [delta])]; + }; + + }; + + exports.JSResolver = JSResolver; + +}); diff --git a/plugins-client/ext.jslanguage/jshint.js b/plugins-client/ext.jslanguage/jshint.js index aab074959ce..ad1b876332c 100644 --- a/plugins-client/ext.jslanguage/jshint.js +++ b/plugins-client/ext.jslanguage/jshint.js @@ -74,7 +74,7 @@ handler.analyzeSync = function(value, ast) { if(disabledJSHintWarnings[i].test(warning.reason)) return; markers.push({ - pos: { + pos: { // TODO quickfix framework needs el/ec in order to be able to select the issue in the editor sl: warning.line-1, sc: warning.character-1 }, @@ -83,9 +83,11 @@ handler.analyzeSync = function(value, ast) { message: warning.reason }); }); + return markers; }; + /** * Gets an object like { foo: true } for JSHint global comments * like / * global foo: true * / diff --git a/plugins-client/ext.jslanguage/scope_analyzer.js b/plugins-client/ext.jslanguage/scope_analyzer.js index 1160fce7b66..1b8c28a7e56 100644 --- a/plugins-client/ext.jslanguage/scope_analyzer.js +++ b/plugins-client/ext.jslanguage/scope_analyzer.js @@ -19,6 +19,8 @@ var completeUtil = require("ext/codecomplete/complete_util"); var handler = module.exports = Object.create(baseLanguageHandler); var outline = require("ext/jslanguage/outline"); var jshint = require("ext/jslanguage/jshint"); +var JSResolver = require('ext/jslanguage/JSResolver').JSResolver; + require("treehugger/traverse"); // add traversal functions to trees var CALLBACK_METHODS = ["forEach", "map", "reduce", "filter", "every", "some"]; @@ -260,6 +262,21 @@ handler.handlesLanguage = function(language) { return language === 'javascript'; }; +handler.getResolutions = function(value, ast, markers, callback){ + var resolver = new JSResolver(value, ast); + resolver.addResolutions(markers); + callback(markers); +}; + +handler.hasResolution = function(value, ast, marker) { + if (marker.resolutions && marker.resolutions.length){ + return true; + } + var resolver = new JSResolver(value, ast); + return resolver.getType(marker); +}; + + var scopeId = 0; var Variable = module.exports.Variable = function Variable(declaration) { @@ -506,8 +523,12 @@ handler.analyze = function(value, ast, callback) { }, 'Function(x, fargs, body)', function(b, node) { if (inLoop && !inLoopAllowed) { + var pos = this.getPos(); + // treehugger doesn't store info on the position of "function" token + var line = handler.doc.getLine(pos.sl); + var sc = line.substring(0, pos.sc).lastIndexOf("function"); markers.push({ - pos: { sl: this.getPos().sl, el: this.getPos().sl, sc: this.getPos().sc, ec: this.getPos().sc + "function".length }, + pos: { sl: pos.sl, el: pos.sl, sc: sc, ec: sc + "function".length }, level: 'warning', type: 'warning', message: "Function created in a loop." diff --git a/plugins-client/ext.language/MarkerResolution.js b/plugins-client/ext.language/MarkerResolution.js new file mode 100644 index 00000000000..92682406638 --- /dev/null +++ b/plugins-client/ext.language/MarkerResolution.js @@ -0,0 +1,25 @@ + +define(function(require, exports, module) { +"use strict"; + +/** +* data structure for resolutions, containing +* a label (short description, to be displayed in the list of resolutions), +* an image (to be displayed in the list of resolutions), +* a preview, +* an array of deltas (containing the changes to be applied), +* the position where the cursor should be after applying +*/ +var MarkerResolution = function(label, image, preview, deltas, cursorTarget){ + return { + label: label, + image: image, + preview: preview, + deltas: deltas, + cursorTarget: cursorTarget + }; +}; + +exports.MarkerResolution = MarkerResolution; + +}); \ No newline at end of file diff --git a/plugins-client/ext.language/base_handler.js b/plugins-client/ext.language/base_handler.js index 93ea551f63b..972a0a79607 100644 --- a/plugins-client/ext.language/base_handler.js +++ b/plugins-client/ext.language/base_handler.js @@ -229,6 +229,22 @@ module.exports = { */ jumpToDefinition: function(doc, fullAst, pos, currentNode, callback) { callback(); + }, + + /** + * Invoked after markers were generated in analyze() + * @return marker array with attached resolutions + */ + getResolutions: function(doc, fullAst, markers, callback) { + callback(); + }, + + /** + * @return true iff the resolver for this marker could generate + * at least one resolution for it + */ + hasResolution: function(doc, fullAst, marker, callback) { + callback(); } }; diff --git a/plugins-client/ext.language/language.js b/plugins-client/ext.language/language.js index 51ef38f6e75..75eb1579022 100644 --- a/plugins-client/ext.language/language.js +++ b/plugins-client/ext.language/language.js @@ -17,6 +17,7 @@ var UIWorkerClient = require("ace/worker/worker_client").UIWorkerClient; var useUIWorker = window.location && /[?&]noworker=1/.test(window.location.search); var complete = require("ext/language/complete"); +var quickfix = require("ext/language/quickfix"); var marker = require("ext/language/marker"); var refactor = require("ext/language/refactor"); var outline = require("ext/language/outline"); @@ -92,6 +93,7 @@ module.exports = ext.register("ext/language/language", { outline.hook(_self, worker); keyhandler.hook(_self, worker); jumptodef.hook(_self, worker); + quickfix.hook(_self); ide.dispatchEvent("language.worker", {worker: worker}); ide.addEventListener("$event.language.worker", function(callback){ diff --git a/plugins-client/ext.language/language.xml b/plugins-client/ext.language/language.xml index b52c4876383..a6f2f43fc6e 100644 --- a/plugins-client/ext.language/language.xml +++ b/plugins-client/ext.language/language.xml @@ -124,4 +124,27 @@ + + + + + + + + + + + diff --git a/plugins-client/ext.language/marker.js b/plugins-client/ext.language/marker.js index e1bbb6313c4..fc878fb7ba2 100644 --- a/plugins-client/ext.language/marker.js +++ b/plugins-client/ext.language/marker.js @@ -83,7 +83,9 @@ module.exports = { var gutterAnno = { guttertext: anno.message, type: anno.level || "warning", - text: anno.message + text: anno.message, + pos: anno.pos, + resolutions: anno.resolutions // row will be filled in updateFloat() }; diff --git a/plugins-client/ext.language/quickfix.js b/plugins-client/ext.language/quickfix.js new file mode 100644 index 00000000000..af53ae4274e --- /dev/null +++ b/plugins-client/ext.language/quickfix.js @@ -0,0 +1,451 @@ +/** + * Cloud9 Language Foundation + * + * @copyright 2011, Ajax.org B.V. + * @license GPLv3 + */ +define(function(require, exports, module) { + +/*global barQuickfixCont sbQuickfix txtQuickfixHolder txtQuickfix txtQuickfixDoc */ + +var ide = require("core/ide"); +var dom = require("ace/lib/dom"); +var code = require("ext/code/code"); +var editors = require("ext/editors/editors"); +var lang = require("ace/lib/lang"); + +var quickfix; + +var oldCommandKey, oldOnTextInput; + +var CLASS_SELECTED = "cc_complete_option selected"; +var CLASS_UNSELECTED = "cc_complete_option"; +var SHOW_DOC_DELAY = 1500; +var SHOW_DOC_DELAY_MOUSE_OVER = 100; +var HIDE_DOC_DELAY = 1000; +var MENU_WIDTH = 400; +var MENU_SHOWN_ITEMS = 9; +var EXTRA_LINE_HEIGHT = 3; +var QFBOX_MINTIME = 500; + + +var ignoreMouseOnce = false; +var isDocShown; +var isDrawDocInvokeScheduled = false; + +var drawDocInvoke = lang.deferredCall(function() { + if (isPopupVisible() && quickfix.quickFixes[quickfix.selectedIdx].preview) { + isDocShown = true; + txtQuickfixDoc.parentNode.show(); + } + isDrawDocInvokeScheduled = false; +}); + +var undrawDocInvoke = lang.deferredCall(function() { + if (!isPopupVisible()) { + isDocShown = false; + txtQuickfixDoc.parentNode.hide(); + } +}); + +function isPopupVisible() { + return barQuickfixCont.$ext.style.display !== "none"; +} + +var commands = require("ext/commands/commands"); + +module.exports = { + + hook: function(ext) { + var _self = this; + quickfix = this; + + ide.addEventListener("tab.afterswitch", function(e) { + var page = e.nextPage; + if (!page || !page.$editor || page.$editor.path != "ext/code/code") + return; + var ace = page.$editor.amlEditor.$editor; + + if (!ace.$markerListener) + _self.initEditor(ace); + }); + + + commands.addCommand({ + name: "quickfix", + hint: "quickfix", + bindKey: {mac: "Ctrl-Shift-Q|Ctrl-Alt-Q", win: "Ctrl-Shift-Q|Ctrl-Alt-Q"}, + isAvailable : function(editor){ + return apf.activeElement.localName == "codeeditor"; + }, + exec: function(editor) { + _self.invoke(); + } + }); + + + }, + + init: function(amlNode) { + }, + + initEditor : function(editor){ + var _self = this; + + editor.on("guttermousedown", editor.$markerListener = function(e) { + _self.editor = editor; + if (e.getButton()) // !editor.isFocused() + return; + var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + if (gutterRegion != "markers") + return; + + var row = e.getDocumentPosition().row; + _self.showQuickfixBox(row, 0); + + }); + }, + + getAnnos: function(row){ + var editor = editors.currentEditor.amlEditor.$editor; + var res = []; + + editor.getSession().languageAnnos.forEach(function(anno, idx){ + if (anno.row == row){ + res.push(anno); + + /* Select the annotation in the editor */ + anno.select = function(){ + if (!(anno.pos.sl && anno.pos.sc && anno.pos.el && anno.pos.ec)){ + return; + } + var startPos = { row: anno.pos.sl, column: anno.pos.sc }; + var endPos = { row: anno.pos.el, column: anno.pos.ec }; + if (startPos.row < endPos.row || startPos.column < endPos.column){ + editor.getSelection().setSelectionRange( + {start: startPos, end: endPos}); + } + }; + + /* + * Returns the screen coordinates of the start of the annotation + */ + anno.getScreenCoordinates = function(){ + return editor.renderer.textToScreenCoordinates(anno.pos.sl, + anno.pos.sc); + }; + } + }); + + res.sort(function(a,b){ return a.pos.sc - b.pos.sc; }); + + return res; + }, + + + + showQuickfixBox: function(row, column) { + var _self = this; + + // Get the annotation on this line that is containing or left of the + // position (row,column) + var annos = _self.getAnnos(row); + if (!annos.length){ + return; + } + for (var i = 0; i < annos.length - 1; i++){ + if (annos[i+1].pos.sc > column){ break; } + } + var anno = annos[i]; + if (!anno.resolutions.length){ + // TODO If some other annotation on this line has resolutions, + // quickfix that one instead + return; + } + + this.editor = editors.currentEditor; + var ace = this.editor.amlEditor.$editor; + this.selectedIdx = 0; + this.scrollIdx = 0; + this.quickfixEls = []; + //this.annos = annos; + this.quickFixes = []; + this.quickfixElement = txtQuickfix.$ext; + this.docElement = txtQuickfixDoc.$ext; + this.cursorConfig = ace.renderer.$cursorLayer.config; + this.lineHeight = this.cursorConfig.lineHeight + EXTRA_LINE_HEIGHT; + var style = dom.computedStyle(this.editor.amlEditor.$ext); + this.quickfixElement.style.fontSize = style.fontSize; + + barQuickfixCont.setAttribute('visible', true); + + + // Monkey patch + if(!oldCommandKey) { + oldCommandKey = ace.keyBinding.onCommandKey; + ace.keyBinding.onCommandKey = this.onKeyPress.bind(this); + oldOnTextInput = ace.keyBinding.onTextInput; + ace.keyBinding.onTextInput = this.onTextInput.bind(this); + } + + // Collect all quickfixes for the given annotation + _self.quickFixes = anno.resolutions; + + // Select it in the editor + anno.select(); + + this.populateQuickfixBox(this.quickFixes); + + apf.popup.setContent("quickfixBox", barQuickfixCont.$ext); + var boxLength = this.quickFixes.length || 1; + var quickfixBoxHeight = 11 + Math.min(10 * this.lineHeight, boxLength * (this.lineHeight)); + + var innerBoxLength = this.quickFixes.length || 1; + var innerQuickfixBoxHeight = Math.min(10 * this.lineHeight, innerBoxLength * (this.lineHeight)); + txtQuickfixHolder.$ext.style.height = innerQuickfixBoxHeight + "px"; + + ignoreMouseOnce = !isPopupVisible(); + + var pos = anno.getScreenCoordinates(); + apf.popup.show("quickfixBox", { + x : pos.pageX, + y : pos.pageY + _self.cursorConfig.lineHeight, + height : quickfixBoxHeight, + width : MENU_WIDTH, + animate : false, + callback : function() { + barQuickfixCont.setHeight(quickfixBoxHeight); + barQuickfixCont.$ext.style.height = quickfixBoxHeight + "px"; + sbQuickfix.$resize(); + // HACK: Need to set with non-falsy value first + _self.quickfixElement.scrollTop = 1; + _self.quickfixElement.scrollTop = 0; + } + }); + + this.popupTime = new Date().getTime(); + document.addEventListener("click", quickfix.closeQuickfixBox, false); + ace.container.addEventListener("DOMMouseScroll", quickfix.closeQuickfixBox, false); + ace.container.addEventListener("mousewheel", quickfix.closeQuickfixBox, false); + }, + + closeQuickfixBox : function(event) { + var qfBoxTime = new Date().getTime() - quickfix.popupTime; + if (!quickfix.forceClose && qfBoxTime < QFBOX_MINTIME){ + return; + } + + quickfix.forceClose = false; + + barQuickfixCont.$ext.style.display = "none"; + if (!editors.currentEditor.amlEditor) // no editor, try again later + return; + var ace = editors.currentEditor.amlEditor.$editor; + + // TODO these calls don't work. + document.removeEventListener("click", quickfix.closeQuickfixBox, false); + ace.container.removeEventListener("DOMMouseScroll", quickfix.closeQuickfixBox, false); + ace.container.removeEventListener("mousewheel", quickfix.closeQuickfixBox, false); + + if(oldCommandKey) { + ace.keyBinding.onCommandKey = oldCommandKey; + ace.keyBinding.onTextInput = oldOnTextInput; + } + oldCommandKey = oldOnTextInput = null; + undrawDocInvoke.schedule(HIDE_DOC_DELAY); + }, + + + populateQuickfixBox: function(quickFixes) { + + var _self = this; + _self.quickfixElement.innerHTML = ""; + var cursorConfig = code.amlEditor.$editor.renderer.$cursorLayer.config; + + // For each quickfix, create a list entry + quickFixes.forEach(function(qfix, qfidx){ + + var annoEl = dom.createElement("div"); + annoEl.className = qfidx === _self.selectedIdx ? CLASS_SELECTED : CLASS_UNSELECTED; + var html = ""; + + if (qfix.image) + html = ""; + + html += '' + qfix.label + ''; + + annoEl.innerHTML = html; + + annoEl.addEventListener("mouseover", function() { + if (ignoreMouseOnce) { + ignoreMouseOnce = false; + return; + } + _self.quickfixEls[_self.selectedIdx].className = CLASS_UNSELECTED; + _self.selectedIdx = qfidx; + _self.quickfixEls[_self.selectedIdx].className = CLASS_SELECTED; + _self.updateDoc(); + if (!isDrawDocInvokeScheduled) + drawDocInvoke.schedule(SHOW_DOC_DELAY_MOUSE_OVER); + }); + + + annoEl.addEventListener("click", function() { + quickfix.forceClose = true; + _self.applyQuickfix(qfix); + }); + + + annoEl.style.height = cursorConfig.lineHeight + EXTRA_LINE_HEIGHT + "px"; + annoEl.style.width = (MENU_WIDTH - 10) + "px"; + _self.quickfixElement.appendChild(annoEl); + _self.quickfixEls.push(annoEl); + }); + + _self.updateDoc(true); + + }, + + updateDoc : function(delayPopup) { + this.docElement.innerHTML = ''; + var selected = this.quickFixes[this.selectedIdx]; + + if (selected && selected.preview) { + if (isDocShown) { + txtQuickfixDoc.parentNode.show(); + } + else { + txtQuickfixDoc.parentNode.hide(); + if (!isDrawDocInvokeScheduled || delayPopup) + drawDocInvoke.schedule(SHOW_DOC_DELAY); + } + this.docElement.innerHTML += + selected.preview.replace(/\n/g, '
') + '
'; + } + else { + txtQuickfixDoc.parentNode.hide(); + } + + this.docElement.innerHTML += ''; + }, + + enable : function() { + }, + + disable : function() { + }, + + destroy : function() { + }, + + applyQuickfix : function(qfix){ + var amlEditor = editors.currentEditor.amlEditor; + var doc = amlEditor.getSession().getDocument(); + + doc.applyDeltas(qfix.deltas); + amlEditor.focus(); + + if (qfix.cursorTarget){ + var cursorTarget = qfix.cursorTarget; + var selection = amlEditor.$editor.getSelection(); + selection.clearSelection(); + selection.moveCursorTo(cursorTarget.line, + cursorTarget.column, false); + } + }, + + onTextInput : function(text, pasted) { + this.closeQuickfixBox(); + }, + + onKeyPress : function(e, hashKey, keyCode) { + + if(e.metaKey || e.ctrlKey || e.altKey) { + this.closeQuickfixBox(); + return; + } + + var keyBinding = editors.currentEditor.amlEditor.$editor.keyBinding; + + switch(keyCode) { + case 0: break; + case 32: // Space + this.closeQuickfixBox(); + break; + case 27: // Esc + this.closeQuickfixBox(); + e.preventDefault(); + break; + case 8: // Backspace + this.closeQuickfixBox(); + e.preventDefault(); + break; + case 37: + case 39: + oldCommandKey.apply(keyBinding, arguments); + this.closeQuickfixBox(); + e.preventDefault(); + break; + case 13: // Enter + case 9: // Tab + this.applyQuickfix(this.quickFixes[this.selectedIdx]); + quickfix.forceClose = true; + this.closeQuickfixBox(); + e.stopPropagation(); + e.preventDefault(); + break; + case 40: // Down + if (this.quickfixEls.length === 1) { + this.closeQuickfixBox(); + break; + } + e.stopPropagation(); + e.preventDefault(); + this.quickfixEls[this.selectedIdx].className = CLASS_UNSELECTED; + if(this.selectedIdx < this.quickFixes.length-1) + this.selectedIdx++; + this.quickfixEls[this.selectedIdx].className = CLASS_SELECTED; + if(this.selectedIdx - this.scrollIdx > MENU_SHOWN_ITEMS) { + this.scrollIdx++; + this.quickfixEls[this.scrollIdx].scrollIntoView(true); + } + this.updateDoc(); + break; + case 38: // Up + if (this.quickfixEls.length === 1) { + this.closeQuickfixBox(); + break; + } + e.stopPropagation(); + e.preventDefault(); + if (this.selectedIdx <= 0) + return; + this.quickfixEls[this.selectedIdx].className = CLASS_UNSELECTED; + this.selectedIdx--; + this.quickfixEls[this.selectedIdx].className = CLASS_SELECTED; + if(this.selectedIdx < this.scrollIdx) { + this.scrollIdx--; + this.quickfixEls[this.scrollIdx].scrollIntoView(true); + } + this.updateDoc(); + break; + } + }, + + invoke: function(forceBox) { + var _self = this; + var editor = editors.currentEditor.amlEditor.$editor; + if (editor.inMultiSelectMode) { + _self.closeQuickfixBox(); + return; + } + _self.forceBox = forceBox; + + var pos = editor.getCursorPosition(); + + _self.showQuickfixBox(pos.row, pos.column); + } + +}; + +}); diff --git a/plugins-client/ext.language/worker.js b/plugins-client/ext.language/worker.js index 3b11d67ff84..9247847f3cb 100644 --- a/plugins-client/ext.language/worker.js +++ b/plugins-client/ext.language/worker.js @@ -418,8 +418,15 @@ function asyncParForEach(array, fn, callback) { asyncForEach(_self.handlers, function(handler, next) { if (handler.handlesLanguage(part.language)) { handler.analyze(part.value, ast, function(result) { - if (result) - partMarkers = partMarkers.concat(result); + if (result){ + handler.getResolutions(part.value, ast, result, function(result2){ + if (result2){ + partMarkers = partMarkers.concat(result2); + }else{ + partMarkers = partMarkers.concat(result); + } + }); + } next(); }); } @@ -455,7 +462,7 @@ function asyncParForEach(array, fn, callback) { callback(); }); }; - + this.checkForMarker = function(pos) { var astPos = {line: pos.row, col: pos.column}; for (var i = 0; i < this.currentMarkers.length; i++) { diff --git a/plugins-client/lib.apf/www/apf-packaged/apf_release.js b/plugins-client/lib.apf/www/apf-packaged/apf_release.js index 114943531d4..dfc94d378e2 100644 --- a/plugins-client/lib.apf/www/apf-packaged/apf_release.js +++ b/plugins-client/lib.apf/www/apf-packaged/apf_release.js @@ -40158,7 +40158,7 @@ apf.runWebkit = function(){ var bytes = Array.prototype.map.call(string, function(c) { return c.charCodeAt(0) & 0xff; }); - this.send(new Uint8Array(bytes).buffer); + this.send(new Uint8Array(bytes)); }; } } diff --git a/plugins-server/cloud9.client-plugins/package.json b/plugins-server/cloud9.client-plugins/package.json index 14a1ab33316..8c43895ac04 100644 --- a/plugins-server/cloud9.client-plugins/package.json +++ b/plugins-server/cloud9.client-plugins/package.json @@ -11,8 +11,7 @@ "provides": ["client-plugins"], "consumes": [ "static", - "log", - "sourcemint" + "log" ] } } \ No newline at end of file diff --git a/plugins-server/cloud9.core/ide.js b/plugins-server/cloud9.core/ide.js index 50b6e6f5dd7..85ea9b31bd8 100644 --- a/plugins-server/cloud9.core/ide.js +++ b/plugins-server/cloud9.core/ide.js @@ -45,7 +45,7 @@ var Ide = module.exports = function(options) { plugins: options.plugins, bundledPlugins: options.bundledPlugins || [], requirejsConfig: options.requirejsConfig, - projectName: options.projectName || this.workspaceDir.split("/").pop(), + projectName: options.projectName || Path.basename(this.workspaceDir), version: options.version, extra: options.extra, hosted: !!options.hosted, diff --git a/plugins-server/cloud9.core/view/ide.tmpl.html b/plugins-server/cloud9.core/view/ide.tmpl.html index a71ab4fc8c8..250ec7ee8f8 100644 --- a/plugins-server/cloud9.core/view/ide.tmpl.html +++ b/plugins-server/cloud9.core/view/ide.tmpl.html @@ -44,7 +44,7 @@ var footer = document.createElement("div"); footer.className = "footer"; // yes this is bad - footer.innerHTML = 'Documentation | Server Status | Support'; + footer.innerHTML = 'Documentation | Server Status | Support'; var content = document.createElement("div"); content.className = "loading-progress"; diff --git a/plugins-server/cloud9.fs/fs/file.js b/plugins-server/cloud9.fs/fs/file.js index b16b90adb35..5e71ac06941 100644 --- a/plugins-server/cloud9.fs/fs/file.js +++ b/plugins-server/cloud9.fs/fs/file.js @@ -29,7 +29,7 @@ require("util").inherits(jsDAV_FS_File, jsDAV_FS_Node); var path = this.path; // is it a chunked upload? var size = handler.httpRequest.headers["x-file-size"]; - if (size) { + if (size && size !== "undefined") { var parts = Util.splitPath(this.path); if (!handler.httpRequest.headers["x-file-name"]) handler.httpRequest.headers["x-file-name"] = parts[1]; diff --git a/plugins-server/cloud9.sourcemint/package.json b/plugins-server/cloud9.sourcemint/package.json deleted file mode 100644 index 152fffc78cc..00000000000 --- a/plugins-server/cloud9.sourcemint/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "cloud9.sourcemint", - "version": "0.0.1", - "main": "sourcemint.js", - "private": true, - "dependencies": { - "sourcemint-loader-js": "0.x", - "sourcemint-platform-nodejs": "0.x" - }, - - "plugin": { - "provides": ["sourcemint"], - "consumes": [ - "log", - "connect" - ] - } -} \ No newline at end of file diff --git a/plugins-server/cloud9.sourcemint/sourcemint.js b/plugins-server/cloud9.sourcemint/sourcemint.js deleted file mode 100644 index 24f2d29db92..00000000000 --- a/plugins-server/cloud9.sourcemint/sourcemint.js +++ /dev/null @@ -1,42 +0,0 @@ -var path = require("path"); -var fs = require("fs"); -var bundler = require("sourcemint-platform-nodejs/lib/bundler"); - -module.exports = function startup(options, imports, register) { - - var plugins = options.plugins || {}; - var prefix = (options.prefix || "/static/bundles"); - - var loaderStatic = imports.connect.getModule().static(path.dirname(require.resolve("sourcemint-loader-js/package.json"))); - - imports.connect.useStart(imports.connect.getModule().router(function(app) { - - // Serve the sourcemint loader file. - app.get(/^(\/loader\.[^\/]*)/, function(req, res) { - req.url = req.params[0]; - loaderStatic(req, res, function() { - res.writeHead(404); - res.end("Not found!"); - }); - }); - - // Serve client plugin bundles if available. - for (var name in plugins) { - var route = new RegExp("^" + prefix.replace(/\//g, "\\/") + "\\/" + name + "(?:\.js)?(\\/.*)?$"); - var packagePath = plugins[name] + "/package.json"; - if (path.existsSync(packagePath)) { - var descriptor = JSON.parse(fs.readFileSync(packagePath)); - if (descriptor.bundled === true) { - imports.log.log("MINT", route, plugins[name]); - app.get(route, bundler.hoist(plugins[name], { - packageIdHashSeed: "c9os" - })); - } - } - }; - })); - - register(null, { - "sourcemint": {} - }); -}; \ No newline at end of file diff --git a/server.js b/server.js index 95b22052755..a01c4cf1a62 100755 --- a/server.js +++ b/server.js @@ -15,6 +15,17 @@ if (configName.indexOf("-") === 0) { configName = "default"; } +// If a password is given as a command line parameter, we hide it +// in the title of the process instead of displaying it in plain +// text. +var title_parts = process.argv.slice(); +title_parts.forEach(function(element, index, array) { + if (element === '--password') { + array[index+1] = 'xxxxxxxx'; + } +}); +process.title = title_parts.join(' '); + var debug = false; var packed = false; var packedName = ""; @@ -22,20 +33,20 @@ var packedName = ""; for (var p = 2; p < process.argv.length; p++) { if (process.argv[p] === "-d") { debug = true; - + // apf debug doesn't exist, or it's older than three days--rebuild it if(!path.existsSync("plugins-client/lib.apf/www/apf-packaged/apf_debug.js") || (path.existsSync("plugins-client/lib.apf/www/apf-packaged/apf_debug.js")) && ((new Date() - fs.statSync("plugins-client/lib.apf/www/apf-packaged/apf_debug.js").mtime.valueOf()) / 86400000) >= 3) { console.log("Building apfdebug for first run..."); - + var buildDebug = spawn("npm", ["run-script", "build-debug"]); - + buildDebug.stderr.setEncoding("utf8"); buildDebug.stderr.on('data', function (data) { console.error(data); }); - + buildDebug.on('exit', function (code) { if (code !== 0) { console.error('build-debug process exited with code ' + code); @@ -65,7 +76,7 @@ for (var p = 2; p < process.argv.length; p++) { " '---''(_/--' `-'\\_) Felix Lee"); var buildPackage = spawn("npm", ["run-script", "build-packed"]); - + buildPackage.stderr.setEncoding("utf8"); buildPackage.stderr.on('data', function (data) { console.error(data);