Neovim journey: reveal current file in Finder

The requirement I had was to be able to open macOS Finder in the folder of the file that I was currently editing. I use this a lot when writing posts for dx13. As images live in the same folder on disk as the post they are used in, it’s useful to be able to quickly flick to the folder and drop an image there for use in a post.

The code itself is pretty simple. I’ll put it here. If you are here just for the code, you can grab it and be on your way. Put this code anywhere in your config:

vim.api.nvim_create_user_command('Rfinder',
    function()
        local path = vim.api.nvim_buf_get_name(0)
        os.execute('open -R ' .. path)
    end,
    {}
)

The interesting part is the story of how I got to the code. Exposing this functionality in Neovim was a small epiphany for me in understanding how vim is different from VS Code, my previous editor:

While Neovim is a powerful editor of itself, configuring it isn’t selecting from a static set of choices, as with VS Code, but instead a way of evolving the editor’s functionality to meet your needs. Adding small bits of function in that configuration is completely normal, unlike in VS Code where an extension is needed.

Perhaps reading through my thought processes will help this click for someone else starting out (for a second time, in my case!) with Neovim.

The story

As I mentioned, my previous editor was VS Code. You configure VS Code’s using a JSON configuration file. There are a lot of features to configure but it’s a static file. Functionality is added using extensions. While extensions are written in JavaScript, which I know decently enough, creating an extension is quite a ceremony. Adding an extension feels like a lot of work and ongoing responsibility, a feeling reinforced by VS Code’s documentation assuming that you will publish your extension. Writing small pieces of functionality like Rfinder just doesn’t feel worth the overhead.

In general, then, extending VS Code involves looking for what other people have already done. First, looking through its functionality to check you didn’t miss something that’s already there. Second, looking for an existing extension. If I couldn’t find what I wanted by this point, I’d give up.

So when I was looking to add to Neovim, I followed that pattern. I spent a while poking around Neovim’s commands, but couldn’t see anything promising. Next I looked for an extension. I found a few things, but they turned into dead ends. I nearly gave up.

But while looking for extensions, I came across a StackOverflow answer that seemed to be partly doing what I wanted. It gave me hope. But the person was just writing code right into their configuration files! What was this? Surely this wasn’t the right thing to do.

One thing that Neovim people seem to do is to put their configurations into public Github repositories. Or at least, enough of them do that it feels like a Thing. So I decided to take the time to read around three or four of these repositories and found out that in fact this was rather common!

I also found You do not need a plugin for this feature, which convinced me further that I should consider just hacking it out. Coming from VS Code’s world, this was a 🤯 moment.

So I decided to sleep on it, and indeed, after sleeping on it I decided that:

  1. I wasn’t adding anything complicated.
  2. I didn’t want to publish my code.
  3. I was happy with it being super-specific to my needs.

So I went ahead and decided to write it direct into my configuration. The only problem was that I didn’t know any of the Lua to make this happen 😕. And I didn’t want to copy/paste. I wanted to learn how to do this the hard way.

Spoiler: it turned out that it wasn’t that hard.

Writing the code

I’d spent most of my time before this epiphany copy/pasting configuration code from the web. So the first thing to do was to find out what I didn’t know. The main two references I went to were:

  1. The Neovim Lua Guide.
  2. Once I’d got a basic hang of things, the Neovim core Lua docs.
  3. I also found How to write Neovim plugins in Lua a valuable read. It showed me that writing code to extend Neovim wasn’t beyond me.

I decided I needed to define my own command. In Neovim, a command is a named thing that you type in. I figured that I’d use this rarely enough that a command would be easier to remember than a mnemonic hot-key mapping.

The Lua guide had the code I needed to define my own user command. After spending a bit more time deciding what to call my command and coming up with Rfinder (for Reveal in finder), I had this:

vim.api.nvim_create_user_command('Rfinder',
    function()
        -- ... some Lua ...
    end,
    {}
)

To do the actual revealing of the file, it didn’t take long to find out that, on macOS, open -r <filename> will reveal a file in Finder. Now I had to find out how to:

  1. Get the filename of the file I was currently editing.
  2. Execute the open command to reveal that file in Finder.

At this stage, even after reading the Neovim’s Lua guide, I wasn’t feeling that familiar with the Neovim Lua API documentation, nor Vim’s general scripting functionality. This made StackOverflow my easiest choice to work out how to get the current file name. I did make sure to read up on each function I found, rather than blind copy/pasting, which made me feel less like I was cheating.

It took some searching on StackOverflow to find the nvim_buf_get_name function. This will give you the full path to the file in the active buffer, ie, the one you are working on. Frankly it took so long to find because, to me, the name of the buffer being its full path was odd.

Thankfully, the nvim_buf_get_name takes 0 as an argument to use the current buffer, so I didn’t have to work out how to do that.

Next, how should I run the open command? Well, I went down a rabbit hole here of looking for built-in Neovim APIs for doing this. But it turned out that I could use Lua’s own function, os.execute.

Thus, as I already revealed way up top, we end up with:

vim.api.nvim_create_user_command('Rfinder',
    function()
        local path = vim.api.nvim_buf_get_name(0)
        os.execute('open -R ' .. path)
    end,
    {}
)

So now I can type :Rfinder in Normal mode to reveal the current file in macOS Finder. It’s not much code, but creating this code opened my mind to how one can customise Neovim in quite a deep way — creating new commands! — really easily, directly in the configuration files. It felt like a step change in my understanding of the editor, and I feel like I’ve somehow grown in my use of Neovim.

← Older
Crypto-mining's component distortion
→ Newer
Twenty years