# Neovim Fennel/Clojure Configuration A Neovim configuration optimized for Lisp development (Fennel, Clojure, Scheme, Racket) using [Fennel](https://fennel-lang.org/) as the configuration language. ## Prerequisites - **Neovim 0.11+** (uses built-in LSP configuration) - **Git** (for plugin installation) ### Optional but Recommended Install language servers for full IDE features: ```bash # Clojure LSP (for Clojure/ClojureScript) brew install clojure-lsp/brew/clojure-lsp # Fennel Language Server (for Fennel) cargo install fennel-language-server # Lua Language Server (for Lua/Neovim plugin development) brew install lua-language-server ``` Or use Mason inside Neovim: `:MasonInstall clojure-lsp lua-language-server` ## Quick Start 1. Clone this repo to `~/.config/nvim` 2. Open Neovim - plugins install automatically on first launch 3. Open a `.fnl` or `.clj` file and start editing ## 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**: ```clojure (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:** ```clojure ;; 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:** ```clojure ;; 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):** ```bash clj -M:repl ``` **Clojure (Leiningen):** ```bash lein repl ``` **Fennel:** ```bash 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: ```clojure ;; 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:** ```clojure ;; 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:** ```clojure ;; 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 | | `f` | Drag form right | | `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:** ```clojure ;; 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:** ```clojure ;; 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: ```clojure (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 ```clojure ;; 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: ```fennel {:1 "author/plugin-name" :config (fn [] (setup-code-here))} ``` ### Add a Keymap Edit `fnl/config/init.fnl`: ```fennel (vim.keymap.set :n "x" ":SomeCommand" {: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 - **Conjure Interactive Tutorial**: `:ConjureSchool` - **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](https://github.com/Olical/nfnl) - Fennel to Lua compiler for Neovim - [Conjure](https://github.com/Olical/conjure) - Interactive REPL environment - [nvim-paredit](https://github.com/julienvincent/nvim-paredit) - Structural editing - [lazy.nvim](https://github.com/folke/lazy.nvim) - Plugin manager - [tokyonight.nvim](https://github.com/folke/tokyonight.nvim) - Colorscheme