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 that allows you to write your Neovim config and plugins in Fennel.
[!IMPORTANT] Hotpot version 2's configuration is incompatible with version 1 (née version 0). For most users, the migration should be simple, see migrating from version 1.
The most dramatic change to all users is the requirement that all macro files must use the extension
.fnlm.If you are unable or do not want to update your configuration, pin your plugin manager version to the
v1.0.0tag.Version 2 has a simpler configuration, better support for directories such as
lspas well as improved support working in multiple project directories with isolated configuration.
[!WARNING] Again, The most dramatic change to all users is the requirement that all macro files must use the extension
.fnlm.
~0.10.x+ but it's untested._G.__hotpot_disable_version_check = true before require('hotpot').vim.pack-- init.lua
vim.pack.add({
{src = "https://github.com/rktjmp/hotpot.nvim",
version = vim.version.range("^2.0.0")}
})
require("hotpot")
-- Most users will then require their "config" module stored
-- somewhere like `fnl/config/init.fnl`...
require("config")
Avoid lazy-loading Hotpot unless you are only using it for plugin development.
Hotpot performs the minimum amount of work on demand internally. Users
configuring vim.pack.add's behaviour via its options table should read the
advanced vim.pack configuration
notes. Most users should use the above instructions.
-- init.lua
local function ensure_installed(plugin, branch)
local user, repo = string.match(plugin, "(.+)/(.+)")
local repo_path = vim.fn.stdpath("data") .. "/lazy/" .. repo
if not (vim.uv or vim.loop).fs_stat(repo_path) then
vim.notify("Installing " .. plugin .. " " .. branch)
local repo_url = "https://github.com/" .. plugin .. ".git"
local out = vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"--branch=" .. branch,
repo_url,
repo_path
})
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone " .. plugin .. ":\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
return repo_path
end
-- Install hotpot in the same manner as lazy.nvim, into Lazy's own plugin directory.
local lazy_path = ensure_installed("folke/lazy.nvim", "stable")
local hotpot_path = ensure_installed("rktjmp/hotpot.nvim", "v2.0.0")
-- As per Lazy's install instructions, but also include hotpot as
-- we have installed it to lazy.nvim's managed directory outside of Neovim's
-- runtimepath.
vim.opt.runtimepath:prepend({hotpot_path, lazy_path})
-- Important! When using Lazy.nvim you *must* require the hotpot module
-- before Lazy.nvim to ensure the module is loaded into memory prior to
-- lazy.nvim altering neovims behaviour.
require("hotpot")
-- Most users will then require their "config" module stored
-- somewhere like `fnl/config/init.fnl`...
require("config")
You must also include Hotpot in your plugins list for Lazy.nvim to correctly manage updates. It is recommended you do not aplly any lazy loading methods to Hotpot.
;; fnl/config/init.fnl
(let [lazy (require :lazy)
api (require :hotpot.api)
context (assert (api.context (vim.fn.stdpath :config)))]
(lazy.setup
;; Define your package specs in any way you like, this is just an example.
;; Be sure to include hotpot, as calling `lazy.setup` interferes with lua module
;; loading, which will cause hotpot to malfunction when loads further parts
;; of itself on demand.
{:spec [{:url "https://github.com/folke/lazy.nvim" :branch :stable}
{:url "https://github.com/rktjmp/hotpot.nvim" :version "^2.0.0"}]
;; You must include hotpots output directory in `performance.rtp.paths`!
:performance {:rtp {:paths [(context.locate :destination)]}}}))
;; If you wish to use `Hotpot fennel update` to download fennel new versions of
;; fennel from the internet, you will also have to add the `target directory`
;; path, as listed in `:checkhealth hotpot` under the `Hotpot fennel update`
;; section.
;;
;; If you have configured your config directory to use `:target :colocate`
;; (which is *not* the default), you may skip the `performance.rtp.paths` step.
~/.config/nvimAnything you would put in lua/ should be put in fnl/, otherwise, put .fnl
files in the standard runtime directories such as lsp/ or ftplugin/. Saving
.fnl files will sync any updates required to any .lua files.
;; ~/.config/nvim/fnl/my-config/hello.fnl
(print :hello)
;; ~/.config/nvim/lsp/my-lang.fnl
(print :setup-some-lsp)
By default for Neovim's config directory, Hotpot stores any compiled .lua
files in a separate location to maintain a clean directory tree, so you won't
see any .lua files.
There is one special exception to the above: .config/nvim/init.fnl will
always compile to .config/nvim/init.lua.
See the commands for some helper commands.
To configure some of Hotpot's behaviour such as colocating .lua, ignoring
files or configuring the Fennel compiler, see configuration.
[!IMPORTANT] Hotpot requires that Fennel macro files (those that you call
import-macrosfor) must use the modern.fnlmextension. Regular Fennel modules (those that you callrequirefor) should use the.fnlextension.
To enable Fennel compilation for a plugin, you must place a .hotpot.fnl file
in the root of the plugin directory. At a bare minimum, this file must specify
the schema and target keys, as shown below.
When writing plugins in Fennel, you'll want to ship .lua code to users, so
you must "colocate" the .fnl and .lua files.
;; projects/my-plugin/.hotpot.fnl
{:schema :hotpot/2
:target :colocate}
[!TIP] Before saving any changes to your
.hotpot.fnlfile, you might want to run:trust, to save yourself being prompted later.
After creating the .hotpot.fnl file, open any .fnl file and save it to
trigger a build, or use the sync command.
See configuration for details on customising Hotpot's behaviour, ignoring files or configuring the Fennel compiler.
Use :Hotpot to interact with Hotpot, :Fnl and its
related commands to evaluate or compile Fennel code from the buffer or command
line.
:HotpotThe :Hotpot command exposes the following subcommands:
sync: manually trigger a compile & clean cycle.locate: find or open a files .lua or .fnl counterpart.watch: enable or disable compile-on-save.fennel: update the bundled fennel version with the latest
from fennel-lang.org.:Hotpot syncSync a given context's .fnl and .lua files. This is the same operation that
occurs when you save a .fnl or .fnlm file.
Supports the following parameters:
context=<path>: sets the context for the command, if not given, the current working directory is used.force: force compilation of all files in the context, even if the .lua is up to date.atomic: allow writing successfully compiled files even if others have compilation errors.verbose: output additional compilation messages.See also the context.sync function exposed by the [API][#api].
:Hotpot locateFind or open a counterpart file, supports the following invocations:
:Hotpot locate <path> -- <commands ...>
Find counterpart file path for <path> and append it to <commands ...>, eg:
:Hotpot locate fnl/my-file.fnl -- vnew would open the counterpart .lua file
in a vnew split.
If path is not given, the current buffer path is used instead, eg: :Hotpot locate -- <commands ...> is equivalent to :Hotpot locate % -- <commands ...>.
:Hotpot locate <path>
Prints the counterpart path, again, if no <path> is given, the current buffer
path is used.
See also the context.locate function exposed by the [API][#api].
:Hotpot watchEnable or disable the compile-on-save behaviour.
Supports the following (mutually exclusive) parameters:
enable: enable syncing on save for all contexts in this session.disable: disable syncing on save for all contexts in this session.:Hotpot fennelUpdate or rollback fennel.lua to the latest version from fennel-lang.org.
Requires curl to be installed.
[!IMPORTANT] Running this is not without some risk, as an updated version of Fennel may be incompatible with Hotpot. This is pretty unlikely unless the API to evaluate or compile fennel code is changed. If a release is only adding new "forms" (eg:
(accumulate ...)) the update should be safe.
Exposes the following sub commands:
:Hotpot fennel versionReports the currently loaded and used version of Fennel.
:Hotpot fennel updateSupports the following parameters:
url=<url>: use given URL instead of finding the latest from fennel-lang.org.force: do not ask whether to update.:Hotpot fennel rollbackRemove downloaded Fennel file and use version shipped with Hotpot.
:FnlOperate on either the command line provided Fennel, or a range from the current buffer either specified on the command line or by selection.
You may always provide a range (:'<,'>Fnl, :%Fnl, etc) or source
string (:Fnl (+ 1 1)). If given both, range always takes precedence.
This command is analogous to Neovim's built in :lua command, and supports the
following flags:
:Fnl
Evaluate the input range or string. Outputs nothing unless the source itself does.
:Fnl=
Evaluate the input range or string and vim.print the result of the expressions.
:Fnl-
Compile the input range or string and vim.print the result of the compilation.
Note that allowedGlobals = false when compling source via :Fnl-, as most
often it's used to compile small snippets for inspection where its common to
reference out-of-selection variables. This means you will not see any warnings
when referencing misspelled or unknown variable as you would during normal
compilation.
:FnlEvalAlias for :Fnl=
:FnlCompileAlias for :Fnl-
:Fnlfile {file.fnl}Evaluates the given file, also supports :Fnlfile= file (output evaluation) and
:Fnlfile- file (output compilation).
:source {file.fnl}Sources given .fnl file. See :h :source
All of Hotpot's behaviour is configured by a .hotpot.fnl file, placed in the
root of your config or plugin directory. These files define a context for
operations in that directory tree.
These contexts are independent of one another and only alter behaviour in the same tree.
If there is no .hotpot.fnl file in Neovim's config directory, a default
configuration is loaded. This is not the case for plugins, which must have a
.hotpot.fnl file.
[!TIP] Before saving any changes to your
.hotpot.fnlfile, you might want to run:trust, to save yourself being prompted later.
;; .hotpot.fnl
{
;; Required, string, valid: hotpot/2
;; Describes expected schema for table.
:schema :hotpot/2
;; Required, string, valid: cache|colocate
;; Describes target location of lua files. `cache` places lua files "out of
;; tree" in a directory loadable by neovim, `colocate` places lua files "in
;; tree", next to their fennel counterparts.
;;
;; When no `.hotpot.fnl` file is present in your config directory,
;; the target defaults to :cache. You may set it to :colocate by adding a
;; .hotpot.fnl file.
;; Be aware that its the users responsibility to remove previously
;; generated lua files when swapping targets in either direction.
;;
;; For plugins, the only valid value is `colocate`.
:target :cache
;; All other keys are optional.
;; Optional, boolean
;; If true (default), any single compilation error will prevent any changes
;; from being written.
:atomic? true
;; Optional, boolean
;; If true (default: false), output messages after every successful
;; compilation instead of just on error.
:verbose? true
;; Optional, function
;; If provided, all compiled fennel source is passed to the function, along
;; with its path, relative to `.hotpot.fnl`. The function must return the
;; modified source.
;; Transform is not called automatically when using the compile and eval API.
:transform (fn [src path] src)
;; Optional, list of strings
;; Glob patterns to ignore when performing compile and clean operations,
;; relative to the .hotpot.fnl file.
;;
;; Files matching `.lua` patterns are never considered orphans and never removed.
;; Files matching `.fnl` patterns are never compiled.
;; Files matching `.fnlm` patterns are never considered when performing stale checks.
:ignore [:some/lib/**/*.lua :junk/*.fnl]
;; Optional, table
;; Fennel compiler options, passed directly to `fennel.compile-string`.
;;
;; Hotpot enables strict global checking by default to prevent referencing
;; unknown or misspelled variables. To restore Fennel's default
;; behaviour, you can set `allowedGlobals` to `false`.
;;
;; If you wish to reference `vim` in your macros, set `:extra-compiler-env {: vim}`.
;;
;; Note that `error-pinpoint` is always forced to false and `filename` is
;; always set to the correct value.
;;
;; See Fennel's own API documentation and --help for further details.
:compiler {:allowedGlobals (icollect [k _ (pairs _G)] k)
:extra-compiler-env {: vim}
:error-pinpoint false}
}
Hotpot provides an API to compile and evaluate arbitrary Fennel code, as well
as compiling files in a project. All interaction is done via a context object.
context(path|nil)Creates a context object. Returns context or nil, error.
All other API interactions are performed through the context object.
(let [api (require :hotpot.api)
ctx (api.context (vim.fn.stdpath :config))]
(ctx.eval "(+ 1 1)"))
path may be:
.hotpot.fnl in its file tree,.hotpot.fnl file,nil.If given a valid path, the context is loaded for use. If given nil, a
default "api" context is created which does not support some operations that
require disk paths, such as sync.
context.compile(string, compiler-options)Compiles the given string, using the context compiler options. Returns true, compiled string or false, error.
Does not automatically apply any transform specified for by the context
configuration. If one was specified, it can be called by context.transform.
context.eval(string, compiler-options)Evaluates the given string, using the context compiler options. Returns true, ...evaluated values or false, error.
Does not automatically apply any transform specified for by the context
configuration. If one was specified, it can be called by context.transform.
context.sync(options|nil)Syncs the context by compiling files in the context. Returns report table.
Supports the following options:
force?: force compilation of all files in the context, even if the .lua is up to date.atomic?: allow writing successfully compiled files even if others have compilation errors.verbose?: output additional compilation messages.compiler: additional Fennel compiler options.Not available for "api" contexts, eg: those created without any path given.
context.transform(string, filename|nil)Apply context transform to string, as defined in the context .hotpot.fnl.
Not available if no transform has been defined.
context.locate(string)Convert given path into its counterpart, eg: given a .fnl file path inside
the context source, convert it to the .lua file path in the context
destination.
Accepts .fnl or .lua file paths. Will construct paths for files that do not
exist if desired.
Not available for "api" contexts, eg: those created without any path given.
[!IMPORTANT] You must change all macro files to use the the
.fnlmextension. This does not require any code changes.
For many users who have not explicitly configured Hotpot, after renaming any
macro files to .fnlm, version 2 should work without drama.
If you are using Lazy.nvim, you should re-check the
installation instructions to correctly set the rtp option.
If you have specifically configured compiler options (via the
compiler.macros/compiler.modules setup options) or use a .hotpot.lua
file, you will need to migrate to a .hotpot.fnl file. See
configuration for details on .hotpot.fnl. You no longer
need to provide separate macros and modules compiler tables. All compiler
options are specified in the compiler key in your .hotpot.fnl file.
The API has been simplified but with simplification, some previously
provided functions have been removed, eg: there is now only eval(source, options), no eval-buffer, eval-file, etc.
Previously Hotpot provided in-editor diagnostics. These have been removed in favour of more complete solutions provided by LSP servers.
require()). This change was made for better
compatibility with things such as the lsp/ runtime directory..lua files from your config, this is still the default behaviour..hotpot.fnl supports configuring different directories with different
compiler options and editing files in one plugin directory while your
current working directory is elsewhere works better..fnlminit-macros.fnl filenames..hotpot.lua is now .hotpot.fnl<config>/init.fnl which is always colocated).fennel-language-server) provides a richer feature set.setup().hotpot.fnl file + compiler key if needed.hotpot.api.compile|evaluate_[file|selection|...]:Fnldo commandsHotpot embeds fennel.lua, see lua/hotpot/fennel.lua for licensing
information.