( oldPath: string, newPath: string, editor: EditorType, onEditorClose: () => void, )
| 162 | * GUI-based editors require args such as "--wait" to block parent process. |
| 163 | */ |
| 164 | export async function openDiff( |
| 165 | oldPath: string, |
| 166 | newPath: string, |
| 167 | editor: EditorType, |
| 168 | onEditorClose: () => void, |
| 169 | ): Promise<void> { |
| 170 | const diffCommand = getDiffCommand(oldPath, newPath, editor); |
| 171 | if (!diffCommand) { |
| 172 | console.error('No diff tool available. Install a supported editor.'); |
| 173 | return; |
| 174 | } |
| 175 | |
| 176 | try { |
| 177 | switch (editor) { |
| 178 | case 'vscode': |
| 179 | case 'vscodium': |
| 180 | case 'windsurf': |
| 181 | case 'cursor': |
| 182 | case 'zed': |
| 183 | // Use spawn for GUI-based editors to avoid blocking the entire process |
| 184 | return new Promise((resolve, reject) => { |
| 185 | const childProcess = spawn(diffCommand.command, diffCommand.args, { |
| 186 | stdio: 'inherit', |
| 187 | shell: true, |
| 188 | }); |
| 189 | |
| 190 | childProcess.on('close', (code) => { |
| 191 | if (code === 0) { |
| 192 | resolve(); |
| 193 | } else { |
| 194 | reject(new Error(`${editor} exited with code ${code}`)); |
| 195 | } |
| 196 | }); |
| 197 | |
| 198 | childProcess.on('error', (error) => { |
| 199 | reject(error); |
| 200 | }); |
| 201 | }); |
| 202 | |
| 203 | case 'vim': |
| 204 | case 'emacs': |
| 205 | case 'neovim': { |
| 206 | // Use execSync for terminal-based editors |
| 207 | const command = |
| 208 | process.platform === 'win32' |
| 209 | ? `${diffCommand.command} ${diffCommand.args.join(' ')}` |
| 210 | : `${diffCommand.command} ${diffCommand.args.map((arg) => `"${arg}"`).join(' ')}`; |
| 211 | try { |
| 212 | execSync(command, { |
| 213 | stdio: 'inherit', |
| 214 | encoding: 'utf8', |
| 215 | }); |
| 216 | } catch (e) { |
| 217 | console.error('Error in onEditorClose callback:', e); |
| 218 | } finally { |
| 219 | onEditorClose(); |
| 220 | } |
| 221 | break; |
no test coverage detected