~technomancy/fennel-mode

Emacs support for the Fennel programming language
Font lock the interpreter line
font-lock eros messages
add eros integration to fennel-proto-repl

clone

read-only
https://git.sr.ht/~technomancy/fennel-mode
read/write
git@git.sr.ht:~technomancy/fennel-mode

You can also use your local clone with git send-email.

#Fennel Mode

Font-lock, indentation, navigation, documentation, and REPL support for the Fennel programming language in Emacs.

Supports M-x imenu for quick navigation to local definitions.

#Installation

Add this to your config:

(autoload 'fennel-mode "/path/to/fennel-mode/fennel-mode" nil t)
(add-to-list 'auto-mode-alist '("\\.fnl\\'" . fennel-mode))

Separately you'll also probably want to use fennel-ls, which works great with eglot. In Emacs 30+, it will work automatically once fennel-ls is installed; in earlier versions you need to add this to your config:

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs '(fennel-mode . ("fennel-ls"))))

#Interactivity

Run M-x fennel-repl RET to open a REPL buffer. Once a REPL is open, you can send code from a fennel-mode buffer to be evaluated.

#Bindings for fennel-mode

  • M-. - Jump to the definition of a globally-visible function (xref)
  • M-, - Jump back to where you were before jumping to definition
  • M-' - Jump to the definition of a function in a module
  • C-c C-k - Reload the module for the current file (requires fennel.seacher)
  • C-c C-l - Display compilation output for the current file
  • C-c C-z - Start or switch to REPL buffer
  • C-c C-t - Reformat current buffer with fnlfmt (separate install)
  • C-c C-d - Ask for a value and show its docstring in the REPL (also C-c C-f)
  • C-c C-v - Show docstring of variable at point
  • C-c C-p - Print macro expansion of expression at point in the REPL
  • C-x C-e - Evaluate last expression before the point
  • C-c C-e - Evaluate current top-level form (also C-M-x)
  • C-c C-n - Evaluate current top-level form, then move to the next
  • C-c C-r - Evaluate the region
  • C-c C-z - Start or switch to REPL buffer

#Bindings for fennel-repl-mode

  • TAB - Completion at point
  • C-c M-o - Clear the REPL output
  • C-c C-d - Ask for a value and show its docstring in the REPL (also C-c C-f)
  • C-c C-v - Show docstring of variable at point
  • M-. - Jump to the definition of a globally-visible function
  • C-c C-z - Toggle back to previous fennel-mode buffer
  • C-c C-q - Quit the REPL

These functions assume a fennel executable is present on your path. You can override the location by setting inferior-lisp-program or invoking C-u M-x fennel-repl. For instance, if you have a stdio REPL in a LÖVE game, you can set this to love ..

Note that many evaluation or jump features require having the functions in the repl scope, not just in scope for the file. The repl cannot "enter" a file as this concept does not exist at runtime in Lua. Running C-c C-k will load the buffer into a module but will not dump its contents into the repl; you would need C-c C-r for that.

#Antifennel

This repo also contains antifennel.el which allows you to compile Lua code to Fennel straight from a lua-mode buffer. It requires installing antifennel first. Install it with:

(autoload 'antifennel-mode "/path/to/fennel-mode/antifennel.el" nil t)
(add-hook 'lua-mode-hook 'antifennel-mode)

Now when antifennel-mode is active in a Lua buffer, you can press C-c C-f to open a new buffer containing the Fennel equivalent of the Lua code.

#Protocol-based REPL

A separate REPL integration is provided via the fennel-proto-repl module. This module provides a client that can connect to a regular Fennel REPL, upgrade it with the protocol code, and provides a more robust interactive experience. Advantages over the default fennel-repl are:

  • IO and evaluation results are separated.
  • Stack traces in Errors can be used to jump to the error location.
  • Evaluation results are shown in the echo area - no need to keep the REPL window open.
  • Running multiple REPLs is easier, and different buffers can be linked to different REPLs.
  • Synchronous and Asynchronous API.
  • Support for Eldoc and Xref without eglot/fennel-ls.
  • Configurable dynamic font-locking of globals and macros
  • Project integration via project.el or projectile.
  • Integration with the eros package.

Installation is similar to the other modules:

(autoload 'fennel-proto-repl "/path/to/fennel-mode/fennel-proto-repl.el" nil t)
(add-hook 'fennel-mode-hook 'fennel-proto-repl-minor-mode)

The fennel-proto-repl-minor-mode re-binds all of the default REPL interaction keys available in fennel-mode to use fennel-proto-repl instead of a regular fennel-repl. In addition to the usual fennel-mode mappings listed above, additional commands are available:

  • C-c C-S-l - link the current buffer to a specific REPL session
  • C-c C-b - Evaluate the whole buffer
  • C-c C-a - Ask for a function and show its argument list in the REPL

Starting the REPL or switching to the existing one is done via the same C-c C-z shortcut, and the new REPL session can be started at any moment with the fennel-proto-repl command. The buffer is automatically linked to the newly created REPL.

Note that fennel-proto-repl requires a Fennel version recent enough to include the ___repl___ variable. If the Fennel doesn't have the ___repl___ variable the Proto REPL won't be started.

#Org Babel support

Installation is similar to the other modules:

(autoload 'ob-fennel "/path/to/fennel-mode/ob-fennel.el" nil t)

Then, the support for Fennel in Org buffers can be activated by adding the following code to the Emacs init file:

(with-eval-after-load 'org
  (require 'ob-fennel))

After that, the #+begin_src fennel code blocks can be executed in Org buffers. The integration requires the fennel-proto-repl module to be available and operational.

The ob-fennel module supports evaluating code from the regular "src blocks", and "inline src blocks" (src_fennel[]{...}), with the ability to access variables and tables defined elsewhere in the file. Multiple sessions can be used, and a different command to start the REPL process can be passed via the :fennel-cmd header argument.

#Flymake support

The Flymake backend depends on the fennel-ls for linting, if you would rather avoid using eglot.

Installation is similar to the other modules:

(autoload 'fennel-ls-flymake "/path/to/fennel-mode/fennel-ls-flymake.el" nil t)

Enabling the Flymake integration for Fennel buffers can be done via a hook:

(add-hook 'fennel-mode-hook 'fennel-ls-flymake)

#Contributing

Send patches to the Fennel mailing list. If you prefer not to use email you can send a pull request on the Codeberg mirror. Please byte-compile to check for warnings.

#Testing

This package has tests stored in the test/ directory. When developing new functionality consider writing a test for it.

You can run the tests with make. By default this runs the tests for fennel-mode alone; for fennel-proto-repl and ob-fennel you can override the TESTS argument or run tests interactively via ert-run-tests-interactively after loading the respecting test file.

Use make EMACS=emacs29 to test against a different Emacs version.

During development it may be more convenient to use M-x ert-run-tests-interactively but try to make sure the tests pass with a fresh Emacs using make before committing.

Copyright © 2018-2025 Phil Hagelberg and contributors

Licensed under the same license as Emacs (GPL v3 or later); see LICENSE