Today I Learned

A Hashrocket project

258 posts about #vim

Vim Verbose Map

Do you have a Vim mapping you’d like to know more about? One technique to explore a mapping is map. Here’s what my machine knows about gcc:

:map gcc
n  gcc           <Plug>CommentaryLine
o  gc            <Plug>Commentary
n  gc            <Plug>Commentary
x  gc            <Plug>Commentary

Need more information? Add verbose, which will also display where it was last set.

:verbose map gcc
n  gcc           <Plug>CommentaryLine
        Last set from ~/hashrocket/dotmatrix/.vim/bundle/vim-commentary/plugin/commentary.vim
o  gc            <Plug>Commentary
        Last set from ~/hashrocket/dotmatrix/.vim/bundle/vim-commentary/plugin/commentary.vim
n  gc            <Plug>Commentary
        Last set from ~/hashrocket/dotmatrix/.vim/bundle/vim-commentary/plugin/commentary.vim
x  gc            <Plug>Commentary
        Last set from ~/hashrocket/dotmatrix/.vim/bundle/vim-commentary/plugin/commentary.vim

Now we know which modes support gcc (normal, plus operator-pending, normal, and ex-mode as gc), what happens when the command is used, and who set it last.

h/t Chris Erin

Python and Neovim

Some Vim plugins require Python, and if you’re using NeoVim, you’ll need to do a bit of extra work to get them working. An error message like this, seen when Python is definitely installed on your machine, is a hint you’re in this predicament:

$ vim
MatchTagAlways unavailable: requires python

Neovim needs its own Python support. Solve the problem with this library, specifically one of the following commands depending on your Python version:

$ pip2 install neovim
$ pip3 install neovim

Escaping Terminal Mode In An Nvim Terminal

A recent TIL by Chris Erin showed how to split open a terminal in a Neovim session — :sp term://zsh.

The terminal is emulated into a Vim buffer which means that you can treat it sort of like any other Vim buffer. You start in Normal mode. Once you use any mapping that would transition you into Insert mode, you’ll instead find yourself in Terminal mode. Terminal mode works just like any zsh session (give ls a try).

Try hitting <Esc> though and you’ll see that you stay in Terminal mode instead of being transitioned back to Normal mode.

So how do you get back to Normal mode?

Hit <Ctrl-\><Ctrl-n>.

This is a pretty awkward key mapping though, so follow the Nvim Terminal docs and bring back the Escape key.

:tnoremap <Esc> <C-\><C-n>

Open a zsh terminal in a split window in Neovim

Opening a terminal in Neovim is one of the more eye opening features of the vim fork. To open a terminal in vim use the :terminal command.

:te
:term
:terminal

Those commands though will use your entire current window for the terminal and by default it opons in bash even though my shell is configured to be zsh

To open a terminal in zsh use:

:te zsh

This still takes up the entire window. Lets now use the split command and the terminal protocol syntax to open a split window with zsh.

:sp term://zsh
:split term://zsh

Pipe text into Vim from stdin

You can pipe text into vim directly from stdin. This can be helpful if you want to edit the output of a long bash command, or if you want to see what an installation script contains before piping it into bash.

curl -fsSL https://raw.github.com/cknadler/vim-anywhere/master/install | vim -

The key is to use the - after the vim command to make it read from stdin. This will open a new buffer with the output of the previous command.

autocmd on a List of Extensions

Yesterday I was VimScripting and learned that the autocmd function can execute against multiple file extensions in a single line of setup. Here’s a contrived example:

" ~/.vimrc
autocmd BufWritePost *.exs,*.ex :! git add %

In this code, after every buffer write on files matching .ex and .exs, add the current buffer to our Git staging area. Learning this helped me reduce duplication in my configuration.

Netrw header toggling with vim-vinegar

vim-vinegar is a vim plugin that “cleans up” Netrw as-

an attempt to mitigate the need for more disruptive “project drawer” style plugins

but it turns off the Netrw header, which, while unnecessary is the second most charming feature of vim.

To reclaim your Netrw header, use I while in Netrw.

As a vim-vinegar bonus, use - while in a buffer to open Netrw in the directory of the current buffer.

Having rapid directory access available changes everything

All quotes are from the the vim-vinegar README. Thank you to Vidal for the hat tip about I.

Fold A Visual Selection And Expand It Back

If I visually select a series of lines — say the open and close tags of a large div in an HTML file I am reading through — and then hit zf, it will be folded into a single line. That line will list how many lines are included in the fold as well as the content of the first line of the fold.

If I later come back to that fold and want to expand it again, I can hit zd to delete (or undo) the fold.

To do this, you’ll want to make sure your foldmethod is set to manual. This can be done by running the following command:

:set foldmethod=manual

See the vim helpfiles (:h fold) for more details.

Vi + Imitation

It’s understood in the Vim community that the name ‘Vim’ stands for ‘Vi + Improved’, because this tool improves upon the classic Vi text editor.

Last night at the Vim Chicago Meetup, I learned that ‘Vim’ once stood for ‘Vi + Imitation.’ Bram Moolenaar, the creator of Vim, changed the name when the feature surpassed the tool it was originally built to imitate.

This was one of many facts I learned during from a fantastic lightning talk on the history of Vim. We recorded it and plan to post it online soon.

Treat words with dash as a word in Vim

By default vim treats two words connected with an underscore e.g. search_tag as a vim word text-object.

This is very useful because you can then use motions and commands on them such as daw to delete a word, or ciw to change inside the word.

That is not the case for dash separated words e.g. search-tag. These types of words are very common in CSS class names and HTML IDs.

To make vim treat dash separated words as a word text-object simply add the following to your .vimrc:

set iskeyword+=-

And source it again.

Jump between #git hunks in Vim with vim-gitgutter

One of my favorite Vim plugins is vim-gitgutter - it shows gutter markings for changes in the current buffer when that file is tracked by git.

It looks like this:

+ = new lines - = deleted line ~ = changed line

It also comes with a few very useful commands for working with hunks, sections of changed code in your file.

To jump between hunks you can use ]c and [c. Since I don’t really use the return and backspace keys in normal mode I have mapped those instead:

nnoremap <silent> <cr> :GitGutterNextHunk<cr>
nnoremap <silent> <backspace> :GitGutterPrevHunk<cr>

This is especially useful on big files (more than a bufferful) with scattered changes.

Simple text file #encryption with Vim

Vim provides a simple text file encryption feature. To make use of it add the following to your .vimrc:

set cryptmethod=blowfish2

This will set the encryption to the strongest algorithm vim supports.

Now to use it simply start editing a file with the -x flag:

$ vim -x mysecret.txt

You will be prompted for a password, and password confirmation. After that you should be able to edit the file and save it normally.

When you open the file again with vim (even without the -x flag) you will be asked to type your password to decrypt the file. If you enter the wrong password all you’ll see is gibberish.

This is not the strongest encryption out there but it works and should suffice for most personal use cases.

NOTE: this will not work with NeoVim.

Ch-ch-ch-ch-changes

For everyfile in vim, vim remembers the position of the changes that have occurred. These change positions are stored in the changes list for each file and can be used for intrafile navigation with the two commands g, and g;.

This is really handy if you switch to a large file and need to quickly flip through the most recently changed sections of that file.

View the entire list of changes for a file with the command :changes.

Vim Projectionist Default File of Type

When setting up vim-projectionist, we can add a directory and filetype like this:

{
  "src/components/*.js": {
    "command": "component"
  }
}

With this in place, :Ecomponent will tab-complete to any JavaScript file in src/components, and :Ecomponent navbar will jump straight to the navbar.js component.

Let’s add a second key that includes a hard-coded file name.

{
"src/components/*.js": {
    "command": "component"
  },
  "src/components/App.js": { "type": "component" }
}

This pair tells vim-projectionist to jump to App.js if no argument is provided to :Ecomponent.

Replace App.js with your root component, and you have a shortcut to the top of your component hierarchy.

Split The Current Window

Generally when I want to open up a buffer in a window split, I use :sp or :vsp and type out the filename. It may be a bit more convenient to use window commands to create the splits.

To open a horizontal split:

Ctrl-w s

To open a vertical split:

Ctrl-w v

With the split made, you can use Ctrl-o/Ctrl-i, BufExplorer, or whatever way you like to navigate between buffers.

h/t Ryan Messner

Deleting Words in Vim

As a recent vim convert, I learn new commands everyday from my fellow rocketeers. Here’s some cool ones for deleting words:

  • Use dw to delete word. Cursor placement is important! If your cursor is not on the first character, it will only delete from your cursor to the end of the word.
  • Use diw to delete inside word. Deletes the entire word that your cursor resides in.
  • Use dt<char> to delete to character. Deletes from your cursor to the specified character.

Jump To The Next Misspelled Word

If you turn on spelling with set spell, Vim will highlight any words it considers misspelled. For a large document it can be tedious to scroll through and find all of the spelling errors.

Fortunately, Vim makes it easy to jump to the next misspelled word. Hit ]s and your cursor will be transported to that word.

Likewise, hit [s to jump backwards in the document to the closest misspelled word behind the cursor.

See h ]s for more details.

From Ruby Variables To JavaScript Variables

I sometimes find myself writing so much Ruby that as soon as I am back in a JavaScript file, my code starts looking like this:

const my_javascript_var = 123;

It would be easy enough to hit caw to the delete the entire word and then retype it as camel case. I happen to have the Abolish.vim plugin installed, so there is an even quicker way.

If I hit crc over the variable, it will be coerced to camel case.

const myJavascriptVar = 123;

If I hit crs then it will be coerced back to snake case. Hit crc one more time and I can get back to writing some JavaScript.

See :h abolish-coercion for more details.

Fix The Spelling Of A Word

If there is a misspelled word (:set spell to see what is misspelled), you can navigate the cursor over that word and hit (from normal mode) z=. This will open up a screen of possible corrections. The one you are most likely looking for will be toward the top. Each possible spelling will have a number next to it. Enter the number of the word you are looking for and hit enter. The spelling of that word will have been updated.

Try misspelling something and give it a try yourself.

See :h z= for more details.

h/t Jake Worth

Run vim command from .... the command line!

Generally vim is started at the command line with the vim command and it comes equipped with the -c flag for running commands.

-c will open vim, run a command, and stay open, so if you wanted to open vim with a smile you could run:

vim -c ':smile'

If you wanted to run a subsitution on a file with vim’s substitute command, then save, then quit, that would look like:

vim -c '%s/frown/upside-down/g | write | quit' frowns.txt

Go To File With Line Number

I often use gf as supported by Vim or with the help of plugins like rails.vim as a way of quickly navigating to existing files. For unloaded files, this loads a buffer with the cursor at the top of the buffer. For existing buffers, it opens to that buffer with the cursor where it was when you left.

Vim also supports a slightly fancier goto file command, gF. If this command is used while the cursor is over a file with a line number appended to the end, it will not only open up a buffer with that file, it will move the cursor to the specified line number.

With this repository, I could try it out by moving the cursor over the following text and hitting gF.

README.md:100

This will open up a buffer for README.md with the cursor at line 100.

See :h gF for more details.

Examine or Diff a file on another #git branch

At times, I’m curious about the changes that have been made for a particular file on a branch that is not the branch I’m on.

The vim-fugitive plugin provides an easy way to open a buffer for a file on another branch with the :Gedit command.

For instance:

:Gedit readme_updates:README.md

Will open a buffer with the contents of the README.md file from the readme_updates branch.

You may want to view that file in comparison to the current file. The :Gdiff command provides us with that functionality:

:Gdiff readme_updates:README.md

Check that an executable exists on the $PATH

When writing a vim plugin interacting with different programs on the server might be necessary. For instance you might want to use ruby to make a network request or perform some file management. But first, the vim plugin should check to see if the program ruby exists which can be accomplished with the executable() vimscript function.

:echo executable('ruby')
1
:echo executable('doesnotexist')
0

This can be used in a condition before running the ruby command.

if executable('ruby')
  system('ruby -e "puts YAML"')
endif

Elixir Static Code Analysis in Vim

Today I learned how to set up Credo, a static analysis tool for Elixir, to run on every Vim buffer write, using Neomake.

To start, I added Credo to my project:

# mix.exs
defp deps do
  {:credo, "~> 0.8", only: [:dev, :test], runtime: false}
end

Including it with:

$ mix deps.get

Next, I loaded and sourced the Neomake plugin with Pathogen:

" ~/.vimbundle
neomake/neomake

Next, I told Neomake to run on every file, enabling Credo for Elixir files:

" ~/.vimrc
let g:neomake_elixir_enabled_makers = ['credo']
autocmd! BufWritePost * Neomake

Now I have Credo style recommendations right in my text editor.

Specifying the location of your vim plugin

If you are working on a vim plugin and you want to keep this plugin amongst your projects rather than with your other vim plugins you can add a new entry to the runtimepath or rtp.

Place the following in your vimrc:

set runtimepath+=~/projects/vim-my-plugin

Or

set rtp+=~/projects/vim-my-plugin

You can confirm that the path was added correctly by checking that setting with:

set rtp

Resize vim window to the size of its content

If you’re writing a vim plugin and in that plugin you are opening a new window to display some content, you may want to resize that window so that it is only the size of the content within. For instance, if you have 10 lines of content but the window is half the screen, then you want to resize the window to 10 lines.

Resizing a window is accomplished with the resize command like:

:resize 10

Getting the number of content lines is accomplished by getting the line number of the last last:

:echo line('$')

Putting the two together is tricky because the argument to the resize command is interpreted literally. :resize line('$') does not work. You have to combine the two into a string and then pass it to the execute command.

:execute('resize ' . line('$'))

Complete a Whole Line in Vim

This week I picked up an excellent Vim command: CTRL-X CTRL-L. This command searches backwards through your project and inserts a matching line after the cursor.

So, if there’s a line in your project like this:

defimpl Phoenix.Param, for: Tilex.Developer do

And I type defimpl (which is unique enough to match just a few lines of code in my project), Vim will suggest matching options, letting me choose a match to complete it:

defimpl Phoenix.Param, for: Tilex.Developer do
defimpl Phoenix.Param, for: Tilex.Developer do

This is useful when writing code in a framework like React.js, where the head of many files often includes a group of repetitive import statements.

See ‘Insert mode completion’ inside insert.txt in the Vim help for more information.

h/t Dorian Karter

Quickly Switch To A Buffer By Number

There are a number of different commands you can use for switching to a buffer by its number. For example, if you want to switch to the buffer assigned 7, you can do :7b or :e #7. However, there is a quicker way than typing out either of those commands.

You may already be familiar with CTRL-^ for switching to the alternate file which is generally the previous buffer. If you precede that command with a number, you will switch to the buffer with that number instead of the alternate file.

From normal mode

7 Ctrl-^

will switch to buffer 7.

See :h Ctrl-^ for more details.

Detect If You Are On A Mac

There are a couple ways of detecting with vimscript if you are on a mac. This can be useful if you are writing a plugin with OS-specific functionality. Here are two ways to make that check.

if has('macunix') || has('mac') || has('osx')
  ...
endif

Alternatively, you can use Vim’s system() function to execute unix’s uname command. This command will give you the name of the operating system. In the event you are using a Mac, the result of uname should be Darwin. The following regex match is a good way to make this check.

if system('uname') =~ "Darwin"
  ...
endif

See :h has(), :h system(), and man uname for more details.

Suppress errors when substituting in vim

The substitute (or s) command in vim throws an error the search pattern is not found.

:substitute/banana/apple
E486: Pattern not found: banana

This error can be suppressed with the e flag at the end of the statement.

:substitute/banana/apple/e

When operating in just one file or on just one line the error is a minor annoyance but when combining the substitute command with :argdo you receive an error for every file in the args list that does not contain the pattern in question.

A typical argdo command would look like this.

:argdo s/banana/apple/gec

The flags are:

g - global

e - suppress errors

c - confirm

Check For An Executable

Sometimes Vim needs to reach outside of itself to use an existing program. For example, wiring up auto-formatting of JavaScript code requires Vim to call out to the prettier binary.

We want our .vimrc files and plugins to, generally, be as portable as possible. What happens if you haven’t yet installed a particular program? Vim will likely experience a runtime exception. One way to get around this is to check for the presence of that program on the path. If it isn’t there, don’t do the thing. We can use the executable() function for this.

if executable('prettier')
  ...
endif

It will return 1 (true) if prettier is an executable on the path, otherwise it will return 0 (false).

See :help executable() for more details.

Move window (tab) in tmux

To move a new window to the left do this:

tmux-prefix, :swap-window -t -1

To move window to the right

tmux-prefix, :swap-window -t +1

You can also bind a key to that command:

bind-key S-Left swap-window -t -1
bind-key S-Right swap-window -t +1

Now it will be tmux-prefix, Shift + Left and tmux-prefix, Shift + Right

Vim move cursor within a visual selection

When expanding a visual selection you might want to increase both sides. For instance, if you have made a selection from line 20 to line 40 you may want to expand the selection to line 18 to line 42. The visual selection can only be expanded with the cursor and the cursor is only on one side, but the cursor can be moved to the other side of the selection with o.

Read more about it with: :help visual-change.

Set filetype/settings for a specific file in Vim

Given a file with a weird extension but an underlying known filetype e.g. yaml file with a .config extension. It is possible to force vim to set the filetype to yaml for that file:

At the top/bottom of the file add a comment (with the filetypes acceptable comment syntax - for yaml it is #):

# vi: ft=yaml
baz:
  foo: 'bar'

Re-open the file and vim will automatically set the filetype to yaml for that file.

This can also be used for setting other setting such as shiftwidth, tabstop etc

I’ve been using this trick for a while but keep forgetting it the exact syntax, usually using # vim: instead of # vi:. Hopefully my wording will make it more easily duckduckgoable.

Typing card suits in vim ♢♡♣♠

Digraphs are two letter combinations that provide a easy way to type and remember non-ascii characters in vim. In insert mode you can type a digraph with Ctrl-k and then two letters that map to the unicode character.

For card suits the pneumonic is lower case c for card and upper case C for clubs, D for diamonds, H for hearts, and S for spades.

cD ♢
cC ♣
cH ♡
cS ♠

You can see all the digraphs with :digraphs.

Now you have all the necessary unicode characters to easily create card games in the terminal!

What file is this?

If you are working in an environment where the status line is not displayed and you don’t know what file you are currently editing, you can use the :file command to give you that information.

:file
"app/models/post.rb" line 43 of 139 --30%-- col 4

:f is the abbreviated version

What Is On The Runtime Path?

All of the plugins, syntax highlighting, language-specific indentation that extend the default behavior of Vim are on the runtime path. If something isn’t on Vim’s runtime path, then Vim won’t know about and as a result will not load it at runtime.

How do we see what is on the runtime path?

The rtp option is the shorthand for runtimepath. Calling set on either of these will show us the list of runtime paths, or at least some of them.

:set rtp

This will generally be a truncated list if you have a lot of plugins. To be sure you are seeing all of them, use echo instead.

:echo &rtp

See :h rtp for more details.

h/t Chris Erin

Add files with matches to the args list

If I want to change all instances of the word Apple to Orange across my entire project I can use the vim command:

:argdo %s/Apple/Orange/gc

But only if the files that have the word Apple are in the args list. To populate the args list you can use the args command in combination with shelling out to git grep with the -l flag.

:args `git grep -l Apple`

Then, when you see the list of files with :args, you’ll see all the files with the word Apple.

Netrw Special Command

Building off this previous post, today I learned a new command in Netrw: the ‘special command’.

When navigating, enter x over any file, and your operating system will open it with its tool of choice (Preview for images and Finder for directories on a Mac, etc.). This is great for exploring a directory.

From the Netrw help:

Certain files, such as html, gif, jpeg, (word/office) doc, etc, files, are
best seen with a special handler (ie. a tool provided with your computer's
operating system).  Netrw allows one to invoke such special handlers by:

        * when Exploring, hit the "x" key
        * when editing, hit gx with the cursor atop the special filename
          (latter not available if the g:netrw_nogx variable exists)

Send SQL to a Tmux Window

Hashrocket.vim provides a mapping that is awesome:

autocmd FileType sql nmap <buffer> <leader>t :<C-U>w \| call Send_to_Tmux("\\i ".expand("%")."\n")<CR>

With this command, if you’re editing an SQL file in a Tmux session, run <leader>t from normal mode, and your file will be read into the session and window of your choice. If there’s a psql session listening there, you’ve got a fast way to iterate on an SQL statement from Vim.

The command even writes the file for you before sending the SQL.

Related File in Rails.vim

Rails.vim is a staple of our Hashrocket workstations. It continues to surprise me.

Today I discovered the :R (related) command, which is a counterpart to the :A (alternate) command. :R looks for the file related to your current buffer.

This command is sophisticated. Assuming you’re following Rails conventions for file organization, :R over most templates will open the associated controller, with your cursor on the line where the action that corresponds with the view name (show for a template called show.html.erb) is defined. The same thing works in reverse; :R over the show method leads you back to show.html.erb.

Re-mark a Word as Bad

Vim’s spell checking feature is pretty great. I use it as a default for .md and .rdoc files on my machine.

Sometimes it incorrectly marks technical words like ‘ExUnit’ as bad (misspelled). To fix this, I type zg over the word in normal mode, which adds it to a list of white-listed words that are no longer considered bad.

The opposite of that command is zw, which comments out the line in the dictionary file. Run :runtime spell/cleanadd.vim to clean up those comments.

Determine the type of buffer

The :set <option> command will show the value of an option. In the case of :set buftype this is generally set to nothing. It’s empty. This is because we’re usually working with a normal buffer. The exception is the quick window. Generally, the buftype of the quick window is quickwindow. The help window also has its own buftype, help.

These values are used programmatically to help create features with vimscript.

See more with: :help buftype