Today I Learned

A Hashrocket project

276 posts about #vim

Jump Back To The Latest Jump Position In Vim

Any sort of jump motion, such as gg or 121G, will be recorded in your Vim session’s jump list. If you’d like to return to the latest position in your jump list, you can tap '' within normal mode.

You can then tap '' again and you’ll be returned to the position you were just at. This is great if you want to toggle back and forth between your current position and the previous position.

If the latest jump position is not within the current buffer, then you will be jumped to the initial cursor position at the top of the file.

You can learn more about this by reference :help jump-motions, :help jumplist, and :help ''.

h/t Jake Worth

Jump Back 🐇

A Vim motion I’ve really been loving lately is '' or ``, which returns you to the previous jump position. It references the previous jump, so using it twice in a row will toggle you back and forth between two locations.

I use this a lot after jumping to the next occurance of a word with *, and then changing that word in some way so I can’t return with *. If you’re using normal mode most of the time, this command can help you walk back through your thought process.

For more info:

:help ''

Restore from Diff in Vim

I often use diffs in Vim to compare my current file with the previous Git commit. vim-fugitive is one way to do this, via commands like :Gdiff or :Gvdiff (vertical option).

From this diff state, we can restore the code from one pane to the other using the command :diffput. This is useful for restoring one line, or a range, from the previous commit to my working copy.

Using vimscript lambdas with map

Vimscript has lambda functions, check them out with :help lambda.

You don’t have to reference an argument with the a: prefix and returns are implicit.

Generally they take the form of:

:let Add = {a, b -> a + b}
:echo Add(1, 2)
 3

You can also pass a lambda to the map func:

:let fruits = ['apple', 'orange', 'banana']
:echo map(fruits, {key, val -> "key: " . key . " val: " . val})
['key: 0 val: apple', 'key: 1 val: orange', 'key: 2 val: banana']

Alwyas be careful with vim’s map() though. It Mutates

To make sure you don’t mutate the list you are mapping over, use copy().

:echo map(copy(fruits), {key, val -> "key: " . key . " val: " . val})

What is that chäräcter ?

If you are in vim and you encounter a weird looking unicode character, and you want to know what the codepoint(s) for that character is/are you can use ga

If my cursor is over ä and I use ga I get output:

<ä> 228, Hex 00e4, Octal 344

If my cursor is over and I use ga I get output:

<a>  97,  Hex 61,  Octal 141 < ̈> 776, Hex 0308, Octal 1410

The first character is a precombined Unicode character, the a with the umlaut already has its own unicode character.

The second character is an a combined with a diacrital mark, and the output of ga reflects that by listing each utf codepoint that composes the character.

The five values that define cursor position in vim

We generally think of a cursor having two coordinates, x and y, row and column, but when I call getcurpos() I get a list with 5 values in it.

:help getcurpos()
:echo getcurpos()
[0, 4124, 8, 0, 57]

Here are the definitions of those numbers:

bufnum - the number of the buffer when calling getpos("'A") to get the position with a mark. Always 0 with getcurpos()

lnum - The line number

col - The number of chars used to go this far to the right. is 2. 10 spaces is 10.

off - Is the number of chars past the end of the line. 0 unless using virtualedit.

curswant - Is the column you started on when starting to navigate with j and k. You might start on col 20, and go down to a line with only 10 columns in which case the cursor would be on col 10, but curswant would still 20. The next navigation to a line with more than 20 characters would put you back on col 20.

With virtualedit turned on curswant will always be the column position of the cursor.

Sort numerically

The sort command :sort in vim sorts by the string representation by default. For numbers this is weird. If you have 2, 12, and 1 on separate lines they end up sorted like this:

1
12
2

To sort them numerically you can pass the n argument to the sort command, like this :sort n which gives you what you want:

1
2
12

Making virtualedit a local option

virtualedit is a global option. This means that when you call setlocal virtualedit it’s not set just for this buffer but for all windows and all buffers. This is confusing, but only options labeled local to buffer in help can be set to the local buffer. virtualedit is labeled as global.

However there is a hack. You can simulate this as a buffer local option by setting it on BufEnter and unsetting it on BufLeave.

In my .vimrc.local I have

autocmd BufNewFile,BufRead,BufEnter *.part setlocal virtualedit+=all
autocmd BufLeave *.part setlocal virtualedit-=all

Set it when you enter, unset it when you leave, and now the setting is confined to the buffer you’d like to use it in.

Print The Relative Path Of The Current File

As a project grows and there are many directories and long path names, Vim will not always have enough room to display the full relative path of the current file. It may have to cut off the initial directories which can make it hard to know where you are.

You can hit Ctrl-g to quickly display the relative path of the current file below the status bar.

example of using ctrl-g

See :help Ctrl-g for more details.

Aborting Git Commits And Rebases Within Vim

When you are amending a commit or doing an interactive rebase of a series of commits, Vim will be open to a buffer full of content related to the respective action. Normally, you’ll make some changes, save the buffer, and then quit — Git will take over from there by processing the commit or rebase.

What if you find yourself in this situation and you want to cancel the commit or rebase? Simply quitting with text already in the buffer will be interpreted by Git as a signal to go ahead and process the commit/rebase.

So, how do we quit without confirming the action?

Vim allows you to quit with an error code.

:cq

This means that irrespective of the content of the buffer, Vim will signal to Git with an error code to not process the commit or rebase, effectively aborting the action.

See :help cq for more details.

Preserve whitespace while joining in Vim

I have this text:

abc
   def

There are 3 spaces on the line before “def” and I want to make sure that space is preserved.

I usually join with J in normal mode. There is, however, a :join command that behaves the same way except you can follow it with an optional !.

By using the abbreviation it is shortened to:

:j!

And the result I get is:

abc   def

Space preserved!

See :help :join for more information.

For When That Escape Key Is Hard To Reach

Using Vim to its full potential requires using just about every key on the keyboard. Of all those keys, the escape key is particularly important. It’s how you get back to normal node.

If you find the escape key hard to reach or your laptop vendor decided you don’t need it anymore, what’s the alternative?

There is a built-in alternative:

Ctrl-[

Note: If your <Esc> key is hard to hit on your keyboard, train yourself to use CTRL-[.

See :help Ctrl-[ for more details.

source

Vim: Peek horizontally without moving the cursor

Some people like wordwrapping in Vim. Personally I find it annoying for code because it can destroy the meaning of the code in the context that it was formatted in by the original author, making it hard to understand where one line begins and ends.

Turning wordwrap off though, can put you in a situation where you have a line of text slightly longer than the screen can show, and you may want to scroll a bit to the right/left for reference but not move your cursor and lose your position with ^ or $.

Vim allows you to do that with zs and ze mappings.

DEMO demo

Learn more with :h zs or :h ze.

Execute command from register

Here’s a cool command

:nmap <leader>s :smile<CR>

If you read the above in vim, how would you execute it? You can use the system buffer or tmux copy+paste and paste it to the command line, but that seems very not-vim.

Vim has a :@ command that will execute registry contents. Just put the line into your registry with yy and then call:

:@"

This executes the contents of the default register

`yo` has been replaced by `]op`

vim-unimpaired is a fantastic vim plugin full of utility mappings, many to do with toggling. I have relied heavily on yo to enter insert mode with the paste option set when I want to use the system buffer for pasting.

Recently, the yo mapping has been repurposed. Read the github issue here.

Instead, ]op is the new mapping to put you in insert mode with the paste option set.

The documentation now looks like this:

                                                *[op* *]op* *yop*
A toggle has not been provided for 'paste' because the typical use case of
wrapping of a solitary insertion is inefficient:  You toggle twice, but
you only paste once (YOPO).  Instead, press [op, ]op, or yop to invoke |o|,
|O|, or |0||C| with 'paste' already set.  Leaving insert mode sets 'nopaste'
automatically.

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.