Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Reload Without User Extensions command #5620

Closed
wants to merge 10 commits into from
10 changes: 6 additions & 4 deletions src/command/Menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 11 additions & 3 deletions src/editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
233 changes: 149 additions & 84 deletions src/extensions/default/DebugCommands/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -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(),
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();

Expand All @@ -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);
Expand All @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should have some other indicator in the UI that we're in this mode--for example, maybe we should put something in the status bar ("User Extensions Disabled" or some such). Otherwise it might be mysterious why the extension manager is turned off.

(If you do do that, note that there's a bug right now in the StatusBar that addIndicator() doesn't actually add the indicator :) See #5682. You have to work around it by adding the indicator manually before calling addIndicator().)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was to have the Extension Manager Button in the right sidebar, but in a red style. We could also add a tooltip like All user extensions are disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@njx, should I put in a workaround or should I wait for the addIndicator() fix?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if I can get the addIndicator() fix in soon.

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"});
}
});
});
1 change: 1 addition & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 10 additions & 1 deletion src/utils/ExtensionLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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

Expand Down
Loading