Swiftpack.co - mickael-menu/ShadowVim as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by mickael-menu.
mickael-menu/ShadowVim 0.1.1
Neovim 𝘪𝘯𝘴𝘪𝘥𝘦 Xcode, for real.
⭐️ 171
🕓 4 weeks ago
.package(url: "https://github.com/mickael-menu/ShadowVim.git", from: "0.1.1")


Neovim inside Xcode, for real.


ShadowVim provides a Vim mode within Xcode powered by a background Neovim instance.

:warning: Still experimental. Keep a backup of your files before editing them with ShadowVim.


  • This is not a Vim emulation, but real Neovim with macros, ., Ex commands, etc.
  • Vim plugins (without UI) work out of the box. Hello vim-surround, argtextobj.vim and whatnot.
  • Add key mappings to trigger native Xcode features from Neovim (e.g. "Jump to Definition" on gd).

See the changelog for the list of upcoming features waiting to be released.

How does it work?

ShadowVim uses macOS's Accessibility API to keep Xcode and Neovim synchronized. It works by:

  1. intercepting key and focus events in Xcode
  2. forwarding them to a background Neovim instance
  3. applying the changes back to Xcode's source editors


Check out the latest release for pre-built binaries for macOS.

Minimum Requirements

ShadowVim macOS Xcode Neovim
latest 13.0 14 0.8


Neovim configuration

:point_up: The default Neovim indent files for Swift are not great. For a better alternative, install keith/swift.vim with your Neovim package manager.

Since many Vim plugins can cause issues with ShadowVim, it is recommended to start from an empty init.vim.

To determine if Neovim is running in ShadowVim, add to your init.vim:

if exists('g:shadowvim')
    " ShadowVim-specific statements

Or to your init.lua:

if vim.g.shadowvim then
    -- ShadowVim-specific statements

To conditionally activate plugins, vim-plug has a few solutions.

Adding key bindings

Only /C--based keyboard shortcuts can be customized in Neovim. -based hotkeys are handled directly by Xcode.

The SVPress user command triggers a keyboard shortcut or mouse click in Xcode. This is convenient to bind Neovim commands to Xcode features, such as:

" Jump to Definition (⌃⌘J).
nmap gd <Cmd>SVPress <LT>C-D-j><CR>

" Show the Quick Help pop-up for the symbol at the caret location (<kbd>⌥ + Left Click</kbd>).
nmap K <Cmd>SVPress <LT>M-LeftMouse><CR>

:warning: The first < needs to be escaped as <LT> when calling SVPress from a key binding.

Modifier macOS Nvim
control C-
option M- or A-
shift S-
command D-

Take a look at the Tips and tricks section for a collection of useful bindings.


Menu bar icon

ShadowVim adds a new menu bar icon (🅽) with a couple of useful features which can also be triggered with global hotkeys:

  • Keys Passthrough (⌃⌥⌘.) lets Xcode handle key events until you press . You must use this when renaming a symbol with Xcode's refactor tools, otherwise the buffer will get messed up.
  • Reset ShadowVim (⌃⌥⌘⎋) kills Neovim and resets the synchronization. This might be useful if you get stuck.

Neovim user commands

The following commands are available in your bindings when Neovim is run by ShadowVim.

  • SVPress triggers a keyboard shortcut or mouse click in Xcode. The syntax is the same as Neovim's key bindings, e.g. SVPress <D-s> to save the current file. Mouse clicks are performed at the current caret location.
  • SVEnableKeysPassthrough switches on the Keys Passthrough mode, which lets Xcode handle key events until you press .
  • SVReset kills Neovim and resets the synchronization. This might be useful if you get stuck.
  • SVSynchronizeUI requests Xcode to reset the current file to the state of the Neovim buffer. You should not need to call this manually.
  • SVSynchronizeNvim requests Neovim to reset the current buffer to the state of the Xcode file. You should not need to call this manually.

Tips and tricks

Don't use :w

Neovim is in read-only mode, so :w won't do anything. Use the usual ⌘S to save your files.

Custom passthrough for hot keys

All keyboard shortcuts that are not using the modifier are sent to Neovim. This means that if you have a global hot key (e.g. ⌥` to open iTerm), it won't work when Xcode is focused.

As a workaround, you can add a custom mapping to your init.vim to retrigger your hot key globally.

map <A-`> <Cmd>SVPress <LT>A-`><CR>

Navigation with C-o and C-i

Cross-buffers navigation is not yet supported with ShadowVim. Therefore, it is recommended to override the C-o and C-i mappings to use Xcode's navigation instead.

nmap <C-o> <Cmd>SVPress <LT>C-D-Left><CR>
nmap <C-i> <Cmd>SVPress <LT>C-D-Right><CR>

Unfortunately, this won't work in read-only source editors. As a workaround, you can rebind Go back to ⌃O and Go forward to ⌃I in Xcode's Key Bindings preferences, then in Neovim:

nmap <C-o> <Cmd>SVPress <LT>C-o><CR>
nmap <C-i> <Cmd>SVPress <LT>C-i><CR>

As SVPress is not recursive, this will perform the native Xcode navigation.

Triggering Xcode's completion

Xcode's completion generally works with ShadowVim. But there are some cases where the pop-up completion does not appear automatically.

As the default Xcode shortcut to trigger the completion () is already used in Neovim to go back to the normal mode, you might want to set a different one in Xcode's Key Bindings preferences. ⌘P is a good candidate, who needs to print their code anyway?

Completion placeholders

You cannot jump between placeholders in a completion snippet using tab, as it is handled by Neovim. As a workaround, you can use these custom Neovim key bindings to select or modify the next placeholder:

nmap gp /<LT>#.\{-}#><CR>gn
nmap cap /<LT>#.\{-}#><CR>cgn

Mouse clicks

Here are some useful bindings simulating mouse clicks.

" Show the Quick Help pop-up for the symbol at the caret location (<kbd>⌥ + Left Click</kbd>).
nmap K <Cmd>SVPress <LT>M-LeftMouse><CR>

" Perform a right click at the caret location.
nmap gr <Cmd>SVPress <LT>RightMouse><CR>

Window management

Use the following bindings to manage Xcode's source editor with the usual C-w-based keyboard shortcuts.

" Split vertically.
map <C-w>v <Cmd>SVPress <LT>C-D-t><CR>
" Split horizontally.
map <C-w>s <Cmd>SVPress <LT>C-M-D-t><CR>

" Close the focused editor.
" Note: Xcode 14 doesn't focus the previous one... As a workaround, ⌃C is triggered to focus the first one.
map <C-w>c <Cmd>SVPress <LT>C-S-D-w><CR><Cmd>SVPress <LT>C-`><CR>
" Close all other source editors.
map <C-w>o <Cmd>SVPress <LT>C-S-M-D-w><CR>

" Focus the editor on the left.
map <C-w>h <Cmd>SVPress <LT>D-j><CR><Cmd>SVPress h<CR><Cmd>SVPress <LT>CR><CR>
" Focus the editor below.
map <C-w>j <Cmd>SVPress <LT>D-j><CR><Cmd>SVPress j<CR><Cmd>SVPress <LT>CR><CR>
" Focus the editor above.
map <C-w>k <Cmd>SVPress <LT>D-j><CR><Cmd>SVPress k<CR><Cmd>SVPress <LT>CR><CR>
" Focus the editor on the right.
map <C-w>l <Cmd>SVPress <LT>D-j><CR><Cmd>SVPress l<CR><Cmd>SVPress <LT>CR><CR>
" Rotate the source editors.
map <C-w>w <Cmd>SVPress <LT>C-`><CR>


Xcode's folding capabilities are limited, but you get the basics with these bindings:

nmap zc <Cmd>SVPress <LT>M-D-Left><CR>
nmap zo <Cmd>SVPress <LT>M-D-Right><CR>
nmap zM <Cmd>SVPress <LT>M-S-D-Left><CR>
nmap zR <Cmd>SVPress <LT>M-S-D-Right><CR>

Opening third-party applications

You can get pretty creative with key bindings. Here's one opening Sourcetree with <leader>st for the current Git repository, using ! to execute a shell command and % to get the path of the edited file.

nmap <leader>st <Cmd>!stree %<CR>


Thanks to kindaVim and SketchyVim for showing me this was possible.

  • Neovim, duh.
  • Sparkle provides the auto-updater.
  • MessagePack.swift is used to communicate with Neovim using MessagePack-RPC.
  • Sauce helps supporting virtual keyboard layouts.
  • NSLogger for keeping me sane while debugging.
  • XcodeGen generates the project, because .xcodeproj is meh.


Stars: 171
Last commit: 2 days ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Release Notes

4 weeks ago


  • Xcode's settings are automatically updated to prevent conflicts when running ShadowVim.
    • The user is prompted with a bunch of terminal commands reverting the changes.


  • Fix Quit and Reset buttons in the error dialogs.
  • Fix synchronizing buffers with extra newlines at the end.
  • Fix activating ShadowVim when restarting Xcode.
  • Improve handling of some AX errors.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics