Tips for using NeoVim as devtool - 1

As a developer, one of the most important things is to find the right tool to write code with. To me, that means this tool needs to be lightweight, portable, and most importantly reliable.

Why VSCode is not for me

VSCode, aka Visual Studio Code, is a code editor developed by Microsoft. It is based on top of the Atom editor that was previously developed by GitHub. As a full stack developer whose main language is JavaScript/TypeScript, I was told to use VSCode when I joined the company. However, as a long-time vim user, I still use the vim mappings when I am using VSCode. This is all done through plugin/extensions. This seems to be okay at first, but over time, I really find VSCode annoying to use and I simply can't tolerate it.

The first thing is that contrary to most popular belief, VSCode is NOT an open source product. Yes, part of the source of VSCode is open-sourced, but the VSCode you downloaded from Microsoft website is not. They have put some proprietary stuff into the end product and I'm not a big fan of that. There are ways to get around this, namely, there are several truly open source edition of VSCode, the one I used to use is called VSCodium, but then you will lose access to the extensions market provided by Microsoft.

Another thing that annoys me is that VSCode or any product that based on VSCode is just too slow and resource intensive -- they are just an electron app running on another Chromium based browser. Having one Chrome is enough for me, so thank you Microsoft (I tried to avoid electron based apps as much as possible, for example, I use web version of Teams, rather than the app one)

How NeoVim/Vim fits in

I had been a long-time vim user, I first began using vim when I was studying computer science at the university. But I had never tried to use vim as my daily tool for software development before 2021. In February of 2021, we had a new dev joining my team. To my surprise, he was using vim (more precisely NeoVim) as his daily driver. This inspired me, and I started to explore ways to use VIM as well. At the time of this writing, I have been using NeoVim as my daily driver for more than a year now. To me, the experience has never been more pleasant. So I would just like to share a few tips on how I use it daily.

Run tests without leaving vim

Suppose you currently have a test file opened in you vim, and you would like to run all the tests within this file, what would you do?

I think most people would open a new window or a new split in Tmux and start the command yarn test filename. At least that was what I was doing when I first encountered this problem. Well, obviously, this is not the ideal solution, because this would be really interruptive, you would have to switch between either a different terminal or a Tmux window.

After several days of using solution 1, I was really tired of it. Switching between different terminal is just too slow and interruptive. So this is when I come to the second solution: in normal mode, use :!yarn test %. Okay, this works, I don't have to leave VIM each time I want to run the test. But now the problem is that running this command is blocking, meaning you can't edit the file while the test is running. This is fine if the test is not long or you are just running a single test using it.only or something like that. But if the text is long enough, it might be 2 or 3 minutes before you can do anything again, which is quite annoying to me.

Starting from Vim 8.0, Vim adds a built-in terminal mode, which is quite handy to use. Once I found this out, I started to use :sp term://yarn test %, this will create a new horizontal split and start a terminal that runs yarn test %, while the tests are running, you can still do whatever you want to do and will not be blocked by the testing itself.
Once the command is finished, you will see the command exit in that window.

This solution is almost perfect, except I use this command too frequently that typing this whole command becomes a pain. So I had to find some way to improve my experience. And this all comes down to mapping this command to something shorter (at least that was what I was thinking at first). So I added the following to my vimrc file cmap rt!! !yarn test % <CR> this saves lots of keystrokes, but still, I need to type it every single time I want to use it. I still feel there's space for improvements.

Final Solution: Key mapping

Based on function 3.1, I started to think if I can map the command to a leader key, it turned out I definitely can, with this map <buffer> <silent> <nowait> <Leader>rt :execute 'sp term://yarn test %'<CR>


There are several caveats with the above solution.
First, this mapping will exist in every file type which is not ideal, cause you don't want to use yarn to test a GO file for example. Therefore, I moved this command to a separate file under the ftplugin folder and call it typescript_mappings.vim.
Second, you will always have to locate the test file before you can run the test. This is not a deal breaker, but it is still not perfect. Usually you would refactor a simple function within a file and all you care about is whether the corresponding tests passes, you don't necessarily need to edit the file or care about where the file is. This prompts me to turn this whole thing into a vimscript function
Here goes the function I created:

map <leader>T :Ft<CR>

if exists('*FindTest')
command! -nargs=? Ft call FindTest(<q-args>)
fun! FindTest(...)
set path+=**

let fileName = expand('%:t:r') . '.test.ts'
execute 'tabnew '
execute 'find '. fileName
execute 'split term://yarn test '. fileName . ' --watch'

This function first set the look path to all the directories recursively, then it expand to the filename excluding the extension, then add .test.ts to it. After that, it opens a new tab, open the test file and then create a terminal, yarn test that file in watch mode.

I put this file under the ftplugin again, but one issue is that because the both the file found and the current file are of the same file type, this means the script will be sourced twice. So I had to put the if exists check. I map this command to T but you can tailor that to your liking.

Update Oct 2022: If you like this article, you may also want to check the second article in this series here, it is about scratch buffer in vim.