2026-02-14 20:30:23 -10:00
2026-02-15 01:24:02 -05:00
2026-02-15 01:15:04 -05:00
2026-02-15 01:15:04 -05:00
2026-02-15 01:12:14 -05:00
2026-01-29 17:59:31 -10:00
2026-02-15 01:05:43 -05:00
2026-02-14 20:30:23 -10:00

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

  1. Clone this repo to ~/.config/nvim
  2. Open Neovim - plugins install automatically on first launch
  3. Run :LispSchool and :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

  1. Start your REPL: clj in terminal
  2. Open your .clj file
  3. Write a function:
    (defn add [a b]
      (+ a b))
    
  4. Space e r to evaluate the top-level form
  5. Test it: type (add 1 2) and Space e e to evaluate
  6. See 3 appear in the HUD

Refactoring with Paredit

Transform (if test a b) to (when test a):

  1. Position cursor on if
  2. ciw to change word, type when
  3. Move to b, dae to delete the element
  4. 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?

  1. Check if the server is installed: :LspInfo
  2. Check the log: :LspLog
  3. Ensure you're in a project with root markers (.git, deps.edn, etc.)

Conjure not connecting?

  1. Make sure your REPL is running before opening the file
  2. For Clojure: ensure nREPL is exposed (default port 7888 or .nrepl-port file)
  3. Check :ConjureLog for 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


Credits

Description
No description provided
Readme 97 KiB
Languages
Fennel 92%
Lua 8%