Files
nvim-config/fnl/plugins/init.fnl
2026-04-30 10:27:39 -04:00

209 lines
9.8 KiB
Fennel

;; 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) ;; lazy.nvim/which-key spec index for key sequence
(local rhs 2) ;; lazy.nvim keys spec index for command/action
[;; 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 []
;; HUD
(set vim.g.conjure#log#hud#enabled true)
(set vim.g.conjure#log#hud#width 0.42)
(set vim.g.conjure#log#hud#height 0.30)
;; Auto-start lein repl when no .nrepl-port found
(set vim.g.conjure#client#clojure#nrepl#connection#auto_repl#enabled true)
(set vim.g.conjure#client#clojure#nrepl#connection#auto_repl#cmd "lein repl :headless")
;; Show result inline as virtual text
(set vim.g.conjure#eval#result_register "*")
(set vim.g.conjure#eval#inline#enabled true)
(set vim.g.conjure#eval#inline#prefix "=> ")
;; Open log as vertical split
(set vim.g.conjure#log#split#enabled true)
(set vim.g.conjure#log#split#direction "vertical")
;; Clojure-specific: strip ANSI from nREPL output
(set vim.g.conjure#client#clojure#nrepl#eval#raw_out true)
;; Keymaps
(let [km vim.keymap.set]
(km :n "<localleader>lv"
(fn [] (vim.cmd "ConjureLogVSplit"))
{:desc "Conjure log vertical split"})
(km :n "<localleader>lt"
(fn [] (vim.cmd "ConjureLogTab"))
{:desc "Conjure log tab"})))}
;; nvim-parinfer - Automatic parenthesis balancing
;; See fnl/config/parinfer.fnl for default-enabled flag and toggle logic
{repo "gpanders/nvim-parinfer"
:ft ["fennel" "clojure" "lisp" "scheme" "racket" "carp" "timl"]
:init (fn []
(let [par (require :config.parinfer)]
(set vim.g.parinfer_enabled par.default-enabled)))
:keys [{lhs "<leader>tpi"
rhs (fn [] (let [par (require :config.parinfer)] (par.toggle)))
:desc "Toggle Parinfer"}]}
;; vim-sexp - Structural editing for S-expressions
;; with tpope's vim-sexp-mappings-for-regular-people
{repo "guns/vim-sexp"
:ft ["fennel" "clojure" "lisp" "scheme" "racket"]
:dependencies ["tpope/vim-repeat"
"tpope/vim-sexp-mappings-for-regular-people"]
:init (fn []
(set vim.g.sexp_filetypes "clojure,scheme,lisp,timl,fennel,racket")
;; Sync sexp auto-insert with parinfer default
(let [par (require :config.parinfer)]
(set vim.g.sexp_enable_insert_mode_mappings
(if par.default-enabled 0 1))))
:config (fn []
;; vim-sexp-mappings-for-regular-people's setup is VimEnter-gated.
;; Under lazy.nvim :ft loading, VimEnter has already fired by the
;; time the plugin sources, so its FileType autocmd never registers
;; and the slurp/barf/swap mappings never attach. Register our own
;; FileType autocmd that maps the <Plug>s directly.
(let [fts (or vim.g.sexp_filetypes "clojure,scheme,lisp,timl,fennel,racket")
attach (fn []
(let [m (fn [lhs rhs]
(vim.keymap.set :n lhs rhs
{:buffer 0 :silent true :remap true}))]
(m "<I" "<Plug>(sexp_insert_at_list_head)")
(m ">I" "<Plug>(sexp_insert_at_list_tail)")
(m "<f" "<Plug>(sexp_swap_list_backward)")
(m ">f" "<Plug>(sexp_swap_list_forward)")
(m "<e" "<Plug>(sexp_swap_element_backward)")
(m ">e" "<Plug>(sexp_swap_element_forward)")
(m ">(" "<Plug>(sexp_emit_head_element)")
(m "<)" "<Plug>(sexp_emit_tail_element)")
(m "<(" "<Plug>(sexp_capture_prev_element)")
(m ">)" "<Plug>(sexp_capture_next_element)")
(m "dsf" "<Plug>(sexp_splice_list)")))]
(vim.api.nvim_create_augroup "sexp_regular_people_fix" {:clear true})
(vim.api.nvim_create_autocmd "FileType"
{:group "sexp_regular_people_fix"
:pattern (vim.split fts ",")
:callback attach})
;; Attach to current buffer (the FileType that triggered lazy load
;; already fired before this autocmd was registered).
(when (vim.tbl_contains (vim.split fts ",") vim.bo.filetype)
(attach))))}
;; Mason - Package manager for LSP servers, DAP servers, linters, formatters
{repo "williamboman/mason.nvim"
:lazy false
:build ":MasonUpdate"
:opts {:ui {:border "rounded"}}}
;; Auto-install LSP servers and tools via Mason
{repo "WhoIsSethDaniel/mason-tool-installer.nvim"
:lazy false
:dependencies ["williamboman/mason.nvim"]
:opts {:ensure_installed ["clojure-lsp" "clj-kondo" "fennel-language-server" "lua-language-server"]}}
;; Hop - EasyMotion-like word jumping
{repo "smoka7/hop.nvim"
:version "*"
:event "VeryLazy"
:config (fn []
(let [hop (require :hop)]
(hop.setup)
(vim.keymap.set :n "gw" (fn [] (hop.hint_words))
{:desc "Hop to word"})))}
;; LispSchool - Interactive structural editing tutorial
;; Source: fnl/lisp-school.fnl (compiled by nfnl)
{:name "lisp-school"
:dir (vim.fn.stdpath "config")
:cmd ["LispSchool" "LispSchoolNext"]
:config (fn []
(vim.api.nvim_create_user_command "LispSchool"
(fn [] (let [ls (require :lisp-school)] (ls.start)))
{:desc "Start LispSchool tutorial"})
(vim.api.nvim_create_user_command "LispSchoolNext"
(fn [] (let [ls (require :lisp-school)] (ls.next)))
{:desc "Next LispSchool lesson"}))}
;; 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"}
{lhs "<leader>h" :group "hunk"}
{lhs "<leader>t" :group "toggle"}
{lhs "<leader>l" :group "log"}]))}
;; blink.cmp - Fast completion engine with LSP support
{repo "saghen/blink.cmp"
:version "*"
:event ["InsertEnter" "CmdlineEnter"]
:opts {:keymap {:preset "super-tab"}
:completion {:documentation {:auto_show true}}
:sources {:default ["lsp" "path" "buffer"]}}}
;; gitsigns.nvim - Git diff signs in the sign column
{repo "lewis6991/gitsigns.nvim"
:event ["BufReadPre" "BufNewFile"]
:opts {:signs {:add {:text "│"}
:change {:text "│"}
:delete {:text "_"}
:topdelete {:text "‾"}
:changedelete {:text "~"}
:untracked {:text "┆"}}
:sign_priority 6
:update_debounce 100
:max_file_length 40000
:preview_config {:border "single"
:style "minimal"
:relative "cursor"
:row 0
:col 1}
:on_attach (fn [bufnr]
(local gs (require :gitsigns))
(fn map [mode l r opts]
(let [o (or opts {})]
(set o.buffer bufnr)
(vim.keymap.set mode l r o)))
(map :n "]c"
(fn []
(if vim.wo.diff "]c"
(do (vim.schedule #(gs.next_hunk)) "<Ignore>")))
{:expr true :desc "Next hunk"})
(map :n "[c"
(fn []
(if vim.wo.diff "[c"
(do (vim.schedule #(gs.prev_hunk)) "<Ignore>")))
{:expr true :desc "Prev hunk"})
(map [:n :v] "<leader>hs" ":Gitsigns stage_hunk<CR>" {:desc "Stage hunk"})
(map [:n :v] "<leader>hr" ":Gitsigns reset_hunk<CR>" {:desc "Reset hunk"})
(map :n "<leader>hS" gs.stage_buffer {:desc "Stage buffer"})
(map :n "<leader>hu" gs.undo_stage_hunk {:desc "Undo stage"})
(map :n "<leader>hR" gs.reset_buffer {:desc "Reset buffer"})
(map :n "<leader>hp" gs.preview_hunk {:desc "Preview hunk"})
(map :n "<leader>hb" #(gs.blame_line {:full true}) {:desc "Blame line"})
(map :n "<leader>tb" gs.toggle_current_line_blame {:desc "Toggle blame"})
(map :n "<leader>hd" gs.diffthis {:desc "Diff this"})
(map :n "<leader>hD" #(gs.diffthis "~") {:desc "Diff this ~"})
(map :n "<leader>td" gs.toggle_deleted {:desc "Toggle deleted"})
(map [:o :x] "ih" ":<C-U>Gitsigns select_hunk<CR>" {:desc "Select hunk"}))}}]