Please keep in mind that I just started writing this guide and new things get added every day. Things about to be added can be found here: issues. Thanks!
- Buffers, windows, tabs?
- Active, loaded, listed, named buffers?
- Mappings?
- Mapleader?
- Registers?
- Marks?
- Motions? Operators? Text objects?
- Autocmds?
- Quickfix and location lists?
- Colorschemes?
- Locality?
- Getting help offline
- Getting help online
- Clipboard
- Restore cursor position when opening file
- Editing remote files
- Managing plugins
- Block insert
- Saner behavior of n and N
- Saner command-line history
- Quickly move current line
- Quickly add empty lines
- Quickly edit your macros
- Quickly jump to header or source file
- Quickly change font size in GUI
- Change cursor style in insert mode
- Don't lose selection when shifting sidewards
- General tips
- Profiling startup time
- Profiling at runtime
- Verbosity
- Debugging Vim scripts
- Debugging syntax files
- Editing small files is slow
- Editing huge files is slow
- Newline used for NUL
- Bracketed paste (or why do I have to set 'paste' all the time?)
- Delays when using escape key in terminal
- Alignment
- Code completion
- Commenters
- Delimiter
- Fuzzy finders
- Grep tools
- Navigation
- Statusline
- Taking notes
- Tmux
- Undo history
- Version control
- Misc
Vim is a text editor. Every time text is shown, the text is part of a buffer. Each file will be opened in its own buffer. Plugins show stuff in their own buffers etc.
Buffers have many attributes, e.g. whether the text it contains is modifiable, or whether it is associated with a file and thus needs to be synchronized to disk on saving.
Windows are viewports onto buffers. If you want to view several files at the same time or even different locations of the same file, you use windows.
And please, please don't call them splits. You can split a window in two, but that doesn't make them splits.
Windows can be split vertically or horizontally and the heights and widths of existing windows can be altered, too. Therefore you can use whatever window layout you prefer.
A tab page (or just tab) is a collection of windows. Thus, if you want to use multiple window layouts, use tabs.
Putting it in a nutshell, if you start Vim without arguments, you'll have one tab page that holds one window that shows one buffer.
By the way, the buffer list is global and you can access any buffer from any tab.
Run Vim like this vim file1
. The file's content will be loaded into a buffer.
You have a loaded buffer now. The content of the buffer is only synchronized
to disk (written back to the file) if you save it within Vim.
Since the buffer is also shown in a window, it's also an active buffer. Now
if you load another file via :e file2
, file1
will become a hidden buffer
and file2
the active one.
Both buffers are also listed, thus they will get listed in the output of
:ls
. Plugin buffers or help buffers are often marked as unlisted, since
they're not regular files you usually edit with a text editor. Listed and
unlisted buffers can be shown via :ls!
.
Unnamed buffers, also often used by plugins, are buffers that don't have an
associated filename. E.g. :enew
will create an unnamed scratch buffer. Add
some text and write it to disk via :w /tmp/foo
, and it will become a named
buffer.
You can define your own mappings with the :map
family of commands. Each
command of that family defines a mappping for a certain set of modes.
Technically Vim comes with a whopping 12 modes, 6 of them can be mapped:
Recursive | Non-recursive | Modes |
---|---|---|
:map |
:noremap |
normal, visual, operator-pending |
:nmap |
:nnoremap |
normal |
:xmap |
:xnoremap |
visual |
:cmap |
:cnoremap |
command-line |
:omap |
:onoremap |
operator-pending |
:imap |
:inoremap |
insert |
E.g. this defines the mapping for normal mode only:
:nmap <space> :echo "foo"<cr>
So far, so good. There's only one problem that can be pretty confusing to
beginners: :nmap
is recursive! That is, the right-hand side takes other
mappings into account.
So you defined a mapping that simply echoes "Foo":
:nmap b :echo "Foo"<cr>
But what if you want to map the default behavior of b
(going one word back) to
another key?
:nmap a b
If you hit a, we expect the cursor to go back a word, but instead
"Foo" is printed in the command-line! Because the right-hand side, b
, was
mapped to another action already, namely :echo "Foo"<cr>
.
The proper way to resolve this problem is to use a non-recursive mapping instead:
:nnoremap a b
Rule of thumb: Always use non-recursive mappings unless recursing is actually desired.
The mapleader is simply a placeholder than can be used with custom mappings and
is set to \
by default.
nnoremap <leader>h :helpgrep<space>
This mapping is triggered by \h
. If you want to use <space>h
instead:
let mapleader = ' '
nnoremap <leader>h :helpgrep<space>
Moreover, there is <localleader>
that is the local counterpart to <leader>
and is supposed to be used for mappings that are local to the buffer, eg.
filetype-specific plugins. It also defaults to \
.
Note: Set the mapleaders before mappings! All leader mappings that are in
effect already, won't change just because the mapleader was changed. :nmap <leader>
will show all normal mode leader mappings with the mapleader resolved
already, so use it to double-check your mappings.
See :h mapleader
and :h maplocalleader
for more.
Registers are slots that save text. Copying text into a register is called yanking and extracing text from a register is called pasting.
Vim provides 10 types of registers:
Type | Character | Filled | Contains text from.. |
---|---|---|---|
Unnamed | " |
implicitly | Last yank or deletion. (d , c , s , x , y ) |
Numbered | 0 to 9 |
implicitly | Register 0 : Last yank. Registers 1 : Last deletion. Register 2 : Second last deletion. And so on. Think of registers 1 -9 as a read-only queue with 9 elements. |
Small delete | - |
implicitly | Last deletion that was less than one line. |
Named | a to z , A to Z |
explicitly | For your own use. If you yank to register a , you replace its text. If you yank to register A , you append to the text in register a . |
Read-only | : , . , % |
implicitly | Register : : Last command. Register . : Last inserted text. Register % : Current filename. |
Alternate buffer | # |
implicitly | Most of the time the previously visited buffer of the current window. See :h alternate-file |
Expression | = |
explicitly | Evaluation of the VimL expression that was yanked. E.g. do this in insert mode: <c-r>=5+5<cr> and "10" will be inserted in the buffer. |
Selection and Drop | + , * , ~ |
implicitly | * and + are the clipboard registers. Register ~ : From last drag'n'drop. |
Black hole | _ |
explicitly | Use this register if you don't want any other registers implicitly affected. E.g. "_dd deletes the current line without affecting registers " , 1 , + , * . |
Last search pattern | / |
implicitly | Last pattern used with / , ? , :global , etc. |
There are numerous exceptions when registers get implicitly filled, so be sure
to read :h registers
.
Yank with y
and paste with p
/P
.
Vim distinguishes between characterwise and linewise selections. See :h linewise
.
Example: linewise
yy
(or just Y
) yanks the current line, move the cursor somewhere else, use
p
to paste below the current line P
for pasting above it.
Example: charwise
Yank the first word with 0yw
, move somewhere else, paste after the cursor on
the current line with p
and before the cursor with P
.
Example: explicit naming of register
"aY
yanks the current line into register a
. Move to another line. "AY
appends the current line to register a
.
I suggest playing around with with all these registers a bit and constantly
checking :reg
, so you can see what's actually happening.
Fun fact: In Emacs "yanking" stands for pasting (or reinserting previously killed text) not copying.
You use marks to remember a position, that is line number and column, in a file.
Marks | Set by.. | Usage |
---|---|---|
a - z |
User | Local to file, thus only valid within one file. Jumping to a lowercase mark, means jumping within the current file. |
A - Z |
User | Global, thus valid between files. Also called file marks. Jumping to a file mark may switch to another buffer. |
0 - 9 |
viminfo | 0 is the position when the viminfo file was written last. In practice this means when the last Vim process ended. 1 is the position of when the second last Vim process ended and so on. |
Put '
/g'
or `
/g`
in front of a register to form a motion.
Use mm
to remember the current position with mark "m". Move around the file
and then jump back via 'm
(first non-blank) or `m
(exact column).
Lowercase marks will be remembed after exiting Vim, if you tell your viminfo
file to do so, see :h viminfo-'
.
Use mM
to remember the current position with file mark "M". Switch to another
buffer and switch back via 'M
or `M
.
Other motions include:
Motion | Jump to.. |
---|---|
'[ , `[ |
First line or character of previously changed or yanked text. |
'] , `] |
Last line or character of previously changed or yanked text. |
'< , `< |
Beginning line or character of last visual selection. |
'> , `> |
Ending line or character of last visual selection. |
'' , `` |
Position before latest jump. |
'" , `" |
Position when last exiting the current buffer. |
'^ , `^ |
Position where last insertion stopped. |
'. , `. |
Position where last change was made. |
'( , `( |
Start of current sentence. |
') , `) |
End of current sentence. |
'{ , `{ |
Start of current paragraph. |
'} , `} |
End of current paragraph. |
Marks can also be used in a range. You probably saw this before and
wondered what it means: Select some text in visual mode and do :
, the
command-line will be prepended with :'<,'>
, which means the following command
would get a range that denotes the visual selection.
Use :marks
to list all marks. Read everything in :h mark-motions
.
Motions move the cursor. You all know h
/j
/k
/l
. Or w
and b
. Even
/
is a motion. They also take a count. 2?the<cr>
jumps to the second last
occurence of "the".
See :h navigation
and everything below for all available motions.
Operators act on a region of text, e.g. d
, ~
, gU
, >
to name just a
few. They get used in two contexts, either in normal or visual mode. In normal
mode, operators come first followed by a motion, e.g. >j
. In visual mode,
operators simply act on the selection, e.g. Vjd
.
Like motions, operators take a count, e.g. 2gUw
makes the rest of the current
word and the next one uppercase. Since motions and operators take counts,
2gU2w
works just as well and executes gU2w
twice.
See :h operator
for all available operators. Use :set tildeop
to make ~
act as an operator.
Text objects act on the surrounding area, opposed to motions that act into one direction. Actually they work on objects, e.g. a whole word, a whole sentence, everything between parentheses, and so on.
Text objects can't be used to move the cursor in normal mode, because even the most-skilled cursors can't jump into two directions at the same time. It works in visual mode though, because then one side of the object is already selected and the cursor simply jumps to the other side.
Text objects start with either i
or a
followed by a character denoting the
object. With i
it only acts on the object itself, with a
on the object plus
trailing whitespace. E.g. diw
deletes the current word and ci(
changes
everything between parentheses.
Text objects don't take a count.
See :h text-objects
for all available text objects.
On many occasions, Vim emits events. You hook into these events by using autocmds.
You wouldn't use Vim if there weren't autocmds. They're used all the time, even
if you don't even know it. Don't believe me? Check :au
, but don't let the
output overwhelm you. These are all the autocmds that are in effect right now!
See :h {event}
for a quick overview of all available events and :h autocmd-events-abc
for more details.
A typical example would be setting filetype-specific settings:
autocmd FileType ruby setlocal shiftwidth=2 softtabstop=2 comments-=:#
But how does a buffer even know that it contains Ruby code? Because another
autocmd detected it as that and set the filetype accordingly which again
triggered the FileType
event.
One of the first things everyone adds to his vimrc is filetype on
. This simply
means that filetype.vim
is read at startup which sets autocmds for almost all
filetypes under the sun.
If you're brave enough, have a look at it: :e $VIMRUNTIME/filetype.vim
. Search
for "Ruby" and you'll find that Vim simply uses the file extension .rb
to
detect Ruby files:
au BufNewFile,BufRead *.rb,*.rbw setf ruby
The BufNewFile
and BufRead
events in this case are hardcoded in the C
sources of Vim and get emitted everytime you open a file via :e
and similar
commands. Afterwards all the hundreds of filetypes from filetype.vim
are
tested for.
Putting it in a nutshell, Vim makes heavy use of events and autocmds but also exposes a clean interface to hook into that event-driven system for customization.
Every time an action has to return a list of locations, quickfix or location lists can be used. In this case a location is a file, a line number and optionally a column.
Examples are compiler errors assembled in a quickfix list or matches of an external grep tool assembled in a location list.
The big advantage over just putting that stuff in an empty buffer is that you get a nice uniform interface for browsing the entries.
At all time there's only one quickfix list, but every window can has its own location list. Both type of lists feel the same, but use slightly different commands for navigation.
Most common commands:
Action | Quickfix | Location |
---|---|---|
open window | :copen |
:lopen |
close window | :cclose |
:lclose |
next entry | :cnext |
:lnext |
previous entry | :cprevious |
:lprevious |
first entry | :cfirst |
:lfirst |
last entry | :clast |
:llast |
See :cc
and everything below for all commands.
Example:
Let's use our good old friend grep
for searching the files in the current
directory recursively for a certain query and put the results in the quickfix
list.
:let &grepprg = 'grep -Rn $* .'
:grep! foo
<grep output - hit enter>
:copen
Assuming any files contained the string "foo", it should be shown now in the quickfix window.
Colorschemes are the way to style your Vim. Vim consists of many components and each of those can be customized with different colors for the foreground, background and a few other attributes like bold text etc. They can be set like this:
:highlight Normal ctermbg=1 guibg=red
This would paint the background of the editor red. See :h :highlight
for more
information.
So, colorschemes are mostly a collection of :highlight
commands.
Actually, most colorschemes are really 2 colorschemes! The example above sets
colors via ctermbg
and guibg
. The former definition will only be used if Vim
was started in a terminal emulator, e.g. xterm. The latter will be used in
graphical environments like gVim.
If you ever happen to use a certain colorscheme in Vim running in a terminal emulator and the colors don't look like the colors in the screenshot at all, chances are that the colorscheme only defined colors for the GUI.
I use gruvbox for the GUI and janah for the terminal.
More colorschemes: here
Many of the concepts mentioned above also have local counterparts:
Global | Local | Scope | Help |
---|---|---|---|
:set |
:setlocal |
buffer or window | :h local-options |
:map |
:map <buffer> |
buffer | :h :map-local |
:autocmd |
:autocmd * <buffer> |
buffer | :h autocmd-buflocal |
:cd |
:lcd |
window | :h :lcd |
<leader> |
<localleader> |
buffer | :h maplocalleader |
Variables also sport different scopes, but will be explained in Vim scriping.
Vim comes with great documentation in the form of single text files with a special layout. Vim uses a system based on tags for accessing certain parts of those help files.
First of all, read this: :help :help
. This will open the file
$VIMRUNTIME/doc/helphelp.txt
in a new window and jump to the :help
tag
within that file.
A few simple rules:
- options are enclosed in single quotes, e.g.
:h 'textwidth'
- VimL functions end in (), e.g.
:h reverse()
- commands start with :, e.g.
:h :echo
You can use <c-d>
(this is ctrl+d) to list all tags that
match the currently entered query. E.g. :h tab<c-d>
will get you a list of all
tags from tab
over 'softtabstop'
to setting-guitablabel
.
You want to list all VimL functions? Simple: :h ()<c-d>
. You want to list all
VimL functions that concern windows? :h win*()<c-d>
.
This quickly becomes second nature, but especially in the beginning, you
sometimes don't know any part of the tag you are looking for. You can only
imagine some keywords that could be involved. :helpgrep
to the rescue!
:helpgrep backwards
This will look for "backwards" in all documentation files and jump to the first
match. The matches will be assembled in the quickfix list. Use :cn
/:cp
to
jump to the next/previous match. Or use :copen
to open the quickfix window,
navigate to an entry and hit <cr>
to jump to that match. See :h quickfix
for
the whole truth.
If you have an issue you can't resolve or are in need of general guidance, see
the vim_use mailing list.
Another great resource is using
IRC. The channel #vim
on
Freenode is huge and usually full of helpful people.
If you want to report a Vim bug, use the vim_dev mailing list.
First of all, Vim needs certain features compiled in for the clipboard to
work. If you want to know what features your version of Vim has compiled in and
whether it comes with GUI support or not, check the output of :version
.
It should contain +clipboard
(-clipboard
means "not compiled in"), for
general access to the clipboard.
If that's not the case and you installed Vim from a package manager, make sure
to install a package called vim-x11
, vim-gtk
, vim-gnome
or similar. Even
if you never use the GUI version of Vim, these packages bundle a Vim with a
bigger feature set compiled in.
You also need +xterm_clipboard
if you want to use the 'clipboard'
option on
a Unix system and don't have a Vim with GUI support.
Related help:
:h 'clipboard'
:h gui-clipboard
:h gui-selections
Also see: Bracketed paste (or why do I have to set 'paste' all the time?)
Windows comes with a clipboard and OSX comes with a pasteboard.
Both work like most users would expect them to work. You copy selected text with
ctrl+c
/cmd+c
and paste them in another application with ctrl+v
/cmd+v
.
Note that copied text is actually transferred to the clipboard, so you can close the application you copied from before pasting in another application without problems.
Whenever this happens, the clipboard register *
gets filled with the
selection. From Vim use "*y
and "*p
to yank and paste from the clipboard
respectively.
If you don't even want to specify the *
register all the time, put this in
your vimrc:
set clipboard=unnamed
Usually all yank/delete/put operations fill the "
register, now the *
register is used for the same operations, therefore simply y
and p
will be
enough.
Let me repeat: Using the option above means that every yank/paste, even when only used in the same Vim window, will alter the clipboard. Decide for yourself if this is useful or not.
If you're even too lazy to type y
, you can send every visual selection to the
clipboard by using these settings:
set clipboard=unnamed,autoselect
set guioptions+=a
Related help files:
:h clipboard-unnamed
:h autoselect
:h 'go_a'
If your OS uses X, things work a bit different. X implements the X Window System Protocol which happens to be at major version 11 since 1987, hence X is also often called X11.
Prior, in X10, cut buffers were introduced that kind of worked like a clipboard as in copied text was actually hold by X and it was accessiable by all ofter applications. This mechanism still exists in X, but its use is deprecated now and most software doesn't use it anymore.
Nowadays data is transferred between applications by the means of selections. From the 3 selection atoms defined, only 2 are used in practice: PRIMARY and CLIPBOARD.
Selections work roughly like this:
Program A: <ctrl+c>
Program A: assert ownership of CLIPBOARD
Program B: <ctrl+v>
Program B: note that ownership of CLIPBOARD is hold by Program A
Program B: request data from Program A
Program A: respond to request and send data to Program B
Program B: receives data from Program A and inserts it into the window
Selection | When used? | How to paste? | How to access from Vim? |
---|---|---|---|
PRIMARY | Selecting text | middle-click , shift+insert |
* register |
CLIPBOARD | Selecting text and ctrl+c |
ctrl+v |
+ register |
NOTE: Selections (no, not even the CLIPBOARD selection) are never kept in
the X server! Thus you lose the data copied with ctrl+c
when the application
closes.
Use "*p
to paste the PRIMARY selection or "+y1G
to yank the entire file to
the CLIPBOARD selection.
If you happen to access one of the two registers all the time, consider using:
set clipboard^=unnamed " * register
" or
set clipboard^=unammedplus " + register
(The ^=
is used to prepend to the default value, :h :set^=
.)
This will make all yank/delete/put operations use either *
or +
instead of
the unnamed register "
. Afterwards you can simply use y
or p
for accessing
your chosen X selection.
Related help:
:h clipboard-unnamed
:h clipboard-unammedplus
Without this, you will always be at line 1 when opening a file. With this, you will be at the position where you left off.
Put this in your vimrc:
autocmd BufReadPost *
\ if line("'\"") > 1 && line("'\"") <= line("$") |
\ exe "normal! g`\"" |
\ endif
This simpy does g`"
(jump to position where you left off without changing
jumplist) if that position still exists (the file might have fewer lines since
it was altered by another program).
This requires the use of a viminfo file: :h viminfo-'
.
Vim comes with the netrw plugin that enables editing remote files. Actually it transfers the remote file to a local temporary file via scp, opens a buffer using that file, and writes the changes back to the remote file on saving.
This is extremely useful if you want to use your local configuration opposed to ssh'ing into a server and use whatever the admins want you to use.
:e scp://[email protected]/.vimrc
If you have a ~/.ssh/config
set up already, this gets used automatically:
Host awesome
HostName awesome.site.com
Port 1234
User bram
Assuming the above content in ~/.ssh/config
, this works just as well:
:e scp//awesome/.vimrc
Similar can be done with a ~/.netrc
, see :h netrw-netrc
.
Make sure to read :h netrw-ssh-hack
and :h g:netrw_ssh_cmd
.
Another possibility is using sshfs which uses FUSE to mount a remote filesystem into your local filesystem.
Pathogen was the first popular tool for
managing plugins. Actually it just adjusts the runtimepath (:h 'rtp'
) to
include all the things put under a certain directory. You have have to clone the
repositories of the plugins there yourself.
Real plugin managers expose commands that help you installing and updating plugins from within Vim. Hereinafter is a list of commonly used plugin managers in alphabetic sequence:
Plug is my favorite, but your mileage may vary.
This is a technique to insert the same text on multiple consecutive lines at the same time. See this demo.
Switch to visual block mode with <c-v>
. Afterwards go down for a few lines.
Hit I
or A
and start entering your text.
It might be a bit confusing at first, but text is always entered for the current line and only after finishing the current insertion, the same text will be applied to all other lines of the prior visual selection.
So a simple example is <c-v>3jItext<esc>
.
If you have lines of different length and want to append the same text right
after the end of each line, do this: <c-v>3j$Atext<esc>
.
Sometime you need to place the cursor somewhere after the end of the current
line. You can't do that by default, but you can set the virtualedit
option:
set virtualedit=all
Afterwards $10l
or 90|
work even after the end of the line.
See :h blockwise-examples
for more info. It might seem complicated at first,
but quickly becomes second nature.
If you want to get real fancy, have a look at multiple-cursors.
The direction of n
and N
depends on whether /
or ?
was used for
searching forward or backward respectively. This is pretty confusing to me.
If you want n
to always search forward and N
backward, use this:
nnoremap <expr> n 'Nn'[v:searchforward]
nnoremap <expr> N 'nN'[v:searchforward]
If you're anything like me, you're used to going to next and previous items via
<c-n>
and <c-p>
respectively. By default, this also works in the
command-line and recalls older or more recent command-lines from history.
So far, so good. But <up>
and <down>
are even smarter! They recall the
command-line whose beginning matches the current command-line. E.g. :echo <up>
may change to :echo "Vim rocks!"
.
Of course I don't want you to reach to the arrow keys, just map it instead:
cnoremap <c-n> <down>
cnoremap <c-p> <up>
I depend on this behaviour several times a day.
Sometimes I need a quick way to move the current line above or below:
nnoremap [e :<c-u>execute 'move -1-'. v:count1<cr>
nnoremap ]e :<c-u>execute 'move +'. v:count1<cr>
These mappings also take a count, so 2]e
moves the current line 2 lines below.
This is surely no must-have, but I prefer the following mappings over
o<esc>
/O<esc>
:
nnoremap [<space> :put! =''<cr>
nnoremap ]<space> :put =''<cr>
This is a real gem! The mapping takes a register (or *
by default) and opens
it in the cmdline-window. Hit <cr>
when you're done editing for setting the
register.
I often use this to correct typos I did while recording a macro.
nnoremap <leader>m :<c-u><c-r>='let @'. v:register .' = '. string(getreg(v:register))<cr><c-f><left>
Use it like this <leader>m
or "q<leader>m
.
This technique can probably be applied to many filetypes. It sets file marks
(see :h marks
) when leaving a source or header file, so you can quickly jump
back to the last accessed one by using 'C
or 'H
(see :h 'A
).
autocmd BufLeave *.{c,cpp} mark C
autocmd BufLeave *.h mark H
NOTE: The info is saved in the viminfo file, so make sure that :set viminfo?
includes :h viminfo-'
.
I think this was taken from tpope's config:
command! Bigger :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)+1', '')
command! Smaller :let &guifont = substitute(&guifont, '\d\+$', '\=submatch(0)-1', '')
I like to use a block cursor in normal mode and i-beam cursor in insert mode. Also when using tmux in the middle.
if empty($TMUX)
let &t_SI = "\<Esc>]50;CursorShape=1\x7"
let &t_EI = "\<Esc>]50;CursorShape=0\x7"
else
let &t_SI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=1\x7\<Esc>\\"
let &t_EI = "\<Esc>Ptmux;\<Esc>\<Esc>]50;CursorShape=0\x7\<Esc>\\"
endif
This simply tells Vim to print a certain sequence of characters (escape sequence) when entering/leaving insert mode. The underlying terminal will process and evaluate it.
There's one drawback though: there are many terminal emulator implementations and not all use the same sequences for doing the same things. The sequences used above might not work with your implementation. Your implementation might not even support different cursor styles. Check the documentation.
The example above works with iTerm2.
If you select one or more lines, you can use <
and >
for shifting them
sidewards. Unfortunately you immediately lose the selection afterwards.
You can use gv
to reselect the last selection (see :h gv
), thus you can work
around it like this:
xnoremap < <gv
xnoremap > >gv
Now you can use >>>>>
on your visual selection without any problems.
NOTE: The same can be achieved using .
, which repeats the last change.
If you encounter a strange behaviour, try reproducing it like this:
vim -u NONE -N
This will start Vim without vimrc (thus default settings) and in nocompatible
mode (which makes it use Vim defaults instead of vi defaults). (See :h --noplugin
for other combinations of what to load at start.)
If you can still reproduce it now, it's most likeley a bug in Vim itself! Report it to the vim_dev mailing list. Most of the time the issue won't be resolved at this time and you'll have to further investigate.
Often plugin updates introduce new/changed/faulty behaviour. If you're using a plugin manager, comment them out until you find the culprit.
Issue is still not resolved? If it's not a plugin, it must be your other settings, so maybe your options or autocmds etc.
Time to use binary search. Repeatedly split the search space in two until you find the culprit line. Due to the nature of binary division, it won't take many steps.
In practice it works like this: Put the :finish
command in the middle of your
vimrc. Vim will skip everything after it. If it still happens, the problem is in
the active upper half. Move the :finish
to the middle of that half.
Otherwise the issue is in the inactive lower half. Move the :finish
to the
middle of that half. And so on.
Vim startup feels slow? Time to crunch some numbers:
vim --startuptime /tmp/startup.log +q && vim /tmp/startup.log
The first column is the most important as it shows the elapsed absolute time. If there is a big jump in time between two lines, the second line is either a very big file or a file with faulty VimL code that is worth investigating.
Vim provides a built-in capability for profiling at runtime and is a great way to find slow code in your environment.
First and foremost, check if :version
shows +profile
, which means that the
profile
feature is enabled. Otherwise you're using a Vim with a smaller
feature set. You want a Vim built with the huge feature set (see :h :version
). Many distros install a Vim with minimal feature set by default, so
you need to install a package called vim-x11
or vim-gtk
(yes, even if you
don't use gvim) for more features.
With that said, we're ready for profiling now. The :profile
command takes a
bunch of sub-commands for specifying what to profile.
If you want to profile everything, do this:
:profile start /tmp/profile.log
:profile file *
:profile func *
<do something in Vim>
<quit Vim>
Vim keeps the profiling information in memory and only writes it out to the
logfile on exit. (Neovim has fixed this using :profile dump
).
Have a look at /tmp/profile.log
. All code that was executed during profiling
will be shown. Every line, how often it was executed and how much time it took.
Most of the time that will be plugin code the user isn't familiar with, but if
you're investigating a certain issue, jump to the bottom of the log. Here are
two different sections FUNCTIONS SORTED ON TOTAL TIME
and FUNCTIONS SORTED ON SELF TIME
that are worth gold. On a quick glance you can see, if a certain
function is taking too long.
Another useful way for observing what Vim is currently doing is increasing the
verbosity level. Currently Vim supports 9 different levels. See :h 'verbose'
for the full list.
:e /tmp/foo
:set verbose=2
:w
:set verbose=0
This would show all the files that get sourced, e.g. the undo file or various plugins that act on saving.
If you only want increase verbosity for a single command, there's also
:verbose
, which simply gets put in front of any other command. It takes the
verbosity level as count and defaults to 1:
:verb set verbose
" verbose=1
:10verb set verbose
" verbose=10
It's very often used with its default verbosity level 1 to show where an option was set last:
:verb set ai?
" Last set from ~/.vim/vimrc
Naturally, the higher the verbosity level the more overwhelming the output. But fear no more, you can simply redirect the output to a file:
:set verbosefile=/tmp/foo | 15verbose echo "foo" | vsplit /tmp/foo
If you ever used a command-line debugger before, :debug
will quickly feel
familiar.
Simply prepend :debug
to any other command and you'll be put into debug mode.
That is, the execution will stop at the first line about to be executed and that
line will be displayed.
See :h >cont
and below for the 6 available debugger commands and note that,
like in gdb and similar debuggers, you can also use their short forms, that is
c
, q
, n
, s
, i
, and f
.
Apart from that those, you're free to use any Vim command, e.g. :echo myvar
,
which gets executed in the context of the current position in the code.
You basically get a
REPL by
simply using :debug 1
.
It would be a pain if you had to single-step through every single line, so of
course we can define breakpoints, too. (Breakpoints are called breakpoints,
because the execution stops when they're hit, thus you can simply skip code
you're not interested in.) See :h :breakadd
, :h :breakdel
, and :h :breaklist
for further details.
Let's assume you want to know what code is run every time you save a file:
:au BufWritePost
" signify BufWritePost
" * call sy#start()
:breakadd func *start
:w
" Breakpoint in "sy#start" line 1
" Entering Debug mode. Type "cont" to continue.
" function sy#start
" line 1: if g:signify_locked
>s
" function sy#start
" line 3: endif
>
" function sy#start
" line 5: let sy_path = resolve(expand('%:p'))
>q
:breakdel *
As you can see, using <cr>
will repeat the previous debugger command, s
in
this case.
:debug
can be used in combination with the verbose option.
Syntax files are often the cause for slowdowns due to wrong and/or complex
regular expressions. If the +profile
feature is compiled in, Vim provides the
super useful :syntime
command.
:syntime on
" hit <c-l> a few times to redraw the window which causes the syntax rules to get applied again
:syntime off
:syntime report
The output contains important metrics. E.g. you can see which regexp takes too long and should be optimized or which regexps are used all the time but never even match.
See :h :syntime
.
Resource | Description |
---|---|
Seven habits of effective text editing | By Bram Moolenaar, the author of Vim. |
Seven habits of effective text editing 2.0 (PDF) | See above. |
IBM DeveloperWorks: Scripting the Vim editor | Five-part series on Vim scripting. |
Learn Vimscript the Hard Way | Develop a Vim plugin from scratch. |
Practical Vim (2nd Edition) | Hands down the best book about Vim. |
Vimcasts.org | Vim screencasts. |
Your problem with Vim is that you don't grok vi | Concise, informative and correct. A real gem. |
Vim distributions are Vim + custom settings + custom plugins from certain authors and are therefore very opinionated.
The problem with such distributions is that they tend to be used by beginners. (More advanced users know how to choose their own plugins and settings after all.) It all goes good until an issue appears. Now where is the problem? The beginner doesn't know what to do and asks for advice on the internet. After long back and forth they figure out that the problem was a weird mapping provided by the distro. But the beginner thought it was a default Vim mapping... Time was wasted, everyone is pissed.
I don't have problems with distributions per se, but please, if you don't understand exactly what they're doing, don't try to get help from others in case of emergencies.
I know that many people don't want to spend hours and hours on customizing an editor (and actually you never stop customizing your vimrc when you finally got hooked), but in the long-term it's much better and more time-efficient to learn how to do stuff manually in the first place.
Repeat after me: "A programmer should know their tools."
Anyway, if you know what you're doing, you might get some inspiration from looking at some distributions:
Command | Message |
---|---|
:Ni! |
Do you demand a shrubbery? |
:h 'sm' |
NOTE: Use of the short form is rated PG. |
:h 42 |
What is the meaning of life, the universe and everything? Douglas Adams, the only person who knew what this question really was about is now dead, unfortunately. So now you might wonder what the meaning of death is... |
:h UserGettingBored |
When the user presses the same key 42 times. Just kidding! :-) |
:h bar |
Ceci n'est pas une pipe. |
:h holy-grail |
You found it, Arthur! |
:h map-modes |
:nunmap can also be used outside of a monastery. |
:help! |
E478: Don't panic! (Glitch? When used in a help buffer (buftype=help ) this works like :h help.txt instead.) |
:smile |
Try it out yourself. ;-) Added in 7.4.1005. |
When Bill Joy created vi, a predecessor of Vim, he did it on a ADM-3A which had no extra cursor buttons but used, you might already guessed it, hjkl instead.
Keyboard layout: click
This also shows why ~
is used to denote the home directory on Unix systems.
Most of the time this is caused by syntax files using complex regular expressions. Particulay the Ruby syntax file caused people to have slowdowns in the past. (Also see Debugging syntax files.)
Moreover, some features tend to impact performance more than others. Check this list to ease slowdowns:
Option | Why? |
---|---|
:set nocursorline |
This makes screen redrawing quite a bit slower. |
:set norelativenumber |
Constantly computing the relative numbers is expensive. |
:set foldmethod=marker |
If the syntax file itself is slow already, foldmethod=syntax makes it even worse. |
:set synmaxcol=200 |
Due to internal representation, Vim has problems with long lines in general. Only syntax highlight till column 200. |
:NoMatchParen |
Uses regular expressions to find the accompanying parenthesis. |
The biggest issue with big files is, that Vim reads the whole file at once. This is done due to how buffers are represented internally. (Discussion on vim_dev@)
If you only want to read, tail hugefile | vim -
is a good workaround.
If you can live without syntax, settings and plugins for the moment:
$ vim -u NONE -N
This should make navigation quite a lot faster, especially since no expensive regular expressions for syntax highlighting are used. You should also tell Vim not to use swapfiles and viminfo files to avoid long delays on writing:
$ vim -n -u NONE -i NONE -N
Putting it in a nutshell, try to avoid using Vim when intending to write really huge files. :\
NUL characters (\0
) in a file, are stored as newline (\n
) in memory and
displayed in a buffer as ^@
.
See man 7 ascii
and :h NL-used-for-Nul
for more information.
Bracketed paste mode allows terminal emulators to distinguish between typed text and pasted text.
Did you ever tried pasting code into Vim and afterwards everything seemed messed up?
This only happens if you paste via cmd+v
, shift-insert
, middle-click
etc.
because then you're just throwing text at the terminal emulator. Vim doesn't
know that you just pasted the text, it thinks you're an extremly fast typist.
Accordingly it tries to indent the lines and fails.
Obviously this is not an issue, if you paste using Vim's registers, e.g. "+p
,
because then Vim knows that you're actually pasting.
To workaround this, you have to :set paste
, so it gets pasted as-is. See :h 'paste'
and :h 'pastetoggle'
.
If you're fed up with toggling 'paste'
all the time, have a look at this fine
plugin that does it for you:
bracketed-paste.
Additional read from the same author as the plugin: here.
Neovim: Neovim tries to make all of this much more seemless and sets bracketed paste mode automatically if the terminal emulator supports it.
If you live in the command-line, you probably use a so-called terminal emulator like xterm, gnome-terminanal, iTerm2, etc. (opposed to a real terminal).
Like their ancestors, terminal emulators use escape
sequences (or control
sequences) to control things like moving the cursor, changing text colors, etc.
They're simply strings of ASCII characters starting with an escape character
(displayed as ^[
). When such a string arrives, the terminal emulator looks up the
accompanying action in the terminfo
database.
To make the problem clearer, I'll explain mapping timeouts first. They always happen when there's ambiguity between mappings:
:nnoremap ,a :echo 'foo'<cr>
:nnoremap ,ab :echo 'bar'<cr>
Both mappings work as expected, but when typing ,a
, there will be a delay of 1
second, because Vim waits whether the user keys in another b
or not.
Escape sequences pose the same problem:
<esc>
is used a lot for returning to normal mode or quitting an action.- Cursor keys are encoded using escape sequences.
- Vim expects Alt (also called Meta key) to send a proper 8-bit encoding with the high bit set, but many terminal emulators don't support it (or don't enable it by default) and send an escape sequence instead.
You can test the above like this: vim -u NONE -N
and type i<c-v><left>
and
you'll see a sequence inserted that starts with ^[
which denotes the escape
character.
Putting it in a nutshell, Vim has a hard time distinguishing between a typed
<esc>
character and a proper escape sequence.
By default Vim uses :set timeout timeoutlen=1000
, so it delays on ambiguity of
mappings and key codes by 1 second. This is a sane value for mappings, but you
can define the key code timeout on its own which is the most common workaround
for this entire issue:
set timeout " for mappings
set timeoutlen=1000 " default value
set ttimeout " for key codes
set ttimeoutlen=10 " unnoticeable small value
Under :h ttimeout
you find a small table showing the relationship between
these options.
If you're using tmux between Vim and your terminal emulator, also put this in
your ~/.tmux.conf
:
set -sg escape-time 0
Here's a list of commonly used colorschemes:
- base16
- gotham
- gruvbox
- janah
- jellybeans
- lucius
- molokai
- railscasts
- seoul256
- solarized (or a lighter variant: flattened)
- tomorrow
- vividchalk
Also see fuzzy finders.