(filePath: string)
| 29 | |
| 30 | // sync IO: called from sync context (React components, sync command handlers) |
| 31 | export function editFileInEditor(filePath: string): EditorResult { |
| 32 | const fs = getFsImplementation() |
| 33 | const inkInstance = instances.get(process.stdout) |
| 34 | if (!inkInstance) { |
| 35 | throw new Error('Ink instance not found - cannot pause rendering') |
| 36 | } |
| 37 | |
| 38 | const editor = getExternalEditor() |
| 39 | if (!editor) { |
| 40 | return { content: null } |
| 41 | } |
| 42 | |
| 43 | try { |
| 44 | fs.statSync(filePath) |
| 45 | } catch { |
| 46 | return { content: null } |
| 47 | } |
| 48 | |
| 49 | const useAlternateScreen = !isGuiEditor(editor) |
| 50 | |
| 51 | if (useAlternateScreen) { |
| 52 | // Terminal editors (vi, nano, etc.) take over the terminal. Delegate to |
| 53 | // Ink's alt-screen-aware handoff so fullscreen mode (where <AlternateScreen> |
| 54 | // already entered alt screen) doesn't get knocked back to the main buffer |
| 55 | // by a hardcoded ?1049l. enterAlternateScreen() internally calls pause() |
| 56 | // and suspendStdin(); exitAlternateScreen() undoes both and resets frame |
| 57 | // state so the next render writes from scratch. |
| 58 | inkInstance.enterAlternateScreen() |
| 59 | } else { |
| 60 | // GUI editors (code, subl, etc.) open in a separate window — just pause |
| 61 | // Ink and release stdin while they're open. |
| 62 | inkInstance.pause() |
| 63 | inkInstance.suspendStdin() |
| 64 | } |
| 65 | |
| 66 | try { |
| 67 | // Use override command if available, otherwise use the editor as-is |
| 68 | const editorCommand = EDITOR_OVERRIDES[editor] ?? editor |
| 69 | execSync_DEPRECATED(`${editorCommand} "${filePath}"`, { |
| 70 | stdio: 'inherit', |
| 71 | }) |
| 72 | |
| 73 | // Read the edited content |
| 74 | const editedContent = fs.readFileSync(filePath, { encoding: 'utf-8' }) |
| 75 | return { content: editedContent } |
| 76 | } catch (err) { |
| 77 | if ( |
| 78 | typeof err === 'object' && |
| 79 | err !== null && |
| 80 | 'status' in err && |
| 81 | typeof (err as { status: unknown }).status === 'number' |
| 82 | ) { |
| 83 | const status = (err as { status: number }).status |
| 84 | if (status !== 0) { |
| 85 | const editorName = toIDEDisplayName(editor) |
| 86 | return { |
| 87 | content: null, |
| 88 | error: `${editorName} exited with code ${status}`, |
no test coverage detected