add readme
This commit is contained in:
@@ -0,0 +1,410 @@
|
|||||||
|
# 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 on the (+ 1 2) form
|
||||||
|
(let [x (+ 1 2)] 3)
|
||||||
|
|
||||||
|
;; After pressing >) (slurp forward)
|
||||||
|
(let [x (+ 1 2) 3])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example - Barfing:**
|
||||||
|
```clojure
|
||||||
|
;; Before
|
||||||
|
(let [x (+ 1 2 3)])
|
||||||
|
|
||||||
|
;; After pressing <) (barf forward) on the + form
|
||||||
|
(let [x (+ 1 2) 3])
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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:**
|
||||||
|
```bash
|
||||||
|
# In your project directory
|
||||||
|
clj -M:repl # or however your project starts a 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 |
|
||||||
|
| `Space e d` | Describe form 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.
|
||||||
|
|
||||||
|
### 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:**
|
||||||
|
```clojure
|
||||||
|
;; Cursor on inner form: (+ 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
|
||||||
|
### 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 when form
|
||||||
|
true (println "hello")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Raise
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `Space o` | Raise form (replace parent with current form) |
|
||||||
|
| `Space O` | Raise element (replace parent with current element) |
|
||||||
|
|
||||||
|
### 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 "<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
|
||||||
|
|
||||||
|
- **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
|
||||||
Reference in New Issue
Block a user