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 rest, recompiling your fennel code
as needed.
;; ~/.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!"
.hotpot.lua
lua/
on save, use different compiler settings per project.h hotpot-cookbook-using-dot-hotpot
vim.loader
supportvim.loader.enable()
if you want the
fastest loading experience.vim.loader
without the bytecode cache if desired, you do not
have to call enable()
.vim.loader
is pretty fast without the bytecode cache.)preprocessor
setup optionAll you need to do is install Hotpot and call require("hotpot")
in your
init.lua
Neovim configuration file.
First lets setup our init.lua
file. In this example we use the lazy.nvim
plugin manager, but other plugin manager will follow the same pattern -- likely
without the runtimepath alterations.
-- ~/.config/nvim/init.lua
-- As per lazy's install instructions
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
-- Bootstap hotpot into lazy plugin dir if it does not exist yet.
local hotpotpath = vim.fn.stdpath("data") .. "/lazy/hotpot.nvim"
if not vim.loop.fs_stat(hotpotpath) then
vim.notify("Bootstrapping hotpot.nvim...", vim.log.levels.INFO)
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"--single-branch",
-- You may with to pin a known version tag with `--branch=vX.Y.Z`
"--branch=v0.9.6",
"https://github.com/rktjmp/hotpot.nvim.git",
hotpotpath,
})
end
-- As per lazy's install instructions, but insert hotpots path at the front
vim.opt.runtimepath:prepend({hotpotpath, lazypath})
require("hotpot") -- optionally you may call require("hotpot").setup(...) here
-- include hotpot as a plugin so lazy will update it
local plugins = {"rktjmp/hotpot.nvim"}
require("lazy").setup(plugins)
-- include the rest of your config
require("say-hello")
The say-hello
module would be put in ~/.config/nvim/fnl/say-hello.fnl
:
;; ~/.config/nvim/fnl/say-hello.fnl
(print :hello!)
Place all your fennel files under a fnl
dir, as you would place lua files
under lua
. This practice extends to other folders outside of your config
directory, such as plugins you may write or install.
With your file in the correct location, you only need to require it like you would any normal lua module.
;; ~/.config/nvim/fnl/is_neat.fnl
;; some kind of fennel code
(fn [what]
(print what "is neat!"))
-- and in ~/.config/nvim/init.lua
local neat = require('is_neat')
neat("fennel") -- => "fennel is neat!"
Hotpot will keep an internal cache of lua code, so you wont see files
cluttering the lua/
directory.
You can may want to read the cookbook or see more options in setup.
The setup()
function may optionally be called. setup()
provides access to
Fennels configuration options as described on
fennel-lang.org as well as some configuration of
hotpot itself.
You do not have to call setup unless you are altering a default option.
require("hotpot").setup({
-- provide_require_fennel defaults to disabled to be polite with other
-- plugins but enabling it is recommended.
provide_require_fennel = false,
enable_hotpot_diagnostics = true,
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
},
-- A function that accepts a string of fennel source and a table of
-- of some information. Can be used to alter fennel code before it is
-- compiled.
preprocessor = nil
}
})
provide_require_fennel
inserts a package.preload
function that will load
Hotpot's copy of fennel when you call (require :fennel)
. This can be useful
for ergonomics or for compatibility with libraries that expect Fennel to be in
package.path
without having to pay the cost of loading the Fennel compiler
when its not used.
enable_hotpot_diagnostics
enable or disable automatic attachment of
diagnostics to fennel buffers. See diagnostics.
compiler.modules
is passed to the Fennel compiler when compiling regular
module files.
compiler.macros
is passed to the Fennel compiler when compiling macro files.
Be sure to include env = "_COMPILER"
unless you have a good reason not to.
compiler.preprocessor
is a function that accepts the fennel source code as a string,
and a table, {: path : modname : macro}
.
Fennel compiler plugins are supported in two forms, as a table (ie. as
described by Fennels documentation) and as a string which should be a module
name. If your plugin needs access to the "compiler environment" (ie. it uses
special forms such as (sym)
or (macroexpand)
not available to "normal"
Fennel code), you should specify the module name and hotpot will load it when
required in the compiler environment.
Note:
The filename
compilation option is always set to the appropriate value and
can not be altered via the setup interface.
The modules
and macros
tables replace the defaults when given,
they are not merged. Include all options you wish to pass to the
compiler!
The compiler
options are not currently passed to any api.compile
functions and are only applied to Hotpots internal/automatic
compilation. If you have use for passing options to api.compile
please
open an issue.
For a complete list of compiler options, see Fennels documentation, specifically the API usage section.
Hotpot can optionally be configured to build lua/
directories on-save with
per-project settings by using a .hotpot.lua
file.
See :h hotpot-cookbook-using-dot-hotpot
in
the COOKBOOK.
Hotpot ships with built in diagnostics feature to show fennel compilation errors via Neovim diagnostics.
It automatically attaches to buffers with the filetype fennel
and updates
when ever you leave insert mode or otherwise change the buffer.
"Macro modules" require a special fennel environment. To detect "macro modules",
Hotpot checks if the buffer filename ends in macro.fnl
or macros.fnl
which is
common practice. It's not currently possible to enable the macro environment in
other contexts (please open an issue).
Hotpot provides a number of functions for evaluating and compiling Fennel code, including helpers to easily operate on strings, selections and buffers for example.
See :h hotpot.api
.
Hotpot provides 3 commands which behave similarly but not exactly like
Neovims Lua commands (see :h lua-commands
).
It also allows the :source
command to work with .fnl
files.
:[range]Fnl {expression}
: Evaluates {expression} or range
If given form is preceded by =
, the result is passed through fennel.view
and printed. Multiple return values are separated with ,
.
You may also use =
when providing a range.
If a range and a form is provided, the range is ignored.
:Fnl (vim.keymap.set ...) ;; evaluates code, no output
:Fnl (values 99 (+ 1 1)) ;; evaluates code, no output
:Fnl =(values 99 (+ 1 1)) ;; evaluates code, outputs "99, 2"
:Fnl=(+ 1 1) ;; You may omit the space
:'<,'>Fnl ;; evaluates selection in current buffer
:1,10Fnl = ;; evaluate lines 1 to 10 in current buffer, prints output
:'<,'>Fnl= ;; again, the space may be omitted
:'<,'>Fnl (print :hello) ;; prints "hello" (range is ignored)
:[range]Fnldo {expression}
: Evaluates {expression} for each line in [range]
The result of the expression replaces each line in turn. Two variables are
available inside {expression}, line
and linenr
.
:'<,'>Fnldo (string.format "%d: %s" linenr (line:reverse))
=> Prepends line number and reverses the contents of line
:Fnlfile {file}
: Evaluates {file}, see also :h :source
.
:Fnlfile %
:Fnlfile my-file.fnl
:source {file}
: See :h :source
Hotpot expects the user to specify most maps themselves via the API functions (see :h hotpot.api
).
It does provide one <Plug>
mapping for operator-pending eval.
<Plug>(hotpot-operator-eval)
Enters operator-pending mode and evaluates the Fennel code specified by the proceeding motion.
map <Plug> ghe <Plug>(hotpot-operator-eval)
gheip -> evauate fennel code in paragraph
Given the directory structure,
mod/fnl/code.fnl
mod/lua/code.lua
and a call (require :code)
, Hotpot will opt to load the lua file instead of
compiling the fennel source and overwriting mod/lua/code.lua
.
This behaviour exists in case a plugin ships with both code in both the lua
and fnl
directories, but the plugin author has post-processed the compiled
lua code, or is using an incompatible fennel version, etc.
In most cases, such as your config, Hotpot wont create mod/lua/code.lua
and
you wont run into any issues but it may encounter friction when writing a
plugin in fennel.
package.path
. This is for safety
purposes because it can be unclear where and when its safe to compile or
overwrite .lua
files. In most usage this wont occur -- files will be found in
the RTP first but it can occur when executing in scratch buffers with the
api or via commands.Hotpot embeds fennel.lua
, see lua/hotpot/fennel.lua
for licensing
information.