* Open a path in the user's configured code editor. * * @param workspaceId - The workspace (used to determine runtime + validate constraints) * @param targetPath - The path to open (workspace directory or specific file) * @param editorConfig - Editor configuration from user settings
(
workspaceId: string,
targetPath: string,
editorConfig: EditorConfig
)
| 75 | * @param editorConfig - Editor configuration from user settings |
| 76 | */ |
| 77 | async openInEditor( |
| 78 | workspaceId: string, |
| 79 | targetPath: string, |
| 80 | editorConfig: EditorConfig |
| 81 | ): Promise<{ success: true; data: void } | { success: false; error: string }> { |
| 82 | try { |
| 83 | if (editorConfig.editor !== "custom") { |
| 84 | return { |
| 85 | success: false, |
| 86 | error: |
| 87 | "Built-in editors are opened via deep links. Select Custom editor to use a command.", |
| 88 | }; |
| 89 | } |
| 90 | |
| 91 | const customCommand = editorConfig.customCommand?.trim(); |
| 92 | if (!customCommand) { |
| 93 | return { success: false, error: "No editor command configured" }; |
| 94 | } |
| 95 | |
| 96 | const allMetadata = await this.config.getAllWorkspaceMetadata(); |
| 97 | const workspace = allMetadata.find((w) => w.id === workspaceId); |
| 98 | |
| 99 | if (!workspace) { |
| 100 | return { success: false, error: `Workspace not found: ${workspaceId}` }; |
| 101 | } |
| 102 | |
| 103 | // Remote runtimes: custom commands run on the local machine and can't access remote paths. |
| 104 | if (isSSHRuntime(workspace.runtimeConfig)) { |
| 105 | return { |
| 106 | success: false, |
| 107 | error: "Custom editors do not support SSH connections for SSH workspaces", |
| 108 | }; |
| 109 | } |
| 110 | |
| 111 | if (isDevcontainerRuntime(workspace.runtimeConfig)) { |
| 112 | return { success: false, error: "Custom editors do not support Dev Containers" }; |
| 113 | } |
| 114 | if (isDockerRuntime(workspace.runtimeConfig)) { |
| 115 | return { success: false, error: "Custom editors do not support Docker containers" }; |
| 116 | } |
| 117 | |
| 118 | const executable = getExecutableFromShellCommand(customCommand); |
| 119 | if (!executable) { |
| 120 | return { success: false, error: `Invalid custom editor command: ${customCommand}` }; |
| 121 | } |
| 122 | |
| 123 | if (!(await this.isCommandAvailable(executable))) { |
| 124 | return { success: false, error: `Editor command not found: ${executable}` }; |
| 125 | } |
| 126 | |
| 127 | // Local - expand tilde (shellQuote prevents shell expansion) |
| 128 | const resolvedPath = targetPath.startsWith("~/") |
| 129 | ? targetPath.replace("~", process.env.HOME ?? "~") |
| 130 | : targetPath; |
| 131 | |
| 132 | const shellCmd = `${customCommand} ${shellQuote(resolvedPath)}`; |
| 133 | log.info(`Opening local path in custom editor: ${shellCmd}`); |
| 134 | const child = spawn(shellCmd, [], { |
no test coverage detected