Initial commit: Neovim config with Fennel via nfnl
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
undodir
|
||||||
|
|
||||||
|
# Generated Lua files (compiled from Fennel by nfnl)
|
||||||
|
lua/config/
|
||||||
|
lua/plugins/
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Claude Code Context
|
||||||
|
|
||||||
|
This is a Neovim configuration using Fennel (a Lisp that compiles to Lua) via nfnl.
|
||||||
|
|
||||||
|
## Key Concepts
|
||||||
|
|
||||||
|
- **nfnl** compiles `.fnl` files to `.lua` on save
|
||||||
|
- **lazy.nvim** is the plugin manager
|
||||||
|
- Bootstrap files are in Lua; user config is in Fennel
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
init.lua - Entry point (Lua, do not convert to Fennel)
|
||||||
|
lua/bootstrap.lua - Core plugins in Lua (nfnl, treesitter, telescope)
|
||||||
|
lua/config/ - AUTO-GENERATED from fnl/config/ - do not edit
|
||||||
|
lua/plugins/ - AUTO-GENERATED from fnl/plugins/ - do not edit
|
||||||
|
fnl/config/ - User config in Fennel (options, keymaps, autocmds)
|
||||||
|
fnl/plugins/ - Plugin specs in Fennel
|
||||||
|
.nfnl.fnl - nfnl configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Editing Rules
|
||||||
|
|
||||||
|
1. **Never edit files in `lua/config/` or `lua/plugins/`** - they are auto-generated
|
||||||
|
2. **Edit Fennel files in `fnl/`** - they compile to `lua/` on save
|
||||||
|
3. **`init.lua` and `lua/bootstrap.lua` stay as Lua** - they bootstrap nfnl
|
||||||
|
|
||||||
|
**IMPORTANT FOR CLAUDE:** After editing any `.fnl` file, compile all Fennel files to Lua:
|
||||||
|
```bash
|
||||||
|
cd ~/.config/nvim && nvim --headless -c "NfnlCompileAllFiles" -c "qa"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fennel Syntax Quick Reference
|
||||||
|
|
||||||
|
```fennel
|
||||||
|
;; Set vim option
|
||||||
|
(set vim.opt.number true)
|
||||||
|
|
||||||
|
;; Define keymap
|
||||||
|
(vim.keymap.set :n "<leader>x" ":cmd<CR>" {:desc "Description"})
|
||||||
|
|
||||||
|
;; Local variable
|
||||||
|
(local foo (require :foo))
|
||||||
|
|
||||||
|
;; Let binding
|
||||||
|
(let [x 1 y 2] (+ x y))
|
||||||
|
|
||||||
|
;; Function
|
||||||
|
(fn my-func [arg] (print arg))
|
||||||
|
|
||||||
|
;; Lambda
|
||||||
|
(fn [x] (* x 2))
|
||||||
|
|
||||||
|
;; Plugin spec (lazy.nvim format)
|
||||||
|
{:1 "author/plugin"
|
||||||
|
:ft ["fennel" "lua"]
|
||||||
|
:config (fn [] (setup-code))}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Tasks
|
||||||
|
|
||||||
|
### Add a plugin
|
||||||
|
Edit `fnl/plugins/init.fnl`, add spec to the vector, save.
|
||||||
|
|
||||||
|
### Add a keymap
|
||||||
|
Edit `fnl/config/init.fnl`, add `(vim.keymap.set ...)`, save.
|
||||||
|
|
||||||
|
### Add an option
|
||||||
|
Edit `fnl/config/init.fnl`, add `(set vim.opt.foo value)`, save.
|
||||||
|
|
||||||
|
### Force recompile all Fennel
|
||||||
|
Run `:NfnlCompileAllFiles` in Neovim.
|
||||||
|
|
||||||
|
## Leader Keys
|
||||||
|
|
||||||
|
- Leader: `<Space>`
|
||||||
|
- Local leader: `<Space>` (same as leader, used by Conjure and paredit)
|
||||||
@@ -0,0 +1,461 @@
|
|||||||
|
# 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 |
|
||||||
|
| `<e` | Drag element left |
|
||||||
|
| `>f` | Drag form right |
|
||||||
|
| `<f` | Drag form left |
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```clojure
|
||||||
|
;; 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:**
|
||||||
|
```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 "<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
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
;; Main Fennel configuration
|
||||||
|
;; This file is compiled to lua/config/init.lua by nfnl
|
||||||
|
|
||||||
|
;; Options
|
||||||
|
(set vim.opt.number true)
|
||||||
|
(set vim.opt.relativenumber true)
|
||||||
|
(set vim.opt.tabstop 2)
|
||||||
|
(set vim.opt.shiftwidth 2)
|
||||||
|
(set vim.opt.expandtab true)
|
||||||
|
(set vim.opt.smartindent true)
|
||||||
|
(set vim.opt.wrap false)
|
||||||
|
(set vim.opt.ignorecase true)
|
||||||
|
(set vim.opt.smartcase true)
|
||||||
|
(set vim.opt.termguicolors true)
|
||||||
|
(set vim.opt.signcolumn "yes")
|
||||||
|
(set vim.opt.updatetime 250)
|
||||||
|
(set vim.opt.undofile true)
|
||||||
|
(set vim.opt.clipboard "unnamedplus")
|
||||||
|
|
||||||
|
;; Keymaps
|
||||||
|
(local keymap vim.keymap.set)
|
||||||
|
|
||||||
|
;; Window navigation
|
||||||
|
(keymap :n "<C-h>" "<C-w>h" {:desc "Move to left window"
|
||||||
|
:silent true})
|
||||||
|
(keymap :n "<C-j>" "<C-w>j" {:desc "Move to lower window"
|
||||||
|
:silent true})
|
||||||
|
(keymap :n "<C-k>" "<C-w>k" {:desc "Move to upper window"
|
||||||
|
:silent true})
|
||||||
|
(keymap :n "<C-l>" "<C-w>l" {:desc "Move to right window"
|
||||||
|
:silent true})
|
||||||
|
|
||||||
|
;; Buffer navigation
|
||||||
|
(keymap :n "<leader>bn" ":bnext<CR>" {:desc "Next buffer"})
|
||||||
|
(keymap :n "<leader>bp" ":bprevious<CR>" {:desc "Previous buffer"})
|
||||||
|
(keymap :n "<leader>bd" ":bdelete<CR>" {:desc "Delete buffer"})
|
||||||
|
|
||||||
|
;; Clear search highlight
|
||||||
|
(keymap :n "<Esc>" ":noh<CR>" {:desc "Clear search highlight"})
|
||||||
|
|
||||||
|
;; Better escape
|
||||||
|
(keymap :i "jk" "<Esc>" {:desc "Escape insert mode"})
|
||||||
|
|
||||||
|
;; Save file
|
||||||
|
(keymap :n "<leader>w" ":w<CR>" {:desc "Save file"})
|
||||||
|
(keymap :n "<leader>fs" ":wa<CR>" {:desc "Save all files" :silent true})
|
||||||
|
|
||||||
|
;; Quit
|
||||||
|
(keymap :n "<leader>q" ":q<CR>" {:desc "Quit"})
|
||||||
|
|
||||||
|
;; Autocommands
|
||||||
|
(local augroup vim.api.nvim_create_augroup)
|
||||||
|
(local autocmd vim.api.nvim_create_autocmd)
|
||||||
|
|
||||||
|
;; Highlight on yank
|
||||||
|
(let [yank-group (augroup "YankHighlight" {:clear true})]
|
||||||
|
(autocmd "TextYankPost" {:group yank-group
|
||||||
|
:callback (fn [] (vim.highlight.on_yank))}))
|
||||||
|
|
||||||
|
;; Remove trailing whitespace on save
|
||||||
|
(let [whitespace-group (augroup "TrimWhitespace" {:clear true})]
|
||||||
|
(autocmd "BufWritePre" {:group whitespace-group
|
||||||
|
:pattern "*"
|
||||||
|
:command "%s/\\s\\+$//e"}))
|
||||||
|
|
||||||
|
;; LSP Configuration (Neovim 0.11+ built-in)
|
||||||
|
;; Configure servers using vim.lsp.config
|
||||||
|
(vim.lsp.config :clojure_lsp
|
||||||
|
{:cmd [:clojure-lsp]
|
||||||
|
:filetypes [:clojure :edn]
|
||||||
|
:root_markers [:deps.edn :project.clj :build.boot :shadow-cljs.edn :.git]})
|
||||||
|
|
||||||
|
(vim.lsp.config :lua_ls
|
||||||
|
{:cmd [:lua-language-server]
|
||||||
|
:filetypes [:lua]
|
||||||
|
:root_markers [:.luarc.json :.luarc.jsonc :.git]
|
||||||
|
:settings {:Lua {:runtime {:version :LuaJIT}
|
||||||
|
:workspace {:library (vim.api.nvim_get_runtime_file "" true)}}}})
|
||||||
|
|
||||||
|
(vim.lsp.config :fennel_language_server
|
||||||
|
{:cmd [:fennel-language-server]
|
||||||
|
:filetypes [:fennel]
|
||||||
|
:root_markers [:.nfnl.fnl :fnl :.git]
|
||||||
|
:settings {:fennel {:workspace {:library (vim.api.nvim_get_runtime_file "" true)}
|
||||||
|
:diagnostics {:globals [:vim]}}}})
|
||||||
|
|
||||||
|
;; Enable the configured LSP servers
|
||||||
|
(vim.lsp.enable [:clojure_lsp :lua_ls :fennel_language_server])
|
||||||
|
|
||||||
|
;; LSP keymaps (set on attach)
|
||||||
|
(autocmd "LspAttach"
|
||||||
|
{:callback (fn [ev]
|
||||||
|
(local opts {:buffer ev.buf})
|
||||||
|
(keymap :n "gd" vim.lsp.buf.definition opts)
|
||||||
|
(keymap :n "gD" vim.lsp.buf.declaration opts)
|
||||||
|
(keymap :n "gr" vim.lsp.buf.references opts)
|
||||||
|
(keymap :n "gi" vim.lsp.buf.implementation opts)
|
||||||
|
(keymap :n "K" vim.lsp.buf.hover opts)
|
||||||
|
(keymap :n "<leader>rn" vim.lsp.buf.rename opts)
|
||||||
|
(keymap :n "<leader>ca" vim.lsp.buf.code_action opts)
|
||||||
|
(keymap :n "[d" (fn [] (vim.diagnostic.jump {:count -1})) opts)
|
||||||
|
(keymap :n "]d" (fn [] (vim.diagnostic.jump {:count 1})) opts)
|
||||||
|
(keymap :n "<leader>e" vim.diagnostic.open_float opts)
|
||||||
|
(keymap :n "<leader>F" vim.lsp.buf.format opts))})
|
||||||
|
|
||||||
|
;; LSP commands (lspconfig-style)
|
||||||
|
(local usercmd vim.api.nvim_create_user_command)
|
||||||
|
|
||||||
|
(usercmd "LspInfo"
|
||||||
|
(fn [] (vim.cmd "checkhealth vim.lsp"))
|
||||||
|
{:desc "Show LSP info"})
|
||||||
|
|
||||||
|
(usercmd "LspStart"
|
||||||
|
(fn [opts]
|
||||||
|
(if (and opts.args (> (length opts.args) 0))
|
||||||
|
(vim.lsp.enable opts.args)
|
||||||
|
(vim.lsp.enable [:clojure_lsp :lua_ls :fennel_language_server])))
|
||||||
|
{:nargs "?"
|
||||||
|
:complete (fn [] [:clojure_lsp :lua_ls :fennel_language_server])
|
||||||
|
:desc "Start LSP server"})
|
||||||
|
|
||||||
|
(usercmd "LspStop"
|
||||||
|
(fn []
|
||||||
|
(each [_ client (ipairs (vim.lsp.get_clients))]
|
||||||
|
(vim.lsp.stop_client client.id)))
|
||||||
|
{:desc "Stop all LSP clients"})
|
||||||
|
|
||||||
|
(usercmd "LspRestart"
|
||||||
|
(fn []
|
||||||
|
(each [_ client (ipairs (vim.lsp.get_clients))]
|
||||||
|
(vim.lsp.stop_client client.id))
|
||||||
|
(vim.defer_fn (fn [] (vim.cmd "edit")) 100))
|
||||||
|
{:desc "Restart LSP clients"})
|
||||||
|
|
||||||
|
(usercmd "LspLog"
|
||||||
|
(fn [] (vim.cmd (.. "edit " (vim.lsp.get_log_path))))
|
||||||
|
{:desc "Open LSP log file"})
|
||||||
|
|
||||||
|
{}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
;; Plugin specs in Fennel
|
||||||
|
;; This file is compiled to lua/plugins/init.lua by nfnl
|
||||||
|
|
||||||
|
(local repo 1) ;; lazy.nvim spec index for the git repo (e.g., "folke/plugin")
|
||||||
|
(local lhs 1) ;; which-key spec index for the key sequence (see which-key below)
|
||||||
|
|
||||||
|
[;; Tokyonight - Colorscheme
|
||||||
|
{repo "folke/tokyonight.nvim"
|
||||||
|
:lazy false
|
||||||
|
:priority 1000
|
||||||
|
:config (fn []
|
||||||
|
(let [tokyonight (require :tokyonight)]
|
||||||
|
(tokyonight.setup
|
||||||
|
{:style "night" ; storm, moon, night, or day
|
||||||
|
:transparent false
|
||||||
|
:terminal_colors true}))
|
||||||
|
(vim.cmd.colorscheme "tokyonight"))}
|
||||||
|
|
||||||
|
;; Conjure - Interactive REPL for Fennel, Clojure, Lisp, etc.
|
||||||
|
{repo "Olical/conjure"
|
||||||
|
:ft ["fennel" "clojure" "lisp" "scheme" "racket" "lua"]
|
||||||
|
:config (fn []
|
||||||
|
;; Enable HUD floating window
|
||||||
|
(set vim.g.conjure#log#hud#enabled true))}
|
||||||
|
|
||||||
|
;; nvim-paredit - Structural editing for Lisp
|
||||||
|
;; Default keybindings: >)/<) slurp/barf forward, <(/>( slurp/barf backward
|
||||||
|
;; >e/<e drag element, >f/<f drag form, E/W/B element motions
|
||||||
|
{repo "julienvincent/nvim-paredit"
|
||||||
|
:ft ["fennel" "clojure" "lisp" "scheme" "racket"]
|
||||||
|
:config (fn []
|
||||||
|
(local paredit (require :nvim-paredit))
|
||||||
|
;; Use default keybindings (>), <), <(, >(, etc.)
|
||||||
|
(paredit.setup {})
|
||||||
|
|
||||||
|
;; Additional vim-sexp compatible bindings
|
||||||
|
(local keymap vim.keymap.set)
|
||||||
|
(local api paredit.api)
|
||||||
|
|
||||||
|
;; dsf - Splice (delete surrounding form)
|
||||||
|
(keymap :n "dsf" api.unwrap_form_under_cursor {:desc "Splice (delete surrounding form)"})
|
||||||
|
|
||||||
|
;; cse( cse) - Wrap in parens
|
||||||
|
(keymap :n "cse(" #(api.wrap_element_under_cursor "(" ")") {:desc "Wrap element in ()"})
|
||||||
|
(keymap :n "cse)" #(api.wrap_element_under_cursor "(" ")") {:desc "Wrap element in ()"})
|
||||||
|
|
||||||
|
;; cse[ cse] - Wrap in brackets
|
||||||
|
(keymap :n "cse[" #(api.wrap_element_under_cursor "[" "]") {:desc "Wrap element in []"})
|
||||||
|
(keymap :n "cse]" #(api.wrap_element_under_cursor "[" "]") {:desc "Wrap element in []"})
|
||||||
|
|
||||||
|
;; cse{ cse} - Wrap in braces
|
||||||
|
(keymap :n "cse{" #(api.wrap_element_under_cursor "{" "}") {:desc "Wrap element in {}"})
|
||||||
|
(keymap :n "cse}" #(api.wrap_element_under_cursor "{" "}") {:desc "Wrap element in {}"}))}
|
||||||
|
|
||||||
|
;; Mason - Package manager for LSP servers, DAP servers, linters, formatters
|
||||||
|
;; Run :MasonInstall clojure_lsp lua_ls to install servers
|
||||||
|
{repo "williamboman/mason.nvim"
|
||||||
|
:cmd ["Mason" "MasonInstall" "MasonUpdate"]
|
||||||
|
:build ":MasonUpdate"
|
||||||
|
:opts {:ui {:border "rounded"}}}
|
||||||
|
|
||||||
|
;; which-key - Shows keybinding hints
|
||||||
|
;; lhs constant defined at top - which-key specs use index [1] for the key sequence
|
||||||
|
{repo "folke/which-key.nvim"
|
||||||
|
:event "VeryLazy"
|
||||||
|
:opts {}
|
||||||
|
:config (fn [_ opts]
|
||||||
|
(local wk (require :which-key))
|
||||||
|
(wk.setup opts)
|
||||||
|
(wk.add
|
||||||
|
[{lhs "<leader>f" :group "find"}
|
||||||
|
{lhs "<leader>b" :group "buffer"}
|
||||||
|
{lhs "<leader>r" :group "refactor"}
|
||||||
|
{lhs "<leader>c" :group "code"}]))}]
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
-- Set leader keys before lazy loads
|
||||||
|
vim.g.mapleader = " "
|
||||||
|
vim.g.maplocalleader = " "
|
||||||
|
|
||||||
|
-- Bootstrap lazy.nvim
|
||||||
|
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
||||||
|
if not (vim.uv or vim.loop).fs_stat(lazypath) then
|
||||||
|
vim.fn.system({
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--filter=blob:none",
|
||||||
|
"https://github.com/folke/lazy.nvim.git",
|
||||||
|
"--branch=stable",
|
||||||
|
lazypath,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vim.opt.rtp:prepend(lazypath)
|
||||||
|
|
||||||
|
-- Setup lazy.nvim
|
||||||
|
require("lazy").setup({
|
||||||
|
{ import = "bootstrap" },
|
||||||
|
{ import = "plugins" },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Load Fennel config (compiled from fnl/config/init.fnl)
|
||||||
|
pcall(require, "config.init")
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
-- Core plugins that must be in Lua (nfnl compiles Fennel, so it can't be in Fennel)
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
"Olical/nfnl",
|
||||||
|
lazy = false,
|
||||||
|
config = function()
|
||||||
|
-- Create global commands that work without opening a fennel file first
|
||||||
|
local api = require("nfnl.api")
|
||||||
|
vim.api.nvim_create_user_command("NfnlCompileAllFiles", function(opts)
|
||||||
|
api["compile-all-files"](opts.args ~= "" and opts.args or nil)
|
||||||
|
end, { desc = "Compile all Fennel files", nargs = "?", complete = "dir" })
|
||||||
|
vim.api.nvim_create_user_command("NfnlCompileFile", function(opts)
|
||||||
|
api["compile-file"]({ path = opts.args ~= "" and opts.args or nil })
|
||||||
|
end, { desc = "Compile current or specified Fennel file", nargs = "?", complete = "file" })
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nvim-treesitter/nvim-treesitter",
|
||||||
|
build = ":TSUpdate",
|
||||||
|
config = function()
|
||||||
|
local langs = { "clojure", "fennel", "lua", "vim", "vimdoc", "query" }
|
||||||
|
|
||||||
|
-- Install parsers asynchronously on first load
|
||||||
|
vim.schedule(function()
|
||||||
|
local installed = require("nvim-treesitter").get_installed()
|
||||||
|
for _, lang in ipairs(langs) do
|
||||||
|
if not vim.list_contains(installed, lang) then
|
||||||
|
require("nvim-treesitter").install(lang)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Enable treesitter highlighting for supported filetypes
|
||||||
|
-- This is required for nvim-paredit to work (needs vim.treesitter.get_node())
|
||||||
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
|
pattern = { "clojure", "fennel", "lua", "vim", "query", "scheme", "lisp" },
|
||||||
|
callback = function()
|
||||||
|
pcall(vim.treesitter.start)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nvim-telescope/telescope.nvim",
|
||||||
|
tag = "0.1.8",
|
||||||
|
dependencies = { "nvim-lua/plenary.nvim" },
|
||||||
|
keys = {
|
||||||
|
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
|
||||||
|
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live grep" },
|
||||||
|
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
|
||||||
|
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help tags" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user