From 974dd712a185918f06418a1a068dceb0213e839e Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 25 Dec 2024 10:00:05 +0530 Subject: [PATCH 1/3] feat: open in system default app in project and working set context menu --- src-node/utils.js | 35 +++++++++++++++++++++++++ src/command/Commands.js | 3 +++ src/command/DefaultMenus.js | 6 ++++- src/document/DocumentCommandHandlers.js | 10 ++++++- src/nls/root/strings.js | 3 ++- src/utils/NodeUtils.js | 14 ++++++++++ 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src-node/utils.js b/src-node/utils.js index 37094812ef..5e0ab484bb 100644 --- a/src-node/utils.js +++ b/src-node/utils.js @@ -226,6 +226,40 @@ function openNativeTerminal({cwd, usePowerShell = false}) { }); } +/** + * Opens a file in the default application for its type on Windows, macOS, and Linux. + * + * @param {string} fullPath - The path to the file/folder to open. + * @returns {Promise} - Resolves if the file/folder is opened successfully, rejects otherwise. + */ +function openInDefaultApp(fullPath) { + return new Promise((resolve, reject) => { + const platform = os.platform(); + let command; + + if (platform === 'win32') { + // Windows: Use 'start' command + command = `start "" "${fullPath}"`; + } else if (platform === 'darwin') { + // macOS: Use 'open' command + command = `open "${fullPath}"`; + } else { + // Linux: Use 'xdg-open' command + command = `xdg-open "${fullPath}"`; + } + + // Execute the command + exec(command, (error) => { + if (error) { + reject(new Error(`Failed to open file: ${error.message}`)); + } else { + resolve(); + } + }); + }); +} + + async function ESLintFile({text, fullFilePath, projectFullPath}) { return lintFile(text, fullFilePath, projectFullPath); @@ -243,5 +277,6 @@ exports.openUrlInBrowser = openUrlInBrowser; exports.getEnvironmentVariable = getEnvironmentVariable; exports.ESLintFile = ESLintFile; exports.openNativeTerminal = openNativeTerminal; +exports.openInDefaultApp = openInDefaultApp; exports._loadNodeExtensionModule = _loadNodeExtensionModule; exports._npmInstallInFolder = _npmInstallInFolder; diff --git a/src/command/Commands.js b/src/command/Commands.js index e95e0e5b6d..dbb945c0a0 100644 --- a/src/command/Commands.js +++ b/src/command/Commands.js @@ -346,6 +346,9 @@ define(function (require, exports, module) { /** Shows current file in open powershell in Windows os */ exports.NAVIGATE_OPEN_IN_POWERSHELL = "navigate.openInPowerShell"; + /** Open current file in the default associated app in the os */ + exports.NAVIGATE_OPEN_IN_DEFAULT_APP = "navigate.openInDefaultApp"; + /** Opens quick open dialog */ exports.NAVIGATE_QUICK_OPEN = "navigate.quickOpen"; // QuickOpen.js doFileSearch() diff --git a/src/command/DefaultMenus.js b/src/command/DefaultMenus.js index d01b0656ac..87208b4568 100644 --- a/src/command/DefaultMenus.js +++ b/src/command/DefaultMenus.js @@ -58,7 +58,9 @@ define(function (require, exports, module) { return err; } _setContextMenuItemsVisible(isPresent, [Commands.FILE_RENAME, - Commands.NAVIGATE_SHOW_IN_FILE_TREE, Commands.NAVIGATE_SHOW_IN_OS, Commands.NAVIGATE_OPEN_IN_TERMINAL]); + Commands.NAVIGATE_SHOW_IN_FILE_TREE, Commands.NAVIGATE_SHOW_IN_OS, + Commands.NAVIGATE_OPEN_IN_TERMINAL, Commands.NAVIGATE_OPEN_IN_POWERSHELL, + Commands.NAVIGATE_OPEN_IN_DEFAULT_APP]); }); } } @@ -303,6 +305,7 @@ define(function (require, exports, module) { if (brackets.platform === "win") { subMenu.addMenuItem(Commands.NAVIGATE_OPEN_IN_POWERSHELL); } + subMenu.addMenuItem(Commands.NAVIGATE_OPEN_IN_DEFAULT_APP); } workingset_cmenu.addMenuDivider(); workingset_cmenu.addMenuItem(Commands.FILE_COPY); @@ -342,6 +345,7 @@ define(function (require, exports, module) { if (brackets.platform === "win") { subMenu.addMenuItem(Commands.NAVIGATE_OPEN_IN_POWERSHELL); } + subMenu.addMenuItem(Commands.NAVIGATE_OPEN_IN_DEFAULT_APP); } project_cmenu.addMenuDivider(); project_cmenu.addMenuItem(Commands.FILE_CUT); diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index 9fdd67420a..afd026f374 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -1974,6 +1974,13 @@ define(function (require, exports, module) { } } + function openDefaultApp() { + const entry = ProjectManager.getSelectedItem(); + if (entry && entry.fullPath) { + NodeUtils.openInDefaultApp(entry.fullPath, true); + } + } + function raceAgainstTime(promise, timeout = 2000) { const timeoutPromise = new Promise((_resolve, reject) => { setTimeout(() => { @@ -2268,7 +2275,7 @@ define(function (require, exports, module) { // Set some command strings let quitString = Strings.CMD_QUIT, showInOS = Strings.CMD_SHOW_IN_FILE_MANAGER, - defaultTerminal = Strings.CMD_OPEN_IN_TERMINAL; + defaultTerminal = Strings.CMD_OPEN_IN_TERMINAL_DO_NOT_TRANSLATE; if (brackets.platform === "win") { quitString = Strings.CMD_EXIT; showInOS = Strings.CMD_SHOW_IN_EXPLORER; @@ -2325,6 +2332,7 @@ define(function (require, exports, module) { if (brackets.platform === "win") { CommandManager.register(Strings.CMD_OPEN_IN_POWER_SHELL, Commands.NAVIGATE_OPEN_IN_POWERSHELL, openPowerShell); } + CommandManager.register(Strings.CMD_OPEN_IN_DEFAULT_APP, Commands.NAVIGATE_OPEN_IN_DEFAULT_APP, openDefaultApp); CommandManager.register(Strings.CMD_NEW_BRACKETS_WINDOW, Commands.FILE_NEW_WINDOW, handleFileNewWindow); CommandManager.register(quitString, Commands.FILE_QUIT, handleFileCloseWindow); CommandManager.register(Strings.CMD_SHOW_IN_TREE, Commands.NAVIGATE_SHOW_IN_FILE_TREE, handleShowInTree); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index af58596188..737e1f0d4d 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -598,9 +598,10 @@ define({ "CMD_SHOW_IN_EXPLORER": "Windows File Explorer", "CMD_SHOW_IN_FINDER": "macOS Finder", "CMD_SHOW_IN_FILE_MANAGER": "File Manager", - "CMD_OPEN_IN_TERMINAL": "Terminal", + "CMD_OPEN_IN_TERMINAL_DO_NOT_TRANSLATE": "Terminal", "CMD_OPEN_IN_CMD": "Command Prompt", "CMD_OPEN_IN_POWER_SHELL": "Power Shell", + "CMD_OPEN_IN_DEFAULT_APP": "System Default App", "CMD_SWITCH_PANE_FOCUS": "Switch Pane Focus", // Debug menu commands diff --git a/src/utils/NodeUtils.js b/src/utils/NodeUtils.js index fa1d79b958..5fcb4a90c2 100644 --- a/src/utils/NodeUtils.js +++ b/src/utils/NodeUtils.js @@ -176,6 +176,19 @@ define(function (require, exports, module) { }); } + /** + * Opens a file in the default application for its type on Windows, macOS, and Linux. + * + * @param {string} fullPath - The path to the file/folder to open. + * @returns {Promise} - Resolves if the file/folder is opened successfully, rejects otherwise. + */ + async function openInDefaultApp(fullPath) { + if(!Phoenix.isNativeApp) { + throw new Error("openInDefaultApp not available in browser"); + } + return utilsConnector.execPeer("openInDefaultApp", window.fs.getTauriPlatformPath(fullPath)); + } + if(NodeConnector.isNodeAvailable()) { // todo we need to update the strings if a user extension adds its translations. Since we dont support // node extensions for now, should consider when we support node extensions. @@ -213,6 +226,7 @@ define(function (require, exports, module) { exports.ESLintFile = ESLintFile; exports.getEnvironmentVariable = getEnvironmentVariable; exports.openNativeTerminal = openNativeTerminal; + exports.openInDefaultApp = openInDefaultApp; /** * checks if Node connector is ready From 0d26ede1a8dfb1bc28e06a0cb1d6a43d2d180cb7 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 25 Dec 2024 10:06:21 +0530 Subject: [PATCH 2/3] fix: console errors on project context menu open --- src/command/DefaultMenus.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/command/DefaultMenus.js b/src/command/DefaultMenus.js index 87208b4568..148cbfd30f 100644 --- a/src/command/DefaultMenus.js +++ b/src/command/DefaultMenus.js @@ -42,7 +42,12 @@ define(function (require, exports, module) { */ function _setContextMenuItemsVisible(enabled, items) { items.forEach(function (item) { - CommandManager.get(item).setEnabled(enabled); + const command = CommandManager.get(item); + if(command) { + // some commands may be only selectively present in browser or in some oses. + // Eg. NAVIGATE_OPEN_IN_POWERSHELL is only present in Windows desktop apps + command.setEnabled(enabled); + } }); } From b04e9284917c24cf6f6a2b3127c5c8aeaa2a8955 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 25 Dec 2024 10:21:40 +0530 Subject: [PATCH 3/3] fix: typo PowerShell should be one word --- src/nls/root/strings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 737e1f0d4d..4619a5fa8f 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -600,7 +600,7 @@ define({ "CMD_SHOW_IN_FILE_MANAGER": "File Manager", "CMD_OPEN_IN_TERMINAL_DO_NOT_TRANSLATE": "Terminal", "CMD_OPEN_IN_CMD": "Command Prompt", - "CMD_OPEN_IN_POWER_SHELL": "Power Shell", + "CMD_OPEN_IN_POWER_SHELL": "PowerShell", "CMD_OPEN_IN_DEFAULT_APP": "System Default App", "CMD_SWITCH_PANE_FOCUS": "Switch Pane Focus",