diff --git a/src/command/Menus.js b/src/command/Menus.js index bc86bcff158..3fafeca55eb 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -468,7 +468,8 @@ define(function (require, exports, module) { */ Menu.prototype.removeMenuDivider = function (menuItemID) { var menuItem, - $HTMLMenuItem; + $HTMLMenuItem, + dividerID; if (!menuItemID) { console.error("removeMenuDivider(): missing required parameters: menuItemID"); @@ -497,7 +498,8 @@ define(function (require, exports, module) { return; } } else { - brackets.app.removeMenuItem(menuItemID, function (err) { + dividerID = menuItemID.substring(menuItemID.lastIndexOf("brackets-menuDivider-")); + brackets.app.removeMenuItem(dividerID, function (err) { if (err) { console.error("removeMenuDivider() -- divider not found: %s (error: %s)", menuItemID, err); } @@ -624,7 +626,7 @@ define(function (require, exports, module) { // For sections, pass in the marker for that section. relativeID = relativeID.sectionMarker; } - + brackets.app.addMenuItem(this.id, name, commandID, bindingStr, displayStr, position, relativeID, function (err) { switch (err) { case NO_ERROR: @@ -947,7 +949,7 @@ define(function (require, exports, module) { // Remove all of the menu items in the menu menu = getMenu(id); - CollectionUtils.forEach(menuItemMap, function (value, key) { + _.forEach(menuItemMap, function (value, key) { if (key.substring(0, id.length) === id) { if (value.isDivider) { menu.removeMenuDivider(key); diff --git a/src/editor/Editor.js b/src/editor/Editor.js index b0822549922..5eb15985572 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -140,10 +140,18 @@ define(function (require, exports, module) { if (indentAuto) { var currentLength = line.length; CodeMirror.commands.indentAuto(instance); - // If the amount of whitespace didn't change, insert another tab + + // If the amount of whitespace and the cursor position didn't change, we must have + // already been at the correct indentation level as far as CM is concerned, so insert + // another tab. if (instance.getLine(from.line).length === currentLength) { - insertTab = true; - to.ch = 0; + var newFrom = instance.getCursor(true), + newTo = instance.getCursor(false); + if (newFrom.line === from.line && newFrom.ch === from.ch && + newTo.line === to.line && newTo.ch === to.ch) { + insertTab = true; + to.ch = 0; + } } } else if (instance.somethingSelected() && from.line !== to.line) { CodeMirror.commands.indentMore(instance); diff --git a/src/extensions/default/DebugCommands/main.js b/src/extensions/default/DebugCommands/main.js index 7e4e9463ac3..f9b2e8d0eca 100644 --- a/src/extensions/default/DebugCommands/main.js +++ b/src/extensions/default/DebugCommands/main.js @@ -43,29 +43,29 @@ define(function (require, exports, module) { StringUtils = brackets.getModule("utils/StringUtils"), Dialogs = brackets.getModule("widgets/Dialogs"), Strings = brackets.getModule("strings"), + AppInit = brackets.getModule("utils/AppInit"), + UrlParams = brackets.getModule("utils/UrlParams").UrlParams, NodeDebugUtils = require("NodeDebugUtils"), PerfDialogTemplate = require("text!htmlContent/perf-dialog.html"), LanguageDialogTemplate = require("text!htmlContent/language-dialog.html"); var KeyboardPrefs = JSON.parse(require("text!keyboard.json")); - - + /** @const {string} Brackets Application Menu Constant */ var DEBUG_MENU = "debug-menu"; /** @const {string} Debug commands IDs */ - var DEBUG_REFRESH_WINDOW = "debug.refreshWindow", // string must MATCH string in native code (brackets_extensions) - DEBUG_SHOW_DEVELOPER_TOOLS = "debug.showDeveloperTools", - DEBUG_RUN_UNIT_TESTS = "debug.runUnitTests", - DEBUG_SHOW_PERF_DATA = "debug.showPerfData", - DEBUG_NEW_BRACKETS_WINDOW = "debug.newBracketsWindow", - DEBUG_SWITCH_LANGUAGE = "debug.switchLanguage", - DEBUG_ENABLE_NODE_DEBUGGER = "debug.enableNodeDebugger", - DEBUG_LOG_NODE_STATE = "debug.logNodeState", - DEBUG_RESTART_NODE = "debug.restartNode"; - - - + var DEBUG_REFRESH_WINDOW = "debug.refreshWindow", // string must MATCH string in native code (brackets_extensions) + DEBUG_SHOW_DEVELOPER_TOOLS = "debug.showDeveloperTools", + DEBUG_RUN_UNIT_TESTS = "debug.runUnitTests", + DEBUG_SHOW_PERF_DATA = "debug.showPerfData", + DEBUG_NEW_BRACKETS_WINDOW = "debug.newBracketsWindow", + DEBUG_RELOAD_WITHOUT_USER_EXTS = "debug.reloadWithoutUserExts", + DEBUG_SWITCH_LANGUAGE = "debug.switchLanguage", + DEBUG_ENABLE_NODE_DEBUGGER = "debug.enableNodeDebugger", + DEBUG_LOG_NODE_STATE = "debug.logNodeState", + DEBUG_RESTART_NODE = "debug.restartNode"; + function handleShowDeveloperTools(commandData) { brackets.app.showDeveloperTools(); } @@ -92,6 +92,114 @@ define(function (require, exports, module) { } } + /** + * Disables Brackets' cache via the remote debugging protocol. + * @return {$.Promise} A jQuery promise that will be resolved when the cache is disabled and be rejected in any other case + */ + function _disableCache() { + var result = new $.Deferred(); + + if (brackets.inBrowser) { + result.resolve(); + } else { + var Inspector = brackets.getModule("LiveDevelopment/Inspector/Inspector"); + var port = brackets.app.getRemoteDebuggingPort ? brackets.app.getRemoteDebuggingPort() : 9234; + Inspector.getDebuggableWindows("127.0.0.1", port) + .fail(result.reject) + .done(function (response) { + var page = response[0]; + if (!page || !page.webSocketDebuggerUrl) { + result.reject(); + return; + } + var _socket = new WebSocket(page.webSocketDebuggerUrl); + // Disable the cache + _socket.onopen = function _onConnect() { + _socket.send(JSON.stringify({ id: 1, method: "Network.setCacheDisabled", params: { "cacheDisabled": true } })); + }; + // The first message will be the confirmation => disconnected to allow remote debugging of Brackets + _socket.onmessage = function _onMessage(e) { + _socket.close(); + result.resolve(); + }; + // In case of an error + _socket.onerror = result.reject; + }); + } + + return result.promise(); + } + + /** + * Does a full reload of the browser window + * @param {string} href The url to reload into the window + */ + function _browserReload(href) { + return CommandManager.execute(Commands.FILE_CLOSE_ALL, { promptOnly: true }).done(function () { + // Give everyone a chance to save their state - but don't let any problems block + // us from quitting + try { + $(ProjectManager).triggerHandler("beforeAppClose"); + } catch (ex) { + console.error(ex); + } + + // Disable the cache to make reloads work + _disableCache().always(function () { + window.location.href = href; + }); + }); + } + + function handleFileReload(commandData) { + var href = window.location.href, + params = new UrlParams(); + + // Make sure the Reload Without User Extensions parameter is removed + params.parse(); + + if (params.get("reloadWithoutUserExts")) { + params.remove("reloadWithoutUserExts"); + } + + if (href.indexOf("?") !== -1) { + href = href.substring(0, href.indexOf("?")); + } + + if (!params.isEmpty()) { + href += "?" + params; + } + + _browserReload(href); + } + + function _handleNewBracketsWindow() { + window.open(window.location.href); + } + + function _handleReloadWithoutUserExts() { + var href = window.location.href, + params = new UrlParams(); + + // Remove all menus to assure extension menus and menu items are removed + _.forEach(Menus.getAllMenus(), function (value, key) { + Menus.removeMenu(key); + }); + + params.parse(); + + if (!params.get("reloadWithoutUserExts")) { + params.put("reloadWithoutUserExts", true); + } + + if (href.indexOf("?") !== -1) { + href = href.substring(0, href.indexOf("?")); + } + + href += "?" + params; + _browserReload(href); + } + function _handleShowPerfData() { var templateVars = { delimitedPerfData: PerfUtils.getDelimitedPerfData(), @@ -135,10 +243,6 @@ define(function (require, exports, module) { }); } - function _handleNewBracketsWindow() { - window.open(window.location.href); - } - function _handleSwitchLanguage() { var stringsPath = FileUtils.getNativeBracketsDirectoryPath() + "/nls"; NativeFileSystem.requestNativeFileSystem(stringsPath, function (fs) { @@ -224,84 +328,28 @@ define(function (require, exports, module) { function (error) {} /* menu already disabled, ignore errors */ ); } - - - /** - * Disables Brackets' cache via the remote debugging protocol. - * @return {$.Promise} A jQuery promise that will be resolved when the cache is disabled and be rejected in any other case - */ - function _disableCache() { - var result = new $.Deferred(); - - if (brackets.inBrowser) { - result.resolve(); - } else { - var Inspector = brackets.getModule("LiveDevelopment/Inspector/Inspector"); - var port = brackets.app.getRemoteDebuggingPort ? brackets.app.getRemoteDebuggingPort() : 9234; - Inspector.getDebuggableWindows("127.0.0.1", port) - .fail(result.reject) - .done(function (response) { - var page = response[0]; - if (!page || !page.webSocketDebuggerUrl) { - result.reject(); - return; - } - var _socket = new WebSocket(page.webSocketDebuggerUrl); - // Disable the cache - _socket.onopen = function _onConnect() { - _socket.send(JSON.stringify({ id: 1, method: "Network.setCacheDisabled", params: { "cacheDisabled": true } })); - }; - // The first message will be the confirmation => disconnected to allow remote debugging of Brackets - _socket.onmessage = function _onMessage(e) { - _socket.close(); - result.resolve(); - }; - // In case of an error - _socket.onerror = result.reject; - }); - } - - return result.promise(); - } - - /** Does a full reload of the browser window */ - function handleFileReload(commandData) { - return CommandManager.execute(Commands.FILE_CLOSE_ALL, { promptOnly: true }).done(function () { - // Give everyone a chance to save their state - but don't let any problems block - // us from quitting - try { - $(ProjectManager).triggerHandler("beforeAppClose"); - } catch (ex) { - console.error(ex); - } - - // Disable the cache to make reloads work - _disableCache().always(function () { - window.location.reload(true); - }); - }); - } /* Register all the command handlers */ // Show Developer Tools (optionally enabled) - CommandManager.register(Strings.CMD_SHOW_DEV_TOOLS, DEBUG_SHOW_DEVELOPER_TOOLS, handleShowDeveloperTools) + CommandManager.register(Strings.CMD_SHOW_DEV_TOOLS, DEBUG_SHOW_DEVELOPER_TOOLS, handleShowDeveloperTools) .setEnabled(!!brackets.app.showDeveloperTools); - CommandManager.register(Strings.CMD_REFRESH_WINDOW, DEBUG_REFRESH_WINDOW, handleFileReload); - CommandManager.register(Strings.CMD_NEW_BRACKETS_WINDOW, DEBUG_NEW_BRACKETS_WINDOW, _handleNewBracketsWindow); + CommandManager.register(Strings.CMD_REFRESH_WINDOW, DEBUG_REFRESH_WINDOW, handleFileReload); + CommandManager.register(Strings.CMD_NEW_BRACKETS_WINDOW, DEBUG_NEW_BRACKETS_WINDOW, _handleNewBracketsWindow); + CommandManager.register(Strings.CMD_RELOAD_WITHOUT_USER_EXTS, DEBUG_RELOAD_WITHOUT_USER_EXTS, _handleReloadWithoutUserExts); // Start with the "Run Tests" item disabled. It will be enabled later if the test file can be found. - CommandManager.register(Strings.CMD_RUN_UNIT_TESTS, DEBUG_RUN_UNIT_TESTS, _runUnitTests) + CommandManager.register(Strings.CMD_RUN_UNIT_TESTS, DEBUG_RUN_UNIT_TESTS, _runUnitTests) .setEnabled(false); - CommandManager.register(Strings.CMD_SHOW_PERF_DATA, DEBUG_SHOW_PERF_DATA, _handleShowPerfData); - CommandManager.register(Strings.CMD_SWITCH_LANGUAGE, DEBUG_SWITCH_LANGUAGE, _handleSwitchLanguage); + CommandManager.register(Strings.CMD_SHOW_PERF_DATA, DEBUG_SHOW_PERF_DATA, _handleShowPerfData); + CommandManager.register(Strings.CMD_SWITCH_LANGUAGE, DEBUG_SWITCH_LANGUAGE, _handleSwitchLanguage); // Node-related Commands - CommandManager.register(Strings.CMD_ENABLE_NODE_DEBUGGER, DEBUG_ENABLE_NODE_DEBUGGER, NodeDebugUtils.enableDebugger); - CommandManager.register(Strings.CMD_LOG_NODE_STATE, DEBUG_LOG_NODE_STATE, NodeDebugUtils.logNodeState); - CommandManager.register(Strings.CMD_RESTART_NODE, DEBUG_RESTART_NODE, NodeDebugUtils.restartNode); + CommandManager.register(Strings.CMD_ENABLE_NODE_DEBUGGER, DEBUG_ENABLE_NODE_DEBUGGER, NodeDebugUtils.enableDebugger); + CommandManager.register(Strings.CMD_LOG_NODE_STATE, DEBUG_LOG_NODE_STATE, NodeDebugUtils.logNodeState); + CommandManager.register(Strings.CMD_RESTART_NODE, DEBUG_RESTART_NODE, NodeDebugUtils.restartNode); _enableRunTestsMenuItem(); @@ -312,6 +360,7 @@ define(function (require, exports, module) { var menu = Menus.addMenu(Strings.DEBUG_MENU, DEBUG_MENU, Menus.BEFORE, Menus.AppMenuBar.HELP_MENU); menu.addMenuItem(DEBUG_SHOW_DEVELOPER_TOOLS, KeyboardPrefs.showDeveloperTools); menu.addMenuItem(DEBUG_REFRESH_WINDOW, KeyboardPrefs.refreshWindow); + menu.addMenuItem(DEBUG_RELOAD_WITHOUT_USER_EXTS); menu.addMenuItem(DEBUG_NEW_BRACKETS_WINDOW); menu.addMenuDivider(); menu.addMenuItem(DEBUG_SWITCH_LANGUAGE); @@ -326,4 +375,20 @@ define(function (require, exports, module) { // exposed for convenience, but not official API exports._runUnitTests = _runUnitTests; + + AppInit.htmlReady(function () { + // If in Reload Without User Extensions mode, update menu and toolbar + var params = new UrlParams(), + $icon = $("#toolbar-extension-manager"); + + params.parse(); + + if (params.get("reloadWithoutUserExts") === "true") { + CommandManager.get(Commands.FILE_EXTENSION_MANAGER).setEnabled(false); + $icon.css({display: "none"}); + } else { + CommandManager.get(Commands.FILE_EXTENSION_MANAGER).setEnabled(true); + $icon.css({display: "block"}); + } + }); }); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 61d7a1c808d..7b0b3030d25 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -432,6 +432,7 @@ define({ "CMD_SHOW_DEV_TOOLS" : "Show Developer Tools", "CMD_REFRESH_WINDOW" : "Reload {APP_NAME}", "CMD_NEW_BRACKETS_WINDOW" : "New {APP_NAME} Window", + "CMD_RELOAD_WITHOUT_USER_EXTS" : "Reload Without User Extensions", "CMD_SWITCH_LANGUAGE" : "Switch Language", "CMD_RUN_UNIT_TESTS" : "Run Tests", "CMD_SHOW_PERF_DATA" : "Show Performance Data", diff --git a/src/utils/ExtensionLoader.js b/src/utils/ExtensionLoader.js index 355a498617c..1ae14d13bf8 100644 --- a/src/utils/ExtensionLoader.js +++ b/src/utils/ExtensionLoader.js @@ -41,7 +41,8 @@ define(function (require, exports, module) { var NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem, FileUtils = require("file/FileUtils"), - Async = require("utils/Async"); + Async = require("utils/Async"), + UrlParams = require("utils/UrlParams").UrlParams; // default async initExtension timeout var INIT_EXTENSION_TIMEOUT = 10000; @@ -317,6 +318,8 @@ define(function (require, exports, module) { * @return {!$.Promise} A promise object that is resolved when all extensions complete loading. */ function init(paths) { + var params = new UrlParams(); + if (_init) { // Only init once. Return a resolved promise. return new $.Deferred().resolve().promise(); @@ -325,6 +328,12 @@ define(function (require, exports, module) { if (!paths) { paths = ["default", "dev", getUserExtensionPath()]; } + + params.parse(); + + if (params.get("reloadWithoutUserExts") === "true") { + paths = ["default", "dev"]; + } // Load extensions before restoring the project diff --git a/src/utils/UrlParams.js b/src/utils/UrlParams.js index a098b19bd59..714386bf404 100644 --- a/src/utils/UrlParams.js +++ b/src/utils/UrlParams.js @@ -42,20 +42,27 @@ define(function (require, exports, module) { * @param {string} url */ UrlParams.prototype.parse = function (url) { + var searchString = "", + urlParams, + p, + self = this; + if (url) { - url = url.substring(url.indexOf("?") + 1); + searchString = url.substring(url.indexOf("?") + 1); } else { - url = window.document.location.search.substring(1); + searchString = window.document.location.search.substring(1); } - var urlParams = url.split("&"), - p, - self = this; - - urlParams.forEach(function (param) { - p = param.split("="); - self._store[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - }); + if (searchString) { + urlParams = searchString.split("&"); + + urlParams.forEach(function (param) { + p = param.split("="); + self._store[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + }); + } else { + self._store = {}; + } }; /** @@ -75,6 +82,21 @@ define(function (require, exports, module) { return this._store[name]; }; + /** + * Remove a name/value string pair + * @param {!string} name + */ + UrlParams.prototype.remove = function (name) { + delete this._store[name]; + }; + +/** + * Returns true if the parameter list is empty else returns false. + */ + UrlParams.prototype.isEmpty = function (name) { + return _.isEmpty(this._store); + }; + /** * Encode name/value pairs as URI components. */