aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/dot_config
diff options
context:
space:
mode:
Diffstat (limited to 'dot_config')
-rw-r--r--dot_config/MangoHud/MangoHud.conf10
-rw-r--r--dot_config/aerc/aerc.conf32
-rw-r--r--dot_config/aerc/binds.conf165
-rw-r--r--dot_config/bat/config25
-rw-r--r--dot_config/cargo/config.toml6
-rw-r--r--dot_config/ccache/ccache.conf2
-rw-r--r--dot_config/clangd/config.yaml23
-rw-r--r--dot_config/fd/ignore1
-rw-r--r--dot_config/fontconfig/fonts.conf35
-rw-r--r--dot_config/fuzzel/fuzzel.ini20
-rw-r--r--dot_config/gamemode.ini7
-rw-r--r--dot_config/gdb/gdbearlyinit1
-rw-r--r--dot_config/gdb/gdbinit6
-rw-r--r--dot_config/ghostty/config37
-rw-r--r--dot_config/git/attributes26
-rw-r--r--dot_config/git/config135
-rw-r--r--dot_config/git/ignore21
-rw-r--r--dot_config/gtk-3.0/gtk.css36
-rw-r--r--dot_config/gtk-3.0/settings.ini17
-rw-r--r--dot_config/ipython/profile_default/ipython_config.py5
-rw-r--r--dot_config/lsd/config.yaml20
-rw-r--r--dot_config/mako/config21
-rw-r--r--dot_config/mimeapps.list101
-rw-r--r--dot_config/mpv/input.conf96
-rw-r--r--dot_config/mpv/mpv.conf110
-rw-r--r--dot_config/mpv/scripts/webtorrent-hook.lua138
-rw-r--r--dot_config/npm/npmrc3
-rw-r--r--dot_config/nvim/after/ftplugin/gitcommit.lua3
-rw-r--r--dot_config/nvim/after/ftplugin/gitrebase.lua16
-rw-r--r--dot_config/nvim/after/ftplugin/mail.lua1
-rw-r--r--dot_config/nvim/after/ftplugin/markdown.lua1
-rw-r--r--dot_config/nvim/after/ftplugin/text.lua3
-rw-r--r--dot_config/nvim/after/lsp/clangd.lua57
-rw-r--r--dot_config/nvim/after/lsp/fortls.lua10
-rw-r--r--dot_config/nvim/after/lsp/lua_ls.lua9
-rw-r--r--dot_config/nvim/filetype.lua14
-rw-r--r--dot_config/nvim/init.lua126
-rw-r--r--dot_config/nvim/lua/config/autocmds.lua128
-rw-r--r--dot_config/nvim/lua/config/keymaps.lua136
-rw-r--r--dot_config/nvim/lua/config/options.lua114
-rw-r--r--dot_config/nvim/lua/plugins/ai.lua34
-rw-r--r--dot_config/nvim/lua/plugins/completion.lua86
-rw-r--r--dot_config/nvim/lua/plugins/debug.lua75
-rw-r--r--dot_config/nvim/lua/plugins/editing.lua61
-rw-r--r--dot_config/nvim/lua/plugins/git.lua123
-rw-r--r--dot_config/nvim/lua/plugins/init.lua58
-rw-r--r--dot_config/nvim/lua/plugins/lsp.lua280
-rw-r--r--dot_config/nvim/lua/plugins/runner.lua77
-rw-r--r--dot_config/nvim/lua/plugins/search.lua60
-rw-r--r--dot_config/nvim/lua/plugins/session.lua77
-rw-r--r--dot_config/nvim/lua/plugins/treesitter.lua83
-rw-r--r--dot_config/nvim/lua/plugins/ui.lua58
-rw-r--r--dot_config/nvim/nvim-pack-lock.json222
-rw-r--r--dot_config/pacman/makepkg.conf9
-rw-r--r--dot_config/paru/paru.conf25
-rw-r--r--dot_config/ripgrep/ripgreprc4
-rw-r--r--dot_config/sh/inputrc20
-rw-r--r--dot_config/streamlink/config3
-rw-r--r--dot_config/sway/config157
-rwxr-xr-xdot_config/sway/executable_display-toggle.sh45
-rw-r--r--dot_config/systemd/user/bridge.service.d/override.conf2
-rw-r--r--dot_config/systemd/user/vdirsyncer.service.d/override.conf2
-rw-r--r--dot_config/user-dirs.dirs8
-rw-r--r--dot_config/user-dirs.locale1
-rw-r--r--dot_config/waybar/config.jsonc67
-rw-r--r--dot_config/waybar/style.css71
-rw-r--r--dot_config/wget/wgetrc1
-rw-r--r--dot_config/yazi/keymap.toml9
-rw-r--r--dot_config/yazi/yazi.toml5
-rw-r--r--dot_config/yt-dlp/config9
-rw-r--r--dot_config/zathura/zathurarc4
-rw-r--r--dot_config/zellij/config.kdl81
-rw-r--r--dot_config/zsh/dot_zprofile116
-rw-r--r--dot_config/zsh/dot_zshrc322
74 files changed, 3972 insertions, 0 deletions
diff --git a/dot_config/MangoHud/MangoHud.conf b/dot_config/MangoHud/MangoHud.conf
new file mode 100644
index 0000000..322ad92
--- /dev/null
+++ b/dot_config/MangoHud/MangoHud.conf
@@ -0,0 +1,10 @@
+cpu_stats
+cpu_temp
+gpu_stats
+gpu_temp
+frame_timing
+font_size=16
+position=bottom-right
+ram
+vram
+background_alpha=0.8
diff --git a/dot_config/aerc/aerc.conf b/dot_config/aerc/aerc.conf
new file mode 100644
index 0000000..25deb62
--- /dev/null
+++ b/dot_config/aerc/aerc.conf
@@ -0,0 +1,32 @@
+[ui]
+styleset-name=gruvbox
+fuzzy-complete=true
+reverse-msglist-order=true
+threading-enabled=true
+show-thread-context=true
+
+[compose]
+address-book-cmd=khard email --remove-first-line --parsable %s
+file-picker-cmd=fzf --multi --query=%s
+empty-subject-warning=true
+no-attachment-warning=^[^>]*attach(ed|ment)
+
+[multipart-converters]
+text/html=pandoc -f markdown -t html --standalone
+
+[filters]
+text/plain=colorize
+text/calendar=calendar
+message/delivery-status=colorize
+message/rfc822=colorize
+text/html=html | colorize
+text/*=bat -fP --file-name="$AERC_FILENAME"
+application/x-sh=bat -fP -l sh
+.headers=colorize
+
+[openers]
+x-scheme-handler/http*=librewolf
+text/html=librewolf
+
+[hooks]
+mail-received=notify-send "[$AERC_ACCOUNT/$AERC_FOLDER] New mail from $AERC_FROM_NAME" "$AERC_SUBJECT"
diff --git a/dot_config/aerc/binds.conf b/dot_config/aerc/binds.conf
new file mode 100644
index 0000000..145efd9
--- /dev/null
+++ b/dot_config/aerc/binds.conf
@@ -0,0 +1,165 @@
+# Binds are of the form <key sequence> = <command to run>
+# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
+# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
+<C-p> = :prev-tab<Enter>
+<C-PgUp> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+<C-PgDn> = :next-tab<Enter>
+<C-t> = :term<Enter>
+? = :help keys<Enter>
+<C-c> = :prompt 'Quit?' quit<Enter>
+<C-q> = :prompt 'Quit?' quit<Enter>
+
+[messages]
+q = :quit<Enter>
+h = :quit<Enter>
+
+j = :next<Enter>
+<Down> = :next<Enter>
+<C-d> = :next 50%<Enter>
+<C-f> = :next 100%<Enter>
+<PgDn> = :next 100%<Enter>
+
+k = :prev<Enter>
+<Up> = :prev<Enter>
+<C-u> = :prev 50%<Enter>
+<C-b> = :prev 100%<Enter>
+<PgUp> = :prev 100%<Enter>
+g = :select 0<Enter>
+G = :select -1<Enter>
+
+J = :next-folder<Enter>
+<C-Down> = :next-folder<Enter>
+K = :prev-folder<Enter>
+<C-Up> = :prev-folder<Enter>
+H = :collapse-folder<Enter>
+<C-Left> = :collapse-folder<Enter>
+L = :expand-folder<Enter>
+<C-Right> = :expand-folder<Enter>
+
+v = :mark -t<Enter>
+<Space> = :mark -t<Enter>:next<Enter>
+V = :mark -v<Enter>
+
+T = :toggle-threads<Enter>
+zc = :fold<Enter>
+zo = :unfold<Enter>
+
+<Enter> = :view<Enter>
+d = :prompt 'Really delete this message?' 'delete-message'<Enter>
+D = :delete<Enter>
+a = :archive flat<Enter>
+A = :unmark -a<Enter>:mark -T<Enter>:archive flat<Enter>
+
+C = :compose<Enter>
+m = :compose<Enter>
+
+rr = :reply -a<Enter>
+rq = :reply -aq<Enter>
+Rr = :reply<Enter>
+Rq = :reply -q<Enter>
+
+c = :cf<space>
+$ = :term<space>
+! = :term<space>
+| = :pipe<space>
+
+/ = :search<space>
+\ = :filter<space>
+n = :next-result<Enter>
+N = :prev-result<Enter>
+<Esc> = :clear<Enter>
+
+s = :split<Enter>
+S = :vsplit<Enter>
+
+[messages:folder=Drafts]
+<Enter> = :recall<Enter>
+
+[view]
+/ = :toggle-key-passthrough<Enter>/
+q = :close<Enter>
+h = :close<Enter>
+O = :open<Enter>
+o = :open<Enter>
+S = :save<space>
+| = :pipe<space>
+D = :delete<Enter>
+A = :archive flat<Enter>
+s = :flag -t<Enter>
+
+<C-l> = :open-link <space>
+
+f = :forward<Enter>
+rr = :reply -a<Enter>
+rq = :reply -aq<Enter>
+Rr = :reply<Enter>
+Rq = :reply -q<Enter>
+
+H = :toggle-headers<Enter>
+<C-k> = :prev-part<Enter>
+<C-Up> = :prev-part<Enter>
+<C-j> = :next-part<Enter>
+<C-Down> = :next-part<Enter>
+J = :next<Enter>
+<C-Right> = :next<Enter>
+K = :prev<Enter>
+<C-Left> = :prev<Enter>
+
+[view::passthrough]
+$noinherit = true
+$ex = <C-x>
+<Esc> = :toggle-key-passthrough<Enter>
+
+[compose]
+# Keybindings used when the embedded terminal is not selected in the compose
+# view
+$noinherit = true
+$ex = <C-x>
+<C-k> = :prev-field<Enter>
+<C-Up> = :prev-field<Enter>
+<C-j> = :next-field<Enter>
+<C-Down> = :next-field<Enter>
+<A-p> = :switch-account -p<Enter>
+<C-Left> = :switch-account -p<Enter>
+<A-n> = :switch-account -n<Enter>
+<C-Right> = :switch-account -n<Enter>
+<tab> = :next-field<Enter>
+<backtab> = :prev-field<Enter>
+<C-p> = :prev-tab<Enter>
+<C-PgUp> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+<C-PgDn> = :next-tab<Enter>
+
+[compose::editor]
+# Keybindings used when the embedded terminal is selected in the compose view
+$noinherit = true
+$ex = <C-x>
+<C-k> = :prev-field<Enter>
+<C-Up> = :prev-field<Enter>
+<C-j> = :next-field<Enter>
+<C-Down> = :next-field<Enter>
+<C-p> = :prev-tab<Enter>
+<C-PgUp> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+<C-PgDn> = :next-tab<Enter>
+
+[compose::review]
+# Keybindings used when reviewing a message to be sent
+y = :send<Enter>
+n = :abort<Enter>
+v = :preview<Enter>
+p = :postpone<Enter>
+q = :choose -o d discard abort -o p postpone postpone<Enter>
+e = :edit<Enter>
+a = :attach<space>
+d = :detach<space>
+
+[terminal]
+$noinherit = true
+$ex = <C-x>
+
+<C-p> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+<C-PgUp> = :prev-tab<Enter>
+<C-PgDn> = :next-tab<Enter>
diff --git a/dot_config/bat/config b/dot_config/bat/config
new file mode 100644
index 0000000..c0afe26
--- /dev/null
+++ b/dot_config/bat/config
@@ -0,0 +1,25 @@
+# This is `bat`s configuration file. Each line either contains a comment or
+# a command-line option that you want to pass to `bat` by default. You can
+# run `bat --help` to get a list of all possible configuration options.
+
+# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes`
+# for a list of all available themes
+--theme="gruvbox-dark"
+
+# Enable this to use italic text on the terminal. This is not supported on all
+# terminal emulators (like tmux, by default):
+#--italic-text=always
+
+# Uncomment the following line to disable automatic paging:
+#--paging=never
+
+# Uncomment the following line if you are using less version >= 551 and want to
+# enable mouse scrolling support in `bat` when running inside tmux. This might
+# disable text selection, unless you press shift.
+#--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse"
+
+# Syntax mappings: map a certain filename pattern to a language.
+# Example 1: use the C++ syntax for .ino files
+# Example 2: Use ".gitignore"-style highlighting for ".ignore" files
+#--map-syntax "*.ino:C++"
+#--map-syntax ".ignore:Git Ignore"
diff --git a/dot_config/cargo/config.toml b/dot_config/cargo/config.toml
new file mode 100644
index 0000000..0ef993e
--- /dev/null
+++ b/dot_config/cargo/config.toml
@@ -0,0 +1,6 @@
+[target.x86_64-unknown-linux-gnu]
+linker = "clang"
+rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=mold"]
+
+[build]
+rustc-wrapper = "sccache"
diff --git a/dot_config/ccache/ccache.conf b/dot_config/ccache/ccache.conf
new file mode 100644
index 0000000..99d2c37
--- /dev/null
+++ b/dot_config/ccache/ccache.conf
@@ -0,0 +1,2 @@
+max_size = 0
+reshare = true
diff --git a/dot_config/clangd/config.yaml b/dot_config/clangd/config.yaml
new file mode 100644
index 0000000..a50287d
--- /dev/null
+++ b/dot_config/clangd/config.yaml
@@ -0,0 +1,23 @@
+---
+Index:
+ StandardLibrary: Yes
+Diagnostics:
+ ClangTidy:
+ FastCheckFilter: None
+ UnusedIncludes: Strict
+ # MissingIncludes: Strict
+ Includes:
+ AnalyzeAngledIncludes: Yes
+InlayHints:
+ Enabled: Yes
+ BlockEnd: Yes
+ DefaultArguments: Yes
+Hover:
+ ShowAKA: Yes
+# ---
+# If:
+# PathMatch: ~/llvm-project/.*
+# Index:
+# External:
+# Server: clangd.devel.appentra.com:5900
+# MountPoint: /home/ruifm/work/pw-project
diff --git a/dot_config/fd/ignore b/dot_config/fd/ignore
new file mode 100644
index 0000000..2d2ecd6
--- /dev/null
+++ b/dot_config/fd/ignore
@@ -0,0 +1 @@
+.git/
diff --git a/dot_config/fontconfig/fonts.conf b/dot_config/fontconfig/fonts.conf
new file mode 100644
index 0000000..63eac54
--- /dev/null
+++ b/dot_config/fontconfig/fonts.conf
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+
+<alias>
+ <family>monospace</family>
+ <prefer>
+ <family>Fira Code</family>
+ <family>DejaVu Sans Mono</family>
+ <family>Inconsolata</family>
+ </prefer>
+ </alias>
+
+ <alias>
+ <family>sans-serif</family>
+ <prefer>
+ <family>DejaVu Sans</family>
+ <family>Droid Sans</family>
+ <family>Ubuntu</family>
+ <family>Helvetica</family>
+ </prefer>
+ </alias>
+
+ <alias>
+ <family>serif</family>
+ <prefer>
+ <family>Latin Modern Math</family>
+ <family>DejaVu Serif</family>
+ <family>CMU Serif</family>
+ <family>Latin Modern Roman</family>
+ <family>Computer Modern</family>
+ </prefer>
+ </alias>
+
+</fontconfig>
diff --git a/dot_config/fuzzel/fuzzel.ini b/dot_config/fuzzel/fuzzel.ini
new file mode 100644
index 0000000..b1ea0bb
--- /dev/null
+++ b/dot_config/fuzzel/fuzzel.ini
@@ -0,0 +1,20 @@
+[main]
+font=mono:size=8
+width=60
+lines=15
+list-executables-in-path=yes
+terminal=ghostty -e
+
+[colors]
+background=282828ff
+text=ebdbb2ff
+prompt=fabd2fff
+input=ebdbb2ff
+match=fb4934ff
+selection=fabd2fff
+selection-text=282828ff
+border=fabd2fff
+
+[border]
+width=1
+radius=0
diff --git a/dot_config/gamemode.ini b/dot_config/gamemode.ini
new file mode 100644
index 0000000..d6a5189
--- /dev/null
+++ b/dot_config/gamemode.ini
@@ -0,0 +1,7 @@
+[general]
+reaper_freq=5
+desiredgov=performance
+defaultgov=performance
+softrealtime=off
+renice=0
+ioprio=0
diff --git a/dot_config/gdb/gdbearlyinit b/dot_config/gdb/gdbearlyinit
new file mode 100644
index 0000000..b73aa96
--- /dev/null
+++ b/dot_config/gdb/gdbearlyinit
@@ -0,0 +1 @@
+set startup-quietly on
diff --git a/dot_config/gdb/gdbinit b/dot_config/gdb/gdbinit
new file mode 100644
index 0000000..8a2852c
--- /dev/null
+++ b/dot_config/gdb/gdbinit
@@ -0,0 +1,6 @@
+set debuginfod enabled on
+set confirm off
+set print pretty on
+set follow-fork-mode child
+set auto-load local-gdbinit on
+set auto-load safe-path /
diff --git a/dot_config/ghostty/config b/dot_config/ghostty/config
new file mode 100644
index 0000000..18bb644
--- /dev/null
+++ b/dot_config/ghostty/config
@@ -0,0 +1,37 @@
+# Theme (verify: ghostty +list-themes | grep -i gruvbox)
+theme = Gruvbox Dark
+
+# Cursor — block blink; disable shell integration cursor override
+cursor-style = block
+cursor-style-blink = true
+shell-integration-features = no-cursor
+
+# OSC 52 clipboard for zellij (default is "ask" which prompts each time)
+clipboard-read = allow
+
+# Bell — request urgency hint on BEL so sway/waybar highlight the workspace
+bell-features = attention,title
+
+# Window — no decorations (sway manages windows)
+window-decoration = none
+confirm-close-surface = false
+resize-overlay = never
+
+# Disable built-in tab/split keybinds (zellij handles multiplexing)
+keybind = ctrl+shift+t=unbind
+keybind = ctrl+shift+n=unbind
+keybind = ctrl+shift+o=unbind
+keybind = ctrl+shift+enter=unbind
+keybind = ctrl+shift+page_up=unbind
+keybind = ctrl+shift+page_down=unbind
+
+# Scroll keybinds (for use outside zellij)
+keybind = ctrl+shift+up=scroll_page_lines:-1
+keybind = ctrl+shift+down=scroll_page_lines:1
+# Copy URL under cursor to clipboard (works inside zellij without mouse)
+keybind = ctrl+shift+u=copy_url_to_clipboard
+
+keybind = alt+u=scroll_page_up
+keybind = alt+d=scroll_page_down
+keybind = alt+g=scroll_to_top
+keybind = alt+shift+g=scroll_to_bottom
diff --git a/dot_config/git/attributes b/dot_config/git/attributes
new file mode 100644
index 0000000..a9faf44
--- /dev/null
+++ b/dot_config/git/attributes
@@ -0,0 +1,26 @@
+*.java merge=mergiraf
+*.rs merge=mergiraf
+*.go merge=mergiraf
+*.js merge=mergiraf
+*.jsx merge=mergiraf
+*.json merge=mergiraf
+*.yml merge=mergiraf
+*.yaml merge=mergiraf
+*.toml merge=mergiraf
+*.html merge=mergiraf
+*.htm merge=mergiraf
+*.xhtml merge=mergiraf
+*.xml merge=mergiraf
+*.c merge=mergiraf
+*.h merge=mergiraf
+*.cc merge=mergiraf
+*.cpp merge=mergiraf
+*.hpp merge=mergiraf
+*.cs merge=mergiraf
+*.dart merge=mergiraf
+*.scala merge=mergiraf
+*.sbt merge=mergiraf
+*.ts merge=mergiraf
+*.tsx merge=mergiraf
+*.py merge=mergiraf
+*.lua merge=mergiraf
diff --git a/dot_config/git/config b/dot_config/git/config
new file mode 100644
index 0000000..87947d0
--- /dev/null
+++ b/dot_config/git/config
@@ -0,0 +1,135 @@
+[user]
+ email = sommerfeld@sommerfeld.dev
+ name = sommerfeld
+ signingkey = 3298945F717C85F8
+[push]
+ autoSetupRemote = true
+ followTags = true
+ gpgSign = if-asked
+[core]
+ whitespace = trailing-space,cr-at-eol
+ pager = delta
+[branch]
+ sort=-committerdate
+[diff]
+ tool = nvimdiff
+ algorithm = histogram
+ colorMoved = default
+ colorMovedWS = allow-indentation-change
+ mnemonicPrefix = true
+ relative = true
+ renameLimit = 10000
+[difftool]
+ prompt = false
+ trustExitCode = true
+[pager]
+ difftool = true
+[difftool "nvimdiff"]
+ cmd = $EDITOR -d $LOCAL $REMOTE
+[difftool "difftastic"]
+ cmd = difft "$MERGED" "$LOCAL" "abcdef1" "100644" "$REMOTE" "abcdef2" "100644"
+[rebase]
+ autoStash = true
+ autoSquash = true
+ updateRefs = true
+[fetch]
+ recurseSubmodules = on-demand
+ prune = true
+[rerere]
+ enabled = true
+[pull]
+ ff = only
+[merge]
+ tool = conflictmarker
+ conflictstyle = zdiff3
+[merge "mergiraf"]
+ name = mergiraf
+ driver = mergiraf merge --git %O %A %B -s %S -x %X -y %Y -p %P
+[mergetool]
+ keepBackup = false
+[mergetool "conflictmarker"]
+ cmd = $EDITOR "$MERGED"
+[commit]
+ gpgsign = true
+[tag]
+ gpgSign = true
+[status]
+ short = true
+ branch = true
+ showUntrackedFiles = all
+ showStash = true
+ submoduleSummary = true
+[help]
+ autocorrect = prompt
+[log]
+ abbrevCommit = true
+ date = short
+[interactive]
+ singleKey = true
+ diffFilter = delta --color-only
+[advice]
+ detachedHead = false
+ skippedCherryPicks = false
+[column]
+ ui = auto
+[format]
+ pretty = oneline
+[delta]
+ navigate = true
+ features = gruvmax-fang zebra-dark
+ dark = true
+ side-by-side = true
+ line-numbers = true
+ relative-paths = true
+ hyperlinks = true
+[absorb]
+ autoStageIfNothingStaged = true
+[alias]
+ ghost-rebase = "!sh -c 'b=$0; u=${1:-origin/master}; wt=$(mktemp -d -t rebwt.XXXXXX); git worktree add -f "$wt" "$b"; git -C "$wt" rebase "$u" || echo "Rebase incomplete"; git worktree remove --force "$wt"'"
+ a = add
+ ab=absorb --and-rebase
+ b = branch -vv
+ bd = branch -D
+ c = commit
+ ca = commit --amend --no-edit
+ cp = cherry-pick
+ d = diff
+ dft = -c diff.external=difft diff
+ dl = -c diff.external=difft log -p --ext-diff
+ ds = -c diff.external=difft show --ext-diff
+ dt = difftool
+ f = fetch
+ fa = fetch --all
+ ff = merge --ff-only
+ find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
+ fmt = clang-format -f
+ fp = push --force-with-lease
+ gone = !git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == \"[gone]\" {print $1}' | xargs -r git branch -D
+ l = log
+ lg = log --graph --oneline
+ lm = log --pretty=medium
+ m = merge
+ mt = mergetool
+ p = push
+ r = rebase
+ re = remote -v
+ ri = rebase -i
+ rt = restore
+ s = status
+ st = status --no-short
+ staged = diff --cached
+ sub = submodule
+ subf = submodule foreach
+ sw = switch
+ swc = switch -c
+ swd = switch -d
+ unstage = reset HEAD --
+ update=!git fetch && git merge --ff-only
+[includeIf "gitdir:/home/*/work/"]
+ path = config-work
+[includeIf "gitdir:~/doxfiles/.git"]
+ path = config-personal
+[includeIf "gitdir:~/dev/copilot/.git"]
+ path = config-personal
+[includeIf "gitdir:~/dev/personal/"]
+ path = config-personal
diff --git a/dot_config/git/ignore b/dot_config/git/ignore
new file mode 100644
index 0000000..07d5ee0
--- /dev/null
+++ b/dot_config/git/ignore
@@ -0,0 +1,21 @@
+*.mod
+*.tar.gz
+*.tar.zst
+*.zip
+.SRCINFO
+**/.cache/clangd/index/
+__pycache__/
+build*/
+/compile_commands.json
+node_modules/
+.env/
+.venv/
+env/
+venv/
+.gdbinit
+.lldbinit
+.nvim.lua
+.nvim/
+.ignore
+/tablegen_compile_commands.yml
+.git.copilot
diff --git a/dot_config/gtk-3.0/gtk.css b/dot_config/gtk-3.0/gtk.css
new file mode 100644
index 0000000..d506506
--- /dev/null
+++ b/dot_config/gtk-3.0/gtk.css
@@ -0,0 +1,36 @@
+.window-frame, .window-frame:backdrop {
+ box-shadow: 0 0 0 black;
+ border-style: none;
+ margin: 0;
+ border-radius: 0;
+}
+
+.titlebar {
+ border-radius: 0;
+}
+
+.window-frame.csd.popup {
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.13);
+}
+
+.header-bar {
+ background-image: none;
+ background-color: #ededed;
+ box-shadow: none;
+}
+headerbar .title {
+ opacity: 0;
+}
+/* Always use background color */
+window {
+ background-color: @theme_bg_color;
+}
+
+/* Fix tooltip background override */
+.tooltip {
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+.tooltip * {
+ background-color: transparent;
+}
diff --git a/dot_config/gtk-3.0/settings.ini b/dot_config/gtk-3.0/settings.ini
new file mode 100644
index 0000000..4339296
--- /dev/null
+++ b/dot_config/gtk-3.0/settings.ini
@@ -0,0 +1,17 @@
+[Settings]
+gtk-theme-name=Adwaita
+gtk-icon-theme-name=Adwaita
+gtk-font-name=Sans 11
+gtk-cursor-theme-name=Adwaita
+gtk-cursor-theme-size=0
+gtk-enable-event-sounds=0
+gtk-enable-input-feedback-sounds=0
+gtk-enable-primary-paste=false
+gtk-xft-antialias=1
+gtk-menu-popup-delay = 0
+gtk-xft-hinting=1
+gtk-xft-hintstyle=hintfull
+gtk-xft-rgba=rgb
+gtk-decoration-layout=menu:
+gtk-application-prefer-dark-theme=1
+gtk-error-bell = 0
diff --git a/dot_config/ipython/profile_default/ipython_config.py b/dot_config/ipython/profile_default/ipython_config.py
new file mode 100644
index 0000000..1ff9f35
--- /dev/null
+++ b/dot_config/ipython/profile_default/ipython_config.py
@@ -0,0 +1,5 @@
+c.InteractiveShell.banner1 = ""
+c.InteractiveShell.colors = "Linux"
+c.InteractiveShell.enable_html_pager = True
+c.TerminalInteractiveShell.confirm_exit = False
+c.TerminalInteractiveShell.extra_open_editor_shortcuts = True
diff --git a/dot_config/lsd/config.yaml b/dot_config/lsd/config.yaml
new file mode 100644
index 0000000..2421130
--- /dev/null
+++ b/dot_config/lsd/config.yaml
@@ -0,0 +1,20 @@
+# == Blocks ==
+# This specifies the columns and their order when using the long and the tree
+# layout.
+# Possible values: permission, user, group, size, size_value, date, name, inode
+blocks:
+ - permission
+ - user
+ - group
+ - size
+ - date
+ - name
+
+# == Icons ==
+icons:
+ # When to use icons.
+ # When "classic" is set, this is set to "never".
+ # Possible values: always, auto, never
+ when: never
+
+layout: oneline
diff --git a/dot_config/mako/config b/dot_config/mako/config
new file mode 100644
index 0000000..161f357
--- /dev/null
+++ b/dot_config/mako/config
@@ -0,0 +1,21 @@
+font=mono 8
+background-color=#282828
+text-color=#ebdbb2
+border-color=#fabd2f
+border-size=1
+border-radius=0
+padding=8
+margin=4
+width=300
+default-timeout=5000
+anchor=top-right
+
+[urgency=low]
+border-color=#8ec07c
+
+[urgency=normal]
+border-color=#fabd2f
+
+[urgency=critical]
+border-color=#fb4934
+default-timeout=0
diff --git a/dot_config/mimeapps.list b/dot_config/mimeapps.list
new file mode 100644
index 0000000..bd3a703
--- /dev/null
+++ b/dot_config/mimeapps.list
@@ -0,0 +1,101 @@
+[Default Applications]
+audio/x-vorbis+ogg=mpv.desktop
+audio/aac=mpv.desktop
+audio/x-aac=mpv.desktop
+audio/m4a=mpv.desktop
+audio/x-m4a=mpv.desktop
+audio/mp1=mpv.desktop
+audio/x-mp1=mpv.desktop
+audio/mp2=mpv.desktop
+audio/x-mp2=mpv.desktop
+audio/mp3=mpv.desktop
+audio/x-mp3=mpv.desktop
+audio/mpeg=mpv.desktop
+audio/x-mpeg=mpv.desktop
+audio/mpegurl=mpv.desktop
+audio/x-mpegurl=mpv.desktop
+audio/mpg=mpv.desktop
+audio/x-mpg=mpv.desktop
+audio/rn-mpeg=mpv.desktop
+audio/ogg=mpv.desktop
+audio/scpls=mpv.desktop
+audio/x-scpls=mpv.desktop
+audio/vnd.rn-realaudio=mpv.desktop
+audio/wav=mpv.desktop
+audio/x-pn-windows-pcm=mpv.desktop
+audio/x-realaudio=mpv.desktop
+audio/x-pn-realaudio=mpv.desktop
+audio/x-ms-wma=mpv.desktop
+audio/x-pls=mpv.desktop
+audio/x-wav=mpv.desktop
+audio/x-flac=mpv.desktop
+audio/x-shorten=mpv.desktop
+audio/x-ape=mpv.desktop
+audio/x-wavpack=mpv.desktop
+audio/x-tta=mpv.desktop
+audio/AMR=mpv.desktop
+audio/ac3=mpv.desktop
+audio/flac=mpv.desktop
+audio/mp4=mpv.desktop
+video/x-ogm+ogg=mpv.desktop
+video/mpeg=mpv.desktop
+video/x-mpeg=mpv.desktop
+video/x-mpeg2=mpv.desktop
+video/mp4=mpv.desktop
+video/msvideo=mpv.desktop
+video/x-msvideo=mpv.desktop
+video/ogg=mpv.desktop
+video/quicktime=mpv.desktop
+video/vnd.rn-realvideo=mpv.desktop
+video/x-ms-afs=mpv.desktop
+video/x-ms-asf=mpv.desktop
+video/x-ms-wmv=mpv.desktop
+video/x-ms-wmx=mpv.desktop
+video/x-ms-wvxvideo=mpv.desktop
+video/x-avi=mpv.desktop
+video/x-fli=mpv.desktop
+video/x-flv=mpv.desktop
+video/x-theora=mpv.desktop
+video/x-matroska=mpv.desktop
+video/webm=mpv.desktop
+video/mp2t=mpv.desktop
+image/x-nikon-nef=imv.desktop
+image/jpeg=imv.desktop
+image/png=imv.desktop
+image/gif=mpv.desktop
+image/svg+xml=librewolf.desktop
+text/markdown=nvim.desktop
+text/plain=nvim.desktop
+text/x-python=nvim.desktop
+text/x-chdr=nvim.desktop
+text/x-tex=nvim.desktop
+application/x-shellscript=nvim.desktop
+application/x-bittorrent=transmission.desktop
+application/pdf=org.pwmt.zathura-pdf-mupdf.desktop
+application/postscript=zathura-pdf-poppler.desktop;org.pwmt.zathura-pdf-mupdf.desktop
+application/rss+xml=rss.desktop
+x-scheme-handler/magnet=transmission.desktop
+x-scheme-handler/mailto=mail.desktop
+application/msword-template=xdot.desktop
+x-scheme-handler/http=librewolf.desktop
+x-scheme-handler/https=librewolf.desktop
+x-scheme-handler/chrome=librewolf.desktop
+text/html=librewolf.desktop
+application/x-extension-htm=librewolf.desktop
+application/x-extension-html=librewolf.desktop
+application/x-extension-shtml=librewolf.desktop
+application/xhtml+xml=librewolf.desktop
+application/x-extension-xhtml=librewolf.desktop
+application/x-extension-xht=librewolf.desktop
+
+[Added Associations]
+x-scheme-handler/http=librewolf.desktop;
+x-scheme-handler/https=librewolf.desktop;
+x-scheme-handler/chrome=librewolf.desktop;
+text/html=librewolf.desktop;
+application/x-extension-htm=librewolf.desktop;
+application/x-extension-html=librewolf.desktop;
+application/x-extension-shtml=librewolf.desktop;
+application/xhtml+xml=librewolf.desktop;
+application/x-extension-xhtml=librewolf.desktop;
+application/x-extension-xht=librewolf.desktop;
diff --git a/dot_config/mpv/input.conf b/dot_config/mpv/input.conf
new file mode 100644
index 0000000..679c4a2
--- /dev/null
+++ b/dot_config/mpv/input.conf
@@ -0,0 +1,96 @@
+# mpv keybindings
+#
+# Location of user-defined bindings: ~/.config/mpv/input.conf
+#
+# Lines starting with # are comments. Use SHARP to assign the # key.
+# Copy this file and uncomment and edit the bindings you want to change.
+#
+# List of commands and further details: DOCS/man/input.rst
+# List of special keys: --input-keylist
+# Keybindings testing mode: mpv --input-test --force-window --idle
+#
+# Use 'ignore' to unbind a key fully (e.g. 'ctrl+a ignore').
+#
+# Strings need to be quoted and escaped:
+# KEY show-text "This is a single backslash: \\ and a quote: \" !"
+#
+# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
+# the modifiers Shift, Ctrl, Alt and Meta (may not work on the terminal).
+#
+# The default keybindings are hardcoded into the mpv binary.
+# You can disable them completely with: --no-input-default-bindings
+
+# Mouse wheels, touchpad or other input devices that have axes
+# if the input devices supports precise scrolling it will also scale the
+# numeric value accordingly
+WHEEL_UP ignore
+WHEEL_DOWN ignore
+WHEEL_LEFT ignore
+WHEEL_RIGHT ignore
+
+## Seek units are in seconds, but note that these are limited by keyframes
+RIGHT seek 5
+LEFT seek -5
+UP seek 60
+DOWN seek -60
+# Do smaller, always exact (non-keyframe-limited), seeks with shift.
+# Don't show them on the OSD (no-osd).
+Shift+RIGHT no-osd seek 1 exact
+Shift+LEFT no-osd seek -1 exact
+Shift+UP no-osd seek 5 exact
+Shift+DOWN no-osd seek -5 exact
+# Skip to previous/next subtitle (subject to some restrictions; see manpage)
+Ctrl+LEFT no-osd sub-seek -1
+Ctrl+RIGHT no-osd sub-seek 1
+# Adjust timing to previous/next subtitle
+#Ctrl+Shift+LEFT sub-step -1
+#Ctrl+Shift+RIGHT sub-step 1
+[ multiply speed 1/1.1 # scale playback speed
+] multiply speed 1.1
+{ multiply speed 0.5
+} multiply speed 2.0
+BS set speed 1.0 # reset speed to normal
+Shift+BS revert-seek # undo previous (or marked) seek
+q quit
+Q quit-watch-later
+ESC set fullscreen no
+f cycle fullscreen # toggle fullscreen
+p cycle pause # toggle pause/playback mode
+> frame-step # advance one frame and pause
+< frame-back-step # go back by one frame and pause
+SPACE cycle pause
+O no-osd cycle-values osd-level 3 1 # cycle through OSD mode
+o show-progress
+P show-progress
+i script-binding stats/display-stats
+I script-binding stats/display-stats-toggle
+` script-binding console/enable
+. script-binding console/enable
+# z add sub-delay -0.1 # subtract 100 ms delay from subs
+# Z add sub-delay +0.1 # add
+#ctrl++ add audio-delay 0.100 # this changes audio/video sync
+#ctrl+- add audio-delay -0.100
+9 add volume -2
+0 add volume 2
+m cycle mute
+v cycle sub-visibility
+s cycle sub # cycle through subtitles
+S cycle sub down # ...backwards
+PLAY cycle pause
+PAUSE cycle pause
+PLAYPAUSE cycle pause
+PLAYONLY set pause no
+PAUSEONLY set pause yes
+STOP quit
+FORWARD seek 60
+REWIND seek -60
+NEXT playlist-next
+PREV playlist-prev
+VOLUME_UP add volume 2
+VOLUME_DOWN add volume -2
+MUTE cycle mute
+CLOSE_WIN quit
+CLOSE_WIN {encode} quit 4
+L cycle-values loop-file "inf" "no" # toggle infinite looping
+ctrl+c quit 4
+DEL script-binding osc/visibility # cycle OSC display
diff --git a/dot_config/mpv/mpv.conf b/dot_config/mpv/mpv.conf
new file mode 100644
index 0000000..890cbe3
--- /dev/null
+++ b/dot_config/mpv/mpv.conf
@@ -0,0 +1,110 @@
+# vim: syntax=config
+
+
+###########
+# General #
+###########
+
+input-ipc-server=/tmp/mpvsocket # listen for IPC on this socket
+save-position-on-quit # handled by a script
+
+# no-border # no window title bar
+msg-module # prepend module name to log messages
+msg-color # color log messages on terminal
+term-osd-bar # display a progress bar on the terminal
+use-filedir-conf # look for additional config files in the directory of the opened file
+#pause # no autoplay
+keep-open # keep the player open when a file's end is reached
+cursor-autohide=100 # autohide the curser after 1s
+prefetch-playlist=yes
+force-seekable=yes
+
+hls-bitrate=max # use max quality for HLS streams
+# not interested in getting videos with a resolution higher than 1080p
+ytdl-format=bv*[height<=1080]+ba/b[height<=1080]
+no-input-default-bindings
+script-opts=ytdl_hook-ytdl_path=yt-dlp
+
+[default]
+
+#########c
+# Cache #
+#########
+
+# Configure the cache to be really big (multiple GBs)
+# We have a lot of memory, so why not use it for something
+
+cache=yes
+# cache-default=1000000 # size in KB
+# cache-backbuffer=250000 # size in KB
+demuxer-max-bytes=1147483647 # ~1 GiB in bytes
+
+#############
+# Subtitles #
+#############
+
+sub-auto=fuzzy # external subs don't have to match the file name exactly to autoload
+sub-file-paths-append=ass # search for external subs in these relative subdirectories
+sub-file-paths-append=srt
+sub-file-paths-append=sub
+sub-file-paths-append=subs
+sub-file-paths-append=subtitles
+sub-file-paths-append=subtitle
+sub-file-paths-append=Ass # search for external subs in these relative subdirectories
+sub-file-paths-append=ASS # search for external subs in these relative subdirectories
+sub-file-paths-append=Srt
+sub-file-paths-append=SRT
+sub-file-paths-append=Sub
+sub-file-paths-append=SUB
+sub-file-paths-append=Subs
+sub-file-paths-append=SUBS
+sub-file-paths-append=Subtitles
+sub-file-paths-append=SUBTITLES
+sub-file-paths-append=Subtitle
+sub-file-paths-append=SUBTITLE
+
+
+#demuxer-mkv-subtitle-preroll # try to correctly show embedded subs when seeking
+#embeddedfonts=yes # use embedded fonts for SSA/ASS subs
+#sub-fix-timing=no # do not try to fix gaps (which might make it worse in some cases)
+#sub-ass-force-style=Kerning=yes # allows you to override style parameters of ASS scripts
+#sub-use-margins
+#sub-ass-force-margins
+
+#############
+# Languages #
+#############
+
+slang=en,eng # automatically select these subtitles (decreasing priority)
+# lang=en,eng # automatically select these audio tracks (decreasing priority)
+
+
+#########
+# Audio #
+#########
+
+audio-file-auto=fuzzy # external audio doesn't has to match the file name exactly to autoload
+audio-pitch-correction=yes # automatically insert scaletempo when playing with higher speed
+volume-max=200 # maximum volume in %, everything above 100 results in amplification
+volume=100 # default volume, 100 = unchanged
+
+
+################
+# Video Output #
+################
+
+hwdec=auto-safe
+
+[no-spoiler]
+osd-level=0
+no-osc
+no-osd-bar
+no-term-osd-bar
+quiet
+sub-auto=no
+
+[hq]
+profile=high-quality
+video-sync=display-resample
+interpolation
+tscale=oversample
diff --git a/dot_config/mpv/scripts/webtorrent-hook.lua b/dot_config/mpv/scripts/webtorrent-hook.lua
new file mode 100644
index 0000000..80d991f
--- /dev/null
+++ b/dot_config/mpv/scripts/webtorrent-hook.lua
@@ -0,0 +1,138 @@
+-- TODO prefetch if next in playlist?
+-- TODO handle torrent with multiple video files (if webtorrent can print json)
+-- - don't close kill webtorrent while still videos unplayed? or in playlist?
+-- - store titles/info when starting webtorrent and check stream-open-filename
+-- for any item in playlist to see if it matches stored entry
+
+local settings = {
+ close_webtorrent = true,
+ remove_files = true,
+ download_directory = "/tmp/webtorrent",
+ webtorrent_flags = "",
+ webtorrent_verbosity = "speed"
+}
+
+(require "mp.options").read_options(settings, "webtorrent-hook")
+
+local open_videos = {}
+
+-- http://lua-users.org/wiki/StringRecipes
+local function ends_with(str, ending)
+ return ending == "" or str:sub(-#ending) == ending
+end
+
+-- https://stackoverflow.com/questions/132397/get-back-the-output-of-os-execute-in-lua
+function os.capture(cmd, decolorize, raw)
+ if decolorize then
+ -- https://github.com/webtorrent/webtorrent-cli/issues/132
+ -- TODO webtorrent should have a way to just print json information with
+ -- no colors
+ -- https://stackoverflow.com/questions/19296667/remove-ansi-color-codes-from-a-text-file-using-bash/30938702#30938702
+ cmd = cmd .. " | sed -r 's/\\x1B\\[(([0-9]{1,2})?(;)?([0-9]{1,2})?)?[m,K,H,f,J]//g'"
+ end
+ local f = assert(io.popen(cmd, 'r'))
+ local s = assert(f:read('*a'))
+ f:close()
+ if raw then return s end
+ s = string.gsub(s, '^%s+', '')
+ s = string.gsub(s, '%s+$', '')
+ -- s = string.gsub(s, '[\n\r]+', ' ')
+ return s
+end
+
+function read_file(file)
+ local fh = assert(io.open(file, "rb"))
+ local contents = fh:read("*all")
+ fh:close()
+ return contents
+end
+
+function play_torrent()
+ local url = mp.get_property("stream-open-filename")
+ if (url:find("magnet:") == 1 or url:find("peerflix://") == 1
+ or url:find("webtorrent://") == 1 or ends_with(url, "torrent")) then
+ if url:find("webtorrent://") == 1 then
+ url = url:sub(14)
+ end
+ if url:find("peerflix://") == 1 then
+ url = url:sub(12)
+ end
+
+ os.execute("mkdir -p " .. settings.download_directory)
+ -- don't reuse files (so multiple mpvs works)
+ local output_file = settings.download_directory
+ .. "/webtorrent-output-" .. mp.get_time() .. ".log"
+ -- --keep-seeding is to prevent webtorrent from quitting once the download
+ -- is done
+ local webtorrent_command = "webtorrent "
+ .. settings.webtorrent_flags
+ .. " --out '" .. settings.download_directory .. "' --keep-seeding \""
+ .. url .. "\" > " .. output_file .. " 2>&1 & echo $!"
+ mp.msg.info("Starting webtorrent server")
+ mp.msg.info(webtorrent_command)
+ local pid = os.capture(webtorrent_command)
+ mp.msg.info("Waiting for webtorrent server")
+
+ local url_command = "tail -f " .. output_file
+ .. " | awk '/Server running at:/ {print $4; exit}'"
+ local url = os.capture(url_command, true)
+ mp.msg.info("Webtorrent server is up")
+
+ local title_command = "awk '/(Seeding|Downloading): / "
+ .. "{gsub(/(Seeding|Downloading): /, \"\"); print; exit}' "
+ .. output_file
+ local title = os.capture(title_command, true)
+ mp.msg.info("Setting media title to: " .. title)
+ mp.set_property("force-media-title", title)
+
+ local path
+ if title then
+ path = settings.download_directory .. "/\"" .. title .. "\""
+ end
+ open_videos[url] = {title=title,path=path,pid=pid}
+
+ mp.set_property("stream-open-filename", url)
+
+ if settings.webtorrent_verbosity == "speed" then
+ local printer_pid
+ local printer_pid_file = settings.download_directory
+ .. "/webtorrent-printer-" .. mp.get_time() .. ".pid"
+ os.execute("tail -f " .. output_file
+ .. " | awk '/Speed:/' ORS='\r' & echo -n $! > "
+ .. printer_pid_file)
+ printer_pid = read_file(printer_pid_file)
+ mp.register_event("file-loaded",
+ function()
+ os.execute("kill " .. printer_pid)
+ end
+ )
+ end
+ end
+end
+
+function webtorrent_cleanup()
+ local url = mp.get_property("stream-open-filename")
+ if settings.close_webtorrent and open_videos[url] then
+ local title = open_videos[url].title
+ local path = open_videos[url].path
+ local pid = open_videos[url].pid
+
+ if pid then
+ mp.msg.info("Closing webtorrent for " .. title .. " with pid " .. pid)
+ os.execute("kill " .. pid)
+ end
+
+ if settings.remove_files then
+ if path then
+ mp.msg.info("Removing media file for " .. title .. " with path " .. path)
+ os.execute("rm -r " .. path)
+ end
+ end
+
+ open_videos[url] = {}
+ end
+end
+
+mp.add_hook("on_load", 50, play_torrent)
+
+mp.add_hook("on_unload", 10, webtorrent_cleanup)
diff --git a/dot_config/npm/npmrc b/dot_config/npm/npmrc
new file mode 100644
index 0000000..ddb589b
--- /dev/null
+++ b/dot_config/npm/npmrc
@@ -0,0 +1,3 @@
+prefix=${XDG_DATA_HOME}/npm
+cache=${XDG_CACHE_HOME}/npm
+tmp=${XDG_RUNTIME_DIR}/npm
diff --git a/dot_config/nvim/after/ftplugin/gitcommit.lua b/dot_config/nvim/after/ftplugin/gitcommit.lua
new file mode 100644
index 0000000..e670035
--- /dev/null
+++ b/dot_config/nvim/after/ftplugin/gitcommit.lua
@@ -0,0 +1,3 @@
+vim.wo.spell = true
+vim.b.undo_ftplugin = (vim.b.undo_ftplugin or "") .. "|setlocal spell<"
+vim.cmd([[match ErrorMsg /\%1l.\%>50v/]])
diff --git a/dot_config/nvim/after/ftplugin/gitrebase.lua b/dot_config/nvim/after/ftplugin/gitrebase.lua
new file mode 100644
index 0000000..1bfcd69
--- /dev/null
+++ b/dot_config/nvim/after/ftplugin/gitrebase.lua
@@ -0,0 +1,16 @@
+local function nvmap(l, r, desc)
+ vim.keymap.set(
+ { "n", "v" },
+ l,
+ ":" .. r .. "<CR>",
+ { buffer = 0, desc = "[G]it rebase " .. desc }
+ )
+end
+
+nvmap("gc", "Cycle", "[C]ycle")
+nvmap("gp", "Pick", "[P]ick")
+nvmap("ge", "Edit", "[E]dit")
+nvmap("gf", "Fixup", "[F]ixup")
+nvmap("gd", "Drop", "[D]rop")
+nvmap("gs", "Squash", "[S]quash")
+nvmap("gr", "Reword", "[R]eword")
diff --git a/dot_config/nvim/after/ftplugin/mail.lua b/dot_config/nvim/after/ftplugin/mail.lua
new file mode 100644
index 0000000..9fe1500
--- /dev/null
+++ b/dot_config/nvim/after/ftplugin/mail.lua
@@ -0,0 +1 @@
+vim.wo.spell = true
diff --git a/dot_config/nvim/after/ftplugin/markdown.lua b/dot_config/nvim/after/ftplugin/markdown.lua
new file mode 100644
index 0000000..9fe1500
--- /dev/null
+++ b/dot_config/nvim/after/ftplugin/markdown.lua
@@ -0,0 +1 @@
+vim.wo.spell = true
diff --git a/dot_config/nvim/after/ftplugin/text.lua b/dot_config/nvim/after/ftplugin/text.lua
new file mode 100644
index 0000000..2179c42
--- /dev/null
+++ b/dot_config/nvim/after/ftplugin/text.lua
@@ -0,0 +1,3 @@
+vim.wo.spell = true
+vim.bo.formatoptions = vim.bo.formatoptions .. "t"
+vim.bo.commentstring = "# %s"
diff --git a/dot_config/nvim/after/lsp/clangd.lua b/dot_config/nvim/after/lsp/clangd.lua
new file mode 100644
index 0000000..e9868ae
--- /dev/null
+++ b/dot_config/nvim/after/lsp/clangd.lua
@@ -0,0 +1,57 @@
+local function validate_bufnr(bufnr)
+ vim.validate("bufnr", bufnr, "number")
+ return bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr
+end
+
+local function switch_source_header_splitcmd(bufnr, splitcmd)
+ local method_name = "textDocument/switchSourceHeader"
+ bufnr = validate_bufnr(bufnr)
+ local client = vim.lsp.get_clients({ bufnr = bufnr, name = "clangd" })[1]
+ if not client then
+ return vim.notify(
+ ("method %s is not supported by any servers active on the current buffer"):format(
+ method_name
+ )
+ )
+ end
+ local params = vim.lsp.util.make_text_document_params(bufnr)
+ client.request(method_name, params, function(err, result)
+ if err then
+ error(tostring(err))
+ end
+ if not result then
+ vim.notify("corresponding file cannot be determined")
+ return
+ end
+ vim.api.nvim_cmd({
+ cmd = splitcmd,
+ args = { vim.uri_to_fname(result) },
+ }, {})
+ end, bufnr)
+end
+
+return {
+ capabilities = {
+ offsetEncoding = { "utf-16" },
+ },
+ on_attach = function(_, bufnr)
+ local function nmap(l, r, desc)
+ vim.keymap.set("n", l, r, { buffer = bufnr, desc = desc })
+ end
+ nmap("gH", function()
+ switch_source_header_splitcmd(bufnr, "edit")
+ end, "[G]o to [H]eader")
+ nmap("gvH", function()
+ switch_source_header_splitcmd(bufnr, "vsplit")
+ end, "[G]o in a [V]ertical split to [H]eader")
+ nmap("gxH", function()
+ switch_source_header_splitcmd(bufnr, "split")
+ end, "[G]o in a [X]horizontal split to [H]eader")
+ nmap("gtH", function()
+ switch_source_header_splitcmd(bufnr, "tabedit")
+ end, "[G]o in a [T]ab to [H]eader")
+ end,
+ init_options = {
+ clangdFileStatus = true,
+ },
+}
diff --git a/dot_config/nvim/after/lsp/fortls.lua b/dot_config/nvim/after/lsp/fortls.lua
new file mode 100644
index 0000000..8899dd2
--- /dev/null
+++ b/dot_config/nvim/after/lsp/fortls.lua
@@ -0,0 +1,10 @@
+return {
+ cmd = {
+ "fortls",
+ "--notify_init",
+ "--hover_signature",
+ "--hover_language=fortran",
+ "--use_signature_help",
+ "--autocomplete_no_snippets",
+ },
+}
diff --git a/dot_config/nvim/after/lsp/lua_ls.lua b/dot_config/nvim/after/lsp/lua_ls.lua
new file mode 100644
index 0000000..af0e681
--- /dev/null
+++ b/dot_config/nvim/after/lsp/lua_ls.lua
@@ -0,0 +1,9 @@
+return {
+ settings = {
+ Lua = {
+ diagnostics = {
+ disable = { "missing-fields" },
+ },
+ },
+ },
+}
diff --git a/dot_config/nvim/filetype.lua b/dot_config/nvim/filetype.lua
new file mode 100644
index 0000000..51f8646
--- /dev/null
+++ b/dot_config/nvim/filetype.lua
@@ -0,0 +1,14 @@
+vim.filetype.add({
+ extension = {
+ eml = "mail",
+ inc = "cpp",
+ def = "cpp",
+ Jenkinsfile = "groovy",
+ },
+ filename = {
+ [".devcontainer.json"] = "jsonc",
+ },
+ pattern = {
+ [".*/.github/workflows/.*%.ya?ml"] = "yaml.ghaction",
+ },
+})
diff --git a/dot_config/nvim/init.lua b/dot_config/nvim/init.lua
new file mode 100644
index 0000000..fc3271b
--- /dev/null
+++ b/dot_config/nvim/init.lua
@@ -0,0 +1,126 @@
+require("config.options")
+
+_G.P = function(v)
+ print(vim.inspect(v))
+ return v
+end
+
+-- Pre-load globals (must be set before plugins load)
+vim.g.copilot_nes_debounce = 500
+
+-- Build hooks for plugins that need post-install steps
+vim.api.nvim_create_autocmd("User", {
+ pattern = "PackChanged",
+ callback = function(ev)
+ if ev.data.kind ~= "install" and ev.data.kind ~= "update" then
+ return
+ end
+ if ev.data.spec.name == "markdown-preview.nvim" then
+ vim.system({ "yarn", "install" }, { cwd = ev.data.path .. "/app" })
+ end
+ end,
+})
+
+local gh = function(x)
+ return "https://github.com/" .. x
+end
+
+vim.pack.add({
+ -- UI
+ gh("ellisonleao/gruvbox.nvim"),
+ gh("saghen/blink.indent"),
+ gh("nvim-lualine/lualine.nvim"),
+ gh("AndreM222/copilot-lualine"),
+
+ -- Treesitter
+ { src = gh("nvim-treesitter/nvim-treesitter"), version = "main" },
+ gh("RRethy/nvim-treesitter-endwise"),
+ gh("nvim-treesitter/nvim-treesitter-context"),
+ gh("JoosepAlviste/nvim-ts-context-commentstring"),
+ gh("aaronik/treewalker.nvim"),
+ gh("LiadOz/nvim-dap-repl-highlights"),
+
+ -- Completion
+ gh("saghen/blink.compat"),
+ { src = gh("saghen/blink.cmp"), version = vim.version.range("*") },
+ gh("rafamadriz/friendly-snippets"),
+ gh("fang2hou/blink-copilot"),
+ gh("rcarriga/cmp-dap"),
+ gh("xzbdmw/colorful-menu.nvim"),
+ { src = gh("saghen/blink.pairs"), version = vim.version.range("*") },
+ { src = gh("saghen/blink.download"), version = "main" },
+
+ -- Editing
+ gh("nmac427/guess-indent.nvim"),
+ gh("kylechui/nvim-surround"),
+ gh("chrisgrieser/nvim-various-textobjs"),
+ gh("monaqa/dial.nvim"),
+ gh("ThePrimeagen/refactoring.nvim"),
+ gh("nvim-lua/plenary.nvim"),
+
+ -- Git
+ gh("akinsho/git-conflict.nvim"),
+ gh("NeogitOrg/neogit"),
+ gh("ruifm/gitlinker.nvim"),
+ gh("lewis6991/gitsigns.nvim"),
+
+ -- LSP
+ gh("folke/lazydev.nvim"),
+ gh("neovim/nvim-lspconfig"),
+ gh("j-hui/fidget.nvim"),
+ gh("williamboman/mason.nvim"),
+ gh("williamboman/mason-lspconfig.nvim"),
+ gh("WhoIsSethDaniel/mason-tool-installer.nvim"),
+ gh("stevearc/conform.nvim"),
+ gh("mrcjkb/rustaceanvim"),
+ gh("mfussenegger/nvim-lint"),
+ gh("rachartier/tiny-inline-diagnostic.nvim"),
+
+ -- Debug
+ { src = gh("miroshQa/debugmaster.nvim"), version = "dashboard" },
+ gh("mfussenegger/nvim-dap"),
+ gh("theHamsta/nvim-dap-virtual-text"),
+ gh("jay-babu/mason-nvim-dap.nvim"),
+
+ -- Runner
+ gh("stevearc/overseer.nvim"),
+
+ -- Search
+ { src = gh("ibhagwan/fzf-lua"), version = "main" },
+
+ -- Session
+ gh("rmagatti/auto-session"),
+
+ -- AI
+ gh("zbirenbaum/copilot.lua"),
+ gh("copilotlsp-nvim/copilot-lsp"),
+
+ -- Misc
+ gh("iamcco/markdown-preview.nvim"),
+ gh("mrjones2014/smart-splits.nvim"),
+ gh("folke/which-key.nvim"),
+ gh("stevearc/quicker.nvim"),
+ gh("stevearc/oil.nvim"),
+}, { confirm = false })
+
+-- Colorscheme (must be set immediately after plugins are on rtp)
+require("gruvbox").setup({})
+vim.o.background = "dark"
+vim.cmd.colorscheme("gruvbox")
+
+-- Plugin configurations (order matters for dependencies)
+require("plugins.ui")
+require("plugins.treesitter")
+require("plugins.completion")
+require("plugins.editing")
+require("plugins.git")
+require("plugins.lsp")
+require("plugins.debug")
+require("plugins.runner")
+require("plugins.search")
+require("plugins.session")
+require("plugins.ai")
+require("plugins.init")
+
+require("config.keymaps")
+require("config.autocmds")
diff --git a/dot_config/nvim/lua/config/autocmds.lua b/dot_config/nvim/lua/config/autocmds.lua
new file mode 100644
index 0000000..2d1ea9b
--- /dev/null
+++ b/dot_config/nvim/lua/config/autocmds.lua
@@ -0,0 +1,128 @@
+local function augroup(name)
+ return vim.api.nvim_create_augroup(name, { clear = true })
+end
+
+local autocmd = vim.api.nvim_create_autocmd
+
+-- Check if we need to reload the file when it changed
+autocmd({ "FocusGained", "TermClose", "TermLeave" }, {
+ group = augroup("checktime"),
+ callback = function()
+ if vim.o.buftype ~= "nofile" then
+ vim.cmd("checktime")
+ end
+ end,
+})
+
+-- Highlight on yank
+autocmd("TextYankPost", {
+ group = augroup("highlight_yank"),
+ callback = vim.hl.on_yank,
+})
+
+-- go to last loc when opening a buffer
+autocmd("BufReadPost", {
+ group = augroup("last_loc"),
+ callback = function(event)
+ local exclude = { "gitcommit" }
+ local buf = event.buf
+ if
+ vim.tbl_contains(exclude, vim.bo[buf].filetype) or vim.b[buf].last_loc
+ then
+ return
+ end
+ vim.b[buf].last_loc = true
+ local mark = vim.api.nvim_buf_get_mark(buf, '"')
+ local lcount = vim.api.nvim_buf_line_count(buf)
+ if mark[1] > 0 and mark[1] <= lcount then
+ pcall(vim.api.nvim_win_set_cursor, 0, mark)
+ end
+ end,
+})
+
+-- close some filetypes with <q>
+autocmd("FileType", {
+ group = augroup("close_with_q"),
+ pattern = {
+ "PlenaryTestPopup",
+ "checkhealth",
+ "dbout",
+ "gitsigns-blame",
+ "help",
+ "lspinfo",
+ "notify",
+ "qf",
+ "startuptime",
+ },
+ callback = function(event)
+ vim.bo[event.buf].buflisted = false
+ vim.schedule(function()
+ vim.keymap.set("n", "q", function()
+ vim.cmd("close")
+ pcall(vim.api.nvim_buf_delete, event.buf, { force = true })
+ end, {
+ buffer = event.buf,
+ silent = true,
+ desc = "Quit buffer",
+ })
+ end)
+ end,
+})
+
+-- make it easier to close man-files when opened inline
+autocmd("FileType", {
+ group = augroup("man_unlisted"),
+ pattern = { "man" },
+ callback = function(event)
+ vim.bo[event.buf].buflisted = false
+ end,
+})
+
+-- Auto create dir when saving a file, in case some intermediate directory does not exist
+autocmd({ "BufWritePre" }, {
+ group = augroup("auto_create_dir"),
+ callback = function(event)
+ if event.match:match("^%w%w+:[\\/][\\/]") then
+ return
+ end
+ local file = vim.uv.fs_realpath(event.match) or event.match
+ vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p")
+ end,
+})
+
+autocmd("BufWritePost", {
+ group = augroup("sway"),
+ pattern = "*/sway/config",
+ command = "!swaymsg reload",
+})
+autocmd("BufWritePost", {
+ group = augroup("waybar"),
+ pattern = "*/waybar/*",
+ command = "!killall -SIGUSR2 waybar",
+})
+autocmd("BufWritePost", {
+ group = augroup("xdg-user-dirs"),
+ pattern = "user-dirs.dirs,user-dirs.locale",
+ command = "!xdg-user-dirs-update",
+})
+autocmd("BufWritePost", {
+ group = augroup("mako"),
+ pattern = "*/mako/config",
+ command = "!makoctl reload",
+})
+autocmd("BufWritePost", {
+ group = augroup("fc-cache"),
+ pattern = "fonts.conf",
+ command = "!fc-cache",
+})
+
+autocmd("FileType", {
+ group = augroup("treesitter_start"),
+ pattern = { "*" },
+ callback = function()
+ if pcall(vim.treesitter.start) then
+ vim.wo.foldexpr = "v:lua.vim.treesitter.foldexpr()"
+ vim.bo.indentexpr = "v:lua.vim.treesitter.indentexpr()"
+ end
+ end,
+})
diff --git a/dot_config/nvim/lua/config/keymaps.lua b/dot_config/nvim/lua/config/keymaps.lua
new file mode 100644
index 0000000..366a37e
--- /dev/null
+++ b/dot_config/nvim/lua/config/keymaps.lua
@@ -0,0 +1,136 @@
+local function map(mode, l, r, desc)
+ vim.keymap.set(mode, l, r, { desc = desc })
+end
+local function cmd(mode, l, r, desc)
+ map(mode, l, "<cmd>" .. r .. "<cr>", desc)
+end
+local function cmdi(mode, l, r, desc)
+ map(mode, l, ":" .. r, desc)
+end
+local function nmap(l, r, desc)
+ map("n", l, r, desc)
+end
+local function vmap(l, r, desc)
+ map("v", l, r, desc)
+end
+local function nvmap(l, r, desc)
+ map({ "n", "v" }, l, r, desc)
+end
+local function ncmd(l, r, desc)
+ cmd("n", l, r, desc)
+end
+local function ncmdi(l, r, desc)
+ cmdi("n", l, r, desc)
+end
+local function vcmdi(l, r, desc)
+ cmdi("v", l, r, desc)
+end
+
+ncmd("<esc>", "nohlsearch")
+
+-- highlight last inserted text
+nmap("gV", "`[v`]")
+
+nmap("<down>", "<c-e>")
+nmap("<up>", "<c-y>")
+
+-- paste over selection without clobbering registers
+vmap("p", '"_dP')
+
+-- Find and Replace binds
+ncmdi("<localleader>s", "%s/")
+vcmdi("<localleader>s", "s/")
+
+ncmd("<leader>x", "wall")
+ncmd("<leader>z", "wqall")
+ncmd("<leader>q", "quitall")
+
+vim.keymap.set(
+ "t",
+ "<esc><esc>",
+ "<c-\\><c-n>",
+ { silent = true, noremap = true, desc = "Exit terminal mode" }
+)
+
+nmap("[w", function()
+ vim.diagnostic.jump({
+ count = -vim.v.count1,
+ severity = { min = vim.diagnostic.severity.WARN },
+ })
+end)
+nmap("]w", function()
+ vim.diagnostic.jump({
+ count = vim.v.count1,
+ severity = { min = vim.diagnostic.severity.WARN },
+ })
+end)
+nmap("[e", function()
+ vim.diagnostic.jump({
+ count = -vim.v.count1,
+ severity = vim.diagnostic.severity.ERROR,
+ })
+end)
+nmap("]e", function()
+ vim.diagnostic.jump({
+ count = vim.v.count1,
+ severity = vim.diagnostic.severity.ERROR,
+ })
+end)
+
+nmap("yp", function()
+ vim.fn.setreg("+", vim.fn.expand("%"))
+end, "[Y]ank [P]ath")
+
+local doas_exec = function(_cmd)
+ vim.fn.inputsave()
+ local password = vim.fn.inputsecret("Password: ")
+ vim.fn.inputrestore()
+ if not password or #password == 0 then
+ vim.notify("Invalid password, doas aborted", vim.log.levels.WARN)
+ return false
+ end
+ local out = vim.fn.system(string.format("doas -S %s", _cmd), password .. "\n")
+ if vim.v.shell_error ~= 0 then
+ print("\r\n")
+ vim.notify(out, vim.log.levels.ERROR)
+ return false
+ end
+ return true
+end
+
+vim.api.nvim_create_user_command("DoasWrite", function(opts)
+ local tmpfile = vim.fn.tempname()
+ local filepath
+ if #opts.fargs == 1 then
+ filepath = opts.fargs[1]
+ else
+ filepath = vim.fn.expand("%")
+ end
+ if not filepath or #filepath == 0 then
+ vim.notify("E32: No file name", vim.log.levels.ERROR)
+ return
+ end
+ -- `bs=1048576` is equivalent to `bs=1M` for GNU dd or `bs=1m` for BSD dd
+ -- Both `bs=1M` and `bs=1m` are non-POSIX
+ local _cmd = string.format(
+ "dd if=%s of=%s bs=1048576",
+ vim.fn.shellescape(tmpfile),
+ vim.fn.shellescape(filepath)
+ )
+ -- no need to check error as this fails the entire function
+ vim.api.nvim_exec2(string.format("write! %s", tmpfile), { output = true })
+ if doas_exec(_cmd) then
+ -- refreshes the buffer and prints the "written" message
+ vim.cmd.checktime()
+ -- exit command mode
+ vim.api.nvim_feedkeys(
+ vim.api.nvim_replace_termcodes("<Esc>", true, false, true),
+ "n",
+ true
+ )
+ end
+ vim.fn.delete(tmpfile)
+end, {
+ nargs = "?",
+ desc = "Write using doas permissions",
+})
diff --git a/dot_config/nvim/lua/config/options.lua b/dot_config/nvim/lua/config/options.lua
new file mode 100644
index 0000000..b2409b5
--- /dev/null
+++ b/dot_config/nvim/lua/config/options.lua
@@ -0,0 +1,114 @@
+local opt = vim.o
+
+-- Persistence
+opt.undofile = true -- persist undo history across sessions
+opt.swapfile = false -- no swap files; rely on undofile for recovery
+
+-- Gutter
+opt.number = true -- show line numbers
+opt.cursorline = true -- highlight current line
+opt.signcolumn = "auto:2" -- up to 2 sign columns, auto-hide when empty
+opt.laststatus = 3 -- single global statusline
+
+-- Indentation (defaults; guess-indent overrides per-buffer)
+opt.expandtab = true -- spaces instead of tabs
+opt.shiftround = true -- round indent to shiftwidth multiples
+opt.shiftwidth = 0 -- follow tabstop value
+opt.softtabstop = -1 -- follow shiftwidth value
+opt.tabstop = 4 -- 4-space tabs
+
+-- Search
+opt.gdefault = true -- substitute all matches per line by default
+opt.ignorecase = true -- case-insensitive search
+opt.smartcase = true -- ...unless query has uppercase
+
+-- Splits
+opt.splitbelow = true -- horizontal splits open below
+opt.splitright = true -- vertical splits open right
+opt.splitkeep = "screen" -- keep text position stable on split
+
+-- Line wrapping
+opt.linebreak = true -- wrap at word boundaries
+opt.breakindent = true -- indent wrapped lines
+opt.textwidth = 80 -- wrap column for formatting
+opt.colorcolumn = "+1" -- highlight column after textwidth
+vim.opt.formatoptions:remove("t") -- don't auto-wrap text while typing
+
+-- Messages
+opt.messagesopt = "wait:5000,history:500" -- message display timing and history depth
+
+vim.opt.shortmess:append({ a = true }) -- abbreviate all file messages
+
+-- Timing
+opt.updatetime = 250 -- CursorHold delay (ms); affects gitsigns, diagnostics
+opt.timeoutlen = 300 -- key sequence timeout (ms); affects which-key popup delay
+
+-- Completion and scrolling
+vim.opt.completeopt = { "menuone", "noselect", "popup", "fuzzy", "nearest" }
+opt.scrolloff = 999 -- keep cursor vertically centered
+opt.sidescrolloff = 5 -- horizontal scroll margin
+
+-- Clipboard (deferred to avoid blocking startup on clipboard detection)
+vim.schedule(function()
+ opt.clipboard = vim.env.SSH_TTY and "" or "unnamedplus"
+end)
+
+opt.mouse = "a" -- enable mouse in all modes
+
+vim.opt.wildmode = { "longest", "full" } -- cmdline completion: longest match, then full menu
+
+vim.opt.cpoptions:remove({ "_" }) -- cw changes to end of word (not compatible vi behavior)
+
+-- Visible whitespace
+vim.opt.listchars = {
+ tab = "> ",
+ space = "·",
+ extends = ">",
+ precedes = "<",
+ nbsp = "+",
+}
+opt.list = true -- show whitespace characters
+
+opt.confirm = true -- confirm before closing unsaved buffers
+
+opt.virtualedit = "block" -- allow cursor past end of line in visual block
+opt.spelloptions = "camel" -- treat camelCase words as separate words for spell check
+
+-- Disable unused providers
+vim.g.loaded_node_provider = 0
+vim.g.loaded_perl_provider = 0
+vim.g.loaded_python3_provider = 0
+
+-- Diff
+vim.opt.diffopt:append({
+ hiddenoff = true, -- disable diff on hidden buffers
+ iblank = true, -- ignore blank line changes
+ iwhiteall = true, -- ignore all whitespace changes
+ algorithm = "histogram", -- better diff algorithm
+})
+
+-- Use ripgrep for :grep
+if vim.fn.executable("rg") then
+ opt.grepprg = "rg\\ --vimgrep"
+ opt.grepformat = "%f:%l:%c:%m"
+end
+
+-- Popup and window borders
+opt.pumblend = 20 -- popup menu transparency
+opt.pumborder = "rounded"
+opt.winborder = "rounded" -- default border for floating windows
+
+-- Folding: set up treesitter-based folds but start with them closed.
+-- Autocmd in autocmds.lua sets foldexpr per-buffer when treesitter is available.
+vim.o.foldmethod = "expr"
+vim.o.foldenable = false
+
+vim.g.mapleader = " "
+vim.g.maplocalleader = ","
+
+-- Session persistence (for auto-session)
+opt.sessionoptions =
+ "blank,buffers,curdir,help,tabpages,winsize,winpos,terminal,localoptions"
+
+opt.exrc = true -- source project-local .nvim.lua files
+
diff --git a/dot_config/nvim/lua/plugins/ai.lua b/dot_config/nvim/lua/plugins/ai.lua
new file mode 100644
index 0000000..8c213b5
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/ai.lua
@@ -0,0 +1,34 @@
+require("copilot").setup({
+ suggestion = { enabled = false },
+ panel = { enabled = false },
+ server_opts_overrides = {
+ settings = {
+ telemetry = {
+ telemetryLevel = "off",
+ },
+ },
+ },
+ nes = {
+ enabled = true,
+ keymap = {
+ accept_and_goto = "<leader>p",
+ accept = false,
+ dismiss = "<Esc>",
+ },
+ },
+})
+
+-- Accept NES in insert mode (copilot.lua only binds normal mode)
+vim.keymap.set("i", "<C-f>", function()
+ local ok, nes = pcall(require, "copilot-lsp.nes")
+ if ok and nes.apply_pending_nes() then
+ return
+ end
+ -- Fallback: native <C-f> (scroll window forward)
+ local key = vim.api.nvim_replace_termcodes("<C-f>", true, false, true)
+ vim.api.nvim_feedkeys(key, "n", false)
+end, { desc = "Accept Copilot NES / scroll forward" })
+
+vim.keymap.set("n", "<leader>tc", function()
+ require("copilot.command").toggle()
+end, { desc = "[T]oggle [C]opilot attachment" })
diff --git a/dot_config/nvim/lua/plugins/completion.lua b/dot_config/nvim/lua/plugins/completion.lua
new file mode 100644
index 0000000..df24a5d
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/completion.lua
@@ -0,0 +1,86 @@
+require("blink.compat").setup({})
+
+require("blink.cmp").setup({
+ keymap = {
+ preset = "cmdline",
+ ["<CR>"] = { "accept", "fallback" },
+ },
+ appearance = {
+ use_nvim_cmp_as_default = true,
+ },
+ completion = {
+ menu = {
+ draw = {
+ columns = { { "kind_icon" }, { "label", gap = 1 } },
+ components = {
+ label = {
+ text = function(ctx)
+ return require("colorful-menu").blink_components_text(ctx)
+ end,
+ highlight = function(ctx)
+ return require("colorful-menu").blink_components_highlight(ctx)
+ end,
+ },
+ },
+ },
+ },
+ list = {
+ selection = {
+ preselect = function()
+ return not require("blink.cmp").snippet_active({ direction = 1 })
+ end,
+ },
+ },
+ documentation = { auto_show = true },
+ },
+ signature = {
+ enabled = true,
+ trigger = {
+ enabled = true,
+ show_on_keyword = true,
+ show_on_insert = true,
+ },
+ },
+ sources = {
+ default = { "lazydev", "lsp", "copilot", "snippets", "path", "buffer" },
+ per_filetype = {
+ ["dap-repl"] = { "dap" },
+ },
+ providers = {
+ path = {
+ opts = {
+ get_cwd = vim.fn.getcwd,
+ },
+ },
+ copilot = {
+ name = "copilot",
+ module = "blink-copilot",
+ score_offset = 100,
+ async = true,
+ },
+ lazydev = {
+ name = "LazyDev",
+ module = "lazydev.integrations.blink",
+ score_offset = 100,
+ },
+ dap = { name = "dap", module = "blink.compat.source" },
+ },
+ },
+})
+
+require("blink.pairs").setup({
+ mappings = {
+ disabled_filetypes = {},
+ },
+ highlights = {
+ groups = {
+ "BlinkIndentOrange",
+ "BlinkIndentViolet",
+ "BlinkIndentBlue",
+ "BlinkIndentRed",
+ "BlinkIndentCyan",
+ "BlinkIndentYellow",
+ "BlinkIndentGreen",
+ },
+ },
+})
diff --git a/dot_config/nvim/lua/plugins/debug.lua b/dot_config/nvim/lua/plugins/debug.lua
new file mode 100644
index 0000000..bef0d1c
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/debug.lua
@@ -0,0 +1,75 @@
+vim.keymap.set("n", "<leader>td", function()
+ require("debugmaster").mode.toggle()
+end, { desc = "[T]oggle [D]ebug mode" })
+
+local dap = require("dap")
+
+local function get_env_vars()
+ local variables = vim.fn.environ()
+ table.insert(variables, { ASAN_OPTIONS = "detect_leaks=0" })
+ return variables
+end
+
+dap.adapters.lldb = {
+ type = "executable",
+ command = "lldb-dap",
+ name = "lldb",
+ env = get_env_vars,
+}
+dap.adapters.gdb = {
+ type = "executable",
+ command = "gdb",
+ args = { "--interpreter=dap" },
+ env = get_env_vars,
+}
+dap.adapters.codelldb = {
+ type = "executable",
+ command = "codelldb",
+ env = get_env_vars,
+}
+
+local function get_program()
+ local _program
+ vim.ui.input({
+ prompt = "Program: ",
+ complete = "file_in_path",
+ }, function(res)
+ _program = res
+ end)
+ return vim.fn.system("which " .. _program):gsub("\n$", "")
+end
+
+local function get_args()
+ local _args
+ vim.ui.input({
+ prompt = "Args: ",
+ default = vim.fn.getreg("+"),
+ complete = "file",
+ }, function(res)
+ _args = res
+ end)
+ return require("dap.utils").splitstr(_args)
+end
+
+dap.configurations.cpp = {
+ {
+ name = "codelldb Launch",
+ type = "codelldb",
+ request = "launch",
+ cwd = "${workspaceFolder}",
+ program = get_program,
+ args = get_args,
+ stopOnEntry = true,
+ console = "integratedTerminal",
+ },
+}
+
+dap.configurations.c = dap.configurations.cpp
+dap.configurations.rust = dap.configurations.cpp
+
+require("nvim-dap-virtual-text").setup({})
+require("mason-nvim-dap").setup({
+ automatic_installation = false,
+ handlers = {},
+ ensure_installed = {},
+})
diff --git a/dot_config/nvim/lua/plugins/editing.lua b/dot_config/nvim/lua/plugins/editing.lua
new file mode 100644
index 0000000..5175516
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/editing.lua
@@ -0,0 +1,61 @@
+require("guess-indent").setup({})
+
+require("various-textobjs").setup({
+ keymaps = {
+ useDefaults = true,
+ },
+})
+
+-- dial.nvim: enhanced increment/decrement on standard vim keys
+vim.keymap.set("n", "<C-a>", function()
+ return require("dial.map").inc_normal()
+end, { expr = true, desc = "Increment" })
+vim.keymap.set("n", "<C-x>", function()
+ return require("dial.map").dec_normal()
+end, { expr = true, desc = "Decrement" })
+vim.keymap.set("v", "<C-a>", function()
+ return require("dial.map").inc_visual()
+end, { expr = true, desc = "Increment" })
+vim.keymap.set("v", "<C-x>", function()
+ return require("dial.map").dec_visual()
+end, { expr = true, desc = "Decrement" })
+vim.keymap.set("v", "g<C-a>", function()
+ return require("dial.map").inc_gvisual()
+end, { expr = true, desc = "Increment (sequential)" })
+vim.keymap.set("v", "g<C-x>", function()
+ return require("dial.map").dec_gvisual()
+end, { expr = true, desc = "Decrement (sequential)" })
+
+-- refactoring.nvim
+require("refactoring").setup({})
+
+vim.keymap.set("x", "<leader>re", function()
+ require("refactoring").refactor("Extract Function")
+end, { desc = "[R]efactor [E]xtract function" })
+vim.keymap.set("x", "<leader>rf", function()
+ require("refactoring").refactor("Extract Function To File")
+end, { desc = "[R]efactor extract function to [F]ile" })
+vim.keymap.set("x", "<leader>rv", function()
+ require("refactoring").refactor("Extract Variable")
+end, { desc = "[R]efactor extract [V]ariable" })
+vim.keymap.set("n", "<leader>rI", function()
+ require("refactoring").refactor("Inline Function")
+end, { desc = "[R]efactor [I]nline function" })
+vim.keymap.set({ "x", "n" }, "<leader>ri", function()
+ require("refactoring").refactor("Inline Variable")
+end, { desc = "[R]efactor [I]nline variable" })
+vim.keymap.set("n", "<leader>rb", function()
+ require("refactoring").refactor("Extract Block")
+end, { desc = "[R]efactor extract [B]lock" })
+vim.keymap.set("n", "<leader>rB", function()
+ require("refactoring").refactor("Extract Block To File")
+end, { desc = "[R]efactor extract [B]lock to file" })
+vim.keymap.set("n", "<leader>rp", function()
+ require("refactoring").debug.printf({})
+end, { desc = "[R]efactor [P]rint" })
+vim.keymap.set({ "x", "n" }, "<leader>rV", function()
+ require("refactoring").debug.print_var({})
+end, { desc = "[R]efactor [P]rint [V]ariable" })
+vim.keymap.set("n", "<leader>rc", function()
+ require("refactoring").debug.cleanup({})
+end, { desc = "[R]efactor [C]leanup" })
diff --git a/dot_config/nvim/lua/plugins/git.lua b/dot_config/nvim/lua/plugins/git.lua
new file mode 100644
index 0000000..b052c33
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/git.lua
@@ -0,0 +1,123 @@
+require("git-conflict").setup({
+ disable_diagnostics = true,
+ default_mappings = {
+ next = "]x",
+ prev = "[x",
+ },
+})
+
+require("neogit").setup({
+ disable_commit_confirmation = true,
+ kind = "split",
+ console_timeout = 5000,
+ auto_show_console = false,
+})
+
+vim.keymap.set("n", "<leader>go", function()
+ require("neogit").open()
+end, { desc = "neo[G]it [O]pen" })
+
+require("gitlinker").setup({
+ callbacks = {
+ ["git.sommerfeld.dev"] = function(url_data)
+ local url = require("gitlinker.hosts").get_base_https_url(url_data)
+ url = url .. "/tree/" .. url_data.file .. "?id=" .. url_data.rev
+ if url_data.lstart then
+ url = url .. "#n" .. url_data.lstart
+ end
+ return url
+ end,
+ },
+})
+
+vim.keymap.set("n", "<leader>gy", function()
+ require("gitlinker").get_buf_range_url("n")
+end)
+vim.keymap.set("v", "<leader>gy", function()
+ require("gitlinker").get_buf_range_url("v")
+end)
+
+require("gitsigns").setup({
+ signs = {
+ change = { show_count = true },
+ delete = { show_count = true },
+ topdelete = { show_count = true },
+ changedelete = { show_count = true },
+ },
+ numhl = true,
+ on_attach = function(bufnr)
+ local gs = require("gitsigns")
+ local function map(mode, l, r, desc)
+ vim.keymap.set(mode, l, r, { buffer = bufnr, desc = desc })
+ end
+ local function nmap(l, r, desc)
+ map("n", l, r, desc)
+ end
+ local function vmap(l, r, desc)
+ map("v", l, r, desc)
+ end
+ -- Navigation
+ nmap("]c", function()
+ if vim.wo.diff then
+ vim.cmd.normal({ "]c", bang = true })
+ else
+ gs.nav_hunk("next")
+ end
+ end, "Jump to next git [c]hange")
+
+ nmap("[c", function()
+ if vim.wo.diff then
+ vim.cmd.normal({ "[c", bang = true })
+ else
+ gs.nav_hunk("prev")
+ end
+ end, "Jump to previous git [c]hange")
+
+ -- Actions
+ nmap("<leader>hs", gs.stage_hunk, "git [s]tage hunk")
+ nmap("<leader>hr", gs.reset_hunk, "git [r]eset hunk")
+ vmap("<leader>hs", function()
+ gs.stage_hunk({ vim.fn.line("."), vim.fn.line("v") })
+ end, "git [s]tage hunk")
+ vmap("<leader>hr", function()
+ gs.reset_hunk({ vim.fn.line("."), vim.fn.line("v") })
+ end, "git [r]eset hunk")
+ nmap("<leader>hS", gs.stage_buffer, "git [S]tage buffer")
+ nmap("<leader>hR", gs.reset_buffer, "git [R]eset buffer")
+ nmap("<leader>hp", gs.preview_hunk, "git [p]review hunk")
+ nmap("<leader>hb", function()
+ gs.blame_line({ full = true })
+ end, "git [b]lame line")
+ nmap(
+ "<leader>tb",
+ gs.toggle_current_line_blame,
+ "[T]oggle git show [b]lame line"
+ )
+ nmap("<leader>hd", gs.diffthis, "git [d]iff against index")
+ nmap("<leader>hD", function()
+ gs.diffthis("~")
+ end, "git [D]iff against last commit")
+ nmap("<leader>hc", gs.change_base, "git [C]hange base to index")
+ nmap("<leader>hC", function()
+ gs.change_base("~")
+ end, "git [C]hange base to HEAD")
+ nmap(
+ "<leader>tgd",
+ gs.preview_hunk_inline,
+ "[T]oggle [G]it show [D]eleted"
+ )
+ nmap("<leader>tgw", gs.toggle_word_diff, "[T]oggle [G]it [W]ord diff")
+ nmap(
+ "<leader>tgl",
+ gs.toggle_linehl,
+ "[T]oggle [G]it [L]ine highlighting"
+ )
+ -- Text object
+ map(
+ { "o", "x" },
+ "ih",
+ ":<C-U>Gitsigns select_hunk<CR>",
+ "git [H]unk text object"
+ )
+ end,
+})
diff --git a/dot_config/nvim/lua/plugins/init.lua b/dot_config/nvim/lua/plugins/init.lua
new file mode 100644
index 0000000..b106b6e
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/init.lua
@@ -0,0 +1,58 @@
+-- Seamless navigation between neovim splits and zellij panes
+require("smart-splits").setup({})
+vim.keymap.set("n", "<C-h>", require("smart-splits").move_cursor_left, { desc = "Move to left split/pane" })
+vim.keymap.set("n", "<C-j>", require("smart-splits").move_cursor_down, { desc = "Move to below split/pane" })
+vim.keymap.set("n", "<C-k>", require("smart-splits").move_cursor_up, { desc = "Move to above split/pane" })
+vim.keymap.set("n", "<C-l>", require("smart-splits").move_cursor_right, { desc = "Move to right split/pane" })
+
+require("which-key").setup({
+ spec = {
+ { "g", group = "[G]oto" },
+ { "yo", group = "Toggle options" },
+ { "]", group = "Navigate to next" },
+ { "[", group = "Navigate to previous" },
+ { "<leader>c", group = "[C]ode", mode = { "n", "x" } },
+ { "<leader>g", group = "[G]it" },
+ { "<leader>h", group = "Git [H]unk", mode = { "n", "v" } },
+ { "<leader>o", group = "[O]verseer" },
+ { "<leader>r", group = "[R]efactor" },
+ { "<leader>w", group = "[W]orkspace" },
+ { "<leader>t", group = "[T]oggle" },
+ },
+})
+
+vim.keymap.set("n", "<leader>?", function()
+ require("which-key").show({ global = false })
+end, { desc = "Buffer Local Keymaps (which-key)" })
+
+require("quicker").setup({
+ keys = {
+ {
+ ">",
+ function()
+ require("quicker").expand({
+ before = 2,
+ after = 2,
+ add_to_existing = true,
+ })
+ end,
+ desc = "Expand quickfix context",
+ },
+ {
+ "<",
+ function()
+ require("quicker").collapse()
+ end,
+ desc = "Collapse quickfix context",
+ },
+ },
+})
+
+vim.keymap.set("n", "<leader>tq", function()
+ require("quicker").toggle()
+end, { desc = "[T]oggle [Q]uickfix" })
+vim.keymap.set("n", "<leader>tl", function()
+ require("quicker").toggle({ loclist = true })
+end, { desc = "[T]oggle [L]oclist" })
+
+require("oil").setup({})
diff --git a/dot_config/nvim/lua/plugins/lsp.lua b/dot_config/nvim/lua/plugins/lsp.lua
new file mode 100644
index 0000000..ddd5bea
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/lsp.lua
@@ -0,0 +1,280 @@
+require("lazydev").setup({
+ library = {
+ { path = "${3rd}/luv/library", words = { "vim%.uv" } },
+ },
+})
+
+vim.lsp.enable("just")
+pcall(vim.lsp.enable, "tblgen_lsp_server")
+
+require("fidget").setup({})
+require("mason").setup({})
+require("mason-lspconfig").setup({
+ ensure_installed = {},
+ automatic_installation = false,
+ handlers = {
+ function(server_name)
+ vim.lsp.enable(server_name)
+ end,
+ },
+})
+require("mason-tool-installer").setup({
+ ensure_installed = {
+ "actionlint",
+ "autotools-language-server",
+ "basedpyright",
+ "bash-language-server",
+ "clangd",
+ "codelldb",
+ "codespell",
+ "css-lsp",
+ "dockerfile-language-server",
+ "gh",
+ "gh-actions-language-server",
+ "groovy-language-server",
+ "hadolint",
+ "html-lsp",
+ "jq",
+ "json-lsp",
+ "jsonlint",
+ "just-lsp",
+ "lua-language-server",
+ "markdownlint",
+ "mdformat",
+ "neocmakelsp",
+ "nginx-config-formatter",
+ "nginx-language-server",
+ "npm-groovy-lint",
+ "prettier",
+ "ruff",
+ "rust-analyzer",
+ "shellcheck",
+ "shellharden",
+ "shfmt",
+ "stylelint",
+ "stylua",
+ "systemd-lsp",
+ "systemdlint",
+ "typescript-language-server",
+ "typos",
+ "yaml-language-server",
+ "yamllint",
+ "yq",
+ },
+})
+
+vim.api.nvim_create_autocmd("LspAttach", {
+ group = vim.api.nvim_create_augroup("lsp-attach", { clear = true }),
+ callback = function(event)
+ local bufnr = event.buf
+
+ local function map(mode, l, r, desc)
+ vim.keymap.set(mode, l, r, { buffer = bufnr, desc = "LSP: " .. desc })
+ end
+ local function nmap(l, r, desc)
+ map("n", l, r, desc)
+ end
+ nmap("<c-]>", vim.lsp.buf.definition, "Goto definition")
+ nmap("gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration")
+
+ local fzf = require("fzf-lua")
+ nmap("gd", fzf.lsp_definitions, "[G]oto [D]efinition")
+ nmap("gvd", function()
+ fzf.lsp_definitions({ jump1_action = fzf.actions.file_vsplit })
+ end, "[G]oto in a [V]ertical split to [D]efinition")
+ nmap("gxd", function()
+ fzf.lsp_definitions({ jump1_action = fzf.actions.file_split })
+ end, "[G]oto in a [X]horizontal split to [D]efinition")
+ nmap("gtd", function()
+ fzf.lsp_definitions({ jump1_action = fzf.actions.file_tabedit })
+ end, "[G]oto in a [T]ab to [D]efinition")
+ nmap("gvt", function()
+ fzf.lsp_typedefs({ jump1_action = fzf.actions.file_vsplit })
+ end, "[G]oto in a [V]ertical split to [T]ype definition")
+ nmap("gxt", function()
+ fzf.lsp_typedefs({ jump1_action = fzf.actions.file_split })
+ end, "[G]oto in a [X]horizontal split to [T]ype definition")
+ nmap("gtt", function()
+ fzf.lsp_typedefs({ jump1_action = fzf.actions.file_tabedit })
+ end, "[G]oto in a [T]ab to [T]ype definition")
+ nmap("gri", fzf.lsp_implementations, "[G]oto [I]mplementation")
+ nmap("grvi", function()
+ fzf.lsp_implementations({ jump1_action = fzf.actions.file_vsplit })
+ end, "[G]oto in a [V]ertical split to [I]mplementation")
+ nmap("grxi", function()
+ fzf.lsp_implementations({ jump1_action = fzf.actions.file_split })
+ end, "[G]oto in a [X]horizontal split to [I]mplementation")
+ nmap("grti", function()
+ fzf.lsp_implementations({ jump1_action = fzf.actions.file_tabedit })
+ end, "[G]oto in a [T]ab to [I]mplementation")
+ nmap("grr", fzf.lsp_references, "[G]oto [R]eferences")
+ nmap("gvr", function()
+ fzf.lsp_references({ jump1_action = fzf.actions.file_vsplit })
+ end, "[G]oto in a [V]ertical split to [R]eferences")
+ nmap("gxr", function()
+ fzf.lsp_references({ jump1_action = fzf.actions.file_split })
+ end, "[G]oto in a [X]horizontal split to [R]eferences")
+ nmap("gtr", function()
+ fzf.lsp_references({ jump1_action = fzf.actions.file_tabedit })
+ end, "[G]oto in a [T]ab to [R]eferences")
+ nmap("<leader>ci", fzf.lsp_incoming_calls, "[C]ode [I]ncoming calls")
+ nmap("<leader>co", fzf.lsp_outgoing_calls, "[C]ode [O]utgoing calls")
+ nmap("gO", fzf.lsp_document_symbols, "d[O]ocument symbols")
+ nmap(
+ "<leader>ws",
+ fzf.lsp_live_workspace_symbols,
+ "[W]orkspace [S]ymbols"
+ )
+ nmap(
+ "<leader>wd",
+ fzf.diagnostics_workspace,
+ "[W]orkspace [D]iagnostics"
+ )
+
+ local client = vim.lsp.get_client_by_id(event.data.client_id)
+ if
+ client
+ and client:supports_method(
+ vim.lsp.protocol.Methods.textDocument_documentHighlight,
+ event.buf
+ )
+ then
+ local highlight_augroup =
+ vim.api.nvim_create_augroup("lsp-highlight", { clear = false })
+ vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
+ buffer = event.buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.document_highlight,
+ })
+
+ vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
+ buffer = event.buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.clear_references,
+ })
+
+ vim.api.nvim_create_autocmd("LspDetach", {
+ group = vim.api.nvim_create_augroup(
+ "lsp-detach",
+ { clear = true }
+ ),
+ callback = function(event2)
+ vim.lsp.buf.clear_references()
+ vim.api.nvim_clear_autocmds({
+ group = "lsp-highlight",
+ buffer = event2.buf,
+ })
+ end,
+ })
+ end
+
+ if
+ client
+ and client:supports_method(
+ vim.lsp.protocol.Methods.textDocument_codeLens,
+ event.buf
+ )
+ then
+ vim.lsp.codelens.enable(true, { bufnr = bufnr })
+ end
+
+ if
+ client
+ and client:supports_method(
+ vim.lsp.protocol.Methods.textDocument_inlayHint,
+ event.buf
+ )
+ then
+ nmap("<leader>th", function()
+ vim.lsp.inlay_hint.enable(
+ not vim.lsp.inlay_hint.is_enabled(event.buf)
+ )
+ end, "[T]oggle Inlay [H]ints")
+ end
+ end,
+})
+
+require("conform").setup({
+ formatters_by_ft = {
+ awk = { "gawk" },
+ bash = { "shfmt" },
+ cmake = { "cmake_format" },
+ css = { "prettier", "stylelint" },
+ groovy = { "npm-groovy-lint" },
+ html = { "prettier" },
+ javascript = { "prettier" },
+ typescript = { "prettier" },
+ jenkins = { "npm-groovy-lint" },
+ json = { "jq", "jsonlint" },
+ jsonc = { "prettier" },
+ just = { "just" },
+ markdown = { "mdformat" },
+ nginx = { "nginxfmt" },
+ lua = { "stylua" },
+ python = { "ruff_format", "ruff_fix", "ruff_organize_imports" },
+ rust = { "rustfmt" },
+ sh = { "shfmt", "shellcheck", "shellharden" },
+ yaml = { "yamllint" },
+ zsh = { "shfmt", "shellcheck", "shellharden" },
+ },
+ default_format_opts = {
+ lsp_format = "fallback",
+ },
+ formatters = {
+ shfmt = {
+ prepend_args = { "-i", "2" },
+ },
+ },
+})
+vim.o.formatexpr = "v:lua.require'conform'.formatexpr()"
+
+vim.keymap.set("", "<leader>f", function()
+ require("conform").format({ async = true, lsp_fallback = true })
+end, { desc = "[F]ormat buffer" })
+
+local lint = require("lint")
+lint.linters_by_ft = {
+ css = { "stylelint" },
+ dockerfile = { "hadolint" },
+ groovy = { "npm-groovy-lint" },
+ jenkins = { "npm-groovy-lint" },
+ json = { "jsonlint" },
+ markdown = { "markdownlint" },
+ makefile = { "checkmake" },
+ systemd = { "systemdlint" },
+ yaml = { "yamllint", "yq" },
+ ghaction = { "actionlint" },
+ zsh = { "zsh" },
+ ["*"] = { "codespell", "typos" },
+}
+vim.api.nvim_create_autocmd({ "BufReadPost", "BufWritePost" }, {
+ group = vim.api.nvim_create_augroup("lint", { clear = true }),
+ callback = function()
+ if vim.opt_local.modifiable:get() then
+ lint.try_lint()
+ end
+ end,
+})
+
+require("tiny-inline-diagnostic").setup({
+ options = {
+ show_source = {
+ if_many = true,
+ },
+ set_arrow_to_diag_color = true,
+ multilines = {
+ enabled = true,
+ },
+ show_all_diags_on_cursorline = true,
+ enable_on_select = true,
+ break_line = {
+ enabled = true,
+ },
+ severity = {
+ vim.diagnostic.severity.ERROR,
+ vim.diagnostic.severity.WARN,
+ vim.diagnostic.severity.INFO,
+ vim.diagnostic.severity.HINT,
+ },
+ },
+})
diff --git a/dot_config/nvim/lua/plugins/runner.lua b/dot_config/nvim/lua/plugins/runner.lua
new file mode 100644
index 0000000..28e4e5f
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/runner.lua
@@ -0,0 +1,77 @@
+local overseer = require("overseer")
+overseer.setup({})
+overseer.add_template_hook({ name = ".*" }, function(task_defn, util)
+ util.add_component(task_defn, {
+ "open_output",
+ on_start = "never",
+ on_complete = "failure",
+ direction = "vertical",
+ })
+end)
+
+vim.keymap.set("n", "<leader>to", function()
+ overseer.toggle()
+end, { desc = "[T]oggle [O]verseer" })
+vim.keymap.set("n", "<leader>ob", function()
+ overseer.run_task({ name = "just build", disallow_prompt = true })
+end, { desc = "[O]verseer [B]uild" })
+vim.keymap.set("n", "<leader>oB", function()
+ overseer.run_task({ name = "just build" })
+end, { desc = "[O]verseer [B]uild" })
+vim.keymap.set("n", "<leader>ot", function()
+ overseer.run_task({ name = "just test", disallow_prompt = true })
+end, { desc = "[O]verseer [J]ust [T]est" })
+vim.keymap.set("n", "<leader>oT", function()
+ overseer.run_task({ name = "just test" })
+end, { desc = "[O]verseer [J]ust [T]est" })
+vim.keymap.set("n", "<leader>of", function()
+ overseer.run_task({
+ name = "just test",
+ disallow_prompt = true,
+ params = { target = vim.fn.expand("%") },
+ })
+end, { desc = "[O]verseer test [F]ile" })
+vim.keymap.set("n", "<leader>oF", function()
+ overseer.run_task({
+ name = "just test",
+ params = { target = vim.fn.expand("%") },
+ })
+end, { desc = "[O]verseer test [F]ile" })
+vim.keymap.set("n", "<leader>od", function()
+ overseer.run_task({
+ name = "just debug=true test",
+ disallow_prompt = true,
+ params = { target = vim.fn.expand("%") },
+ })
+end, { desc = "[O]verseer [d]ebug test file" })
+vim.keymap.set("n", "<leader>oD", function()
+ overseer.run_task({
+ name = "just debug=true test",
+ params = { target = vim.fn.expand("%") },
+ })
+end, { desc = "[O]verseer [D]ebug test file" })
+vim.keymap.set("n", "<leader>oa", function()
+ overseer.run_task({
+ name = "just test_autofix",
+ disallow_prompt = true,
+ params = { target = vim.fn.expand("%") },
+ })
+end, { desc = "[O]verseer [A]utofix" })
+vim.keymap.set("n", "<leader>or", function()
+ overseer.run_task()
+end, { desc = "[O]verseer [R]un" })
+vim.keymap.set("n", "<leader>os", function()
+ vim.cmd("OverseerShell")
+end, { desc = "[O]verseer [S]hell" })
+vim.keymap.set("n", "<leader>ol", function()
+ local tasks = overseer.list_tasks({
+ sort = function(a, b)
+ return a.id > b.id
+ end,
+ })
+ if vim.tbl_isempty(tasks) then
+ vim.notify("No tasks found", vim.log.levels.WARN)
+ else
+ overseer.run_action(tasks[1], "restart")
+ end
+end, { desc = "[O]verseer run [L]ast" })
diff --git a/dot_config/nvim/lua/plugins/search.lua b/dot_config/nvim/lua/plugins/search.lua
new file mode 100644
index 0000000..a36cddc
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/search.lua
@@ -0,0 +1,60 @@
+local fzflua = require("fzf-lua")
+fzflua.setup({
+ keymap = {
+ builtin = {
+ true,
+ ["<M-p>"] = "toggle-preview",
+ },
+ },
+ grep = {
+ hidden = true,
+ RIPGREP_CONFIG_PATH = "~/.config/ripgrep/ripgreprc",
+ },
+ lsp = {
+ includeDeclaration = false,
+ },
+ actions = {
+ files = {
+ true,
+ ["ctrl-x"] = fzflua.actions.file_split,
+ },
+ },
+})
+fzflua.register_ui_select()
+
+vim.keymap.set("n", "<localleader>b", function()
+ fzflua.buffers()
+end, { desc = "fzf-lua [B]uffers" })
+vim.keymap.set("n", "<localleader>/", function()
+ fzflua.live_grep()
+end, { desc = "fzf-lua live grep" })
+vim.keymap.set("n", "<localleader>f", function()
+ fzflua.files()
+end, { desc = "fzf-lua [F]iles" })
+vim.keymap.set("n", "<leader><leader>", function()
+ fzflua.global()
+end, { desc = "fzf-lua global picker" })
+vim.keymap.set("n", "<localleader>d", function()
+ fzflua.diagnostics()
+end, { desc = "fzf-lua [D]iagnostics" })
+vim.keymap.set("n", "<localleader>r", function()
+ fzflua.resume()
+end, { desc = "fzf-lua [R]esume" })
+vim.keymap.set("n", "<localleader>gc", function()
+ fzflua.git_bcommits()
+end, { desc = "[G]it buffer [C]commits" })
+vim.keymap.set("v", "<localleader>gc", function()
+ fzflua.git_bcommits_range()
+end, { desc = "[G]it [C]commits for selected range" })
+vim.keymap.set("n", "<localleader>gC", function()
+ fzflua.git_commits()
+end, { desc = "[G]it (all) [C]commits" })
+vim.keymap.set("n", "<localleader>gb", function()
+ fzflua.git_branches()
+end, { desc = "[G]it [B]ranches" })
+vim.keymap.set("n", "<localleader>gs", function()
+ fzflua.git_status()
+end, { desc = "[G]it [S]tatus" })
+vim.keymap.set("n", "<localleader>gS", function()
+ fzflua.git_stash()
+end, { desc = "[G]it [S]tash" })
diff --git a/dot_config/nvim/lua/plugins/session.lua b/dot_config/nvim/lua/plugins/session.lua
new file mode 100644
index 0000000..a094727
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/session.lua
@@ -0,0 +1,77 @@
+local function get_cwd_as_name()
+ local dir = vim.fn.getcwd(0)
+ return dir:gsub("[^A-Za-z0-9]", "_")
+end
+local overseer = require("overseer")
+
+require("auto-session").setup({
+ use_git_branch = true,
+ pre_save_cmds = {
+ function()
+ overseer.save_task_bundle(
+ get_cwd_as_name(),
+ nil,
+ { on_conflict = "overwrite" }
+ )
+ end,
+ },
+ pre_restore_cmds = {
+ function()
+ for _, task in ipairs(overseer.list_tasks({})) do
+ task:dispose(true)
+ end
+ end,
+ },
+ post_restore_cmds = {
+ function()
+ overseer.load_task_bundle(
+ get_cwd_as_name(),
+ { ignore_missing = true, autostart = false }
+ )
+ end,
+ },
+ save_extra_data = function(_)
+ local ok, breakpoints = pcall(require, "dap.breakpoints")
+ if not ok or not breakpoints then
+ return
+ end
+
+ local bps = {}
+ local breakpoints_by_buf = breakpoints.get()
+ for buf, buf_bps in pairs(breakpoints_by_buf) do
+ bps[vim.api.nvim_buf_get_name(buf)] = buf_bps
+ end
+ if vim.tbl_isempty(bps) then
+ return
+ end
+ local extra_data = {
+ breakpoints = bps,
+ }
+ return vim.fn.json_encode(extra_data)
+ end,
+
+ restore_extra_data = function(_, extra_data)
+ local json = vim.fn.json_decode(extra_data)
+
+ if json.breakpoints then
+ local ok, breakpoints = pcall(require, "dap.breakpoints")
+
+ if not ok or not breakpoints then
+ return
+ end
+ vim.notify("restoring breakpoints")
+ for buf_name, buf_bps in pairs(json.breakpoints) do
+ for _, bp in pairs(buf_bps) do
+ local line = bp.line
+ local opts = {
+ condition = bp.condition,
+ log_message = bp.logMessage,
+ hit_condition = bp.hitCondition,
+ }
+ breakpoints.set(opts, vim.fn.bufnr(buf_name), line)
+ end
+ end
+ end
+ end,
+ suppressed_dirs = { "~/", "/" },
+})
diff --git a/dot_config/nvim/lua/plugins/treesitter.lua b/dot_config/nvim/lua/plugins/treesitter.lua
new file mode 100644
index 0000000..a4a488c
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/treesitter.lua
@@ -0,0 +1,83 @@
+require("treewalker").setup({})
+
+vim.keymap.set({ "n", "v" }, "<a-k>", "<cmd>Treewalker Up<cr>", { silent = true, desc = "Moves up to the previous neighbor node" })
+vim.keymap.set({ "n", "v" }, "<a-j>", "<cmd>Treewalker Down<cr>", { silent = true, desc = "Moves up to the next neighbor node" })
+vim.keymap.set({ "n", "v" }, "<a-h>", "<cmd>Treewalker Left<cr>", { silent = true, desc = "Moves to the first ancestor node that's on a different line from the current node" })
+vim.keymap.set({ "n", "v" }, "<a-l>", "<cmd>Treewalker Right<cr>", { silent = true, desc = "Moves to the next node down that's indented further than the current node" })
+vim.keymap.set("n", "<s-a-k>", "<cmd>Treewalker SwapUp<cr>", { silent = true, desc = "Swaps the highest node on the line upwards in the document" })
+vim.keymap.set("n", "<s-a-j>", "<cmd>Treewalker SwapDown<cr>", { silent = true, desc = "Swaps the biggest node on the line downward in the document" })
+vim.keymap.set("n", "<s-a-h>", "<cmd>Treewalker SwapLeft<cr>", { silent = true, desc = "Swap the node under the cursor with its previous neighbor" })
+vim.keymap.set("n", "<s-a-l>", "<cmd>Treewalker SwapRight<cr>", { silent = true, desc = "Swap the node under the cursor with its next neighbor" })
+
+require("nvim-treesitter").install({
+ "awk",
+ "bash",
+ "c",
+ "cmake",
+ "comment",
+ "cpp",
+ "css",
+ "csv",
+ "diff",
+ "dockerfile",
+ "doxygen",
+ "editorconfig",
+ "fortran",
+ "git_config",
+ "git_rebase",
+ "gitattributes",
+ "gitcommit",
+ "gitignore",
+ "groovy",
+ "gpg",
+ "hlsplaylist",
+ "html",
+ "http",
+ "ini",
+ "javascript",
+ "jq",
+ "jsdoc",
+ "json",
+ "just",
+ "llvm",
+ "lua",
+ "luadoc",
+ "luap",
+ "make",
+ "markdown",
+ "markdown_inline",
+ "query",
+ "passwd",
+ "printf",
+ "python",
+ "regex",
+ "readline",
+ "requirements",
+ "rust",
+ "sql",
+ "ssh_config",
+ "strace",
+ "tablegen",
+ "todotxt",
+ "toml",
+ "typescript",
+ "vim",
+ "vimdoc",
+ "xcompose",
+ "xml",
+ "xresources",
+ "yaml",
+})
+
+require("nvim-dap-repl-highlights").setup({})
+require("treesitter-context").setup({})
+
+require("ts_context_commentstring").setup({
+ enable_autocmd = false,
+})
+local get_option = vim.filetype.get_option
+vim.filetype.get_option = function(filetype, option)
+ return option == "commentstring"
+ and require("ts_context_commentstring.internal").calculate_commentstring()
+ or get_option(filetype, option)
+end
diff --git a/dot_config/nvim/lua/plugins/ui.lua b/dot_config/nvim/lua/plugins/ui.lua
new file mode 100644
index 0000000..50a2114
--- /dev/null
+++ b/dot_config/nvim/lua/plugins/ui.lua
@@ -0,0 +1,58 @@
+-- blink.indent (gruvbox setup is in init.lua)
+require("blink.indent").setup({
+ scope = {
+ highlights = {
+ "BlinkIndentOrange",
+ "BlinkIndentViolet",
+ "BlinkIndentBlue",
+ "BlinkIndentRed",
+ "BlinkIndentCyan",
+ "BlinkIndentYellow",
+ "BlinkIndentGreen",
+ },
+ underline = {
+ enabled = true,
+ highlights = {
+ "BlinkIndentOrangeUnderline",
+ "BlinkIndentVioletUnderline",
+ "BlinkIndentBlueUnderline",
+ "BlinkIndentRedUnderline",
+ "BlinkIndentCyanUnderline",
+ "BlinkIndentYellowUnderline",
+ "BlinkIndentGreenUnderline",
+ },
+ },
+ },
+})
+
+require("lualine").setup({
+ options = {
+ icons_enabled = false,
+ theme = "gruvbox_dark",
+ component_separators = "",
+ section_separators = "|",
+ disabled_filetypes = {
+ winbar = {
+ "dap-view",
+ "dap-repl",
+ "dap-view-term",
+ },
+ },
+ },
+ sections = {
+ lualine_a = { "filetype", { "filename", path = 1 } },
+ lualine_b = { "%l/%L:%c:%o" },
+ lualine_c = { "diff" },
+ lualine_x = { "searchcount", "selectioncount" },
+ lualine_y = { "overseer", "copilot" },
+ lualine_z = { "diagnostics" },
+ },
+ inactive_sections = {
+ lualine_a = {},
+ lualine_b = {},
+ lualine_c = { "filename" },
+ lualine_x = {},
+ lualine_y = {},
+ lualine_z = {},
+ },
+})
diff --git a/dot_config/nvim/nvim-pack-lock.json b/dot_config/nvim/nvim-pack-lock.json
new file mode 100644
index 0000000..6b17553
--- /dev/null
+++ b/dot_config/nvim/nvim-pack-lock.json
@@ -0,0 +1,222 @@
+{
+ "plugins": {
+ "auto-session": {
+ "rev": "62437532b38495551410b3f377bcf4aaac574ebe",
+ "src": "https://github.com/rmagatti/auto-session"
+ },
+ "blink-copilot": {
+ "rev": "7ad8209b2f880a2840c94cdcd80ab4dc511d4f39",
+ "src": "https://github.com/fang2hou/blink-copilot"
+ },
+ "blink.cmp": {
+ "rev": "78336bc89ee5365633bcf754d93df01678b5c08f",
+ "src": "https://github.com/saghen/blink.cmp",
+ "version": ">=0.0.0"
+ },
+ "blink.compat": {
+ "rev": "1454f14a8d855a578ceeba77c62538fa1459a67c",
+ "src": "https://github.com/saghen/blink.compat"
+ },
+ "blink.download": {
+ "rev": "a459c8fbf51359902971874b5ebe05f6602db8b4",
+ "src": "https://github.com/saghen/blink.download",
+ "version": "'main'"
+ },
+ "blink.indent": {
+ "rev": "9c80820ca77218a8d28e70075d6f44a1609911fe",
+ "src": "https://github.com/saghen/blink.indent"
+ },
+ "blink.pairs": {
+ "rev": "4e43012356d33a26f69eae475f746fbe9f325f44",
+ "src": "https://github.com/saghen/blink.pairs",
+ "version": ">=0.0.0"
+ },
+ "cmp-dap": {
+ "rev": "ea92773e84c0ad3288c3bc5e452ac91559669087",
+ "src": "https://github.com/rcarriga/cmp-dap"
+ },
+ "colorful-menu.nvim": {
+ "rev": "b51a659459df8d078201aefc995db8175ed55e84",
+ "src": "https://github.com/xzbdmw/colorful-menu.nvim"
+ },
+ "conform.nvim": {
+ "rev": "086a40dc7ed8242c03be9f47fbcee68699cc2395",
+ "src": "https://github.com/stevearc/conform.nvim"
+ },
+ "copilot-lsp": {
+ "rev": "1b6d8273594643f51bb4c0c1d819bdb21b42159d",
+ "src": "https://github.com/copilotlsp-nvim/copilot-lsp"
+ },
+ "copilot-lualine": {
+ "rev": "222e90bd8dcdf16ca1efc4e784416afb5f011c31",
+ "src": "https://github.com/AndreM222/copilot-lualine"
+ },
+ "copilot.lua": {
+ "rev": "ad7e729e9a6348f7da482be0271d452dbc4c8e2c",
+ "src": "https://github.com/zbirenbaum/copilot.lua"
+ },
+ "debugmaster.nvim": {
+ "rev": "c2a07077bebfb97bc8dd4ab300d7734a453683e3",
+ "src": "https://github.com/miroshQa/debugmaster.nvim",
+ "version": "'dashboard'"
+ },
+ "dial.nvim": {
+ "rev": "f2634758455cfa52a8acea6f142dcd6271a1bf57",
+ "src": "https://github.com/monaqa/dial.nvim"
+ },
+ "fidget.nvim": {
+ "rev": "889e2e96edef4e144965571d46f7a77bcc4d0ddf",
+ "src": "https://github.com/j-hui/fidget.nvim"
+ },
+ "friendly-snippets": {
+ "rev": "6cd7280adead7f586db6fccbd15d2cac7e2188b9",
+ "src": "https://github.com/rafamadriz/friendly-snippets"
+ },
+ "fzf-lua": {
+ "rev": "5bf93b12f70c7cc89a2cc6ee026b82f111815be8",
+ "src": "https://github.com/ibhagwan/fzf-lua",
+ "version": "'main'"
+ },
+ "git-conflict.nvim": {
+ "rev": "a1badcd070d176172940eb55d9d59029dad1c5a6",
+ "src": "https://github.com/akinsho/git-conflict.nvim"
+ },
+ "gitlinker.nvim": {
+ "rev": "cc59f732f3d043b626c8702cb725c82e54d35c25",
+ "src": "https://github.com/ruifm/gitlinker.nvim"
+ },
+ "gitsigns.nvim": {
+ "rev": "8d82c240f190fc33723d48c308ccc1ed8baad69d",
+ "src": "https://github.com/lewis6991/gitsigns.nvim"
+ },
+ "gruvbox.nvim": {
+ "rev": "154eb5ff5b96d0641307113fa385eaf0d36d9796",
+ "src": "https://github.com/ellisonleao/gruvbox.nvim"
+ },
+ "guess-indent.nvim": {
+ "rev": "84a4987ff36798c2fc1169cbaff67960aed9776f",
+ "src": "https://github.com/nmac427/guess-indent.nvim"
+ },
+ "lazydev.nvim": {
+ "rev": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d",
+ "src": "https://github.com/folke/lazydev.nvim"
+ },
+ "lualine.nvim": {
+ "rev": "a905eeebc4e63fdc48b5135d3bf8aea5618fb21c",
+ "src": "https://github.com/nvim-lualine/lualine.nvim"
+ },
+ "markdown-preview.nvim": {
+ "rev": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee",
+ "src": "https://github.com/iamcco/markdown-preview.nvim"
+ },
+ "mason-lspconfig.nvim": {
+ "rev": "0a3b42c3e503df87aef6d6513e13148381495c3a",
+ "src": "https://github.com/williamboman/mason-lspconfig.nvim"
+ },
+ "mason-nvim-dap.nvim": {
+ "rev": "9a10e096703966335bd5c46c8c875d5b0690dade",
+ "src": "https://github.com/jay-babu/mason-nvim-dap.nvim"
+ },
+ "mason-tool-installer.nvim": {
+ "rev": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc",
+ "src": "https://github.com/WhoIsSethDaniel/mason-tool-installer.nvim"
+ },
+ "mason.nvim": {
+ "rev": "b03fb0f20bc1d43daf558cda981a2be22e73ac42",
+ "src": "https://github.com/williamboman/mason.nvim"
+ },
+ "neogit": {
+ "rev": "e06745228600a585b88726fc9fba44a373c15a47",
+ "src": "https://github.com/NeogitOrg/neogit"
+ },
+ "nvim-dap": {
+ "rev": "45a69eba683a2c448dd9ecfc4de89511f0646b5f",
+ "src": "https://github.com/mfussenegger/nvim-dap"
+ },
+ "nvim-dap-repl-highlights": {
+ "rev": "f31deba47fe3ee6ff8d2f13d9dbd06b2d1ae06b5",
+ "src": "https://github.com/LiadOz/nvim-dap-repl-highlights"
+ },
+ "nvim-dap-virtual-text": {
+ "rev": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6",
+ "src": "https://github.com/theHamsta/nvim-dap-virtual-text"
+ },
+ "nvim-lint": {
+ "rev": "eab58b48eb11d7745c11c505e0f3057165902461",
+ "src": "https://github.com/mfussenegger/nvim-lint"
+ },
+ "nvim-lspconfig": {
+ "rev": "d10ce09e42bb0ca8600fd610c3bb58676e61208d",
+ "src": "https://github.com/neovim/nvim-lspconfig"
+ },
+ "nvim-surround": {
+ "rev": "9291040de8cd8a4439eb64c441e8d5d2bf884a5a",
+ "src": "https://github.com/kylechui/nvim-surround"
+ },
+ "nvim-treesitter": {
+ "rev": "4916d6592ede8c07973490d9322f187e07dfefac",
+ "src": "https://github.com/nvim-treesitter/nvim-treesitter",
+ "version": "'main'"
+ },
+ "nvim-treesitter-context": {
+ "rev": "b0c45cefe2c8f7b55fc46f34e563bc428ef99636",
+ "src": "https://github.com/nvim-treesitter/nvim-treesitter-context"
+ },
+ "nvim-treesitter-endwise": {
+ "rev": "8fe8a95630f4f2c72a87ba1927af649e0bfaa244",
+ "src": "https://github.com/RRethy/nvim-treesitter-endwise"
+ },
+ "nvim-ts-context-commentstring": {
+ "rev": "6141a40173c6efa98242dc951ed4b6f892c97027",
+ "src": "https://github.com/JoosepAlviste/nvim-ts-context-commentstring"
+ },
+ "nvim-various-textobjs": {
+ "rev": "ad78e9d925c95d675b32dd7ba6d253f96ce063fe",
+ "src": "https://github.com/chrisgrieser/nvim-various-textobjs"
+ },
+ "oil.nvim": {
+ "rev": "0fcc83805ad11cf714a949c98c605ed717e0b83e",
+ "src": "https://github.com/stevearc/oil.nvim"
+ },
+ "overseer.nvim": {
+ "rev": "f818eefff81f4b12fb7cf236f1b6c16768a2fcbc",
+ "src": "https://github.com/stevearc/overseer.nvim"
+ },
+ "plenary.nvim": {
+ "rev": "74b06c6c75e4eeb3108ec01852001636d85a932b",
+ "src": "https://github.com/nvim-lua/plenary.nvim"
+ },
+ "quicker.nvim": {
+ "rev": "063cc44da1eef8681bbd653b29d3bc961780886a",
+ "src": "https://github.com/stevearc/quicker.nvim"
+ },
+ "refactoring.nvim": {
+ "rev": "6784b54587e6d8a6b9ea199318512170ffb9e418",
+ "src": "https://github.com/ThePrimeagen/refactoring.nvim"
+ },
+ "rustaceanvim": {
+ "rev": "2497c7b2a61d4eb5dad716371a64ea82fb068bee",
+ "src": "https://github.com/mrcjkb/rustaceanvim"
+ },
+ "smart-splits.nvim": {
+ "rev": "ba2850ff3d3b09785a7105c69d06a12117d4b97d",
+ "src": "https://github.com/mrjones2014/smart-splits.nvim"
+ },
+ "tiny-inline-diagnostic.nvim": {
+ "rev": "57a0eb84b2008c76e77930639890d9874195b1e1",
+ "src": "https://github.com/rachartier/tiny-inline-diagnostic.nvim"
+ },
+ "tmux.nvim": {
+ "rev": "32ceaf2793582955ef9576809730878c4d2d9426",
+ "src": "https://github.com/aserowy/tmux.nvim"
+ },
+ "treewalker.nvim": {
+ "rev": "6fbceceb8966620da8970b727b6daa358b982805",
+ "src": "https://github.com/aaronik/treewalker.nvim"
+ },
+ "which-key.nvim": {
+ "rev": "3aab2147e74890957785941f0c1ad87d0a44c15a",
+ "src": "https://github.com/folke/which-key.nvim"
+ }
+ }
+}
diff --git a/dot_config/pacman/makepkg.conf b/dot_config/pacman/makepkg.conf
new file mode 100644
index 0000000..7233660
--- /dev/null
+++ b/dot_config/pacman/makepkg.conf
@@ -0,0 +1,9 @@
+CFLAGS="-march=native -Ofast -pipe -fomit-frame-pointer"
+RUSTFLAGS="-C opt-level=2 -C target-cpu=native"
+MAKEFLAGS="-j4"
+BUILDDIR=/tmp/makepkg
+COMPRESSGZ=(pigz -c -f -n)
+COMPRESSBZ2=(pbzip2 -c -f)
+COMPRESSXZ=(xz -c -z - --threads=0)
+COMPRESSZST=(zstd -c -z -q - --threads=0)
+PKGEXT='.pkg.tar.zst'
diff --git a/dot_config/paru/paru.conf b/dot_config/paru/paru.conf
new file mode 100644
index 0000000..57cb11b
--- /dev/null
+++ b/dot_config/paru/paru.conf
@@ -0,0 +1,25 @@
+#
+# /etc/paru.conf
+# ~/.config/paru/paru.conf
+#
+# See the paru.conf(5) manpage for options
+
+#
+# GENERAL OPTIONS
+#
+[options]
+PgpFetch
+Devel
+Provides
+DevelSuffixes = -git -cvs -svn -bzr -darcs -always
+# BottomUp
+RemoveMake
+#SudoLoop
+#UseAsk
+CombinedUpgrade
+BatchInstall
+# UpgradeMenu
+NewsOnUpgrade
+CleanAfter
+SaveChanges
+NoWarn = sommerfeld-*
diff --git a/dot_config/ripgrep/ripgreprc b/dot_config/ripgrep/ripgreprc
new file mode 100644
index 0000000..f808311
--- /dev/null
+++ b/dot_config/ripgrep/ripgreprc
@@ -0,0 +1,4 @@
+--hidden
+--follow
+--smart-case
+--glob=!.git
diff --git a/dot_config/sh/inputrc b/dot_config/sh/inputrc
new file mode 100644
index 0000000..e770fea
--- /dev/null
+++ b/dot_config/sh/inputrc
@@ -0,0 +1,20 @@
+$include /etc/inputrc
+
+set enable-keypad on
+set bell-style none
+set show-all-if-ambiguous on
+
+# Color files by types
+set colored-stats On
+# Append char to indicate type
+set visible-stats On
+# Mark symlinked directories
+set mark-symlinked-directories On
+# Color the common prefix
+set colored-completion-prefix On
+# Color the common prefix in menu-complete
+set menu-complete-display-prefix On
+
+set echo-control-characters off
+
+set enable-bracketed-paste on
diff --git a/dot_config/streamlink/config b/dot_config/streamlink/config
new file mode 100644
index 0000000..6916230
--- /dev/null
+++ b/dot_config/streamlink/config
@@ -0,0 +1,3 @@
+player=mpv
+default-stream=best
+twitch-disable-reruns
diff --git a/dot_config/sway/config b/dot_config/sway/config
new file mode 100644
index 0000000..bbd2eb0
--- /dev/null
+++ b/dot_config/sway/config
@@ -0,0 +1,157 @@
+# ── Variables ─────────────────────────────────────────────────────────────────
+set $mod Mod4
+set $term ghostty
+set $menu fuzzel
+
+# ── Appearance ────────────────────────────────────────────────────────────────
+font mono 8
+default_border pixel 1
+default_floating_border pixel 1
+gaps inner 0
+gaps outer 0
+
+# Gruvbox dark colors
+# class border bg text indicator child_border
+client.focused #fabd2f #282828 #ebdbb2 #fabd2f #fabd2f
+client.focused_inactive #3c3836 #282828 #928374 #3c3836 #3c3836
+client.unfocused #3c3836 #282828 #928374 #3c3836 #3c3836
+client.urgent #fb4934 #282828 #ebdbb2 #fb4934 #fb4934
+
+# ── Input ─────────────────────────────────────────────────────────────────────
+input type:keyboard {
+ xkb_layout us
+ xkb_options caps:escape
+ repeat_delay 300
+ repeat_rate 50
+}
+
+input type:touchpad {
+ tap enabled
+ natural_scroll enabled
+ dwt enabled
+}
+
+# ── Behavior ──────────────────────────────────────────────────────────────────
+focus_follows_mouse yes
+seat * hide_cursor 3000
+
+# ── Output ────────────────────────────────────────────────────────────────────
+output * bg #282828 solid_color
+
+# ── Window rules ──────────────────────────────────────────────────────────────
+for_window [class="feh"] floating enable
+for_window [app_id="imv"] floating enable
+for_window [class="Tor Browser"] floating enable
+
+# ── Standard keybinds (sway defaults) ─────────────────────────────────────────
+bindsym $mod+Return exec $term
+bindsym $mod+Shift+q kill
+bindsym $mod+d exec $menu
+
+# Navigation
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+
+# Move windows
+bindsym $mod+Shift+h move left
+bindsym $mod+Shift+j move down
+bindsym $mod+Shift+k move up
+bindsym $mod+Shift+l move right
+
+# Workspaces
+bindsym $mod+1 workspace number 1
+bindsym $mod+2 workspace number 2
+bindsym $mod+3 workspace number 3
+bindsym $mod+4 workspace number 4
+bindsym $mod+5 workspace number 5
+bindsym $mod+6 workspace number 6
+bindsym $mod+7 workspace number 7
+bindsym $mod+8 workspace number 8
+bindsym $mod+9 workspace number 9
+bindsym $mod+0 workspace number 10
+
+bindsym $mod+Shift+1 move container to workspace number 1
+bindsym $mod+Shift+2 move container to workspace number 2
+bindsym $mod+Shift+3 move container to workspace number 3
+bindsym $mod+Shift+4 move container to workspace number 4
+bindsym $mod+Shift+5 move container to workspace number 5
+bindsym $mod+Shift+6 move container to workspace number 6
+bindsym $mod+Shift+7 move container to workspace number 7
+bindsym $mod+Shift+8 move container to workspace number 8
+bindsym $mod+Shift+9 move container to workspace number 9
+bindsym $mod+Shift+0 move container to workspace number 10
+
+# Layout
+bindsym $mod+b splith
+bindsym $mod+v splitv
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout toggle split
+bindsym $mod+f fullscreen
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+bindsym $mod+a focus parent
+
+# Scratchpad
+bindsym $mod+Shift+minus move scratchpad
+bindsym $mod+minus scratchpad show
+
+# Resize mode
+mode "resize" {
+ bindsym h resize shrink width 10px
+ bindsym j resize grow height 10px
+ bindsym k resize shrink height 10px
+ bindsym l resize grow width 10px
+ bindsym Escape mode "default"
+ bindsym Return mode "default"
+}
+bindsym $mod+r mode "resize"
+
+# Reload / exit
+bindsym $mod+Shift+c reload
+bindsym $mod+Shift+e exec swaynag -t warning -m 'Exit sway?' -B 'Yes' 'swaymsg exit'
+
+# ── Personal keybinds ─────────────────────────────────────────────────────────
+
+# Workspace cycling
+bindsym $mod+Tab workspace next_on_output
+bindsym $mod+Shift+Tab workspace prev_on_output
+
+# Volume
+bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5%
+bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5%
+bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle
+bindsym $mod+Shift+m exec pactl set-sink-mute @DEFAULT_SINK@ toggle
+bindsym $mod+m exec pactl set-source-mute @DEFAULT_SOURCE@ toggle
+
+# Media
+bindsym XF86AudioPlay exec playerctl play-pause
+bindsym XF86AudioNext exec playerctl next
+bindsym XF86AudioPrev exec playerctl previous
+
+# Screenshots
+bindsym Print exec grim -g "$(slurp)" ~/pics/screenshots/$(date +%Y-%m-%d-%H-%M-%S).png
+bindsym Shift+Print exec grim ~/pics/screenshots/$(date +%Y-%m-%d-%H-%M-%S).png
+
+# Lock & pause
+bindsym $mod+Shift+s exec "playerctl -a pause; swaylock -f -c 282828"
+
+# Notifications
+bindsym $mod+n exec makoctl dismiss
+bindsym $mod+Shift+n exec makoctl dismiss --all
+
+# Display mode switching
+bindsym --no-repeat F7 exec ~/.config/sway/display-toggle.sh
+
+# ── Bar ───────────────────────────────────────────────────────────────────────
+bar {
+ swaybar_command waybar
+}
+
+# ── Autostart ─────────────────────────────────────────────────────────────────
+exec systemctl --user import-environment WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
+exec dbus-update-activation-environment --systemd WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
+exec mako
+exec "sleep 2 && ~/.config/sway/display-toggle.sh init"
diff --git a/dot_config/sway/executable_display-toggle.sh b/dot_config/sway/executable_display-toggle.sh
new file mode 100755
index 0000000..f39bf9b
--- /dev/null
+++ b/dot_config/sway/executable_display-toggle.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# Toggle display mode: laptop-off ↔ side-by-side
+# Bound to F7 in sway config; also runs at startup with "init"
+
+STATE_FILE="${XDG_RUNTIME_DIR:-/tmp}/display-mode"
+
+OUTPUTS=$(swaymsg -t get_outputs -r)
+LAPTOP=$(echo "$OUTPUTS" | jq -r '[.[] | select(.name | test("^eDP")) | .name] | first // empty')
+EXTERNAL=$(echo "$OUTPUTS" | jq -r '[.[] | select(.name | test("^eDP") | not) | .name] | first // empty')
+
+if [ -z "$EXTERNAL" ]; then
+ [ -z "$1" ] && notify-send "Display" "No external display connected"
+ exit 0
+fi
+
+[ -z "$LAPTOP" ] && exit 0
+
+LAPTOP_WIDTH=$(echo "$OUTPUTS" | jq -r ".[] | select(.name == \"$LAPTOP\") | .current_mode.width // .modes[0].width")
+[ -z "$LAPTOP_WIDTH" ] && LAPTOP_WIDTH=1920
+
+if [ "$1" = "init" ]; then
+ NEXT="laptop-off"
+else
+ CURRENT=$(cat "$STATE_FILE" 2>/dev/null || echo "laptop-off")
+ case "$CURRENT" in
+ laptop-off) NEXT="side-by-side" ;;
+ *) NEXT="laptop-off" ;;
+ esac
+fi
+
+case "$NEXT" in
+ laptop-off)
+ swaymsg output "$LAPTOP" disable || true
+ swaymsg output "$EXTERNAL" enable || true
+ swaymsg workspace number 1 || true
+ echo "laptop-off" > "$STATE_FILE"
+ [ -z "$1" ] && notify-send "Display" "Laptop screen off"
+ ;;
+ side-by-side)
+ swaymsg output "$LAPTOP" enable pos 0 0 || true
+ swaymsg output "$EXTERNAL" enable pos "$LAPTOP_WIDTH" 0 || true
+ echo "side-by-side" > "$STATE_FILE"
+ [ -z "$1" ] && notify-send "Display" "Side by side"
+ ;;
+esac
diff --git a/dot_config/systemd/user/bridge.service.d/override.conf b/dot_config/systemd/user/bridge.service.d/override.conf
new file mode 100644
index 0000000..3938b3c
--- /dev/null
+++ b/dot_config/systemd/user/bridge.service.d/override.conf
@@ -0,0 +1,2 @@
+[Service]
+Environment="PASSWORD_STORE_DIR=%h/.local/share/password-store" \ No newline at end of file
diff --git a/dot_config/systemd/user/vdirsyncer.service.d/override.conf b/dot_config/systemd/user/vdirsyncer.service.d/override.conf
new file mode 100644
index 0000000..6bc6060
--- /dev/null
+++ b/dot_config/systemd/user/vdirsyncer.service.d/override.conf
@@ -0,0 +1,2 @@
+[Service]
+Environment="PASSWORD_STORE_DIR=%h/.local/share/password-store"
diff --git a/dot_config/user-dirs.dirs b/dot_config/user-dirs.dirs
new file mode 100644
index 0000000..6dd2f73
--- /dev/null
+++ b/dot_config/user-dirs.dirs
@@ -0,0 +1,8 @@
+XDG_DESKTOP_DIR="$HOME/"
+XDG_DOCUMENTS_DIR="$HOME/docs"
+XDG_DOWNLOAD_DIR="$HOME/dl"
+XDG_MUSIC_DIR="$HOME/"
+XDG_PICTURES_DIR="$HOME/pics"
+XDG_PUBLICSHARE_DIR="$HOME/"
+XDG_TEMPLATES_DIR="$HOME/"
+XDG_VIDEOS_DIR="$HOME/vids"
diff --git a/dot_config/user-dirs.locale b/dot_config/user-dirs.locale
new file mode 100644
index 0000000..3e0b419
--- /dev/null
+++ b/dot_config/user-dirs.locale
@@ -0,0 +1 @@
+en_US \ No newline at end of file
diff --git a/dot_config/waybar/config.jsonc b/dot_config/waybar/config.jsonc
new file mode 100644
index 0000000..f3b0813
--- /dev/null
+++ b/dot_config/waybar/config.jsonc
@@ -0,0 +1,67 @@
+{
+ "layer": "top",
+ "height": 24,
+ "spacing": 0,
+
+ "modules-left": ["sway/workspaces", "sway/mode"],
+ "modules-center": ["sway/window"],
+ "modules-right": ["pulseaudio", "cpu", "memory", "temperature", "network", "battery", "clock", "tray"],
+
+ "sway/workspaces": {
+ "disable-scroll": true
+ },
+
+ "sway/window": {
+ "max-length": 60
+ },
+
+ "cpu": {
+ "format": "CPU {usage}%",
+ "interval": 5,
+ "tooltip": false
+ },
+
+ "memory": {
+ "format": "MEM {}%",
+ "interval": 10,
+ "tooltip-format": "{used:0.1f}G / {total:0.1f}G"
+ },
+
+ "temperature": {
+ "format": "{temperatureC}°C",
+ "critical-threshold": 80
+ },
+
+ "network": {
+ "format-wifi": "{essid} ({signalStrength}%)",
+ "format-ethernet": "{ifname}",
+ "format-disconnected": "disconnected",
+ "tooltip-format": "{ifname}: {ipaddr}/{cidr}"
+ },
+
+ "battery": {
+ "format": "BAT {capacity}%",
+ "format-charging": "CHR {capacity}%",
+ "format-plugged": "PLG {capacity}%",
+ "states": {
+ "warning": 30,
+ "critical": 15
+ }
+ },
+
+ "pulseaudio": {
+ "format": "VOL {volume}%",
+ "format-muted": "MUTE",
+ "scroll-step": 5,
+ "on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle"
+ },
+
+ "clock": {
+ "format": "{:%a %d %b %H:%M}",
+ "tooltip-format": "{:%Y-%m-%d %H:%M:%S}"
+ },
+
+ "tray": {
+ "spacing": 8
+ }
+}
diff --git a/dot_config/waybar/style.css b/dot_config/waybar/style.css
new file mode 100644
index 0000000..44df159
--- /dev/null
+++ b/dot_config/waybar/style.css
@@ -0,0 +1,71 @@
+* {
+ font-family: mono;
+ font-size: 8pt;
+}
+
+window#waybar {
+ background-color: #282828;
+ color: #ebdbb2;
+ border-bottom: 1px solid #3c3836;
+}
+
+#workspaces button {
+ padding: 0 5px;
+ color: #928374;
+ background-color: transparent;
+ border: none;
+ border-radius: 0;
+}
+
+#workspaces button.focused {
+ color: #ebdbb2;
+ background-color: #3c3836;
+ border-bottom: 2px solid #fabd2f;
+}
+
+#workspaces button.urgent {
+ color: #ebdbb2;
+ background-color: #fb4934;
+}
+
+#mode {
+ color: #fabd2f;
+ font-weight: bold;
+}
+
+#clock,
+#battery,
+#cpu,
+#memory,
+#temperature,
+#network,
+#pulseaudio,
+#tray {
+ padding: 0 6px;
+}
+
+#battery.warning {
+ color: #fabd2f;
+}
+
+#battery.critical {
+ color: #fb4934;
+}
+
+#temperature.critical {
+ color: #fb4934;
+}
+
+#pulseaudio.muted {
+ color: #928374;
+}
+
+tooltip {
+ background-color: #3c3836;
+ border: 1px solid #504945;
+ border-radius: 0;
+}
+
+tooltip label {
+ color: #ebdbb2;
+}
diff --git a/dot_config/wget/wgetrc b/dot_config/wget/wgetrc
new file mode 100644
index 0000000..a378bfd
--- /dev/null
+++ b/dot_config/wget/wgetrc
@@ -0,0 +1 @@
+hsts-file = ~/.local/share/wget-hsts
diff --git a/dot_config/yazi/keymap.toml b/dot_config/yazi/keymap.toml
new file mode 100644
index 0000000..b7ed5c7
--- /dev/null
+++ b/dot_config/yazi/keymap.toml
@@ -0,0 +1,9 @@
+[mgr]
+prepend_keymap = [
+ { on = "!", run = 'shell "$SHELL" --block', desc = "Open shell here" },
+ { on = [ "g", "r" ], run = 'shell -- ya emit cd "$(git rev-parse --show-toplevel 2>/dev/null || echo .)"', desc = "Go to git root" },
+ { on = [ "g", "." ], run = "cd ~/dotfiles", desc = "Go to dotfiles" },
+ { on = [ "g", "x" ], run = "cd ~/doxfiles", desc = "Go to doxfiles" },
+ { on = [ "g", "s" ], run = "cd ~/sync", desc = "Go to sync" },
+ { on = [ "g", "S" ], run = "cd ~/pics/screenshots", desc = "Go to screenshots" },
+]
diff --git a/dot_config/yazi/yazi.toml b/dot_config/yazi/yazi.toml
new file mode 100644
index 0000000..793ca59
--- /dev/null
+++ b/dot_config/yazi/yazi.toml
@@ -0,0 +1,5 @@
+[mgr]
+show_hidden = true
+scrolloff = 10
+linemode = "mtime"
+title_format = "Yazi: {cwd}"
diff --git a/dot_config/yt-dlp/config b/dot_config/yt-dlp/config
new file mode 100644
index 0000000..16dcbd7
--- /dev/null
+++ b/dot_config/yt-dlp/config
@@ -0,0 +1,9 @@
+# ignore unavailable videos
+-i
+# not interested in getting videos with a resolution higher than 1080p
+-f bv*[height<=1080]+ba/b[height<=1080]
+--sub-lang en,eng,pt,pt_pt,es,es_es
+--embed-subs
+
+--embed-thumbnail
+--embed-metadata
diff --git a/dot_config/zathura/zathurarc b/dot_config/zathura/zathurarc
new file mode 100644
index 0000000..c3e8d6f
--- /dev/null
+++ b/dot_config/zathura/zathurarc
@@ -0,0 +1,4 @@
+set selection-clipboard clipboard
+set incremental-search true
+set window-title-basename true
+set zoom-center true
diff --git a/dot_config/zellij/config.kdl b/dot_config/zellij/config.kdl
new file mode 100644
index 0000000..87ba6d1
--- /dev/null
+++ b/dot_config/zellij/config.kdl
@@ -0,0 +1,81 @@
+// Gruvbox dark theme (built-in)
+theme "gruvbox-dark"
+
+// Session resurrection
+serialize_pane_viewport true
+
+// Scrollback
+scroll_buffer_size 50000
+
+// Clipboard: copy to system clipboard on selection (OSC 52)
+copy_on_select true
+
+// Auto-attach to existing session with the same name instead of erroring
+attach_to_session true
+
+// Disable startup popups that overwrite stow-managed config when dismissed
+show_startup_tips false
+show_release_notes false
+
+// Clean UI: no pane borders, compact single-line status bar
+pane_frames false
+default_layout "compact"
+
+// Custom keybindings (merged with defaults)
+keybinds {
+ shared_except "locked" {
+ // Direct tab access (Alt+number)
+ bind "Alt 1" { GoToTab 1; }
+ bind "Alt 2" { GoToTab 2; }
+ bind "Alt 3" { GoToTab 3; }
+ bind "Alt 4" { GoToTab 4; }
+ bind "Alt 5" { GoToTab 5; }
+ bind "Alt 6" { GoToTab 6; }
+ bind "Alt 7" { GoToTab 7; }
+ bind "Alt 8" { GoToTab 8; }
+ bind "Alt 9" { GoToTab 9; }
+
+ // Tab management
+ bind "Alt t" { NewTab; }
+ bind "Alt [" { GoToPreviousTab; }
+ bind "Alt ]" { GoToNextTab; }
+
+ // Pane management
+ bind "Alt w" { ToggleFocusFullscreen; }
+ bind "Alt x" { CloseFocus; }
+ bind "Alt e" { EditScrollback; }
+
+ // Session
+ bind "Alt q" { Detach; }
+
+ // Quick resize without entering resize mode
+ bind "Alt -" { Resize "Decrease"; }
+ bind "Alt =" { Resize "Increase"; }
+
+ // Seamless vim/zellij pane navigation (requires smart-splits.nvim)
+ bind "Ctrl h" {
+ MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.3.0/vim-zellij-navigator.wasm" {
+ name "move_focus"
+ payload "left"
+ }
+ }
+ bind "Ctrl j" {
+ MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.3.0/vim-zellij-navigator.wasm" {
+ name "move_focus"
+ payload "down"
+ }
+ }
+ bind "Ctrl k" {
+ MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.3.0/vim-zellij-navigator.wasm" {
+ name "move_focus"
+ payload "up"
+ }
+ }
+ bind "Ctrl l" {
+ MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.3.0/vim-zellij-navigator.wasm" {
+ name "move_focus"
+ payload "right"
+ }
+ }
+ }
+}
diff --git a/dot_config/zsh/dot_zprofile b/dot_config/zsh/dot_zprofile
new file mode 100644
index 0000000..30b5b21
--- /dev/null
+++ b/dot_config/zsh/dot_zprofile
@@ -0,0 +1,116 @@
+# Login shell configuration — sourced once per session by zsh.
+# Sets environment variables, XDG paths, tool config, and host-specific overrides.
+
+# Guard against double-sourcing (e.g. nested login shells)
+[[ -n $__ZPROFILE_SOURCED ]] && return
+__ZPROFILE_SOURCED=1
+
+# ── PATH ──────────────────────────────────────────────────────────────────────
+typeset -U path # deduplicate PATH entries
+path=("$HOME/.local/bin" "$HOME/.local/share/nvim/mason/bin" $path)
+
+# ── XDG Base Directories ─────────────────────────────────────────────────────
+export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_DATA_HOME="$HOME/.local/share"
+export XDG_STATE_HOME="$HOME/.local/state"
+export XDG_CACHE_HOME="$HOME/.cache"
+
+# ── Locale ────────────────────────────────────────────────────────────────────
+export LANG=en_US.UTF-8
+
+# ── Terminal ──────────────────────────────────────────────────────────────────
+case $TERM in
+ *256color|*truecolor) export COLORTERM=24bit ;;
+esac
+
+export TERMINAL='ghostty'
+export BROWSER='linkhandler'
+export OPENER='xdg-open'
+
+# ── Editors ───────────────────────────────────────────────────────────────────
+export EDITOR='nvim'
+export VISUAL='nvim'
+export DIFFPROG='nvim -d'
+export MANPAGER='nvim +Man!'
+export MANWIDTH=999
+
+# ── less ──────────────────────────────────────────────────────────────────────
+export LESS="-F --RAW-CONTROL-CHARS"
+[[ -r /usr/bin/source-highlight-esc.sh ]] && export LESSOPEN="| /usr/bin/source-highlight-esc.sh %s"
+
+# ── GPG / SSH ─────────────────────────────────────────────────────────────────
+unset SSH_AGENT_PID
+export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
+
+# ── FZF ───────────────────────────────────────────────────────────────────────
+export FZF_DEFAULT_COMMAND="fd --type file --follow --hidden --exclude .git --color=always"
+export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
+export FZF_DIRS_COMMAND="fd --type d --follow --hidden --exclude .git --color=always"
+export FZF_DEFAULT_OPTS="--ansi --layout=reverse --inline-info --cycle --color=dark --color=fg:-1,bg:-1,hl:#5fff87,fg+:-1,bg+:-1,hl+:#ffaf5f --color=info:#af87ff,prompt:#5fff87,pointer:#ff87d7,marker:#ff87d7,spinner:#ff87d7"
+export FZF_CTRL_T_OPTS="--preview 'bat --color=always --style=numbers --line-range=:500 {}' --select-1 --exit-0"
+export FZF_CTRL_R_OPTS="--preview 'echo {}' --preview-window down:3:hidden:wrap --bind '?:toggle-preview' --sort --exact"
+export FZF_ALT_C_OPTS="--preview 'tree -C {} | head -200'"
+
+# ── Git prompt ────────────────────────────────────────────────────────────────
+export GIT_PS1_SHOWDIRTYSTATE=1
+export GIT_PS1_SHOWSTASHSTATE=1
+unset GIT_PS1_SHOWUNTRACKEDFILES
+export GIT_PS1_SHOWUPSTREAM="verbose"
+export GIT_PS1_SHOWCONFLICTSTATE="yes"
+export GIT_PS1_DESCRIBE_STYLE="branch"
+export GIT_PS1_SHOWCOLORHINTS=1
+export GIT_PS1_HIDE_IF_PWD_IGNORED=1
+
+# ── GCC ───────────────────────────────────────────────────────────────────────
+export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+
+# ── Java ──────────────────────────────────────────────────────────────────────
+# System AA fonts, GTK L&F, XDG prefs dir, GTK2 for compatibility
+export _JAVA_OPTIONS="-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel -Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java -Djdk.gtk.version=2"
+# Fix for non-reparenting WMs (sway, dwm, etc.)
+export _JAVA_AWT_WM_NONREPARENTING=1
+
+# ── Miscellaneous ─────────────────────────────────────────────────────────────
+export QT_QPA_PLATFORMTHEME=qt6ct
+export NO_AT_BRIDGE=1 # suppress GTK accessibility bus warnings
+export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"
+export INPUTRC="$XDG_CONFIG_HOME/sh/inputrc"
+
+# ── Wayland ───────────────────────────────────────────────────────────────────
+export XDG_CURRENT_DESKTOP=sway
+export MOZ_ENABLE_WAYLAND=1
+
+# ── XDG cleanup: keep $HOME tidy ─────────────────────────────────────────────
+# https://wiki.archlinux.org/title/XDG_Base_Directory#Partial
+export CARGO_HOME="$XDG_DATA_HOME/cargo"
+export CUDA_CACHE_PATH="$XDG_CACHE_HOME/nv"
+export GOPATH="$XDG_DATA_HOME/go"
+export GRADLE_USER_HOME="$XDG_DATA_HOME/gradle"
+export NODE_REPL_HISTORY="$XDG_DATA_HOME/node_repl_history"
+export PASSWORD_STORE_DIR="$XDG_DATA_HOME/password-store"
+export RUFF_CACHE_DIR="$XDG_CACHE_HOME/ruff"
+export RUSTUP_HOME="$XDG_DATA_HOME/rustup"
+export WGETRC="$XDG_CONFIG_HOME/wget/wgetrc"
+export WINEPREFIX="$XDG_DATA_HOME/wineprefixes/default"
+
+# ── Host-specific ─────────────────────────────────────────────────────────────
+case $(uname -n) in
+ halley2)
+ export LIBVA_DRIVER_NAME="iHD"
+ export MESA_LOADER_DRIVER_OVERRIDE="iris"
+ export VAAPI_MPEG4_ENABLED=true
+ ;;
+ hercules)
+ export OCL_ICD_VENDORS=nvidia
+ [[ -r "$XDG_CONFIG_HOME/sh/work-envrc" ]] && source "$XDG_CONFIG_HOME/sh/work-envrc"
+ ;;
+esac
+
+# ── Secrets (from pass) ──────────────────────────────────────────────────────
+(( $+commands[pass] )) && export FIRECRAWL_API_KEY="$(pass show copilot/firecrawl-api-key)"
+
+# ── Auto-start sway on VT1 ────────────────────────────────────────────────────
+if [[ -z $WAYLAND_DISPLAY && $XDG_VTNR == 1 ]]; then
+ export XDG_SESSION_TYPE=wayland
+ exec sway
+fi
diff --git a/dot_config/zsh/dot_zshrc b/dot_config/zsh/dot_zshrc
new file mode 100644
index 0000000..fad4bca
--- /dev/null
+++ b/dot_config/zsh/dot_zshrc
@@ -0,0 +1,322 @@
+# Interactive zsh configuration.
+
+# ── Terminal ──────────────────────────────────────────────────────────────────
+stty -ixon # disable XON/XOFF flow control (frees Ctrl-S/Ctrl-Q)
+ttyctl -f # freeze terminal state; programs can't leave it broken
+
+# ── Options ───────────────────────────────────────────────────────────────────
+# Note: appendhistory, nomatch, notify are zsh defaults — not set here.
+setopt autocd # cd by typing directory name
+setopt extendedglob # extended glob patterns (#, ~, ^)
+setopt interactivecomments # allow # comments in interactive shell
+setopt rmstarsilent # don't confirm rm *
+setopt prompt_subst # expand variables/functions in prompt
+setopt auto_pushd # cd pushes old dir onto stack (cd -<TAB> to browse)
+setopt pushd_ignore_dups # don't push duplicate dirs onto stack
+unsetopt beep # no terminal bell
+
+# ── History ───────────────────────────────────────────────────────────────────
+HISTFILE="$XDG_STATE_HOME/zsh/history"
+HISTSIZE=50000
+SAVEHIST=50000
+setopt extended_history # save timestamp and duration per entry
+setopt share_history # share history across concurrent sessions
+setopt hist_ignore_all_dups # remove older duplicate when adding new entry
+setopt hist_find_no_dups # skip duplicates when searching history
+setopt hist_reduce_blanks # trim superfluous whitespace from entries
+setopt hist_ignore_space # commands starting with space are not saved
+
+# ── Emacs keybindings ─────────────────────────────────────────────────────────
+bindkey -e
+
+# ── Prompt ────────────────────────────────────────────────────────────────────
+autoload -Uz colors && colors
+source /usr/share/git/completion/git-prompt.sh
+PROMPT='%B%{$fg[green]%}%n%{$reset_color%}@%{$fg[cyan]%}%m%{$reset_color%}:%b%{$fg[yellow]%}%~%{$reset_color%}$(__git_ps1 " (%s)")%(?..[%{$fg[red]%}%?%{$reset_color%}]) %(!.#.>) '
+
+# ── Completion ────────────────────────────────────────────────────────────────
+fpath=($XDG_DATA_HOME/zsh/completion $fpath)
+autoload -Uz compinit && compinit -d "$XDG_CACHE_HOME/zsh/zcompdump"
+
+zstyle ':completion:*' menu select # arrow-key driven menu for ambiguous completions
+zstyle ':completion:*' completer _expand_alias _complete _ignored _match _approximate
+# │ │ │ │ └ fuzzy match (typo tolerance)
+# │ │ │ └ try pattern matching
+# │ │ └ include normally hidden completions
+# │ └ standard completion
+# └ expand aliases before completing
+zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS} # colorize file completions like ls
+zstyle ':completion:*' use-cache on # cache completions (speeds up pip, dpkg, etc.)
+zstyle ':completion:*' cache-path "$XDG_CACHE_HOME/zsh"
+zstyle ':completion:*:match:*' original only # only show original when pattern-matching
+zstyle ':completion:*:functions' ignored-patterns '_*' # hide internal completion functions
+zstyle ':completion:*:*:kill:*' menu yes select # interactive menu for kill completion
+zstyle ':completion:*:kill:*' force-list always # always show process list for kill
+zstyle ':completion:*:cd:*' ignore-parents parent pwd # cd never completes . or ..
+zstyle ':completion::complete:*' gain-privileges 1 # use doas/sudo for privileged completions
+zstyle -e ':completion:*:approximate:*' \
+ max-errors 'reply=($((($#PREFIX+$#SUFFIX)/3))numeric)' # allow 1 typo per 3 chars typed
+
+_comp_options+=(globdots) # include hidden files in completion
+
+# ── Terminal key setup ─────────────────────────────────────────────────────────
+# Application mode ensures terminfo values are valid during line editing.
+# Without this, some terminals send wrong sequences for special keys.
+if (( ${+terminfo[smkx]} && ${+terminfo[rmkx]} )); then
+ autoload -Uz add-zle-hook-widget
+ function zle_application_mode_start { echoti smkx }
+ function zle_application_mode_stop { echoti rmkx }
+ add-zle-hook-widget -Uz zle-line-init zle_application_mode_start
+ add-zle-hook-widget -Uz zle-line-finish zle_application_mode_stop
+fi
+
+# Up/Down stored for history-substring-search bindings (set after plugin source)
+typeset -g -A key
+key[Up]="${terminfo[kcuu1]}"
+key[Down]="${terminfo[kcud1]}"
+
+# ── Custom keybindings ────────────────────────────────────────────────────────
+bindkey \^U backward-kill-line
+
+# Word navigation (Ctrl-Right also accepts autosuggestion word-by-word — fish-like)
+bindkey '^[[1;5C' forward-word # Ctrl-Right
+bindkey '^[[1;5D' backward-word # Ctrl-Left
+bindkey '^[[1;3C' forward-word # Alt-Right
+bindkey '^[[1;3D' backward-word # Alt-Left
+bindkey '^H' backward-kill-word # Ctrl-Backspace
+bindkey '^[[3;5~' kill-word # Ctrl-Delete
+
+# Ctrl-Z: toggle foreground/background (no need to type 'fg')
+toggle-fg-bg() {
+ if (( ${#jobstates} )); then
+ zle .push-input
+ BUFFER="fg"
+ zle .accept-line
+ else
+ zle .push-input
+ zle .clear-screen
+ fi
+}
+zle -N toggle-fg-bg
+bindkey '^Z' toggle-fg-bg
+
+# Ctrl-D exits even on non-empty line
+exit_zsh() { exit }
+zle -N exit_zsh
+bindkey '^D' exit_zsh
+
+# Ctrl-X Ctrl-E: edit command in $EDITOR
+autoload -Uz edit-command-line
+zle -N edit-command-line
+bindkey "^X^E" edit-command-line
+
+# Ctrl-Y: copy current command line to clipboard (OSC 52 — terminal-native)
+copy-line-to-clipboard() { printf '\033]52;c;%s\a' "$(echo -n "$BUFFER" | base64)" }
+zle -N copy-line-to-clipboard
+bindkey '^Y' copy-line-to-clipboard
+
+# ── Word style ────────────────────────────────────────────────────────────────
+# Ctrl-W/Alt-B/Alt-F use shell quoting rules for word boundaries
+autoload -Uz select-word-style
+select-word-style shell
+
+# ── Smart dot expansion ───────────────────────────────────────────────────────
+# Typing .. automatically expands: ... → ../.. , .... → ../../.. , etc.
+rationalise-dot() {
+ if [[ $LBUFFER = *.. ]]; then
+ LBUFFER+=/..
+ else
+ LBUFFER+=.
+ fi
+}
+zle -N rationalise-dot
+bindkey . rationalise-dot
+
+# ── Window title ──────────────────────────────────────────────────────────────
+autoload -Uz add-zsh-hook
+
+xterm_title_precmd() { print -Pn -- '\e]2;%~\a' }
+xterm_title_preexec() { print -Pn -- '\e]2;%~ %# ' && print -n -- "${(q)1}\a" }
+
+if [[ "$TERM" == (xterm-ghostty|st*|screen*|xterm*|rxvt*|tmux*|putty*|konsole*|gnome*) ]]; then
+ add-zsh-hook -Uz precmd xterm_title_precmd
+ add-zsh-hook -Uz preexec xterm_title_preexec
+fi
+
+# ── Zellij tab naming (dir:cmd like tmux) ────────────────────────────────────
+if [[ -n "$ZELLIJ" ]]; then
+ _zellij_dir() { [[ "$PWD" == "$HOME" ]] && echo '~' || echo "${PWD##*/}"; }
+ _zellij_tab_idx() { echo $(( $(zellij action current-tab-info 2>/dev/null | grep -oP 'position: \K\d+') + 1 )); }
+ _zellij_tab_precmd() { zellij action rename-tab "$(_zellij_tab_idx):$(_zellij_dir)" 2>/dev/null; }
+ _zellij_tab_preexec() { zellij action rename-tab "$(_zellij_tab_idx):$(_zellij_dir):${1%% *}" 2>/dev/null; }
+ add-zsh-hook precmd _zellij_tab_precmd
+ add-zsh-hook preexec _zellij_tab_preexec
+fi
+
+# ── Recent directories ────────────────────────────────────────────────────────
+autoload -Uz chpwd_recent_dirs cdr
+add-zsh-hook chpwd chpwd_recent_dirs
+[[ -d ${XDG_STATE_HOME}/zsh ]] || mkdir -p "${XDG_STATE_HOME}/zsh"
+zstyle ':chpwd:*' recent-dirs-file "$XDG_STATE_HOME/zsh/chpwd-recent-dirs"
+zstyle ':completion:*:*:cdr:*:*' menu selection
+
+# ── OSC 7 — report CWD to terminal (zellij uses this for new pane/tab CWD) ──
+_osc7_chpwd() {
+ printf '\e]7;file://%s%s\e\\' "${HOST}" "${PWD}"
+}
+add-zsh-hook chpwd _osc7_chpwd
+_osc7_chpwd
+
+# ── Help system ───────────────────────────────────────────────────────────────
+autoload -Uz run-help run-help-git run-help-ip
+(( $+aliases[run-help] )) && unalias run-help
+alias help=run-help
+
+# ── Bracketed paste ───────────────────────────────────────────────────────────
+autoload -Uz bracketed-paste-magic
+zle -N bracketed-paste bracketed-paste-magic
+
+# ── Aliases ───────────────────────────────────────────────────────────────────
+# Files
+alias l='lsd -l'
+alias la='lsd -lA'
+alias lt='lsd --tree'
+alias mkdir='mkdir -p'
+alias du='du -h'
+alias df='df -h'
+alias free='free -h'
+
+# Grep / diff with color
+alias grep='grep --color=auto'
+alias fgrep='grep -F --color=auto'
+alias egrep='grep -E --color=auto'
+alias diff='diff --color=auto'
+alias dmesg='dmesg --color=auto'
+alias dm='dmesg --color=always | less -r'
+
+# Networking
+alias ip="ip -color=auto"
+alias lsip="ip -human -color=auto --brief address show"
+alias ipa="ip -stats -details -human -color=auto address show"
+alias ipecho='curl ipecho.net/plain'
+alias ss='doas ss -tupnl'
+
+# Privilege escalation
+alias sudo='doas'
+alias sudoedit='doasedit'
+alias gimme='doas chown $USER:$(id -gn $USER)'
+alias pacdiff='doas pacdiff'
+
+# Pacman
+alias pacopt='comm -13 <(pacman -Qqdt | sort) <(pacman -Qqdtt | sort)'
+
+# Git
+alias g='git'
+
+# Systemd
+alias sys='systemctl'
+alias ssys='doas systemctl'
+alias sysu='systemctl --user'
+
+# Navigation
+alias c='clear'
+
+# Yazi: cd-on-exit wrapper
+y() {
+ local tmp="$(mktemp -t "yazi-cwd.XXXXXX")"
+ command yazi "$@" --cwd-file="$tmp"
+ IFS= read -r -d '' cwd < "$tmp"
+ [[ "$cwd" != "$PWD" ]] && [[ -d "$cwd" ]] && builtin cd -- "$cwd"
+ rm -f -- "$tmp"
+}
+
+# Tools
+alias stow='stow -R --no-folding --adopt'
+alias curl='curlie'
+alias cpr='rsync --archive -hh --partial --info=stats1,progress2 --modify-window=1'
+alias mvr='rsync --archive -hh --partial --info=stats1,progress2 --modify-window=1 --remove-source-files'
+alias sub='subliminal download -l en'
+
+# Neovim
+alias n='nvim'
+alias ndiff='nvim -d'
+alias nd='nvim -d'
+alias nview='nvim -R'
+alias nv='nvim -R'
+alias ng='nvim +Neogit'
+
+# Zellij: smart attach — 0 sessions: create, 1: attach, many: welcome picker
+za() {
+ if [[ -n $ZELLIJ ]]; then
+ echo "Already inside zellij" >&2
+ return 1
+ fi
+ local -a sessions=("${(@f)$(zellij list-sessions -ns 2>/dev/null)}")
+ sessions=(${sessions:#})
+ case ${#sessions} in
+ 0) zellij ;;
+ 1) zellij attach "${sessions[1]}" ;;
+ *) zellij -l welcome ;;
+ esac
+}
+
+# Just
+alias j='just'
+
+# LLVM / Clang tooling
+alias ncmake='cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS="$DEV_CFLAGS" -DCMAKE_CXX_FLAGS="$DEV_CFLAGS" -DCMAKE_INSTALL_PREFIX=build/install -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build'
+alias ircc='clang -S -emit-llvm -fno-discard-value-names -O0 -Xclang -disable-O0-optnone -o -'
+alias irfc='flang -S -emit-llvm -O0 -o -'
+alias astcc='clang -Xclang -ast-dump -fsyntax-only'
+alias astfc='flang -fc1 -fdebug-dump-parse-tree'
+alias symfc='flang -fc1 -fdebug-dump-symbols'
+alias gdbr='gdb -ex start --args'
+
+# GitHub Copilot CLI
+alias copilot='gh copilot --autopilot --enable-all-github-mcp-tools --yolo --resume'
+
+# ── Alias completions ─────────────────────────────────────────────────────────
+compdef g=git
+compdef j=just
+compdef n=nvim ndiff=nvim nd=nvim nview=nvim nv=nvim
+compdef sys=systemctl ssys=systemctl sysu=systemctl
+compdef l=lsd la=lsd lt=lsd
+
+# ── GPG agent ─────────────────────────────────────────────────────────────────
+# Set GPG_TTY to this shell's actual TTY (not the login console) and tell
+# the agent so pinentry prompts appear in the right terminal
+export GPG_TTY=$TTY
+gpg-connect-agent updatestartuptty /bye &>/dev/null
+
+# ── Zoxide (smart directory jumping) ──────────────────────────────────────────
+# z foo → jump to frecency-ranked dir matching "foo"
+# zi → interactive picker with fzf
+eval "$(zoxide init zsh)"
+
+# ── FZF ───────────────────────────────────────────────────────────────────────
+source <(fzf --zsh)
+
+# Ctrl-X Ctrl-R: search history with fzf and immediately execute
+fzf-history-widget-accept() {
+ fzf-history-widget
+ zle accept-line
+}
+zle -N fzf-history-widget-accept
+bindkey '^X^R' fzf-history-widget-accept
+
+_fzf_compgen_path() { fd --hidden --follow --exclude ".git" . "$1" }
+_fzf_compgen_dir() { fd --type d --hidden --follow --exclude ".git" . "$1" }
+
+# ── Plugins (must be sourced last) ────────────────────────────────────────────
+# Highlight config must be set BEFORE sourcing the plugin
+ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets pattern)
+typeset -A ZSH_HIGHLIGHT_STYLES
+ZSH_HIGHLIGHT_STYLES[comment]='fg=yellow'
+source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+
+source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
+bindkey '^[[Z' autosuggest-accept # Shift-Tab to accept suggestion
+
+source /usr/share/zsh/plugins/zsh-history-substring-search/zsh-history-substring-search.zsh
+[[ -n "${key[Up]}" ]] && bindkey -- "${key[Up]}" history-substring-search-up
+[[ -n "${key[Down]}" ]] && bindkey -- "${key[Down]}" history-substring-search-down