summary refs log tree commit diff
path: root/.vim
diff options
context:
space:
mode:
Diffstat (limited to '.vim')
-rw-r--r--.vim/after/ftplugin/dirbuf.lua2
-rw-r--r--.vim/after/ftplugin/gitmessengerpopup.lua1
-rw-r--r--.vim/after/ftplugin/gitrebase.lua80
-rw-r--r--.vim/after/ftplugin/go.lua2
-rw-r--r--.vim/after/ftplugin/mail.lua4
-rw-r--r--.vim/after/ftplugin/markdown.lua57
-rw-r--r--.vim/after/ftplugin/rust.lua1
-rw-r--r--.vim/after/ftplugin/tex.lua8
-rw-r--r--.vim/after/ftplugin/todofile.lua23
-rw-r--r--.vim/after/snippets/mail.snippets15
-rw-r--r--.vim/after/snippets/mail.snippets.todo18
-rw-r--r--.vim/after/snippets/readme1
-rw-r--r--.vim/colorizer-nohash.patch14
-rw-r--r--.vim/init.lua191
-rw-r--r--.vim/lazy-lock.json56
-rw-r--r--.vim/lua/internal/align.lua124
-rw-r--r--.vim/lua/internal/colors.lua18
-rw-r--r--.vim/lua/internal/commit-preview.lua67
-rw-r--r--.vim/lua/internal/cursor.lua11
-rw-r--r--.vim/lua/internal/dap.lua104
-rw-r--r--.vim/lua/internal/find.lua106
-rw-r--r--.vim/lua/internal/job.lua106
-rw-r--r--.vim/lua/internal/lsp.lua66
-rw-r--r--.vim/lua/internal/lsp_symbols.lua8
-rw-r--r--.vim/lua/internal/misc.lua99
-rw-r--r--.vim/lua/internal/snips.lua29
-rw-r--r--.vim/lua/internal/sortline.lua78
-rw-r--r--.vim/lua/internal/statusline.lua66
-rw-r--r--.vim/lua/internal/syncbg.lua46
-rw-r--r--.vim/lua/internal/zk.lua7
-rw-r--r--.vim/lua/plugins.lua495
-rw-r--r--.vim/snippets/all.lua9
-rw-r--r--.vim/snippets/cmake.lua26
-rw-r--r--.vim/snippets/lua.lua22
-rw-r--r--.vim/snippets/mail.lua11
35 files changed, 1249 insertions, 722 deletions
diff --git a/.vim/after/ftplugin/dirbuf.lua b/.vim/after/ftplugin/dirbuf.lua
index 109e3a4..4f41efc 100644
--- a/.vim/after/ftplugin/dirbuf.lua
+++ b/.vim/after/ftplugin/dirbuf.lua
@@ -1 +1,3 @@
 vim.opt_local.list = false
+
+vim.keymap.set('n', '/', ':Telescope current_buffer_fuzzy_find<cr>', { buffer = vim.api.nvim_get_current_buf() })
diff --git a/.vim/after/ftplugin/gitmessengerpopup.lua b/.vim/after/ftplugin/gitmessengerpopup.lua
new file mode 100644
index 0000000..ab4688f
--- /dev/null
+++ b/.vim/after/ftplugin/gitmessengerpopup.lua
@@ -0,0 +1 @@
+vim.keymap.set('n', 'gd', require('internal.commit-preview').under_cursor, { buffer = 0, desc = 'commit preview' })
diff --git a/.vim/after/ftplugin/gitrebase.lua b/.vim/after/ftplugin/gitrebase.lua
index e48b296..6b9e5dc 100644
--- a/.vim/after/ftplugin/gitrebase.lua
+++ b/.vim/after/ftplugin/gitrebase.lua
@@ -1,59 +1,33 @@
-local commit_preview = function()
+local function commit_preview()
+  local beforewin = vim.api.nvim_get_current_win()
   local commit = vim.api.nvim_get_current_line():match([[ ([0-9a-f]+) ]])
-  local result = require('internal.job')
-    .jobstart({
-      command = 'git',
-      args = { 'show', commit },
-      format_title = function(_, _)
-        return 'git-show'
-      end,
-      populate_quickfix = false,
-    })
-    :wait()
-    :result()
-
-  -- create buffer
-  local bufnr = vim.api.nvim_create_buf(false, true)
-  assert(bufnr, 'Failed to create buffer')
-  vim.api.nvim_buf_set_name(bufnr, commit)
-  vim.api.nvim_buf_set_option(bufnr, 'filetype', 'git')
-  -- add lines
-  vim.api.nvim_buf_set_option(bufnr, 'modifiable', true)
-  vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, result)
-  vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)
+  if not commit then
+    return
+  end
+  require('internal.commit-preview').commit_preview(commit, function(bufnr, winid)
+    -- keymaps:
+    local close = function()
+      pcall(vim.api.nvim_win_close, winid, true)
+      pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
+    end
+    vim.keymap.set('n', 'q', close, { buffer = bufnr })
 
-  -- create window
-  local winpos = 'belowright'
-  local winheight = #result <= 25 and #result or 25
-  vim.cmd(string.format('%s %dsplit', winpos, winheight))
-  local winid = vim.api.nvim_get_current_win()
-  -- vim.cmd(string.format("buffer %d", bufnr))
-  vim.api.nvim_win_set_buf(winid, bufnr)
-  vim.cmd('wincmd p')
+    local goup = function()
+      vim.api.nvim_set_current_win(beforewin)
+      vim.cmd('normal k')
+      vim.cmd('normal cp')
+    end
+    vim.keymap.set('n', '<up>', goup, { buffer = bufnr })
+    vim.keymap.set('n', '<C-p>', goup, { buffer = bufnr })
 
-  local close = function()
-    pcall(vim.api.nvim_win_close, winid, true)
-    pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
-  end
-  -- close with q
-  vim.keymap.set('n', 'q', close, { buffer = bufnr })
-  -- close on CursorMoved
-  local old_cursor = vim.api.nvim_win_get_cursor(0)
-  local group = vim.api.nvim_create_augroup('gitcommit_win_' .. winid, {})
-  vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
-    group = group,
-    callback = function()
-      local cursor = vim.api.nvim_win_get_cursor(0)
-      if
-        (old_cursor[1] ~= cursor[1] or old_cursor[2] ~= cursor[2])
-        and vim.api.nvim_get_current_win() ~= winid
-      then
-        close()
-        return
-      end
-      old_cursor = cursor
-    end,
-  })
+    local godown = function()
+      vim.api.nvim_set_current_win(beforewin)
+      vim.cmd('normal j')
+      vim.cmd('normal cp')
+    end
+    vim.keymap.set('n', '<down>', godown, { buffer = bufnr })
+    vim.keymap.set('n', '<C-n>', godown, { buffer = bufnr })
+  end)
 end
 
 vim.keymap.set('n', 'cp', commit_preview, { buffer = 0, desc = 'commit preview' })
diff --git a/.vim/after/ftplugin/go.lua b/.vim/after/ftplugin/go.lua
index 94eaa0e..cda6a34 100644
--- a/.vim/after/ftplugin/go.lua
+++ b/.vim/after/ftplugin/go.lua
@@ -5,7 +5,7 @@ vim.keymap.set('n', '<leader>i', function()
   if not iface or iface == '' then
     return
   end
-  require('misc').word_under_cursor(function(word)
+  require('internal.cursor').word(function(word)
     vim.cmd('normal ][o')
     local args = {
       'impl',
diff --git a/.vim/after/ftplugin/mail.lua b/.vim/after/ftplugin/mail.lua
index 1aa5caf..6e0fa20 100644
--- a/.vim/after/ftplugin/mail.lua
+++ b/.vim/after/ftplugin/mail.lua
@@ -1,2 +1,4 @@
 require('internal.spell')
-vim.opt.cursorline = false
+
+vim.opt_local.cursorline = false
+vim.opt_local.commentstring = '> %s'
diff --git a/.vim/after/ftplugin/markdown.lua b/.vim/after/ftplugin/markdown.lua
index 87fe69d..7e6b22e 100644
--- a/.vim/after/ftplugin/markdown.lua
+++ b/.vim/after/ftplugin/markdown.lua
@@ -1,4 +1,4 @@
-require('internal.spell')
+-- require('internal.spell')
 vim.opt_local.foldenable = false
 vim.opt_local.foldlevel = 1
 
@@ -8,9 +8,7 @@ vim.fn.matchadd('Conceal', '- \\[ \\]\\&- \\zs\\[ ', 0, -1, { conceal = '☐ ' }
 vim.fn.matchadd('Conceal', '- \\[x\\]\\&- \\zs\\[x', 0, -1, { conceal = '🞕 ' })
 vim.fn.matchadd('Conceal', '- \\[[ x]\\zs\\]', 0, -1, { conceal = ' ' })
 
-vim.keymap.set('n', '<Plug>Markdown_OpenUrlUnderCursor', function()
-  require('internal.misc').open_under_cursor('url-launcher')
-end)
+vim.keymap.set('n', '<Plug>Markdown_OpenUrlUnderCursor', vim.ui.open)
 
 vim.keymap.set(
   'n',
@@ -45,31 +43,26 @@ end
 vim.keymap.set('n', '<space><space>', toggle_checkbox, { desc = 'toggle checkbox', buffer = true })
 vim.keymap.set('i', '<c-space>', toggle_checkbox, { desc = 'toggle checkbox', buffer = true })
 
--- vim.keymap.set('n', '<leader>zn', function() require('zk').new() end, {buffer=true})
--- vim.keymap.set('n', '<leader>zi', function() require('zk').index() end, {buffer=true})
--- vim.cmd [[ command! -nargs=* ZettelkastenNew lua require('zk').new(nil, <f-args>) ]]
-
-vim.keymap.set('n', 'gt', function()
-  require('telescope._extensions.todo-comments').exports.todo({ cwd = vim.env.ZK_NOTEBOOK_DIR })
-end, { desc = 'show zk todos', buffer = true })
-
-vim.keymap.set('n', 'o', function()
-  local cursor = vim.api.nvim_win_get_cursor(0)
-  -- need to decrement the row number here, to account for 0 indexing
-  local node = vim.treesitter.get_node_at_pos(0, cursor[1] - 1, cursor[2], {})
-  while node do
-    if node:type() == 'list_item' then
-      local ln = vim.treesitter.get_node_text(node:child(0), 0, {})
-      local ln_start = string.match(ln, '^(%d+)%.')
-      if ln_start ~= nil then
-        ln = ln:gsub(ln_start, tonumber(ln_start) + 1)
-      end
-      vim.api.nvim_buf_set_lines(0, cursor[1], cursor[1], false, { ln })
-      vim.api.nvim_feedkeys('jA', 'n', false)
-      return
-    end
-    node = node:parent()
-  end
-
-  vim.api.nvim_feedkeys('o', 'n', false)
-end, { desc = 'smart-o', buffer = true })
+-- vim.keymap.set('n', 'o', function()
+--   local cursor = vim.api.nvim_win_get_cursor(0)
+--   -- need to decrement the row number here, to account for 0 indexing
+--   local node = vim.treesitter.get_node({
+--     bufnr = 0,
+--     pos = { cursor[1] - 1, cursor[2] },
+--   })
+--   while node do
+--     if node:type() == 'list_item' then
+--       local ln = vim.treesitter.get_node_text(node:child(0), 0, {})
+--       local ln_start = string.match(ln, '^(%d+)%.')
+--       if ln_start ~= nil then
+--         ln = ln:gsub(ln_start, tonumber(ln_start) + 1)
+--       end
+--       vim.api.nvim_buf_set_lines(0, cursor[1], cursor[1], false, { ln })
+--       vim.api.nvim_feedkeys('jA', 'n', false)
+--       return
+--     end
+--     node = node:parent()
+--   end
+--
+--   vim.api.nvim_feedkeys('o', 'n', false)
+-- end, { desc = 'smart-o', buffer = true })
diff --git a/.vim/after/ftplugin/rust.lua b/.vim/after/ftplugin/rust.lua
new file mode 100644
index 0000000..a26bebe
--- /dev/null
+++ b/.vim/after/ftplugin/rust.lua
@@ -0,0 +1 @@
+vim.opt_local.makeprg = [[cargo run]]
diff --git a/.vim/after/ftplugin/tex.lua b/.vim/after/ftplugin/tex.lua
index 740ad48..bf62d26 100644
--- a/.vim/after/ftplugin/tex.lua
+++ b/.vim/after/ftplugin/tex.lua
@@ -1,2 +1,8 @@
-vim.opt_local.makeprg = 'latexmk -1 -n %'
+local find_doc_root = require('lspconfig.util').root_pattern('Tectonic.toml')
+if find_doc_root(vim.fn.expand('%:p:h')) then
+  vim.opt_local.makeprg = 'latextbx mk -1'
+else
+  vim.opt_local.makeprg = 'latextbx mk -1 %'
+end
+
 vim.opt_local.textwidth = 80
diff --git a/.vim/after/ftplugin/todofile.lua b/.vim/after/ftplugin/todofile.lua
new file mode 100644
index 0000000..2080d39
--- /dev/null
+++ b/.vim/after/ftplugin/todofile.lua
@@ -0,0 +1,23 @@
+local function toggle_todo()
+  local ln, n = vim.api.nvim_get_current_line(), 0
+  ln, n = string.gsub(ln, 'TODO', 'DONE', 1)
+  if n > 0 then
+    goto set_line
+  end
+  ln, n = string.gsub(ln, 'DONE', 'TODO', 1)
+  if n > 0 then
+    goto set_line
+  end
+  ln, n = string.gsub(ln, '^(-|*) ', '%1 TODO ')
+  if n > 0 then
+    goto set_line
+  end
+
+  goto exit -- skip over set_current_line
+
+  ::set_line::
+  vim.api.nvim_set_current_line(ln)
+
+  ::exit::
+end
+vim.keymap.set('n', '<space><space>', toggle_todo, { desc = 'toggle todo', buffer = true })
diff --git a/.vim/after/snippets/mail.snippets b/.vim/after/snippets/mail.snippets
new file mode 100644
index 0000000..afca39f
--- /dev/null
+++ b/.vim/after/snippets/mail.snippets
@@ -0,0 +1,15 @@
+
+snippet sehr "Sehr geehrte (...)"
+	Sehr geehrte Damen und Herren,
+	
+	$0
+
+snippet mfg "Mit freundlichen Grüßen"
+	Mit freundlichen Grüßen,
+	
+	Robert Günzler
+
+snippet sign "Signature"
+	${1:Cheers},
+	
+	Robert Günzler
diff --git a/.vim/after/snippets/mail.snippets.todo b/.vim/after/snippets/mail.snippets.todo
deleted file mode 100644
index 1b49c9d..0000000
--- a/.vim/after/snippets/mail.snippets.todo
+++ /dev/null
@@ -1,18 +0,0 @@
-
-snippet sehr "Sehr geehrte (...)"
-Sehr geehrte Damen und Herren,
-
-$0
-endsnippet
-
-snippet mfg "Mit freundlichen Grüßen"
-Mit freundlichen Grüßen,
-
-Robert Günzler
-endsnippet
-
-snippet sign "Signature"
-${1:Cheers},
-
-Robert Günzler
-endsnippet
diff --git a/.vim/after/snippets/readme b/.vim/after/snippets/readme
new file mode 100644
index 0000000..c71464a
--- /dev/null
+++ b/.vim/after/snippets/readme
@@ -0,0 +1 @@
+move to ~/.vim/snippets and implement in lua
diff --git a/.vim/colorizer-nohash.patch b/.vim/colorizer-nohash.patch
new file mode 100644
index 0000000..2215c76
--- /dev/null
+++ b/.vim/colorizer-nohash.patch
@@ -0,0 +1,14 @@
+diff --git a/lua/colorizer.lua b/lua/colorizer.lua
+index e47e079..d986a4a 100644
+--- a/lua/colorizer.lua
++++ b/lua/colorizer.lua
+@@ -190,7 +190,7 @@ local function rgb_hex_parser(line, i, minlen, maxlen)
+ 	if i > 1 and byte_is_alphanumeric(line:byte(i-1)) then
+ 		return
+ 	end
+-	if line:byte(i) ~= b_hash then
++	if line:byte(i) ~= b_hash and (not vim.g.colorizer_nohash) then
+ 		return
+ 	end
+ 	local j = i + 1
+
diff --git a/.vim/init.lua b/.vim/init.lua
index f6118c0..d46dbfc 100644
--- a/.vim/init.lua
+++ b/.vim/init.lua
@@ -1,4 +1,15 @@
 -- vim: fdm=marker fdl=0
+
+vim.cmd [[
+  function! LessInitFunc()
+    set nolist
+    set nocursorcolumn nocursorline
+    set laststatus=0
+    set readonly
+    set nonumber
+  endfunction
+]]
+
 local g = vim.g
 g.mapleader = ' ' -- <leader> key to <space>
 g.maplocalleader = '\\'
@@ -24,7 +35,8 @@ opt.fillchars:append({
 })
 opt.foldenable = true
 opt.foldmethod = 'expr'
-opt.foldexpr = 'nvim_treesitter#foldexpr()'
+opt.foldexpr = "v:lua.vim.treesitter.foldexpr()"
+opt.foldtext = 'v:lua.vim.treesitter.foldtext()'
 -- opt.foldlevel = 0
 opt.foldlevelstart = 999
 opt.hidden = true
@@ -44,7 +56,7 @@ opt.listchars:append({
 opt.mouse = 'a'
 opt.number = true
 opt.relativenumber = false
-opt.pumheight = 0 -- use available screenspace
+opt.pumheight = 12 -- 0 use available screenspace
 opt.redrawtime = 1500
 opt.ruler = true
 opt.scrolloff = 3
@@ -99,18 +111,16 @@ g.undodir = undodir
 opt.undofile = true
 -- }}}
 
-g.do_filetype_lua = true
--- g.do_legacy_filetype = false
--- g.did_load_filetypes = 0
-
-vim.g.night_and_day = 'night'
+opt.background = 'dark'
+require('internal.syncbg').setup()
 
 require('_globals')
 require('plugins')
 
-require('internal.align').setup({ bindings = true })
 require('internal.hardmode')
 
+-- TODO: load internal.* like normal plugins using lazy
+
 -- DIAGNOSITIC {{{
 vim.diagnostic.config({
   signs = true,
@@ -119,33 +129,23 @@ vim.diagnostic.config({
   },
   float = {
     severity = { min = vim.diagnostic.severity.INFO },
-    focusable = false,
-    source = 'always',
-    prefix = '',
-    scope = 'cursor',
   },
   virtual_text = {
-    severity = { max = vim.diagnostic.severity.HINT },
-    source = true,
-    prefix = '',
+    severity = { min = vim.diagnostic.severity.ERROR },
   },
-  -- virtual_lines    = { prefix = '⬐  ' },
-
-  update_in_insert = false,
   severity_sort = true,
 })
 vim.fn.sign_define({
-  { name = 'DiagnosticSignError', text = '✗', texthl = 'DiagnosticSignError' },
-  -- {name='DiagnosticSignWarn',  text='⚡', texthl='DiagnosticSignWarn'},
-  { name = 'DiagnosticSignWarn', text = '⚐', texthl = 'DiagnosticSignWarn' },
+  { name = 'DiagnosticSignError', text = '✗', texthl = 'DiagnosticSignError', linehl = 'Search' },
+  { name = 'DiagnosticSignWarn', text = '▷', texthl = 'DiagnosticSignWarn' },
   { name = 'DiagnosticSignInfo', text = 'ℹ', texthl = 'DiagnosticSignInfo' },
   { name = 'DiagnosticSignHint', text = '🞶', texthl = 'DiagnosticSignHint' },
 })
-vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'next diagnostic' })
-vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'prev diagnostic' })
+vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Next diagnostic' })
+vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Prev diagnostic' })
 vim.keymap.set(
   'n',
-  '<localleader>d',
+  'DD',
   vim.diagnostic.open_float,
   { desc = 'show line diagnositics' }
 )
@@ -168,7 +168,8 @@ vim.keymap.set('n', '<tab>', [[ :normal za<cr> ]], { silent = true })
 
 vim.keymap.set({ 'n', 'v' }, 'gf', function()
   local Path = require('plenary.path')
-  local cfile = vim.fn.expand('<cfile>')
+  -- call twice to expand env vars
+  local cfile = vim.fn.expand(vim.fn.expand('<cfile>'))
   if string.sub(cfile, 0, 7) == 'file://' then
     cfile = string.sub(cfile, 8)
   end
@@ -191,33 +192,11 @@ vim.keymap.set('v', 'T', function()
   vim.api.nvim_input('<esc>')
 end)
 
-local misc = require('internal.misc')
-vim.keymap.set('n', 'gd', function()
-  misc.open_under_cursor(nil, true)
-end, { desc = 'open under cursor' })
-vim.keymap.set('n', 'gx', function()
-  misc.open_under_cursor('url-launcher')
-end, { desc = 'url-launcher' })
-vim.keymap.set('n', 'gu', function()
-  misc.open_under_cursor('url-launcher')
-end, { desc = 'url-launcher' })
--- vim.keymap.set('n',  '<localleader>gs', function() misc.word_under_cursor(function(s) vim.fn.feedkeys(':%s/' .. s .. '//g'); cmd [[ call feedkeys("\<left>\<left>") ]] end) end)
-
-vim.keymap.set('n', '<leader>r', misc.live_grep, { desc = 'live grep (rg)' })
-vim.keymap.set('n', '<leader>f', misc.find_files, { desc = 'find files (fd)' })
-
 vim.keymap.set('n', '<localleader>dgl', 'diffget LOCAL', { desc = 'diff get local' })
 vim.keymap.set('n', '<localleader>dgr', 'diffget REMOTE', { desc = 'diff get remote' })
 vim.keymap.set('n', '<localleader>dpl', 'diffpush LOCAL', { desc = 'diff push local' })
 vim.keymap.set('n', '<localleader>dpr', 'diffpush REMOTE', { desc = 'diff push remote' })
 
-vim.keymap.set(
-  'x',
-  '<localleader>f',
-  [[<ESC><CMD>lua require("internal.misc").fold_block()<CR>]],
-  { noremap = true, silent = true, desc = 'fold block' }
-)
-
 -- vim.keymap.set('v', '<c-r>', [[y:%s/<c-r>h//gc<left><left><left>]], { noremap = true, silent = true })
 vim.keymap.set('n', 'yP', function()
   local fnln = (vim.fn.expand('%:p') .. ':' .. vim.fn.line('.'))
@@ -231,6 +210,16 @@ vim.keymap.set('n', 'yT', function()
 end, { desc = 'yank file name' })
 -- }}}
 
+-- MENU -- {{{
+vim.cmd [[unmenu PopUp.-1-]]
+vim.cmd [[unmenu PopUp.How-to\ disable\ mouse]]
+
+vim.cmd [[nnoremenu PopUp.Inspect :normal K<cr>]]
+vim.cmd [[nnoremenu PopUp.Goto :normal gd<cr>]]
+vim.cmd [[nnoremenu PopUp.References :normal gR<cr>]]
+vim.cmd [[nnoremenu PopUp.Diagnostic :normal DD<cr>]]
+-- }}}
+
 -- AUTO GROUPS -- {{{
 local augroup = vim.api.nvim_create_augroup
 local autocmd = vim.api.nvim_create_autocmd
@@ -251,7 +240,7 @@ do
       if (not qf == vim.empty_dict()) and qf and qf.size > 0 then
         -- check if the quickfix buffer is already open
         for bufnr in ipairs(vim.api.nvim_list_bufs()) do
-          if vim.api.nvim_buf_get_option(bufnr, 'buftype') == 'quickfix' then
+          if vim.api.nvim_buf_get_option_value(bufnr, 'buftype') == 'quickfix' then
             return
           end
         end
@@ -334,38 +323,87 @@ autocmd({ 'BufRead', 'BufNewFile' }, {
   group = augroup('DetectBinDir', {}),
 })
 
-autocmd({ 'TermOpen' }, {
-  pattern = 'term://*',
-  callback = function()
-    local bufnr = vim.api.nvim_get_current_buf()
-    vim.opt_local.number = false
-    vim.opt_local.relativenumber = false
-    vim.opt_local.signcolumn = 'no'
-    vim.cmd.split('#0') -- split the previous window
-    vim.api.nvim_win_set_height(vim.fn.win_findbuf(bufnr)[1], 20)
-    vim.cmd.startinsert()
+-- autocmd({ 'FileType' }, {
+--   pattern = 'dirbuf',
+--   callback = function()
+--     if not vim.startswith(vim.api.nvim_buf_get_name(0), '/home/robert/zettelkasten') then return end
+--     vim.notify('hi')
+--     for i, l in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, true)) do
+--       vim.notify(vim.inspect(i, l))
+--     end
+--   end,
+--   group = augroup('DetectZkDirbuf', {}),
+-- })
+
+-- autocmd({ 'TermOpen' }, {
+--   pattern = 'term://*',
+--   callback = function()
+--     local bufnr = vim.api.nvim_get_current_buf()
+--     vim.opt_local.number = false
+--     vim.opt_local.relativenumber = false
+--     vim.opt_local.signcolumn = 'no'
+--     vim.cmd.split('#0') -- split the previous window
+--     vim.api.nvim_win_set_height(vim.fn.win_findbuf(bufnr)[1], 20)
+--     vim.cmd.startinsert()
+--   end,
+--   group = augroup('CustomizeTermBuffer', {}),
+-- })
+
+-- custom diagnostic settings for zk
+autocmd({ 'DiagnosticChanged' }, {
+  pattern = '*.md',
+  callback = function(args)
+    if not (args.data.diagnostics or args.data.diagnostics[1].source == 'zk') then
+      return
+    end
+    if vim.b.did_configure_diagnostics then
+      return
+    end
+    for key, ns in pairs(vim.api.nvim_get_namespaces()) do
+      if vim.startswith(key, 'vim.lsp.zk') then
+        vim.b.did_configure_diagnostics = true
+        vim.diagnostic.config({
+          signs = {
+            severity = vim.diagnostic.severity.INFO,
+          },
+          virtual_text = {
+            source = false,
+            prefix = '',
+          },
+        }, ns)
+      end
+    end
   end,
-  group = augroup('CustomizeTermBuffer', {}),
+  group = augroup('DiagnosticConfigZk', {}),
 })
--- }}}
 
--- USER COMMANDS -- {{{
-vim.api.nvim_create_user_command('Make', function(opts)
-  require('internal.job').make(opts.fargs)
-end, { nargs = '*', desc = 'run make target' })
-vim.api.nvim_create_user_command('Sh', function(opts)
-  require('internal.job').sh(table.concat(opts.fargs, ' '))
-end, { nargs = '*', desc = 'run shell command' })
-
-require('internal.misc').setup({
-  grep = true,
-  find = true,
-  sortline = true,
-})
+do
+  local group = augroup('VisualModeRelativeNumber', {})
+  autocmd({ 'ModeChanged' }, {
+    pattern = '*:[vV\x16]*',
+    callback = function()
+      vim.opt_local.relativenumber = true
+    end,
+    group = group,
+  })
+  autocmd({ 'ModeChanged' }, {
+    pattern = '[vV\x16]*:*',
+    callback = function()
+      -- vim.opt_local.relativenumber = (vim.api.nvim_get_mode().mode ~= '^[vV\x16]')
+      vim.opt_local.relativenumber = false
+    end,
+    group = group,
+  })
+end
 -- }}}
 
+require('internal.misc').setup()
+require('internal.find').setup()
+require('internal.sortline').setup()
+
 _G.statusline_enable_notifysend = false
 require('internal.statusline')
+
 require('internal.job').setup({})
 
 -- TODO: remove eventually
@@ -393,5 +431,12 @@ vim.filetype.add({
   },
   filename = {
     ['Caddyfile'] = 'conf',
+    ['TODO'] = 'todofile',
   },
+  pattern = {
+    ['${ZK_NOTEBOOK_DIR}/.*md$'] = 'zk.markdown',
+  }
 })
+
+-- use bash parsers for gentoo ebuilds
+vim.treesitter.language.register('bash', 'ebuild')
diff --git a/.vim/lazy-lock.json b/.vim/lazy-lock.json
new file mode 100644
index 0000000..ec2c5bf
--- /dev/null
+++ b/.vim/lazy-lock.json
@@ -0,0 +1,56 @@
+{
+  "Comment.nvim": { "branch": "master", "commit": "0236521ea582747b58869cb72f70ccfa967d2e89" },
+  "LuaSnip": { "branch": "master", "commit": "8f3d3465ba5c7ade0a8adb41eca5736f291a3fa8" },
+  "aerial.nvim": { "branch": "master", "commit": "83a79f39b709c20be4c830d241379fa85ef21a7c" },
+  "clangd_extensions.nvim": { "branch": "main", "commit": "2992ba8c13c2de41f91a7c7488bf1c48bcec31fe" },
+  "cmp-cmdline": { "branch": "main", "commit": "d250c63aa13ead745e3a40f61fdd3470efde3923" },
+  "cmp-nvim-lsp": { "branch": "main", "commit": "5af77f54de1b16c34b23cba810150689a3a90312" },
+  "cmp-nvim-lsp-document-symbol": { "branch": "main", "commit": "f0f53f704c08ea501f9d222b23491b0d354644b0" },
+  "cmp-nvim-lsp-signature-help": { "branch": "main", "commit": "3d8912ebeb56e5ae08ef0906e3a54de1c66b92f1" },
+  "cmp-nvim-lua": { "branch": "main", "commit": "f12408bdb54c39c23e67cab726264c10db33ada8" },
+  "cmp-path": { "branch": "main", "commit": "91ff86cd9c29299a64f968ebb45846c485725f23" },
+  "cmp-spell": { "branch": "master", "commit": "32a0867efa59b43edbb2db67b0871cfad90c9b66" },
+  "cmp_luasnip": { "branch": "master", "commit": "05a9ab28b53f71d1aece421ef32fee2cb857a843" },
+  "gentoo-syntax": { "branch": "master", "commit": "2bbb23d32d0546e78e7ecc3b310951b86c781780" },
+  "git-messenger.vim": { "branch": "master", "commit": "8a61bdfa351d4df9a9118ee1d3f45edbed617072" },
+  "gitlinker.nvim": { "branch": "master", "commit": "542f51784f20107ef9ecdadc47825204837efed5" },
+  "gitsigns.nvim": { "branch": "main", "commit": "035da036e68e509ed158414416c827d022d914bd" },
+  "grapple.nvim": { "branch": "main", "commit": "493f174a1ace3f2d55ba2191129e43b3875b9124" },
+  "guess-indent.nvim": { "branch": "main", "commit": "b8ae749fce17aa4c267eec80a6984130b94f80b2" },
+  "indent-blankline.nvim": { "branch": "master", "commit": "3d08501caef2329aba5121b753e903904088f7e6" },
+  "lazy.nvim": { "branch": "main", "commit": "3f13f080434ac942b150679223d54f5ca91e0d52" },
+  "lsp-status.nvim": { "branch": "master", "commit": "54f48eb5017632d81d0fd40112065f1d062d0629" },
+  "markdown-table-mode.nvim": { "branch": "main", "commit": "e10bf5a604cf650e174b78d057294a7430466b88" },
+  "mini.align": { "branch": "main", "commit": "b820379e333da87b4ee1eca41792aecea998eee3" },
+  "nvim-autopairs": { "branch": "master", "commit": "4f41e5940bc0443fdbe5f995e2a596847215cd2a" },
+  "nvim-cmp": { "branch": "main", "commit": "8f3c541407e691af6163e2447f3af1bd6e17f9a3" },
+  "nvim-code-action-menu": { "branch": "main", "commit": "8c7672a4b04d3cc4edd2c484d05b660a9cb34a1b" },
+  "nvim-colorizer.lua": { "branch": "master", "commit": "36c610a9717cc9ec426a07c8e6bf3b3abcb139d6" },
+  "nvim-dap": { "branch": "master", "commit": "6ae8a14828b0f3bff1721a35a1dfd604b6a933bb" },
+  "nvim-dap-ui": { "branch": "master", "commit": "5934302d63d1ede12c0b22b6f23518bb183fc972" },
+  "nvim-lspconfig": { "branch": "master", "commit": "ae0651d850f8f9313d4db3f96fe24dbf054edeb4" },
+  "nvim-navic": { "branch": "master", "commit": "8649f694d3e76ee10c19255dece6411c29206a54" },
+  "nvim-treesitter": { "branch": "master", "commit": "160e5d52c841dc9261c0b2dc6f253bddbcf3d766" },
+  "nvim-treesitter-refactor": { "branch": "master", "commit": "65ad2eca822dfaec2a3603119ec3cc8826a7859e" },
+  "nvim-treesitter-textobjects": { "branch": "master", "commit": "23b820146956b3b681c19e10d3a8bc0cbd9a1d4c" },
+  "orgmode.nvim": { "branch": "master", "commit": "389e91f6f935aa845bc0cd13dd80f75431c34751" },
+  "playground": { "branch": "master", "commit": "ba48c6a62a280eefb7c85725b0915e021a1a0749" },
+  "plenary.nvim": { "branch": "master", "commit": "08e301982b9a057110ede7a735dd1b5285eb341f" },
+  "popup.nvim": { "branch": "master", "commit": "b7404d35d5d3548a82149238289fa71f7f6de4ac" },
+  "spinner.nvim": { "branch": "master", "commit": "886ce6bed656aed95126d88ea388284aab3c4994" },
+  "telescope-dap.nvim": { "branch": "master", "commit": "8c88d9716c91eaef1cdea13cb9390d8ef447dbfe" },
+  "telescope-fzf-native.nvim": { "branch": "main", "commit": "9ef21b2e6bb6ebeaf349a0781745549bbb870d27" },
+  "telescope-lsp-handlers.nvim": { "branch": "trunk", "commit": "de02085d6af1633942549a238bc7a5524fa9b201" },
+  "telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
+  "telescope.nvim": { "branch": "master", "commit": "35f94f0ef32d70e3664a703cefbe71bd1456d899" },
+  "todo-comments.nvim": { "branch": "main", "commit": "a7e39ae9e74f2c8c6dc4eea6d40c3971ae84752d" },
+  "twilight.nvim": { "branch": "main", "commit": "8b7b50c0cb2dc781b2f4262a5ddd57571556d1e4" },
+  "vim-indent-object": { "branch": "master", "commit": "8ab36d5ec2a3a60468437a95e142ce994df598c6" },
+  "vim-lastplace": { "branch": "master", "commit": "cf0abc5f89c88f0c5219abe89334a8a3ef91fefd" },
+  "vim-markdown": { "branch": "master", "commit": "a657e697376909c41475a686eeef7fc7a4972d94" },
+  "vim-repeat": { "branch": "master", "commit": "24afe922e6a05891756ecf331f39a1f6743d3d5a" },
+  "vim-surround": { "branch": "master", "commit": "3d188ed2113431cf8dac77be61b842acb64433d9" },
+  "which-key.nvim": { "branch": "main", "commit": "4433e5ec9a507e5097571ed55c02ea9658fb268a" },
+  "zen-mode.nvim": { "branch": "main", "commit": "78557d972b4bfbb7488e17b5703d25164ae64e6a" },
+  "zk-nvim": { "branch": "main", "commit": "e2b6d62b18a88249016bf917d4e5bb0e417ac974" }
+}
\ No newline at end of file
diff --git a/.vim/lua/internal/align.lua b/.vim/lua/internal/align.lua
deleted file mode 100644
index 1ada01d..0000000
--- a/.vim/lua/internal/align.lua
+++ /dev/null
@@ -1,124 +0,0 @@
--- based on the ideas from:
--- https://github.com/RRethy/nvim-align
-
-local M = {}
-
-function M.align_lines(pat, line1, line2, preview_ns, preview_bufnr)
-  local re = vim.regex(pat)
-  local bufnr = vim.api.nvim_get_current_buf()
-  local lines = vim.api.nvim_buf_get_lines(bufnr, line1 - 1, line2, false)
-  local newlines = {}
-  local preview_buf_line = 0
-
-  -- find the longest match
-  local max = -1
-  for _, line in pairs(lines) do
-    local s = re:match_str(line)
-    if s and max < s then
-      max = s
-    end
-  end
-
-  -- exit if nothing was found
-  if max == -1 then
-    return error('nothing found')
-  end
-
-  for i, line in pairs(lines) do
-    local s = re:match_str(line)
-    if s then
-      local rep = max - s
-      local changeset = {
-        string.sub(line, 1, s),
-        string.rep(' ', rep),
-        string.sub(line, s + 1),
-      }
-      local newline = table.concat(changeset)
-
-      -- append to changse if not inside the preview callback
-      if not preview_ns then
-        newlines[#newlines + 1] = newline
-      end
-
-      if preview_ns ~= nil then
-        -- set extmarks inside the live buffer
-        vim.api.nvim_buf_set_extmark(bufnr, preview_ns, line1 + i - 2, 0, {
-          hl_mode = 'combine',
-          virt_text_pos = 'overlay',
-          virt_text = {
-            { changeset[1] },
-            { changeset[2], 'Substitute' },
-            { changeset[3] },
-          },
-        })
-
-        -- modify preview buffer
-        if preview_bufnr ~= nil then
-          local prefix = string.format('|%d| ', line1 + i - 1)
-          vim.api.nvim_buf_set_lines(
-            preview_bufnr,
-            preview_buf_line,
-            preview_buf_line,
-            false,
-            { prefix .. newline }
-          )
-          vim.api.nvim_buf_add_highlight(
-            preview_bufnr,
-            preview_ns,
-            'Substitute',
-            preview_buf_line,
-            #prefix + s,
-            #prefix + s + #changeset[2]
-          )
-          preview_buf_line = preview_buf_line + 1
-        end
-      end
-    end
-  end
-
-  -- only change buffer when not previewing
-  if not preview_ns then
-    -- exit if nothing was changed
-    if #newlines == 0 then
-      return error('nothing changed')
-    end
-    vim.api.nvim_buf_set_lines(bufnr, line1 - 1, line2, 0, newlines)
-    return 0
-  end
-
-  if preview_ns ~= nil then
-    -- open preview buffer only if there is more than a single line of change
-    return (#preview_buf_line > 1) and 2 or 1
-  end
-end
-
-function M.align(pat)
-  local top, bot = vim.fn.getpos("'<"), vim.fn.getpos("'>")
-  M.align_lines(pat, top[2] - 1, bot[2])
-  vim.fn.setpos("'<", top)
-  vim.fn.setpos("'>", bot)
-end
-
-local function aligncmd(opts, preview_ns, preview_bufnr)
-  return M.align_lines(opts.fargs[1], opts.line1, opts.line2, preview_ns, preview_bufnr)
-end
-
-local default_opts = {
-  bindings = true,
-}
-
-function M.setup(opts)
-  opts = vim.tbl_extend('keep', opts or {}, default_opts)
-
-  vim.api.nvim_create_user_command(
-    'SimpleAlign',
-    aligncmd,
-    { nargs = 1, range = '%', preview = aligncmd }
-  )
-
-  -- if opts.bindings then
-  --   vim.keymap.set('v', '<enter>', ':SimpleAlign ')
-  -- end
-end
-
-return M
diff --git a/.vim/lua/internal/colors.lua b/.vim/lua/internal/colors.lua
new file mode 100644
index 0000000..a03501f
--- /dev/null
+++ b/.vim/lua/internal/colors.lua
@@ -0,0 +1,18 @@
+return {
+  base00 = '#080808',
+  base01 = '#db4d6d',
+  base02 = '#5dac81',
+  base03 = '#fad689',
+  base04 = '#58b2dc',
+  base05 = '#70649a',
+  base06 = '#69b0ac',
+  base07 = '#bdc0ba',
+  base08 = '#4f4f48',
+  base09 = '#cb1b45',
+  base0A = '#86c166',
+  base0B = '#f7d94c',
+  base0C = '#2ea9df',
+  base0D = '#8a6bbe',
+  base0E = '#81c7d4',
+  base0F = '#fffffb',
+}
\ No newline at end of file
diff --git a/.vim/lua/internal/commit-preview.lua b/.vim/lua/internal/commit-preview.lua
new file mode 100644
index 0000000..512b3fb
--- /dev/null
+++ b/.vim/lua/internal/commit-preview.lua
@@ -0,0 +1,67 @@
+local M = {}
+
+function M.under_cursor()
+  require('internal.cursor').word(require('internal.commit-preview').commit_preview)
+end
+
+function M.commit_preview(commit, on_attach)
+  if not commit then return end
+
+  local result = require('internal.job')
+      .jobstart({
+        command = 'git',
+        args = { 'show', '--stat', '--patch', commit },
+        format_title = function(_, _)
+          return 'git-show'
+        end,
+        populate_quickfix = false,
+      })
+      :wait()
+      :result()
+
+  -- create buffer
+  local bufnr = vim.api.nvim_create_buf(false, true)
+  assert(bufnr, 'Failed to create buffer')
+  vim.api.nvim_buf_set_name(bufnr, commit)
+  vim.api.nvim_set_option_value('filetype', 'commitpreview', { buf = bufnr })
+
+  -- add lines
+  vim.api.nvim_set_option_value('modifiable', true, { buf = bufnr })
+  vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, result)
+  vim.api.nvim_set_option_value('modifiable', false, { buf = bufnr })
+
+  -- create window
+  local winpos = 'belowright'
+  local winheight = #result <= 25 and #result or 25
+  vim.cmd(string.format('%s %dsplit', winpos, winheight)) -- open split
+  local winid = vim.api.nvim_get_current_win()
+  vim.api.nvim_win_set_buf(winid, bufnr)
+  vim.cmd('wincmd p')
+
+  vim.keymap.set('n', 'gd', M.under_cursor, { buffer = bufnr })
+  vim.keymap.set('n', 'q', function() vim.api.nvim_win_close(winid, true) end, { buffer = bufnr })
+
+  if on_attach then on_attach(bufnr, winid) end
+  -- close on CursorMoved (anywhere but git-show-buf)
+  -- local old_cursor = vim.api.nvim_win_get_cursor(beforewin)
+  -- local group = vim.api.nvim_create_augroup('gitcommit_win_' .. winid, {})
+  -- vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
+  --   group = group,
+  --   callback = function()
+  --     local cursor = vim.api.nvim_win_get_cursor(0)
+  --     if
+  --         (old_cursor[1] ~= cursor[1] or old_cursor[2] ~= cursor[2])
+  --         and vim.api.nvim_get_current_win() ~= winid
+  --     then
+  --       close()
+  --       return
+  --     end
+  --     old_cursor = cursor
+  --   end,
+  -- })
+
+  -- focus
+  vim.api.nvim_set_current_win(winid)
+end
+
+return M
diff --git a/.vim/lua/internal/cursor.lua b/.vim/lua/internal/cursor.lua
new file mode 100644
index 0000000..ad8d8b2
--- /dev/null
+++ b/.vim/lua/internal/cursor.lua
@@ -0,0 +1,11 @@
+local M = {}
+
+function impl(obj, cb) return cb(vim.fn.expand(vim.fn.expand(obj))) end
+
+function M.word(cb) return impl('<cword>', cb) end
+
+function M.expr(cb) return impl('<cexpr>', cb) end
+
+function M.file(cb) return impl('<cfile>', cb) end
+
+return M
diff --git a/.vim/lua/internal/dap.lua b/.vim/lua/internal/dap.lua
index 1790575..4b1d776 100644
--- a/.vim/lua/internal/dap.lua
+++ b/.vim/lua/internal/dap.lua
@@ -2,13 +2,16 @@
 
 local dap = require('dap')
 dap.defaults.fallback.terminal_win_cmd = 'belowright 10new'
-dap.defaults.fallback.terminal_win_cmd = 'belowright 10new'
+dap.defaults.fallback.focus_terminal = false
 
 -- open repl when session starts
 dap.listeners.after['event_initialized']['me'] = function()
-  dap.repl.toggle()
+  -- dap.repl.toggle()
 end
 
+-- try loading dap-launch.json
+-- require('dap.ext.vscode').load_launchjs(vim.fn.getcwd() .. '/dap-launch.json')
+
 -- signs
 vim.fn.sign_define({
   { name = 'DapBreakpoint', text = '🞱', texthl = 'DapBreakpoint', linehl = 'DapBreakpointLn' },
@@ -21,12 +24,12 @@ vim.fn.sign_define({
 vim.keymap.set('n', '<leader>dd', dap.toggle_breakpoint, { desc = 'toggle breakpoint' }) -- convenience
 vim.keymap.set('n', '<leader>db', dap.toggle_breakpoint, { desc = 'toggle breakpoint' })
 vim.keymap.set('n', '<leader>dc', dap.continue, { desc = 'continue' })
-vim.keymap.set('n', '<leader>do', dap.step_over, { desc = 'step over' })
+vim.keymap.set('n', '<leader>ds', dap.step_over, { desc = 'step over' })
 vim.keymap.set('n', '<leader>di', dap.step_into, { desc = 'step into' })
-vim.keymap.set('n', '<leader>dO', dap.step_out, { desc = 'step out' })
+vim.keymap.set('n', '<leader>do', dap.step_out, { desc = 'step out' })
 vim.keymap.set('n', '<leader>dR', dap.repl.toggle, { desc = 'toggle REPL' })
 vim.keymap.set('n', '<leader>dL', dap.run_last, { desc = 'run last' })
-vim.keymap.set('n', '<leader>dt', dap.terminate, { desc = 'terminate' })
+vim.keymap.set('n', '<leader>dq', dap.terminate, { desc = 'terminate' })
 
 local widgets = require('dap.ui.widgets')
 vim.keymap.set('n', '<leader>dh', widgets.hover, { desc = 'hover' })
@@ -39,15 +42,23 @@ vim.keymap.set(
 
 -- Common
 local bin_from_var_or_pick = function()
-  local ok, retval = pcall(vim.api.nvim_buf_get_var, 0, 'dap_bin')
+  local ok, retval = pcall(vim.api.nvim_get_var, 'dap_bin') -- global let g:dap_bin
   if ok then
+    local workspace_folders = vim.lsp.buf.list_workspace_folders()
+    if #workspace_folders >= 1 then
+      retval = retval:gsub(':workspace', workspace_folders[1])
+    end
     return retval
   end
+  -- NOTE: try to resolve binaries from build system
   local bin = nil
-  vim.ui.input({ prompt = 'DAP Binary: ' }, function(choice)
+  vim.ui.input({ prompt = 'DAP: binary to launch: ' }, function(choice)
     bin = choice
   end)
-  return bin
+  if not bin == nil then
+    return bin
+  end
+  return false
 end
 
 -- Go {{{
@@ -106,59 +117,12 @@ dap.configurations.go = {
 -- end of Go }}}
 
 -- C/C++/Rust {{{
--- dap.adapters.cppdbg = {
---   id = 'cppdbg',
---   type = 'executable',
---   command = vim.env.HOME .. '/.local/share/nvim/dap/ms-vscode.cpptools/extension/debugAdapters/bin/OpenDebugAD7',
--- }
 dap.adapters.lldb = {
   type = 'executable',
-  command = vim.trim(vim.fn.system('command -v lldb-vscode')),
+  command = '/usr/bin/lldb-vscode',
   name = 'lldb',
 }
 
--- dap.configurations.c = {
---   {
---     name = 'Launch binary',
---     type = 'cppdbg',
---     request = 'launch',
---     program = function()
---       local file = vim.g.dapbin
---       if file then return file end
---       -- TODO: assuming meson
---       file = vim.trim(vim.fn.system(
---         [[jq -r '..|select(.type?=="executable")|.filename|.[0]' ]] ..
---         vim.lsp.buf.list_workspace_folders()[1] ..
---         [[/build/meson-info/intro-targets.json]]
---       ))
---       local fd = vim.loop.fs_open(file, "r", 438)
---       if file and fd then
---         vim.loop.fs_close(fd)
---         return file
---       end
---       vim.ui.input('binary: ', function(result) file = result end)
---       return file
---     end,
---     cwd = '${workspaceFolder}',
---     stopOnEntry = true,
---   },
---   {
---     name = 'Attach to gdbserver',
---     type = 'cppdbg',
---     request = 'launch',
---     MIMode = 'gdb',
---     miDebuggerServerAddress = 'localhost:1234',
---     miDebuggerPath = '/usr/bin/gdb',
---     cwd = '${workspaceFolder}',
---     program = function()
---       local file = vim.g.dapbin
---       if file then return file end
---       vim.ui.input('binary: ', function(result) file = result end)
---       return file
---     end,
---   },
--- }
-
 dap.configurations.c = {
   {
     name = 'Launch',
@@ -178,6 +142,32 @@ dap.configurations.c = {
   },
 }
 dap.configurations.cpp = dap.configurations.c
-dap.configurations.rust = dap.configurations.c
 
+-- enable rust types
+dap.configurations.rust = vim.tbl_extend('force', dap.configurations.c, {
+  initCommands = function()
+    local rustc_sysroot = vim.fn.trim(vim.fn.system('rustc --print sysroot'))
+
+    local script_import = 'command script import "'
+        .. rustc_sysroot
+        .. '/lib/rustlib/etc/lldb_lookup.py"'
+    local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'
+
+    local commands = {}
+    local file = io.open(commands_file, 'r')
+    if file then
+      for line in file:lines() do
+        table.insert(commands, line)
+      end
+      file:close()
+    end
+    table.insert(commands, 1, script_import)
+
+    return commands
+  end,
+})
 -- end of C/C++/Rust }}}
+
+local dapui = require('dapui')
+dapui.setup {}
+vim.keymap.set('n', '<leader>du', dapui.toggle, { desc = 'toggle ui' })
diff --git a/.vim/lua/internal/find.lua b/.vim/lua/internal/find.lua
new file mode 100644
index 0000000..32bdea5
--- /dev/null
+++ b/.vim/lua/internal/find.lua
@@ -0,0 +1,106 @@
+local M = {}
+
+local get_searchdirs = function(dirs)
+  dirs = dirs or {}
+  if type(dirs) == 'table' and #dirs > 0 then
+    return dirs
+  elseif type(dirs) == 'string' and (dirs == ':workspace') then
+    dirs = vim.lsp.buf.list_workspace_folders()
+  elseif type(dirs) == 'string' and not (dirs == '') then
+    dirs = vim.split(dirs, ',')
+  end
+  -- skip asking if we're looking at known filetypes
+  local ft = vim.api.nvim_buf_get_option(0, 'filetype')
+  if #dirs == 0 and (ft == 'dirbuf' or ft == 'NvimTree' or ft == 'alpha') then
+    dirs = { '%:p:h' }
+  end
+  -- ask user
+  if #dirs == 0 then
+    vim.ui.input({ prompt = 'Search directories: ', default = '%:p:h' }, function(result)
+      dirs = { result }
+    end)
+  end
+  -- default to something reasonable
+  if #dirs == 0 then
+    dirs = { '%:p:h' }
+  end
+  -- finally return
+  return dirs
+end
+
+function onchoice(opts, cb)
+  if not cb then
+    return
+  end
+  opts.attach_mappings = function(prompt_bufnr)
+    require('telescope.actions').select_default:replace(function()
+      local selection = require('telescope.actions.state').get_selected_entry()
+      if selection == nil then
+        return
+      end
+      require('telescope.actions').close(prompt_bufnr)
+      on_choice(selection.path)
+    end)
+    return true
+  end
+end
+
+function search_shim(fn, search_dirs, opts, cb)
+  opts = opts or {}
+  opts.search_dirs = get_searchdirs(search_dirs)
+  opts.prompt_title = vim.fn.expand(table.concat(opts.search_dirs, ','))
+  onchoice(opts, cb)
+  fn(opts)
+end
+
+function M.live_grep(search_dirs, opts, cb)
+  search_shim(require('telescope.builtin').live_grep, search_dirs, opts, cb)
+end
+
+function M.find_files(search_dirs, opts, cb)
+  -- convenience aliases
+  if type(search_dirs) == 'string' and search_dirs == 'data' then
+    search_dirs = vim.fn.stdpath('data')
+  end
+  search_shim(require('telescope.builtin').find_files, search_dirs, opts, cb)
+end
+
+function M.setup(opts)
+  vim.keymap.set('n', ';f', function()
+    vim.notify('use gsf', vim.log.levels.ERROR)
+  end, { desc = 'find files' })
+  vim.keymap.set('n', ';g', function()
+    vim.notify('use gsg', vim.log.levels.ERROR)
+  end, { desc = 'live grep' })
+
+  vim.keymap.set('n', 'gsg', function()
+    M.live_grep(':workspace')
+  end, { desc = 'live grep in workspace' })
+  vim.keymap.set('n', 'gsG', M.live_grep, { desc = 'live grep ask' })
+  vim.keymap.set('n', 'gs-', function()
+    M.live_grep('%:p:h')
+  end, { desc = 'live grep in parent' })
+
+  vim.keymap.set('n', 'gsf', function()
+    M.find_files(':workspace')
+  end, { desc = 'find files in workspace' })
+  vim.keymap.set('n', 'gsF', M.find_files, { desc = 'find files ask' })
+
+  vim.api.nvim_create_user_command('Grep', function(o)
+    M.live_grep(table.concat(o.fargs))
+  end, {
+    desc = 'live grep (rg)',
+    nargs = '*',
+    complete = 'dir',
+  })
+
+  vim.api.nvim_create_user_command('Find', function(o)
+    M.find_files(table.concat(o.fargs))
+  end, {
+    desc = 'find files (fd)',
+    nargs = '*',
+    complete = 'dir',
+  })
+end
+
+return M
diff --git a/.vim/lua/internal/job.lua b/.vim/lua/internal/job.lua
index 42a548b..2e97f16 100644
--- a/.vim/lua/internal/job.lua
+++ b/.vim/lua/internal/job.lua
@@ -28,11 +28,28 @@ end
 function M.jobstart(opts)
   opts = opts or {}
 
-  local spinner_ok, spinner = pcall(require, 'spinner.core')
+  opts = vim.tbl_extend('keep', opts or {}, {
+    enable_spinner = vim.g.job_enable_spinner,
+    populate_quickfix = vim.g.job_populate_quickfix,
+    attach = vim.g.job_attach,
+  })
+
+  -- vim.notify_once(vim.inspect(opts), vim.log.levels.DEBUG)
 
-  -- default options
-  opts.enable_spinner = (opts.enable_spinner or true) and spinner_ok
-  opts.populate_quickfix = opts.populate_quickfix or 'onerror'
+  if opts.attach then
+    return vim.schedule_wrap(function()
+      vim.cmd.terminal({ args = { opts.command, unpack(opts.args) } })
+    end)()
+  end
+
+  local spinner = {}
+  if opts.enable_spinner then
+    local ok = false
+    ok, spinner = pcall(require, 'spinner.core')
+    if not ok then
+      opts.enable_spinner = false
+    end
+  end
 
   if opts.format_title == nil then
     opts.format_title = table.concat({ opts.command, unpack(opts.args) }, ' ')
@@ -102,58 +119,63 @@ function M.jobstart(opts)
   return j
 end
 
-function M.make(extra_args)
-  local makeprg = vim.fn.expandcmd(vim.opt.makeprg:get())
-  -- split makeprg into command + args
-  local args = vim.split(makeprg, ' ')
-  local command = args[1]
-  table.remove(args, 1)
+function M.sh(command, opts)
+  return M.jobstart(vim.tbl_extend('keep', opts or {}, {
+    command = vim.env.SHELL,
+    args = { '-c', command },
+    -- strip 'sh -c' from the title (used by progress reporting etc.)
+    format_title = function(_, args)
+      return table.concat({ unpack(args, 2, #args) }, ' ')
+    end,
+  }))
+end
 
+function M.make(opts)
+  opts = opts or {}
+
+  local makeprg = vim.fn.expandcmd(vim.opt.makeprg:get())
   local cwd = vim.fn.expand('%:p:h')
 
   if not (string.match(makeprg, 'make') == nil) then
     -- detect makefile
-    cwd = require('lspconfig.util').root_pattern('Makefile')(cwd)
-  end
-
-  for _, v in pairs(extra_args or {}) do
-    table.insert(args, v)
+    opts.cwd = require('lspconfig.util').root_pattern('Makefile')(cwd)
   end
-  -- table.foreach(extra_args or {}, function(_, v) end)
 
-  return M.jobstart({
-    command = command,
-    args = args,
-    cwd = cwd,
-  })
-end
-
-function M.sh(command_string)
-  return M.jobstart({
-    command = vim.env.SHELL,
-    args = { '-c', command_string },
-
-    -- strip 'sh -c' from the title (used by progress reporting etc.)
-    format_title = function(_, args)
-      return table.concat({ unpack(args, 2, #args) }, ' ')
-    end,
-  })
+  return M.sh(makeprg, opts)
 end
 
 function M.list()
   require('telescope._extensions').manager['jobs']['jobs']()
 end
 
-function M.setup()
-  require('telescope').load_extension('jobs')
+function M.setup(opts)
+  opts = vim.tbl_extend('keep', opts, { telescope = true, mappings = true })
+
+  -- defaults
+  vim.g.job_enable_spinner = true
+  vim.g.job_populate_quickfix = 'onerror'
+  vim.g.job_attach = false
+
+  if opts.telescope then
+    require('telescope').load_extension('jobs')
+    vim.api.nvim_create_user_command('Jobs', M.list, { desc = 'list jobs managed by internal.job' })
+    if opts.mappings then
+      vim.keymap.set('n', '<leader>jl', M.list, { desc = 'list jobs managed by internal.job' })
+    end
+  end
+
+  vim.api.nvim_create_user_command('Make', function(a)
+    require('internal.job').make(a.fargs)
+  end, { nargs = '*', desc = 'run make target or &makeprg with internal.job' })
 
-  vim.keymap.set(
-    'n',
-    '<leader>J',
-    require('internal.job').list,
-    { desc = 'list jobs managed by internal.job' }
-  )
-  vim.api.nvim_create_user_command('Jobs', M.list, { desc = 'list jobs managed by internal.job' })
+  vim.api.nvim_create_user_command('Sh', function(a)
+    require('internal.job').sh(table.concat(a.fargs, ' '))
+  end, { nargs = '*', desc = 'run shell command with internal.job' })
+
+  if opts.mappings then
+    vim.keymap.set('n', '<leader>jm', M.make, { desc = 'run &makeprg with internal.job' })
+    vim.keymap.set('n', '<leader>js', ':Sh ', { desc = 'run shell commands with internal.job' })
+  end
 end
 
 return M
diff --git a/.vim/lua/internal/lsp.lua b/.vim/lua/internal/lsp.lua
index 1c8c29c..b0aa7dc 100644
--- a/.vim/lua/internal/lsp.lua
+++ b/.vim/lua/internal/lsp.lua
@@ -8,7 +8,7 @@
 local my_attach = function(client, bufnr)
   if vim.opt.diff:get() then
     vim.notify('not running LSP client in diff mode', vim.log.levels.WARN)
-    vim.lsp.stop_client()
+    vim.lsp.stop_client(client)
     return
   end
 
@@ -79,14 +79,16 @@ local my_attach = function(client, bufnr)
     )
   end
   if client.supports_method('textDocument/formatting') then
-    vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr()')
+    -- if client.server_capabilities.documentFormattingProvider then
+    vim.api.nvim_set_option_value('formatexpr', 'v:lua.vim.lsp.formatexpr()', { buf = bufnr })
     vim.keymap.set('n', '<leader>f', vim.lsp.buf.format, { buffer = bufnr, desc = 'format' })
     vim.api.nvim_create_autocmd('BufWritePre', {
       buffer = bufnr,
-      group = group,
       callback = function()
+        vim.cmd.mkview({ bang = true })
         require('internal.lsp').format(vim.fn.expand('<afile>:p'))
       end,
+      group = vim.api.nvim_create_augroup('LspAutoFormat', {}),
     })
   end
   if client.supports_method('textDocument/typeDefinition') then
@@ -114,7 +116,8 @@ local my_attach = function(client, bufnr)
     )
   end
   if client.supports_method('textDocument/codeAction') then
-    vim.keymap.set('n', 'ga', vim.lsp.buf.code_action, { buffer = bufnr, desc = 'code action' })
+    -- vim.keymap.set('n', 'ga', vim.lsp.buf.code_action, { buffer = bufnr, desc = 'code action' })
+    vim.keymap.set('n', 'ga', vim.cmd.CodeActionMenu, { buffer = bufnr, desc = 'code action' })
   end
   if client.supports_method('textDocument/documentHighlight') then
     vim.keymap.set(
@@ -135,22 +138,25 @@ local my_exit = function(_, _, _) end
 
 local capabilities = function()
   local caps = vim.lsp.protocol.make_client_capabilities()
+
   -- enable lsp-based snippets
-  caps.textDocument.completion.completionItem.snippetSupport = true
-  caps.textDocument.completion.completionItem.resolveSupport = {
-    properties = {
-      'documentation',
-      'detail',
-      'additionalTextEdits',
-    },
-  }
+  caps = vim.tbl_extend('keep', caps or {}, require('cmp_nvim_lsp').default_capabilities())
+
   -- add window/workDoneProgress capability
   caps = vim.tbl_extend('keep', caps or {}, require('lsp-status').capabilities)
   return caps
 end
 
 local servers = {
-  clangd = {},
+  clangd = {
+    cmd = {
+      'clangd',
+      '--clang-tidy',
+      '--header-insertion=iwyu',
+      '--import-insertions',
+      '--header-insertion-decorators',
+    }
+  },
   elixirls = {
     cmd = { 'elixir-ls' },
     settings = {
@@ -177,7 +183,7 @@ local servers = {
   },
   lua_ls = {
     -- cmd = { 'lua-language-server' },
-    cmd = { 'luals' },
+    cmd = { 'luals' }, -- custom firejail-wrapped
     settings = {
       Lua = {
         runtime = {
@@ -237,15 +243,9 @@ local servers = {
       },
     },
   },
+  tsserver = {},
   pylsp = {},
-  -- tsserver = { disabled = true,
-  --   cmd = {
-  --     'toolbox', 'run', '--',
-  --     'sh', '-c',
-  --     '. /etc/profile.d/nvm.sh && typescript-language-server --stdio'
-  --   },
-  -- },
-  -- zls = { disabled = true },
+  zls = {},
 }
 
 local setup = function()
@@ -256,7 +256,7 @@ local setup = function()
     border = _G.floating_win_border,
   })
   vim.lsp.handlers['textDocument/signature_help'] =
-    vim.lsp.with(vim.lsp.handlers.signature_help, { border = _G.floating_win_border })
+      vim.lsp.with(vim.lsp.handlers.signature_help, { border = _G.floating_win_border })
 
   local caps = capabilities()
   for server, opts in pairs(servers) do
@@ -283,16 +283,18 @@ local format = function(afile)
     return
   end
 
-  local ok, isMatch = pcall(string.match, afile, '^/home/robert/devel/upstream')
-  if not ok or isMatch then
-    return
-  end
-
   local errhandler = function(err)
-    vim.notify('fmt failed: ' .. err, vim.log.levels.ERROR)
+    vim.notify('internal.lsp.format: ' .. err, vim.log.levels.ERROR)
     return err
   end
+
+  -- save folds
+  pcall(vim.cmd.mkview, { bang = true })
+
+  -- run lsp format
   xpcall(vim.lsp.buf.format, errhandler)
+
+  -- run organize imports for go
   if string.match(afile, '.go$') then
     xpcall(
       vim.lsp.buf.code_action,
@@ -300,8 +302,12 @@ local format = function(afile)
       { context = { only = { 'source.organizeImports' } }, apply = true }
     )
   end
+
+  -- restore folds
+  pcall(vim.cmd.silent, { 'loadview', bank = true })
+
   -- vim.notify('%#MoreMsg#󰃢%#Italic# fmt', vim.log.levels.INFO)
-  vim.notify('󰃢 ', vim.log.levels.INFO)
+  vim.notify('internal.lsp.format: 󰃢', vim.log.levels.INFO)
 end
 
 return {
diff --git a/.vim/lua/internal/lsp_symbols.lua b/.vim/lua/internal/lsp_symbols.lua
index ce8b1a7..97a1b0e 100644
--- a/.vim/lua/internal/lsp_symbols.lua
+++ b/.vim/lua/internal/lsp_symbols.lua
@@ -4,16 +4,16 @@ return {
   Boolean = '󰦍 ',
   Class = '󰙀 ',
   Color = '󰉦 ',
-  Constant = ' ',
+  Constant = '󰐀 ',
   Constructor = '󱇿 ',
-  Enum = ' ',
-  EnumMember = ' ',
+  Enum = '󱃢 ',
+  EnumMember = '󱃡 ',
   Event = '󱐋 ',
   -- Field = '󰽜 ',
   Field = '󰆈 ',
   File = '󰈔 ',
   Folder = '󰝰 ',
-  Function = ' ',
+  Function = '󰊕 ',
   Interface = '󱦜 ',
   Key = '󰌋 ',
   Keyword = '󰓽 ',
diff --git a/.vim/lua/internal/misc.lua b/.vim/lua/internal/misc.lua
index 8664cbc..a92b7aa 100644
--- a/.vim/lua/internal/misc.lua
+++ b/.vim/lua/internal/misc.lua
@@ -2,53 +2,6 @@
 
 local M = {}
 
-function M.do_under_cursor(obj, cb)
-  return cb(vim.fn.expand(vim.fn.expand(obj)))
-end
-
-function M.word_under_cursor(cb)
-  return M.do_under_cursor('<cword>', cb)
-end
-
-function M.expr_under_cursor(cb)
-  return M.do_under_cursor('<cexpr>', cb)
-end
-
-function M.file_under_cursor(cb)
-  return M.do_under_cursor('<cfile>', cb)
-end
-
-function M.open_under_cursor(cmd, detect_cmd)
-  return M.file_under_cursor(function(txt)
-    txt = vim.trim(txt)
-    if not cmd and detect_cmd then
-      if vim.regex([[^http.*$]]):match_str(txt) then
-        cmd = 'url-launcher'
-      end
-      if vim.regex([[^(\.\./|[\w\d_/\-])*(\.[\w\d]+)?$]]):match_str(txt) then
-        cmd = ':edit'
-      end
-      vim.notify('open_under_cursor: detected command ' .. txt .. ' => ' .. (cmd or '<nil>'))
-    end
-    if not cmd then
-      vim.fn.inputsave()
-      vim.ui.input('open with: ', function(result)
-        cmd = result
-      end)
-      vim.fn.inputrestore()
-      if not cmd then
-        return
-      end
-    end
-    -- detect vim command
-    if cmd:sub(1, 1) == ':' then
-      vim.cmd(cmd:sub(2) .. ' ' .. txt)
-    else
-      vim.loop.spawn(cmd, { args = { txt } })
-    end
-  end)
-end
-
 local function get_visual_selection()
   local top = vim.fn.getpos("'<")
   top = { ln = top[2], col = top[3] }
@@ -132,7 +85,7 @@ local get_searchdirs = function(dirs)
   return dirs
 end
 
-function onchoice(opts, cb)
+local function onchoice(opts, cb)
   if not cb then
     return
   end
@@ -143,7 +96,7 @@ function onchoice(opts, cb)
         return
       end
       require('telescope.actions').close(prompt_bufnr)
-      on_choice(selection.path)
+      cb(selection.path)
     end)
     return true
   end
@@ -209,35 +162,37 @@ function M.link_preview()
   local ln = vim.api.nvim_win_get_cursor(0)[1] - 1
 
   require('job')
-    .jobstart({
-      format_title = function()
-        return 'link-preview'
-      end,
-      command = vim.env.SHELL,
-      args = {
-        '-c',
-        [[ curl -sSfL ]]
+      .jobstart({
+        format_title = function()
+          return 'link-preview'
+        end,
+        command = vim.env.SHELL,
+        args = {
+          '-c',
+          [[ curl -sSfL ]]
           .. link
           .. [[ | grep -iPo '(?<=property="og:title" content=")([^\"]+)(?=")' ]],
-      },
-      populate_quickfix = false,
-      enable_recording = true,
-    })
-    :after_success(vim.schedule_wrap(function(j, code, _)
-      local results = j:result()
-      if not code == 0 or #results == 0 then
-        return
-      end
-
-      local ns = vim.api.nvim_create_namespace('')
-      vim.api.nvim_buf_set_extmark(buf, ns, ln, 0, {
-        virt_text = { { results[1], 'Error' } },
-        virt_text_pos = 'eol',
+        },
+        populate_quickfix = false,
+        enable_recording = true,
       })
-    end))
+      :after_success(vim.schedule_wrap(function(j, code, _)
+        local results = j:result()
+        if not code == 0 or #results == 0 then
+          return
+        end
+
+        local ns = vim.api.nvim_create_namespace('')
+        vim.api.nvim_buf_set_extmark(buf, ns, ln, 0, {
+          virt_text = { { results[1], 'Error' } },
+          virt_text_pos = 'eol',
+        })
+      end))
 end
 
 function M.setup(opts)
+  opts = opts or {}
+
   if opts.sortline then
     vim.api.nvim_create_user_command(
       'SortLine',
diff --git a/.vim/lua/internal/snips.lua b/.vim/lua/internal/snips.lua
index 91bce13..bb9fdef 100644
--- a/.vim/lua/internal/snips.lua
+++ b/.vim/lua/internal/snips.lua
@@ -48,7 +48,7 @@ local comment = function(wrapped, blockcomment)
     })
     local tbl = vim.split(cs or '', '%s', { plain = true, trimempty = true })
     return (#tbl == 0) and { '', '' }
-      or ((#tbl == 1) and { tbl[1] .. ' ', '' } or { tbl[1], tbl[2] })
+        or ((#tbl == 1) and { tbl[1] .. ' ', '' } or { tbl[1], tbl[2] })
   end
 
   if type(wrapped) ~= 'table' then
@@ -78,12 +78,12 @@ end
 local exec = function(command)
   return ls.f(function(_, _, ...)
     local results, code = require('plenary.job')
-      :new({
-        command = vim.env.SHELL,
-        args = { '-c', ... },
-        enable_recording = true,
-      })
-      :sync()
+        :new({
+          command = vim.env.SHELL,
+          args = { '-c', ... },
+          enable_recording = true,
+        })
+        :sync()
     if not code == 0 or #results == 0 then
       error(string.format('exec "%s" failed', ...))
     end
@@ -102,6 +102,10 @@ local file_contents = function(path, vargs)
 end
 
 ls.setup({
+  history = true,
+  update_events = 'TextChanged,TextChangedI',
+  delete_check_events = 'TextChanged',
+
   ext_opts = {
     [types.snippet] = {
       active = {
@@ -118,13 +122,18 @@ ls.setup({
       },
     },
   },
-
   snip_env = vim.tbl_extend('keep', ls.session.config.snip_env, {
+    f = ls.f,
+    i = ls.i,
+    s = ls.s,
+    t = ls.t,
+    c = ls.c,
+    sn = ls.sn,
+    fmt = require('luasnip.extras.fmt').fmt,
     autowrap = autowrap,
     exec = exec,
     comment = comment,
     file_contents = file_contents,
-
     user_email = {
       exec([[git config --get user.name]]),
       ls.t(' <'),
@@ -139,7 +148,7 @@ ls.setup({
 })
 
 -- snipmate snippets
-require('luasnip.loaders.from_snipmate').lazy_load({ paths = './after/snippets' })
+-- require('luasnip.loaders.from_snipmate').lazy_load({ paths = './after/snippets' })
 -- lua snippets
 require('luasnip.loaders.from_lua').lazy_load({ paths = './snippets' })
 
diff --git a/.vim/lua/internal/sortline.lua b/.vim/lua/internal/sortline.lua
new file mode 100644
index 0000000..69f7f64
--- /dev/null
+++ b/.vim/lua/internal/sortline.lua
@@ -0,0 +1,78 @@
+local M = {}
+
+local function get_visual_selection(intact)
+  intact = intact or false
+  local top = vim.fn.getpos("'<")
+  top = { ln = top[2], col = top[3] }
+  local bot = vim.fn.getpos("'>")
+  bot = { ln = bot[2], col = bot[3] }
+  local lines = vim.api.nvim_buf_get_lines(0, (top.ln - 1), bot.ln, false)
+  if not intact then
+    if vim.opt.selection:get() == 'inclusive' then
+      lines[#lines] = lines[#lines]:sub(1, bot.col)
+    else
+      lines[#lines] = lines[#lines]:sub(1, (bot.col - 1))
+    end
+    lines[1] = lines[1]:sub(top.col)
+  end
+  return top, bot, lines
+end
+
+local function sortline(line, a, o, separator)
+  separator = separator or ' '
+  local result, _ = string.gsub(line, string.sub(line, a, o),
+    table.concat(vim.fn.sort(vim.fn.split(string.sub(line, a, o), separator)), separator), 1)
+  return result
+end
+
+function M.sort_lines(_, preview_ns, preview_bufnr)
+  local bufnr = vim.api.nvim_get_current_buf()
+
+  local top, bot, lines = get_visual_selection(true)
+  vim.pretty_print(top, bot, lines)
+  for i = 1, #lines do
+    local a = 0; if i == 1 then a = top.col end
+    local o = -1; if i == #lines then o = bot.col end
+    lines[i] = sortline(lines[i], a, o, ' ')
+  end
+
+  if not preview_ns then
+    vim.api.nvim_buf_set_lines(bufnr, top.ln - 1, bot.ln, false, lines)
+    return 0
+  end
+
+  -- inccommand preview
+  if preview_ns ~= nil then
+    for i, line in ipairs(lines) do
+      vim.api.nvim_buf_set_extmark(bufnr, preview_ns, top.ln + i - 2, 0, {
+        hl_mode = 'combine',
+        virt_text_pos = 'overlay',
+        virt_text = { { line, 'Substitute' } },
+      })
+
+      if preview_bufnr ~= nil then
+        local prefix = string.format('|%d| ', top.ln + i - 1)
+        vim.api.nvim_buf_set_lines(preview_bufnr, i - 1, -1, false, { prefix .. line })
+        vim.api.nvim_buf_add_highlight(
+          preview_bufnr,
+          preview_ns,
+          'Substitute',
+          i,
+          #prefix,
+          #prefix + #line
+        )
+      end
+    end
+    return (#lines > 1 and 2 or 1)
+  end
+end
+
+function M.setup(opts)
+  vim.api.nvim_create_user_command(
+    'SortLine',
+    M.sort_lines,
+    { desc = 'sort line', range = '%', preview = M.sort_lines }
+  )
+end
+
+return M
diff --git a/.vim/lua/internal/statusline.lua b/.vim/lua/internal/statusline.lua
index 9d1ddc7..8ed5cae 100644
--- a/.vim/lua/internal/statusline.lua
+++ b/.vim/lua/internal/statusline.lua
@@ -7,6 +7,8 @@ function _G.statusline()
 
   sl = sl .. [[%-h%-w%-q%-f %-m %-r]]
 
+  sl = sl .. statusline_append(statusline_grapple(bufnr), 'StatusLineGrapple', hi)
+
   -- sl = sl .. statusline_append(statusline_ts(), 'StatusLineTreesitter', hi)
 
   sl = sl .. [[ %= ]] -- spacer
@@ -64,18 +66,16 @@ function _G.statusline_append(element, hi, default_hi, opts)
 end
 
 function _G.statusline_combine_hi(outer_name, inner_name)
-  local outer = vim.api.nvim_get_hl_by_id(vim.api.nvim_get_hl_id_by_name(outer_name), true)
-  local inner = vim.api.nvim_get_hl_by_id(vim.api.nvim_get_hl_id_by_name(inner_name), true)
-  local hi = 'auto' .. outer_name .. inner_name
-  if pcall(vim.api.nvim_get_hl_by_name, hi, true) then
-    return hi
-  end
-  local gui
-  gui = inner.bold and 'bold'
-  gui = inner.italic and 'italic'
-  gui = inner.underline and 'underline'
-  vim.api.nvim_set_hl(0, hi, { bg = outer.background, fg = inner.foreground, gui = gui })
-  return hi
+  local outer = vim.api.nvim_get_hl(0, { name = outer_name, create = false })
+  local inner = vim.api.nvim_get_hl(0, { name = inner_name, create = false })
+  local name = ('auto' .. outer_name .. inner_name)
+  vim.api.nvim_get_hl(0,
+    { name = name, create = true })
+  local hi = inner
+  hi.bg = inner.fg
+  hi.fg = outer.bg
+  vim.api.nvim_set_hl(0, name, hi)
+  return name
 end
 
 function _G.statusline_lsp_diagnostics(bufnr, default_hi)
@@ -86,20 +86,20 @@ function _G.statusline_lsp_diagnostics(bufnr, default_hi)
   if #error_num > 0 then
     local errs = vim.fn.sign_getdefined('DiagnosticSignError')[1].text .. #error_num
     s = s
-      .. statusline_append(
-        errs,
-        statusline_combine_hi('StatusLineDiagnostics', 'DiagnosticError'),
-        default_hi
-      )
+        .. statusline_append(
+          errs,
+          statusline_combine_hi('StatusLine', 'DiagnosticError'),
+          default_hi
+        )
   end
   if #warn_num > 0 then
     local warns = vim.fn.sign_getdefined('DiagnosticSignWarn')[1].text .. #warn_num
     s = s
-      .. statusline_append(
-        warns,
-        statusline_combine_hi('StatusLineDiagnostics', 'DiagnosticWarn'),
-        default_hi
-      )
+        .. statusline_append(
+          warns,
+          statusline_combine_hi('StatusLine', 'DiagnosticWarn'),
+          default_hi
+        )
   end
   return vim.trim(s)
 end
@@ -128,6 +128,16 @@ function _G.statusline_ts()
   return table.concat(s, ' > ')
 end
 
+function _G.statusline_grapple(bufnr)
+  local g = package.loaded.grapple
+  if not g then return '' end
+  return g.statusline()
+  -- if not g or not g.exists({ buffer = bufnr }) then
+  --   return ''
+  -- end
+  -- return '➥ ' .. g.key({ buffer = bufnr })
+end
+
 function _G.statusline_lspstatus()
   local lst = package.loaded['lsp-status']
   if not lst then
@@ -175,12 +185,12 @@ function _G.statusline_notify(default_hi)
   local s = ''
   if n.hi then
     s = s
-      .. statusline_append(
-        n.lvl_string .. ' ',
-        statusline_combine_hi('StatusLineNotify', n.hi),
-        default_hi,
-        { append_space = false }
-      )
+        .. statusline_append(
+          n.lvl_string .. ' ',
+          statusline_combine_hi('StatusLineNotify', n.hi),
+          default_hi,
+          { append_space = false }
+        )
   end
   s = s .. statusline_append(vim.inspect(n.message), 'StatusLineNotify', default_hi)
   return vim.trim(s)
diff --git a/.vim/lua/internal/syncbg.lua b/.vim/lua/internal/syncbg.lua
new file mode 100644
index 0000000..418e17e
--- /dev/null
+++ b/.vim/lua/internal/syncbg.lua
@@ -0,0 +1,46 @@
+local M = {}
+
+M.init = function()
+  -- get tty
+  local tty_handle = io.popen('tty')
+  if tty_handle == nil then return end
+  local tty = tty_handle:read('*a')
+  tty_handle:close()
+  if tty:find("not a tty") then error('not a tty') end
+
+  M.tty = tty
+end
+
+M.update = function()
+  if M.tty == nil then return end
+
+  if not M.normal then
+    -- get colorscheme Normal highlight
+    local normal = vim.api.nvim_get_hl(0, { name = 'Normal', create = false })
+    if (normal.bg == nil) then return end
+    -- cache old value for use with VimResume
+    M.normal = normal
+  end
+
+  -- emit CSI to change terminal background to Normal.bg
+  os.execute('printf "\\033]11;' .. string.format('#%06x', M.normal.bg) .. '\\007" > ' .. M.tty)
+
+  -- set colorscheme Normal.bg to NONE, making it transparent
+  vim.cmd [[hi Normal ctermbg=NONE guibg=NONE]]
+end
+
+M.reset = function()
+  if M.tty == nil then return end
+
+  os.execute('printf "\\033]111\\007" > ' .. M.tty)
+end
+
+M.setup = function(opts)
+  opts = opts or {}
+
+  M.init()
+  vim.api.nvim_create_autocmd({ 'ColorScheme', 'UIEnter', 'VimResume' }, { callback = M.update })
+  vim.api.nvim_create_autocmd({ 'UILeave' }, { callback = M.reset })
+end
+
+return M
diff --git a/.vim/lua/internal/zk.lua b/.vim/lua/internal/zk.lua
new file mode 100644
index 0000000..5e17786
--- /dev/null
+++ b/.vim/lua/internal/zk.lua
@@ -0,0 +1,7 @@
+-- vim.keymap.set('n', '<leader>zn', function() require('zk').new() end, {buffer=true})
+-- vim.keymap.set('n', '<leader>zi', function() require('zk').index() end, {buffer=true})
+-- vim.cmd [[ command! -nargs=* ZettelkastenNew lua require('zk').new(nil, <f-args>) ]]
+
+vim.keymap.set('n', 'gt', function()
+  require('telescope._extensions.todo-comments').exports.todo({ cwd = vim.env.ZK_NOTEBOOK_DIR })
+end, { desc = 'show zk todos', buffer = true })
diff --git a/.vim/lua/plugins.lua b/.vim/lua/plugins.lua
index 7028cda..d53eaf6 100644
--- a/.vim/lua/plugins.lua
+++ b/.vim/lua/plugins.lua
@@ -19,7 +19,7 @@ vim.g.loaded_netrwSettings = 1
 vim.g.loaded_netrwFileHandlers = 1
 
 local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
-if not vim.loop.fs_stat(lazypath) then
+if not vim.uv.fs_stat(lazypath) then
   vim.fn.system({
     'git',
     'clone',
@@ -85,6 +85,10 @@ local pluginspec = {
             require('telescope.builtin').quickfix()
           end)
         end, { buffer = bufnr, desc = 'list hunks' })
+        vim.keymap.set('n', '<leader>gs', vim.cmd.Gitsigns, {
+          buffer = bufnr,
+          desc = 'gitsigns',
+        })
       end,
     },
   },
@@ -92,23 +96,26 @@ local pluginspec = {
   {
     'lukas-reineke/indent-blankline.nvim',
     lazy = false,
+    main = 'ibl',
     opts = {
-      char = '▏',
-      char_highlight_list = { 'Whitespace' },
-      -- show_trailing_blankline_indent = false,
-      -- show_end_of_line = true,
-      use_treesitter = true,
-      filetype_exclude = { 'alpha' },
-      -- strict_tabs = true,
-      show_current_context = false,
-      -- context_patterns = { 'class', 'function', 'method', 'block' },
-      -- context_highlight_list = {'Folded'},
-      context_highlight_list = { 'deEmph' },
+      indent = {
+        char = '▏',
+        highlight = { 'NonText' },
+      },
+      exclude = {
+        filetypes = { 'alpha' },
+      },
+      scope = {
+        enabled = true,
+        show_start = false,
+        show_end = false,
+        highlight = { 'Comment' },
+      },
     },
     keys = {
       {
         '<leader>ti',
-        [[:IndentBlanklineToggle!<cr>]],
+        vim.cmd.IBLToggle,
         desc = 'toggle indent-blankline',
       },
     },
@@ -127,18 +134,6 @@ local pluginspec = {
   },
 
   {
-    'editorconfig/editorconfig-vim',
-    event = 'VeryLazy',
-    config = function()
-      vim.api.nvim_create_autocmd('FileType', {
-        pattern = { 'gitcommit' },
-        command = [[let b:EditorConfig_disable = 1]],
-        group = vim.api.nvim_create_augroup('EditorConfigDisable', {}),
-      })
-    end,
-  },
-
-  {
     'windwp/nvim-autopairs',
     priority = 40,
     event = 'InsertEnter',
@@ -147,29 +142,87 @@ local pluginspec = {
 
   {
     'elihunter173/dirbuf.nvim',
+    enabled = true,
+    dev = true,
     lazy = false,
-    config = function()
-      require('dirbuf').setup({
-        hash_padding = 4,
-        sort_order = 'directories_first',
-      })
-      vim.api.nvim_create_autocmd('FileType', {
-        pattern = 'dirbuf',
-        callback = function()
-          vim.keymap.set('n', 'gT', function()
-            vim.loop.spawn('swaymsg', { args = { 'exec', '--', 'foot', '-D', vim.fn.expand('%') } })
-            -- vim.fn.system('swaymsg exec -- foot -D ' .. vim.fn.expand('%'))
-          end, { buffer = 0, desc = 'open in foot terminal' })
+    keys = {
+      {
+        '<M-l>',
+        function()
+          local cpath = require('dirbuf').get_cursor_path()
+          for _, w in ipairs(vim.api.nvim_list_wins()) do
+            if vim.api.nvim_get_option_value('previewwindow', { win = w }) then
+              if vim.api.nvim_buf_get_name(vim.api.nvim_win_get_buf(w)) == cpath then
+                vim.cmd.pclose()
+                return
+              end
+            end
+          end
+          require('dirbuf').enter('pedit')
         end,
-        group = vim.api.nvim_create_augroup('DirBufEnter', {}),
-      })
-    end,
+        desc = 'Dirbuf: preview file',
+      },
+    },
+  },
+  {
+    'nvim-tree/nvim-tree.lua',
+    version = '*',
+    main = 'nvim-tree',
+    enabled = false,
+    lazy = false,
+    dependencies = { 'nvim-tree/nvim-web-devicons' },
+    opts = {
+      renderer = {
+        icons = {
+          show = {
+            file = false,
+            folder = false,
+            folder_arrow = true,
+            git = false,
+          },
+          glyphs = {
+            default = '󰈤 ',
+            symlink = '󰌹 ',
+            bookmark = 'b',
+            folder = {
+              arrow_open = '▾',
+              arrow_closed = '▸',
+              default = '󰉖 ',
+              open = '󰷏 ',
+              empty = '󱞞 ',
+              symlink = '󰌹 ',
+            },
+          },
+        },
+      },
+      -- on_attach = function(bufnr)
+      -- end,
+    },
+    keys = {
+      {
+        '-',
+        function()
+          require('nvim-tree.api').tree.open({
+            current_window = true,
+            path = vim.fn.expand('%:p:h'),
+          })
+        end,
+        desc = 'NvimTree: open containing directory',
+      },
+      {
+        'gt',
+        function()
+          require('nvim-tree.api').tree.toggle()
+        end,
+        desc = 'NvimTree: toggle',
+      },
+    },
   },
 
   {
     'nvim-treesitter/nvim-treesitter',
     lazy = false,
-    build = ':TSUpdate',
+    build = ':TSUpdateSync',
     config = function(_, opts)
       local tsconf = require('nvim-treesitter.parsers').get_parser_configs()
       tsconf.gotmpl = {
@@ -193,12 +246,10 @@ local pluginspec = {
         'comment',
         'css',
         'dockerfile',
-        -- 'elixir',
         'go',
         'gomod',
         'gowork',
         'hcl',
-        'help',
         'html',
         'java',
         'javascript',
@@ -212,19 +263,21 @@ local pluginspec = {
         'markdown_inline',
         'ninja',
         'norg',
+        'query',
         'regex',
         'rust',
         'scss',
         'toml',
         'typescript',
         'vim',
+        'vimdoc',
         'yaml',
         'zig',
+        'yuck', -- eww
         -- experimental
         'hare',
         'gotmpl',
       },
-      -- disable = { 'jsonc' },
       highlight = {
         enable = true,
         additional_vim_regex_highlighting = { 'markdown' },
@@ -278,7 +331,10 @@ local pluginspec = {
     'nvim-treesitter/nvim-treesitter-textobjects',
     keys = { 'ib', 'ob', 'ic', 'oc', 'if', 'of', 'il', 'ol', 'is', 'os' },
   },
-  { 'nvim-treesitter/playground', cmd = 'TSPlaygroundToggle' },
+  {
+    'nvim-treesitter/playground',
+    cmd = { 'TSPlaygroundToggle', 'TSHighlightCapturesUnderCursor' },
+  },
 
   {
     'neovim/nvim-lspconfig',
@@ -323,7 +379,10 @@ local pluginspec = {
       local cmp = require('cmp')
       local luasnip = require('luasnip')
       cmp.setup({
-        experimental = { ghost_text = true },
+        experimental = {
+          ghost_text = { hl_group = 'NonText' },
+          native_menu = false,
+        },
         -- disable completion in comments
         enabled = function()
           local ok, enable = pcall(function()
@@ -340,45 +399,41 @@ local pluginspec = {
           end)
           return not ok or enable
         end,
+        -- performance = {
+        --   max_view_entries = 20,
+        -- },
         preselect = cmp.PreselectMode.Item,
         window = {
           completion = {
+            scrollbar = true,
             border = 'none',
           },
           documentation = {
             border = 'solid',
           },
         },
+        completion = {
+          autocomplete = false,
+        },
+        sorting = {
+          comparators = {
+            cmp.config.compare.offset,
+            cmp.config.compare.exact,
+            cmp.config.compare.recently_used,
+            require("clangd_extensions.cmp_scores"),
+            cmp.config.compare.kind,
+            cmp.config.compare.sort_text,
+            cmp.config.compare.length,
+            cmp.config.compare.order,
+          },
+        },
         mapping = {
-          ['<c-space>'] = cmp.complete,
-          ['<cr>'] = cmp.mapping(function(fallback)
-            if cmp.visible() then
-              cmp.confirm({ select = true })
-            else
-              fallback()
-            end
-          end, { 'i', 's' }),
-          ['<c-n>'] = cmp.mapping(function(fallback)
-            if cmp.visible() then
-              cmp.select_next_item({ behavior = require('cmp.types').cmp.SelectBehavior.Select })
-            else
-              fallback()
-            end
-          end, { 'i', 's' }),
-          ['<c-p>'] = cmp.mapping(function(fallback)
-            if cmp.visible() then
-              cmp.select_prev_item({ behavior = require('cmp.types').cmp.SelectBehavior.Select })
-            else
-              fallback()
-            end
-          end, { 'i', 's' }),
-          ['<tab>'] = cmp.mapping(function(fallback)
-            if cmp.visible() then
-              cmp.confirm({ select = true })
-            else
-              fallback()
-            end
-          end, { 'i', 'c', 's' }),
+          ['<c-space>'] = cmp.mapping.complete({ reason = cmp.ContextReason.Auto }),
+          ['<c-x>'] = cmp.mapping.complete({ reason = cmp.ContextReason.Auto }),
+          ['<cr>'] = cmp.mapping.confirm({ select = true }),
+          ['<c-n>'] = cmp.mapping.select_next_item({ behavior = require('cmp.types').cmp.SelectBehavior.Select }),
+          ['<c-p>'] = cmp.mapping.select_prev_item({ behavior = require('cmp.types').cmp.SelectBehavior.Select }),
+          ['<tab>'] = cmp.mapping.confirm({ select = true }),
           ['<s-tab>'] = cmp.mapping(function(fallback)
             if cmp.visible() then
               -- NOTE: poor mans replacement for cmp.confirm without expanding snippet
@@ -417,12 +472,8 @@ local pluginspec = {
               fallback()
             end
           end, { 'i', 's' }),
-          ['<s-pagedown>'] = cmp.mapping(function()
-            cmp.scroll_docs(-4)
-          end, { 'i', 's' }),
-          ['<s-pageup>'] = cmp.mapping(function()
-            cmp.scroll_docs(4)
-          end, { 'i', 's' }),
+          ['<s-pagedown>'] = cmp.mapping.scroll_docs(-4),
+          ['<s-pageup>'] = cmp.mapping.scroll_docs(4),
         },
         snippet = {
           expand = function(args)
@@ -455,6 +506,8 @@ local pluginspec = {
             }
             item.kind = require('internal.lsp_symbols')[item.kind] or ''
             item.menu = menus[entry.source.name]
+            local maxw = vim.api.nvim_win_get_width(0) - 25
+            item.abbr = string.sub(item.abbr, 1, (#item.abbr > maxw) and maxw or #item.abbr)
             return item
           end,
           expandable_indicator = true,
@@ -478,18 +531,10 @@ local pluginspec = {
             end,
           }),
           ['<c-n>'] = cmp.mapping({
-            c = function()
-              if cmp.visible() then
-                cmp.select_next_item({ behavior = require('cmp.types').cmp.SelectBehavior.Insert })
-              end
-            end,
+            c = cmp.mapping.select_next_item({ behavior = require('cmp.types').cmp.SelectBehavior.Insert }),
           }),
           ['<c-p>'] = cmp.mapping({
-            c = function()
-              if cmp.visible() then
-                cmp.select_prev_item({ behavior = require('cmp.types').cmp.SelectBehavior.Insert })
-              end
-            end,
+            c = cmp.mapping.select_prev_item({ behavior = require('cmp.types').cmp.SelectBehavior.Insert }),
           }),
         },
         sources = cmp.config.sources({
@@ -567,9 +612,22 @@ local pluginspec = {
             },
             n = {
               ['<C-Space>'] = require('telescope.actions.layout').toggle_preview,
+              [';<bs>'] = function()
+                vim.cmd.Telescope('resume')
+              end,
             },
           },
         },
+        pickers = {
+          buffers = {
+            sort_mru = true,
+            mappings = {
+              i = {
+                ["<C-w>"] = require('telescope.actions').delete_buffer,
+              }
+            }
+          }
+        },
         extensions = {
           ['fzf'] = {
             fuzzy = true,
@@ -649,7 +707,7 @@ local pluginspec = {
         desc = 'command history',
       },
       {
-        ';l',
+        ';bf',
         function()
           require('telescope.builtin').current_buffer_fuzzy_find()
         end,
@@ -670,12 +728,22 @@ local pluginspec = {
         desc = 'diagnostics',
       },
       {
+        ';j',
+        function()
+          require('telescope.builtin').jumplist()
+        end,
+        desc = 'jumplist',
+      },
+      {
         ';v',
         function()
           require('telescope.builtin').find_files({
             prompt_title = 'vimrc',
             previewer = false,
-            cwd = vim.fn.stdpath('config'),
+            search_dirs = {
+              vim.fn.stdpath('config'),
+              vim.fn.stdpath('data') .. '/lazy',
+            }
           })
         end,
         desc = 'vim config',
@@ -683,7 +751,7 @@ local pluginspec = {
       {
         'gw',
         function()
-          require('internal.misc').word_under_cursor(function(s)
+          require('internal.cursor').word(function(s)
             require('telescope.builtin').grep_string({ search = s })
           end)
         end,
@@ -705,21 +773,29 @@ local pluginspec = {
         mode = 'background',
       })
     end,
+    build = [[git cherry-pick patch]],
   },
 
-  { 'gentoo/gentoo-syntax', lazy = false },
+  { 'gentoo/gentoo-syntax',                     lazy = false },
 
   {
     'NMAC427/guess-indent.nvim',
     lazy = false,
     opts = {
       auto_cmd = true,
-      filetype_exclude = { 'netrw', 'tutor', 'dirbuf' },
+      filetype_exclude = { 'netrw', 'tutor', 'dirbuf', 'nvimtree' },
       buftype_exclude = { 'help', 'nofile', 'terminal', 'prompt' },
     },
   },
 
   {
+    'echasnovski/mini.align',
+    version = false,
+    opts = {},
+    keys = { 'ga', 'gA' }
+  },
+
+  {
     url = 'https://git.sr.ht/~robertgzr/spinner.nvim',
     opts = {
       spinner = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' },
@@ -730,6 +806,7 @@ local pluginspec = {
   {
     'stevearc/aerial.nvim',
     cmd = { 'AerialOpen', 'AerialToggle' },
+    keys = { { '<space><space>', vim.cmd.AerialToggle, desc = 'Toggle aerial' } },
     opts = {
       backends = { 'lsp', 'treesitter', 'markdown' },
       filter_kind = {
@@ -785,12 +862,13 @@ local pluginspec = {
     event = 'VeryLazy',
     config = function()
       vim.lsp.handlers['textDocument/codeAction'] =
-        require('code_action_menu').open_code_action_menu
+          require('code_action_menu').open_code_action_menu
     end,
   },
 
   {
     'jose-elias-alvarez/null-ls.nvim',
+    enabled = false,
     ft = {
       'sh',
       'bash',
@@ -834,7 +912,7 @@ local pluginspec = {
           nls.builtins.formatting.stylua,
           -- nls.builtins.formatting.gofumpt,
           -- nls.builtins.formatting.json_tool,
-          nls.builtins.formatting.mdformat,
+          -- nls.builtins.formatting.mdformat,
 
           -- custom sources
           beautifier.with({ filetypes = { 'html', 'gotmpl.html' }, command = 'html-beautify' }),
@@ -847,16 +925,21 @@ local pluginspec = {
 
   {
     'mfussenegger/nvim-dap',
-    dependencies = { 'nvim-telescope/telescope-dap.nvim' },
+    dependencies = {
+      'nvim-telescope/telescope-dap.nvim',
+      'rcarriga/nvim-dap-ui',
+    },
     config = function()
       require('telescope').load_extension('dap')
       require('internal.dap')
     end,
-    keys = { '<leader>d' },
+    keys = {
+      '<leader>d',
+    },
   },
-  { 'farmergreg/vim-lastplace', event = 'VeryLazy' }, -- restore last cursor position (and handle many edge cases)
-  { 'tpope/vim-surround', event = 'VeryLazy' },
-  { 'tpope/vim-repeat', event = 'VeryLazy' }, -- extend '.' to plugins
+  { 'farmergreg/vim-lastplace',        event = 'VeryLazy' }, -- restore last cursor position (and handle many edge cases)
+  { 'tpope/vim-surround',              event = 'VeryLazy' },
+  { 'tpope/vim-repeat',                event = 'VeryLazy' }, -- extend '.' to plugins
   { 'michaeljsmith/vim-indent-object', event = 'VeryLazy' }, -- indentation text-objects
   -- {'kshenoy/vim-signature'}, -- toggle, display and navigate marks
   -- {'rhysd/conflict-marker.vim'},
@@ -949,7 +1032,7 @@ local pluginspec = {
   },
 
   {
-    'mickael-menu/zk-nvim',
+    'zk-org/zk-nvim',
     cmd = { 'ZkNew', 'ZkNotes' },
     ft = { 'markdown', 'neorg' },
     config = function()
@@ -965,18 +1048,38 @@ local pluginspec = {
           },
           auto_attach = {
             enabled = true,
-            filetypes = { 'markdown', 'neorg' },
+            filetypes = { 'markdown' },
           },
         },
       })
     end,
     keys = {
       {
-        ';z',
+        ';zn',
+        vim.cmd.ZkNotes,
+        desc = 'zk notes',
+      },
+      {
+        ';zb',
+        vim.cmd.ZkBacklinks,
+        desc = 'zk backlinks',
+      },
+      {
+        ';zt',
+        vim.cmd.ZkTags,
+        desc = 'zk tags',
+      },
+      {
+        ';zl',
+        vim.cmd.ZkLinks,
+        desc = 'zk links',
+      },
+      {
+        ';zN',
         function()
-          require('zk').edit(nil, { multi_select = false })
+
         end,
-        desc = 'zk',
+        desc = 'zk new note',
       },
     },
   },
@@ -987,6 +1090,7 @@ local pluginspec = {
 
   {
     'lervag/vimtex',
+    enabled = false,
     ft = { 'latex', 'tex' },
     config = function()
       local g = vim.g
@@ -995,33 +1099,26 @@ local pluginspec = {
       g.vimtex_compiler_method = 'tectonic'
       g.vimtex_compiler_tectonic = "{'executable': 'tectonic'}"
       g.vimtex_view_method = 'zathura'
-      -- g.vimtex_view_zathura_check_libsynctex = false
-      -- g.vimtex_view_use_temp_files = true
-      -- g.vimtex_view_forward_search_on_start = false
     end,
   },
 
   {
-    'plasticboy/vim-markdown',
+    'preservim/vim-markdown',
     ft = 'markdown',
+    enabled = true,
     config = function()
-      local g = vim.g
-      g.vim_markdown_folding_disabled = true
-      g.vim_markdown_math = true
-      g.vim_markdown_frontmatter = true
-      g.vim_markdown_json_frontmatter = true
-      g.vim_markdown_toml_frontmatter = true
-      g.vim_markdown_yaml_frontmatter = true
-      -- g.vim_markdown_folding_level = 2
-      g.vim_markdown_strikethrough = true
-      g.vim_markdown_auto_insert_bullets = true
-      g.vim_markdown_new_list_item_indent = false
-      g.vim_markdown_conceal = true
-      g.vim_markdown_conceal_code_blocks = true
+      vim.g.vim_markdown_frontmatter = 0         -- handled by tre-sitter
+      vim.g.vim_markdown_strikethrough = 1
+      vim.g.vim_markdown_conceal_code_blocks = 0 -- doesn't work due to tree-sitter: https://github.com/nvim-treesitter/nvim-treesitter/issues/2825
+      vim.g.vim_markdown_no_default_key_mappings = 1
     end,
   },
 
   {
+    'https://git.sr.ht/~p00f/clangd_extensions.nvim',
+  },
+
+  {
     'folke/zen-mode.nvim',
     dependencies = {
       {
@@ -1038,8 +1135,8 @@ local pluginspec = {
     cmd = { 'ZenMode' },
     opts = {
       window = {
-        backdrop = 1,
-        width = 0.3,
+        -- backdrop = 1,
+        -- width = 0.3,
         -- height = 1,
         options = {
           number = false,
@@ -1053,6 +1150,7 @@ local pluginspec = {
           showcmd = false,
         },
         twilight = { enabled = true },
+        gitsigns = { enabled = false },
       },
     },
   },
@@ -1068,6 +1166,7 @@ local pluginspec = {
 
   {
     'goolord/alpha-nvim',
+    enabled = false,
     cmd = 'Alpha',
     config = function()
       local MAX_WIDTH = 80
@@ -1104,18 +1203,18 @@ local pluginspec = {
             -- exit here if we're not showing a label
             if label_len == 0 then
               return string.rep(' ', margin)
-                .. string.rep('─', max_width)
-                .. string.rep(' ', margin)
+                  .. string.rep('─', max_width)
+                  .. string.rep(' ', margin)
             end
             if label_len > max_width then
               return error('overfull box: ' .. label)
             end
             max_width = max_width - label_len
             return string.rep(' ', margin)
-              .. string.rep('─', max_width / 2)
-              .. label
-              .. string.rep('─', max_width / 2)
-              .. string.rep(' ', margin)
+                .. string.rep('─', max_width / 2)
+                .. label
+                .. string.rep('─', max_width / 2)
+                .. string.rep(' ', margin)
           end,
         }
       end
@@ -1156,7 +1255,7 @@ local pluginspec = {
 
             -- initialize the oldfiles table
             local oldfiles = {}
-            local cwd = vim.loop.cwd()
+            local cwd = vim.uv.cwd()
             for _, v in pairs(vim.v.oldfiles) do
               if #oldfiles == 10 then
                 break
@@ -1219,7 +1318,7 @@ local pluginspec = {
 
             for _, bufnr in ipairs(bufnrs) do
               local bufpath =
-                require('plenary.path').new(vim.fn.getbufinfo(bufnr)[1].name):shorten(1, { -2, -1 })
+                  require('plenary.path').new(vim.fn.getbufinfo(bufnr)[1].name):shorten(1, { -2, -1 })
               table.insert(items, button('b' .. bufnr, bufpath, ':b' .. bufnr .. '<cr>'))
             end
             return items
@@ -1274,35 +1373,24 @@ local pluginspec = {
   },
 
   {
-    'ruifm/gitlinker.nvim',
+    'linrongbin16/gitlinker.nvim',
+    cmd = { 'GitLink' },
     dependencies = { 'nvim-lua/plenary.nvim' },
-    config = {
-      opts = {
-        -- action_callback = require('gitlinker.actions').copy_to_clipboard,
-      },
+    opts = {
+      message = true,
     },
     keys = {
       {
-        '<leader>gy',
-        function()
-          require('gitlinker').get_buf_range_url()
-        end,
-        desc = 'gitlinker',
-      },
-      {
-        mode = 'v',
-        '<leader>gy',
-        function()
-          require('gitlinker').get_buf_range_url()
-        end,
-        desc = 'gitlinker',
+        '<leader>gl',
+        vim.cmd.GitLink,
+        desc = 'gitlinker -> clipboard',
       },
     },
   },
 
   {
     'rhysd/git-messenger.vim',
-    cmd = 'GitMessenger',
+    cmd = { 'GitMessenger' },
     config = function()
       -- vim.g.git_messenger_include_diff = false
       vim.g.git_messenger_always_into_popup = true
@@ -1310,33 +1398,54 @@ local pluginspec = {
       vim.g.git_messenger_no_default_mappings = true
     end,
     keys = {
-      { '<leader>gm', vim.cmd.GitMessenger, desc = 'git-messenger' },
+      { '<leader>gb', vim.cmd.GitMessenger, desc = 'blame (git-messenger)' },
     },
   },
 
   {
     'folke/todo-comments.nvim',
+    lazy = false,
     cmd = { 'TodoTelescope' },
-    config = true,
+    config = {
+      keywords = {
+        FIX = { icon = '󰃤' },
+        TODO = { icon = '󰸞' },
+        HACK = { icon = '󰈸' },
+        WARN = { icon = '󱇎' },
+        PERF = { icon = '󰅒' },
+        NOTE = { icon = '󰐃' },
+        TEST = { icon = '󰂓' },
+      },
+      search = {
+        pattern = [[\b(KEYWORDS)(|\(.+\)):]],
+      },
+      highlight = {
+        keyword = "bg",
+        pattern = [[(KEYWORDS)]],
+      }
+    },
     keys = {
-      { ';t', vim.cmd.TodoTelescope, desc = 'todo-comments' },
+      { ';t', vim.cmd.TodoTelescope,                               desc = 'todo-comments' },
+      { "]t", function() require("todo-comments").jump_next() end, desc = "Next todo comment" },
+      { "[t", function() require("todo-comments").jump_prev() end, desc = "Previous todo comment" },
     },
   },
 
   {
     'cbochs/grapple.nvim',
     dependencies = { 'nvim-lua/plenary.nvim' },
-    cmd = 'GrapplePopup',
+    cmd = { 'GrapplePopup' },
     opts = {
+      icons = false,
       popup_options = {
         border = 'none',
       },
     },
     keys = {
       {
-        '<leader>m',
+        '<leader>ml',
         function()
-          require('grapple').popup_tags()
+          require('grapple').toggle_tags()
         end,
         desc = 'grapple: show tags',
       },
@@ -1372,11 +1481,34 @@ local pluginspec = {
   },
 
   {
+    'tjdevries/sg.nvim',
+    enabled = false,
+    lazy = false,
+    build = 'cargo build --workspace',
+    dependencies = { 'nvim-lua/plenary.nvim' },
+    config = function()
+      require('sg').setup({
+        on_attach = require('internal.lsp').my_attach,
+      })
+    end,
+    keys = {
+      {
+        '<leader>s',
+        function()
+          require('sg.telescope').fuzzy_search_results()
+        end,
+        desc = 'sg.nvim: fuzzy search results',
+      },
+    },
+  },
+
+  {
     url = 'https://git.sr.ht/~robertgzr/karafuru',
+    dev = true,
     lazy = false,
-    -- build = 'make colorscheme',
+    enabled = false,
     priority = 1000,
-    cond = vim.g.night_and_day == 'night',
+    cond = (vim.opt.background:get() == 'dark'),
     config = function(plugin)
       vim.opt.rtp:append(plugin.dir .. '/vim')
       vim.cmd.colorscheme('karafuru')
@@ -1386,8 +1518,9 @@ local pluginspec = {
   {
     'yorik1984/newpaper.nvim',
     lazy = false,
+    enabled = false,
     priority = 1000,
-    cond = vim.g.night_and_day == 'day',
+    cond = (vim.opt.background:get() == 'light'),
     config = function()
       vim.cmd.colorscheme('newpaper')
     end,
@@ -1398,14 +1531,48 @@ local pluginspec = {
     lazy = false,
     enabled = false,
     priority = 1000,
-    cond = vim.g.night_and_day == 'night',
+    cond = (vim.opt.background:get() == 'dark'),
     config = function()
       vim.opt.background = 'dark'
       vim.cmd.colorscheme('oxocarbon')
     end,
   },
+
+  {
+    'mcchrish/zenbones.nvim',
+    dependencies = { 'rktjmp/lush.nvim' },
+    lazy = false,
+    enabled = false,
+    priority = 1000,
+    config = function()
+      vim.opt.termguicolors = true
+      local scheme = 'tokyobones'
+      vim.g[scheme] = {
+        lighten_non_text = 20,
+        transparent_background = (vim.opt.background:get() == 'dark'),
+      }
+      vim.cmd.colorscheme(scheme)
+    end,
+  },
+
+  -- quarantine
+  {
+    'zbirenbaum/copilot.lua',
+    enabled = false,
+    cmd = { 'Copilot' },
+    opts = {
+      panel = { auto_refresh = true },
+      suggestion = { auto_trigger = true },
+    },
+  },
+  {
+    'Kicamon/markdown-table-mode.nvim',
+    enabled = true,
+    ft = { 'markdown' },
+  },
 }
 
 require('lazy').setup(pluginspec, {
   defaults = { lazy = true },
+  dev = { path = '~/src' },
 })
diff --git a/.vim/snippets/all.lua b/.vim/snippets/all.lua
index f218086..0d0e3e6 100644
--- a/.vim/snippets/all.lua
+++ b/.vim/snippets/all.lua
@@ -9,7 +9,8 @@ local todo_comment = function(keys)
 end
 
 return {
-  s('me', user_email),
+  -- s('my', user_email),
+  s('mename', t([[Robert Günzler]])),
   s('date', exec([[date --rfc-email]])),
   s(
     'today',
@@ -46,7 +47,7 @@ return {
     {
       callbacks = {
         [-1] = {
-          [events.enter] = function(_, _)
+          [require('luasnip.util.events').enter] = function(_, _)
             vim.opt_local.filetype = 'sh'
           end,
         },
@@ -54,6 +55,10 @@ return {
     }
   ),
 
+  -- mini symbols
+  s('check', t([[✔]])),
+  s('cross', t([[✖]])),
+
   -- licenses (SPDX header)
   s({ trig = 'spdx-mit', name = 'MIT license' }, comment({ t([[SPDX-License-Identifier: MIT]]) })),
   s(
diff --git a/.vim/snippets/cmake.lua b/.vim/snippets/cmake.lua
new file mode 100644
index 0000000..946fab7
--- /dev/null
+++ b/.vim/snippets/cmake.lua
@@ -0,0 +1,26 @@
+---@diagnostic disable: undefined-global
+return {
+  s(
+    { trig = 'debugvar', desc = 'cmake_print_variables' },
+    fmt(
+      [=[
+      include(CMakePrintHelpers)
+      cmake_print_variables({})
+    ]=],
+      { i(1) }
+    )
+  ),
+  s(
+    { trig = 'debugprop', desc = 'cmake_print_properties' },
+    fmt(
+      [=[
+      include(CMakePrintHelpers)
+      cmake_print_properties(
+          TARGETS {}
+          PROPERTIES {}
+      )
+    ]=],
+      { i(1), i(2) }
+    )
+  ),
+}
diff --git a/.vim/snippets/lua.lua b/.vim/snippets/lua.lua
index dc75ec8..6e9e58a 100644
--- a/.vim/snippets/lua.lua
+++ b/.vim/snippets/lua.lua
@@ -1,22 +1,24 @@
 ---@diagnostic disable: undefined-global
 return {
   s(
-    'use',
+    'plug',
     fmt(
       [[
-    use {{'{}'{} -- {{{{{{
-      config = function()
-        require('{}').setup {{}}
-      end}} -- }}}}}}
-    ]],
+      {{
+        '{}',
+        enabled = true,
+        {},
+        opts = {{
+          {}
+        }},
+      }},
+      ]],
       {
         i(1, 'username/repo'),
         c(2, {
-          t(','),
-          t(', disabled = true,'),
-          fmt([[, cmd = {{'{}'}}, module = {{'{}'}},]], { i(1, 'Cmd'), i(2, 'module') }),
+          fmt([[cmd = {{'{}'}}]], { i(1, 'Cmd') }),
         }),
-        i(3, 'module'),
+        i(3),
       }
     )
   ),
diff --git a/.vim/snippets/mail.lua b/.vim/snippets/mail.lua
new file mode 100644
index 0000000..a9ccfc6
--- /dev/null
+++ b/.vim/snippets/mail.lua
@@ -0,0 +1,11 @@
+---@diagnostic disable: undefined-global
+return {
+  s(
+    { trig = 'sehr', desc = 'Sehr geehrte ...' },
+    t([[Sehr geehrte Damen und Herren,]])
+  ),
+  s(
+    { trig = 'mfg', desc = 'Mit freundlichen Grüßen' },
+    t([[Mit freundlichen Grüßen,]])
+  ),
+}