Vim is a [[Vim License]] licensed modal text editor written in [[C|C]].
- [Website](https://www.vim.org/)
- [Github](https://github.com/vim/vim)
- [Wikipedia](<https://en.wikipedia.org/wiki/Vim_(text_editor)>)
> Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient.
See also: [[NeoVim]], [[Vim Preconfigs]]
# Notability
One of the most ubiquitous editors in Unix-land for over 3 decades, leaving a huge mark on the way developers write code.
I used Vim heavily starting in 2006 but mostly moved to [[NeoVim]] soon after it was released.
# Philosophy
## Slop
The official Vim repo is likely being actively swamped with LLM code slop which has resulted in numerous bugs and regressions. The maintainers don't care and hide mentions of this.[^7]
# OS Support
> Vim runs under MS-Windows (7, 8, 10, 11), macOS, Haiku, VMS and almost all flavours of UNIX. Porting to other systems should not be very difficult.
>
> Older versions of Vim run on MS-DOS, MS-Windows 95/98/Me/NT/2000/XP/Vista, Amiga DOS, Atari MiNT, BeOS, RISC OS and OS/2.
- [[Linux]]
- [[BSD]] (all?)
- [[Haiku]]
- [[MacOS]]
- [[Windows]]
- [[DOS]]
- [[VMS (OS)]]
# Features
- Modal text editing
- Scriptable with [[Vimscript]] and [[Vim9script]]
# History
Vim was first released in 1991 and was for many years one of the most popular editors used for code and distributed with nearly every Linux distro in place of the traditional Unix editor `vi`.
Due to growing dissatisfaction with the tech debt of the Vim codebase and the lack of real updates from the primary developer, a fork called [[NeoVim]] was created in 2014.
The release of NeoVim seemed to motivate Vim's developer and many new (NeoVim-incompatible) changes have been released since.
- 2022 - [[Vim9script]]
The original Vim developer Bram Moolenaar died in 2023 on August 3rd. Contributor [Christian Brabandt](https://github.com/chrisbra) took over as lead.
# Tips
## Insert Single Character and Return to Normal Mode
It is possible to *replace* a single character using the `r` normal command (like `rX` to replace the current character with `X`). But there is no built in way to do the same thing with insert.
It feels like this should be a standard operation, but it isn't available.
### Static
To insert a single, *predetermined* character from normal mode (such as underscore `_` when you press `ctrl-_`) you can create a specific new key mapping:
```vimscript
:noremap <C-_> i_<Esc>r
```
But this makes it impossible to use that key for a normal command.
### Dynamic
If you want to grab just any *arbitrary* single character (typed afterwards), it is more of a challenge. Some register and expression abuse is necessary.
```vimscript
:nmap <silent> <C-i> "=nr2char(getchar())<cr>P
```
The downside of this approach is that the cursor disappears after you trigger it, so you might lose your place briefly. It will return after the insert.
## Insert Multicursor
```vim
ctl-v
(select lines)
I
```
`I` insert before selection
`A` insert after selection
## Open URL Under Cursor
`gx` from normal mode.
## Open File Under Cursor
`gf` from normal mode or by selecting the text in visual mode.
### In Split Window
`ctrl-w f` or `ctrl-w ctrl-f` from normal mode[^9] opens up the filename under the cursor in a horizontal split.
#### In a Vertical Split
To open it up in a vertical split you can simulate this by just opening a vertical split first: `ctrl-w vgf`.[^9]
One alternative is to remap[^9] the extra mapping for horizontal splits for smother mnemonics: `nnoremap <C-W><C-F> <C-W>vgf`
### In New Tab
`ctrl-w gf` from normal mode.[^9]
### Autoadd Suffix
If the word under the cursor doesn't list the file extension, the `suffixesadd` setting[^12] can be used to specify what extensions Vim should look for when using `gf`[^13].
This setting might be modified per filetype, like `.c` files might enable `.c` and `.h` like:
```
:set suffixesadd=.c,.h
```
This option is local to the current buffer, so you don't need to worry about it affecting other files or filetypes.
- ? This doesn't work with `<cfile>` without custom handling?
### Create File
Typically you can only edit *existing* files. But the the `<cfile>` variable can be used in command mode to create files:
```
:edit <cfile>
```
This can be mapped to something: `nnoremap ?????` (need to figure out a good prefix)
More advanced versions can make this work in more complex situations[^11] or prompt[^10] the user before creating files.
## Run Selection
If you *yank* the selection first you can run it like this:
```viml
:@"
```
## Pipe Visual Selection to Shell Command
```viml
:'<,'>:w !external_command
```
To replace the selected text with the output of the command, leave the `:w` out:
```viml
:'<,'> !external_command
```
## Close Buffer
To delete a buffer:
```viml
:bd
```
To completely wipe out a buffer, including undo history:[^1]
```viml
:bw
```
## Change Case
In **visual** mode, to change the case[^2] of selected text press `u` for lowercase and `U` for uppercase.
In **normal** and **visual** mode, the tilde `~` command toggles case between upper and lower.
| Toggle<br>Command | Effect |
| ----------------- | -------------------------------- |
| `~` | Toggle case of current character |
| `g~~` | Toggle case of entire line |
| `g~w` | Toggle case of current word |
In **normal** mode, to change the case of text from the current cursor position to the end of a motion press `Gu
for lowercase or `GU
for uppercase where `
is the motion.
Here are some example commands:[^2]
| Lowercase<br>Command | Uppercase<br>Command | Effect |
| -------------------- | -------------------- | -------------------------------------------------------------- |
| `guw` | `gUw` | Everything until final character in current word |
| `guaw` | `gUaw` | Entire current word |
| `gu
| `gU
| Everything until the end of the line |
| `guu` | `gUU` | Entire line |
See also: [[Vim Motions]]
#### Capitalize Words
In **command** mode, you can capitalize[^3] just the first letter of each word:
```viml
:s/\<./\u&/g
```
Prefix with `%` to affect the entire document, or remove the `/g` and prefix with `'<,'>` to affect a selection.
In Vim's regex *replacement* dialect `\u` is uppercase next char and `\l` is lowercase next char, while `\U` is uppercase all following chars and `\L` is lowercase all following chars. These sequences may be prematurely terminated by each other or with the generic `\e` end syntax.
Unfortunately this choice of end syntax means that `\e` is not available for the [[ASCII Table#ESC Escape Code|escape character]], so you must type it literally with `ctrl-v esc`.
Similarly frustrating, `\n` inserts a `NUL` character rather than a newline, to get a newline you need to use `\r`, which in other programs represents a carriage return.[^4]
## Inspect Character Under Cursor
>[!FIXME]
> Some of this info is provided by the plugin `tpopt/vim-characterize`
To get detailed information[^5] about the character the cursor is on press `ga` or use the command `:as[cii]`. It will display the following information in the message area (two examples - top and bottom):
```
<\> 92, Hex 5c, Oct 134, Digr //
↑ ↑ ↑ ↑ ↑
│ │ └ hex └ octal └ digraph
│ └ decimal
├─ character representation
│ ┌ decimal
│ │ ┌ octal
│ │ │ ┌ codepoint
│ │ │ │ ┌ name ┌ digraph
↓ ↓ ↓ ↓ ↓ ↓
<^[> 27, \033, U+001B ESCAPE, <C-K>EC
```
I don't know why the display is so inconsistent between characters, but this isn't the half of it. Sometimes it will also display "M codes" like `<M-^A>` and other fields not explained in the documentation.
To see *just* the simple hex representation of the bytes under the cursor use `g8`. The hex values will display in the status area. Each pair of hex values represents a single byte. For [[ASCII]] characters there will only be a single pair, but for [[Unicode Standard|UTF-8]] characters it may display up to four pairs.
For example, the down arrow (↓) used in the diagram above shows up as `e2 86 93` with `g8` and with `ga` shows:
```
<↓> 8595, U+2193 DOWNWARDS ARROW, <C-K>-v, ↓ ↓ ↓ ↓
```
This is has a couple of new fields, in addition to the control-k digraph, there are a list of HTML-entity-like names.
## Line Editing and Navigation
| Command | |
| ----------- | -------------------------------------------------- |
| `d
or `D` | Delete (cut) from current character to end of line |
| `y
or `Y` | Yank (copy) from current character to end of line |
## Quickly Edit Vim Config
```viml
:edit $MYVIMRC
```
## Evaluate Arbitrary Vim Script
```viml
:source path/to/script.vim
```
Or directly from a command shell:
```sh
vim +source path/to/script.vim
```
Or directly from a command shell without first invoking any init scripts:
```sh
vim -u NONE +source path/to/script.vim
```
## Execute Command on Matching Lines
The `:global` (or `:g`) command can be used to run any `ex` command on a match.[^6]
```viml
:g/pattern/command
```
This is particularly useful to delete all matching/non-matching lines. For example to delete all empty lines:
```
:g/^$/d
```
To run on lines that *don't* match, use `:g!` or `:v`.
## Setting Options In Different Scopes
`:set` is used to set options.
Options can be:
- global - one option used for everything
- local - the option only affects the current window or buffer
- window - the option only affects the current window (AKA pane/split) and new splits copy the options of the one it split from
- buffer - the option affects the current buffer, regardless of window
- global-local - same as global, but may be overridden locally by using `:setlocal`[^14] instead, but these only apply to the given split *and* buffer type
Note: `:set` with global-local options also sets the *local* option (I think that this is only if it has already set locally first).
## Setting the Filetype
Vim has two different ways to set the filetype, the main way is:
```viml
:set filetype=foo
```
But when writing a script you might want to guess about a filetype and not overwrite a known one like this:
```
:setfiletype foo
```
They look similar and this is very confusing, but the second one is weaker than the first and does nothing if `did_filetype()` returns `true`.[^15]
## Paste Over
The easy solution is to select what you want to overwrite in visual mode and then paste.
# Resources
- Official [Vim scripts site](https://www.vim.org/scripts/)
- GitHub [mirror](https://github.com/vim-scripts?tab=repositories) of most Vim scripts (ceased mirroring at the end of 2023)
## Cheat Sheets
- https://vim.rtorr.com/
- https://devhints.io/vim
## Color Schemes
- https://alvinalexander.com/linux/vi-vim-editor-color-scheme-syntax/
- https://vimcolorschemes.com/i/trending/b.dark
- https://github.com/morhetz/gruvbox
- One of the big ones, with lots of support for different plugins
- https://github.com/sainnhe/everforest/blob/master/colors/everforest.vim
- Slightly nicer than a solarized but in the same vein
## Plugins
See: [[Vim Plugins]]
## Related
- [[Taskwarrior]] - https://taskwarrior.org/
- Markdown Ctags - https://github.com/jszakmeister/markdown2ctags
- find tags - https://github.com/matt-snider/vim-tagquery
## Single Character Insert
- https://antifandom.com/vim/wiki/Insert_a_single_character
- https://superuser.com/questions/581572/insert-single-character-in-vim
- https://stackoverflow.com/questions/1557893/making-inserting-a-single-character-in-vim-an-atomic-operation
- https://www.vim.org/scripts/script.php?script_id=2136 (Tim Pop's Repeat.vim)
- https://github.com/tpope/vim-repeat (GitHub mirror)
- https://www.vim.org/scripts/script.php?script_id=2810 (Peter Hodge's InsertChar.vim)
- https://github.com/vim-scripts/InsertChar (GitHub mirror)
- https://github.com/aur-archive/vim-insertchar (AUR PKGBUILD)
### Related
https://stackoverflow.com/questions/36432919/how-can-i-map-underscore-in-vim
- https://stackoverflow.com/questions/27882964/in-vim-remapping-how-do-i-capture-and-reuse-any-character
# References
[^1]: https://stackoverflow.com/questions/1269648/how-do-i-close-a-single-buffer-out-of-many-in-vim
[^2]: https://stackoverflow.com/questions/2946051/changing-case-in-vim
[^3]: https://stackoverflow.com/questions/17440659/capitalize-first-letter-of-each-word-in-a-selection-using-vim
[^4]: https://stackoverflow.com/questions/71323/how-to-replace-a-character-by-a-newline-in-vim
[^5]: https://vimtricks.com/p/inspect-character-under-cursor-in-vim/
[^6]: https://stackoverflow.com/a/706083
https://stackoverflow.com/questions/46781951/efficient-way-to-delete-line-containing-certain-text-in-vim-with-prompt#comment134971070_73580651
[^7]: https://github.com/vim/vim/issues/18800#issuecomment-3568417192
[^8]: https://github.com/neovim/neovim/issues/38186#issuecomment-4018682912
[^9]: https://vi.stackexchange.com/questions/3364/open-filename-under-cursor-like-gf-but-in-a-new-tab-or-split
[^10]: https://stackoverflow.com/a/76345912
[^11]: https://stackoverflow.com/questions/6158294/how-to-create-and-open-for-editing-a-nonexistent-file-whose-path-is-under-the-cu
[^12]: https://vimhelp.org/options.txt.html#%27suffixesadd%27
[^13]: https://neovim.io/doc/user/editing/#gf
[^14]: https://vimhelp.org/options.txt.html#global-local
[^15]: https://vi.stackexchange.com/questions/16341/what-is-the-difference-between-set-ft-and-setfiletype