github github
stars 279
issues 10
subscribers 6
forks 4



4 months ago

🍲 Hotpot

You take this home, throw it in a pot, add some broth, some neovim... baby, you got a stew going!

~ Fennel Programmers (probably)

Hotpot is a Fennel compiler plugin for Neovim. Just (require :my-fennel) and Hotpot does the cooking for you 🍻. Seamlessly mix and match Fennel and Lua as little or as much as you want. Your Fennel code is only compiled when it (or a dependency such as a macro) are changed and everything is stored in a bytecode cache for super fast startup time. It can do ahead of time compilation too :floppy_disk:.

;; ~/.config/nvim/fnl/is_neat.fnl
;; put your fennel code in fnl/
(fn [what] (print what "is neat!"))
-- and require it like normal in your lua file
local neat = require('is_neat') -- compiled & cached on demand
neat("fennel") -- => "fennel is neat!"

New in 0.5.2! 🍩 Improved diagnostics! 🍾 Compiler plugin support!



  • Neovim 0.7.2+
  • Fanatical devotion to parentheses.


Hotpot can be installed via any package manager but you may prefer to manually install it and let your package manager update it. This allows you to configure your package manager with Fennel.

You must call require("hotpot") before you attempt to require any Fennel files. If you do not do this manually, Neovim will call it for you but the order and time that this occurs can be non-deterministic.

If you only want to experiment with Fennel, adding rktjmp/hotpot.nvim to your plugin manager is probably good enough.

-- ~/.config/nvim/init.lua

-- This init.lua file will clone hotpot into your plugins directory if
-- it is missing. Do not forget to also add hotpot to your plugin manager
-- or it may uninstall hotpot!

-- Consult your plugin-manager documentation for where it installs plugins.
-- packer.nvim
-- local hotpot_path = vim.fn.stdpath('data') .. '/site/pack/packer/start/hotpot.nvim'
-- paq.nvim
local hotpot_path = vim.fn.stdpath('data') .. '/site/pack/paqs/start/hotpot.nvim'

if vim.fn.empty(vim.fn.glob(hotpot_path)) > 0 then
  print("Could not find hotpot.nvim, cloning new copy to", hotpot_path)
  vim.fn.system({'git', 'clone',
                 'https://github.com/rktjmp/hotpot.nvim', hotpot_path})
  vim.cmd("helptags " .. hotpot_path .. "/doc")

-- Enable fnl/ support

-- Now you can load fennel code, so you could put the rest of your
-- config in a separate `~/.config/nvim/fnl/my_config.fnl` or
-- `~/.config/nvim/fnl/plugins.fnl`, etc.
-- example using paq.nvim
require "paq" {

The nightly branch merges Fennel HEAD into Hotpot each day.

The main purpose of this is to run the test suite against upcoming releases. If the test suite fails, the changes will not be merged, so it should be reasonably stable to use day-to-day.

Because the nightly branch's primary purpose is to run tests, there is no guarantee that it wont be recreated, renamed or force-pushed onto at some point in the future, which would require you do manually force pull or create a fresh clone.

For a preview of upcoming Fennel features, you can view the changelog.

Windows installations may require additional setup depending on your account privileges.


Hotpot accepts the following configuration options, with defaults as shown.

You do not have to call setup unless you are altering a default option.

See h: hotpot-setup for more details.

  -- allows you to call `(require :fennel)`.
  -- recommended you enable this unless you have another fennel in your path.
  -- you can always call `(require :hotpot.fennel)`.
  provide_require_fennel = false,
  -- show fennel compiler results in when editing fennel files
  enable_hotpot_diagnostics = true,
  -- compiler options are passed directly to the fennel compiler, see
  -- fennels own documentation for details.
  compiler = {
    -- options passed to fennel.compile for modules, defaults to {}
    modules = {
      -- not default but recommended, align lua lines with fnl source
      -- for more debuggable errors, but less readable lua.
      -- correlate = true
    -- options passed to fennel.compile for macros, defaults as shown
    macros = {
      env = "_COMPILER", -- MUST be set along with any other options
      -- you may wish to disable fennels macro-compiler sandbox in some cases,
      -- this allows access to tables like `vim` or `os` inside macro functions.
      -- See fennels own documentation for details on these options.
      -- compilerEnv = _G,
      -- allowGlobals = false,


See the Hotpot Cookbook for guides and examples about Hotpot Reflect, using the API, ahead of time compilation, commands, etc.


Hotpot provides a number of functions for evaluating and compiling Fennel code, including helpers to easily operate on strings, selections and buffers for example.

For complete details, see :h hotpot-api and Using the API.

How does Hotpot work?

Hotpot has three main systems, the lua cache, the bytecode cache and the module loader.

The lua cache contains our compiled fennel code. When requiring a fennel module, we must first compile that fennel code into lua, then save the result to disk. This allows the user to easily view the result of the compilation for debugging. See :h hotpot.api.cache.

The bytecode cache is a special file (normally called the index), loaded into memory when Neovim starts. It contains the machine readable code for every module that Neovim has previously loaded. By caching modules in-memory and in a machine readable format, we can find and resolve modules very quickly as most of the "heavy lifting" is already done. By maintaining a bytecode cache we can achieve up to 15x performance increases.

The bytecode cache contains information about when the cache was created for each module, so any modifications made to the original source files or dependencies can be detected and reloaded into the cache.

The module loader will find and load lua (or fennel) modules. First it will search the index and then Neovims runtime path for source files that match the requested module name. If a source file is found, it is compiled to lua (if needed), then the bytecode is saved to the index, then the module is returned to the user.

As an example, given require("my.module") Hotpot will check the following locations, in order, and return the first match.

  • index
  • $RUNTIMEPATH/lua/my/module.lua
  • $RUNTIMEPATH/lua/my/module/init.lua
  • $RUNTIMEPATH/fnl/my/module.fnl
  • $RUNTIMEPATH/fnl/my/module/init.fnl
  • <package.path>/my/module.lua
  • <package.path>/my/module.fnl

You can see that it will preference a bytecode cache, then .lua files over .fnl, if they exist.


Hotpot must be able to create symlinks for some core functionality which Windows may disallow by default, depending on your account type and Windows version.

To enable symlink creation without elevated privileges, you may have to enable "Developer Mode" in your account settings.

See "Enable your device for development" and "Symlinks in Windows 10".


Hotpot embeds fennel.lua, see lua/hotpot/fennel.lua for licensing information.