Today I Learned

A Hashrocket project

315 posts about #vim

Turning off a specific linter w/ALE

Ale comes configured with a set of default linters for each filetype it might encounter.

For typescript, if eslint is available as an executable, eslint will run, lint your code and display the results in vim. To turn off eslint for typescript you can set the variable g:ale_linters_ignore in your vimrc like this:

let g:ale_linters_ignore = {
      \   'typescript': ['eslint'],
      \}

Currently, I’m going through the typescript exercism track and I want to be able to play around a little bit with the syntax and would prefer not having a typescript linter at the moment.

Repeat subs with `g&` and `:&` and `:&&`

The tricks from Vim Un-Alphabet keep coming. Repeating your substitution is a cool trick and you can do it one of 3 ways.

g& will repeat the last substitution you did, but for the whole file, whatever file you’re in at the moment.

:& will repeat the last substitution on the line you are on but you get to change the flags if you want. So you can decide now to make it global with :&g.

:&& will repeat the last substitution on the line you are on with the flags you used for that substitution.

3 very nice tricks to smooth out your workflow when making substitutions.

Jump to the last place you were in INSERT mode

I’ve known for a while that you can jump to the last place you edited with the gi command but it’s always been slightly annoying to me that gi places you into INSERT mode.

To get back to the same place, but in NORMAL mode you can use the ^ mark by typing `^. This mark is reset everytime you leave edit mode. You can see what that mark is set to with :marks ^ Shoutout to Josh Branchaud and his Vim Un-Alphabet series for teaching me a new vim trick!

Word navigation when underscores are in the word

Phil Capel posted a til recently that talked about using the _ character as a word boundary by removing it from the iskeyword list with:

:se iskeyword-=_

So now w navigates to the next underscore in long_id_for_var and you can copy long with yiw when your cursor is on long.

My addendum to this is that navigating with W will still go to the next space separated word, B will go the beginning of the previous space separated word, and if your cursor is on long, yiW will copy long_if_for_var.

Use the word under the cursor with Ctrl-R Ctrl-A

Everybody at Hashrocket has some solution for searching for the word under the cursor.

Some people created a mapping, but as I try to keep to native vim functionality as much as possible I copied the current word with yiw and then typed:

:Rg <C-R>0

Where <C-R>0 writes whatever is in register 0 to the command.

Instead, the command mapping <C-R><C-A> writes the word currently under the cursor to the command, so I can just skip the yiw.

:Rg <C-R><C-A>

Will search for the word under the cursor.

See :help c_CTRL-R_CTRL-A for more info.

:set backupcopy=yes to trigger fs events for node

While trying to get hot module reloading working with parcel today I noticed that sometimes parcel’s server didn’t register when a file was saved.

It turns out that vim has some weird behaviour around writing files that prevents node’s filesystem watcher from getting events that the file changed.

You can read about that weird behaviour here

You can get around this behaviour with:

:set backupcopy=yes

according to vim help backupcopy this will:

make a copy of the file and overwrite the original one

triggering the fs event.

UUIDs in Vim with Ruby/JavaScript 🆔

UUID’s (universally unique identifiers) are really handy. I use them for all sorts of things, like key props in a React map, and I like to have them easily accessible when writing code.

Today I wrote two Vim mappings to support this workflow. I can shell out to Ruby and use SecureRandom:

" Insert a UUID with Ruby
nnoremap ruid :read !ruby -e "require 'securerandom'; p SecureRandom.uuid"<cr>

Or, if you’re happier in the JavaScript ecosystem, here’s a similar command using Node and the uuid library. I did not have to install uuid, it was already available to me.

" Insert a UUID with JS
nnoremap juid :read !node -e "var uuid = require('uuid'); console.log(uuid.v4())"<cr>

Change up to next underscore "_" in vim

ct_ will change up to the underscore and leave it be.

cf_ will change up to the underscore and eat it as well.

Alternatively, you can use set iskeyword-=_ which will make the “_” character a valid word boundary. This might be preferable if you, like me, tend to use ciw more often than just cw.

EDIT: Because iskeyword is how the syntax highlighting is managed, you’ll probably notice that for methods/functions that contain keywords the highlighting is strange after playing with this. I found that I like nnoremap <leader>e :set iskeyword-=_<cr>diw:set iskeyword+=_<cr>i as a way to allow me to more easily edit the words I want without messing with the highlighting.

Change the CodeLens annotation color in CoC

CoC is the fanciest Language Server Protocol client for (neo)vim. It even implements Code Lens annotations, like reference counts in Javascript:

code lens reference counts

Ok neat. But by default, virtual text in neovim is in the normal highlight group. That means it looks just like code, and I’m already confused enough. After much source diving, I found that the highlight group used by CoC for this is CocCodeLens, and used like so:

:hi CocCodeLens guifg=White

code lens reference counts with better color

My code is much more readable.

Vim Mark That Test! 🔖

This week Chris Erin taught me a technique that has changed my testing game: Vim mark your current Ruby test with mA.

When writing TDD-style code in a test-crazy language like Ruby, I tend to follow a predictable workflow: create a test, watch it fail, bounce around the code making changes, and run/edit the test again. This loop can repeat for hours on a complex feature.

There are many ways you could navigate back to your test from elsewhere; a very efficient method is to mark it in Vim with mA. Like any mark, you can return to it with `A. Why ‘A’? Capitalized marks are file marks and are valid between files. With this command, you can mindlessly jump back to your test from anywhere in the jumplist stack.

Ergodox vim input for numpad keys can be wonky

I use the ergodox ez keyboard. When you’re setting it up you can select either the numpad value of a key, or the shifted/regular value. These will behave slightly differently depending on your settings for application keypad mode. This is something that is set at the application layer, so you may end up dealing with vim recieing <esc>Ok if you are trying to type +. You can either try to find the way to get your application (in my case Manjaro’s terminal emulator) to change the keypad mode (I couldn’t find that), or you can setup an innoremap section in your vimrc to cover the cases where you definitely don’t want the escape sequence.

:inoremap <Esc>Oq 1
:inoremap <Esc>Or 2
:inoremap <Esc>Os 3
:inoremap <Esc>Ot 4
:inoremap <Esc>Ou 5
:inoremap <Esc>Ov 6
:inoremap <Esc>Ow 7
:inoremap <Esc>Ox 8
:inoremap <Esc>Oy 9
:inoremap <Esc>Op 0
:inoremap <Esc>On .
:inoremap <Esc>OQ /
:inoremap <Esc>OR *
:inoremap <Esc>Ol +
:inoremap <Esc>OS -
:inoremap <Esc>OM <Enter>

This sequence is for PuTTY, but you can see which key to put after the <Esc>O by looking at what registers on keydown events. Alternatively, for ergodox users, only use the shifted/regular values for keys.

Search and Replace Control Characters In Vim

In vim, if you want to search and replace non-printable control characters (for example you have ^M scattered throughout your file and it’s messing up an import) you can use ctrl+v+<char>. The ctrl+v allows you to type the control character, but you must hold down control for both of the key presses.

:%s/ctrl+v+M/

This would search and replace all non-printable ^M characters in your text.

Pass args to a custom vim command

Custom commands are easy in vim:

:command HelloWorld echo "hello world"
:HelloWorld
" outputs hello world

But what if I want to pass an arg to the command?

First you have to specify that you want args with the -narg flag. Then you need to have declare where the args would go with <q-args>.

:command! -narg=1 Say :echo "hello" <q-args>
:Say world
" outputs hello world

fill your quickfix window with lint

File names I can’t jump to frustrate me. Today I ran $ npx eslint and my computer said “I looked at a file, and found a problem on this line, in this column. Do you want to see it? Good for you. Go type out the file name in your editor then.”

ButI wanted a jump list of all the eslint errors in my project. Eslint is a kind of compiler, right? Vim knows compilers.

:set makeprg=npx\ eslint\ -f\ unix

Now I can

:make

and behold!

:cw

I can now see all of the errors and warnings for the project, and nimbly jump betwixt.

Vim Tags in Visual Mode 🏷

This is my 400th TIL! 🎉

I’ll file this under ‘Vim is endlessly composable’. Today I learned that Vim tags can be used to define a range in visual mode. Here’s how you’d fold your code between two Vim tags.

Go to the first tag. If you marked it 1, here’s how you’d do that:

m1

Enter visual mode and extend to your second tag 2:

m2

Enter command mode and fold the range:

:fold

Which automatically extends to:

:'<,'>fold

I use this in big markdown files to hide all but the thing I’m currently working on. Enjoy.

Vim Reverse Sort

I use Vim’s :sort all the time. If I can’t have a more meaningful algorithm than alphabetical, at least my lists are in some kind of order.

This function comes in surprisingly useful while writing GitHub-style checkbox lists.

- [x] Check mail
- [ ] Play guitar
- [x] Write TIL

Sorting this list alphabeticaly puts the undone items at the top.

- [ ] Play guitar
- [x] Check mail
- [x] Write TIL

Reverse the order (in classic Vim style) with a bang:

:sort!

Pretty-Print JSON in NeoVim/Vim using jq

I’ve written here before about how to pretty-print JSON in Vim but since then I have found an even easier method using jq.

jq is an amazing command line utility for processing, querying and formatting JSON. I use it all the time when I get a response from an API request and I want to extract information or simply to pretty-print it with colors. All you have to do is pipe the curl results into jq:

curl https://til.hashrocket.com/api/developer_posts.json?username=doriankarter | jq

image

You can also use jq inside of NeoVim to pretty print a JSON string, right in your buffer using this command:

:%!jq

demo

Open the Vim Quickfix

Today I learned a new Vim command, :copen or :cope. The headline for this command is that it “open[s] a window to show the current list of errors”. The side benefit is that if you already have quickfix window in your buffers, like you would after greping the codebase, it will open or reopen that quickfix buffer.

See :help :copen for more info.

Reset a Vim Split

When I make a Vim horizontal split, the two panes are evenly sized. I then often use :resize n to make one pane larger or smaller. To restore the panes to their evenly sized split, use <vim-leader> =. In the Hashrocket dotfiles, this translates to CTRL + W =.

Send a Command's Output to Vim (Part II) ↪️

In a Hashrocket blog post, 10 Vim Commands for a Better Workflow, I wrote about :.! <command>, which replaces the output of <command> with your current line.

Today I learned what this technique is called: filtering. From the docs:

filter is a program that accepts text at standard input, changes it in some way, and sends it to standard output. You can use the commands below to send some text through a filter, so that it is replaced by the filter output.

An even shorter version is just !!<command> in normal mode. A use case for this would be writing docs, where command-line output (ls, curl, etc.) can help demonstrate an idea.

Check out :help !! to see all the permutations of this interesting command.

Go to next ALE error

Has ALE overtaken your vim setup like it has mine? It constantly runs linters, compilers and formatters, just waiting for you to slip up so that it can put an X in the gutter.

Those X’s are really quite handy. They generally point me to the next place in the code that I need to make a change.

To get there quickly you can goto the next ALE error with:

:ALENext

This will stop at the last error in the file though. To have it wrap around use:

:ALENextWrap

I really enjoy vim-unimpaired’s handy bracket mappings, but I don’t use ]a that move between args (because I don’t use args very often).

To setup my own handy bracket mappings for ALE:

:nmap ]a :ALENextWrap<CR>
:nmap [a :ALEPreviousWrap<CR>
:nmap ]A :ALELast
:nmap [A :ALEFirst

Open FZF Result In A Split In Vim

The fzf.vim plugin allows you to do speedy fuzzy searches for filenames and line-by-line content.

Once you’ve narrowed down the results and found what you’re interested in, you can hit <enter> and a new buffer will open over what was already in the window. You can also open that file as a split.

Hitting Ctrl-x will open the file under the cursor as a horizontal split.

Hitting Ctrl-v will alternatively open that file as a vertical split.

Edit A File Starting On The Last Line

Generally when you start editing a file whether as a new Vim session (vim file.txt) or in an existing Vim session (:e file.txt), your cursor will be positioned at the beginning of the file.

You can start editing a file with the cursor positioned at the end of a file using an edit command — include + with no line number. This may be useful for a large file or even if you just know that you’ll be adding content directly to the bottom of the file.

If you are starting a new Vim session:

$ vim + file.txt

or if you are already in a Vim session:

:e + file.txt

See man vim or :h +cmd for more details.

Edit A File At A Specific Line Number In Vim

I use long-running Vim sessions where I constantly open files as new buffers with the :edit (or :e) command. Generally, when I open a new file I end up with the cursor at the top of the buffer and go from there. But what if I have a specific line number in mind and I want the cursor to start there?

The :edit command can receive arguments, including a line number argument. To open up to line 159, I can include the +159 argument in the command.

:edit +159 path/to/the/file.txt

See :h :edit and :h +cmd for more details about how :edit works and what the different arguments can do.

Search Backward Through A File

There are a number of ways to search for a match in a file. One I use quite often is hitting * while the cursor is over the word I want to find matches for. It searches forward jumping to the next occurrence of that word.

It turns out there is a way of doing the same thing, but searching backward to the previous occurrence of the word. If you hit # with the cursor over a word, it will jump backward through the file until it finds an occurrence of that word. Keep hitting # to keep searching backward.

See :h # for more details.

Clear Out The Jump List In Vim

Vim uses a jump list to track all they jumps you’ve made during a session. Vim can even be configured to keep a record of those jumps between sessions. This is really handy for a long-lived project, but what if you want those jumps cleared out?

You can clear them out for the current and subsequent windows using :clearjumps. The jump list for existing windows will be unchanged and once you start a new session, the full jump list will be restored.

See :h :clearjumps for more details.

Navigate To The Nth Column On A Line In Vim

You can navigate the cursor to a specific column of the current line using the | character. For instance typing

45|

will navigate your cursor to the 45th column of the current line. If you type a number that exceeds the number of columns on the line, your cursor will be placed on the last column.

Here is what the help files have to say about |:

|           To screen column [count] in the current line.
            |exclusive| motion.  Ceci n'est pas une pipe.

Change DOS to Unix text file format in VIM

The last few times I had to use a .txt file as an input, I’ve run into difficult-to-troubleshoot parsing errors. What could be wrong? I check and re-check the text file, and I can’t find any mistakes!

If the text file came from a Windows machine, this may due to a difference in line-ending between DOS and Unix text files. You can check and change this file format in Vim.

Open your file in Vim and, in normal mode, type :set ff? to see what the file format is. If it is DOS, then type :set ff=unix to change it to Unix.

Quit When There Is An Argument List

To start a Vim session with multiple files in the argument list, name multiple files when invoking Vim:

$ vim README.md LICENSE

The first file in the argument list, and the current buffer, is README.md. The last file in the argument list is LICENSE.

At this point if you try to quit, Vim will prevent you saying 1 more file to edit. If we look at the docs for :q and :wq, we see something along the lines of:

This fails when the last file in the argument list has not been edited.

Vim wants to ensure that you’ve paid attention to every file that you loaded up into your argument list. If you’d like to quit regardless. then this is where the :q! and :wq! variants come in handy. This commands will skip the argument list check.

Set Vim Filetype or Syntax with Modeline

Here’s a problem I faced today: I’m writing a Thor CLI. The convention for that project is a file called cli, written in Ruby but with no .rb extension. Vim highlights it and otherwise treats it like a conf file, but it’s Ruby. I want my editor to progamatically recognize that and act accordingly.

One solution is Vim’s modeline. With modeline enabled, either of these settings will enable syntax highlighting on buffer read. They can both be used at the same time as shown.

# vi: syntax=ruby
# vi: filetype=ruby

def ruby_here
end

Using vim-surround With A Visual Selection

The vim-surround plugin allows you to do a variety of actions that have to do with the surrounding characters of text objects.

The S keystroke allows you to surround a visual selection with the following character.

First, make a visual selection. Then hit S. Then hit a surround character such as ( or [ and the area of text that has been visually selected will be wrapped with the respective surround characters.

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.