From 982eee63b61e84b7ae0265857328cf0e3bc19d14 Mon Sep 17 00:00:00 2001 From: Kevin Jiang Date: Tue, 6 Jan 2026 18:31:38 -0800 Subject: [PATCH] Hx support (#16032) --- packages/core/src/utils/editor.test.ts | 26 +++++++++++++++++++++++++- packages/core/src/utils/editor.ts | 9 ++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/core/src/utils/editor.test.ts b/packages/core/src/utils/editor.test.ts index 52520ee71f..82b886f366 100644 --- a/packages/core/src/utils/editor.test.ts +++ b/packages/core/src/utils/editor.test.ts @@ -78,6 +78,7 @@ describe('editor utils', () => { commands: ['agy'], win32Commands: ['agy.cmd'], }, + { editor: 'hx', commands: ['hx'], win32Commands: ['hx'] }, ]; for (const { editor, commands, win32Commands } of testCases) { @@ -318,6 +319,14 @@ describe('editor utils', () => { }); }); + it('should return the correct command for helix', () => { + const command = getDiffCommand('old.txt', 'new.txt', 'hx'); + expect(command).toEqual({ + command: 'hx', + args: ['--vsplit', '--', 'old.txt', 'new.txt'], + }); + }); + it('should return null for an unsupported editor', () => { // @ts-expect-error Testing unsupported editor const command = getDiffCommand('old.txt', 'new.txt', 'foobar'); @@ -385,7 +394,7 @@ describe('editor utils', () => { }); } - const terminalEditors: EditorType[] = ['vim', 'neovim', 'emacs']; + const terminalEditors: EditorType[] = ['vim', 'neovim', 'emacs', 'hx']; for (const editor of terminalEditors) { it(`should call spawnSync for ${editor}`, async () => { @@ -441,6 +450,15 @@ describe('editor utils', () => { expect(allowEditorTypeInSandbox('neovim')).toBe(true); }); + it('should allow hx in sandbox mode', () => { + vi.stubEnv('SANDBOX', 'sandbox'); + expect(allowEditorTypeInSandbox('hx')).toBe(true); + }); + + it('should allow hx when not in sandbox mode', () => { + expect(allowEditorTypeInSandbox('hx')).toBe(true); + }); + const guiEditors: EditorType[] = [ 'vscode', 'vscodium', @@ -503,6 +521,12 @@ describe('editor utils', () => { expect(isEditorAvailable('emacs')).toBe(true); }); + it('should return true for hx when installed and in sandbox mode', () => { + (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/hx')); + vi.stubEnv('SANDBOX', 'sandbox'); + expect(isEditorAvailable('hx')).toBe(true); + }); + it('should return true for neovim when installed and in sandbox mode', () => { (execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/nvim')); vi.stubEnv('SANDBOX', 'sandbox'); diff --git a/packages/core/src/utils/editor.ts b/packages/core/src/utils/editor.ts index a4ca04b292..b71a0b23eb 100644 --- a/packages/core/src/utils/editor.ts +++ b/packages/core/src/utils/editor.ts @@ -16,7 +16,7 @@ const GUI_EDITORS = [ 'zed', 'antigravity', ] as const; -const TERMINAL_EDITORS = ['vim', 'neovim', 'emacs'] as const; +const TERMINAL_EDITORS = ['vim', 'neovim', 'emacs', 'hx'] as const; const EDITORS = [...GUI_EDITORS, ...TERMINAL_EDITORS] as const; const GUI_EDITORS_SET = new Set(GUI_EDITORS); @@ -49,6 +49,7 @@ export const EDITOR_DISPLAY_NAMES: Record = { zed: 'Zed', emacs: 'Emacs', antigravity: 'Antigravity', + hx: 'Helix', }; export function getEditorDisplayName(editor: EditorType): string { @@ -93,6 +94,7 @@ const editorCommands: Record< zed: { win32: ['zed'], default: ['zed', 'zeditor'] }, emacs: { win32: ['emacs.exe'], default: ['emacs'] }, antigravity: { win32: ['agy.cmd'], default: ['agy'] }, + hx: { win32: ['hx'], default: ['hx'] }, }; export function checkHasEditorType(editor: EditorType): boolean { @@ -182,6 +184,11 @@ export function getDiffCommand( command: 'emacs', args: ['--eval', `(ediff "${oldPath}" "${newPath}")`], }; + case 'hx': + return { + command: 'hx', + args: ['--vsplit', '--', oldPath, newPath], + }; default: return null; }