Neovim Fennel/Clojure Configuration
A Neovim configuration optimized for Lisp development (Fennel, Clojure, Scheme, Racket) using Fennel as the configuration language.
New here? Run these built-in tutorials to get up to speed fast:
| Command | What it teaches |
|---|---|
:LispSchool |
Structural editing — navigating forms, slurp/barf, wrap/splice, text objects, parinfer. 8 hands-on lessons with practice exercises. Advance with ]l. |
:ConjureSchool |
REPL-driven development — evaluating code, inspecting results, using the log buffer. |
Prerequisites
- Neovim 0.11+ (uses built-in LSP configuration)
- Git (for plugin installation)
Installing Neovim with bob-nvim
bob is a Neovim version manager that makes it easy to install, switch between, and update Neovim versions.
# 1. Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 2. Install bob
cargo install bob-nvim
# 3. Install and use the latest version
bob install latest
bob use latest
# Or install a specific version
bob install 0.11.6
bob use 0.11.6
Make sure ~/.local/share/bob/nvim-bin is in your PATH.
Quick Start
- Clone this repo to
~/.config/nvim - Open Neovim - plugins install automatically on first launch
- Run
:LispSchooland:ConjureSchool(see above) to learn the workflow
Understanding Lisp Editing
If you're new to Lisp, here are the key concepts that make editing different from other languages.
S-Expressions (Sexps)
All Lisp code is made of nested parenthesized expressions called s-expressions or sexps:
(defn greet [name] ; <- this whole thing is a "form" or "sexp"
(str "Hello, " name)) ; <- this is a nested form
- Form: A complete s-expression (anything in balanced parens/brackets/braces)
- Element: A single item - either an atom (
name,42,:keyword) or a form
Structural Editing
Instead of editing text character-by-character, Lisp programmers edit structures. The paredit plugin keeps your parentheses balanced automatically and lets you manipulate code by its structure.
Example - Slurping:
;; Before: cursor inside the (+ 1 2) form
(foo (+ 1 2) 3 4)
;; After pressing >) (slurp forward) - pulls 3 into the form
(foo (+ 1 2 3) 4)
Example - Barfing:
;; Before: cursor inside the (+ 1 2 3) form
(foo (+ 1 2 3) 4)
;; After pressing <) (barf forward) - pushes 3 out of the form
(foo (+ 1 2) 3 4)
Interactive Development (REPL)
Lisp development is interactive. You connect to a running program (REPL) and evaluate code as you write it. Conjure handles this - you can evaluate any expression and see results immediately.
Keybindings Reference
Leader key: Space
Press Space and wait to see available options via which-key.
General Navigation
| Key | Action |
|---|---|
Ctrl-h/j/k/l |
Move between windows |
Space f f |
Find files (Telescope) |
Space f g |
Live grep in project |
Space f b |
List open buffers |
Space f h |
Search help tags |
Space b n |
Next buffer |
Space b p |
Previous buffer |
Space b d |
Delete buffer |
General Editing
| Key | Action |
|---|---|
jk |
Escape insert mode |
Space w |
Save file |
Space f s |
Save all files |
Space q |
Quit |
Esc |
Clear search highlight |
LSP (Language Server)
Available when editing files with LSP support (Clojure, Fennel, Lua):
| Key | Action |
|---|---|
gd |
Go to definition |
gD |
Go to declaration |
gr |
Find references |
gi |
Go to implementation |
K |
Show hover documentation |
Space r n |
Rename symbol |
Space c a |
Code actions |
Space e |
Show diagnostic popup |
Space F |
Format buffer |
[d |
Previous diagnostic |
]d |
Next diagnostic |
Commands: :LspInfo, :LspStart, :LspStop, :LspRestart, :LspLog
Conjure - REPL Integration
Conjure connects your editor to a running REPL, allowing you to evaluate code interactively.
Starting a REPL
Clojure (deps.edn):
clj -M:repl
Clojure (Leiningen):
lein repl
Fennel:
fennel --repl
Conjure auto-connects when you open a Lisp file and a REPL is running.
Evaluation Keybindings
All Conjure bindings start with Space (local leader):
| Key | Action |
|---|---|
Space e e |
Evaluate form under cursor |
Space e r |
Evaluate root (top-level) form |
Space e b |
Evaluate entire buffer |
Space e f |
Evaluate file |
Space e ! |
Evaluate form and replace with result |
Space e w |
Evaluate word under cursor |
Log Buffer
Results appear in a floating HUD window. To interact with the full log:
| Key | Action |
|---|---|
Space l v |
Open log in vertical split |
Space l s |
Open log in horizontal split |
Space l t |
Open log in new tab |
Space l q |
Close log windows |
Space l r |
Reset/clear the log |
Documentation
| Key | Action |
|---|---|
K |
Show documentation for symbol under cursor |
Tip: Learn Interactively
Run :ConjureSchool for an interactive tutorial inside Neovim.
nvim-paredit - Structural Editing
Paredit keeps parentheses balanced and provides structural editing commands.
All paredit operations are dot-repeatable. Press . to repeat the last structural edit:
;; Start with cursor inside (+ 1 2)
(foo (+ 1 2) a b c d e)
;; >) slurp forward
(foo (+ 1 2 a) b c d e)
;; . repeat slurp
(foo (+ 1 2 a b) c d e)
;; . repeat again
(foo (+ 1 2 a b c) d e)
Navigation
Move by elements (atoms or forms) rather than words:
| Key | Action |
|---|---|
W |
Next element head |
B |
Previous element head |
E |
Next element tail |
gE |
Previous element tail |
( |
Jump to parent form's opening paren |
) |
Jump to parent form's closing paren |
T |
Jump to top-level form's head |
Slurp and Barf
Slurp: Pull the next/previous element INTO the current form Barf: Push the last/first element OUT OF the current form
| Key | Action |
|---|---|
>) |
Slurp forward (pull next element in) |
<( |
Slurp backward (pull previous element in) |
<) |
Barf forward (push last element out) |
>( |
Barf backward (push first element out) |
Visual example - forward operations:
;; Cursor inside: (+ 1 2)
(foo (+ 1 2) 3 4)
;; >) slurp forward - pull 3 into the form
(foo (+ 1 2 3) 4)
;; >) again - pull 4 in
(foo (+ 1 2 3 4))
;; <) barf forward - push 4 back out
(foo (+ 1 2 3) 4)
Visual example - backward operations:
;; Cursor inside: (+ 1 2)
(a b (+ 1 2))
;; <( slurp backward - pull b into the form
(a (b + 1 2))
;; >( barf backward - push b back out
(a b (+ 1 2))
Dragging Elements
Move elements/forms left and right within their parent:
| Key | Action |
|---|---|
>e |
Drag element right |
<e |
Drag element left |
>f |
Drag form right |
<f |
Drag form left |
Example:
;; Cursor on 'a'
(foo a b c)
;; >e drag element right - swap a and b
(foo b a c)
Wrapping
Wrap an element in delimiters:
| Key | Action |
|---|---|
cse( or cse) |
Wrap element in () |
cse[ or cse] |
Wrap element in [] |
cse{ or cse} |
Wrap element in {} |
Splice (Unwrap)
| Key | Action |
|---|---|
dsf |
Splice - delete surrounding form, keeping contents |
Example:
;; Before (cursor inside the when form)
(when true (println "hello"))
;; dsf - splice, removing the surrounding parens
when true (println "hello")
Raise
Replace the parent form with the current form/element:
| Key | Action |
|---|---|
Space o |
Raise form (replace parent with current form) |
Space O |
Raise element (replace parent with current element) |
Example:
;; Before: cursor on (inner value)
(outer (inner value))
;; Space o - raise form, replacing parent with current form
(inner value)
Text Objects
Use with operators like d, c, y, v:
| Text Object | Meaning |
|---|---|
af |
Around form (including parens) |
if |
Inside form (excluding parens) |
aF |
Around top-level form |
iF |
Inside top-level form |
ae |
Around element |
ie |
Inside element |
Examples:
daf- Delete around form (delete entire form including parens)cif- Change inside form (replace form contents)yae- Yank around element
Workflow Examples
Editing a Clojure Function
- Start your REPL:
cljin terminal - Open your
.cljfile - Write a function:
(defn add [a b] (+ a b)) Space e rto evaluate the top-level form- Test it: type
(add 1 2)andSpace e eto evaluate - See
3appear in the HUD
Refactoring with Paredit
Transform (if test a b) to (when test a):
- Position cursor on
if ciwto change word, typewhen- Move to
b,daeto delete the element - Done:
(when test a)
Wrapping Code in a Let
;; Start with:
(+ x y)
;; Position on the form, then: cse(
((+ x y))
;; Now you have wrapped parens. Type your let:
(let [z 1] (+ x y))
File Structure
~/.config/nvim/
├── init.lua # Entry point (Lua)
├── lua/
│ ├── bootstrap.lua # Core plugins (nfnl, treesitter, telescope)
│ ├── config/ # AUTO-GENERATED from fnl/config/
│ └── plugins/ # AUTO-GENERATED from fnl/plugins/
└── fnl/
├── config/init.fnl # Your settings, keymaps, LSP config
└── plugins/init.fnl # Plugin specifications
Important: Edit files in fnl/, not lua/config/ or lua/plugins/ - those are auto-generated.
Customization
Add a Plugin
Edit fnl/plugins/init.fnl, add a spec to the vector:
{:1 "author/plugin-name"
:config (fn [] (setup-code-here))}
Add a Keymap
Edit fnl/config/init.fnl:
(vim.keymap.set :n "<leader>x" ":SomeCommand<CR>" {:desc "Do something"})
Change the Colorscheme
Edit the tokyonight config in fnl/plugins/init.fnl, or add a different colorscheme plugin.
Troubleshooting
LSP not working?
- Check if the server is installed:
:LspInfo - Check the log:
:LspLog - Ensure you're in a project with root markers (
.git,deps.edn, etc.)
Conjure not connecting?
- Make sure your REPL is running before opening the file
- For Clojure: ensure nREPL is exposed (default port 7888 or
.nrepl-portfile) - Check
:ConjureLogfor connection errors
Paredit feels wrong?
Structural editing takes practice. Start with just >) and <) for slurp/barf, then gradually add more commands. The :help nvim-paredit documentation is excellent.
Learning Resources
- Structural Editing Tutorial:
:LispSchool— 8 interactive lessons on vim-sexp - Conjure Interactive Tutorial:
:ConjureSchool— learn REPL-driven development - Fennel Language: https://fennel-lang.org/tutorial
- Clojure for the Brave and True: https://www.braveclojure.com/
- Paredit Guide (Emacs, but concepts apply): https://www.emacswiki.org/emacs/ParEdit
Credits
- nfnl - Fennel to Lua compiler for Neovim
- Conjure - Interactive REPL environment
- nvim-paredit - Structural editing
- lazy.nvim - Plugin manager
- tokyonight.nvim - Colorscheme