Skip to content

Commit 8321aba

Browse files
yuki-yanoyuvrajangadsingh
authored andcommitted
feat: add Neovim editor support (google-gemini#1448)
1 parent 55d929f commit 8321aba

File tree

3 files changed

+68
-30
lines changed

3 files changed

+68
-30
lines changed

packages/cli/src/ui/editors/editorSettingsManager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const EDITOR_DISPLAY_NAMES: Record<EditorType, string> = {
2323
windsurf: 'Windsurf',
2424
cursor: 'Cursor',
2525
vim: 'Vim',
26+
neovim: 'Neovim',
2627
};
2728

2829
class EditorSettingsManager {
@@ -36,6 +37,7 @@ class EditorSettingsManager {
3637
'windsurf',
3738
'cursor',
3839
'vim',
40+
'neovim',
3941
];
4042
this.availableEditors = [
4143
{

packages/core/src/utils/editor.test.ts

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ describe('editor utils', () => {
6060
{ editor: 'windsurf', command: 'windsurf', win32Command: 'windsurf' },
6161
{ editor: 'cursor', command: 'cursor', win32Command: 'cursor' },
6262
{ editor: 'vim', command: 'vim', win32Command: 'vim' },
63+
{ editor: 'neovim', command: 'nvim', win32Command: 'nvim' },
6364
{ editor: 'zed', command: 'zed', win32Command: 'zed' },
6465
];
6566

@@ -139,31 +140,41 @@ describe('editor utils', () => {
139140
});
140141
}
141142

142-
it('should return the correct command for vim', () => {
143-
const command = getDiffCommand('old.txt', 'new.txt', 'vim');
144-
expect(command).toEqual({
145-
command: 'vim',
146-
args: [
147-
'-d',
148-
'-i',
149-
'NONE',
150-
'-c',
151-
'wincmd h | set readonly | wincmd l',
152-
'-c',
153-
'highlight DiffAdd cterm=bold ctermbg=22 guibg=#005f00 | highlight DiffChange cterm=bold ctermbg=24 guibg=#005f87 | highlight DiffText ctermbg=21 guibg=#0000af | highlight DiffDelete ctermbg=52 guibg=#5f0000',
154-
'-c',
155-
'set showtabline=2 | set tabline=[Instructions]\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)',
156-
'-c',
157-
'wincmd h | setlocal statusline=OLD\\ FILE',
158-
'-c',
159-
'wincmd l | setlocal statusline=%#StatusBold#NEW\\ FILE\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)',
160-
'-c',
161-
'autocmd WinClosed * wqa',
162-
'old.txt',
163-
'new.txt',
164-
],
143+
const terminalEditors: Array<{
144+
editor: EditorType;
145+
command: string;
146+
}> = [
147+
{ editor: 'vim', command: 'vim' },
148+
{ editor: 'neovim', command: 'nvim' },
149+
];
150+
151+
for (const { editor, command } of terminalEditors) {
152+
it(`should return the correct command for ${editor}`, () => {
153+
const diffCommand = getDiffCommand('old.txt', 'new.txt', editor);
154+
expect(diffCommand).toEqual({
155+
command,
156+
args: [
157+
'-d',
158+
'-i',
159+
'NONE',
160+
'-c',
161+
'wincmd h | set readonly | wincmd l',
162+
'-c',
163+
'highlight DiffAdd cterm=bold ctermbg=22 guibg=#005f00 | highlight DiffChange cterm=bold ctermbg=24 guibg=#005f87 | highlight DiffText ctermbg=21 guibg=#0000af | highlight DiffDelete ctermbg=52 guibg=#5f0000',
164+
'-c',
165+
'set showtabline=2 | set tabline=[Instructions]\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)',
166+
'-c',
167+
'wincmd h | setlocal statusline=OLD\\ FILE',
168+
'-c',
169+
'wincmd l | setlocal statusline=%#StatusBold#NEW\\ FILE\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)',
170+
'-c',
171+
'autocmd WinClosed * wqa',
172+
'old.txt',
173+
'new.txt',
174+
],
175+
});
165176
});
166-
});
177+
}
167178

168179
it('should return null for an unsupported editor', () => {
169180
// @ts-expect-error Testing unsupported editor
@@ -240,7 +251,7 @@ describe('editor utils', () => {
240251
});
241252
}
242253

243-
const execSyncEditors: EditorType[] = ['vim'];
254+
const execSyncEditors: EditorType[] = ['vim', 'neovim'];
244255
for (const editor of execSyncEditors) {
245256
it(`should call execSync for ${editor} on non-windows`, async () => {
246257
Object.defineProperty(process, 'platform', { value: 'linux' });
@@ -293,6 +304,15 @@ describe('editor utils', () => {
293304
expect(allowEditorTypeInSandbox('vim')).toBe(true);
294305
});
295306

307+
it('should allow neovim in sandbox mode', () => {
308+
process.env.SANDBOX = 'sandbox';
309+
expect(allowEditorTypeInSandbox('neovim')).toBe(true);
310+
});
311+
312+
it('should allow neovim when not in sandbox mode', () => {
313+
expect(allowEditorTypeInSandbox('neovim')).toBe(true);
314+
});
315+
296316
const guiEditors: EditorType[] = [
297317
'vscode',
298318
'vscodium',
@@ -348,5 +368,11 @@ describe('editor utils', () => {
348368
process.env.SANDBOX = 'sandbox';
349369
expect(isEditorAvailable('vim')).toBe(true);
350370
});
371+
372+
it('should return true for neovim when installed and in sandbox mode', () => {
373+
(execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/nvim'));
374+
process.env.SANDBOX = 'sandbox';
375+
expect(isEditorAvailable('neovim')).toBe(true);
376+
});
351377
});
352378
});

packages/core/src/utils/editor.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,19 @@ export type EditorType =
1212
| 'windsurf'
1313
| 'cursor'
1414
| 'vim'
15+
| 'neovim'
1516
| 'zed';
1617

1718
function isValidEditorType(editor: string): editor is EditorType {
18-
return ['vscode', 'vscodium', 'windsurf', 'cursor', 'vim', 'zed'].includes(
19-
editor,
20-
);
19+
return [
20+
'vscode',
21+
'vscodium',
22+
'windsurf',
23+
'cursor',
24+
'vim',
25+
'neovim',
26+
'zed',
27+
].includes(editor);
2128
}
2229

2330
interface DiffCommand {
@@ -43,6 +50,7 @@ const editorCommands: Record<EditorType, { win32: string; default: string }> = {
4350
windsurf: { win32: 'windsurf', default: 'windsurf' },
4451
cursor: { win32: 'cursor', default: 'cursor' },
4552
vim: { win32: 'vim', default: 'vim' },
53+
neovim: { win32: 'nvim', default: 'nvim' },
4654
zed: { win32: 'zed', default: 'zed' },
4755
};
4856

@@ -97,8 +105,9 @@ export function getDiffCommand(
97105
case 'zed':
98106
return { command, args: ['--wait', '--diff', oldPath, newPath] };
99107
case 'vim':
108+
case 'neovim':
100109
return {
101-
command: 'vim',
110+
command,
102111
args: [
103112
'-d',
104113
// skip viminfo file to avoid E138 errors
@@ -172,7 +181,8 @@ export async function openDiff(
172181
});
173182
});
174183

175-
case 'vim': {
184+
case 'vim':
185+
case 'neovim': {
176186
// Use execSync for terminal-based editors
177187
const command =
178188
process.platform === 'win32'

0 commit comments

Comments
 (0)