I’ve been doing Advent of Code this year in Fennel.
(I’m updating my answers every day in a GitHub repo.)
I use a few different computers day to day and I need to setup each computer with
the proper Lua libraries and Fennel compiler.
For speed, I want to run the Fennel compiler under LuaJIT and I also want to have
access to the inspect.lua
library for debugging.
I got everything working alright on MacOS but on Linux, Fennel wasn’t detecting
luarocks libraries.
That’s when I realized I don’t actually need an extra program to compile and run Fennel.
I already have everything I need built directly into Neovim.
Running Fennel scripts through Neovim gives me access to all of Neovim’s builtin APIs.
This is a fantastic environment for debugging and development since a lot of
helper functions already exist. For example vim.inspect
implements the same
functionality as inspect.lua
and Neovim even provides a short hand for
printing vim.inspect
’s results using vim.pretty_print
.
I already partly setup Neovim as a Fennel compiler in a previous blog post.
The implementation I originally came up with worked well for compiling a file to output
but what if I want to write that output to stdout instead?
Naively, I initially thought I could just use the print()
function.
command("FnlCompile", function(t)
if debug.traceback ~= fennel.traceback then
debug.traceback = fennel.traceback
end
local in_path, out_path = unpack(vim.fn.split(t.args, " "))
assert(in_path, "missing input path")
local stream = open_stream(in_path)
local out = fnl_compile(stream)
if out_path ~= nil then
local file = assert(io.open(out_path, "w"))
file:write(out)
file:close()
else
-- if no file path is provided lets just print the contents
print(out)
end
end, { nargs = 1 })
FENNEL_COMPILE=true nvim --headless -c "FnlCompile test.fnl" +q
But then I ran into an issue: print
doesn’t print to stdout when this command is run.
No matter how I redirected the output, I couldn’t get
the it to print to stdout.
A quick search, helped me find this GitHub issue.
I found that, you have to explicitly write your output to /dev/stdout
.
My fix was to create a helper function for printing to stdout.
local function print_stdout(message)
message = vim.fn.split(message, "\n")
vim.fn.writefile(message, "/dev/stdout")
end
Now I can print the results of compilation directly to stdout.
I also need to setup a way to run individual Fennel scripts in the Neovim environment. The Fennel compiler API includes a function for evaluation of Fennel files. I can expose this function using a simple Neovim command.
command("FnlRun", function(t)
local in_path = unpack(vim.fn.split(t.args, " "))
assert(in_path, "missing input path")
local out = fennel.dofile(in_path)
-- print an extra newline to seperate script output from from the printed return values
print("\n")
print_stdout(vim.inspect(out))
end, { nargs = 1 })
Now I can run a Fennel script using:
FENNEL_COMPILE=true nvim --headless -c "FnlRun test.fnl" +q
This provides a very nice DX. My goal for using Fennel in Advent of Code is to practice writing future Neovim plugins. I no longer have to setup Fennel, Lua, or luarocks. I have my entire compile and run cycle going through Neovim which I already have installed on all my systems.