about summary refs log tree commit diff
path: root/fnl/nvrc
diff options
context:
space:
mode:
authorsefidel <contact@sefidel.net>2022-02-10 00:24:03 +0900
committersefidel <contact@sefidel.net>2022-02-10 00:24:03 +0900
commit72d448e384249103748ee83b587c45924e4bc44d (patch)
tree2aa05a6aaf8c7aa37a8c278fd2fede6e62ff2218 /fnl/nvrc
downloadnvimrc-72d448e384249103748ee83b587c45924e4bc44d.tar.gz
nvimrc-72d448e384249103748ee83b587c45924e4bc44d.zip
Initial commit
Diffstat (limited to 'fnl/nvrc')
-rw-r--r--fnl/nvrc/colors.fnl81
-rw-r--r--fnl/nvrc/events.fnl47
-rw-r--r--fnl/nvrc/ignite.fnl6
-rw-r--r--fnl/nvrc/keymaps.fnl45
-rw-r--r--fnl/nvrc/lib/io.fnl31
-rw-r--r--fnl/nvrc/macro/color.fnl9
-rw-r--r--fnl/nvrc/macro/event.fnl63
-rw-r--r--fnl/nvrc/macro/keymap.fnl46
-rw-r--r--fnl/nvrc/macro/misc.fnl1
-rw-r--r--fnl/nvrc/macro/pack.fnl80
-rw-r--r--fnl/nvrc/macro/set.fnl79
-rw-r--r--fnl/nvrc/macro/thread.fnl15
-rw-r--r--fnl/nvrc/macro/toolkit.fnl39
-rw-r--r--fnl/nvrc/options.fnl51
-rw-r--r--fnl/nvrc/pack.fnl34
-rw-r--r--fnl/nvrc/packs/blankline.fnl11
-rw-r--r--fnl/nvrc/packs/cmp.fnl56
-rw-r--r--fnl/nvrc/packs/feline.fnl144
-rw-r--r--fnl/nvrc/packs/gitsigns.fnl7
-rw-r--r--fnl/nvrc/packs/lsp_signature.fnl15
-rw-r--r--fnl/nvrc/packs/lspconfig.fnl50
-rw-r--r--fnl/nvrc/packs/nvimtree.fnl44
-rw-r--r--fnl/nvrc/packs/telescope.fnl49
-rw-r--r--fnl/nvrc/packs/treesitter.fnl29
-rw-r--r--fnl/nvrc/utils.fnl127
25 files changed, 1159 insertions, 0 deletions
diff --git a/fnl/nvrc/colors.fnl b/fnl/nvrc/colors.fnl
new file mode 100644
index 0000000..854b79f
--- /dev/null
+++ b/fnl/nvrc/colors.fnl
@@ -0,0 +1,81 @@
+(local colors {:bg "#202020"
+               :nvimbg "#151515"
+               :fg "#cbc0ab"
+               :white "#e8e8d3"
+               :black "#181818"
+               :black2 "#101010"
+               :bblack "#252525"
+               :grey "#888888"
+               :grey2 "#666666"
+               :bgrey "#999999"
+               :red "#cf6a4c"
+               :orange "#fabb6e"
+               :yellow "#fad07a"
+               :lyellow "#ffe2a9"
+               :green "#99ad6a"
+               :skyblue "#8fbfdc"
+               :blue "#8197bf"
+               :violet "#c6b6ee"
+               :magenta "#f0a0c0"
+               :sign "#333333"})
+
+(fn colors.apply []
+  (local {: highlight} (require :nvrc.macro.color))
+  (vim.cmd "colorscheme jellybeans")
+
+  ; Buffer
+  (highlight :EndOfBuffer {:fg (. colors :bg)})
+  (highlight :FloatBorder {:fg (. colors :blue)})
+  (highlight :NormalFloat {:bg (. colors :bblack)})
+
+  ; Pmenu
+  (highlight :Pmenu {:bg (. colors :bblack)})
+  (highlight :PmenuSbar {:bg (. colors :bblack)})
+  (highlight :PmenuSel {:fg (. colors :orange) :bg (. colors :sign)})
+  (highlight :PmenuThumb {:bg (. colors :skyblue)})
+  (highlight :CmpItemAbbr {:fg (. colors :fg)})
+  (highlight :CmpItemAbbrMatch {:fg (. colors :fg)})
+  (highlight :CmpItemKind {:fg (. colors :fg)})
+  (highlight :CmpItemMenu {:fg (. colors :fg)})
+
+  ; Misc
+  (highlight :StatusLine {:bg (. colors :black)})
+  (highlight :StatusLineNC {:fg (. colors :grey) :bg (. colors :black) :underline true})
+  (highlight :LineNr {:fg (. colors :grey)})
+  (highlight :NvimInternalError {:fg (. colors :red)})
+  (highlight :VertSplit {:fg (. colors :grey2)})
+
+  ; Gitsigns
+  (highlight :DiffAdd {:fg (. colors :green) :bg (. colors :sign)})
+  (highlight :DiffChange {:fg (. colors :yellow) :bg (. colors :sign)})
+  (highlight :DiffChangeDelete {:fg (. colors :red) :bg (. colors :sign)})
+  (highlight :DiffModified {:fg (. colors :red) :bg (. colors :sign)})
+  (highlight :DiffDelete {:fg (. colors :red) :bg (. colors :sign)})
+
+  ; Nvimtree
+  (highlight :NvimTreeNormal {:bg (. colors :black)})
+  (highlight :NvimTreeNormalNC {:bg (. colors :black)})
+  (highlight :NvimTreeStatuslineNC {:fg (. colors :black) :bg (. colors :black)})
+  (highlight :NvimTreeVertSplit {:fg (. colors :black) :bg (. colors :black)})
+  (highlight :NvimTreeWindowPicker {:fg (. colors :red) :bg (. colors :black2)})
+  (highlight :NvimTreeIndentMarker {:fg (. colors :grey)})
+  (highlight :NvimTreeGitDirty {:fg (. colors :red)})
+  (highlight :NvimTreeRootFolder {:fg (. colors :red) :underline true})
+  (highlight :NvimTreeEmptyFolderName {:fg (. colors :skyblue)})
+  (highlight :NvimTreeFolderIcon {:fg (. colors :skyblue)})
+  (highlight :NvimTreeFolderName {:fg (. colors :skyblue)})
+  (highlight :NvimTreeOpenedFolderName {:fg (. colors :magenta)})
+  (highlight :NvimTreeEndOfBuffer {:fg (. colors :black2)})
+
+  ; Telescope
+  (highlight :TelescopeBorder {:fg (. colors :fg)})
+  (highlight :TelescopePromptBorder {:fg (. colors :fg)})
+  (highlight :TelescopePromptNormal {:fg (. colors :fg)})
+  (highlight :TelescopePromptPrefix {:fg (. colors :red)})
+  (highlight :TelescopeNormal {:bg :NONE})
+  (highlight :TelescopePreviewTitle {:fg (. colors :nvimbg) :bg (. colors :green)})
+  (highlight :TelescopePromptTitle {:fg (. colors :nvimbg) :bg (. colors :red)})
+  (highlight :TelescopeResultsTitle {:fg (. colors :nvimbg) :bg (. colors :skyblue)})
+  (highlight :TelescopeSelection {:link :Search}))
+
+colors
diff --git a/fnl/nvrc/events.fnl b/fnl/nvrc/events.fnl
new file mode 100644
index 0000000..a798fcf
--- /dev/null
+++ b/fnl/nvrc/events.fnl
@@ -0,0 +1,47 @@
+(import-macros {: au!
+                : ac!} :nvrc.macro.event)
+(import-macros {: set!
+                : setl!} :nvrc.macro.set)
+(local {: echo!} (require :nvrc.lib.io))
+
+(local {: line
+        : mode} vim.fn)
+(fn cmd! [...] (vim.cmd ...))
+(fn bufexists? [...] (= (vim.fn.bufexists ...) 1))
+
+; Restore cursor style to beam on exit
+(au! restore-cursor
+          (ac! VimLeave * #(set! guicursor ["a:ver75-blinkon0"])))
+
+; Restore the last cursor line
+(au! restore-last-cursor-line
+          (ac! BufReadPost * #(if (and (> (line "'\"") 1)
+                                            (<= (line "'\"") (line "$")))
+                                     (cmd! "normal! g'\""))))
+
+; Resize splits on window resize
+(au! resize-splits-on-resize
+          (ac! VimResized * "wincmd ="))
+
+;; Read file when it changes on disk
+(au! read-file-on-disk-change
+          (ac! [FocusGained BufEnter CursorHold CursorHoldI] *
+                    #(if (and (not= :c (mode))
+                              (not (bufexists? "[Command Line]")))
+                       (cmd! "checktime")))
+          (ac! FileChangedShellPost *
+                    #(echo! "File changed on disk. Buffer reloaded.")))
+
+(au! terminal-options
+          ;; Enter Terminal-mode (insert) automatically
+          (ac! TermOpen * "startinsert")
+          ;; Disables line number on terminal buffers
+          (ac! TermOpen * #(do
+                                  (setl! nonumber)
+                                  (setl! norelativenumber)))
+          ;; Disables spell on terminal buffers
+          (ac! TermOpen * #(setl! nospell))
+          ;; Disables sign column on terminal buffers
+          (ac! TermOpen * #(setl! signcolumn :no))
+          ;; Disables colorcolumn on terminal buffers
+          (ac! TermOpen * #(setl! colorcolumn [])))
diff --git a/fnl/nvrc/ignite.fnl b/fnl/nvrc/ignite.fnl
new file mode 100644
index 0000000..04f416b
--- /dev/null
+++ b/fnl/nvrc/ignite.fnl
@@ -0,0 +1,6 @@
+; Get the rocket going
+
+(require :nvrc.options)
+(require :nvrc.pack)
+(require :nvrc.keymaps)
+(require :nvrc.events)
diff --git a/fnl/nvrc/keymaps.fnl b/fnl/nvrc/keymaps.fnl
new file mode 100644
index 0000000..67b0d70
--- /dev/null
+++ b/fnl/nvrc/keymaps.fnl
@@ -0,0 +1,45 @@
+(import-macros {: map!} :nvrc.macro.keymap)
+(import-macros {: setv!} :nvrc.macro.set)
+
+(map! [n] :<space> "" "")
+(setv! mapleader " ")
+
+(map! [n :silent] :<leader>fe ":NvimTreeToggle <cr>")
+(map! [n :silent] :<leader>ft ":NvimTreeFocus <cr>")
+
+(map! [n :silent] :<leader>/
+      ":lua require('Comment.api').toggle_current_linewise() <cr>")
+(map! [v :silent] :<leader>/
+      ":lua require('Comment.api').toggle_linewise_op(vim.fn.visualmode()) <cr>")
+
+(map! [n :silent] :<leader>ff ":Telescope find_files <cr>")
+(map! [n :silent] :<leader>fc ":Telescope grep_string <cr>")
+(map! [n :silent] :<leader>fs ":Telescope live_grep <cr>")
+(map! [n :silent] :<leader>fa
+      ":Telescope find_files follow=true no_ignore=true hidden=true <cr>")
+(map! [n :silent] :<leader>fb ":Telescope buffers <cr>")
+(map! [n :silent] :<leader>gc ":Telescope git_commits <cr>")
+(map! [n :silent] :<leader>gs ":Telescope git_status <cr>")
+
+(map! [n] :f :<plug>Lightspeed_f)
+(map! [n] :F :<plug>Lightspeed_F)
+(map! [n] :t :<plug>Lightspeed_t)
+(map! [n] :T :<plug>Lightspeed_T)
+
+(map! [n :silent] :<leader>rm ":TZMinimalist <cr>")
+(map! [n :silent] :<leader>rf ":TZFocus <cr>")
+(map! [n :silent] :<leader>ra ":TZAtaraxis <cr>")
+
+(map! [t] :jk "<C-\\><C-n>")
+(map! [t :silent] :JK "<C-\\><C-n> :lua require ('nvrc.utils').close_buf() <cr>")
+(map! [n :silent] :<leader>tl ":Telescope terms <cr>")
+; FIXME This opens on top of existing vertical/horizontal term
+(map! [n :silent] :<leader>th ":execute 15 .. 'new +terminal' | let b:term_type = 'hori' <cr>")
+(map! [n :silent] :<leader>tv ":execute 'vnew +terminal' | let b:term_type = 'vert' <cr>")
+(map! [n :silent] :<leader>tn ":execute 'terminal' | let b:term_type = 'wind' <cr>")
+
+(map! [n :silent] :<leader>q ":lua require('nvrc.utils').close_buf() <cr>")
+(map! [n :silent] :<leader>ya ":%y+ <cr>")
+(map! [n :silent] :<leader>bn ":enew <cr>")
+(map! [n :silent] :<leader>wn ":tabnew <cr>")
+(map! [n :silent] :<leader>lt ":set nu! <cr>")
diff --git a/fnl/nvrc/lib/io.fnl b/fnl/nvrc/lib/io.fnl
new file mode 100644
index 0000000..7d20ed7
--- /dev/null
+++ b/fnl/nvrc/lib/io.fnl
@@ -0,0 +1,31 @@
+(fn cmd! [...] (vim.cmd ...))
+(local {: format
+        : sub} string)
+
+(fn str? [x]
+  (= :string (type x)))
+
+(lambda double-quote [s]
+  "Add double quotes at the beginning and end of the string."
+  (assert (str? s) "expected string for s")
+  (format "\"%s\"" s))
+
+(lambda echo! [s]
+  "Print a vim message without any format."
+  (cmd! (format "echom %s" (double-quote s))))
+
+(lambda warn! [s]
+  "Print a vim message with a warning format."
+  (cmd! (format "echohl WarningMsg
+                 echom %s
+                 echohl None" (double-quote s))))
+
+(lambda err! [s]
+  "Print a vim message with an error format."
+  (cmd! (format "echohl ErrorMsg
+                 echom %s
+                 echohl None" (double-quote s))))
+
+{: echo!
+ : warn!
+ : err!}
diff --git a/fnl/nvrc/macro/color.fnl b/fnl/nvrc/macro/color.fnl
new file mode 100644
index 0000000..9636956
--- /dev/null
+++ b/fnl/nvrc/macro/color.fnl
@@ -0,0 +1,9 @@
+(fn tbl? [x]
+  (= :table (type x)))
+
+(fn highlight [group-arg colset]
+  "Add a highlighting group."
+  (each [_ group (ipairs (if (tbl? group-arg) group-arg [group-arg]))]
+      (vim.api.nvim_set_hl 0 group colset)))
+
+{: highlight}
diff --git a/fnl/nvrc/macro/event.fnl b/fnl/nvrc/macro/event.fnl
new file mode 100644
index 0000000..3453431
--- /dev/null
+++ b/fnl/nvrc/macro/event.fnl
@@ -0,0 +1,63 @@
+(import-macros {: as->} :nvrc.macro.thread)
+
+(local {: format} string)
+(local {: insert : concat} table)
+
+(local {: fn? : gensym-checksum : vlua} (require :nvrc.macro.toolkit))
+
+(fn last [xs]
+  (. xs (length xs)))
+
+(fn ->str [x]
+  (tostring x))
+
+(fn includes? [xs x]
+  (accumulate [is? false _ v (ipairs xs) :until is?]
+    (= v x)))
+
+(lambda au! [name ...]
+  "Defines an autocommand group using the vim API."
+  `(do
+     (vim.cmd ,(format "augroup %s" name))
+     (vim.cmd :autocmd!)
+     (do
+       ,...)
+     (vim.cmd "augroup END")))
+
+(lambda aub! [name ...]
+  "Defines a buffer-local autocommand group using the vim API."
+  `(do
+     (vim.cmd ,(format "augroup %s" name))
+     (vim.cmd "autocmd! * <buffer>")
+     (do
+       ,...)
+     (vim.cmd "augroup END")))
+
+(lambda ac! [events pattern ...]
+  "Defines an autocommand using the vim API."
+  (let [events (as-> [$ events] (if (sequence? $) $ [$])
+                     (icollect [_ v (ipairs $)]
+                       (->str v)) (concat $ ","))
+        pattern (as-> [$ pattern] (if (sequence? $) $ [$])
+                      (icollect [_ v (ipairs $)]
+                        (->str v)) (concat $ ","))
+        once? (or (includes? [...] `++once) (includes? [...] :++once))
+        nested? (or (includes? [...] `++nested) (includes? [...] :++nested))
+        command (last [...])]
+    (if (fn? command)
+        (let [fsym (gensym-checksum "__" command)]
+          `(do
+             (global ,fsym ,command)
+             (vim.cmd ,(format (if (and once? nested?)
+                                   "autocmd %s %s ++once ++nested call %s" once?
+                                   "autocmd %s %s ++once call %s" nested?
+                                   "autocmd %s %s ++nested call %s"
+                                   "autocmd %s %s call %s")
+                               events pattern (vlua fsym)))))
+        `(vim.cmd ,(format (if (and once? nested?)
+                               "autocmd %s %s ++once ++nested %s" once?
+                               "autocmd %s %s ++once %s" nested?
+                               "autocmd %s %s ++nested %s" "autocmd %s %s %s")
+                           events pattern command)))))
+
+{: au! : aub! : ac!}
diff --git a/fnl/nvrc/macro/keymap.fnl b/fnl/nvrc/macro/keymap.fnl
new file mode 100644
index 0000000..7a784ab
--- /dev/null
+++ b/fnl/nvrc/macro/keymap.fnl
@@ -0,0 +1,46 @@
+(local {: gmatch} string)
+(local {: insert} table)
+
+(local {: fn?} (require :nvrc.macro.toolkit))
+
+(fn ->str [x]
+  (tostring x))
+
+(fn nil? [x]
+  (= nil x))
+
+(fn str? [x]
+  (= :string (type x)))
+
+(fn tbl? [x]
+  (= :table (type x)))
+
+(lambda map! [[modes & options] lhs rhs ?desc]
+  "Defines a new mapping using the lua API.
+  Supports all the options that the API supports."
+  (assert-compile (sym? modes) "expected symbol for modes" modes)
+  (assert-compile (tbl? options) "expected table for options" options)
+  (assert-compile (str? lhs) "expected string for lhs" lhs)
+  (assert-compile (or (str? rhs) (list? rhs) (fn? rhs) (sym? rhs))
+                  "expected string or list or function or symbol for rhs" rhs)
+  (assert-compile (or (nil? ?desc) (str? ?desc))
+                  "expected string or nil for description" ?desc)
+  (let [modes (icollect [char (gmatch (->str modes) ".")]
+                char)
+        options (collect [_ v (ipairs options)]
+                  (->str v)
+                  true)
+        rhs (if (and (not (fn? rhs)) (list? rhs)) `#,rhs rhs)
+        desc (if (and (not ?desc) (or (fn? rhs) (sym? rhs))) (view rhs) ?desc)
+        options (if desc (doto options (tset :desc desc)) options)]
+    `(vim.keymap.set ,modes ,lhs ,rhs ,options)))
+
+(lambda mapb! [[modes & options] lhs rhs ?description]
+  "Defines a new mapping using the lua API.
+  Supports all the options that the API supports.
+  Automatically sets the `:buffer` option."
+  (let [options (doto options
+                  (insert :buffer))]
+    (map! [modes (unpack options)] lhs rhs ?description)))
+
+{: map! : mapb!}
diff --git a/fnl/nvrc/macro/misc.fnl b/fnl/nvrc/macro/misc.fnl
new file mode 100644
index 0000000..cde60ce
--- /dev/null
+++ b/fnl/nvrc/macro/misc.fnl
@@ -0,0 +1 @@
+{:disable-builtins! #(vim.tbl_map #(tset vim.g (.. :loaded_ $) 1) $)}
diff --git a/fnl/nvrc/macro/pack.fnl b/fnl/nvrc/macro/pack.fnl
new file mode 100644
index 0000000..5ba7896
--- /dev/null
+++ b/fnl/nvrc/macro/pack.fnl
@@ -0,0 +1,80 @@
+(fn str? [x]
+  (= :string (type x)))
+
+(fn nil? [x]
+  (= nil x))
+
+(fn tbl? [x]
+  (= :table (type x)))
+
+(local {: format} string)
+(local {: insert} table)
+
+(global nvrc/pack [])
+(global nvrc/rock [])
+
+(lambda pack [identifier ?options]
+  "Returns a mixed table with the identifier as the first sequential element
+  and options as hash-table items.
+  See https://github.com/wbthomason/packer.nvim for information about the
+  options."
+  (assert-compile (str? identifier) "expected string for identifier" identifier)
+  (assert-compile (or (nil? ?options) (tbl? ?options))
+                  "expected table for options" ?options)
+  (let [options (or ?options {})
+        options (collect [k v (pairs options)]
+                         (if
+                           (= k :req) (values :config (format "require('nvrc.packs.%s')" v))
+                           (= k :init) (values :config (format "require('%s').setup()" v))
+                           (= k :defer) (values :setup (format "require('nvrc.utils').defer_unpack('%s', 5)" v))
+                           (values k v)))]
+    (doto options
+          (tset 1 identifier))))
+
+(lambda pack! [identifier ?options]
+  "Declares a plugin with its options.
+  This is a mixed table saved on the global compile-time variable nvrc/pack.
+  See https://github.com/wbthomason/packer.nvim for information about the
+  options."
+  (assert-compile (str? identifier) "expected string for identifier" identifier)
+  (assert-compile (or (nil? ?options) (tbl? ?options))
+                  "expected table for options" ?options)
+  (insert nvrc/pack (pack identifier ?options)))
+
+(lambda rock [identifier ?options]
+  "Returns a mixed table with the identifier as the first sequential element
+  and options as hash-table items.
+  See https://github.com/wbthomason/packer.nvim for information about the
+  options."
+  (assert-compile (str? identifier) "expected string for identifier" identifier)
+  (assert-compile (or (nil? ?options) (tbl? ?options))
+                  "expected table for options" ?options)
+  (let [options (or ?options {})]
+    (doto options
+          (tset 1 identifier))))
+
+(lambda rock! [identifier ?options]
+  "Declares a plugin with its options.
+  This is a mixed table saved on the global compile-time variable nvrc/rock.
+  See https://github.com/wbthomason/packer.nvim for information about the
+  options."
+  (assert-compile (str? identifier) "expected string for identifier" identifier)
+  (assert-compile (or (nil? ?options) (tbl? ?options))
+                  "expected table for options" ?options)
+  (insert nvrc/rock (rock identifier ?options)))
+
+(lambda unpack! []
+  "Initializes the plugin manager with the previously declared plugins and
+  their options."
+  (let [packs (icollect [_ v (ipairs nvrc/pack)]
+                        `(use ,v))
+        rocks (icollect [_ v (ipairs nvrc/rock)]
+                        `(use_rocks ,v))]
+    `((. (require :packer) :startup) #(do
+         ,(unpack (icollect [_ v (ipairs packs) :into rocks] v))))))
+
+{: pack
+ : pack!
+ : rock
+ : rock!
+ : unpack!}
diff --git a/fnl/nvrc/macro/set.fnl b/fnl/nvrc/macro/set.fnl
new file mode 100644
index 0000000..144bdef
--- /dev/null
+++ b/fnl/nvrc/macro/set.fnl
@@ -0,0 +1,79 @@
+(local {: fn? : gensym-checksum : vlua} (require :nvrc.macro.toolkit))
+
+(fn str? [x]
+  (= :string (type x)))
+
+(fn ->str [x]
+  (tostring x))
+
+(fn nil? [x]
+  (= nil x))
+
+(fn includes? [xs x]
+  (accumulate [is? false _ v (ipairs xs) :until is?]
+    (= v x)))
+
+(lambda set! [name ?value]
+  "Set a vim option via the lua API.
+  The name of the option must a symbol.
+  If no value is specified, if the name begins with 'no' the value
+  becomes false, true otherwise."
+  (assert-compile (sym? name) "expected symbol for name" name)
+  (let [name (->str name)
+        value (or ?value (not (name:match "^no")))
+        name (or (name:match "^no(.+)$") name)]
+    (if (fn? value)
+      (let [vsym (gensym-checksum "__" value)]
+        `(do
+            (global ,vsym ,value)
+            (tset vim.opt ,name ,(vlua vsym))))
+      (match (name:sub -1)
+        "+" `(: (. vim.opt ,(name:sub 1 -2)) :append ,value)
+        "-" `(: (. vim.opt ,(name:sub 1 -2)) :remove ,value)
+        "^" `(: (. vim.opt ,(name:sub 1 -2)) :prepend ,value)
+        _ `(tset vim.opt ,name ,value)))))
+
+(lambda setl! [name ?value]
+  "Set a vim local option via the lua API.
+  The name of the option must a symbol.
+  If no value is specified, if the name begins with 'no' the value
+  becomes false, true otherwise."
+  (assert-compile (sym? name) "expected symbol for name" name)
+  (let [name (->str name)
+        value (or ?value
+                  (not (name:match "^no")))
+        name (or (name:match "^no(.+)$")
+                 name)]
+    (if (fn? value)
+      (let [fsym (gensym-checksum "__" value)]
+        `(do
+           (global ,fsym ,value)
+           (tset vim.opt_local ,name ,(vlua fsym))))
+      (match (name:sub -1)
+        :+ `(: (. vim.opt_local ,(name:sub 1 -2)) :append ,value)
+        :- `(: (. vim.opt_local ,(name:sub 1 -2)) :remove ,value)
+        :^ `(: (. vim.opt_local ,(name:sub 1 -2)) :prepend ,value)
+        _ `(tset vim.opt_local ,name ,value)))))
+
+(lambda setv! [name value]
+  "Set a vim variable via the lua API.
+  The name can be either a symbol or a string.
+  If the name begins with [gbwt] followed by [/:.], the name
+  is scoped to the respective scope."
+  (assert-compile (or (str? name) (sym? name))
+                  "expected string or symbol for name" name)
+  (let [name (->str name)
+        scope (when (includes? ["g/" "b/" "w/" "t/"
+                                "g." "b." "w." "t."
+                                "g:" "b:" "w:" "t:"] (name:sub 1 2))
+                (name:sub 1 1))
+        name (if
+               (nil? scope) name
+               (name:sub 3))]
+    `(tset ,(match scope
+              :b 'vim.b
+              :w 'vim.w
+              :t 'vim.t
+              _ 'vim.g) ,name ,value)))
+
+{: set! : setl! : setv!}
diff --git a/fnl/nvrc/macro/thread.fnl b/fnl/nvrc/macro/thread.fnl
new file mode 100644
index 0000000..9060b04
--- /dev/null
+++ b/fnl/nvrc/macro/thread.fnl
@@ -0,0 +1,15 @@
+(local {: insert} table)
+
+(lambda as-> [[binding expr] ...]
+  "A threading macro where the first argument is the value binded to the
+  second argument, which must be a symbol.
+  This binding is valid for the whole body of the threading macro."
+  (assert-compile (sym? binding) "expected symbol for binding" binding)
+  `(let [,binding ,expr
+         ,(unpack (accumulate [exprs [] _ expr (ipairs [...])]
+                    (doto exprs
+                      (insert binding)
+                      (insert expr))))]
+     ,binding))
+
+{: as->}
diff --git a/fnl/nvrc/macro/toolkit.fnl b/fnl/nvrc/macro/toolkit.fnl
new file mode 100644
index 0000000..8a2bc50
--- /dev/null
+++ b/fnl/nvrc/macro/toolkit.fnl
@@ -0,0 +1,39 @@
+(local {: format} string)
+
+(fn ->str [x]
+  (tostring x))
+
+(fn head [xs]
+  (. xs 1))
+
+(fn fn? [x]
+  "Returns whether the parameter(s) is a function.
+  A function is defined as any list with 'fn or 'hashfn as
+  their first element."
+  (and (list? x) (or (= `fn (head x)) (= `hashfn (head x)))))
+
+(lambda gensym-checksum [...]
+  "Generates a new symbol from the checksum of the object
+  passed as a parameter.
+  The parameter first is casted into a string using the
+  function `fennel.view`.
+  If only one parameter is passed to the function the return
+  value is the checksum as a symbol.
+  If two parameters are passed, the first one is considered
+  the prefix.
+  If three parameters are passed, the first one is considered
+  the prefix and the last one is considered the suffix.
+  This function depends on the md5 library and the fennel library."
+  (match [...]
+    [prefix object suffix] (let [{: view} (require :fennel)
+                                 {:sumhexa md5} (require :md5)]
+                              (sym (.. prefix (md5 (view object)) suffix)))
+    [prefix object] (gensym-checksum prefix object "")
+    [object] (gensym-checksum "" object "")))
+
+(lambda vlua [x]
+  "Return a symbol mapped to `v:lua.%s()`, where `%s` is the symbol."
+  (assert-compile (sym? x) "expected symbol for x" x)
+  (format "v:lua.%s()" (->str x)))
+
+{: fn? : gensym-checksum : vlua}
diff --git a/fnl/nvrc/options.fnl b/fnl/nvrc/options.fnl
new file mode 100644
index 0000000..b9a59bc
--- /dev/null
+++ b/fnl/nvrc/options.fnl
@@ -0,0 +1,51 @@
+(import-macros {: set! : setv!} :nvrc.macro.set)
+(local {: disable-builtins!} (require :nvrc.macro.misc))
+
+(set! clipboard :unnamedplus)
+
+; Interface
+(set! cul)
+(set! cmdheight 1)
+(set! number)
+(set! numberwidth 2)
+(set! shortmess+ :sI)
+(set! splitbelow)
+(set! splitright)
+(set! termguicolors)
+(set! lazyredraw)
+
+; Style
+(set! expandtab)
+(set! tabstop 8)
+(set! shiftwidth 2)
+(set! smartindent)
+(set! list)
+(set! listchars {:tab ">-" :extends ">" :precedes "<" :trail "*" :nbsp "+"})
+
+; Miscellaneous
+(set! ignorecase)
+(set! smartcase)
+(set! mouse :a)
+(set! timeoutlen 400)
+(set! updatetime 250)
+(set! undofile)
+(set! whichwrap+ "<>[]hl")
+
+(disable-builtins! [:2html_plugin
+                   :getscript
+                   :getscriptPlugin
+                   :gzip
+                   :logipat
+                   :netrw
+                   :netrwPlugin
+                   :netrwSettings
+                   :netrwFileHandlers
+                   :matchit
+                   :tar
+                   :tarPlugin
+                   :rrhelper
+                   :spellfile_plugin
+                   :vimball
+                   :vimballPlugin
+                   :zip
+                   :zipPlugin])
diff --git a/fnl/nvrc/pack.fnl b/fnl/nvrc/pack.fnl
new file mode 100644
index 0000000..a1c611a
--- /dev/null
+++ b/fnl/nvrc/pack.fnl
@@ -0,0 +1,34 @@
+(import-macros {: pack! : unpack!} :nvrc.macro.pack)
+
+(pack! :wbthomason/packer.nvim)
+(pack! :rktjmp/hotpot.nvim)
+(pack! :lewis6991/impatient.nvim)
+(pack! :nvim-lua/plenary.nvim {:module :plenary})
+
+(pack! :nanotech/jellybeans.vim {:event :VimEnter :config "require('nvrc.colors').apply()"})
+(pack! :feline-nvim/feline.nvim {:req :feline :after :jellybeans.vim})
+(pack! :lukas-reineke/indent-blankline.nvim {:event :BufRead :req :blankline})
+(pack! :NvChad/nvim-colorizer.lua {:event :BufRead})
+(pack! :nvim-treesitter/nvim-treesitter {:req :treesitter :defer :nvim-treesitter :run ":TSUpdate"})
+(pack! :lewis6991/gitsigns.nvim {:req :gitsigns :defer :gitsigns.nvim})
+(pack! :Pocco81/TrueZen.nvim {:cmd [:TZMinimalist :TZFocus :TZAtaraxis]})
+(pack! :stefandtw/quickfix-reflector.vim {:ft :qf})
+
+(pack! :neovim/nvim-lspconfig {:req :lspconfig :module :lspconfig :setup (fn []
+                                                                            ((. (require :nvrc.utils) :defer_unpack) :nvim-lspconfig 100)
+                                                                            (vim.defer_fn #(vim.cmd "if &ft == 'packer' | echo '' | else | silent! e %") 150))})
+(pack! :ray-x/lsp_signature.nvim {:req :lsp_signature :after :nvim-lspconfig})
+(pack! :hrsh7th/nvim-cmp {:req :cmp :event :InsertEnter})
+(pack! :hrsh7th/cmp-nvim-lsp {:after :nvim-cmp})
+
+(pack! :luukvbaal/stabilize.nvim {:after :jellybeans.vim :init :stabilize})
+(pack! :numToStr/Comment.nvim {:module :Comment :init :Comment})
+; TODO: https://github.com/kyazdani42/nvim-tree.lua/issues/951
+(pack! :kyazdani42/nvim-tree.lua {:req :nvimtree :cmd [:NvimTreeToggle :NvimTreeFocus] :commit "d8bf1ad"})
+(pack! :ggandor/lightspeed.nvim {:keys [:s :S :x :X :f :F]})
+(pack! :nvim-telescope/telescope.nvim {:req :telescope :module :telescope :cmd :Telescope})
+(pack! :boppyt/nvrc-extra)
+
+(pack! :bakpakin/fennel.vim {:ft :fennel})
+
+(unpack!)
diff --git a/fnl/nvrc/packs/blankline.fnl b/fnl/nvrc/packs/blankline.fnl
new file mode 100644
index 0000000..3139174
--- /dev/null
+++ b/fnl/nvrc/packs/blankline.fnl
@@ -0,0 +1,11 @@
+(local {: setup} (require :indent_blankline))
+
+(setup {:indentLine_enabled 1
+        :char "▏"
+        :filetype_exclude {:help :terminal
+                           :packer :lspinfo
+                           :TelescopePrompt :TelescopeResults
+                           :lsp-installer ""}
+        :buftype_exclude {1 :terminal}
+        :show_trailing_blankline_indent false
+        :show_first_indent_level false})
diff --git a/fnl/nvrc/packs/cmp.fnl b/fnl/nvrc/packs/cmp.fnl
new file mode 100644
index 0000000..3b70af5
--- /dev/null
+++ b/fnl/nvrc/packs/cmp.fnl
@@ -0,0 +1,56 @@
+(import-macros {: set!} :nvrc.macro.opt)
+(local cmp (require :cmp))
+
+(set! completeopt "menuone,noselect")
+
+(local icons {:Text "(t)"
+              :Method "(m)"
+              :Function "(f)"
+              :Constructor "(cs)"
+              :Field "(s)"
+              :Variable "(v)"
+              :Class "(c)"
+              :Interface "(i)"
+              :Module "(m)"
+              :Property "(p)"
+              :Unit "(u)"
+              :Value "(v)"
+              :Enum "(e)"
+              :Keyword "(k)"
+              :Snippet "(sn)"
+              :Color "(co)"
+              :File "(fi)"
+              :Reference "(r)"
+              :Folder "(fl)"
+              :EnumMember "(em)"
+              :Constant "(cn)"
+              :Struct "(s)"
+              :Event "(ev)"
+              :Operator "(op)"
+              :TypeParameter "(tp)"})
+
+(cmp.setup {
+            :formatting {:format (fn [entry vim-item]
+                                   (set vim-item.kind
+                                        (string.format "%s %s"
+                                                       (. icons vim-item.kind)
+                                                       vim-item.kind))
+                                   (set vim-item.menu
+                                        (. {:nvim_lsp "[LSP]"}
+                                           entry.source.name))
+                                   vim-item)}
+            :mapping {:<C-p> (cmp.mapping.select_prev_item)
+                      :<C-n> (cmp.mapping.select_next_item)
+                      :<C-d> (cmp.mapping.scroll_docs (- 4))
+                      :<C-f> (cmp.mapping.scroll_docs 4)
+                      :<C-Space> (cmp.mapping.complete)
+                      :<C-e> (cmp.mapping.close)
+                      :<CR> (cmp.mapping.confirm {:behavior cmp.ConfirmBehavior.Replace
+                                                  :select true})
+                      :Tab (fn [fallback]
+                             (if (cmp.visible) (cmp.select_next_item)"")
+                                 (fallback))
+                      :<S-Tab> (fn [fallback]
+                                 (if (cmp.visible) (cmp.select_next_item)"")
+                                 (fallback))}
+            :sources {1 {:name :nvim_lsp}}})
diff --git a/fnl/nvrc/packs/feline.fnl b/fnl/nvrc/packs/feline.fnl
new file mode 100644
index 0000000..4953a7d
--- /dev/null
+++ b/fnl/nvrc/packs/feline.fnl
@@ -0,0 +1,144 @@
+(local feline (require :feline))
+(local lsp (require :feline.providers.lsp))
+(local lsp_severity vim.diagnostic.severity)
+(local vi_mode (require :feline.providers.vi_mode))
+(local git (require :feline.providers.git))
+
+(local colors (require :nvrc.colors))
+(local utils (require :nvrc.utils))
+
+(local vi_mode_colors {:NORMAL (. colors :green)
+                       :INSERT (. colors :red)
+                       :VISUAL (. colors :yellow)
+                       :OP (. colors :green)
+                       :BLOCK (. colors :skyblue)
+                       :REPLACE (. colors :violet)
+                       :V-REPLACE (. colors :violet)
+                       :ENTER (. colors :skyblue)
+                       :MORE (. colors :skyblue)
+                       :SELECT (. colors :orange)
+                       :COMMAND (. colors :green)
+                       :SHELL (. colors :green)
+                       :TERM (. colors :green)
+                       :NONE (. colors :yellow)})
+
+(local modules {:pad {:provider "▊ " :hl {:fg (. colors :skyblue)}}
+                :current_position {:provider :position
+                                   :left_sep " "
+                                   :right_sep {1 " "
+                                               2 {:str :vertical_bar_thin
+                                                  :hl {:fg :fg :bg :bg}}}}
+                :vi_mode {:provider :vi_mode
+                          :icon ""
+                          :right_sep " "
+                          :hl (fn []
+                                {:name (vi_mode.get_mode_highlight_name)
+                                 :fg (vi_mode.get_mode_color)})}
+                :file {:info {:provider {:name :file_info
+                                         :opts {:file_modified_icon "[+]"
+                                                :file_readonly_icon "!w "}}
+                              :icon ""
+                              :right_sep " "
+                              :hl {:fg (. colors :orange) :style :bold}}
+                       :encoding {:provider :file_encoding
+                                  :icon ""
+                                  :right_sep " "
+                                  :hl {:fg (. colors :magenta) :style :bold}}
+                       :type {:provider :file_type
+                              :icon ""
+                              :right_sep " "
+                              :hl {:fg (. colors :magenta) :style :bold}}
+                       :size {:provider :file_size
+                              :right_sep {1 " "
+                                          2 {:str :vertical_bar_thin
+                                             :hl {:fg :fg :bg :bg}}}}}
+                :line_percentage {:provider :line_percentage
+                                  :right_sep " "
+                                  :hl {:style :bold}}
+                :scroll_bar {:provider :scroll_bar
+                             :right_sep " "
+                             :hl {:fg (. colors :skyblue) :style :bold}}
+                :lsp {:name {:provider :lsp_client-names
+                             :icon ""
+                             :right_sep " "
+                             :hl {:fg (. colors :yellow)}}
+                      :load_fidget {:provider #(utils.lsp_fidget)
+                                    :enabled #(utils.will_it_fit 80)
+                                    :hl {:fg (. colors :green)}}
+                      :diag_err {:provider :diagnostic_errors
+                                 :enabled #(lsp.diagnostics_exist lsp_severity.ERROR)
+                                 :left_sep " "
+                                 :icon :E
+                                 :hl {:fg (. colors :red)}}
+                      :diag_warn {:provider :diagnostic_warnings
+                                  :enabled #(lsp.diagnostics_exist lsp_severity.WARN)
+                                  :left_sep " "
+                                  :icon :W
+                                  :hl {:fg (. colors :yellow)}}
+                      :diag_info {:provider :diagnostic_info
+                                  :enabled #(lsp.diagnostics_exist lsp_severity.INFO)
+                                  :left_sep " "
+                                  :icon :I
+                                  :hl {:fg (. colors :skyblue)}}
+                      :diag_hint {:provider :diagnostic_hints
+                                  :enabled #(lsp.diagnostics_exist lsp_severity.HINT)
+                                  :left_sep " "
+                                  :icon :H
+                                  :hl {:fg (. colors :white)}}}
+                :git {:branch {:provider :git_branch
+                               :enabled #(git.git_info_exists)
+                               :icon "*"
+                               :right_sep " "
+                               :hl {:fg (. colors :violet) :style :bold}}
+                      :add {:provider :git_diff_added
+                            :enabled #(git.git_info_exists)
+                            :icon "+"
+                            :right_sep " "
+                            :hl {:fg (. colors :green)}}
+                      :change {:provider :git_diff_changed
+                               :enabled #(git.git_info_exists)
+                               :icon "~"
+                               :right_sep " "
+                               :hl {:fg (. colors :orange)}}
+                      :remove {:provider :git_diff_removed
+                               :enabled #(git.git_info_exists)
+                               :icon "-"
+                               :right_sep " "
+                               :hl {:fg (. colors :red)}}}})
+
+(local comps {:left {:active [(. modules :pad)
+                              (. modules :vi_mode)
+                              (. modules :file :info)
+                              (. modules :file :size)
+                              (. modules :current_position)
+                              (. modules :lsp :diag_err)
+                              (. modules :lsp :diag_warn)
+                              (. modules :lsp :diag_info)
+                              (. modules :lsp :diag_hint)]
+                     :inactive []}
+              :middle {:active [(. modules :lsp :load_fidget)]
+                       :inactive []}
+              :right {:active [(. modules :git :branch)
+                               (. modules :git :add)
+                               (. modules :git :change)
+                               (. modules :git :remove)
+                               (. modules :file :encoding)
+                               (. modules :file :type)
+                               (. modules :line_percentage)
+                               (. modules :scroll_bar)]
+                      :inactive []}})
+
+(local components
+       {:active [comps.left.active comps.middle.active comps.right.active]
+        :inactive [comps.left.inactive comps.middle.inactive comps.right.inactive]})
+
+(local properties
+       {:force_inactive {:filetypes [:NvimTree :packer]
+                         :buftypes [:terminal :packer]}})
+
+(feline.setup {:theme colors
+               :default_bg (. colors :bg)
+               :default_fg (. colors :fg)
+               : components
+               : properties
+               : vi_mode_colors})
diff --git a/fnl/nvrc/packs/gitsigns.fnl b/fnl/nvrc/packs/gitsigns.fnl
new file mode 100644
index 0000000..fe500a8
--- /dev/null
+++ b/fnl/nvrc/packs/gitsigns.fnl
@@ -0,0 +1,7 @@
+(local {: setup} (require :gitsigns))
+
+(setup {:signs {:add {:hl :DiffAdd :text "│" :numhl :GitSignsAddNr}
+                :change {:hl :DiffChange :text "│" :numhl :GitSignsChangeNr}
+                :delete {:hl :DiffDelete :text "_" :numhl :GitSignsDeleteNr}
+                :topdelete {:hl :DiffDelete :text "‾" :numhl :GitSignsDeleteNr}
+                :changedelete {:hl :DiffChangeDelete :text "~" :numhl :GitSignsChangeNr}}})
diff --git a/fnl/nvrc/packs/lsp_signature.fnl b/fnl/nvrc/packs/lsp_signature.fnl
new file mode 100644
index 0000000..b14a374
--- /dev/null
+++ b/fnl/nvrc/packs/lsp_signature.fnl
@@ -0,0 +1,15 @@
+(local {: setup} (require :lsp_signature))
+
+(setup {:bind true
+        :doc_lines 0
+        :floating_window true
+        :fix_pos true
+        :hint_enable true
+        :hint_scheme :String
+        :hint_prefix "(i) "
+        :hi_parameter :Search
+        :max_height 22
+        :max_width 120
+        :handler_opts {:border :single}
+        :zindex 200
+        :padding ""})
diff --git a/fnl/nvrc/packs/lspconfig.fnl b/fnl/nvrc/packs/lspconfig.fnl
new file mode 100644
index 0000000..83a4146
--- /dev/null
+++ b/fnl/nvrc/packs/lspconfig.fnl
@@ -0,0 +1,50 @@
+(local lsp (require :lspconfig))
+(local {: highlight} (require :nvrc.macro.color))
+(local {: merge} (require :nvrc.utils))
+(local colors (require :nvrc.colors))
+
+(fn on_attach [client bufnr]
+  (highlight :DiagnosticError {:fg (. colors :red)})
+  (highlight :DiagnosticWarn {:fg (. colors :yellow)})
+  (highlight :DiagnosticInformation {:fg (. colors :green)})
+  (highlight :DiagnosticHint {:fg (. colors :grey)})
+  (if client.resolved_capabilities.document_highlight
+      (do
+        (highlight :LspReferenceRead {:underline true})
+        (highlight :LspReferenceText {:underline true})
+        (highlight :LspReferenceWrite {:underline true})
+        (vim.api.nvim_exec "augroup lsp_document_highlight
+        autocmd! * <buffer>
+        autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
+        augroup END" false))))
+
+(fn better_root_pattern [patterns except-patterns]
+  "match path if one of the given patterns is matched, EXCEPT if one of the except-patterns is matched"
+  (fn [path]
+    (when (not ((lsp.util.root_pattern except-patterns) path))
+      ((lsp.util.root_pattern patterns) path))))
+
+(local default_capabilities (vim.lsp.protocol.make_client_capabilities))
+
+(fn init_lsp [lsp-name ?opts]
+  "initialize a language server with defaults"
+  (let [merged-opts (merge {: on_attach
+                                  :capabilities default_capabilities}
+                                 (or ?opts {}))]
+    ((. lsp lsp-name :setup) merged-opts)))
+
+(tset vim.lsp.handlers :textDocument/publishDiagnostics
+      (vim.lsp.with vim.lsp.diagnostic.on_publish_diagnostics
+                    {:update_in_insert false
+                     :virtual_text {:prefix "-"}
+                     :signs false}))
+
+(init_lsp :ccls)
+(init_lsp :hls)
+(init_lsp :rust_analyzer)
+
+(vim.cmd "highlight link LspSemantic_type Include")
+(vim.cmd "highlight link LspSemantic_function Identifier")
+(vim.cmd "highlight link LspSemantic_struct Number")
+(vim.cmd "highlight LspSemantic_variable guifg=gray")
+(vim.cmd "highlight link LspSemantic_keyword Structure")
diff --git a/fnl/nvrc/packs/nvimtree.fnl b/fnl/nvrc/packs/nvimtree.fnl
new file mode 100644
index 0000000..83326ea
--- /dev/null
+++ b/fnl/nvrc/packs/nvimtree.fnl
@@ -0,0 +1,44 @@
+(import-macros {: setv!} :nvrc.macro.set)
+(local {: setup} (require :nvim-tree))
+
+(setv! nvim_tree_add_trailing 0)
+(setv! nvim_tree_git_hl 1)
+(setv! nvim_tree_highlight_opened_files 0)
+(setv! nvim_tree_indent_markers 1)
+(setv! nvim_tree_quit_on_open 0)
+(setv! nvim_tree_root_folder_modifier
+      (table.concat {1 ":t:gs?$?/.." 2 (string.rep " " 1000) 3 "?:gs?^??"}))
+(setv! nvim_tree_window_picker_exclude
+      {:filetype {1 :notify 2 :packer 3 :qf} :buftype {1 :terminal}})
+
+(setv! nvim_tree_show_icons {:folders 0 :files 0 :git 0 :folder_arrows 0})
+
+; Nvimtree still shows folder icon despite folders being disabled
+; Maybe the nvim_tree_show_icons option isn't really working?
+(setv! nvim_tree_symlink_arrow " -> ")
+(setv! nvim_tree_icons {:default ""
+                       :symlink "~"
+                       :git {:deleted :x
+                             :ignored "?"
+                             :renamed "->"
+                             :staged "*"
+                             :unmerged "!"
+                             :unstaged "!"
+                             :untracked "!"}
+                       :folder {:default "+"
+                                :empty "?"
+                                :empty_open "-"
+                                :open "-"
+                                :symlink "~"
+                                :symlink_open "~-"}})
+
+(setup {:filters {:dotfiles false}
+        :disable_netrw true
+        :hijack_netrw true
+        :auto_close false
+        :open_on_tab false
+        :hijack_cursor true
+        :update_cwd true
+        :update_focused_file {:enable true :update_cwd false}
+        :view {:allow_resize true :side :left :width 25 :hide_root_folder true}
+        :git {:enable false :ignore false}})
diff --git a/fnl/nvrc/packs/telescope.fnl b/fnl/nvrc/packs/telescope.fnl
new file mode 100644
index 0000000..9f9eb0f
--- /dev/null
+++ b/fnl/nvrc/packs/telescope.fnl
@@ -0,0 +1,49 @@
+(local telescope (require :telescope))
+
+(telescope.setup {:defaults {:vimgrep_arguments {1 :rg
+                                                 2 :--color=never
+                                                 3 :--no-heading
+                                                 4 :--with-filename
+                                                 5 :--line-number
+                                                 6 :--column
+                                                 7 :--smart-case}
+                             :prompt_prefix "Search: "
+                             :selection_caret "  "
+                             :entry_prefix "  "
+                             :initial_mode :insert
+                             :selection_strategy :reset
+                             :sorting_strategy :ascending
+                             :layout_strategy :horizontal
+                             :layout_config {:horizontal {:prompt_position :top
+                                                          :preview_width 0.55
+                                                          :results_width 0.8}
+                                             :vertical {:mirror false}
+                                             :width 0.87
+                                             :height 0.8
+                                             :preview_cutoff 120}
+                             :file_sorter (. (require :telescope.sorters)
+                                             :get_fuzzy_file)
+                             :file_ignore_patterns {1 :node_modules}
+                             :generic_sorter (. (require :telescope.sorters)
+                                                :get_generic_fuzzy_sorter)
+                             :path_display {1 :truncate}
+                             :winblend 0
+                             :border {}
+                             :borderchars {1 "─"
+                                           2 "│"
+                                           3 "─"
+                                           4 "│"
+                                           5 "╭"
+                                           6 "╮"
+                                           7 "╯"
+                                           8 "╰"}
+                             :use_less false
+                             :set_env {:COLORTERM :truecolor}
+                             :file_previewer (. (require :telescope.previewers)
+                                                :vim_buffer_cat.new)
+                             :grep_previewer (. (require :telescope.previewers)
+                                                :vim_buffer_vimgrep.new)
+                             :qflist_previewer (. (require :telescope.previewers)
+                                                  :vim_buffer_qflist.new)}})
+
+(telescope.load_extension :terms)
diff --git a/fnl/nvrc/packs/treesitter.fnl b/fnl/nvrc/packs/treesitter.fnl
new file mode 100644
index 0000000..6e02ae5
--- /dev/null
+++ b/fnl/nvrc/packs/treesitter.fnl
@@ -0,0 +1,29 @@
+(local {: setup} (require :nvim-treesitter.configs))
+
+(local colors (require :nvrc.colors))
+
+(setup {:ensure_installed [:fennel :lua]
+        :highlight {:enable true}
+        :indent {:enable true}
+        :refactor {:highlight_definitions {:enable true}
+                   :highlight_current_scope {:enable false}
+                   :smart_rename {:enable true
+                                  :keymaps {:smart_rename :<localleader>rn}}
+                   :navigation {:enable true
+                                :keymaps {:goto_definition :<localleader>gd
+                                          :list_definitions :<localleader>ld
+                                          :list_definitions_toc :<localleader>td
+                                          :goto_next_usage :<a-*>
+                                          :goto_previous_usage "<a-#>"}}}
+        :textobjects {:select {:enable true
+                               :lookahead true
+                               :keymaps {:if "@function.inner"
+                                         :af "@function.outer"
+                                         :ic "@class.inner"
+                                         :ac "@class.outer"
+                                         :ia "@parameter.inner"
+                                         :aa "@parameter.outer"}}
+                      :swap {:enable true
+                             :swap_next {:<localleader>> "@parameter.inner"}
+                             :swap_previous {:<localleader>< "@parameter.inner"}}}
+        :matchup {:enable true}})
diff --git a/fnl/nvrc/utils.fnl b/fnl/nvrc/utils.fnl
new file mode 100644
index 0000000..6812c0c
--- /dev/null
+++ b/fnl/nvrc/utils.fnl
@@ -0,0 +1,127 @@
+(fn tbl? [x]
+  (= :table (type x)))
+
+(fn count [xs]
+  (if (tbl? xs) (table.maxn xs)
+      (not xs) 0
+      (length xs)))
+
+(fn run! [f xs]
+  "Execute the function (for side effects) for every xs."
+  (when xs
+    (let [nxs (count xs)]
+      (when (> nxs 0)
+        (for [i 1 nxs]
+          (f (. xs i)))))))
+
+(fn reduce [f init xs]
+  "Reduce xs into a result by passing each subsequent value into the fn with
+  the previous value as the first arg. Starting with init."
+  (var result init)
+  (run! (fn [x]
+                (set result (f result x))) xs)
+  result)
+
+(fn merge! [base ...]
+  (reduce (fn [acc m]
+                  (when m
+                    (each [k v (pairs m)]
+                      (tset acc k v)))
+                  acc) (or base {}) [...]))
+
+(fn merge [...]
+  (merge! {} ...))
+
+(fn lsp_fidget []
+  "Simple implementation of LSP fidget. Returns current LSP status with a spinner"
+  (let [lsp (. (vim.lsp.util.get_progress_messages) 1)]
+    (when lsp
+      (let [msg (or lsp.message "")
+          percentage (or lsp.percentage 0)
+          title (or lsp.title "")
+          spinners {1"⠋" 2 "⠙" 3 "⠹" 4 "⠸" 5 "⠼" 6 "⠴" 7 "⠦" 8 "⠧" 9 "⠇" 10 "⠏"}
+          success-icon "^.^!"
+          ms (/ (vim.loop.hrtime) 1000000)
+          frame (% (math.floor (/ ms 120)) (length spinners))]
+      (when (>= percentage 70)
+        (let [ertn [(string.format " %%<%s %s %s (%s%%%%) "
+                                   success-icon
+                                   title msg
+                                   percentage)]]
+          (lua "return (table.unpack or _G.unpack)(ertn)")))
+      (let [ertn [(string.format " %%<%s %s %s (%s%%%%) "
+                                 (. spinners
+                                    (+ frame 1))
+                                 title msg
+                                 percentage)]]
+        (lua "return (table.unpack or _G.unpack)(ertn)"))))) "")
+
+(fn will_it_fit [width winid]
+  "Returns whether this module will fit in the given width"
+  (> (vim.api.nvim_win_get_width (or (tonumber winid) 0)) width))
+
+(fn close_buf [force]
+  "ojroques/nvim-bufdel 'BSD-2'"
+  (let [opts {:next :cycle :quit false}]
+    (fn switch-buffer [windows buf]
+      (let [cur-win (vim.fn.winnr)]
+        (each [_ winid (ipairs windows)]
+          (set-forcibly! winid (or (tonumber winid) 0))
+          (vim.cmd (string.format "%d wincmd w" (vim.fn.win_id2win winid)))
+          (vim.cmd (string.format "buffer %d" buf)))
+        (vim.cmd (string.format "%d wincmd w" cur-win))))
+
+    (fn get-next-buf [buf]
+      (var next (vim.fn.bufnr "#"))
+      (when (and (= opts.next :alternate) (= (vim.fn.buflisted next) 1))
+        (lua "return next"))
+      (for [i 0 (- (vim.fn.bufnr "$") 1) 1]
+        (set next (+ (% (+ buf i) (vim.fn.bufnr "$")) 1))
+        (when (= (vim.fn.buflisted next) 1)
+          (lua "return next"))))
+
+    (local buf (vim.fn.bufnr))
+    (when (= (vim.fn.buflisted buf) 0)
+      (vim.cmd :close)
+      (lua "return "))
+    (when (< (length (vim.fn.getbufinfo {:buflisted 1})) 2)
+      (when opts.quit
+        (if force (vim.cmd :qall!) (vim.cmd "confirm qall"))
+        (lua "return "))
+      (local (term _)
+             (pcall (fn []
+                      (vim.api.nvim_buf_get_var buf :term_type))))
+      (when term
+        (vim.cmd (string.format "setlocal nobl" buf))
+        (vim.cmd :enew)
+        (lua "return "))
+      (vim.cmd :enew)
+      (vim.cmd :bp))
+    (local next-buf (get-next-buf buf))
+    (local windows (. (. (vim.fn.getbufinfo buf) 1) :windows))
+    (if (or force (= (vim.fn.getbufvar buf :&buftype) :terminal))
+        (let [(term type) (pcall (fn []
+                                   (vim.api.nvim_buf_get_var buf :term_type)))]
+          (if term
+              (if (= type :wind)
+                  (do
+                    (vim.cmd (string.format "%d bufdo setlocal nobl" buf))
+                    (vim.cmd :BufferLineCycleNext))
+                  (let [cur-win (vim.fn.winnr)]
+                    (vim.cmd (string.format "%d wincmd c" cur-win))
+                    (lua "return ")))
+              (do
+                (switch-buffer windows next-buf)
+                (vim.cmd (string.format "bd! %d" buf)))))
+        (do
+          (switch-buffer windows next-buf)
+          (vim.cmd (string.format "silent! confirm bd %d" buf))))
+    (when (= (vim.fn.buflisted buf) 1)
+      (switch-buffer windows buf))))
+
+(fn defer_unpack [pack after]
+  (when pack
+    (set-forcibly! after (or after 0))
+    (vim.defer_fn (fn [] ((. (require :packer) :loader) pack)) after)))
+
+{: merge : lsp_fidget : will_it_fit : close_buf : defer_unpack}