The day I chose Lua over Javascript

Amir Bilu
ITNEXT
Published in
4 min readDec 19, 2022

--

I’ve been working at Tabnine for the past two years.

Tabnine is an awesome AI code generation that works directly in your IDE, providing inline, whole-line, full-function code completions.

While Tabnine works with several IDEs, it didn’t support full-function completions in Vim (Neovim, to be precise), which is my IDE of choice.

I was somewhat uncomfortable working on a product that I couldn’t experience on a daily basis — so I decided to make full-function completions available in Vim.

Getting started

Having worked on our VS Code & Intellij plugins, I knew that first I needed to make sure that virtual lines API exists in Neovim.

I googled and found this.

Great!

While I’ve been working with Vim for many years now — and have a pretty decent configurations set — I’ve never really developed something for it — I mostly copy-pasted configurations of other plugins into my init.vim file.

So where should I begin?

I knew from past experience (mostly from using other plugins) that Neovim plugins are written in Lua or Viml.

Viml doesn’t look like anything, so it was immediately off the table, and I’ve never coded in Lua..and the syntax looked a little…meh.

Hoping I could avoid using Lua, I searched online for other solutions.

This was the first thing that caught my eye.

A node client for Neovim! Boom, I couldn’t have hoped for more. My initial thought was that I’d fork the tabnine-vscode project (written in typescript) and quickly make the adjustments for Neovim. I figured it would take me 2 days, tops!

1 hour later, and I was already frustrated. There was almost no documentation for even the simplest things. I barely managed to run the most basic example.

I tried searching for Neovim plugins written with this SDK, to get some idea of how to start. There were practically none. I looked at the node-client repo — it was barely maintained. But I wasn’t ready to give up yet.

After spending a few hours trying to adjust tabnine-vscode plugin, I managed to get some basic configuration to run. But it still felt unbaked. However, with no documentation or examples out there, I was stuck. I realized I was on the wrong path.

I searched Neovim’s GitHub page, where I found pynvim — a Python client for Neovim. Great! People love Python! I assumed there’d be a gazillion Python plugins. Wrong. To my surprise, there were almost none.

Okaaaaay, I get it, Neovim. I’ll learn Lua!

Accepting my fate

According to Lua.org, Lua was built to be a general-purpose embedded programming language that’s designed to support procedural programming with data-description facilities.

The Neovim runtime for Lua is pretty straightforward, so I get why devs choose it.

Global Vim object

Nevim exposes a global vim object, to interact with its API, e.g:

vim.fn.col(“.”) to get the current column under the cursor.

or vim.api.nvim_buf_set_text to add text into the buffer.

The documentation of this API is great, and can be easily searched with :help command.

I’ve also learnt that while Lua itself misses simple operations like array.map or string.split (or tables in the Lua jargon), Neovim exposes them, for example vim.tbl_map or vim.fn.split.

Simplicity

Lua misses curly scopes, which I tend to dislike in programming languages. But I’ve learned to love its simplicity. Syntax resembles other high level languages — as opposed to Viml.

There’s no fancy/complex syntax that you need to get used to, shortening the learning curve.

Basically, Lua does its job, which is to be embedded in other platforms, perfectly.

Lua modules

  • Lua modules resemble Javascript commonjs modules
  • Vim has a notion of runtime path, a list of directories where it looks for modules in runtime
  • Vimplug (and other plugin managers) loads plugins into the runtime path for you
  • When a Lua module is within the runtime path, it can be loaded easily by using require(‘module-name’) inside your init.vim/init.lua

Documentation

Vimdocs doss a great job covering all the Lua functions under the Vim global object.

Lua documentation is also pretty good. The site look something from the 90s, but the content is decent.

Summary

I can definitely understand why everyone chooses Lua when writing Neovim plugins.

It feels like the node & Python clients are just unsuccessful experiment of the Neovim team to make plugin development easier, while Lua works exactly the way it was meant to.

Plus, when you get over the learning curve of learning a new language, it’s even simple, and I dare say — joyful!

Bottom line: Go for Lua when you develop Neovim plugins. You may even enjoy it 🙂

Tabnine for neovim — give it a try! https://github.com/codota/tabnine-nvim

--

--

Senior Software Engineer @Tabnine, vim lover, handstand expert, and using arch btw