about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--LICENSES/aniseed.md24
-rw-r--r--LICENSES/zest.md9
-rw-r--r--README.md16
-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
-rw-r--r--init.lua35
-rw-r--r--lua/md5.lua397
31 files changed, 1641 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8cb205e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+plugin
diff --git a/LICENSES/aniseed.md b/LICENSES/aniseed.md
new file mode 100644
index 0000000..cf1ab25
--- /dev/null
+++ b/LICENSES/aniseed.md
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/LICENSES/zest.md b/LICENSES/zest.md
new file mode 100644
index 0000000..b1e62e7
--- /dev/null
+++ b/LICENSES/zest.md
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2021 tsbohc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..99180e7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+<div align="center">
+
+# .nvrc
+> *Elegant neovim config written in fennel, for more civilized age.*
+
+> Happy hacking!
+
+</div>
+
+# Shoutouts
+- [rktjmp/hotpot.nvim](https://github.com/rktjmp/hotpot.nvim)
+- [wbthomason/packer.nvim](https://github.com/wbthomason/packer.nvim)
+- [datwaft/nvim.conf](https://github.com/datwaft/nvim.conf)
+- [kolja/vimrc](https://github.com/kolja.vimrc)
+- [tsbohc/zest.nvim](https://github.com/tsbohc/zest.nvim)
+- [Olical/aniseed](https://github.com/Olical/aniseed)
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}
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..07f792e
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,35 @@
+local ok, impatient = pcall(require, "impatient")
+if ok then
+  impatient.enable_profile()
+else
+  vim.notify(impatient)
+end
+
+local fmt = string.format
+local run = vim.api.nvim_command
+
+vim.opt.shadafile = "NONE"
+
+local function assert_installed(repo, type)
+  local _, _, pack_name = string.find(repo, [[%S+/(%S+)]])
+  local install_path = vim.fn.stdpath("data") .. "/site/pack/packer/" .. (type or "start") .. "/" .. pack_name
+  if vim.fn.empty(vim.fn.glob(install_path)) ~= 0 then
+    print(fmt("Pack '%s' not found, cloning to %s", pack_name, install_path))
+    vim.fn.system {
+      "git",
+      "clone",
+      "https://github.com/" .. repo,
+      install_path
+    }
+    run(fmt("packadd %s", pack_name))
+  end
+end
+
+assert_installed("wbthomason/packer.nvim")
+assert_installed("rktjmp/hotpot.nvim")
+
+require("hotpot").setup { provide_require_fennel = true }
+
+vim.opt.termguicolors = true
+
+require("nvrc.ignite")
diff --git a/lua/md5.lua b/lua/md5.lua
new file mode 100644
index 0000000..eef3732
--- /dev/null
+++ b/lua/md5.lua
@@ -0,0 +1,397 @@
+
+local md5 = {
+  _VERSION     = "md5.lua 1.1.0",
+  _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
+  _URL         = "https://github.com/kikito/md5.lua",
+  _LICENSE     = [[
+    MIT LICENSE
+
+    Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be included
+    in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+  ]]
+}
+
+-- bit lib implementions
+
+local char, byte, format, rep, sub =
+  string.char, string.byte, string.format, string.rep, string.sub
+local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
+
+local ok, bit = pcall(require, 'bit')
+if ok then
+  bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
+else
+  ok, bit = pcall(require, 'bit32')
+
+  if ok then
+
+    bit_not = bit.bnot
+
+    local tobit = function(n)
+      return n <= 0x7fffffff and n or -(bit_not(n) + 1)
+    end
+
+    local normalize = function(f)
+      return function(a,b) return tobit(f(tobit(a), tobit(b))) end
+    end
+
+    bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
+    bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
+
+  else
+
+    local function tbl2number(tbl)
+      local result = 0
+      local power = 1
+      for i = 1, #tbl do
+        result = result + tbl[i] * power
+        power = power * 2
+      end
+      return result
+    end
+
+    local function expand(t1, t2)
+      local big, small = t1, t2
+      if(#big < #small) then
+        big, small = small, big
+      end
+      -- expand small
+      for i = #small + 1, #big do
+        small[i] = 0
+      end
+    end
+
+    local to_bits -- needs to be declared before bit_not
+
+    bit_not = function(n)
+      local tbl = to_bits(n)
+      local size = math.max(#tbl, 32)
+      for i = 1, size do
+        if(tbl[i] == 1) then
+          tbl[i] = 0
+        else
+          tbl[i] = 1
+        end
+      end
+      return tbl2number(tbl)
+    end
+
+    -- defined as local above
+    to_bits = function (n)
+      if(n < 0) then
+        -- negative
+        return to_bits(bit_not(math.abs(n)) + 1)
+      end
+      -- to bits table
+      local tbl = {}
+      local cnt = 1
+      local last
+      while n > 0 do
+        last      = n % 2
+        tbl[cnt]  = last
+        n         = (n-last)/2
+        cnt       = cnt + 1
+      end
+
+      return tbl
+    end
+
+    bit_or = function(m, n)
+      local tbl_m = to_bits(m)
+      local tbl_n = to_bits(n)
+      expand(tbl_m, tbl_n)
+
+      local tbl = {}
+      for i = 1, #tbl_m do
+        if(tbl_m[i]== 0 and tbl_n[i] == 0) then
+          tbl[i] = 0
+        else
+          tbl[i] = 1
+        end
+      end
+
+      return tbl2number(tbl)
+    end
+
+    bit_and = function(m, n)
+      local tbl_m = to_bits(m)
+      local tbl_n = to_bits(n)
+      expand(tbl_m, tbl_n)
+
+      local tbl = {}
+      for i = 1, #tbl_m do
+        if(tbl_m[i]== 0 or tbl_n[i] == 0) then
+          tbl[i] = 0
+        else
+          tbl[i] = 1
+        end
+      end
+
+      return tbl2number(tbl)
+    end
+
+    bit_xor = function(m, n)
+      local tbl_m = to_bits(m)
+      local tbl_n = to_bits(n)
+      expand(tbl_m, tbl_n)
+
+      local tbl = {}
+      for i = 1, #tbl_m do
+        if(tbl_m[i] ~= tbl_n[i]) then
+          tbl[i] = 1
+        else
+          tbl[i] = 0
+        end
+      end
+
+      return tbl2number(tbl)
+    end
+
+    bit_rshift = function(n, bits)
+      local high_bit = 0
+      if(n < 0) then
+        -- negative
+        n = bit_not(math.abs(n)) + 1
+        high_bit = 0x80000000
+      end
+
+      local floor = math.floor
+
+      for i=1, bits do
+        n = n/2
+        n = bit_or(floor(n), high_bit)
+      end
+      return floor(n)
+    end
+
+    bit_lshift = function(n, bits)
+      if(n < 0) then
+        -- negative
+        n = bit_not(math.abs(n)) + 1
+      end
+
+      for i=1, bits do
+        n = n*2
+      end
+      return bit_and(n, 0xFFFFFFFF)
+    end
+  end
+end
+
+-- convert little-endian 32-bit int to a 4-char string
+local function lei2str(i)
+  local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
+  return f(0)..f(8)..f(16)..f(24)
+end
+
+-- convert raw string to big-endian int
+local function str2bei(s)
+  local v=0
+  for i=1, #s do
+    v = v * 256 + byte(s, i)
+  end
+  return v
+end
+
+-- convert raw string to little-endian int
+local function str2lei(s)
+  local v=0
+  for i = #s,1,-1 do
+    v = v*256 + byte(s, i)
+  end
+  return v
+end
+
+-- cut up a string in little-endian ints of given size
+local function cut_le_str(s,...)
+  local o, r = 1, {}
+  local args = {...}
+  for i=1, #args do
+    table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
+    o = o + args[i]
+  end
+  return r
+end
+
+local swap = function (w) return str2bei(lei2str(w)) end
+
+-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
+-- 10/02/2001 jcw@equi4.com
+
+local CONSTS = {
+  0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+  0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+  0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+  0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+  0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+  0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+  0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+  0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+  0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+  0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+  0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+  0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+  0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+  0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+  0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+  0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+  0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
+}
+
+local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
+local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
+local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
+local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
+local z=function (ff,a,b,c,d,x,s,ac)
+  a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
+  -- be *very* careful that left shift does not cause rounding!
+  return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
+end
+
+local function transform(A,B,C,D,X)
+  local a,b,c,d=A,B,C,D
+  local t=CONSTS
+
+  a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
+  d=z(f,d,a,b,c,X[ 1],12,t[ 2])
+  c=z(f,c,d,a,b,X[ 2],17,t[ 3])
+  b=z(f,b,c,d,a,X[ 3],22,t[ 4])
+  a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
+  d=z(f,d,a,b,c,X[ 5],12,t[ 6])
+  c=z(f,c,d,a,b,X[ 6],17,t[ 7])
+  b=z(f,b,c,d,a,X[ 7],22,t[ 8])
+  a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
+  d=z(f,d,a,b,c,X[ 9],12,t[10])
+  c=z(f,c,d,a,b,X[10],17,t[11])
+  b=z(f,b,c,d,a,X[11],22,t[12])
+  a=z(f,a,b,c,d,X[12], 7,t[13])
+  d=z(f,d,a,b,c,X[13],12,t[14])
+  c=z(f,c,d,a,b,X[14],17,t[15])
+  b=z(f,b,c,d,a,X[15],22,t[16])
+
+  a=z(g,a,b,c,d,X[ 1], 5,t[17])
+  d=z(g,d,a,b,c,X[ 6], 9,t[18])
+  c=z(g,c,d,a,b,X[11],14,t[19])
+  b=z(g,b,c,d,a,X[ 0],20,t[20])
+  a=z(g,a,b,c,d,X[ 5], 5,t[21])
+  d=z(g,d,a,b,c,X[10], 9,t[22])
+  c=z(g,c,d,a,b,X[15],14,t[23])
+  b=z(g,b,c,d,a,X[ 4],20,t[24])
+  a=z(g,a,b,c,d,X[ 9], 5,t[25])
+  d=z(g,d,a,b,c,X[14], 9,t[26])
+  c=z(g,c,d,a,b,X[ 3],14,t[27])
+  b=z(g,b,c,d,a,X[ 8],20,t[28])
+  a=z(g,a,b,c,d,X[13], 5,t[29])
+  d=z(g,d,a,b,c,X[ 2], 9,t[30])
+  c=z(g,c,d,a,b,X[ 7],14,t[31])
+  b=z(g,b,c,d,a,X[12],20,t[32])
+
+  a=z(h,a,b,c,d,X[ 5], 4,t[33])
+  d=z(h,d,a,b,c,X[ 8],11,t[34])
+  c=z(h,c,d,a,b,X[11],16,t[35])
+  b=z(h,b,c,d,a,X[14],23,t[36])
+  a=z(h,a,b,c,d,X[ 1], 4,t[37])
+  d=z(h,d,a,b,c,X[ 4],11,t[38])
+  c=z(h,c,d,a,b,X[ 7],16,t[39])
+  b=z(h,b,c,d,a,X[10],23,t[40])
+  a=z(h,a,b,c,d,X[13], 4,t[41])
+  d=z(h,d,a,b,c,X[ 0],11,t[42])
+  c=z(h,c,d,a,b,X[ 3],16,t[43])
+  b=z(h,b,c,d,a,X[ 6],23,t[44])
+  a=z(h,a,b,c,d,X[ 9], 4,t[45])
+  d=z(h,d,a,b,c,X[12],11,t[46])
+  c=z(h,c,d,a,b,X[15],16,t[47])
+  b=z(h,b,c,d,a,X[ 2],23,t[48])
+
+  a=z(i,a,b,c,d,X[ 0], 6,t[49])
+  d=z(i,d,a,b,c,X[ 7],10,t[50])
+  c=z(i,c,d,a,b,X[14],15,t[51])
+  b=z(i,b,c,d,a,X[ 5],21,t[52])
+  a=z(i,a,b,c,d,X[12], 6,t[53])
+  d=z(i,d,a,b,c,X[ 3],10,t[54])
+  c=z(i,c,d,a,b,X[10],15,t[55])
+  b=z(i,b,c,d,a,X[ 1],21,t[56])
+  a=z(i,a,b,c,d,X[ 8], 6,t[57])
+  d=z(i,d,a,b,c,X[15],10,t[58])
+  c=z(i,c,d,a,b,X[ 6],15,t[59])
+  b=z(i,b,c,d,a,X[13],21,t[60])
+  a=z(i,a,b,c,d,X[ 4], 6,t[61])
+  d=z(i,d,a,b,c,X[11],10,t[62])
+  c=z(i,c,d,a,b,X[ 2],15,t[63])
+  b=z(i,b,c,d,a,X[ 9],21,t[64])
+
+  return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
+         bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
+end
+
+----------------------------------------------------------------
+
+local function md5_update(self, s)
+  self.pos = self.pos + #s
+  s = self.buf .. s
+  for ii = 1, #s - 63, 64 do
+    local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
+    assert(#X == 16)
+    X[0] = table.remove(X,1) -- zero based!
+    self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
+  end
+  self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
+  return self
+end
+
+local function md5_finish(self)
+  local msgLen = self.pos
+  local padLen = 56 - msgLen % 64
+
+  if msgLen % 64 > 56 then padLen = padLen + 64 end
+
+  if padLen == 0 then padLen = 64 end
+
+  local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
+  md5_update(self, s)
+
+  assert(self.pos % 64 == 0)
+  return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
+end
+
+----------------------------------------------------------------
+
+function md5.new()
+  return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
+           pos = 0,
+           buf = '',
+           update = md5_update,
+           finish = md5_finish }
+end
+
+function md5.tohex(s)
+  return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
+end
+
+function md5.sum(s)
+  return md5.new():update(s):finish()
+end
+
+function md5.sumhexa(s)
+  return md5.tohex(md5.sum(s))
+end
+
+return md5