Theory, tips, and tricks after reading Drew Neil's Practical VIM & An unordered list of tips picked up along the way
nnoremap <Left>Ā :echo "Use h"<CR>
nnoremap <Right>Ā :echo "Use l"<CR>
nnoremap <Up>Ā :echo "Use k"<CR>
nnoremap <Down>Ā :echo "Use j"<CR>
set clipboard=unnamed
Added to .vimrc will make Vim yank to the clipboard, to be pasted anywhere.
- If you keep yanking code and messing it up somehow with
d
orx
, you can always use the yank dedicated register"0p
will tell vim to "put from yank register and not from the unnamed one" - You can also cut to a specific register:
"adw
willdelete
aword
to the "a" register. - Last thingā-āyou can interact in the same way with the OS clipboard! usingĀ
"+{command}
will tell vim to do the command to/from the clipboard register.
Can be done with:
-
get into vertical-block mode by using
ctrl-V
. 2. mark the lines you want to modify. 3.shift-I
#
space
esc
the best way to add a prefix to lines. adding a suffix also works if afterctrl-V
you use$
and replaceshift-I
withshift-A
-
using the super awesome Vim-Commentary plugin by tpope.
Vim uses *
to search the current word on normal mode, but when using visual mode you'd expect it would search your highlighted text, but it doesn't, fix it!
" Operating on a complete search match
xnoremap *Ā :<C-u>call <SID>VSetSearch()<CR>/<C-R>=@/<CR><CR>
xnoremap #Ā :<C-u>call <SID>VSetSearch()<CR>?<C-R>=@/<CR><CR>
Some search tips:
1. You can specify search direction in VIM; use `/` to search forward and `?` to search backwards (`n` and `N` will iterate over results backwards too)
2. Highlight search results with setting `set hlsearch`
3. After using the highlight, it can be a annoying if it stays, disable using `:noh` or take it another step and map to something like `Ctrl+l`: `nnoremap <C-l>Ā :noh<CR>`
4. Find number of search matches (without a plugin): `:%s///gn` (telling vim to search and replace without contents makes it use the last search and then tells it to count.
5. Want to position cursor on the *end* of the search result instead of the beginning? use `e`:Ā
`/searchphrase/e`. Forgot and already searched? use `//e`ā-ābecomes useful when you'd like to operate on phrases that can be part of words.
6. Ignore / force search case sensitivity: use `/c` to ignore case, and `/C` to force it. This overrides the setting of `ignorecase`
function s:VSetSearch()
Ā let temp = @s
Ā norm! gv"sy
Ā let @/ = '\V'Ā . substitute(escape(@s, '/\'), '\n', '\\n', 'g')
Ā let @s = temp
endfunction
Now, on visual mode, mark a text and press *
to search forward, and #
to do the same backwards.
:
then <Ctrl-f>
you get a history list you can browse
If you want to vim-search it just use /
and search
set ignorecase
if you want vim to
(inspired by DAS talks about moving in large chunks, I added the scroll feel):
nnoremap <C-j> 10jzz
nnoremap <C-k> 10kzz
Maps moving up or down with Ctrl
pressed, to a bigger motion (10
) while keeping the line in the center of the screen (zz
). This gives a better "scroll" feel.
Mapping shell commands is useful, e.g for running tests, this is something usually set right in the session and not as a permanent setting. Something like this:
nmap ttĀ :! crystal spec <enter>
Maps tt
in normal mode to run crystal spec
and press enter.
If you yank a text and right before pasting it you use d
or c
to remove the text you want replaced you need to go back and yank it again.
One solution is to mark v
the unwanted text and then p
on it.
BUTā-āusing the black hole register is a good option tooā-ā
If you want to remove text whether using x
/s
/c
/d
use "_
before
"
is sending your yanked / deleted text into a register, by providing "_
it goes to /dev/null
, allowing you to keep working with the unnamed register without changing it in the process.
Incrementing numbers in vim: Ctrl-a
Decrementing numbers in vim: Ctrl-x
Very useful in macros where you want to programmatically run through lists and increment numbers.
Using NERDTree is a convenient way to browse through files if you're not familiar with the project (if you are, use https://github.com/Yggdroot/LeaderF)
If you have a macro running on a file, and you'd like to run it on many of them, you can integrate commands jumping to nerdtree and opening the next file:
<macro-commands>^Whj^M^Wl
Where ^W
being ctrl-W to switch to window selector, h
moving left to the file browser, j
moving down to the next file on the list, ^M
is Enter which opens the file, and again ^Wl
is ctrl-W and l
moving to the right window again.
Running this combination in a loop can apply the same <macro-commands>
on a list of files with relative ease.
Open Vim and hit
Ctrl+o+o
You don't need to leave VIM!
:wĀ !sudo tee %
Of course this can be aliased to anything e.g sudowrite
Added this to myĀ .vimrc: nnoremap DD ""dd
. I find it useful when many times I want to delete before pasting.
:f
Mark your lines and :sort
But you can also:
Sort and remove duplications with unique flag:
:sort u
Sort in reverse:
:sort!
Numeric sort:
:sort n
More at http://vim.wikia.com/wiki/Sort_lines
:set list
Useful for yaml and indentation based structures debugging
:set nolist
to disable
listchars
has different ways of highlighting tabs spaces etc.
Try set listchars=tab:..,trail:_,extends:>,precedes:<,nbsp:~
and then insert a tab (when :set list
is on) and see the changes
func! DeleteTrailingWS()
Ā exe "normal mz"
Ā %s/\s\+$//ge
Ā exe "normal `z"
endfunc
au BufWrite * silent call DeleteTrailingWS()
:so %
When in a merge conflict try :Gdiff
(or :Gvdiff
to open vertically)
You'll get a 3-way split panes, the middle one is the working file and on the sides the two options: HEAD or target branch, which can be referred to by buffer names //2
and //3
(:ls
will show all buffer full names).
You now have two options:
- When standing in the working copy you can
diffget <buffer name/number>
- Go to other pane and
diffput <buffer name/number>
I mapped the process to ease the flow like that:
" Open a vertical diff
nnoremap <leader>gdĀ :Gvdiff<CR>
" Get the left pane changes
nnoremap gdhĀ :diffget //2<CR>
" Get the right pane changes
nnoremap gdlĀ :diffget //3<CR>
" Stage the changes (will automatically close all buffers leaving you in the center)
nnoremap <leader>gwĀ :Gwrite<CR>
go
while browsing will open a file without changing the context
gi
will do the same in a split
?
will transform the tree pane to the help will all options
Open a terminal withĀ :terminal While using the Terminal go to normal mode with CTRL-W N orĀ CTRL-\ CTRL-N Kill the session with CTRL-W CTRL-C
When in a merge conflict try :Gdiff
(or :Gvdiff
to open vertically)
Youāll get a 3-way split panes, the middle one is the working file and on the sides the two options: HEAD or target branch, which can be referred to by buffer names //2
and //3
(:ls
will show all buffer full names).
You now have two options:
- When standing in the working copy you can
diffget <buffer name/number>
- Go to other pane and
diffput <buffer name/number>
I mapped the process to ease the flow like that:
" Open a vertical diff
nnoremap <leader>gd :Gvdiff<CR>
" Get the left pane changes
nnoremap gdh :diffget //2<CR>
" Get the right pane changes
nnoremap gdl :diffget //3<CR>
" Stage the changes (will automatically close all buffers leaving you in the center)
nnoremap <leader>gw :Gwrite<CR>
Another useful shortcut is to jump to the next hunk of changes with [c
or ]c
Really nice and visible way to solve conflicts!
I've turned this into a blog post
Natively you can enlarge a pane by <C-w> _
for horizontal and <C-w> |
for vertical ones.
I added a script that uses these like Tmuxās zoom-in feature:
" Pane Zoom
func! PaneZoom()
res 100
vert res 1000
endfunc
nnoremap <C-w>z :call PaneZoom()<CR>
Now I can <C-w> z
anywhere. (Revert the split to be even with <C-w> =
)
:help 42
Putting aside the :%s/<text>/<replace text>/g
option and its interactive version :%s/<text>/<replace text>/gc
, you can use the gn
tool for quick find and replace:
- Search for your pattern e.g
/const
- Use
cgn
which basically edits the next matchcg
and them jumps to the next onen
- Then you can simply use
.
to repeat the last command on as many matches you want.
http://vimcasts.org/episodes/operating-on-search-matches-using-gn/ https://medium.com/@schtoeffel/you-don-t-need-more-than-one-cursor-in-vim-2c44117d51db
w
jumps one word, if you have svc.cluster.local
these are 3 words. But if you use W
vim will address word as the characters between 2 spaces, so that svc.cluster.local
becomes one. Why is it useful? First - better movement around VIM, another example is trying to wrap with parentheses for example, where I can use ysiW
(using tpope/vim-surround) to include exactly what I want
:wa
Using fzf
you can :Ag
to search for any string which immediately shows up results.
BUT better yet, you can send the word under the cursor to it and populate results to a preview pane
https://github.com/ggreer/the_silver_searcher
This is the mapping that sends the result to ag
with preview:
nnoremap <leader>F
\ :call fzf#vim#ag('', fzf#vim#with_preview({'options': ['--query', expand('<cword>')]}))<cr>
- Open any number of tabs you wish to work with
- Type
:mksession
header-files-work.vim and hit enter - Your current session of open tabs will be stored in a file header-files-work.vim
- Close all tabs and Vim
- Either start vim with your session using :
vim -S header-files-work.vim
or open vim with any other file and enter command mode to type::source header-files-work.vim
and BOOM!
If you work on a file and would like to save it with a new name (like save as) you can:
:w <NAME>
would write an external file and let you keep working on the original one:sav <NAME>
would write an external file and change your current buffer to the new one as well (a more likely scenario)
Sometimes itās visibly comfortable to fold pieces of code.
z
is the controller here, when combined with f
it folds the motion added to it (zf'm
will fold until the marker āmā).
Most intuitive way is to have a visual block marked and just zf
to fold.
za
toggles the created fold on and off.
zd
deletes a fold.
These can be applied automatically with other modes of folding: :setlocal foldmethod=indent
for example will create basic folding under indentation levels of code.
More helpful docs: https://vim.fandom.com/wiki/Folding
zf#j creates a fold from the cursor down # lines.
zf/ string creates a fold from the cursor to string .
zj moves the cursor to the next fold.
zk moves the cursor to the previous fold.
za toggle a fold at the cursor.
zo opens a fold at the cursor.
zO opens all folds at the cursor.
zc closes a fold under cursor.
zm increases the foldlevel by one.
zM closes all open folds.
zr decreases the foldlevel by one.
zR decreases the foldlevel to zero -- all folds will be open.
zd deletes the fold at the cursor.
zE deletes all folds.
[z move to start of open fold.
]z move to end of open fold.
An awesome way to jump between sections when editing a file (https://vim.fandom.com/wiki/Using_marks):
ma
marks a line asa
(or any other letter)- Jump to the mark with
'a
- TIP:
'
is always a marker of your previous location, so by''
you can always jump back (kinda likecd -
in bash)
I looked for a way to see these markers visible (when you have more than 2-3 its hard to remember them), so thereās a nice plugin
If you have a line broken to many parts or just want to join two lines into 1 try Shift + j
In vim surround when you wrap with {
or [
you always get an unwanted space (e.g { $var }
) to lose the spaces, use the closing brace (}
)
You can send a vim split to span full screen with J
:
If you have two splits side by side, and you open another one it stays on the focused side when creating it. To make it span both original splits on the bottom: <CR-W>J
Refresh syntax highlight when it gets messed up in long files : :syntax sync fromstart
:!open % (macOS)
:!xdg-open % (Linux)
so if youāre editing an html file for example it goes straight to the browser
If you visually mark a text and press u
it turns the text to lowercase completely.
Probably not that exciting, but stopping a highlight of a current search, I used to search for a non existing string until today like /sss
which is dumb..
I just added a map of nnoremap ss :noh<CR>
which for me ss
is something I was used to do and also an abbreviation of āstop searchā
Vim has an internal spell check (:help spell
).
- Set it with
:setlocal spell spelllang=en_us
- Run through the marked bad words with
[s
/]s
- Use
zg
to mark a word as āgoodā (āadd to dictionaryā) zw
will mark an untagged word as ābadā
Some more options like file-local words for spelling, undo options etc can be found in the help section.
If youāre in insert mode and want to use a normal mode command without completely changing mode,
you can Ctrl + o
which lets you send a single normal command without leaving.
Consider this:
Iām in insert mode and want to delete a word,
Option 1:
esc
+ dw
+ i
Option 2:
Ctrl + o
+ dw
30% less strokes :sl
Working with YouCompleteMe like a boss
If you use YouCompleteMe plugin it has plenty of extra features for different languages.
So now when I use TypeScript I can ./install.py --ts-completer
and beyond the completion there are hidden gems like GoTo definition or references, smart refactoring, organizing imports etc etc, hereās a short list of mappings I added:
nmap ygr :YcmCompleter GoToReferences
nmap ygt :YcmCompleter GoToType
nmap yGt :YcmCompleter GetType
nmap yGd :YcmCompleter GetDoc
nmap yfi :YcmCompleter FixIt
nmap yr :YcmCompleter RefactorRename
nmap yfm :YcmCompleter Format
nmap yo :YcmCompleter OrganizeImports
Docs are here: https://github.com/ycm-core/YouCompleteMe#goto-commands (edited)
Use |
preceeded with a number of a charter you wish to find.
Usefull when debugging and the debugger notifies of an error on line x and on char y, you can then use :<x>
followed by <y>|
.
A numerical example: "An error was found on line 12 character 3":
:12
3|
:reg
shows the current state of all registes and what is stored in them.
The numbered ones are a kind of a clipboard history showing the last 10 yanked / cut objects.
command! UnMinify call UnMinify()
function! UnMinify()
%s/{\ze[^\r\n]/{\r/g
%s/){/) {/g
%s/};\?\ze[^\r\n]/\0\r/g
%s/;\ze[^\r\n]/;\r/g
%s/[^\s]\zs[=&|]\+\ze[^\s]/ \0 /g
normal ggVG=
endfunction
I found myself fighting a lot with vim windows when working on widescreens - I use vim on my left screen and the code naturally starts at the edge. I wanted a shortcut to ābringā the code closer to my eyes (that is - the right edge where my eyes rest). So I wrote this small function:
function! ComeCloser()
:vnew<CR>
:vertical resize 50
:wincmd l
endfunc
nnoremap <C-w>x :call ComeCloser()<CR>
:mks
will save your current session with all open tabs, for example Iām working on company.com project:
:mks
company.vim
And next time I want to reopen the same state:
vim -S mdi.vim
[another tip: closing all tabs at once: :qa
]
Useful for duplicating a function for a similar structure and saves a few strokes
nmap <leader>yyb V}y}p<CR>
When editting sudo-protected files like /etc/hosts, Vim won't let you write, and you'll have to exit. Use sudo for the edit and re-do your work.
The trick is to pipe the contents of the file write from Vim to sudo vim
, here's the mapping:
command Sudow :w !sudo -A tee %
Note! -A
means the system will use a SUDO_ASKPASS
utility to determine the password externally.
The simplest solution is to set a bash script that echos the password upon execution and setting it in the environment:
- Create a file
~/bin/pw.sh
:
#!/bin/bash
echo 'mypasswrd'
- Set it in the enviroment (this should be set in your bashrc or any other rc file):
export SUDO_ASKPASS=~/pw.sh
I find Vim8 and Neovim sometimes lose touch with syntax highliting. Mostly this happens on long file or extra long sessions. In order to sync Vim with the syntax I've mapped syntax sync
command:
nnoremap <leader>sr :syntax sync fromstart<CR>
Using coc.vim is becoming a blessing. One awesome faeture is refactoring a variable / function name changing all calls respectively:
nmap <leader>rn <Plug>(coc-rename)
Use Vim fugitive to split a file with it's comparison to a branch:
# Can be used as Gvsplit for vertical
:Gsplit <branch>
Creating a shortcut to product an if err != nil
block when writing go.
Below is a function followed by the map (last line):
function! s:IfErr()
let bpos = wordcount()['cursor_bytes']
let out = systemlist('iferr -pos ' . bpos, bufnr('%'))
if len(out) == 1
return
endif
let pos = getcurpos()
call append(pos[1], out)
silent normal! j=2j
call setpos('.', pos)
silent normal! 4j
endfunction
command! -buffer -nargs=0 IfErr call s:IfErr()
nnoremap <leader>ie :IfErr<CR>
Code taken from: https://github.com/koron/iferr/blob/master/vim/ftplugin/go/iferr.vim
To change two vertically split windows to horizonally split
Ctrl-w t Ctrl-w K
Horizontally to vertically:
Ctrl-w t Ctrl-w H