Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mickael-menu/ShadowVim
Neovim ๐ช๐ฏ๐ด๐ช๐ฅ๐ฆ Xcode, for real.
https://github.com/mickael-menu/ShadowVim
Last synced: 19 days ago
JSON representation
Neovim ๐ช๐ฏ๐ด๐ช๐ฅ๐ฆ Xcode, for real.
- Host: GitHub
- URL: https://github.com/mickael-menu/ShadowVim
- Owner: mickael-menu
- License: gpl-3.0
- Created: 2023-01-07T14:43:51.000Z (almost 2 years ago)
- Default Branch: develop
- Last Pushed: 2023-07-09T13:10:17.000Z (over 1 year ago)
- Last Synced: 2024-10-26T20:42:30.658Z (23 days ago)
- Language: Swift
- Homepage:
- Size: 392 KB
- Stars: 341
- Watchers: 14
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
ShadowVim
Neovim inside Xcode, for real.
## Description
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.
### Highlights
* 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`](https://github.com/tpope/vim-surround), [`argtextobj.vim`](https://github.com/vim-scripts/argtextobj.vim) and whatnot.
* Add key mappings to trigger native Xcode features from Neovim (e.g. "Jump to Definition" on `gd`).[See the changelog](CHANGELOG.md) 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## Install
[Check out the latest release](https://github.com/mickael-menu/ShadowVim/releases) for pre-built binaries for macOS.
### Minimum Requirements
| ShadowVim | macOS | Xcode | Neovim |
|------------|-------|-------|--------|
| 0.2+ | 13.0 | 14 | 0.9 |
| 0.1 | 13.0 | 14 | 0.8 |## Setup
### Neovim configuration
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`:
```vim
if exists('g:shadowvim')
" ShadowVim-specific statements
endif
```Or to your `init.lua`:
```lua
if vim.g.shadowvim then
-- ShadowVim-specific statements
end
```To conditionally activate plugins, `vim-plug` has a
[few solutions](https://github.com/junegunn/vim-plug/wiki/tips#conditional-activation).:point_up: The default Neovim indent files for Swift are not great. For a better alternative, install [`keith/swift.vim`](https://github.com/keith/swift.vim) with your Neovim package manager.
#### 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:
```viml
" Jump to Definition (โโJ).
map gd SVPress C-D-j>" Find Selected Symbol in Workspace (โโงโJ).
map gr SVPress C-S-D-f>" Show the Quick Help pop-up for the symbol at the caret location (โฅ + Left Click).
map K SVPress M-LeftMouse>
```:warning: The first `<` needs to be escaped as `` 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](#tips-and-tricks) section for a collection of useful bindings.
## Usage
### Menu bar icon
ShadowVim adds a new menu bar icon (๐ ฝ) with a couple of useful features which can also be triggered with global hotkeys:
* **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 ` to save the current file. Mouse clicks are performed at the current caret location.
* `SVOpenTUI` launches a Terminal window with a Neovim text user interface of the embedded Neovim instance.
* This is useful to solve issues with Neovim such as a blocking prompt.
* `SVReset` kills Neovim and resets the synchronization. This might be useful if you get stuck.
* `SVSetInputUI` lets Xcode handle all key events. Press Esc to cancel.
* `SVSetInputNvim` forwards key events to Neovim, even in Insert mode. Press Esc to cancel.
* `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.
```viml
map SVPress A-`>
```### 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.
```viml
map SVPress C-D-Left>
map SVPress C-D-Right>
```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:
```viml
map SVPress C-o>
map SVPress C-i>
```As `SVPress` is not recursive, this will perform the native Xcode navigation.
### Mouse clicks
Here are some useful bindings simulating mouse clicks.
```viml
" Show the Quick Help pop-up for the symbol at the caret location (โฅ + Left Click).
map K SVPress M-LeftMouse>" Perform a right click at the caret location.
map gR SVPress RightMouse>
```### Window management
Use the following bindings to manage Xcode's source editor with the usual C-w-based keyboard shortcuts.
```viml
" Split vertically.
map v SVPress C-D-t>
" Split horizontally.
map s SVPress C-M-D-t>" 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 SVPress C-S-D-w>SVPress C-`>
" Close all other source editors.
map o SVPress C-S-M-D-w>" Focus the editor on the left.
map h SVPress D-j>SVPress hSVPress CR>
" Focus the editor below.
map j SVPress D-j>SVPress jSVPress CR>
" Focus the editor above.
map k SVPress D-j>SVPress kSVPress CR>
" Focus the editor on the right.
map l SVPress D-j>SVPress lSVPress CR>
" Rotate the source editors.
map w SVPress C-`>
```### Center cursor line with `zz`
To emulate the Vim `zz` command, you will need to set a custom keyboard shortcut for the **Center Selection in Visual Area** command in the Xcode **Key Bindings** preferences. For example, โโฅโงโL.
Then, add the following binding in your Neovim config:
```viml
map zz SVPress C-M-S-D-l>
```### Folds
Xcode's folding capabilities are limited, but you get the basics with these bindings:
```viml
map zc SVPress M-D-Left>
map zo SVPress M-D-Right>
map zM SVPress M-S-D-Left>
map zR SVPress M-S-D-Right>
```### Opening third-party applications
You can get pretty creative with key bindings. Here's one opening [Sourcetree](https://www.sourcetreeapp.com/) with <leader>st for the current Git repository, using `!` to execute a shell command and `%` to get the path of the edited file.
```viml
map st silent !stree "%"
```This keybinding opens a new iTerm tab at the root of the Git repository for the current buffer.
```viml
map sh silent !open -a iTerm `(cd "%:p:h"; git rev-parse --show-toplevel)`
```### Triggering Xcode's completion
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?
## Attributions
Thanks to [kindaVim](https://kindavim.app/) and [SketchyVim](https://github.com/FelixKratz/SketchyVim) for showing me this was possible.
* [Neovim](https://github.com/neovim/neovim), duh.
* [Sparkle](https://sparkle-project.org/) provides the auto-updater.
* [MessagePack.swift](https://github.com/a2/MessagePack.swift) is used to communicate with Neovim using MessagePack-RPC.
* [Sauce](https://github.com/Clipy/Sauce) helps supporting virtual keyboard layouts.
* [NSLogger](https://github.com/fpillet/NSLogger) for keeping me sane while debugging.
* [XcodeGen](https://github.com/yonaskolb/XcodeGen) generates the project, because `.xcodeproj` is meh.