Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ask HN: What is one Vim trick most people don't know?
45 points by wawhal on June 29, 2018 | hide | past | favorite | 97 comments


If you forgot to sudo vi, you've already edited the file, saving the file as root without first saving it as your own and then having to move it:

<ESC>:w !sudo tee %

Pipes the content of the file to a tee process as root and lets tee save it under the current file name. Vim will tell you the file has changed on disk and ask if it should reload it afterwards.


> If you forgot to sudo vi

IMO, it's better not to do that in the first place (which, of course, only makes your tip all the more useful!)


Make sure you are 100% confident that you know the proper syntax for this when you use it. I once blanked by mistake an important file, lucky for me I had a backup.


Generally a good idea, before you sudo anything. That's one of the reasons to prefer this approach to `sudo vim`, actually - that's essentially "sudo of anything vim does", and that's a lot of very different things just a couple keystrokes away.


Not really a trick of Vim itself, but when using Vim from the terminal I find ctrl-z very useful to background Vim, type in a few shell commands for git or whatever and then use "fg" to hop back into Vim.


vim 8 and neovim have pretty functional in-built terminals (:term). You can try that if you're using one of these.


That's more keystrokes. ;)


Not if you bind it to ^Z. ;)


Well, you can't. The terminal translates Ctrl-Z into SIGTSTP before it even reaches vim. Alternative suggestion:

  nmap gz :term zsh<CR>
Same number of keystrokes, and does not override any existing normal-mode command from what I can see.


    :nnoremap <C-z> :term<Return>
works for me (tmux in urxvt).

Ctrl-Z only gets converted to SIGTSTP if the terminal has ISIG set. For example, try `stty -isig; sleep 5` and try to suspend or interrupt the sleep command.


Only a few more keystrokes is:

:sh ENTER

or for more control over which shell, or to run a shell with any arguments, or even to run any command w/ or w/o args:

:!bash

:!bash -o vi

:!bash some_script.sh

:!some_command some args

and after any of those, just press Ctrl-D or type exit ENTER to return to the vim session.

Very fast and easy to do. I use it all the time.


Many of the things here, as of writing, are quite elementary Vim tips. A lesser known trick that I learnt quite recently and have found very useful is `gn`, which visually selects the next search hit (similarly `gN` for the previous hit). That's not so useful in itself, but it can be combined with an operation, such as `c` or `y`, etc., which can then be repeated using the `.`.


Macros, Macros, Macros. These are a game changer when editing large files. Use the `q` key and followed by any letter you like, I default to `a`. Perform some action, like deleting a line, appending to the start of a line, or searching for a word then deleting it. Press `esc`, then type in the number of times you want to repeat the action, followed by `@` and the letter you chose. I.E `10@a` and it will perform that action 10 times.


Also fun: macros can call themselves. So if you want to repeat something across a whole file, it's sometimes easiest to write an infinitely recursive macro and let it error when it bumps up against the bottom of the file.


Vim can be great for quickly doing tedious text transformations. Far easier to get right than doing it the 'proper way' with a script in Python/Perl/whatever.

To do something like scan through a JSON or XML document and split a 'name' field/tag into 'firstName'/'lastName', Vim is the tool I'd reach for.

I think of it as 'cursor-oriented programming', but what I do with it isn't really programming, it's generally of the pattern of 1. Find start of pattern 2. Transform pattern 3. GOTO 1

Or at worst, the decorate/sort/undecorate pattern.

But it boils down to repeatedly applying a macro.


A useful thing to know when using macros is that the letter you press after `q` is just the register in which Vim stores the macro. Registers are also used when copying and pasting. As such, if you want to edit a macro, you can just paste it into a new buffer (e.g., `"ap` for register `a`), edit it as you wish, then copy it back into that register (`:%y a`). You can even use this method to write macros from scratch, rather than recording them first.


`Ctrl + v` to visually select one character at a time (instead of say `Shift + v` that selects a whole line) and then use the movement keys to select a block of characters horizontally as well as vertically. Imagine several well indented HTML <li class="something"> elements, you could for instance delete/modify all the class attributes on all lines visually in one go (to insert text use `Shift + i`, type, then Esc).


I think I don’t quite get you. Doesn’t “v” do the same thing?


What robotvert means is rectangular selection across multiple lines. For example, suppose you have the following text:

  <li class="wrong">first</li>
  <li class="wrong">second</li>
  <li class="wrong">third</li>
You want to change all the instances of "wrong" to "right". So you put the cursor on the first "w", then `Ctrl-V` to enter blockwise visual mode, then `e2j` to select the "wrong" words, then `c` to remove all of them and enter insert mode to type a replacement. The replacement text is then applied to each line separately. Full series of keystrokes (starting with the cursor on the "w" in the first line) if you want to follow along:

  <Ctrl-V>e2jcright<ESC>


:norm(al) command Allows you to do some of the vim magic also on remote systems where you don't have all your plugins.

Example:

(1) Select block with `C-v`

(2) Hit colon `:`

(3) Write norm and enter any sequence as you would be in normal mode: `norm I# ` (prepend selection with a comment sign)

(4) Admire the result.


yes, awesome!!!


Quick renaming of variables: what to type"explanation

    gd" go to definition of variable
    :%s/" beginning of a replace...
    Ctrl-r /" inserts the last word searched into the command, so what "gd" found
    /newname/gc" finish the replace command, g=global replace, c=confirm each replace
Save a character going to a line: instead of using :42<enter> to go to line 42, can use 42G

Auto-indent: =

Go to last file: Ctrl-^

Abbreviations: put this in your ~/.vimrc

    iab cmain int main() {
    \<CR>    return 0;
    \<CR>}
Insert the contents of a file: :r <filename>

I like to have a ~/gitmessage.txt which has a template I like, then when I do git commit all I have to do is:

    :r ~/gitmessage.txt
Vim + Ctags:

Ctags link usages and definitions so you can navigate through code like in an IDE without the bloat of an IDE. Works best with C, but works pretty well with any language with C-like syntax (and more).

Goto tag (definition) of what's under cursor: Ctrl-]

Go back: Ctrl-t

Open tag in a preview: Ctrl-w

:tag <tag> " go to tag, like:

    :tag someFunction
    :tag SOME_CONSTANT


> Insert the contents of a file: :r <filename>

The file name can be a pipe instead.

Let’s say you have a Makefile based project and the dependencies aren’t as complete as they ought to be. With your cursor on this line in the Makefile:

    SOURCES = \
You can say

    :r !ls src/*.c
That will put the complete list of extant C source files into the file at that position, complete with the “src/“ path prefix. Then to format the list properly for “make” syntax, use Shift-V to select the list, hit the right angle bracket (“>”) once to indent it a level, then say:

     :s/$/ \/
This trick of replacing the start (“^”) or end (“$”) of a line is very often helpful. Here I’ve used it to ask Vim to put a space and backslash after every line to allow the SOURCES variable definition to continue from one line to the next.

(This will look like “:’<,’>s...” in your editor, with the bit between the colon and s being inserted by Vim; it means “front the start of the selection to the end.”)

Remove the final backslash and it’s done.

The advantage of working this way is that you aren’t manually transcribing things the computer already knows and can report on command.

The possibilities here are vast. For instance, if your C source files are buried under subdirectories of `src`, say this instead:

    :r !find src -name \*.c


`Shift+v` to select a block of text and do a single operation to the same portion of text on several lines (regexp, aligning, etc.)

There's also `vt` + some character to select text from the cursor to the next occurrence of that character.


I've been looking for something like `Shift+v`! Thanks for that. +1 for `vt` as well.


Also ctrl-v allows you to manipulate a block of text, or insert text on multiple lines at once using I or A for before or after the block.


A couple of nice ones: `gq` for re-wrapping a visually selected block of text (<shift>-v is simplest) to your text width.

`=` for re-indenting a selection to your current shift-width settings etc. (or `gg=G` to just do the whole file)


`gq` will work with a text object, so you don't need to go to the trouble of visually selecting a block. Say, for example, you have a mis-wrapped paragraph in a text file, providing your cursor is anywhere within that paragraph, `gqip` will rewrap the whole paragraph.


The nice thing about using `=` instead of `gq` is that you can “set equalprg” to something smarter than the internal Vim formatter, such as `fmt` or `par`:

    https://www.gnu.org/software/coreutils/manual/coreutils.html#fmt-invocation

    http://www.nicemice.net/par/
These formatters will do nice things like choose line breaks intelligently to reduce the raggedness of the right edge. I just tested `gqip` on a paragraph in a Markdown document here that was formatted with `fmt`: the `fmt` output put breaks in such that all but the last line was within one character of equal, whereas `gqip` had line lengths varying as much as 5 characters on a 72 character line length.

A quicker alternative to Shift-V, arrows, then `=` to reformat a paragraph with `fmt` is this:

    :map <F2> !}fmt -w72<CR>
That works anywhere inside the paragraph like with your `gqip`, but it uses the smarter `fmt` paragraph formatter.

Select your keystroke to taste. I chose F2 so long ago that I forget why that was a good idea at the time.


You can use `Ctrl + C` instead of `ESC`

I figured this out today. And I submitted this Ask HN to find out more such things.


> You can use `Ctrl + C` instead of `ESC`

Even better: Map "jk" to Escape. Enter normal mode without even leaving the home keys; so much easier.

.vimrc:

    inoremap jk <Esc>


It's not recommended to use <C-C> in place of Esc because it doesn't trigger an InsertLeave event


Guy who uses <C-c> here. Is that bad?


It is if one of your plugins uses the InsertLeave event to do its work. If in doubt, `grep -R InsertLeave ~/.vim`.


Also ctrl+[ , if that's any easier.


Fun fact is that `C-[` is the same keypress as `<ESC>` due to how ascii control codes work http://jkorpela.fi/chars/c0.html


Those are incredibly frustrating when you're trying to find new bindings, just about all the non-control ones are already used.


I use a lit this ed (http://man.openbsd.org/4.4BSD-Lite2/ed.1) commands from vi (not just vim).

For instance:

Delele first 20 lines:

<ESC>:0,20d

Delete lines from current location to end of file:

<ESC>:,$d

Search and replace from regrex:

<ESC>:0,$s/regex/replacement/g


Along the same line, the move command.

Copy lines 0 through 20 to the current line:

<ESC>:0,20t.

I end up using variants of this and the above multiple times every work day.


Easier way to delete (or cut):

(from cursor next 20 lines) <ESC> d20d

When deleted is on your copy buffer, just prest p tu paste it from cursor.


Or if you don't like counting lines: d15gg

Delete from the current line to a line with a particular line number you already see on the left if you have line numbering enabled.


Line number display can be relative or absolute.


Ha! Good to know.


Delete lines from current location to end of file:

dG

is a lot faster.


any reason for not using :%s/a/b/g for the last one?


I have two favourites : alt+* (search for the word I currently standing on) and ciw (delete the word you standing on and enter insert mode).


> I have two favourites : alt+* (search for the word I currently standing on)

Just '*' does this, no need for alt.


Whops that's true. I use non us keyboard layout where you have to press alt to get the * char. I pulled out this shortcut from muscle memory.


I use ciw (= "change inner word") also a lot, very helpful!


* - search current word forward # - search for it backward


'Esc :q' to quit vim seems to be the one trick most people don't know.


Better/Other ways to exit VI/vim:

  * ctrl+z; kill %1
  * Open a new terminal and ignore the one with vim running.
  * ZZ
  * Pull the power cord, erasing any sign of it running.
  * ESC :q!
  * ESC :wq
  * ESC :exit
  * ESC :quit
Of course ESC and :q usually works too ;)


ZQ is another one. Like ZZ, but doesn't save. I mainly use ZZ and ZQ to quit.


Mine is playing with errorformat (personal blogspam: http://flukus.github.io/vim-errorformat-demystified.html) and combining this with :cf (read errors from file) or :cb (read errors from buffer) to parse a file/buffer into the quicklist. Great for dealing with log files.


Not sure if most people don't know it, but this is useful (in Windows vim):

In Unix vi(m), you use Ctrl-V to quote a special character to actually insert it into the text (or into a last line mode command; see example below).

So in Unix if you want to insert a carriage return (Ctrl-M) into a file or a command, you do Ctrl-V Ctrl-M. That does not work in Windows with gVim because Ctrl-V means paste from clipboard. So you use Ctrl-Q instead of Ctrl-V, like in this example, where you want to use vim to remove all the carriage returns (CRs) from the file (say if you were going to use the file on Unix later [1]):

    :%s/Ctrl-QEnter//g
where instead of typing the letters Enter, you hit the Enter key.

The %s means do the command for all lines in the file (% for all lines, s for substitute), the command is search-and-replace (old pattern with new pattern, here Ctrl-M with nothing), and the g(lobal) at the end means do it for all occurrences in the line (otherwise it does it only for the first occurrence on the line).

This will show on your screen as:

    :%s/^M//g (the Ctrl-Q does not show on screen)
where the ^M represents the CR or Enter.

The same command on Unix would be:

    :%s/Ctrl-VEnter//g
This will show on your screen as:

    :%s/^M//g (the Ctrl-V does not show on screen)
[1] Of course there are other ways to remove the CRs from the file, like a) using a dos2unix utility, a) many modern editors will do it for you (via a setting or a menu option), c) push it to Git from Windows and then pull it from Unix (with appropriate Git settings for line-ending handling), etc.


I forgot to mention that on for both the Unix and Windows versions of that above command, you have to hit Enter at the end of it (after the g), because last line mode (or ex mode) commands require that, unlike regular vim commands which run immediately, without needing an Enter.


There's a command for that.

    " Use Unix line feeds.
    :set fileformat=unix

    " Use Windows line feeds.
    :set fileformat=dos

    " Shortcut
    :set ff=unix

    " See the current format
    :set ff


Yes, I was aware of that (but thanks), and that's why I had written above:

>a) many modern editors will do it for you

although I tend not to use those particular vim options much.

And for anyone else who does not know, many other vim set commands also have shortcuts, like:

:se ai (for :set autoindent)

and ts for tabstop, sw for shiftwidth, ic for ignorecase, wm for wrapmargin and many others, probably.

I tend to do this in my .vimrc or .exrc as soon as I start using any new instance (on a new machine) of vi/vim:

:se ai ts=4 sw=4 showmode showmatch ignorecase expandtab report=0

Also, like:

>" See the current format

> :set ff

you can do this for any boolean setting, like readonly, ignorecase, etc.:

:se readonly?

:se ic?

to query their values.


A slightly longer trick but..

:tabe newfile to open a new file.

Then, add the following to your ~/.vimrc (on MacOS)

map <Esc>f <Esc>:tabn<cr> map <Esc>b <Esc>:tabp<cr>

Now you can switch between tabs using option + arrow keys


Also, there are standard gt and gT that are same amount of keystrokes and very mnemonic too.



`gv` for reselecting the last selection

Assigning different functions to the arrow keys helps when learning vim navigation


Yes! Took me (embarrassingly) long to discover since I could not figure out how to google it


If you're in Insert Mode, and you want to paste the thing you last yanked, without leaving insert mode, you can do the following:<C-r>0

That pastes the contents of the 0 register into the buffer. Very useful. That'll work for all registers too.


Can also do <C-r>" (double quote instead of 0) to paste from the default yank register while in Insert Mode.


Better keyboard scrolling with this simple mapping:

nmap <C-j> 3j3<C-e>

nmap <C-k> 3k3<C-y>

This maps the <C-j> and <C-k> keys to a scroll action similar to mouse scrolling: the cursors stays in a fixed position while the whole screen moves up or down 3 rows.


I similarly map <C-j> 5j and <C-k> 5k and find it to be one of the most useful bindings I use, can't VIM without it these days


Diff mode, delicious! Just edit the 2 files with vim -O file1 file2, and in vim do <ESC>:diffthis<enter>C-w<right>:diffthis<enter>, and voila!

Vimgrep and the quickfix window are also pretty cool.


:diffoff is another great thing to know, particularly when using vimdiff to merge things (e.g. in git). I find that turning diffing off and on for various windows can more quickly give a clear picture of what changed where.


Turning off the ability to enter recording mode. I still occasionally type :Q instead of :q and I’d like that to also quit...or even to just no-op.

Any tips on how to just rip recoding mode completely out of my vim?


nnoremap Q <nop>

removes the recording mode and throws a simple error when you use :Q instead of :q


command! Q q

will make :Q behave like :q


If you like these, it's worth skimming `:help index` and `:help option-summary` occasionally. You're likely to find at least one interesting new keybinding or setting.


'ct' followed by a character later in the line. Erases all text from the cursor up to that character, and enters insert mode.


Writing some vimscript is actually not that bad and opens plenty of possibilities for advanced, context aware code editing.


How to quit is probably what most people don’t know, when it’s been invoked by something looking for $EDITOR


`:compiler` for parsing lines in the quickfix window - great for jumping directly to unit test failures


:vsplit filename

to open file called "filename" in a vertical split of the console window. Also

:split filename

will open it in a horizontal split


'Shift+zz' to save and exit


Vim has at least three ways of doing this.

You can do :wq return, or the abbreviated form of :x return, or as you say, ZZ


A lot of people seem to be unaware of switching files within vim instead of exiting and reinvoking.


I think that's probably more to people not wanting to learn how to do it right. For the longest time, I only taught myself just enough vim to be able to use it for basic editing, because it's convenient and it's better than nano.


To note how:

- ctrl+p plugin to find files in project.

- :bn / :bp / :b<number> to switch between files (buffers)

- :bd to close file

I've mapped 11 to go to previous file, 22 to go to next file, 33 to close file.


Vim has tabs. They work great.


zz - scroll the line with the cursor to the center of the screen

zt - scroll the line with the cursor to the top

zb - scroll the line with the cursor to the bottom

also:

Ctrl-O - jump to last (think "o"lder) cursor positions

Ctrl-I - jump to next cursor positions (after using Ctrl-O)


Ctrl-e and Ctrl-y to scroll with the cursor on the same line.


    git grep -n foo | vim - -c 'cbuf!'


Ctrl+V for making and operating on block selections.


I know i am going to get downvoted for this but :q!


Ctrl-a increases a number in visual model


copy a line using <Esc>1 yy and past simply <Esc> p


You don't need the 1; `yy` or `Y` in normal mode is enough to copy the whole current line.


How to quit it


  killall vim
There you go. :)


One of my favorite Vim tricks (which also works in vi) is the general technique of piping a specified range of text through a command (a Unix-style filter that reads from standard input and writes to standard output), with the output of that command then replacing that range of text.

The text can be anything from a single line, some arbitrary range of lines (defined via vim marks like a and b or other letters, some paragraphs (see example below), to the entire file - and the filter command can be anything at all, too. The Cartesian product of those two things makes it a very powerful technique.

An example is this key mapping of the character v to a command that format paragraphs of text using the fmt command [1]:

map v !}fmt -w 64<CR>

This says: map the letter v to the command following it (after the space), which is:

    !}fmt -w 64<CR>
which runs/pipes (!) the current paragraph (when at top of a para, the cursor movement } defines a para) through the fmt -w 64 command, which formats its input text into lines of not more than 64 characters each.

(I just used v because it was one of the characters that Vim does not already assign a command to - any other available character will work.)

I have the mapping in my _vimrc on Windows and .vimrc on Unix.

With that mapping, I can reformat any paragraph (where paragraphs in vim are defined by a blank line either above and below them or both), just by going to the top of the paragraph (with { ) and typing v (no Enter needed).

For example, if I have this (single) line in a file in vim:

  the quick brown fox jumped the quick brown fox jumped the quick brown fox jumped the quick brown fox jumped the quick brown fox jumped
and I press v when at the start of that line, it gets reformatted into this para:

  the quick brown fox jumped the quick
  brown fox jumped the quick brown fox
  jumped the quick brown fox jumped the
  quick brown fox jumped
I typically use this technique when writing text content (non-programming content, such as documentation, tech articles, blog posts, etc.) or sometimes even for block comments of text in programs.

You can also create a different version of that formatting command to filter the entire text file through fmt in one shot (that would be "!Gfmt -w 64" when at top of file, where G moves to the last line), but I tend not to use that approach much, because it sometimes messes up some paragraphs which should not be reformatted. So I do it a para at a time.

If you use the sort command instead of fmt, that will sort the range of lines you select, and the sorted lines replace the original lines. This is of course useful in text files and (text format) data files, but is also sometimes useful in source code, e.g. to sort lines of import statements (say in Python or Go - although Go has commands for that), ranges of variable declarations into alphabetical order, array literals, etc.).

[1] (Fmt is a Unix command, but you can probably get it in many of the Unix subsystems that run on Windows too, like Cygwin, UWin, Windows Subsystem for Linux too). c The command that you run the selected text through, can also be a Unix or Windows pipeline, which makes the technique even more powerful.


ZZ


I agree it's a good command to know, but you might explain what it does: save and exit.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: