From 659fb33ca744dd39f8d543aa1fc917cac9a79605 Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Thu, 7 Dec 2023 22:35:57 +0100
Subject: [PATCH] Sketch out tempest neovim runtime

---
 home/features/cli/fish/default.nix            |  25 +-
 home/features/neovim/config/ftplugin/tex.lua  |  22 -
 home/features/neovim/config/lazy-lock.json    |   9 +-
 .../config/lua/my/abbreviations/unicode.lua   |   6 +-
 .../neovim/config/lua/my/helpers/math/mod.lua |  15 -
 .../config/lua/my/plugins/clipboard-image.lua |  42 --
 .../neovim/config/lua/my/plugins/cmp.lua      |   1 -
 .../neovim/config/lua/my/plugins/crates.lua   |  51 --
 .../config/lua/my/plugins/haskell-tools.lua   |  32 --
 .../config/lua/my/plugins/lspconfig.lua       |  24 +-
 .../config/lua/my/plugins/rust-tools.lua      |  25 -
 .../neovim/config/lua/my/plugins/vimtex.lua   |  26 -
 .../features/neovim/config/lua/my/runtime.lua | 132 +++++
 home/features/neovim/default.nix              | 290 ++++++++---
 .../neovim/plugins/clipboard-image.lua        |  15 +
 home/tethys.nix                               |   4 +-
 modules/common/neovim.nix                     | 452 +++++++++++++++---
 overlays/default.nix                          |   2 +-
 18 files changed, 783 insertions(+), 390 deletions(-)
 delete mode 100644 home/features/neovim/config/lua/my/helpers/math/mod.lua
 delete mode 100644 home/features/neovim/config/lua/my/plugins/clipboard-image.lua
 delete mode 100644 home/features/neovim/config/lua/my/plugins/crates.lua
 delete mode 100644 home/features/neovim/config/lua/my/plugins/haskell-tools.lua
 delete mode 100644 home/features/neovim/config/lua/my/plugins/rust-tools.lua
 delete mode 100644 home/features/neovim/config/lua/my/plugins/vimtex.lua
 create mode 100644 home/features/neovim/config/lua/my/runtime.lua
 create mode 100644 home/features/neovim/plugins/clipboard-image.lua

diff --git a/home/features/cli/fish/default.nix b/home/features/cli/fish/default.nix
index c636470..3508aeb 100644
--- a/home/features/cli/fish/default.nix
+++ b/home/features/cli/fish/default.nix
@@ -1,8 +1,28 @@
 { pkgs, config, lib, ... }:
 {
+  # {{{ Fzf 
+  programs.fzf = {
+    enable = true;
+    defaultOptions = [ "--no-scrollbar" ];
+
+
+    changeDirWidgetOptions = [
+      "--preview '${lib.getExe pkgs.eza} --icons --tree --color=always {}'"
+    ];
+
+    fileWidgetOptions = [
+      "--preview '${lib.getExe pkgs.bat} --number --color=always {}'"
+    ];
+  };
+
+  stylix.targets.fzf.enable = true;
+  # }}}
+  # {{{ Fish
   programs.fish = {
     enable = true;
+    interactiveShellInit = builtins.readFile ./config.fish;
 
+    # {{{ Plugins 
     plugins =
       let
         plugins = with pkgs.fishPlugins; [
@@ -12,7 +32,6 @@
           puffer # Text expansion (i.e. expanding .... to ../../../)
           sponge # Remove failed commands and whatnot from history
           forgit # Git tui thingy? (I'm still trying this one out)
-          fzf-fish # Fuzzy finder for things like files
           colored-man-pages # Self explainatory:)
         ];
       in
@@ -22,12 +41,12 @@
           name = plugin.pname;
           inherit (plugin) src;
         });
-
-    interactiveShellInit = builtins.readFile ./config.fish;
+    # }}}
   };
 
   satellite.persistence.at.state.apps.fish.directories = [
     "${config.xdg.dataHome}/fish"
     "${config.xdg.dataHome}/z" # The z fish plugin
   ];
+  # }}}
 }
diff --git a/home/features/neovim/config/ftplugin/tex.lua b/home/features/neovim/config/ftplugin/tex.lua
index 84b604b..4e8ad72 100644
--- a/home/features/neovim/config/ftplugin/tex.lua
+++ b/home/features/neovim/config/ftplugin/tex.lua
@@ -6,28 +6,6 @@ require("my.abbreviations.math").setup()
 
 vim.opt.conceallevel = 0
 
--- {{{ Older functions for calculating things inside vim
--- vim.keymap.set("n", "<leader>lg", function()
---   if not pcall(function()
---     local a = tonumber(vim.fn.input("A: "))
---     local b = tonumber(vim.fn.input("B: "))
---
---     local g, x, y = require("my.helpers.math.mod").gcd(a, b)
---
---     vim.fn.input("Result: " .. g .. " " .. x .. " " .. y)
---   end) then vim.fn.input("No results exist") end
--- end, { buffer = true, desc = "Gcd calculator" })
---
--- vim.keymap.set("n", "<leader>li", function()
---   if not pcall(function()
---     local class = tonumber(vim.fn.input("Mod class: "))
---     local num = tonumber(vim.fn.input("Number: "))
---
---     vim.fn.input("Result: " .. require("my.helpers.math.mod").modinverse(num, class))
---   end) then vim.fn.input("No results exist") end
--- end, { buffer = true, desc = "Mod inverse calculator" })
--- }}}
-
 local abbreviations = {
   -- Other fancy symvols
   { "tmat", "^T" }, -- Tranpose of a matrix
diff --git a/home/features/neovim/config/lazy-lock.json b/home/features/neovim/config/lazy-lock.json
index 147d9a5..50b7e11 100644
--- a/home/features/neovim/config/lazy-lock.json
+++ b/home/features/neovim/config/lazy-lock.json
@@ -2,7 +2,7 @@
   "LuaSnip": { "branch": "master", "commit": "409535b8fc54c650eb845b0c35e0cc7f08810284" },
   "alpha-nvim": { "branch": "main", "commit": "63a860e7ed3ae41ee92481ea65a48fb35431ae21" },
   "catppuccin": { "branch": "main", "commit": "490078b1593c6609e6a50ad5001e7902ea601824" },
-  "clipboard-image.nvim": { "branch": "main", "commit": "af8fdaad7e6fed0741e18dbf2f57bdc7494adaee" },
+  "clipboard-image": { "branch": "main", "commit": "485de5493d196154db30f85665f8ac480ce116a2" },
   "cmp-buffer": { "branch": "main", "commit": "3022dbc9166796b644a841a02de8dd1cc1d311fa" },
   "cmp-cmdline": { "branch": "main", "commit": "8ee981b4a91f536f52add291594e89fb6645e451" },
   "cmp-emoji": { "branch": "main", "commit": "19075c36d5820253d32e2478b6aaf3734aeaafa0" },
@@ -10,7 +10,7 @@
   "cmp-path": { "branch": "main", "commit": "91ff86cd9c29299a64f968ebb45846c485725f23" },
   "cmp_luasnip": { "branch": "master", "commit": "18095520391186d634a0045dacaa346291096566" },
   "conform": { "branch": "master", "commit": "5bf1405fd234d469243ea6f394e0aeec9ea53bd8" },
-  "crates.nvim": { "branch": "main", "commit": "d5caf28aba49e81ac4099426231f3cf3c151013a" },
+  "crates": { "branch": "main", "commit": "b8ea20fda2e1029fbbb1bae7a9eab35c84037ca0" },
   "dhall-vim": { "branch": "master", "commit": "68500ef46ff3706f46c99db3be7a0c8abcf6a3ae" },
   "dressing.nvim": { "branch": "master", "commit": "6bde51adabba06f7fd4a469885a85f36d78a5f52" },
   "fidget.nvim": { "branch": "main", "commit": "0ba1e16d07627532b6cae915cc992ecac249fb97" },
@@ -19,7 +19,7 @@
   "gitlinker.nvim": { "branch": "master", "commit": "cc59f732f3d043b626c8702cb725c82e54d35c25" },
   "gitsigns.nvim": { "branch": "main", "commit": "af0f583cd35286dd6f0e3ed52622728703237e50" },
   "harpoon": { "branch": "master", "commit": "21f4c47c6803d64ddb934a5b314dcb1b8e7365dc" },
-  "haskell-tools.nvim": { "branch": "master", "commit": "b19df600da8ef5fb4fb280815415ebd2a4228f0f" },
+  "haskell-tools": { "branch": "master", "commit": "92e097c6832405fb64e4c44a7ce8bebe7836cae6" },
   "hydra.nvim": { "branch": "master", "commit": "3ced42c0b6a6c85583ff0f221635a7f4c1ab0dd0" },
   "hyprland-vim-syntax": { "branch": "main", "commit": "8488a24b50882da969979103b4d668c70e7995b9" },
   "idris2-nvim": { "branch": "main", "commit": "3a2b4d2b5ffeab9e47298456c59c31b4e1ddebc9" },
@@ -48,7 +48,7 @@
   "nvim-cmp": { "branch": "main", "commit": "51f1e11a89ec701221877532ee1a23557d291dd5" },
   "nvim-comment": { "branch": "main", "commit": "e9ac16ab056695cad6461173693069ec070d2b23" },
   "nvim-lspconfig": { "branch": "master", "commit": "a981d4447b92c54a4d464eb1a76b799bc3f9a771" },
-  "nvim-tree.lua": { "branch": "master", "commit": "18c7a3119839adc4599d838726deae662859c8b2" },
+  "nvim-tree": { "branch": "master", "commit": "7d1760f892951dd6a118dae1d7a1d8df5f029edf" },
   "nvim-treesitter": { "branch": "master", "commit": "0791b5ebb590a2d44e20640c52679df1fc42e8ab" },
   "nvim-treesitter-context": { "branch": "master", "commit": "a17c31268b56d53624fdc9cb03a225d4a17cabdb" },
   "nvim-treesitter-textobjects": { "branch": "master", "commit": "9e519b6146512c8e2e702faf8ac48420f4f5deec" },
@@ -59,6 +59,7 @@
   "presence.nvim": { "branch": "main", "commit": "87c857a56b7703f976d3a5ef15967d80508df6e6" },
   "purescript-vim": { "branch": "main", "commit": "82348352e6568fcc0385bd7c99a8ead3a479feea" },
   "rasi.vim": { "branch": "main", "commit": "eac9969cf935cd4380987dc99bfa10d69d3f34a6" },
+  "rust-tools": { "branch": "master", "commit": "0cc8adab23117783a0292a0c8a2fbed1005dc645" },
   "rust-tools.nvim": { "branch": "master", "commit": "0cc8adab23117783a0292a0c8a2fbed1005dc645" },
   "scrap.nvim": { "branch": "main", "commit": "16db44ae9403ec9c4b140394f294475d1af80a18" },
   "smart-splits.nvim": { "branch": "master", "commit": "7aad6019dee974a01333523a5b8e122b7e7da454" },
diff --git a/home/features/neovim/config/lua/my/abbreviations/unicode.lua b/home/features/neovim/config/lua/my/abbreviations/unicode.lua
index 6a103e9..7d70556 100644
--- a/home/features/neovim/config/lua/my/abbreviations/unicode.lua
+++ b/home/features/neovim/config/lua/my/abbreviations/unicode.lua
@@ -6,10 +6,7 @@ local M = {}
 M.unicode = {
   -- {{{ Logic
   { "frl", "∀" }, -- [f]o[r]al[l]
-
-
-
-  { "exs",    "∃" }, -- [e][x]ist[s]
+  { "exs", "∃" }, -- [e][x]ist[s]
   { "land", "∧" }, -- [l]ogical [and]
   { "Land", "⋀" }, -- arbitrary [l]ogical [and]
   { "lor", "∨" }, -- [l]ogical [or]
@@ -23,6 +20,7 @@ M.unicode = {
   -- }}}
   -- {{{ Set theory
   { "nolla", "∅" },
+  { "carprod", "×" }, -- cartesian product
   { "sect", "∩" }, -- set intersection
   { "Sect", "⋂" }, -- arbitrary set intersection
   { "dsect", "⊓" }, -- disjoint set intersection (whatever that means lol)
diff --git a/home/features/neovim/config/lua/my/helpers/math/mod.lua b/home/features/neovim/config/lua/my/helpers/math/mod.lua
deleted file mode 100644
index 50de361..0000000
--- a/home/features/neovim/config/lua/my/helpers/math/mod.lua
+++ /dev/null
@@ -1,15 +0,0 @@
-local M = {}
-
-function M.modinverse(b, m)
-  local g, x, _ = M.gcd(b, m)
-  if g ~= 1 then return nil end
-  return x % m
-end
-
-function M.gcd(a, b)
-  if a == 0 then return b, 0, 1 end
-  local g, x1, y1 = M.gcd(b % a, a)
-  return g, y1 - (math.floor(b / a)) * x1, x1
-end
-
-return M
diff --git a/home/features/neovim/config/lua/my/plugins/clipboard-image.lua b/home/features/neovim/config/lua/my/plugins/clipboard-image.lua
deleted file mode 100644
index 8bb09ee..0000000
--- a/home/features/neovim/config/lua/my/plugins/clipboard-image.lua
+++ /dev/null
@@ -1,42 +0,0 @@
-local M = {
-  "postfen/clipboard-image.nvim",
-  cmd = "PasteImg",
-}
-
-local function img_name()
-  vim.fn.inputsave()
-  local name = vim.fn.input("Name: ")
-  vim.fn.inputrestore()
-
-  if name == nil or name == "" then
-    return os.date("%y-%m-%d-%H-%M-%S")
-  end
-  return name
-end
-
-function M.init()
-  vim.keymap.set(
-    "n",
-    "<leader>p",
-    ":PasteImg<cr>",
-    { desc = "[P]aste image from clipboard" }
-  )
-end
-
-function M.config()
-  require("clipboard-image").setup({
-    default = {
-      img_name = img_name,
-    },
-    tex = {
-      img_dir = { "%:p:h", "img" },
-      affix = "\\includegraphics[width=\\textwidth]{%s}",
-    },
-    typst = {
-      img_dir = { "%:p:h", "img" },
-      affix = '#image("%s", width: 100)',
-    },
-  })
-end
-
-return M
diff --git a/home/features/neovim/config/lua/my/plugins/cmp.lua b/home/features/neovim/config/lua/my/plugins/cmp.lua
index 425cd26..8d60f4b 100644
--- a/home/features/neovim/config/lua/my/plugins/cmp.lua
+++ b/home/features/neovim/config/lua/my/plugins/cmp.lua
@@ -117,7 +117,6 @@ function M.config()
       { name = "cmdline" },
     }),
   })
-
 end
 
 return M
diff --git a/home/features/neovim/config/lua/my/plugins/crates.lua b/home/features/neovim/config/lua/my/plugins/crates.lua
deleted file mode 100644
index f095fca..0000000
--- a/home/features/neovim/config/lua/my/plugins/crates.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local K = require("my.keymaps")
-local M = {
-  "saecki/crates.nvim",
-  event = "BufReadPost Cargo.toml",
-  dependencies = { "nvim-lua/plenary.nvim" },
-  config = function()
-    local crates = require("crates")
-
-    crates.setup({
-      null_ls = {
-        enabled = true,
-        name = "crates.nvim",
-      },
-    })
-
-    vim.api.nvim_create_autocmd("InsertEnter", {
-      group = vim.api.nvim_create_augroup("CmpSourceCargo", {}),
-      pattern = "Cargo.toml",
-      callback = function()
-        require("cmp").setup.buffer({ sources = { { name = "crates" } } })
-      end,
-    })
-
-    local function nmap(from, to, desc)
-      K.nmap(from, to, desc, true, true)
-    end
-
-    nmap("<leader>lct", crates.toggle, "[c]rates [t]oggle")
-    nmap("<leader>lcr", crates.reload, "[c]rates [r]efresh")
-
-    nmap("<leader>lcH", crates.open_homepage, "[c]rates [H]omepage")
-    nmap("<leader>lcR", crates.open_repository, "[c]rates [R]repository")
-    nmap("<leader>lcD", crates.open_documentation, "[c]rates [D]ocumentation")
-    nmap("<leader>lcC", crates.open_crates_io, "[c]rates [C]rates.io")
-
-    nmap("<leader>lcv", crates.show_versions_popup, "[c]rates [v]ersions")
-    nmap("<leader>lcf", crates.show_features_popup, "[c]rates [f]eatures")
-    nmap("<leader>lcd", crates.show_dependencies_popup, "[c]rates [d]eps")
-    nmap("K", crates.show_popup, "crates popup")
-
-    local wk = require("which-key")
-
-    wk.register({
-      ["<leader>lc"] = {
-        name = "[l]ocal [c]rates",
-      },
-    })
-  end,
-}
-
-return M
diff --git a/home/features/neovim/config/lua/my/plugins/haskell-tools.lua b/home/features/neovim/config/lua/my/plugins/haskell-tools.lua
deleted file mode 100644
index 9d16960..0000000
--- a/home/features/neovim/config/lua/my/plugins/haskell-tools.lua
+++ /dev/null
@@ -1,32 +0,0 @@
-local lspconfig = require("my.plugins.lspconfig")
-local M = {
-  "mrcjkb/haskell-tools.nvim",
-  dependencies = {
-    "nvim-lua/plenary.nvim",
-  },
-  version = "^2",
-  ft = { "haskell", "lhaskell", "cabal", "cabalproject" },
-}
-
-function M.config()
-  vim.g.haskell_tools = {
-    hls = {
-      on_attach = lspconfig.on_attach,
-      settings = {
-        haskell = {
-          formattingProvider = "fourmolu",
-          -- This seems to work better with custom preludes
-          -- See this issue https://github.com/fourmolu/fourmolu/issues/357
-          plugin = { fourmolu = { config = { external = true } } },
-        },
-      },
-    },
-    tools = {
-      hover = {
-        enable = false,
-      },
-    },
-  }
-end
-
-return M
diff --git a/home/features/neovim/config/lua/my/plugins/lspconfig.lua b/home/features/neovim/config/lua/my/plugins/lspconfig.lua
index 5fa7ced..c207256 100644
--- a/home/features/neovim/config/lua/my/plugins/lspconfig.lua
+++ b/home/features/neovim/config/lua/my/plugins/lspconfig.lua
@@ -30,27 +30,13 @@ local M = {
 function M.on_attach(client, bufnr)
   -- {{{ Keymap helpers
   local opts = function(desc)
-    return { noremap = true, silent = true, desc = desc, buffer = bufnr }
+    return { silent = true, desc = desc, buffer = bufnr }
   end
 
   local nmap = function(from, to, desc)
     vim.keymap.set("n", from, to, opts(desc))
   end
   -- }}}
-  -- {{{ Auto format
-  local function format()
-    vim.lsp.buf.format({ async = false, bufnr = bufnr })
-  end
-
-  -- if client.supports_method("textDocument/formatting") then
-  --   nmap("<leader>F", format, "[F]ormat")
-  --   vim.api.nvim_create_autocmd("BufWritePre", {
-  --     group = vim.api.nvim_create_augroup("LspFormatting", { clear = false }),
-  --     buffer = bufnr,
-  --     callback = format,
-  --   })
-  -- end
-  -- }}}
   -- {{{ Go to declaration / references / implementation
   nmap("gd", vim.lsp.buf.definition, "[G]o to [d]efinition")
   nmap("gi", vim.lsp.buf.implementation, "[G]o to [i]mplementation")
@@ -131,7 +117,7 @@ local servers = {
       Lua = {
         ---@diagnostic disable-next-line: missing-fields
         format = {
-          enable = true
+          enable = true,
         },
         -- Do not send telemetry data containing a randomized but unique identifier
         telemetry = {
@@ -215,9 +201,9 @@ function lspconfig.config()
   diagnostics_icons()
   -- -- {{{ Change on-hover borders
   vim.lsp.handlers["textDocument/hover"] =
-      vim.lsp.with(vim.lsp.handlers.hover, { border = "single" })
+    vim.lsp.with(vim.lsp.handlers.hover, { border = "single" })
   vim.lsp.handlers["textDocument/signatureHelp"] =
-      vim.lsp.with(vim.lsp.handlers.signature_help, { border = "single" })
+    vim.lsp.with(vim.lsp.handlers.signature_help, { border = "single" })
   -- -- }}}
 
   local capabilities = M.capabilities()
@@ -230,7 +216,7 @@ function lspconfig.config()
 
     require("lspconfig")[lsp].setup({
       on_attach = details.on_attach,
-      settings = details.settings,   -- Specific per-language settings
+      settings = details.settings, -- Specific per-language settings
       flags = {
         debounce_text_changes = 150, -- This will be the default in neovim 0.7+
       },
diff --git a/home/features/neovim/config/lua/my/plugins/rust-tools.lua b/home/features/neovim/config/lua/my/plugins/rust-tools.lua
deleted file mode 100644
index 9b7597a..0000000
--- a/home/features/neovim/config/lua/my/plugins/rust-tools.lua
+++ /dev/null
@@ -1,25 +0,0 @@
-local K = require("my.keymaps")
-local lspconfig = require("my.plugins.lspconfig")
-
-local M = {
-  "simrat39/rust-tools.nvim",
-  config = function()
-    require("rust-tools").setup({
-      server = {
-        on_attach = function(client, bufnr)
-          K.nmap(
-            "<leader>lc",
-            "<cmd>RustOpenCargo<cr>",
-            "Open [c]argo.toml",
-            true,
-            true
-          )
-
-          lspconfig.on_attach(client, bufnr)
-        end,
-      },
-    })
-  end,
-}
-
-return M
diff --git a/home/features/neovim/config/lua/my/plugins/vimtex.lua b/home/features/neovim/config/lua/my/plugins/vimtex.lua
deleted file mode 100644
index 580c1b6..0000000
--- a/home/features/neovim/config/lua/my/plugins/vimtex.lua
+++ /dev/null
@@ -1,26 +0,0 @@
-local M = {
-  "lervag/vimtex", -- latex support
-  ft = "tex",
-  enabled = false
-}
-
-function M.config()
-  vim.g.vimtex_view_method = "zathura"
-  vim.g.Tex_DefaultTargetFormat = "pdf"
-  vim.g.vimtex_compiler_latexmk = {
-    options = {
-      "-pdf",
-      "-shell-escape",
-      "-verbose",
-      "-file-line-error",
-      "-synctex=1",
-      "-interaction=nonstopmode",
-    },
-  }
-
-  vim.g.vimtex_fold_enabled = 0
-  vim.g.vimtex_imaps_enabled = 0
-  vim.g.vimtex_syntax_conceal_disable = 1
-end
-
-return M
diff --git a/home/features/neovim/config/lua/my/runtime.lua b/home/features/neovim/config/lua/my/runtime.lua
new file mode 100644
index 0000000..5bc1401
--- /dev/null
+++ b/home/features/neovim/config/lua/my/runtime.lua
@@ -0,0 +1,132 @@
+local M = {}
+
+-- {{{ General helpers
+local function string_chars(str)
+  local chars = {}
+  for i = 1, #str do
+    table.insert(chars, str:sub(i, i))
+  end
+  return chars
+end
+
+local function with_default(default, given)
+  if given == nil then
+    return default
+  else
+    return given
+  end
+end
+-- }}}
+-- {{{ API wrappers
+-- {{{ Keymaps
+function M.set_keymap(opts, context)
+  if context == nil then
+    context = {}
+  end
+
+  local buffer = nil
+
+  if context.bufnr ~= nil then
+    buffer = context.bufnr
+  end
+
+  vim.keymap.set(
+    string_chars(with_default("n", opts.mode)),
+    opts.mapping,
+    opts.action,
+    {
+      desc = opts.desc,
+      buffer = with_default(buffer, opts.buffer),
+      expr = opts.expr,
+      silent = with_default(true, opts.silent),
+    }
+  )
+end
+-- }}}
+-- {{{ Autocmds
+function M.create_autocmd(opts)
+  local callback
+
+  if type(opts.callback) == "function" then
+    callback = opts.callback
+  end
+
+  if type(opts.callback) == "table" then
+    callback = function(event)
+      M.configure(opts.callback, event)
+    end
+  end
+
+  vim.api.nvim_create_autocmd(opts.event, {
+    group = vim.api.nvim_create_augroup(opts.group, {}),
+    pattern = opts.pattern,
+    callback = callback,
+  })
+end
+-- }}}
+-- }}}
+-- {{{ Main config runtime
+local function recursive_assign(source, destination)
+  for key, value in pairs(source) do
+    if type(value) == "table" then
+      destination[key] = destination[key] or {}
+      recursive_assign(value, destination[key])
+    else
+      destination[key] = value
+    end
+  end
+end
+
+function M.configure(opts, context)
+  if type(opts.vim) == "table" then
+    recursive_assign(opts, vim)
+  end
+
+  if type(opts.keys) == "table" then
+    local keys = opts.keys
+
+    -- Detect single key passed instead of array
+    if keys.mapping ~= nil then
+      keys = { keys }
+    end
+
+    for _, keymap in ipairs(keys) do
+      M.set_keymap(keymap, context)
+    end
+  end
+
+  if type(opts.autocmds) == "table" then
+    local autocmds = opts.autocmds
+
+    -- Detect single autocmd passed instead of array
+    if autocmds.pattern ~= nil then
+      autocmds = { autocmds }
+    end
+
+    for _, autocmd in ipairs(autocmds) do
+      M.create_autocmd(autocmd)
+    end
+  end
+
+  if type(opts.setup) == "table" then
+    for key, arg in pairs(opts.setup) do
+      require(key).setup(arg)
+    end
+  end
+
+  if
+    type(context) == "table"
+    and context.opts ~= nil
+    and context.lazy ~= nil
+  then
+    -- This is a terrible way to do it :/
+    require(context.lazy.name).setup(context.opts)
+  end
+
+  if type(opts.callback) == "function" then
+    opts.callback(context)
+  end
+end
+-- }}}
+
+return M
diff --git a/home/features/neovim/default.nix b/home/features/neovim/default.nix
index 69f2da1..e39f159 100644
--- a/home/features/neovim/default.nix
+++ b/home/features/neovim/default.nix
@@ -62,8 +62,8 @@ let
     # Latex setup
     texlive.combined.scheme-full # Latex stuff
     python38Packages.pygments # required for latex syntax highlighting
-    sage
-    sagetex # sage in latex
+    # sage
+    # sagetex # sage in latex
 
     # required for the telescope fzf extension
     gnumake
@@ -208,28 +208,32 @@ in
         };
     };
   # }}}
-  # {{{ Custom module testing 
+  # {{{ Plugins
   satellite.neovim.styluaConfig = ../../../stylua.toml;
-  satellite.neovim.env.module = "my.helpers.env";
+  satellite.neovim.runtime = {
+    env = "my.helpers.env";
+    languageServerOnAttach = "my.plugins.lspconfig";
+    tempest = "my.runtime";
+  };
 
-  # {{{ Nvim-tree 
+  # {{{ ui
+  # {{{ nvim-tree 
   satellite.neovim.lazy.nvim-tree = {
     package = "kyazdani42/nvim-tree.lua";
 
+    env.blacklist = [ "vscode" "firenvim" ];
     setup = true;
-    cond = nlib.blacklistEnv [ "vscode" "firenvim" ];
 
     keys.mapping = "<C-n>";
     keys.desc = "Toggle [n]vim-tree";
     keys.action = "<cmd>NvimTreeToggle<cr>";
   };
   # }}}
-  # {{{ Lualine
+  # {{{ lualine
   satellite.neovim.lazy.lualine = {
     package = "nvim-lualine/lualine.nvim";
-    name = "lualine";
 
-    cond = nlib.blacklistEnv [ "vscode" "firenvim" ];
+    env.blacklist = [ "vscode" "firenvim" ];
     event = "VeryLazy";
 
     opts = {
@@ -254,71 +258,17 @@ in
     };
   };
   # }}}
-  # {{{ Winbar
+  # {{{ winbar
   satellite.neovim.lazy.winbar = {
     package = "fgheng/winbar.nvim";
-    name = "winbar";
 
-    cond = nlib.blacklistEnv [ "vscode" "firenvim" ];
+    env.blacklist = [ "vscode" "firenvim" ];
     event = "VeryLazy";
 
     opts.enabled = true;
   };
   # }}}
-  # {{{ Flash
-  satellite.neovim.lazy.flash = {
-    package = "folke/flash.nvim";
-    name = "flash";
-
-    cond = nlib.blacklistEnv [ "vscode" ];
-    keys =
-      let keybind = mode: mapping: action: desc: {
-        inherit mapping desc mode;
-        action = nlib.thunk /* lua */ ''require("flash").${action}()'';
-      };
-      in
-      [
-        (keybind "nxo" "s" "jump" "Flash")
-        (keybind "nxo" "S" "treesitter" "Flash Treesitter")
-        (keybind "o" "r" "remote" "Remote Flash")
-        (keybind "ox" "R" "treesitter_search" "Treesitter Search")
-        (keybind "c" "<C-S>" "toggle" "Toggle Flash Search")
-      ];
-
-    # Disable stuff like f/t/F/T
-    opts.modes.char.enabled = false;
-  };
-  # }}}
-  # {{{ Conform.nvim
-  satellite.neovim.lazy.conform = {
-    package = "stevearc/conform.nvim";
-    name = "conform";
-
-    cond = nlib.blacklistEnv [ "vscode" ];
-    event = "BufReadPost";
-
-    opts.format_on_save.lsp_fallback = true;
-    opts.formatters_by_ft = {
-      lua = [ "stylua" ];
-      python = [ "ruff_format" ];
-      javascript = [ [ "prettierd" "prettier" ] ];
-    };
-  };
-  # }}}
-  # {{{ Neoconf
-  satellite.neovim.lazy.neoconf = {
-    package = "folke/neoconf.nvim";
-    name = "neoconf";
-
-    cmd = "Neoconf";
-    opts.import = {
-      vscode = true; # local .vscode/settings.json
-      coc = false; # global/local coc-settings.json
-      nlsp = false; # global/local nlsp-settings.nvim json settings
-    };
-  };
-  # }}}
-  # {{{ Harpoon
+  # {{{ harpoon
   satellite.neovim.lazy.harpoon = {
     package = "ThePrimeagen/harpoon";
     keys =
@@ -355,4 +305,212 @@ in
   };
   # }}}
   # }}}
+  # {{{ editing 
+  # {{{ flash
+  satellite.neovim.lazy.flash = {
+    package = "folke/flash.nvim";
+
+    env.blacklist = [ "vscode" ];
+    keys =
+      let keybind = mode: mapping: action: desc: {
+        inherit mapping desc mode;
+        action = nlib.thunk /* lua */ ''require("flash").${action}()'';
+      };
+      in
+      [
+        (keybind "nxo" "s" "jump" "Flash")
+        (keybind "nxo" "S" "treesitter" "Flash Treesitter")
+        (keybind "o" "r" "remote" "Remote Flash")
+        (keybind "ox" "R" "treesitter_search" "Treesitter Search")
+        (keybind "c" "<C-S>" "toggle" "Toggle Flash Search")
+      ];
+
+    # Disable stuff like f/t/F/T
+    opts.modes.char.enabled = false;
+  };
+  # }}}
+  # {{{ clipboard-image
+  satellite.neovim.lazy.clipboard-image = {
+    package = "postfen/clipboard-image.nvim";
+
+    env.blacklist = [ "firenvim" ];
+    cmd = "PasteImg";
+
+    keys = {
+      mapping = "<leader>p";
+      action = "<cmd>PasteImg<cr>";
+      desc = "[P]aste image from clipboard";
+    };
+
+    opts.default.img_name = nlib.import ./plugins/clipboard-image.lua "img_name";
+    opts.tex = {
+      img_dir = [ "%:p:h" "img" ];
+      affix = "\\includegraphics[width=\\textwidth]{%s}";
+    };
+    opts.typst = {
+      img_dir = [ "%:p:h" "img" ];
+      affix = ''#image("%s", width: 100)'';
+    };
+  };
+  # }}}
+  # }}}
+  # {{{ ide
+  # {{{ conform
+  satellite.neovim.lazy.conform = {
+    package = "stevearc/conform.nvim";
+
+    env.blacklist = [ "vscode" ];
+    event = "BufReadPost";
+
+    opts.format_on_save.lsp_fallback = true;
+    opts.formatters_by_ft = let prettier = [ [ "prettierd" "prettier" ] ]; in
+      {
+        lua = [ "stylua" ];
+        python = [ "ruff_format" ];
+
+        javascript = prettier;
+        typescript = prettier;
+        javascriptreact = prettier;
+        typescriptreact = prettier;
+        html = prettier;
+        css = prettier;
+        markdown = prettier;
+      };
+  };
+  # }}}
+  # {{{ neoconf
+  satellite.neovim.lazy.neoconf = {
+    package = "folke/neoconf.nvim";
+
+    cmd = "Neoconf";
+
+    opts.import = {
+      vscode = true; # local .vscode/settings.json
+      coc = false; # global/local coc-settings.json
+      nlsp = false; # global/local nlsp-settings.nvim json settings
+    };
+  };
+  # }}}
+  # }}}
+  # {{{ language support 
+  # {{{ haskell
+  satellite.neovim.lazy.haskell-tools = {
+    package = "mrcjkb/haskell-tools.nvim";
+    dependencies.lua = [ "nvim-lua/plenary.nvim" ];
+    version = "^2";
+
+    env.blacklist = [ "vscode" ];
+    ft = [ "haskell" "lhaskell" "cabal" "cabalproject" ];
+
+    setup.vim.g.haskell_tools = {
+      hls = {
+        on_attach = nlib.lua /* lua */ ''require("my.plugins.lspconfig").on_attach'';
+        settings.haskell = {
+          formattingProvider = "fourmolu";
+
+          # This seems to work better with custom preludes
+          # See this issue https://github.com/fourmolu/fourmolu/issues/357
+          plugin.fourmolu.config.external = true;
+        };
+      };
+
+      # I think this wasn't showing certain docs as I expected (?)
+      tools.hover.enable = false;
+    };
+  };
+  # }}}
+  # {{{ rust 
+  # {{{ rust-tools 
+  satellite.neovim.lazy.rust-tools = {
+    package = "simrat39/rust-tools.nvim";
+
+    env.blacklist = [ "vscode" ];
+    ft = "rust";
+
+    opts.server.on_attach = nlib.languageServerOnAttach {
+      keys = {
+        mapping = "<leader>lc";
+        action = "<cmd>RustOpenCargo<cr>";
+        desc = "Open [c]argo.toml";
+      };
+    };
+  };
+  # }}}
+  # {{{ crates 
+  satellite.neovim.lazy.crates = {
+    package = "saecki/crates.nvim";
+    dependencies.lua = [ "nvim-lua/plenary.nvim" ];
+
+    env.blacklist = [ "vscode" ];
+    event = "BufReadPost Cargo.toml";
+
+    # {{{ Set up null_ls source
+    opts.null_ls = {
+      enabled = true;
+      name = "crates";
+    };
+    # }}}
+
+    setup.autocmds = [
+      # {{{ Load cmp source on insert 
+      {
+        event = "InsertEnter";
+        group = "CargoCmpSource";
+        pattern = "Cargo.toml";
+        callback = nlib.thunkString /* lua */ ''
+          require("cmp").setup.buffer({ sources = { { name = "crates" } } })
+        '';
+      }
+      # }}}
+      # {{{ Load keybinds on attach
+      {
+        event = "BufReadPost";
+        group = "CargoKeybinds";
+        pattern = "Cargo.toml";
+        # {{{ Register which-key info
+        callback.callback = nlib.contextThunk /* lua */ ''
+          require("which-key").register({
+            ["<leader>lc"] = {
+              name = "[l]ocal [c]rates",
+              bufnr = context.bufnr
+            },
+          })
+        '';
+        # }}}
+
+        callback.keys =
+          let
+            # {{{ Keymap helpers 
+            keymap = mapping: action: desc: {
+              inherit mapping desc;
+              action = nlib.lua ''require("crates").${action}'';
+            };
+
+            keyroot = "<leader>lc";
+            # }}}
+          in
+          # {{{ Keybinds
+          [
+            (keymap "${keyroot}t" "toggle" "[c]rates [t]oggle")
+            (keymap "${keyroot}r" "reload" "[c]rates [r]efresh")
+
+            (keymap "${keyroot}H" "open_homepage" "[c]rate [H]omephage")
+            (keymap "${keyroot}R" "open_repository" "[c]rate [R]epository")
+            (keymap "${keyroot}D" "open_documentation" "[c]rate [D]ocumentation")
+            (keymap "${keyroot}C" "open_crates_io" "[c]rate [C]rates.io")
+
+            (keymap "${keyroot}v" "show_versions_popup" "[c]rate [v]ersions")
+            (keymap "${keyroot}f" "show_features_popup" "[c]rate [f]eatures")
+            (keymap "${keyroot}d" "show_dependencies_popup" "[c]rate [d]eps")
+            (keymap "K" "show_popup" "[c]rate popup")
+          ];
+        # }}}
+      }
+      # }}}
+    ];
+  };
+  # }}}
+  # }}}
+  # }}}
+  # }}}
 }
diff --git a/home/features/neovim/plugins/clipboard-image.lua b/home/features/neovim/plugins/clipboard-image.lua
new file mode 100644
index 0000000..2922ce2
--- /dev/null
+++ b/home/features/neovim/plugins/clipboard-image.lua
@@ -0,0 +1,15 @@
+local M = {}
+
+function M.img_name()
+  vim.fn.inputsave()
+  local name = vim.fn.input("Name: ")
+  vim.fn.inputrestore()
+
+  if name == nil or name == "" then
+    return os.date("%y-%m-%d-%H-%M-%S")
+  end
+
+  return name
+end
+
+return M
diff --git a/home/tethys.nix b/home/tethys.nix
index c4a05ca..bf7e2c1 100644
--- a/home/tethys.nix
+++ b/home/tethys.nix
@@ -27,7 +27,7 @@
     # {{{ Editors for different formats 
     gimp # Image editing
     lmms # Music software
-    kicad # PCB editing
+    # kicad # PCB editing
     libreoffice # Free office suite
     # }}}
     # {{{ Gaming 
@@ -41,7 +41,7 @@
     obsidian # Notes
     peek # GIF recorder
     mpv # Video player
-    plover.dev # steno engine
+    # plover.dev # steno engine
     qbittorrent # Torrent client
     # google-chrome # Not my primary browser, but sometimes needed in webdev
     # obs-studio # video recorder
diff --git a/modules/common/neovim.nix b/modules/common/neovim.nix
index b4cd745..a24ffec 100644
--- a/modules/common/neovim.nix
+++ b/modules/common/neovim.nix
@@ -1,4 +1,5 @@
-# Additional theming primitives not provided by stylix
+# This module provides personalised helpers for managing plugins
+# using lazy.nvim and a set of custom runtime primitives.
 { pkgs, lib, config, ... }:
 let
   inherit (lib) types;
@@ -7,22 +8,24 @@ let
 
   # {{{ Custom types 
   myTypes = {
-    zeroOrMore = t: types.nullOr (types.either t (types.listOf t));
+    oneOrMany = t: types.either t (types.listOf t);
+    zeroOrMore = t: types.nullOr (myTypes.oneOrMany t);
 
+    # {{{ Lua code 
     luaCode = types.nullOr (types.oneOf [
       types.str
       types.path
       myTypes.luaLiteral
     ]);
 
-    luaLiteral = types.submodule (_: {
+    luaLiteral = types.submodule {
       options.__luaEncoderTag = lib.mkOption {
         type = types.enum [ "lua" ];
       };
       options.value = lib.mkOption {
         type = types.str;
       };
-    });
+    };
 
     luaValue = types.nullOr (types.oneOf [
       types.str
@@ -31,12 +34,12 @@ let
       (types.attrsOf myTypes.luaValue)
       (types.listOf myTypes.luaValue)
     ]);
-
-    # {{{ Key type
+    # }}}
+    # {{{ Lazy key
     lazyKey = types.oneOf [
       types.str
       (types.submodule
-        (_: {
+        {
           options.mapping = lib.mkOption {
             type = types.str;
             description = "The lhs of the neovim mapping";
@@ -67,11 +70,151 @@ let
             type = types.nullOr types.str;
             description = "Description for the current keymapping";
           };
-        }))
+        })
     ];
     # }}} 
-    # {{{ Lazy module type 
-    lazyModule = lib.fix (lazyModule: types.submodule (_: {
+    # {{{ Tempest key
+    tempestKey = types.submodule {
+      options = {
+        mapping = lib.mkOption {
+          example = "<leader>a";
+          type = types.str;
+          description = "The lhs of the neovim mapping";
+        };
+
+        action = lib.mkOption {
+          example = "<C-^>";
+          type = types.either types.str myTypes.luaLiteral;
+          description = "The rhs of the neovim mapping";
+        };
+
+        bufnr = lib.mkOption {
+          default = null;
+          example = true;
+          type = types.nullOr
+            (types.oneOf [
+              types.bool
+              types.integer
+              myTypes.luaLiteral
+            ]);
+          description = ''
+            The index of the buffer to apply local keymaps to. Can be set to 
+            `true` to refer to the current buffer
+          '';
+        };
+
+        mode = lib.mkOption {
+          default = null;
+          example = "nov";
+          type = types.nullOr types.str;
+          description = "The vim modes the mapping should take effect in";
+        };
+
+        silent = lib.mkOption {
+          default = null;
+          example = true;
+          type = types.nullOr types.bool;
+          description = "Whether the logs emitted by the keymap should be supressed";
+        };
+
+        expr = lib.mkOption {
+          default = null;
+          example = true;
+          type = types.nullOr types.bool;
+          description = "If set to `true`, the mapping is treated as an action factory";
+        };
+
+        desc = lib.mkOption {
+          default = null;
+          type = types.nullOr types.str;
+          description = "Description for the current keymapping";
+        };
+      };
+    };
+    # }}}
+    # {{{ Tempest autocmd
+    tempestAutocmd = types.submodule {
+      options = {
+        event = lib.mkOption {
+          example = "InsertEnter";
+          type = myTypes.oneOrMany types.str;
+          description = "Events to bind autocmd to";
+        };
+
+        pattern = lib.mkOption {
+          example = "Cargo.toml";
+          type = myTypes.oneOrMany types.str;
+          description = "File name patterns to run autocmd on";
+        };
+
+        group = lib.mkOption {
+          example = "CargoCmpSource";
+          type = types.str;
+          description = "Name of the group to create and assign autocmd to";
+        };
+
+        callback = lib.mkOption {
+          example.vim.opt.cmdheight = 1;
+          type = types.oneOf [
+            myTypes.tempestConfiguration
+            myTypes.luaCode
+          ];
+          description = ''
+            Code to run when the respctive event occurs. Will pass the event
+            object as context, which might be used for things like assigning 
+            a buffer number to local keymaps automatically.
+          '';
+        };
+      };
+    };
+    # }}}
+    # {{{ Tempest configuration
+    tempestConfiguration = types.submodule {
+      options = {
+        vim = lib.mkOption {
+          default = null;
+          type = myTypes.luaValue;
+          example.opt.cmdheight = 0;
+          description = "Values to assign to the `vim` lua global object";
+        };
+
+        keys = lib.mkOption {
+          default = null;
+          type = myTypes.zeroOrMore myTypes.tempestKey;
+          description = ''
+            Arbitrary key mappings to create. The keymappings might 
+            automatically be buffer specific depending on the context. For 
+            instance, keymappings created inside autocmds will be local unless
+            otherwise specified.
+          '';
+        };
+
+        autocmds = lib.mkOption {
+          default = null;
+          type = myTypes.zeroOrMore myTypes.tempestAutocmd;
+          description = "Arbitrary autocmds to create";
+        };
+
+        setup = lib.mkOption {
+          default = null;
+          type = types.nullOr (types.attrsOf myTypes.luaValue);
+          example.lualine.opts.theme = "auto";
+          description = ''
+            Key-pair mappings for options to pass to .setup functions imported
+            from different modules
+          '';
+        };
+
+        callback = lib.mkOption {
+          default = null;
+          type = types.nullOr myTypes.luaCode;
+          description = "Arbitrary code to run after everything else has been configured";
+        };
+      };
+    };
+    # }}}
+    # {{{ Lazy module
+    lazyModule = lib.fix (lazyModule: types.submodule ({ name ? null, ... }: {
       options = {
         package = lib.mkOption {
           type = types.oneOf [
@@ -83,7 +226,7 @@ let
         };
 
         name = lib.mkOption {
-          default = null;
+          default = name;
           type = types.nullOr types.str;
           description = "Custom name to use for the module";
           example = "lualine";
@@ -116,7 +259,7 @@ let
 
         dependencies.lua = lib.mkOption {
           default = [ ];
-          type = types.listOf lazyModule;
+          type = types.listOf (types.either types.str lazyModule);
           description = "Lazy.nvim module dependencies";
         };
 
@@ -132,12 +275,23 @@ let
           description = "Condition based on which to enable/disbale loading the package";
         };
 
+        env.blacklist = lib.mkOption {
+          default = [ ];
+          type = types.listOf (types.enum [ "firenvim" "vscode" "neovide" ]);
+          description = "Environments to blacklist plugin on";
+        };
+
         setup = lib.mkOption {
           default = null;
-          type = types.oneOf [ myTypes.luaCode types.bool ];
+          type = types.nullOr (types.oneOf [
+            myTypes.tempestConfiguration
+            myTypes.luaCode
+            types.bool
+          ]);
           description = ''
             Lua function (or module) to use for configuring the package.
-            Used instead of the canonically named `config` because said property has a special name in nix'';
+            Used instead of the canonically named `config` because said name has a special meaning in nix
+          '';
         };
 
         event = lib.mkOption {
@@ -187,29 +341,41 @@ let
   };
   # }}}
   # {{{ Lua encoders
-  mkRawLuaObject = chunks:
-    ''
-      {
-        ${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
-      }
-    '';
-
+  # We provide a custom set of helpers for generating lua code for nix.enable
+  #
   # An encoder is a function from some nix value to a string containing lua code. 
   # This object provides combinators for writing such encoders.
   luaEncoders = {
+    # {{{ "Raw" helpers 
+    mkRawLuaObject = chunks:
+      ''
+        {
+          ${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
+        }
+      '';
+    # }}}
     # {{{ General helpers 
     identity = given: given;
+    # `const` is mostly useful together with `bind`. See the lua encoder for 
+    # lazy modules for example usage.
+    const = code: _: code;
+    # Conceptually, this is the monadic bind operation for encoders.
+    # This implementation is isomoprhic to that of the reader monad in haskell.
     bind = encoder: given: encoder given given;
+    # This is probably the most useful combinnator defined in this entire object.
+    # Most of the combinators in the other categories are based on this.
     conditional = predicate: caseTrue: caseFalse:
       luaEncoders.bind (given: if predicate given then caseTrue else caseFalse);
+    # This is simply left-composition of functions
     map = f: encoder: given: encoder (f given);
+    # This is simply right-composition of functions
+    postmap = f: encoder: given: f (encoder given);
+    # This is mostly useful for debugging
     trace = message: luaEncoders.map (f: lib.traceSeq message (lib.traceVal f));
     fail = mkMessage: v: builtins.throw (mkMessage v);
-    const = code: _: code;
     # }}}
     # {{{ Base types
-    # TODO: figure out escaping and whatnot
-    string = string: ''"${string}"'';
+    string = given: ''"${lib.escape ["\"" "\\"] (toString given)}"'';
     bool = bool: if bool then "true" else "false";
     number = toString;
     nil = _: "nil";
@@ -217,6 +383,10 @@ let
     boolOr = luaEncoders.conditional lib.isBool luaEncoders.bool;
     numberOr = luaEncoders.conditional (e: lib.isFloat e || lib.isInt e) luaEncoders.number;
     nullOr = luaEncoders.conditional (e: e == null) luaEncoders.nil;
+    # We pipe a combinator which always fail through a bunch of
+    # `(thing)or : encoder -> encoder` functions, building up a combinator which
+    # can handle more and more kinds of values, until we eventually build up
+    # something that should be able to handle everything we throw at it.
     anything = lib.pipe (luaEncoders.fail (v: "Cannot figure out how to encode value ${builtins.toJSON v}")) [
       (luaEncoders.attrsetOfOr luaEncoders.anything)
       (luaEncoders.listOfOr luaEncoders.anything)
@@ -228,31 +398,43 @@ let
     ];
     # }}}
     # {{{ Lua code
-    luaCode = tag:
-      luaEncoders.luaCodeOr
-        (luaEncoders.conditional lib.isPath
-          (path: "dofile(${luaEncoders.string path}).${tag}")
-          luaEncoders.identity);
+    # Tagged lua code can be combined with other combinators without worrying
+    # about conflicts regarding how strings are interpreted.
     luaCodeOr =
       luaEncoders.conditional (e: lib.isAttrs e && (e.__luaEncoderTag or null) == "lua")
         (obj: obj.value);
+    # This is the most rudimentary (and currently only) way of handling paths.
+    luaImportOr = tag:
+      luaEncoders.conditional lib.isPath
+        (path: "dofile(${luaEncoders.string path}).${tag}");
+    # Accepts both tagged and untagged strings of lua code.
+    luaString = luaEncoders.luaCodeOr luaEncoders.identity;
+    # This simply combines the above combinators into one.
+    luaCode = tag: luaEncoders.luaImportOr tag luaEncoders.luaString;
     # }}}
     # {{{ Lists
     listOf = encoder: list:
-      mkRawLuaObject (lib.lists.map encoder list);
-    tryNonemptyList = encoder: luaEncoders.conditional
-      (l: l == [ ])
-      luaEncoders.nil
-      (luaEncoders.listOf encoder);
+      luaEncoders.mkRawLuaObject (lib.lists.map encoder list);
     listOfOr = encoder:
       luaEncoders.conditional
         lib.isList
         (luaEncoders.listOf encoder);
+    # Returns nil when given empty lists
+    tryNonemptyList = encoder: luaEncoders.conditional
+      (l: l == [ ])
+      luaEncoders.nil
+      (luaEncoders.listOf encoder);
     oneOrMany = encoder: luaEncoders.listOfOr encoder encoder;
+    # Can encode:
+    # - zero values as nil
+    # - one value as itself
+    # - multiple values as a list
     zeroOrMany = encoder: luaEncoders.nullOr (luaEncoders.oneOrMany encoder);
+    # Coerces non list values to lists of one element.
     oneOrManyAsList = encoder: luaEncoders.map
       (given: if lib.isList given then given else [ given ])
       (luaEncoders.listOf encoder);
+    # Coerces lists of one element to said element.
     listAsOneOrMany = encoder:
       luaEncoders.map
         (l: if lib.length l == 1 then lib.head l else l)
@@ -260,7 +442,7 @@ let
     # }}}
     # {{{ Attrsets
     attrsetOf = encoder: object:
-      mkRawLuaObject (lib.mapAttrsToList
+      luaEncoders.mkRawLuaObject (lib.mapAttrsToList
         (name: value:
           let result = encoder value;
           in
@@ -270,7 +452,14 @@ let
         object
       );
     attrsetOfOr = of: luaEncoders.conditional lib.isAttrs (luaEncoders.attrsetOf of);
-    attrset = noNils: listOrder: listSpec: spec: attrset:
+    # This is the most general combinator provided in this section.
+    #
+    # We accept:
+    # - a `noNils` flag which will automatically remove any nil properties
+    # - order of props that should be interpreted as list elements
+    # - spec of props that should be interpreted as list elements
+    # - record of props that should be interpreted as attribute props
+    attrset = noNils: listOrder: spec: attrset:
       let
         shouldKeep = given:
           if noNils then
@@ -280,7 +469,7 @@ let
 
         listChunks = lib.lists.map
           (attr:
-            let result = listSpec.${attr} (attrset.${attr} or null);
+            let result = spec.${attr} (attrset.${attr} or null);
             in
             lib.optionalString (shouldKeep result) result
           )
@@ -289,17 +478,18 @@ let
           (attr: encoder:
             let result = encoder (attrset.${attr} or null);
             in
-            lib.optionalString (shouldKeep result)
+            lib.optionalString (!(lib.elem attr listOrder) && shouldKeep result)
               "${attr} = ${result}"
           )
           spec;
       in
-      mkRawLuaObject (listChunks ++ objectChunks);
+      luaEncoders.mkRawLuaObject (listChunks ++ objectChunks);
     # }}}
   };
 
   e = luaEncoders;
   # }}}
+  # {{{ Helpers 
   # Format and write a lua file to disk
   writeLuaFile = path: name: text:
     let
@@ -312,18 +502,21 @@ let
       cp --no-preserve=mode ${unformatted} $out/${destination}
       ${lib.getExe pkgs.stylua} --config-path ${cfg.styluaConfig} $out/${destination}
     '';
+  # }}}
 in
 {
+  # {{{ Option declaration 
   options.satellite.neovim = {
     lazy = lib.mkOption {
       default = { };
-      description = "Record of persistent locations (eg: /persist)";
+      description = "Record of plugins to install using lazy.nvim";
       type = types.attrsOf myTypes.lazyModule;
     };
 
+    # {{{ Generated
     generated = {
       lazy = lib.mkOption {
-        type = types.attrsOf (types.submodule (_: {
+        type = types.attrsOf (types.submodule {
           options = {
             raw = lib.mkOption {
               type = types.lines;
@@ -335,7 +528,7 @@ in
               description = "The lua script generated using the other options";
             };
           };
-        }));
+        });
         description = "Attrset containing every module generated from the lazy configuration";
       };
 
@@ -351,8 +544,10 @@ in
         description = "List of packages to give neovim access to";
       };
     };
-
+    # }}}
+    # {{{ Lua generation lib 
     lib = {
+      # {{{ Basic lua generators
       lua = lib.mkOption {
         default = value: { inherit value; __luaEncoderTag = "lua"; };
         type = types.functionTo myTypes.luaLiteral;
@@ -364,67 +559,166 @@ in
         type = types.functionTo (types.functionTo myTypes.luaLiteral);
         description = "import some identifier from some module";
       };
+      # }}}
+      # {{{ Encoders 
+      encode = lib.mkOption {
+        default = luaEncoders.anything;
+        type = types.functionTo types.str;
+        description = "Encode a nix value to a lua string";
+      };
 
-      blacklistEnv = lib.mkOption {
-        default = given: cfg.lib.lua ''
-          require(${e.string cfg.env.module}).blacklist(${e.listOf e.string given})
+      encodeTempestConfiguration = lib.mkOption {
+        default = given:
+          e.attrset true [ ]
+            {
+              vim = e.anything;
+              callback = e.nullOr e.luaString;
+              setup = e.nullOr (e.attrsetOf e.anything);
+              keys = e.zeroOrMany (e.attrset true [ ] {
+                mapping = e.string;
+                action = e.luaCodeOr e.string;
+                desc = e.nullOr e.string;
+                expr = e.nullOr e.bool;
+                mode = e.nullOr e.string;
+                silent = e.nullOr e.bool;
+                buffer = e.nullOr (e.luaCodeOr (e.boolOr e.number));
+              });
+              autocmds = e.zeroOrMany (e.attrset true [ ] {
+                event = e.oneOrMany e.string;
+                pattern = e.oneOrMany e.string;
+                group = e.string;
+                callback = e.conditional lib.isAttrs
+                  cfg.lib.encodeTempestConfiguration
+                  e.luaString;
+              });
+            }
+            given;
+        type = types.functionTo types.str;
+        description = "Generate a lua object for passing to my own lua runtime for configuration";
+      };
+      # }}}
+      # {{{ Thunks
+      # This version of `nlib.thunk` is required in ceratain cases because 
+      # of issues with `types.oneOf [types.submodule ..., types.submodule]` not
+      # working as intended atm.
+      thunkString = lib.mkOption {
+        default = given: /* lua */ ''
+          function() ${e.luaString given} end
         '';
-        type = types.functionTo myTypes.luaLiteral;
-        description = "Generate a lazy.cond predicate which disables a module if one of the given envs is active";
+        type = types.functionTo types.str;
+        description = "Wrap a lua expression into a lua function as a string";
       };
 
       thunk = lib.mkOption {
-        default = given: cfg.lib.lua ''
-          function() return ${given} end
-        '';
+        default = given: cfg.lib.lua (cfg.lib.thunkString given);
         type = types.functionTo myTypes.luaLiteral;
         description = "Wrap a lua expression into a lua function";
       };
-    };
 
-    env = {
-      module = lib.mkOption {
+
+      contextThunk = lib.mkOption {
+        default = given: cfg.lib.lua /* lua */ ''
+          function(context) ${e.luaString given} end
+        '';
+        type = types.functionTo myTypes.luaLiteral;
+        description = "Wrap a lua expression into a lua function taking an argument named `context`";
+      };
+      # }}}
+      # {{{ Language server on attach
+      languageServerOnAttach = lib.mkOption {
+        default = given: cfg.lib.lua /* lua */ ''
+          function(client, bufnr)
+            require(${e.string cfg.runtime.tempest}).configure(${cfg.lib.encodeTempestConfiguration given}, 
+              { client = client; bufnr = bufnr; })
+
+            require(${e.string cfg.runtime.languageServerOnAttach}).on_attach(client, bufnr)
+          end
+        '';
+        type = types.functionTo myTypes.luaCode;
+        description = "Attach a language server and run some additional code";
+      };
+      # }}}
+    };
+    # }}}
+    # {{{ Neovim runtime module paths
+    runtime = {
+      env = lib.mkOption {
         type = types.str;
         example = "my.helpers.env";
-        description = "Module where to import env flags from";
+        description = "Module to import env flags from";
+      };
+
+      tempest = lib.mkOption {
+        type = types.str;
+        example = "my.runtime.tempest";
+        description = "Module to import the tempest runtime from";
+      };
+
+      languageServerOnAttach = lib.mkOption {
+        type = types.str;
+        example = "my.runtime.lspconfig";
+        description = "Module to import langauge server .on_attach function from";
       };
     };
+    # }}}
 
     styluaConfig = lib.mkOption {
       type = types.path;
       description = "Config to use for formatting lua modules";
     };
   };
-
+  # }}}
+  # {{{ Config generation 
+  # {{{ Lazy module generation 
   config.satellite.neovim.generated.lazy =
     let
+      # {{{ Lazy key encoder
       lazyKeyEncoder =
-        e.stringOr (e.attrset true [ "mapping" "action" ]
-          {
-            mapping = e.string;
-            action = e.nullOr (e.luaCodeOr e.string);
-          }
-          {
-            mode = e.nullOr
-              (e.map
-                lib.strings.stringToCharacters
-                (e.listAsOneOrMany e.string));
-            desc = e.nullOr e.string;
-            ft = e.zeroOrMany e.string;
-          });
-
+        e.stringOr (e.attrset true [ "mapping" "action" ] {
+          mapping = e.string;
+          action = e.nullOr (e.luaCodeOr e.string);
+          mode = e.nullOr
+            (e.map
+              lib.strings.stringToCharacters
+              (e.listAsOneOrMany e.string));
+          desc = e.nullOr e.string;
+          ft = e.zeroOrMany e.string;
+        });
+      # }}}
+      # {{{ Lazy spec encoder 
       lazyObjectEncoder = e.bind
         (opts: e.attrset true [ "package" ]
-          { package = e.string; }
           {
+            package = e.string;
             name = e.nullOr e.string;
             main = e.nullOr e.string;
             tag = e.nullOr e.string;
             version = e.nullOr e.string;
-            dependencies = e.map (d: d.lua) (e.tryNonemptyList lazyObjectEncoder);
+            dependencies = e.map (d: d.lua) (e.tryNonemptyList (e.stringOr lazyObjectEncoder));
             lazy = e.nullOr e.bool;
-            cond = e.nullOr (e.luaCode "cond");
-            config = e.const (e.nullOr (e.boolOr (e.luaCode "config")) opts.setup);
+            cond =
+              if opts.env.blacklist != [ ] then
+                assert lib.asserts.assertMsg (opts.cond == null)
+                  "env.blacklist overrides plugin condition";
+                e.const /* lua */ ''
+                  require(${e.string cfg.runtime.env}).blacklist(${e.listOf e.string opts.env.blacklist})
+                ''
+              else
+                e.nullOr (e.luaCode "cond");
+
+            config = _:
+              let
+                wrap = given: /* lua */''
+                  function(lazy, opts)
+                    require(${e.string cfg.runtime.tempest}).configure(${given}, 
+                      { lazy = lazy; opts = opts; })
+                  end
+                '';
+              in
+              e.conditional lib.isAttrs
+                (e.postmap wrap cfg.lib.encodeTempestConfiguration)
+                (e.nullOr (e.boolOr (e.luaCode "config")))
+                opts.setup;
             init = e.nullOr (e.luaCode "init");
             event = e.zeroOrMany e.string;
             cmd = e.zeroOrMany e.string;
@@ -433,9 +727,10 @@ in
             passthrough = e.anything;
             opts = e.anything;
           });
+      # }}}
 
       makeLazyScript = opts: ''
-        -- This file was generated by nix
+        -- ❄️ This file was generated using nix ^~^
         return ${lazyObjectEncoder opts}
       '';
     in
@@ -451,12 +746,15 @@ in
       name = "lazy-nvim-modules";
       paths = lib.attrsets.mapAttrsToList (_: m: m.module) cfg.generated.lazy;
     };
+  # }}}
 
   config.satellite.neovim.generated.dependencies =
     lib.pipe cfg.lazy
       [
         (lib.attrsets.mapAttrsToList (_: m: m.dependencies.nix))
         lib.lists.flatten
-      ]
-  ;
+      ];
+  # }}}
 }
+
+
diff --git a/overlays/default.nix b/overlays/default.nix
index 5e9b0ed..4055913 100644
--- a/overlays/default.nix
+++ b/overlays/default.nix
@@ -11,7 +11,7 @@
     # });
 
     # {{{ Discordchatexporter
-    discordchatexporter-cli = prev.discordchatexporter-cli.overrideAttrs (_: rec {
+    discordchatexporter-cli = prev.discordchatexporter-cli.overrideAttrs (_: {
       version = "unstable-2023-06-21";
       src = prev.fetchFromGitHub {
         owner = "tyrrrz";