Code Self Study Forum

Vim Tips Thread

This thread is for posting Vim tips. A great way to improve Vim skills is to learn a few new tricks at a time and slowly incorporate them into your workflow.

I’ll start with a few tips, and we can keep adding more in the comments as we learn them.

Here are a few related tips about % and ! in Vim:

How to run Vim without sudo

I suspect that it’s better not to run Vim with sudo. Plugins and some other things might be able to execute code as root if Vim is run as root. I open Vim without sudo and then save protected files like this:

:w !sudo tee %

Find out more about the tee command by typing man tee in a bash (or similar) terminal.

Running shell commands on your file

An exclamation point (!) will run shell commands (like in the :w !sudo tee % example above).

Say you have an unsorted list with duplicates in your editor like this:

elephant
bear
cat
alligator
alligator
bear
dog

You could sort it and remove duplicates by visually selecting all (ggVG – go to top, visually select line, go to bottom) and then running this command:

:!sort|uniq

That takes the selected text and runs (!) it through the shell’s sort command and then pipes it into the uniq command. The final result should be:

alligator
bear
cat
dog
elephant

You can use a variety of shell commands there.

Another quick example: read the output of a command into your current buffer:

:r !ls -alh

(It should work in Emacs’ Evil Mode too.)

Reloading the .vimrc file while editing it

% refers to the current file in Vim. (See the sudo example above for one example.)

If you’re editing your .vimrc file and want to reload it without restarting vim, you can source the file (execute the commands in it) with:

:source %

or shorthand:

:so %

% and find/replace

The % in a Vim search and replace also refers to the current file:

:%s/foo/bar/gc

It means: within the file (%) substitute foo with bar globally, and confirm each change.

If you visually select some text (for example, with shift-v), you can enter command mode with the : and then perform a substitution. The percent sign (meaning the current file) won’t be there this time, but it will look more like this:

:'<,'>s/foo/bar/gc

You can also limit substitutions by line numbers. So the following command would perform a search and replace to just lines 20-25:

:20,25s/foo/bar/gc

It can be fun to play around with ex too. (Command mode in Vim uses ex.) I think that seeing the older way of doing things with ex makes Vim’s way of doing things seem less strange. To print the file you can type %p and the same substitution commands (:3,4/s/t/TTTTT/g) work:

To open a file in ex do something like:

$ ex my-file.txt

To switch from ex to Vim, type :visual.

Another quick example: read your software versions into a README file:

:r !ruby -v
:r !rails -v

…in order to write something like this without leaving the editor:

## Versions

* Ruby 2.4.1
* Rails 5.1.4

(Pausing Vim is easy too: ctrl-z to put it to sleep, and fg at the command line to revive it.)

I just saw this site mentioned today:
http://vimcasts.org/

I’ve been looking for a better way to navigate files than nerdtree or the ctrl-p plugin. I was going to try unite, but then found out that this it’s being abandoned for denite.

It looks interesting so far. I installed it with Vim plug and then added one line in my .vimrc file to enable searching for files with ctrl-p:

nnoremap <C-p> :<C-u>Denite file_rec<CR>

My next step will be to read: :h denite.

Example of searching for files with the ctrl-p shortcut:

There are more tips here:

and a video that I haven’t looked at yet, unfortunately only in Japanese:

I saw this on HN – it allows you to make HTTP requests in Vim:

It got me thinking that it’s pretty easy to do in default Vim without any plug-in.

Type this on a blank line in Vim:

curl https://api.github.com/orgs/codeselfstudy

Then press Esc to go into normal mode.

Press yy to “yank” (copy) the link into a register.

Then type:

:r !<C-r>"

where <C-r> is ctrl-r.

(C-r " will paste the contents of that register, which contains the yanked text of the curl request.)

Alternatively, you could type the following from normal mode, though it’s more difficult to edit the curl command once you enter command-line mode (as soon as your press : you enter command-line mode):

:r !curl https://api.github.com/orgs/codeselfstudy

Then press Enter to load the response in the Vim buffer.

An example POST request with curl:

curl -X POST https://jsonplaceholder.typicode.com/posts -H "Content-Type: application/json" -d '{"title": "Saluton Mondo", "body": "Lorem ipsum", "userId": 127}'

Here’s a quick example:

asciicast

I was trying to figure out how to close all open buffers at once, without quitting, and came across this post.

The command is:

:buffdo bd

Here is a list of other commands mentioned:

:bd          - deletes the current buffer, error if there are unwritten changes
:bd!         - deletes the current buffer, no error if unwritten changes
:bufdo bd    - deletes all buffers, stops at first error (unwritten changes)
:bufdo! bd   - deletes all buffers except those with unwritten changes
:bufdo! bd!  - deletes all buffers, no error on any unwritten changes

:bw          - completely deletes the current buffer, error if there are unwritten changes
:bw!         - completely deletes the current buffer, no error if unwritten changes
:bufdo bw    - completely deletes all buffers, stops at first error (unwritten changes)
:bufdo! bw   - completely deletes all buffers except those with unwritten changes
:bufdo! bw!  - completely deletes all buffers, no error on any unwritten changes

:set confirm - confirm changes (Yes, No, Cancel) instead of error

:ls          - list open buffers
:b N         - open buffer number N (as shown in ls)
:tabe +Nbuf  - open buffer number N in new tab
:bnext       - go to the next buffer (:bn also)
:bprevious   - go to the previous buffer (:bp also)

I haven’t used :tabe +Nbuf – I usually open the current buffer in a new tab with ctrl-w shift-t.

If anyone hasn’t discovered it yet, gq is a useful Vim command for wrapping lines.

It lets you write long lines without thinking about the number of characters. When finished typing, visually highlight the lines (shift-v and move up or down to select the text to reformat), and then type gq to wrap it.

asciicast

I also have this in my ~/.vimrc file:

" Use par for better `gq` formatting
" See http://vimcasts.org/episodes/formatting-text-with-par/
set formatprg=par
let $PARINIT = 'rTbgqR B=.,?_A_a Q=_s>|'

(gq should work in Emacs/Evil too.)

I’m writing some Python lately, and it’s useful, because PEP8 doesn’t like lines to be more than 79 characters long, and my editor complains…

I didn’t watch the video below, but I liked the auto-complete tips from the slides.

Examples:
(in insert mode)

  • type ctrl-x ctrl-f to show a dropdown menu with auto-completed filenames.
  • ctrl-x ctrl-] completes from tags.
  • ctrl-x ctrl-n completes from names in the current file
  • ctrl-n completes for anything specified by the “complete” option

ctrl-p (previous) and ctrl-n (next) move up and down the menus.

The video might have more information.

I finally have Denite set up the way I want, and it’s great. I pasted my denite config from .vimrc below. RipGrep is installed.

" Denite ideas from https://github.com/sodiumjoe/dotfiles/blob/master/vimrc#L242 and elsewhere.
" reset 50% winheight on window resize
augroup deniteresize
  autocmd!
  autocmd VimResized,VimEnter * call denite#custom#option('default',
        \'winheight', winheight(0) / 2)
augroup end

call denite#custom#option('default', {
      \ 'prompt': '❯'
      \ })

call denite#custom#var('file_rec', 'command',
      \ ['rg', '--files', '--glob', '!.git', ''])
call denite#custom#var('grep', 'command', ['rg'])
call denite#custom#var('grep', 'default_opts',
      \ ['--hidden', '--vimgrep', '--no-heading', '-S'])
call denite#custom#var('grep', 'recursive_opts', [])
call denite#custom#var('grep', 'pattern_opt', ['--regexp'])
call denite#custom#var('grep', 'separator', ['--'])
call denite#custom#var('grep', 'final_opts', [])
call denite#custom#map('insert', '<Esc>', '<denite:enter_mode:normal>',
      \'noremap')
call denite#custom#map('normal', '<Esc>', '<NOP>',
      \'noremap')

" ctrl-v and ctrl-h to open files in vertical and horizontal splits
call denite#custom#map('insert', '<C-v>', '<denite:do_action:vsplit>',
      \'noremap')
call denite#custom#map('normal', '<C-v>', '<denite:do_action:vsplit>',
      \'noremap')
call denite#custom#map('insert', '<C-h>', '<denite:do_action:split>',
      \'noremap')
call denite#custom#map('normal', '<C-h>', '<denite:do_action:split>',
      \'noremap')
call denite#custom#map('normal', 'dw', '<denite:delete_word_after_caret>',
      \'noremap')

" ctrl-n and ctrl-p to move down and up the denite menus
call denite#custom#map('insert', '<C-n>', '<denite:move_to_next_line>', 'noremap')
call denite#custom#map('insert', '<C-p>', '<denite:move_to_previous_line>', 'noremap')

" ctrl-p to find files
nnoremap <C-p> :<C-u>Denite file_rec<CR>

" leader-s to find buffers
nnoremap <leader>s :<C-u>Denite buffer<CR>
nnoremap <leader><leader>s :<C-u>DeniteBufferDir buffer<CR>

" leader-8 to grep for current word
nnoremap <leader>8 :<C-u>DeniteCursorWord grep:. -mode=normal<CR>

" leader-/ to grep
nnoremap <leader>/ :<C-u>Denite grep:. -mode=normal<CR>
nnoremap <leader><leader>/ :<C-u>DeniteBufferDir grep:. -mode=normal<CR>
nnoremap <leader>d :<C-u>DeniteBufferDir file_rec<CR>
nnoremap <leader><leader>r :<C-u>Denite -resume -cursor-pos=+1<CR>

" leader-o to load custom menu
nnoremap <leader>o :<C-u>Denite menu<CR>

hi link deniteMatchedChar Special

" Add custom menus
let s:menus = {}

let s:menus.config_files = {
    \ 'description': 'Edit config files'
    \ }

" quick access to config files
let s:menus.config_files.file_candidates = [
    \ ['.aliases', '~/.aliases'],
    \ ['.zshenv', '~/.zshenv'],
    \ ['.zshrc', '~/.zshrc'],
    \ ['.vimrc', '~/.vimrc'],
    \ ['.i3conf', '~/.config/i3/config'],
    \ ['.i3status.conf', '~/.i3status.conf'],
    \ ['.ctags', '~/.ctags'],
    \ ['global .gitconfig', '~/.gitconfig'],
    \ ['global .gitignore', '~/.gitignore'],
    \ ['.muttrc', '~/.muttrc'],
    \ ['.npmrc', '~/.npmrc'],
    \ ['.mongojsrc.js', '~/.mongojsrc.js'],
    \ ['.psqlrc', '~/.psqlrc'],
    \ ['.pgpass', '~/.pgpass'],
    \ ['.pythonrc', '~/.pythonrc'],
    \ ['.tmux.conf', '~/.tmux.conf'],
    \ ['.tern-config', '~/.tern-config'],
    \ ['.vuerc', '~/.vuerc'],
    \ ['.xinitrc', '~/.xinitrc'],
    \ ['.Xmodmap', '~/.Xmodmap'],
    \ ['.taskbook.json', '~/.taskbook.json'],
    \ ]

" let s:menus.vim = {
"     \ 'description': 'Edit Vim config files'
"     \ }
" let s:menus.vim.file_candidates = [
"     \ ]

" let s:menus.vim.command_candidates = [
"     \ ['Split the window', 'vnew'],
"     \ ['Open zsh menu', 'Denite menu:zsh'],
"     \ ]

call denite#custom#var('menu', 'menus', s:menus)

These are all pretty interesting, but why are they in private discussions?

The forum was originally private. When I opened it up to the public, I moved most of the existing posts into a private section, because people may have posted with the assumption that their posts would not be exposed to search engines.

I can see that. In the case of this thread though, I think it is really informative, and seems to be only your posts, so I say make it public! :slight_smile:

1 Like

Done :slight_smile:

1 Like

A post was split to a new topic: Which-key for Vim and Emacs

Here’s another post with tips:

Security problem

It looks like it can be fixed by putting this in your .vimrc and updating vim/neovim:

set nomodeline