Playdate Development with Neovim
I have been using Vim/Neovim as my editor for two years now, but I haven’t made an effort to dive into the topic that is LSP—Language Server Protocol. Since I also want to get into game development for the Playdate, I figured I’d marry those two topics and see how far I can get making Neovim my Playdate IDE (integrated development environment).
Others have tried that before and have shared their approach in the Neovim central thread on the Playdate forum.
Add LSP Features to Neovim
Neovim supports LSP by acting as an LSP client. I decided not to go with a one-stop shop solution like LSP Zero to learn a bit more about the underlying mechanics. There is abundant material on (Neo)Vim and LSP online, but everyone does it a tiny bit different which makes it tricky to piece everything together. My advice: Take the time to learn Lua and learn from other people’s configuration files.
Some resources that have helped me shape my understanding:
- Setup nvim-lspconfig + nvim-cmp
- Ultimate Neovim Config | 2024
- Neovim Config - Part 3 — LSP
- Config by ThePrimeagen
- Config by Treeman
After doing my research and understanding the vocabulary of the LSP landscape, I decided to go with these well-established plugins:
- nvim-lspconfig for LSP server configurations
- mason to install LSP servers
- For now, I have only installed the lua-language-server (or
lua-ls
inlspconfig
speak); I will need to improve my configuration once I want to add more language servers
- For now, I have only installed the lua-language-server (or
- nvim-cmp for autocompletion
- LuaSnip for snippets
My Neovim configuration is on Sourcehut. The other listed dependencies are necessary for plugins to play nice with each other and to extend the autocompletion capabilities.
Playdate-Specific Configurations
After working through the Playdate documentation for Lua and installing the SDK (software development kit), I wanted to add Playdate-specific settings to my LSP configuration. All possible settings can be found in the wiki of the Lua language server.
I changed my settings to achieve the following:
- Add the SDK files as a library so that types are picked up by autocompletion
- Declare
import
of the Playdate runtime as an alternative torequire
- Change
completion.requireSeparator
to use slashes instead of dots
- Change
- Make the additional assignment operators known
It was not enough to add $PLAYDATE_SDK_PATH/CoreLibs
as a library; I would get some autocompletion but not everything. For example, the global playdate
did not get picked up along with all fields as it seems. playdate-luacats is doing a much better job! In the end, I decided to add both: $PLAYDATE_SDK_PATH
(without the /CoreLibs
) to complete CoreLibs
imports, and playdate-luacats
for everything else.
2025-02-21: Compiler Plugin
The Playdate SDK comes with its own compiler pdc
. Since leaving Vim to build my code and going back into Vim to make necessary changes is a cumbersome workflow, I started to look into ways of compiling my code inside the editor. Vim offers a couple of solutions for that.
The naïve approach is running executables via Vim’s command line, for example :!pdc % build/out.pdx
where %
is a shorthand for the filepath of the active buffer. I am calling it naïve because there are two flaws: (1) the compiler output is gone as soon as I hit a key, and (2) the compiling happens synchronously, i.e. I can’t operate Vim while waiting for the compilation to finish.
The first point can be addressed by creating a compiler plugin. Vim ships with many compiler plugins out of the box—use :compiler <tab>
to cycle through them—but not for pdc
. Luckily, creating one myself comes down to only setting two options: makeprg
for the executable I want to run my code against, and errorformat
to tell Vim how to parse compiler output into the quickfix list.
if vim.g.current_compiler ~= nil then
return
end
vim.g.current_compiler = "pdc"
-- Create build folder
local cwd = vim.fn.getcwd()
local project = vim.fn.fnamemodify(cwd, ":t")
vim.fn.mkdir(cwd .. "/build", "p") -- If exists, exits silently
local errorformat = {
"%t%*[^:]: %f:%l:%m"
}
vim.o.makeprg = "pdc " .. cwd .. "/source " .. cwd .. "/build/" .. project .. ".pdx"
vim.o.errorformat = table.concat(errorformat, ",")
This code also created a dedicated build folder to keep things clean in my project directory. Now I can tell Vim to use my compiler plugin for Playdate Lua files with :compiler pdc
and run :make
to compile my code. If there are any issues, Vim will populate the quickfix list with entries that take me to the respective lines.
To solve the second nuisance—synchronous compilation that blocks me—I installed the plugin vim-dispatch. This extension adds the command :Make
which compiles my code asynchronously and automatically opens the quickfix list should there be any issues. Nice!
Recommended Resources
On Vim compiler plugins, the quickfix list, and vim-dispatch:
- Chapter Compile Code and Navigate Errors with the Quickfix List in Practical Vim, Second Edition by Drew Neil
- Part 4 Working with the Quickfix List in Modern Vim by Drew Neil
2025-02-27: More Convenience
Spoiler: I am madly in love with my current Playdate development setup in Neovim!
What did I do? First, I created an ftplugin for Lua that sets pdc
as the compiler and adds a keyboard shortcut for :Make
if there is a source/main.lua
file in the working directory. I use that—admittedly somewhat flimsy—check (derived from Playdate’s suggested project structure) to detect whether a Lua project is also a Playdate project. I might change that to checking for a pdxinfo
file at the project root.
Second, I extended my makeprg
command to include a call to the Playdate simulator after compiling the code. Thanks to the added support for Neovim’s terminal emulator via the dispatch-neovim plugin, I get all the console output of the simulator straight into my editor. Build and run at the press of a button!
