Episode #4

IDE-like Refactors, Snippets, Tests, Hover Documentation, Commenting, and Git

This episode showcases a grab-bag of features that developers accustomed to IDEs will appreciate in vim, such as how to execute the current file from within the editor, how to run tests related to the current function, how to integrate with git source control to stage and commit files, how to write and use code snippets, how to do syntax-aware commenting and uncommenting, and how to avail of automatic project-wide method name refactors using the LSP.

May 10, 2020

Show Notes


  • neovim - The vim fork I happen to use. I have no opinion on whether it's better or worse than regular Vim. This video used version 0.4.3
  • ripgrep - Faster grep/ack/ag replacement
  • diff-so-fancy - Better-looking git diff's in the terminal
  • python-language-server - The python component used in combination with vim-lsp to deliver refactoring across the project.

Vim Plugins

  • fugitive.vim - Incredible git integration with Vim.
  • caw.vim - Syntax-aware commenting in Vim.
  • unimpaired.vim - A bunch of ergonomic "bracket mappings" (e.g. [b to switch buffers)
  • projectionist - matching of "alternate" and "related" files in a project (e.g. source code with tests).
  • ALE linter - Linting you'll notice through the video.
  • vim-test - A Vim wrapper for running tests on different granularities.
  • vim-lsp - Language Server Protocol integration for Vim. Neovim will soon include a built-in LSP integration, so presumably most of the neovim community will switch away from third party plugins once the feature stabilizes.
  • UltiSnips - a snippet solution for Vim.



Transcribed by Rugo Obi

Next, I'm going to show a sort of random grab-bag of features, provided mostly via plugins, that are going to be useful for anyone who is accustomed to an IDE-like experience.

So, first off, let's execute a file from within Vim. So I'm going to edit a non-existent file, you can see at the bottom I'm typing. :e hello.py. Next I'm going to create a shebang for executing it, and that's going to be #! /usr/bin/env python. And all this will do is print hello to stdout. I'm going to repeat that line about 10 times with 10p. I'm going to save and then hit <leader>-2, my shortcut for executing the command.

In case you're curious, this command, this mapping rather is this: nnoremap <leader>2 :w<CR>:! ./%<cr> . You can see at the bottom of the screen, writes and then executes the current file.

Okay. With that done, let's move back to the library.

I'm switching buffers with b]. Now, there's a getJSON function right at the bottom. I want to look at the test for that. So I can get the test for this file using the Projectionist plugin and typing :AV. And here if you look at the left-hand pane, and the second-to-last line, you'll see /__tests__/, which is my convention for test files here and I've sort of taught Vim this.

Now, once I'm within this getJSON spec, its it block, I can run :TestNearest to execute all that. You can see that Vim opens up a little pane there and only the getJSON test is run, whereas the others are skipped. Now, that’s how to test.

The next thing that might be interesting, is to look at some documentation for a function. I'm going to call on the Language Server Protocol here, and execute the "hover: command. And there you can see a bunch of types and other information.

Now, imagine I want to do some snippets. Let's say that this line here to the end is something I see myself needing often. So I can open up the :UltiSnippet plugin. I just have to auto-complete the name after typing Ulti at the bottom and create my own snippet. So what I'm going to do is just copy what I have here. In fact, I created this earlier so I'm just gonna delete this. And let’s delete within these brackets and put the ($0) signifier here, which means the position for the cursor.

javascript snippet js "json" b JSON.stringify($0) endsnippet

Now, if I go back here, all I have to type is js. That was the first argument, the snippet on the left, and then you can see at the very bottom of the overlay, there's a new source Snips: json. And now I press 'ctrl-q' here. And bingo, I've got that snippet working.

Now, I'm going to go and close this for now.

And let's look at how Git interacts with all this. I'm going to save this file. And I'm going to save the .py file, and then run the :Git command via the Git Fugitive plugin.

And with :G, we get this sort of Git window. I can use capital P to do a git add patch. And here we see the addition of the JSON.stringify() function. Lets stage that for the hell of it. And also we see this unstaged file, I press ‘s’ to stage that. Now I press 'cc' to actually do a commit. I don't want to save this because this is some junk, I've just added. So I'm going to use ZQ to back out of all this.

One last thing worth mentioning is the Caw comment plugin.

So, you can see with the current line there, if I type, 'gcc', it will be commented using the syntax that's appropriate for the current file type. And if I type the same thing again, it will be uncommented. This also works for larger visual selections like so.

I haven't given the Python language any serious love In this screencast yet. So, let me correct that by demonstrating a mainstay in IDE development. That of renaming a function across many files automatically.

So I'm going to open up Vim and find the audio_tools.py file. Within this, there's a function combine_audio_files that I'd like to rename.

So I'm going to look at every use of this, and you can see it's being called into the splitter.py file, and then called a couple of times within it. So now I'm going to rename that function using the Language Server Protocol to combine_wav_files since that's a more accurate description of what this does. I'll give that a second. And there it's done. Now, let me visit this splitter.py file. And let's have a look for the /wav. Yes. And you can see that it's now importing combine_wav_files. And also, it is being used as such. And we can confirm that would git diff here.