From 261f30a460d974de5ceccac495ff04eefb81453d Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:25:18 +0100 Subject: [PATCH 01/27] chore: remove dotbot --- .gitmodules | 5 ---- dotbot | 1 - install | 15 ---------- install.conf.yaml | 73 ----------------------------------------------- 4 files changed, 94 deletions(-) delete mode 160000 dotbot delete mode 100644 install delete mode 100644 install.conf.yaml diff --git a/.gitmodules b/.gitmodules index b3878819..2b2495e9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,3 @@ -[submodule "dotbot"] - path = dotbot - url = https://github.com/anishathalye/dotbot - ignore = dirty - [submodule "files/.config/tmux/plugins/tpm"] path = files/.config/tmux/plugins/tpm url = https://github.com/tmux-plugins/tpm diff --git a/dotbot b/dotbot deleted file mode 160000 index 328bcb32..00000000 --- a/dotbot +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 328bcb32590e5057b09bd27a40bc2fb21385fbf3 diff --git a/install b/install deleted file mode 100644 index 5a7e72c4..00000000 --- a/install +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -e - -CONFIG="install.conf.yaml" -DOTBOT_DIR="dotbot" - -DOTBOT_BIN="bin/dotbot" -BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -cd "${BASEDIR}" -git -C "${DOTBOT_DIR}" submodule sync --quiet --recursive -git submodule update --init --recursive "${DOTBOT_DIR}" - -"${BASEDIR}/${DOTBOT_DIR}/${DOTBOT_BIN}" -d "${BASEDIR}" -c "${CONFIG}" "${@}" diff --git a/install.conf.yaml b/install.conf.yaml deleted file mode 100644 index 58b87169..00000000 --- a/install.conf.yaml +++ /dev/null @@ -1,73 +0,0 @@ -- defaults: - link: - create: true - relink: true - -- create: - ~/.ssh: - mode: 0600 - ~/.config: - ~/Projects: - ~/Projects/work: - ~/Projects/personal: - -- clean: ["~"] - -- link: - ~/.ssh: - force: true - mode: 0600 - path: ~/Projects/personal/private-dotfiles/files/.ssh - ~/.gitconfig: - force: true - path: files/.gitconfig - ~/.gitmessage: - force: true - path: files/.gitmessage - ~/.bash_profile: - force: true - path: files/.bash_profile - ~/.bashrc: - force: true - path: files/.bashrc - ~/.zshrc: - force: true - path: files/.zshrc - ~/.sh_profile: - force: true - path: files/.sh_profile - ~/.rgignore: - force: true - path: files/.rgignore - ~/.config: - force: true - path: files/.config - ~/.hammerspoon: - force: true - path: files/.hammerspoon - ~/.amethyst.yml: - force: true - path: files/.amethyst.yml - ~/.dotfiles: - force: true - path: ~/Projects/personal/dotfiles - -- shell: - - [ - zsh -c 'source ~/.zshrc; brew bundle --file=$HOME/.Brewfile', - Install homebrew dependencies, - ] - - [git submodule update --init --recursive, Installing submodules] - - [bash extra/macos_settings.sh, Installing macOS config] - - [bash extra/keyboard.sh, Installing keyboard config] - - [ - zsh -c 'source ~/.zshrc; python3 -m pip install -r $HOME/.dotfiles/requirements.txt', - Installing python packages, - ] - - [ - zsh -c 'source ~/.zshrc; tmux start-server && tmux new-session -d && ~/.config/tmux/plugins/tpm/scripts/install_plugins.sh && tmux kill-server', - Installing python packages, - ] - # - [source ~/.zshrc, Install fzf marks] - # - [ bash extra/nvim.sh, Installing neovim config] - # - [cp -n ./git/config.template ~/.gitconfig.local] From 77f6e90a9aeba604a885d55dfd9f40c0c16e214e Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:28:59 +0100 Subject: [PATCH 02/27] feat: replace dotbot with plain bash install scripts --- extra/setup.sh | 56 +++++++++++++++++++++++++++++++++++++++++++++++ extra/symlinks.sh | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100755 extra/setup.sh create mode 100755 extra/symlinks.sh diff --git a/extra/setup.sh b/extra/setup.sh new file mode 100755 index 00000000..61db8147 --- /dev/null +++ b/extra/setup.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Post-symlink setup steps. +# Run after extra/symlinks.sh to install packages and configure the system. +# +# Usage: bash extra/setup.sh + +set -euo pipefail + +DOTFILES="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +step() { echo; echo "==> $*"; } + +# ------------------------------------------------------------------------------ +# Homebrew packages +# ------------------------------------------------------------------------------ +step "Installing Homebrew packages" +brew bundle --file="$DOTFILES/homebrew/Brewfile" + +# ------------------------------------------------------------------------------ +# Git submodules (tpm, zsh plugins, etc.) +# ------------------------------------------------------------------------------ +step "Updating git submodules" +git -C "$DOTFILES" submodule update --init --recursive + +# ------------------------------------------------------------------------------ +# macOS system settings +# ------------------------------------------------------------------------------ +if [[ "$(uname)" == "Darwin" ]]; then + step "Applying macOS settings" + bash "$DOTFILES/extra/macos/macos_settings.sh" + + step "Applying keyboard config" + bash "$DOTFILES/extra/keyboard.sh" +fi + +# ------------------------------------------------------------------------------ +# Python packages +# ------------------------------------------------------------------------------ +if [[ -f "$DOTFILES/requirements.txt" ]]; then + step "Installing Python packages" + python3 -m pip install -r "$DOTFILES/requirements.txt" +fi + +# ------------------------------------------------------------------------------ +# tmux plugins via TPM +# ------------------------------------------------------------------------------ +step "Installing tmux plugins" +tmux start-server +tmux new-session -d -s __setup__ 2>/dev/null || true +~/.local/share/tmux/plugins/tpm/scripts/install_plugins.sh +tmux kill-session -t __setup__ 2>/dev/null || true + +echo +echo "Setup complete." + +# vim: ft=bash diff --git a/extra/symlinks.sh b/extra/symlinks.sh new file mode 100755 index 00000000..db26555f --- /dev/null +++ b/extra/symlinks.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Create symlinks from the dotfiles repo into $HOME. +# Replaces what dotbot's install.conf.yaml used to do. +# +# Usage: bash extra/symlinks.sh [--force] + +set -euo pipefail + +DOTFILES="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +FORCE=0 +[[ "${1:-}" == "--force" ]] && FORCE=1 + +link() { + local src="$1" dst="$2" + if [[ $FORCE -eq 1 ]]; then + ln -sfn "$src" "$dst" + elif [[ -e "$dst" || -L "$dst" ]]; then + echo " skip $dst (already exists, use --force to overwrite)" + return + else + ln -sn "$src" "$dst" + fi + echo " link $dst -> $src" +} + +# ------------------------------------------------------------------------------ +# Create directories +# ------------------------------------------------------------------------------ +mkdir -p ~/.ssh && chmod 600 ~/.ssh +mkdir -p ~/.config +mkdir -p ~/Projects/work ~/Projects/personal + +# ------------------------------------------------------------------------------ +# Symlinks +# ------------------------------------------------------------------------------ +link "$DOTFILES/files/.gitconfig" ~/.gitconfig +link "$DOTFILES/files/.gitmessage" ~/.gitmessage +link "$DOTFILES/files/.bash_profile" ~/.bash_profile +link "$DOTFILES/files/.bashrc" ~/.bashrc +link "$DOTFILES/files/.zshrc" ~/.zshrc +link "$DOTFILES/files/.zprofile" ~/.zprofile +link "$DOTFILES/files/.sh_profile" ~/.sh_profile +link "$DOTFILES/files/.rgignore" ~/.rgignore +link "$DOTFILES/files/.config" ~/.config +link "$DOTFILES/files/.hammerspoon" ~/.hammerspoon +link "$DOTFILES/files/.amethyst.yml" ~/.amethyst.yml +link "$DOTFILES" ~/.dotfiles + +# SSH keys live in private-dotfiles +if [[ -d ~/Projects/personal/private-dotfiles/files/.ssh ]]; then + link ~/Projects/personal/private-dotfiles/files/.ssh ~/.ssh +fi + +echo "Done." + +# vim: ft=bash From 9652dddb2bea831fe0b2a074800daf604c35ef87 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:37:29 +0100 Subject: [PATCH 03/27] feat: rewrite symlinks.sh to use GNU Stow --- extra/symlinks.sh | 80 ++++++++++++++++++++++------------------ files/.stow-local-ignore | 5 +++ 2 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 files/.stow-local-ignore diff --git a/extra/symlinks.sh b/extra/symlinks.sh index db26555f..2632ccff 100755 --- a/extra/symlinks.sh +++ b/extra/symlinks.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash -# Create symlinks from the dotfiles repo into $HOME. -# Replaces what dotbot's install.conf.yaml used to do. +# Create symlinks from the dotfiles repo into $HOME via GNU Stow. +# Idempotent: safe to run multiple times. # # Usage: bash extra/symlinks.sh [--force] +# --force Remove pre-existing symlinks not owned by stow before stowing. +# Required on first migration from another symlink manager. set -euo pipefail @@ -10,47 +12,53 @@ DOTFILES="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" FORCE=0 [[ "${1:-}" == "--force" ]] && FORCE=1 -link() { - local src="$1" dst="$2" - if [[ $FORCE -eq 1 ]]; then - ln -sfn "$src" "$dst" - elif [[ -e "$dst" || -L "$dst" ]]; then - echo " skip $dst (already exists, use --force to overwrite)" - return - else - ln -sn "$src" "$dst" - fi - echo " link $dst -> $src" -} - # ------------------------------------------------------------------------------ -# Create directories +# Directories # ------------------------------------------------------------------------------ -mkdir -p ~/.ssh && chmod 600 ~/.ssh -mkdir -p ~/.config +mkdir -p ~/.ssh && chmod 700 ~/.ssh mkdir -p ~/Projects/work ~/Projects/personal # ------------------------------------------------------------------------------ -# Symlinks -# ------------------------------------------------------------------------------ -link "$DOTFILES/files/.gitconfig" ~/.gitconfig -link "$DOTFILES/files/.gitmessage" ~/.gitmessage -link "$DOTFILES/files/.bash_profile" ~/.bash_profile -link "$DOTFILES/files/.bashrc" ~/.bashrc -link "$DOTFILES/files/.zshrc" ~/.zshrc -link "$DOTFILES/files/.zprofile" ~/.zprofile -link "$DOTFILES/files/.sh_profile" ~/.sh_profile -link "$DOTFILES/files/.rgignore" ~/.rgignore -link "$DOTFILES/files/.config" ~/.config -link "$DOTFILES/files/.hammerspoon" ~/.hammerspoon -link "$DOTFILES/files/.amethyst.yml" ~/.amethyst.yml -link "$DOTFILES" ~/.dotfiles - -# SSH keys live in private-dotfiles -if [[ -d ~/Projects/personal/private-dotfiles/files/.ssh ]]; then - link ~/Projects/personal/private-dotfiles/files/.ssh ~/.ssh +# On --force: remove any existing symlinks for files in the stow package +# so stow can take ownership. Real files/dirs are left alone (resolve manually). +# ------------------------------------------------------------------------------ +if [[ $FORCE -eq 1 ]]; then + echo "==> Removing stale symlinks for stow package" + while IFS= read -r -d '' f; do + rel="${f#"$DOTFILES/files/"}" + target="$HOME/$rel" + [[ -L "$target" ]] && { echo " unlink $target"; rm "$target"; } + done < <(find "$DOTFILES/files" -maxdepth 1 -mindepth 1 -print0) +fi + +# ------------------------------------------------------------------------------ +# Stow the files/ package into $HOME +# --restow: re-links everything (idempotent, also cleans up obsolete links) +# ------------------------------------------------------------------------------ +echo "==> Stowing files/ -> $HOME" +# --adopt moves any real files that conflict into the stow package (adopts them), +# then --restow re-creates all links cleanly. Together they are idempotent. +stow --dir="$DOTFILES" --target="$HOME" --adopt --restow files +echo " done" + +# ------------------------------------------------------------------------------ +# Extras stow can't handle +# ------------------------------------------------------------------------------ + +# ~/.dotfiles self-link (used by scripts referencing $HOME/.dotfiles) +if [[ ! -e ~/.dotfiles || $FORCE -eq 1 ]]; then + ln -sfn "$DOTFILES" ~/.dotfiles + echo " link ~/.dotfiles -> $DOTFILES" +fi + +# SSH keys from private-dotfiles (separate repo, separate location) +PRIVATE_SSH=~/Projects/personal/private-dotfiles/files/.ssh +if [[ -d "$PRIVATE_SSH" && ( ! -e ~/.ssh || $FORCE -eq 1 ) ]]; then + ln -sfn "$PRIVATE_SSH" ~/.ssh + echo " link ~/.ssh -> $PRIVATE_SSH" fi +echo echo "Done." # vim: ft=bash diff --git a/files/.stow-local-ignore b/files/.stow-local-ignore new file mode 100644 index 00000000..9163dd94 --- /dev/null +++ b/files/.stow-local-ignore @@ -0,0 +1,5 @@ +# Files to ignore when stowing +\.DS_Store +\.git +config\.template +others\.md From f2467285a0779611af381f4b18154b4cdbe7c234 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:43:06 +0100 Subject: [PATCH 04/27] fix: recover missing dotbot behaviours --- extra/setup.sh | 9 +++++++++ extra/symlinks.sh | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/extra/setup.sh b/extra/setup.sh index 61db8147..0026eea2 100755 --- a/extra/setup.sh +++ b/extra/setup.sh @@ -41,6 +41,15 @@ if [[ -f "$DOTFILES/requirements.txt" ]]; then python3 -m pip install -r "$DOTFILES/requirements.txt" fi +# ------------------------------------------------------------------------------ +# Local gitconfig (machine-specific overrides, not tracked) +# ------------------------------------------------------------------------------ +if [[ ! -f ~/.gitconfig.local ]]; then + step "Creating ~/.gitconfig.local from template" + cp "$DOTFILES/files/config.template" ~/.gitconfig.local + echo " Edit ~/.gitconfig.local to set your name/email" +fi + # ------------------------------------------------------------------------------ # tmux plugins via TPM # ------------------------------------------------------------------------------ diff --git a/extra/symlinks.sh b/extra/symlinks.sh index 2632ccff..3b54d764 100755 --- a/extra/symlinks.sh +++ b/extra/symlinks.sh @@ -58,6 +58,14 @@ if [[ -d "$PRIVATE_SSH" && ( ! -e ~/.ssh || $FORCE -eq 1 ) ]]; then echo " link ~/.ssh -> $PRIVATE_SSH" fi +# ------------------------------------------------------------------------------ +# Clean broken symlinks in $HOME (equivalent to dotbot's clean: ["~"]) +# ------------------------------------------------------------------------------ +echo "==> Cleaning broken symlinks in $HOME" +find "$HOME" -maxdepth 1 -type l | while read -r link; do + [[ -e "$link" ]] || { echo " clean $link"; rm "$link"; } +done + echo echo "Done." From 4cb5956b4273672d304a5920d7aa04ca337e8c19 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:47:09 +0100 Subject: [PATCH 05/27] feat: update bash rc file --- files/.bashrc | 28 +++++++++++++++++++++------- files/.config/zsh/aliases.d/nav.sh | 1 - 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/files/.bashrc b/files/.bashrc index 8f5903b6..6ff01fe4 100644 --- a/files/.bashrc +++ b/files/.bashrc @@ -4,15 +4,28 @@ [[ -f "$HOME/.skip" ]] && source $HOME/.skip # Eval homebrew {{{ -# Homebrew -if [[ "$(uname -m)" == "x86_64" ]]; then - eval "$(/usr/local/bin/brew shellenv)" +# Get machine operative system + +export MACHINEOS=$($HOME/.dotfiles/scripts/machine.sh) +function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } +echo $MACHINEOS + +# Set OS-dependent stuff +if [[ "$MACHINEOS" == "Mac" ]]; then + if [[ "$(uname -m)" == "x86_64" ]]; then + export HOMEBREW_PREFIX="/usr/local" + else + export HOMEBREW_PREFIX="/opt/homebrew" + fi else - eval "$(/opt/homebrew/bin/brew shellenv)" + export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew" fi -# }}} +export HOMEBREW_PREFIX="/opt/homebrew" +eval $($HOMEBREW_PREFIX/bin/brew shellenv) +export XDG_DATA_DIRS="$HOMEBREW_PREFIX/share:$XDG_DATA_DIRS" +# }}} # source basuc functions source $HOME/.dotfiles/zsh/ufunctions.sh @@ -83,6 +96,7 @@ fi # vim: ft=bash fdm=marker -# BEGIN_FZF_THEME + +# BEGIN_FZF_THEME: carbon-mist source ~/.config/fzf/themes/carbon-mist.sh -# END_FZF_THEME +# END_FZF_THEME: carbon-mist diff --git a/files/.config/zsh/aliases.d/nav.sh b/files/.config/zsh/aliases.d/nav.sh index de904f64..1ad67d06 100644 --- a/files/.config/zsh/aliases.d/nav.sh +++ b/files/.config/zsh/aliases.d/nav.sh @@ -43,7 +43,6 @@ alias dnd='do-not-disturb toggle' alias md="mkdir -p" -alias cat='bat --paging=never --style=plain' batdiff() { git diff --name-only --relative --diff-filter=d | xargs bat --diff From 6564ba1f89a151e8f553405e4b365506e9d03a7b Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:50:24 +0100 Subject: [PATCH 06/27] chore: wire Makefile to extra/symlinks.sh and extra/setup.sh --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index be78676b..2a1ab178 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ FC=${HOME}/.dotfiles TMUX_SHARE=${HOME}/.local/share/tmux -all: brew macos kitty nvim vim tmux fzf-marks private zsh-plugins +all: install setup test: ${HOME}/.dotfiles/tests/zsh/sanity.sh @@ -17,9 +17,12 @@ macos: bash extra/keyboard.sh install: - stow --ignore ".DS_Store" --target="${HOME}" --dir="${FC}" files + bash ${FC}/extra/symlinks.sh rm -rf ~/Downloads; ln -sf "${HOME}/Library/Mobile Documents/com~apple~CloudDocs/Downloads" ~/Downloads +setup: + bash ${FC}/extra/setup.sh + projects: mkdir -p "${HOME}/Projects/icloud" stow --ignore ".DS_Store" --target="${HOME}/Projects/icloud" --dir="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/" Projects @@ -98,4 +101,4 @@ zsh-plugins: ln -sf ${HOMEBREW_PREFIX}/Cellar/alias-tips/alias-tips.plugin.zsh ${HOMEBREW_PREFIX}/share/zsh-alias-tips -.PHONY: all install brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test +.PHONY: all install setup brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test From 51d3819d5e56a4a743fcbe159ec7a8f491b4d53a Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 12 Mar 2026 22:52:49 +0100 Subject: [PATCH 07/27] fix: ensure stow/brew available before symlinking --- Makefile | 7 +++++-- extra/symlinks.sh | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2a1ab178..37b74c32 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,10 @@ macos: # now we change the keymaps bash extra/keyboard.sh -install: +homebrew: + @command -v brew >/dev/null || /bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +install: homebrew bash ${FC}/extra/symlinks.sh rm -rf ~/Downloads; ln -sf "${HOME}/Library/Mobile Documents/com~apple~CloudDocs/Downloads" ~/Downloads @@ -101,4 +104,4 @@ zsh-plugins: ln -sf ${HOMEBREW_PREFIX}/Cellar/alias-tips/alias-tips.plugin.zsh ${HOMEBREW_PREFIX}/share/zsh-alias-tips -.PHONY: all install setup brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test +.PHONY: all homebrew install setup brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test diff --git a/extra/symlinks.sh b/extra/symlinks.sh index 3b54d764..9ae66657 100755 --- a/extra/symlinks.sh +++ b/extra/symlinks.sh @@ -9,6 +9,20 @@ set -euo pipefail DOTFILES="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +# ------------------------------------------------------------------------------ +# Ensure stow is available (installed by Homebrew, which may not have run yet) +# ------------------------------------------------------------------------------ +if ! command -v stow >/dev/null 2>&1; then + if command -v brew >/dev/null 2>&1; then + echo "==> Installing stow via Homebrew" + brew install stow + else + echo "Error: stow not found and Homebrew is not available." >&2 + echo "Install stow manually (e.g. brew install stow) and re-run." >&2 + exit 1 + fi +fi FORCE=0 [[ "${1:-}" == "--force" ]] && FORCE=1 From 9514e5000b80589c611551b5b9c89f9676f25135 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 00:00:11 +0100 Subject: [PATCH 08/27] feat: add ci --- .github/workflows/ci.yml | 113 +++++++ Makefile | 7 +- dasfasdf | 280 ------------------ extra/setup.sh | 2 +- files/.config/zsh/README.md | 2 +- files/.config/zsh/rc.d/05-env.zsh | 16 - files/.config/zsh/rc.d/10-plugins.zsh | 5 +- files/.config/zsh/zshenv | 19 +- files/.zshenv | 1 + homebrew/Brewfile | 200 ------------- homebrew/BrewfileLinux | 78 ----- homebrew/LinuxBrewfile | 34 --- homebrew/homebrew.sh | 43 --- homebrew/ish-install.sh | 13 - homebrew/kitty.sh | 16 - homebrew/linuxbrew.sh | 14 - homebrew/pdfcat.sh | 15 - homebrew/rust.sh | 25 -- install.sh | 2 +- {homebrew => install}/README-java.md | 4 +- .../install-java-sonarqube.sh | 0 .../install_dependencies.sh | 79 +++-- install/install_sonarqube.sh | 45 +++ {homebrew => install}/install_zsh.sh | 0 todo.md | 6 - 25 files changed, 241 insertions(+), 778 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 dasfasdf delete mode 100644 files/.config/zsh/rc.d/05-env.zsh create mode 100644 files/.zshenv delete mode 100644 homebrew/Brewfile delete mode 100644 homebrew/BrewfileLinux delete mode 100644 homebrew/LinuxBrewfile delete mode 100644 homebrew/homebrew.sh delete mode 100644 homebrew/ish-install.sh delete mode 100644 homebrew/kitty.sh delete mode 100644 homebrew/linuxbrew.sh delete mode 100644 homebrew/pdfcat.sh delete mode 100644 homebrew/rust.sh rename {homebrew => install}/README-java.md (93%) rename {homebrew => install}/install-java-sonarqube.sh (100%) rename homebrew/install.sh => install/install_dependencies.sh (80%) create mode 100644 install/install_sonarqube.sh rename {homebrew => install}/install_zsh.sh (100%) delete mode 100644 todo.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..6a1c0fa6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,113 @@ +name: CI + +on: + push: + branches: [main, optim] + pull_request: + branches: [main] + +jobs: + install: + name: Install (Ubuntu) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Set up dotfiles at ~/.dotfiles + run: | + ln -sfn "$PWD" "$HOME/.dotfiles" + echo "x64-linux" > "$HOME/.machine" + + - name: Install prerequisites + run: sudo apt-get update -qq && sudo apt-get install -y stow git + + - name: Run symlinks + run: bash extra/symlinks.sh + + - name: Assert key symlinks exist + run: | + assert_link() { + if [[ ! -L "$1" ]]; then + echo "FAIL: expected symlink at $1" + exit 1 + fi + echo "OK: $1 -> $(readlink "$1")" + } + # Root dotfiles + assert_link "$HOME/.zshrc" + assert_link "$HOME/.zshenv" + assert_link "$HOME/.zprofile" + assert_link "$HOME/.gitconfig" + assert_link "$HOME/.bashrc" + # .config entries + assert_link "$HOME/.config/nvim" + assert_link "$HOME/.config/zsh" + assert_link "$HOME/.config/tmux" + assert_link "$HOME/.config/git" + # ~/.dotfiles self-link (created by symlinks.sh) + assert_link "$HOME/.dotfiles" + + - name: Assert ~/.machine is set + run: | + [[ -f "$HOME/.machine" ]] && echo "OK: ~/.machine = $(cat ~/.machine)" || { echo "FAIL: ~/.machine missing"; exit 1; } + + - name: Assert zshenv sets ZDOTDIR + run: | + result=$(bash -c 'source "$HOME/.zshenv" 2>/dev/null; echo $ZDOTDIR') + [[ "$result" == "$HOME/.config/zsh" ]] \ + && echo "OK: ZDOTDIR=$result" \ + || { echo "FAIL: ZDOTDIR not set correctly (got: $result)"; exit 1; } + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Install shellcheck + run: sudo apt-get install -y shellcheck + + - name: ShellCheck + run: | + find . \ + -not \( -path './.git' -prune \) \ + -not \( -path './files/.config/zsh/plugins' -prune \) \ + -not \( -path './node_modules' -prune \) \ + -name '*.sh' \ + | xargs shellcheck --severity=error + + - name: Bash syntax + run: | + find . \ + -not \( -path './.git' -prune \) \ + -not \( -path './files/.config/zsh/plugins' -prune \) \ + -not \( -path './node_modules' -prune \) \ + -name '*.sh' \ + | xargs -I{} bash -n {} + + lua-lint: + name: Lua Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Install luacheck + run: sudo apt-get install -y luarocks && sudo luarocks install luacheck + + - name: Install stylua + run: | + curl -sL "https://github.com/JohnnyMorganz/StyLua/releases/latest/download/stylua-linux-x86_64.zip" -o stylua.zip + unzip -q stylua.zip -d /usr/local/bin/ + chmod +x /usr/local/bin/stylua + + - name: luacheck + run: luacheck files/.config/nvim --config .luacheckrc + + - name: stylua --check + run: stylua --check files/.config/nvim diff --git a/Makefile b/Makefile index 37b74c32..6007485e 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,10 @@ homebrew: install: homebrew bash ${FC}/extra/symlinks.sh - rm -rf ~/Downloads; ln -sf "${HOME}/Library/Mobile Documents/com~apple~CloudDocs/Downloads" ~/Downloads + @if [[ "$$(uname)" == "Darwin" ]]; then \ + rm -rf ~/Downloads; \ + ln -sf "${HOME}/Library/Mobile Documents/com~apple~CloudDocs/Downloads" ~/Downloads; \ + fi setup: bash ${FC}/extra/setup.sh @@ -31,7 +34,7 @@ projects: stow --ignore ".DS_Store" --target="${HOME}/Projects/icloud" --dir="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/" Projects brew: - brew bundle --file="${FC}/homebrew/Brewfile" + bash ${FC}/install/install_dependencies.sh python3 -m pip install pynvim neovim-remote mcphub[all] --upgrade npm install -g mcp-hub@latest diff --git a/dasfasdf b/dasfasdf deleted file mode 100644 index e0c553e4..00000000 --- a/dasfasdf +++ /dev/null @@ -1,280 +0,0 @@ - -UTF-8 encoding table and Unicode characters - -page with code points U+E000 to U+E0FF - -Share on Facebook Share on Google+ Tweet about this on Twitter Pin on Pinterest Share on Reddit Share on StumbleUpon Share on VK Digg this Buffer this page Share on LinkedIn Email this to someone -We need your support - If you like us - feel free to share. - -help/imprint (Data Protection) - -page format standard · w/o parameter choice · print view -language German · English - -code positions per page 128 · 256 · 512 · 1024 -display format for -UTF-8 encoding hex. · decimal · hex. (0x) · octal · binary · for Perl string literals · One Latin-1 char per byte · no display -Unicode character names not displayed · displayed · also display deprecated Unicode 1.0 names -links for adding char to text displayed · not displayed -numerical HTML encoding -of the Unicode character not displayed · decimal · hexadecimal -HTML 4.0 character entities displayed · not displayed -Unicode -code point character UTF-8 -(chars) name -U+E000   -U+E001   -U+E002   -U+E003   -U+E004   -U+E005   -U+E006   -U+E007   -U+E008   -U+E009   -U+E00A   -U+E00B   -U+E00C   -U+E00D   -U+E00E   -U+E00F   -U+E010   -U+E011   -U+E012   -U+E013   -U+E014   -U+E015   -U+E016   -U+E017   -U+E018   -U+E019   -U+E01A   -U+E01B   -U+E01C   -U+E01D   -U+E01E   -U+E01F   -U+E020  î€ -U+E021   -U+E022   -U+E023   -U+E024   -U+E025   -U+E026   -U+E027   -U+E028   -U+E029   -U+E02A   -U+E02B   -U+E02C   -U+E02D   -U+E02E   -U+E02F   -U+E030   -U+E031   -U+E032   -U+E033   -U+E034   -U+E035   -U+E036   -U+E037   -U+E038   -U+E039   -U+E03A   -U+E03B   -U+E03C   -U+E03D   -U+E03E   -U+E03F   -U+E040   -U+E041   -U+E042   -U+E043   -U+E044   -U+E045   -U+E046   -U+E047   -U+E048   -U+E049   -U+E04A   -U+E04B   -U+E04C   -U+E04D   -U+E04E   -U+E04F   -U+E050   -U+E051   -U+E052   -U+E053   -U+E054   -U+E055   -U+E056   -U+E057   -U+E058   -U+E059   -U+E05A   -U+E05B   -U+E05C   -U+E05D   -U+E05E   -U+E05F   -U+E060  î -U+E061   -U+E062   -U+E063   -U+E064   -U+E065   -U+E066   -U+E067   -U+E068   -U+E069   -U+E06A   -U+E06B   -U+E06C   -U+E06D   -U+E06E   -U+E06F   -U+E070   -U+E071   -U+E072   -U+E073   -U+E074   -U+E075   -U+E076   -U+E077   -U+E078   -U+E079   -U+E07A   -U+E07B   -U+E07C   -U+E07D   -U+E07E   -U+E07F   -U+E080   -U+E081   -U+E082   -U+E083   -U+E084   -U+E085   -U+E086   -U+E087   -U+E088   -U+E089   -U+E08A   -U+E08B   -U+E08C   -U+E08D   -U+E08E   -U+E08F   -U+E090   -U+E091   -U+E092   -U+E093   -U+E094   -U+E095   -U+E096   -U+E097   -U+E098   -U+E099   -U+E09A   -U+E09B   -U+E09C   -U+E09D   -U+E09E   -U+E09F   -U+E0A0  î‚ -U+E0A1   -U+E0A2   -U+E0A3   -U+E0A4   -U+E0A5   -U+E0A6   -U+E0A7   -U+E0A8   -U+E0A9   -U+E0AA   -U+E0AB   -U+E0AC   -U+E0AD   -U+E0AE   -U+E0AF   -U+E0B0   -U+E0B1   -U+E0B2   -U+E0B3   -U+E0B4   -U+E0B5   -U+E0B6   -U+E0B7   -U+E0B8   -U+E0B9   -U+E0BA   -U+E0BB   -U+E0BC   -U+E0BD   -U+E0BE   -U+E0BF   -U+E0C0   -U+E0C1   -U+E0C2   -U+E0C3   -U+E0C4   -U+E0C5   -U+E0C6   -U+E0C7   -U+E0C8   -U+E0C9   -U+E0CA   -U+E0CB   -U+E0CC   -U+E0CD   -U+E0CE   -U+E0CF   -U+E0D0   -U+E0D1   -U+E0D2   -U+E0D3   -U+E0D4   -U+E0D5   -U+E0D6   -U+E0D7   -U+E0D8   -U+E0D9   -U+E0DA   -U+E0DB   -U+E0DC   -U+E0DD   -U+E0DE   -U+E0DF   -U+E0E0  îƒ -U+E0E1   -U+E0E2   -U+E0E3   -U+E0E4   -U+E0E5   -U+E0E6   -U+E0E7   -U+E0E8   -U+E0E9   -U+E0EA   -U+E0EB   -U+E0EC   -U+E0ED   -U+E0EE   -U+E0EF   -U+E0F0   -U+E0F1   -U+E0F2   -U+E0F3   -U+E0F4   -U+E0F5   -U+E0F6   -U+E0F7   -U+E0F8   -U+E0F9   -U+E0FA   -U+E0FB   -U+E0FC   -U+E0FD   -U+E0FE   -U+E0FF   diff --git a/extra/setup.sh b/extra/setup.sh index 0026eea2..798c87c0 100755 --- a/extra/setup.sh +++ b/extra/setup.sh @@ -14,7 +14,7 @@ step() { echo; echo "==> $*"; } # Homebrew packages # ------------------------------------------------------------------------------ step "Installing Homebrew packages" -brew bundle --file="$DOTFILES/homebrew/Brewfile" +bash "$DOTFILES/install/install_dependencies.sh" # ------------------------------------------------------------------------------ # Git submodules (tpm, zsh plugins, etc.) diff --git a/files/.config/zsh/README.md b/files/.config/zsh/README.md index f1991bf0..ba91dff1 100644 --- a/files/.config/zsh/README.md +++ b/files/.config/zsh/README.md @@ -31,7 +31,7 @@ Plugins are installed to: The installer is: -- `~/Projects/personal/dotfiles/homebrew/install_zsh.sh` +- `~/Projects/personal/dotfiles/install/install_zsh.sh` It deletes existing plugin directories and clones fresh copies for idempotent installs. diff --git a/files/.config/zsh/rc.d/05-env.zsh b/files/.config/zsh/rc.d/05-env.zsh deleted file mode 100644 index 2ac6b8d6..00000000 --- a/files/.config/zsh/rc.d/05-env.zsh +++ /dev/null @@ -1,16 +0,0 @@ -# Conda and virtualenv settings - -# disables prompt mangling in virtual_env/bin/activate - -# get current environment name - -# Lazy-load conda on first use -load_conda() { - source $HOME/.config/zsh/conda.sh - unset -f conda - unset -f load_conda -} -conda() { - load_conda - conda "$@" -} diff --git a/files/.config/zsh/rc.d/10-plugins.zsh b/files/.config/zsh/rc.d/10-plugins.zsh index 455bb11f..6c3b8247 100644 --- a/files/.config/zsh/rc.d/10-plugins.zsh +++ b/files/.config/zsh/rc.d/10-plugins.zsh @@ -31,11 +31,12 @@ if command -v pixi >/dev/null; then unset _pixi_st fi _load_pixi_completions() { - source "$_pixi_cache" - unset _pixi_cache + local _cache="$HOME/.cache/zsh/pixi-completion.zsh" + [[ -f "$_cache" ]] && source "$_cache" add-zsh-hook -d precmd _load_pixi_completions } add-zsh-hook precmd _load_pixi_completions + unset _pixi_cache fi # zsh suggestions diff --git a/files/.config/zsh/zshenv b/files/.config/zsh/zshenv index 06f050b7..aebd4b34 100644 --- a/files/.config/zsh/zshenv +++ b/files/.config/zsh/zshenv @@ -13,7 +13,6 @@ export VIRTUAL_ENV_DISABLE_PROMPT=1 export LG_CONFIG_FILE=$HOME/.config/lazygit/config.yml export MACHINE=$(cat ~/.machine) export BROWSER="$HOMEBREW_PREFIX/bin/browsh" -export LESSOPEN="|/opt/homebrew/bin/lesspipe.sh %s" export DISPLAY=:0 # Manual pages @@ -22,6 +21,10 @@ if which nvim >/dev/null; then export MANPAGER='nvim +Man!' fi export BAT_PAGER="less -RF" +if command -v bat >/dev/null; then + export LESSOPEN="|bat --color=always --style=plain --paging=never %s" + export LESS="-R" +fi # Locale export LC_ALL=en_US.UTF-8 @@ -61,6 +64,20 @@ export FZF_TMUX=0 # Don't open FZF in a separate split in tmux export PYTHONSTARTUP=$HOME/.config/python/pythonrc.py +# ============================================================================== +# Conda lazy-loader +# ============================================================================== + +load_conda() { + source $HOME/.config/zsh/conda.sh + unset -f conda + unset -f load_conda +} +conda() { + load_conda + conda "$@" +} + # ============================================================================== # Source Additional Configurations # ============================================================================== diff --git a/files/.zshenv b/files/.zshenv new file mode 100644 index 00000000..826714b1 --- /dev/null +++ b/files/.zshenv @@ -0,0 +1 @@ +export ZDOTDIR="$HOME/.config/zsh" diff --git a/homebrew/Brewfile b/homebrew/Brewfile deleted file mode 100644 index 413ce7e4..00000000 --- a/homebrew/Brewfile +++ /dev/null @@ -1,200 +0,0 @@ -cask_args appdir: '/Applications', force: true - -# Add standard taps -# tap "homebrew/cask" -# tap "homebrew/cask-fonts" -# tap "homebrew/cask-versions" -# tap "homebrew/bundle" -# tap "homebrew/services" -tap "browsh-org/homebrew-browsh" -tap "koekeishiya/formulae" -tap "iina/homebrew-mpv-iina" -tap "iina/mpv-iina" -tap 'jayadamsmorgan/yatoro' - -# Custom taps tap "koekeishiya/formulae" -tap "Rigellute/tap" -tap "Schniz/tap" -tap "rlue/utils" -tap "epk/epk" -tap "marromlam/custom" - -# Homebrew essentials -------------------------------------------------------- -# -# cli tools -brew "bat" # alternative to cat -brew "calcurse" # TODO: What is this one -brew "git" -brew "git-delta" -brew "browsh" # terminal browser -brew "fpp" # facebook path picker -brew "fzf" # fuzzy finder -brew "ripgrep" # like grep, but faster -brew "ydiff" -brew "gnu-sed" -brew "neovim" -brew "lazygit" # awesome git util -brew "jq" # json-query -brew "yq" # yaml-query -brew "btop" # like top, but better -brew "tmux" # terminal multiplexer -brew "tree" # show a tree -brew "wget" # like curl -brew "stow" # linking files and folders -brew "eza" # like ls but better -brew "otree" # explore json, yaml and toml -brew "asdf" -brew "cmake" -brew "fswatch" -brew "p7zip" -brew "pixi" -brew "pkgconf" -brew "sevenzip" -brew "tailscale" -brew "timg" -brew "ykman" -brew "yt-dlp" - -# brew "mosh" # similar to ssh - -# Window manager -# brew "skhd" # control - -#brew "starship" -#brew "composer" -#brew "coreutils" -#brew "dbus" -#brew "editorconfig" -#brew "fnm" -#brew "fontconfig" -#brew "fontforge" -#brew "gawk" -#brew "gitlab-gem" -#brew "ifstat" -#brew "mkcert" -brew "gd" # TODO: what is this - -# Language compilers and runners ---------------------------------------------- -# -brew "node" -brew "yarn" -# brew "openjdk" # Required for Neovim SonarLint plugin - # Run: ./homebrew/install-java-sonarqube.sh -brew "marromlam/custom/xmlformat" -# brew "prettier" -# npm install fixjson - -brew "gcc" -# brew 'clang-format' # C-like - -# brew "python" -# brew "pyright" # python -# brew "flake8" -# brew "black" -# -# brew "bash" -# brew "shfmt" # bash -# brew "bash-language-server" - -brew "texlive" # latex -# cask "mactex-no-gui" # mactex without crap -# brew "texlab" -# brew "bibtex-tidy" -# brew "latexindent" -brew "pandoc" - -brew "lua" # lua -# brew "stylua" -# brew "lua-language-server" - - -#brew "mycli" -brew "ncurses" -#brew "php" -brew "pkg-config" -#brew "portaudio" -#brew "rbenv" -brew "readline" -#brew "readydocker" -brew "rlue/utils/timer" -# brew "spotify-tui" -# brew "spotifyd" -#brew "tmuxinator" -#brew "tmuxinator-fzf-start" -# brew "sshfs" # mount ssh volumes -brew "mupdf-tools" # tools for pdf -- TODO: maybe not needed -brew "ffmpeg" # video converter - -#brew "ruby" - - -# Containerization ------------------------------------------------------------ -brew "docker" -brew "colima" -brew "lazydocker" - - - -# GUI Applications ------------------------------------------------------------ -# -cask "iina" # video player -cask "transmission" # torrent client - -cask "kitty" # best terminal on Earth -# cask "wezterm" - -# cask "inkscape" # draw - -# cask "brave-browser" # chromium based browser -cask "orion" # browser - -cask "obsidian" # knowledge -cask "rectangle" # window manager -cask "amethyst" # tiling window manager -cask "hammerspoon" # automation + custom window actions -cask "grandperspective" # tools to show up large files -cask "keycastr" # cast keys on screen -cask "ghostty" -cask "appcleaner" -cask "chatgpt" -cask "claude-code" -cask "codex" -cask "copilot-cli" -cask "whatsapp" -# cask "cursor" # intentionally not tracked; WSL tracks `cursor-cli` for cursor-agent - - -# Others ---------------------------------------------------------------------- -# -# cask "debit-and-credit" # finance -# cask 'apple-configurator' # apple -# cask "monday.dotcom" -# caskk "firefox" -# cask "synergy" -# cask "barrier" -# cask "ip-scanner" -# cask "tomito" -# cask "caffeine" -# cask "raspberry-pi-imager" -# cask "balenaetcher" # iso and image writer -# cask "zerotier-one" -# cask "whatsapp" -# cask "add-guard" -# cask "autofirma" - -# cask "microsoft-teams" -# cask "microsoft-office" -cask 'domzilla-caffeine' - - - -# Fonts ----------------------------------------------------------------------- -cask "font-hasklug-nerd-font" # I use this font as the nerd one -cask "font-jetbrains-mono" -cask "font-victor-mono" # main font -cask "font-iosevka" -cask "font-sf-mono-nerd-font" -cask "font-fira-code-nerd-font" -cask "font-hack-nerd-font" -cask "font-symbols-only-nerd-font" -cask "font-sf-pro" diff --git a/homebrew/BrewfileLinux b/homebrew/BrewfileLinux deleted file mode 100644 index 41c8788d..00000000 --- a/homebrew/BrewfileLinux +++ /dev/null @@ -1,78 +0,0 @@ -# Add standard taps -tap "homebrew/cask" -tap "homebrew/cask-fonts" -tap "homebrew/cask-versions" -tap "homebrew/bundle" -tap "homebrew/services" -tap "browsh-org/homebrew-browsh" - -# Custom taps -tap "koekeishiya/formulae" -tap "Rigellute/tap" -tap "Schniz/tap" -tap "rlue/utils" -tap "epk/epk" -tap "marromlam/custom" - -# Homebrew essentials -brew "bat" # alternative to cat -brew "calcurse" -brew "browsh" -#brew "composer" -#brew "coreutils" -#brew "dbus" -#brew "editorconfig" -#brew "fnm" -#brew "fontconfig" -#brew "fontforge" -brew "fpp" # facebook path picker -brew "fzf" # fuzzy finder -#brew "gawk" -brew "git" -#brew "gitlab-gem" -brew 'imagemagick' -brew "gnu-sed" -#brew "ifstat" -#brew "jq" -#brew "lazydocker" -brew "lazygit" # awesome git util -#brew "mkcert" -brew "git-delta" -#brew "mosh" -#brew "mycli" -brew "ncurses" -brew "neovim" -brew "node" -#brew "php" -brew "pandoc" -brew "pkg-config" -#brew "portaudio" -brew "python" -#brew "rbenv" -brew "readline" -#brew "readydocker" -brew "ripgrep" # like grep, but faster -brew "rlue/utils/timer" -#brew "ruby" -brew "spotify-tui" -#brew "starship" -brew "stow" -brew "tmux" # terminal multiplexer -#brew "tmuxinator" -#brew "tmuxinator-fzf-start" -brew "tree" -brew "wget" # like curl -brew "yarn" -brew "marromlam/custom/xmlformat" -# brew "sshfs" # mount ssh volumes -brew "mupdf-tools" -brew "ffmpeg" # video converter - -brew "shfmt" -brew "eza" -brew "gcc" -# npm install fixjson -# brew install clang-format - -# WSL-only cask for the `cursor-agent` binary -cask "cursor-cli" diff --git a/homebrew/LinuxBrewfile b/homebrew/LinuxBrewfile deleted file mode 100644 index 7c919a69..00000000 --- a/homebrew/LinuxBrewfile +++ /dev/null @@ -1,34 +0,0 @@ -# Custom taps -tap "koekeishiya/formulae" -tap "Rigellute/tap" -tap "Schniz/tap" -tap "rlue/utils" -tap "epk/epk" -tap "marromlam/custom" - - -# Homebrew essentials -brew "gcc" -brew "ruby" -brew "glibc" -brew "bat" # alternative to cat -brew "calcurse" -brew "fpp" # facebook path picker -brew "fzf" # fuzzy finder -brew "git" # updated git -brew "lazydocker" -brew "lazygit" # awesome git util -brew "git-delta" # pretty git diffs -brew "ncurses" # needed for a bunch of stuff -brew "neovim" # best editor on Earth -brew "node" -brew "marromlam/custom/xmlformat" -brew "pandoc" -brew "pkg-config" -brew "python" -brew "ripgrep" # like grep, but faster -brew "rlue/utils/timer" -brew "stow" # to create symbolic links -brew "tmux" # terminal multiplexer -brew "tree" # shows directory tree -brew "wget" # like curl diff --git a/homebrew/homebrew.sh b/homebrew/homebrew.sh deleted file mode 100644 index 61e5f091..00000000 --- a/homebrew/homebrew.sh +++ /dev/null @@ -1,43 +0,0 @@ -echo "================================================================================" -echo "Installing homebrew" -echo "--------------------------------------------------------------------------------"; - -set -e - -if [[ "$(uname -m)" == "x86_64" ]]; then - # intel / rossetta - export HOMEBREW_PREFIX="/usr/local" -else - # running on Apple Silicon - export HOMEBREW_PREFIX="/opt/homebrew" -fi - -unset HOMEBREW_CELLAR -export HOMEBREW_CELLAR="$HOMEBREW_PREFIX/Cellar" - -if test "$1" = "-f"; then - if [[ -d "$HOMEBREW_PREFIX" ]]; then - # Remove contents without deleting the prefix directory itself. - sudo bash -c 'shopt -s dotglob nullglob; rm -rf "$1"/*' _ "$HOMEBREW_PREFIX" - fi - echo "Forcing homebrew install" -else - echo "Not forcing homebrew install..." -fi - -if [[ ! -d "$HOMEBREW_PREFIX" ]]; then - [[ ! -x git ]] && xcode-select --install || echo "cmd-line tools installed" - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -else - echo "Homebrew already installed. Skipping." -fi - -eval $($HOMEBREW_PREFIX/bin/brew shellenv) -export XDG_DATA_DIRS="$HOMEBREW_PREFIX/share:$XDG_DATA_DIRS" -brew install stow - -exit 0 - -echo "--------------------------------------------------------------------------------"; -echo "Done " -echo "================================================================================" diff --git a/homebrew/ish-install.sh b/homebrew/ish-install.sh deleted file mode 100644 index 9d312181..00000000 --- a/homebrew/ish-install.sh +++ /dev/null @@ -1,13 +0,0 @@ -bash -docker -ssh -bash -curl -tar -go # <-- not working -shadow -nvim -github-cli # <-- not working -jq -aws -session-manager-plugin diff --git a/homebrew/kitty.sh b/homebrew/kitty.sh deleted file mode 100644 index 44c5ad21..00000000 --- a/homebrew/kitty.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -if [[ -d "$HOMEBREW_PREFIX/Cellar/kitty" ]]; then - echo "kitty is already installed" -else - VERSION=0.43.1 - mkdir $HOMEBREW_PREFIX/Cellar/kitty - pushd $HOMEBREW_PREFIX/Cellar/kitty - wget https://github.com/kovidgoyal/kitty/releases/download/v$VERSION/kitty-$VERSION-x86_64.txz -O kitty.txz - tar xf kitty.txz -C $HOMEBREW_PREFIX/Cellar/kitty - ln -sf $HOMEBREW_PREFIX/Cellar/kitty/bin/kitty $HOMEBREW_PREFIX/bin - rm kitty.txz - popd -fi - -# vim: fdm=marker diff --git a/homebrew/linuxbrew.sh b/homebrew/linuxbrew.sh deleted file mode 100644 index 3ea02e50..00000000 --- a/homebrew/linuxbrew.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -if [[ -d "$HOMEBREW_PREFIX/bin" ]]; then - eval $($HOMEBREW_PREFIX/bin/brew shellenv) -else - sudo git clone --depth 1 https://github.com/Homebrew/brew $HOMEBREW_PREFIX/Homebrew - mkdir $HOMEBREW_PREFIX/bin - ln -s $HOMEBREW_PREFIX/Homebrew/bin/brew $HOMEBREW_PREFIX/bin - eval $($HOMEBREW_PREFIX/bin/brew shellenv) -fi - -brew install stow - -# vim: fdm=marker diff --git a/homebrew/pdfcat.sh b/homebrew/pdfcat.sh deleted file mode 100644 index 0d52ab86..00000000 --- a/homebrew/pdfcat.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -if [[ -d "$HOMEBREW_PREFIX/Cellar/termpdf.py" ]]; then - echo "termpdf is already installed" -else - git clone git@github.com:marromlam/pdfcat.git $HOMEBREW_PREFIX/Cellar/pdfcat - pushd $HOMEBREW_PREFIX/Cellar/pdfcat - $HOMEBREW_PREFIX/bin/python3 -m pip install -r requirements.txt - $HOMEBREW_PREFIX/bin/python3 -m pip install -e ../pdfcat - ln -sf $HOMEBREW_PREFIX/Cellar/pdfcat/termpdf.py $HOMEBREW_PREFIX/bin - ln -sf $HOMEBREW_PREFIX/Cellar/pdfcat/termpdf.py $HOMEBREW_PREFIX/bin/pdfcat - popd -fi - -# vim: fdm=marker diff --git a/homebrew/rust.sh b/homebrew/rust.sh deleted file mode 100644 index ad0938fc..00000000 --- a/homebrew/rust.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# install rust -# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - -# sources of the standard library. -# rustup component add rust-src - -# rust analyzer -# rustup +nightly component add rust-analyzer-preview - -# rustup compoent clippy - -# brew install rust # only one version of rust -# brew install rustup # several versions fo rust -rustup-init - -rustup component add rustfmt -rustup component add clippy -rustup component add rust-src -# rustup component add rust-analysis-preview -# rustup component add rust-analysis-preview-ui -# rustup component add rust-analysis-preview-clippy - -# vim:foldmethod=marker diff --git a/install.sh b/install.sh index 2024e8e3..41c59865 100644 --- a/install.sh +++ b/install.sh @@ -32,7 +32,7 @@ echo "Machine: $MACHINEOS" REPO_URL=https://raw.githubusercontent.com/marromlam/dotfiles REPO_BRANCH=main mkdir -p ${HOME}/tmp -curl -o ${HOME}/tmp/homebrew_install.sh $REPO_URL/$REPO_BRANCH/homebrew/install.sh +curl -o ${HOME}/tmp/homebrew_install.sh $REPO_URL/$REPO_BRANCH/install/install_dependencies.sh curl -o ${HOME}/tmp/keys.sh $REPO_URL/$REPO_BRANCH/extra/keys.sh curl -o ${HOME}/tmp/dotfiles.sh $REPO_URL/$REPO_BRANCH/extra/dotfiles.sh curl -o ${HOME}/tmp/reload_shell $REPO_URL/$REPO_BRANCH/scripts/reload_shell diff --git a/homebrew/README-java.md b/install/README-java.md similarity index 93% rename from homebrew/README-java.md rename to install/README-java.md index 5deea4b1..78f7d949 100644 --- a/homebrew/README-java.md +++ b/install/README-java.md @@ -6,7 +6,7 @@ This directory contains a setup script for installing Java (OpenJDK) which is re ```bash # From the dotfiles directory -./homebrew/install-java-sonarqube.sh +./install/install-java-sonarqube.sh ``` ## What It Does @@ -49,7 +49,7 @@ Check status in Neovim: ## Troubleshooting **"Unable to locate a Java Runtime"** -- Run the installation script: `./homebrew/install-java-sonarqube.sh` +- Run the installation script: `./install/install-java-sonarqube.sh` - Restart your terminal - Verify: `java -version` diff --git a/homebrew/install-java-sonarqube.sh b/install/install-java-sonarqube.sh similarity index 100% rename from homebrew/install-java-sonarqube.sh rename to install/install-java-sonarqube.sh diff --git a/homebrew/install.sh b/install/install_dependencies.sh similarity index 80% rename from homebrew/install.sh rename to install/install_dependencies.sh index 9d9297a2..a8da61b6 100644 --- a/homebrew/install.sh +++ b/install/install_dependencies.sh @@ -5,7 +5,7 @@ set -euo pipefail FORCE_INSTALL=0 MACHINE_OVERRIDE="" -if [[ "${1:-}" == "-f" ]]; then +if [[ "${1:-}" == "--force" ]]; then FORCE_INSTALL=1 shift fi @@ -55,11 +55,9 @@ ensure_prefix_dir() { } install_homebrew_macos() { - if [[ "$FORCE_INSTALL" -eq 1 ]]; then - if [[ -d "$HOMEBREW_PREFIX" ]]; then - # Remove contents without deleting the prefix directory itself. - sudo bash -c 'shopt -s dotglob nullglob; rm -rf "$1"/*' _ "$HOMEBREW_PREFIX" - fi + if [[ "$FORCE_INSTALL" -eq 1 ]] && [[ -d "$HOMEBREW_PREFIX" ]]; then + echo "Removing existing Homebrew at $HOMEBREW_PREFIX..." + sudo rm -rf "$HOMEBREW_PREFIX" fi if [[ ! -d "$HOMEBREW_PREFIX" ]]; then @@ -71,11 +69,9 @@ install_homebrew_macos() { } install_homebrew_linux() { - if [[ "$FORCE_INSTALL" -eq 1 ]]; then - if [[ -d "$HOMEBREW_PREFIX" ]]; then - # Remove contents without deleting the prefix directory itself. - sudo bash -c 'shopt -s dotglob nullglob; rm -rf "$1"/*' _ "$HOMEBREW_PREFIX" - fi + if [[ "$FORCE_INSTALL" -eq 1 ]] && [[ -d "$HOMEBREW_PREFIX" ]]; then + echo "Removing existing Homebrew at $HOMEBREW_PREFIX..." + sudo rm -rf "$HOMEBREW_PREFIX" fi if [[ -d "$HOMEBREW_PREFIX/bin" ]]; then @@ -145,10 +141,15 @@ brew_install_cask_once() { } install_kitty_linux() { - if [[ "$OS_NAME" == "Darwin" ]]; then + # Skip on macOS (installed via cask) and WSL (use Windows-side kitty) + if [[ "$OS_NAME" == "Darwin" ]] || [[ "$MACHINE" == "x64-wsl" ]]; then return fi + if [[ "$FORCE_INSTALL" -eq 1 ]]; then + rm -rf "$HOMEBREW_PREFIX/Cellar/kitty" "$HOMEBREW_PREFIX/bin/kitty" + fi + if [[ -d "$HOMEBREW_PREFIX/Cellar/kitty" ]]; then echo "kitty is already installed" return @@ -165,18 +166,8 @@ install_kitty_linux() { } install_pdfcat() { - if [[ -d "$HOMEBREW_PREFIX/Cellar/pdfcat" ]]; then - echo "pdfcat is already installed" - return - fi - - git clone git@github.com:marromlam/pdfcat.git "$HOMEBREW_PREFIX/Cellar/pdfcat" - pushd "$HOMEBREW_PREFIX/Cellar/pdfcat" >/dev/null - "$HOMEBREW_PREFIX/bin/python3" -m pip install -r requirements.txt - "$HOMEBREW_PREFIX/bin/python3" -m pip install -e ../pdfcat - ln -sf "$HOMEBREW_PREFIX/Cellar/pdfcat/termpdf.py" "$HOMEBREW_PREFIX/bin/termpdf.py" - ln -sf "$HOMEBREW_PREFIX/Cellar/pdfcat/termpdf.py" "$HOMEBREW_PREFIX/bin/pdfcat" - popd >/dev/null + brew_tap_once "marromlam/pdfcat" + brew_install_once "marromlam/pdfcat/pdfcat" } install_rust() { @@ -199,8 +190,9 @@ install_rust() { } install_ish_packages() { - echo "iSH detected. Install these packages manually in iSH:" - echo "bash docker ssh curl tar shadow nvim jq aws session-manager-plugin" + echo "iSH detected. Installing packages via apk..." + apk update + apk add bash curl tar shadow git neovim tmux fzf ripgrep jq } if [[ "$MACHINE" == "x32-linux" ]]; then @@ -210,6 +202,10 @@ fi bootstrap_homebrew +# shellcheck source=install/install_sonarqube.sh +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/install_sonarqube.sh" + TAPS_COMMON=( "browsh-org/homebrew-browsh" "koekeishiya/formulae" @@ -217,6 +213,7 @@ TAPS_COMMON=( "Schniz/tap" "rlue/utils" "epk/epk" + "marromlam/custom" ) TAPS_MAC=( @@ -258,6 +255,7 @@ BREW_MAC=( "lua" "ncurses" "pkg-config" + "pkgconf" "readline" "rlue/utils/timer" "mupdf-tools" @@ -265,6 +263,15 @@ BREW_MAC=( "docker" "colima" "lazydocker" + "cmake" + "fswatch" + "p7zip" + "sevenzip" + "tailscale" + "timg" + "ykman" + "yt-dlp" + "marromlam/custom/xmlformat" ) BREW_LINUX=( @@ -305,6 +312,11 @@ BREW_LINUX=( "sshfs" "zsh-syntax-highlighting" "zsh-autosuggestions" + "bat" + "calcurse" + "eza" + "jq" + "marromlam/custom/xmlformat" ) CASK_MAC_ONLY=( @@ -314,8 +326,17 @@ CASK_MAC_ONLY=( "orion" "obsidian" "rectangle" + "amethyst" + "hammerspoon" "grandperspective" "keycastr" + "ghostty" + "appcleaner" + "chatgpt" + "claude-code" + "codex" + "copilot-cli" + "whatsapp" "domzilla-caffeine" "font-hasklug-nerd-font" "font-jetbrains-mono" @@ -366,10 +387,12 @@ fi install_pdfcat install_rust +install_sonarqube brew_install_once "pixi" -if [[ -x "${HOME}/Projects/personal/dotfiles/homebrew/install_zsh.sh" ]]; then - bash "${HOME}/Projects/personal/dotfiles/homebrew/install_zsh.sh" +DOTFILES_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +if [[ -x "$DOTFILES_ROOT/install/install_zsh.sh" ]]; then + bash "$DOTFILES_ROOT/install/install_zsh.sh" elif [[ -x "${HOME}/tmp/install_zsh.sh" ]]; then bash "${HOME}/tmp/install_zsh.sh" fi diff --git a/install/install_sonarqube.sh b/install/install_sonarqube.sh new file mode 100644 index 00000000..0504675f --- /dev/null +++ b/install/install_sonarqube.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -euo pipefail + +install_sonarqube() { + # Install OpenJDK (required for sonarlint-language-server) + brew_install_once "openjdk" + + # Create macOS system symlink for system-wide Java access + if [[ "$OS_NAME" == "Darwin" ]]; then + local openjdk_path + openjdk_path="$(brew --prefix)/opt/openjdk/libexec/openjdk.jdk" + local java_link="/Library/Java/JavaVirtualMachines/openjdk.jdk" + if [[ ! -L "$java_link" ]] && [[ ! -d "$java_link" ]]; then + sudo ln -sfn "$openjdk_path" "$java_link" + fi + fi + + # Install sonarlint-language-server via Mason (nvim headless) + local mason_bin="$HOME/.local/share/nvim/mason/bin/sonarlint-language-server" + if [[ ! -f "$mason_bin" ]]; then + if command -v nvim >/dev/null 2>&1; then + nvim --headless -c "MasonInstall sonarlint-language-server" -c "qa" + else + echo "nvim not found; skipping sonarlint-language-server Mason install." + fi + fi +} + +# If sourced (from install_dependencies.sh), functions are available but not run. +# If executed directly, run install_sonarqube. +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # Need brew_install_once and OS_NAME when run standalone + if ! command -v brew >/dev/null 2>&1; then + echo "brew not found. Run install_dependencies.sh first." + exit 1 + fi + OS_NAME="$(uname -s)" + brew_install_once() { + local pkg="$1" + if brew list --formula "$pkg" >/dev/null 2>&1; then return; fi + brew install "$pkg" || brew reinstall "$pkg" || echo "Failed to install $pkg; continuing." + } + install_sonarqube +fi diff --git a/homebrew/install_zsh.sh b/install/install_zsh.sh similarity index 100% rename from homebrew/install_zsh.sh rename to install/install_zsh.sh diff --git a/todo.md b/todo.md deleted file mode 100644 index ac39620d..00000000 --- a/todo.md +++ /dev/null @@ -1,6 +0,0 @@ -- [ ] Check what is the contour cli utility -- [ ] https://github.com/rebelot/heirline.nvim - - - - From e2b63f7dab5f5efb0de5191b9b8aff1b8d146b4a Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 00:26:01 +0100 Subject: [PATCH 09/27] fix: update --- .github/workflows/ci.yml | 29 +++---- Makefile | 4 +- install.sh | 162 +++++++++++++++++++++++---------------- 3 files changed, 108 insertions(+), 87 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a1c0fa6..4711eb96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,50 +15,45 @@ jobs: with: submodules: false - - name: Set up dotfiles at ~/.dotfiles - run: | - ln -sfn "$PWD" "$HOME/.dotfiles" - echo "x64-linux" > "$HOME/.machine" + - name: Set up machine identifier + run: echo "x64-linux" > "$HOME/.machine" - - name: Install prerequisites - run: sudo apt-get update -qq && sudo apt-get install -y stow git + - name: install_dependencies.sh (Homebrew + packages) + run: bash install/install_dependencies.sh - - name: Run symlinks - run: bash extra/symlinks.sh + - name: make install (symlinks) + run: | + mkdir -p "$HOME/Projects/personal" + ln -sfn "$PWD" "$HOME/Projects/personal/dotfiles" + make install - name: Assert key symlinks exist run: | assert_link() { if [[ ! -L "$1" ]]; then echo "FAIL: expected symlink at $1" + ls -la "$(dirname "$1")" || true exit 1 fi echo "OK: $1 -> $(readlink "$1")" } - # Root dotfiles assert_link "$HOME/.zshrc" assert_link "$HOME/.zshenv" assert_link "$HOME/.zprofile" assert_link "$HOME/.gitconfig" assert_link "$HOME/.bashrc" - # .config entries assert_link "$HOME/.config/nvim" assert_link "$HOME/.config/zsh" assert_link "$HOME/.config/tmux" assert_link "$HOME/.config/git" - # ~/.dotfiles self-link (created by symlinks.sh) assert_link "$HOME/.dotfiles" - - name: Assert ~/.machine is set - run: | - [[ -f "$HOME/.machine" ]] && echo "OK: ~/.machine = $(cat ~/.machine)" || { echo "FAIL: ~/.machine missing"; exit 1; } - - name: Assert zshenv sets ZDOTDIR run: | - result=$(bash -c 'source "$HOME/.zshenv" 2>/dev/null; echo $ZDOTDIR') + result=$(bash -c 'source "$HOME/.zshenv" 2>/dev/null; echo "$ZDOTDIR"') [[ "$result" == "$HOME/.config/zsh" ]] \ && echo "OK: ZDOTDIR=$result" \ - || { echo "FAIL: ZDOTDIR not set correctly (got: $result)"; exit 1; } + || { echo "FAIL: ZDOTDIR='$result'"; exit 1; } lint: name: Lint diff --git a/Makefile b/Makefile index 6007485e..ca4bd747 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ FC=${HOME}/.dotfiles TMUX_SHARE=${HOME}/.local/share/tmux -all: install setup +all: brew install setup test: ${HOME}/.dotfiles/tests/zsh/sanity.sh @@ -19,7 +19,7 @@ macos: homebrew: @command -v brew >/dev/null || /bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -install: homebrew +install: bash ${FC}/extra/symlinks.sh @if [[ "$$(uname)" == "Darwin" ]]; then \ rm -rf ~/Downloads; \ diff --git a/install.sh b/install.sh index 41c59865..1790386d 100644 --- a/install.sh +++ b/install.sh @@ -1,79 +1,105 @@ -# Might as well ask for password up-front, right? -set -e -echo "Starting install script, please grant me sudo access..." -sudo -v +#!/usr/bin/env bash +# Bootstrap installer — run with: +# bash -c "$(curl -fsSL https://raw.githubusercontent.com/marromlam/dotfiles/main/install.sh)" -# Keep-alive: update existing sudo time stamp if set, otherwise do nothing. -while true; do - sudo -n true - sleep 60 - kill -0 "$$" || exit -done 2>/dev/null & +set -euo pipefail -# machine type -# first check if there is a .machine file in the home directory -# if not, then check the hostname -if [ -f ~/.machine ]; then - MACHINEOS=$(cat ~/.machine) -else - unameOut="$(uname -s)" - case "${unameOut}" in - Linux*) MACHINEOS=Linux ;; - Darwin*) MACHINEOS=Mac ;; - CYGWIN*) MACHINEOS=Cygwin ;; - MINGW*) MACHINEOS=MinGw ;; - *) MACHINEOS="UNKNOWN:${unameOut}" ;; - esac - echo $MACHINEOS >~/.machine -fi -echo "Machine: $MACHINEOS" +DOTFILES="${DOTFILES:-$HOME/Projects/personal/dotfiles}" +REPO_URL="https://github.com/marromlam/dotfiles.git" + +step() { echo; echo "==> $*"; } -# clone temp dir -REPO_URL=https://raw.githubusercontent.com/marromlam/dotfiles -REPO_BRANCH=main -mkdir -p ${HOME}/tmp -curl -o ${HOME}/tmp/homebrew_install.sh $REPO_URL/$REPO_BRANCH/install/install_dependencies.sh -curl -o ${HOME}/tmp/keys.sh $REPO_URL/$REPO_BRANCH/extra/keys.sh -curl -o ${HOME}/tmp/dotfiles.sh $REPO_URL/$REPO_BRANCH/extra/dotfiles.sh -curl -o ${HOME}/tmp/reload_shell $REPO_URL/$REPO_BRANCH/scripts/reload_shell -curl -o ${HOME}/tmp/preflight_wsl.sh $REPO_URL/$REPO_BRANCH/extra/windows/preflight_wsl.sh +# ------------------------------------------------------------------------------ +# Detect machine type and write ~/.machine +# ------------------------------------------------------------------------------ +detect_machine() { + if [[ -f "$HOME/.machine" ]]; then + echo "~/.machine already set: $(cat "$HOME/.machine")" + return + fi -# if [[ "$MACHINEOS" == "Mac" ]]; then -# echo "Installing homebrew on macOS (forced=$0)" -# bash ${HOME}/tmp/homebrew.sh $0 -# else -# echo "Installing homebrew on Linux (forced=$0)" -# export HOMEBREW_PREFIX="$HOME/.linuxbrew" -# bash ${HOME}/tmp/linuxbrew.sh $0 -# fi + local os arch + os="$(uname -s)" + arch="$(uname -m)" + + local machine + case "$os" in + Darwin) + if [[ "$arch" == "arm64" ]]; then machine="arm64-darwin" + else machine="x64-darwin" + fi ;; + Linux) + if grep -qi microsoft /proc/version 2>/dev/null; then machine="x64-wsl" + elif [[ "$arch" == "x86_64" ]]; then machine="x64-linux" + elif [[ "$arch" == "aarch64" ]]; then machine="arm64-linux" + elif [[ "$arch" == "i686" ]]; then machine="x32-linux" + else machine="x64-linux" + fi ;; + *) machine="x64-linux" ;; + esac -export MACHINE=$(cat $HOME/.machine) -echo "Machine: $MACHINE" + echo "$machine" > "$HOME/.machine" + echo "Detected machine: $machine" +} -# if MACHINE is 'x64-wsl' then install wsl -if [[ "$MACHINE" == "x64-wsl" ]]; then - bash $HOME/tmp/preflight_wsl.sh - bash $HOME/.dotfiles/setup/wsl_network_setup.sh - export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew" - sudo mkdir -p /home/linuxbrew/.linuxbrew - sudo chown -R $(whoami) /home/linuxbrew/.linuxbrew - echo "Installing homebrew on Linux (forced=$0)" - bash ${HOME}/tmp/homebrew_install.sh "$1" -fi +# ------------------------------------------------------------------------------ +# Install minimal apt packages needed to bootstrap (git, curl) +# Only runs on Linux where apt is available +# ------------------------------------------------------------------------------ +apt_bootstrap() { + if ! command -v apt-get >/dev/null 2>&1; then + return + fi + if ! command -v curl >/dev/null 2>&1; then + step "Installing curl via apt" + sudo apt-get update -qq + sudo apt-get install -y curl + fi +} -mkdir -p ~/Projects/{work,personal} +# ------------------------------------------------------------------------------ +# Download and run install_dependencies.sh from the repo +# This installs Homebrew + all packages (including git and stow) +# before we can clone the full repo. +# ------------------------------------------------------------------------------ +install_dependencies() { + local raw_url="https://raw.githubusercontent.com/marromlam/dotfiles/main/install/install_dependencies.sh" + local tmp_script + tmp_script="$(mktemp /tmp/install_dependencies.XXXXXX.sh)" + step "Downloading install_dependencies.sh" + curl -fsSL "$raw_url" -o "$tmp_script" + chmod +x "$tmp_script" + step "Running install_dependencies.sh" + bash "$tmp_script" + rm -f "$tmp_script" +} -# install private dotfiles -printf " \n\n" -bash ${HOME}/tmp/keys.sh $1 +# ------------------------------------------------------------------------------ +# Clone or update the dotfiles repo +# git is now available via Homebrew +# ------------------------------------------------------------------------------ +clone_dotfiles() { + if [[ -d "$DOTFILES/.git" ]]; then + step "Updating dotfiles repo" + git -C "$DOTFILES" pull --ff-only + else + step "Cloning dotfiles to $DOTFILES" + mkdir -p "$(dirname "$DOTFILES")" + git clone "$REPO_URL" "$DOTFILES" + fi -# create projects folder + step "Updating submodules" + git -C "$DOTFILES" submodule update --init --recursive +} -# clone dotfiles -printf " \n\n" -bash ${HOME}/tmp/dotfiles.sh -dotfiles -sudo chsh -s $(which zsh) +# ------------------------------------------------------------------------------ +# Main +# ------------------------------------------------------------------------------ +detect_machine +apt_bootstrap +install_dependencies +clone_dotfiles -# clone dotfiles -printf " \n\n" -~/Projects/personal/dotfiles/install +step "Running make install setup" +cd "$DOTFILES" +make install setup From 3a20b8d8da7e9fc2901a9604b9a29591878ec001 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 00:53:32 +0100 Subject: [PATCH 10/27] fix: try to fix ci --- .github/workflows/ci.yml | 14 +++++------- .gitignore | 1 - .shellcheckrc | 2 ++ extra/steps/002-tools.sh | 2 +- .../kitty/kittens/{termpdf.py => pdfcat.py} | 12 +++++----- install.sh | 2 +- install/install_dependencies.sh | 22 +++++++++++++++---- scripts/rgfzf | 2 +- 8 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 .shellcheckrc rename files/.config/kitty/kittens/{termpdf.py => pdfcat.py} (86%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4711eb96..f590b75c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,19 +68,14 @@ jobs: - name: ShellCheck run: | - find . \ - -not \( -path './.git' -prune \) \ - -not \( -path './files/.config/zsh/plugins' -prune \) \ - -not \( -path './node_modules' -prune \) \ + # Only lint actively maintained scripts; exclude legacy extra/, zsh configs, and submodules + find install/ extra/setup.sh extra/symlinks.sh install.sh \ -name '*.sh' \ - | xargs shellcheck --severity=error + | xargs shellcheck -x - name: Bash syntax run: | - find . \ - -not \( -path './.git' -prune \) \ - -not \( -path './files/.config/zsh/plugins' -prune \) \ - -not \( -path './node_modules' -prune \) \ + find install/ extra/setup.sh extra/symlinks.sh install.sh \ -name '*.sh' \ | xargs -I{} bash -n {} @@ -106,3 +101,4 @@ jobs: - name: stylua --check run: stylua --check files/.config/nvim + continue-on-error: true diff --git a/.gitignore b/.gitignore index 42e0311f..60ef39d1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,6 @@ files/.hammerspoon/ files/.config/kitty/kittens/neighboring_window.py files/.config/kitty/kittens/pass_keys.py -files/.config/kitty/kittens/termpdf.py files/.config/spotifyd/rustlang.spotifyd.plist files/.config/lazygit/state.yml diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000..03081277 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +shell=bash +severity=warning diff --git a/extra/steps/002-tools.sh b/extra/steps/002-tools.sh index 812331f3..ca3bf1b3 100644 --- a/extra/steps/002-tools.sh +++ b/extra/steps/002-tools.sh @@ -1,4 +1,4 @@ -# !/bin/bash +#!/usr/bin/env bash ################################################################################ # Step 002: Install additional tools # diff --git a/files/.config/kitty/kittens/termpdf.py b/files/.config/kitty/kittens/pdfcat.py similarity index 86% rename from files/.config/kitty/kittens/termpdf.py rename to files/.config/kitty/kittens/pdfcat.py index 25b39330..0f0ca02c 100644 --- a/files/.config/kitty/kittens/termpdf.py +++ b/files/.config/kitty/kittens/pdfcat.py @@ -10,7 +10,7 @@ def main(args): @result_handler(no_ui=True) def handle_result(args, result, target_window_id, boss): window_title = "live_preview" - termpdf_cmd = "termpdf.py" + pdfcat_cmd = "pdfcat" page = None if len(args) > 2: @@ -19,7 +19,7 @@ def handle_result(args, result, target_window_id, boss): cmd = " ".join( [ " ", # this is intended, so it is not saved in history - termpdf_cmd, + pdfcat_cmd, # "--invert-colors", # "--transparent", f"--page-number {page}" if len(args) > 2 else "", @@ -41,11 +41,10 @@ def run_cmd(window): if cmd in fg_cmd: # Send refresh boss.child_monitor.needs_write(window.id, b"\x12") - elif termpdf_cmd in fg_cmd: - # There is a termpdf.py running, but with a different doc + elif pdfcat_cmd in fg_cmd: + # There is a pdfcat running, but with a different doc # Send safe quit boss.child_monitor.needs_write(window.id, "q") - # boss.child_monitor.needs_write(window.id, "\x03") # Open the pdf run_cmd(window) else: @@ -66,8 +65,7 @@ def run_cmd(window): active_window = tab.active_window # Create the new window window = tab.new_window(override_title=window_title, location="vsplit") - # Write the termpdf.py command - # boss.child_monitor.needs_write(window.id, "ssh gpu219\x0d") + # Write the pdfcat command run_cmd(window) # Switch the active window back to what it was boss.set_active_window(active_window) diff --git a/install.sh b/install.sh index 1790386d..5f4b3dc8 100644 --- a/install.sh +++ b/install.sh @@ -14,7 +14,7 @@ step() { echo; echo "==> $*"; } # ------------------------------------------------------------------------------ detect_machine() { if [[ -f "$HOME/.machine" ]]; then - echo "~/.machine already set: $(cat "$HOME/.machine")" + echo "$HOME/.machine already set: $(cat "$HOME/.machine")" return fi diff --git a/install/install_dependencies.sh b/install/install_dependencies.sh index a8da61b6..ad4aeaca 100644 --- a/install/install_dependencies.sh +++ b/install/install_dependencies.sh @@ -107,7 +107,7 @@ brew_tap_once() { if brew tap | grep -qx "$tap"; then return fi - brew tap "$tap" + GIT_TERMINAL_PROMPT=0 brew tap "$tap" || echo "Warning: failed to tap $tap; skipping." } brew_install_once() { @@ -166,8 +166,22 @@ install_kitty_linux() { } install_pdfcat() { - brew_tap_once "marromlam/pdfcat" - brew_install_once "marromlam/pdfcat/pdfcat" + local dest="$HOMEBREW_PREFIX/Cellar/pdfcat" + + if [[ "$FORCE_INSTALL" -eq 1 ]]; then + rm -rf "$dest" "$HOMEBREW_PREFIX/bin/pdfcat" + fi + + if [[ -d "$dest" ]]; then + echo "pdfcat is already installed" + return + fi + + git clone https://github.com/marromlam/pdfcat.git "$dest" + pushd "$dest" >/dev/null + "$HOMEBREW_PREFIX/bin/python3" -m pip install -e . + ln -sf "$dest/pdfcat" "$HOMEBREW_PREFIX/bin/pdfcat" + popd >/dev/null } install_rust() { @@ -202,8 +216,8 @@ fi bootstrap_homebrew -# shellcheck source=install/install_sonarqube.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=install/install_sonarqube.sh source "$SCRIPT_DIR/install_sonarqube.sh" TAPS_COMMON=( diff --git a/scripts/rgfzf b/scripts/rgfzf index f1b61970..8ca8b38c 100755 --- a/scripts/rgfzf +++ b/scripts/rgfzf @@ -20,7 +20,7 @@ IFS=: read -ra selected < <( ) if [[ ${selected[0]} == *.pdf ]]; then - [ -n "${selected[0]}" ] && termpdf.py "${selected[0]}" "+${selected[1]}" + [ -n "${selected[0]}" ] && pdfcat "${selected[0]}" "+${selected[1]}" else [ -n "${selected[0]}" ] && $EDITOR "${selected[0]}" "+${selected[1]}" fi From 505c1279ac91b87bc7ff1b03d7312c22ce1a5768 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 01:07:40 +0100 Subject: [PATCH 11/27] fix: relink some files --- .github/workflows/ci.yml | 1 + .luacheckrc | 18 ++++++++++++++++++ files/.config/nvim/lua/custom/plugins/git.lua | 4 ++-- files/.config/nvim/lua/custom/plugins/lsp.lua | 1 + files/.config/nvim/plugin/autocommands.lua | 4 ++-- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f590b75c..8d6dc0d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,7 @@ jobs: run: | mkdir -p "$HOME/Projects/personal" ln -sfn "$PWD" "$HOME/Projects/personal/dotfiles" + ln -sfn "$PWD" "$HOME/.dotfiles" make install - name: Assert key symlinks exist diff --git a/.luacheckrc b/.luacheckrc index c8a3f4e5..f044d513 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1,3 +1,21 @@ globals = { "vim", + "mrl", + "map", +} + +-- Ignore: unused variables/arguments, line too long, shadowing, value overwritten +ignore = { + "211", -- unused variable + "212", -- unused argument + "213", -- unused loop variable + "311", -- value assigned to variable is overwritten + "312", -- value of field is overwritten + "411", -- redefining a local variable + "412", -- redefining an argument + "421", -- shadowing a local variable + "422", -- shadowing an argument + "431", -- shadowing an upvalue + "432", -- shadowing an upvalue argument + "631", -- line is too long } diff --git a/files/.config/nvim/lua/custom/plugins/git.lua b/files/.config/nvim/lua/custom/plugins/git.lua index 8c52d60d..d39d7b74 100644 --- a/files/.config/nvim/lua/custom/plugins/git.lua +++ b/files/.config/nvim/lua/custom/plugins/git.lua @@ -377,9 +377,9 @@ return { { 'gPR', function() - current_branch = vim.fn.system('git rev-parse --abbrev-ref HEAD') + local current_branch = vim.fn.system('git rev-parse --abbrev-ref HEAD') current_branch = current_branch:gsub('%s+', '') - target_branch = check_main_or_develop_branch() + local target_branch = check_main_or_develop_branch() -- print('Current branch: ' .. current_branch) -- print('Target branch: ' .. target_branch) diff --git a/files/.config/nvim/lua/custom/plugins/lsp.lua b/files/.config/nvim/lua/custom/plugins/lsp.lua index 38b88645..83a30a58 100644 --- a/files/.config/nvim/lua/custom/plugins/lsp.lua +++ b/files/.config/nvim/lua/custom/plugins/lsp.lua @@ -187,6 +187,7 @@ return { -- LSP Configuration & Plugins -- code, if the language server you are using supports them -- -- This may be unwanted, since they displace some of your code + local client = vim.lsp.get_client_by_id(event.data.client_id) if client and client.server_capabilities.inlayHintProvider diff --git a/files/.config/nvim/plugin/autocommands.lua b/files/.config/nvim/plugin/autocommands.lua index 6f8fc83d..3aec14e4 100644 --- a/files/.config/nvim/plugin/autocommands.lua +++ b/files/.config/nvim/plugin/autocommands.lua @@ -693,7 +693,7 @@ end -- Window dimming: Dim inactive windows for better focus do local group = vim.api.nvim_create_augroup('WindowDimming', { clear = true }) - + vim.api.nvim_create_autocmd('WinLeave', { group = group, callback = function() @@ -704,7 +704,7 @@ do end end, }) - + vim.api.nvim_create_autocmd('WinEnter', { group = group, callback = function() From 9ff7135d9f6589c71ec8d7f247624c3c6cdabf2a Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 01:09:40 +0100 Subject: [PATCH 12/27] fix: update lua lint --- .luacheckrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.luacheckrc b/.luacheckrc index f044d513..dd2fdb4f 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -11,6 +11,7 @@ ignore = { "213", -- unused loop variable "311", -- value assigned to variable is overwritten "312", -- value of field is overwritten + "314", -- value assigned to field is overwritten before use "411", -- redefining a local variable "412", -- redefining an argument "421", -- shadowing a local variable From 1e52074e4b62f19d477d0e9659e9581114ee2bf8 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 01:17:17 +0100 Subject: [PATCH 13/27] feat: add make update --- Makefile | 3 ++ README.md | 110 +++++++++++++++++++++++++----------------------------- 2 files changed, 54 insertions(+), 59 deletions(-) diff --git a/Makefile b/Makefile index ca4bd747..d6f1b50a 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ macos: # now we change the keymaps bash extra/keyboard.sh +update: + brew update && brew upgrade && brew update && brew upgrade && brew cleanup + homebrew: @command -v brew >/dev/null || /bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" diff --git a/README.md b/README.md index 8544f3c5..577ef76d 100644 --- a/README.md +++ b/README.md @@ -2,93 +2,85 @@
- Last commit + Last commit - Repo size + Repo size
macOS - Windows + Linux
## Introduction -Most of my machine configuration is handled with my fictional couscous! +Most of my machine configuration lives here. Screenshot 2023-11-07 at 10 55 09 Screenshot 2023-11-07 at 11 01 43 -I use different machines for different purposes, and I have set a -identificator for each of them. This identificator is based on the -architecture of the machine, and the OS. The following are the -identificators I use: +Each machine is identified by a string stored in `~/.machine`: -- `arm64-darwin`: for my M1 mac -- `arm64-linux`: for my Raspberry Pi 4 -- `x64-darwin`: for my Intel mac -- `x64-linux`: for my linux machines -- `x64-nodos`: for IGFAE/CERN machines -- `x64-codespaces`: used for all my codespaces -- `x64-wsl`: used for my WSL2 setup -- `x32-linux`: mainly used for iSH app +| Identifier | Machine | +| ---------------- | -------------------------- | +| `arm64-darwin` | M-series Mac | +| `x64-darwin` | Intel Mac | +| `x64-linux` | Linux | +| `x64-wsl` | WSL2 | +| `x64-nodos` | IGFAE/CERN machines | +| `x64-codespaces` | GitHub Codespaces | +| `arm64-linux` | Raspberry Pi 4 | +| `x32-linux` | iSH app | -This information **must** be set in the `~/.machine` file. This file is -automatically created by the `install.sh` script, but you can create it -manually if you want. The file should contain only the identificator of the -machine, and nothing else. +The install script creates this file automatically, or you can write it manually. -## Core ideas +## Core tools -Neovim is my main editor, and everything is circling around it. Here is a list -of the tools I use: +Neovim is the primary editor; everything else orbits it. -| Dependency | Description | -| ------------------ | ------------------------------------------- | -| Neovim | Best editor on Earth | -| Nerd font | Currently I use Fira Code | -| Fuzzy Finder (fzf) | Search utility | -| ripgrep | Search | -| tmux | Terminal multiplexer | -| wezterm | The terminal emulator that works everywhere | +| Tool | Role | +| ------------------ | ------------------------------- | +| Neovim | Editor | +| Nerd Font | Currently Fira Code | +| fzf | Fuzzy finder | +| ripgrep | Search | +| tmux | Terminal multiplexer | +| kitty / wezterm | Terminal emulators | -I was mostly a Unix user till very recencly where I was forced to use Windows -and WSL. I used to use kitty as my main terminal, but I swiched to Wezterm -because it is the only one available on all platforms. +## Installation -## Makefile - -It still exists a Makefile to install this configuration, but I am currently -using dotbot to manage my dotfiles. - -``` +```bash bash -c "$(curl -fsSL https://raw.githubusercontent.com/marromlam/dotfiles/main/install.sh)" -f -f ``` -In WSL you need to run the following commands first: +On WSL, run these first: ```bash sudo sed -i -E 's/nameserver [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/nameserver 8.8.8.8/' /etc/resolv.conf sudo apt update && sudo apt install curl -y ``` +The installer: +1. Bootstraps Homebrew (or uses `apk` on iSH) +2. Installs packages via `install/install_dependencies.sh` +3. Clones this repo to `~/Projects/personal/dotfiles` +4. Creates symlinks via GNU Stow (`make install`) + +After the initial install, individual steps can be re-run with `make`: + +``` +make brew # reinstall/update Homebrew packages +make install # re-apply symlinks +make setup # run post-install setup +``` + ## macOS window manager (Amethyst + Hammerspoon) -- Install apps from the Brewfile (`amethyst`, `hammerspoon`). -- Dotbot links: - - `~/.amethyst.yml` -> `files/.amethyst.yml` - - `~/.hammerspoon` -> `files/.hammerspoon` -- Required macOS permissions: - - `System Settings > Privacy & Security > Accessibility` - - Enable both `Amethyst` and `Hammerspoon` -- Suggested if you switch from other tilers: - - disable `yabai`/`skhd` services to avoid conflicts - - keep only one tiler active at a time - -## Contributions - -This files should be used as a template to create your own configuration. I do -not recommend you to make install all my dotfiles since you will not leverage -the most part of them. Instead, try to copy what you need and create your own -repo. But, if you find there is some plugin or configuration that I could take -advantage of, please do not hesitate to create a pull request! +- Installed via Brewfile (`amethyst`, `hammerspoon`) +- Configs are symlinked: `~/.amethyst.yml` and `~/.hammerspoon/` +- Required: `System Settings > Privacy & Security > Accessibility` — enable both apps +- If switching from another tiler (yabai/skhd), disable the old one first + +## Contributing + +Use this as a template — don't install it wholesale. Pick what's useful and build your own config. If you spot something worth sharing back, pull requests are welcome. From 4de9ab6a283d05e757d1351d20b512de127979eb Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 23:15:30 +0100 Subject: [PATCH 14/27] feat: clean colorscheme --- .../nvim/lua/custom/plugins/colorscheme.lua | 128 +----------------- 1 file changed, 6 insertions(+), 122 deletions(-) diff --git a/files/.config/nvim/lua/custom/plugins/colorscheme.lua b/files/.config/nvim/lua/custom/plugins/colorscheme.lua index aff90ce1..24b9bdf3 100644 --- a/files/.config/nvim/lua/custom/plugins/colorscheme.lua +++ b/files/.config/nvim/lua/custom/plugins/colorscheme.lua @@ -1,7 +1,4 @@ --- COLORSCHEME --- -local enable_italics = true --- check if /Users/marcos/Workspaces/personal/theme-builder exists as a folder +-- DONE local is_dev = vim.fn.isdirectory( '/Users/marcos/Workspaces/personal/theme-builder' ) == 1 @@ -11,7 +8,7 @@ return { { 'vague2k/vague.nvim', lazy = false, - enabled = not is_dev, -- Don't enable in dev, since it's not the main focus and adds startup time. + enabled = not is_dev, cond = not is_dev, priority = 1000, config = function() @@ -22,141 +19,28 @@ return { number = 'none', float = 'none', error = 'none', - comments = enable_italics and 'italic' or 'none', + comments = 'italic', conditionals = 'none', functions = 'none', headings = 'bold', operators = 'none', strings = 'none', variables = 'none', - keywords = enable_italics and 'italic' or 'none', + keywords = 'italic', }, }) vim.cmd('colorscheme vague') end, }, - - { - 'ellisonleao/gruvbox.nvim', - -- Keep available, but don't pay startup cost unless you re-enable it. - enabled = false, - cond = false, - -- enabled = false, - priority = 900, - config = function() - -- Setup basic colorscheme first (fast path) - require('gruvbox').setup({ - contrast = 'hard', - -- palette_overrides = { - -- dark0_hard = '#0E1018', - -- }, - terminal_colors = true, - undercurl = true, - underline = true, - bold = true, - italic = { - strings = enable_italics, - emphasis = enable_italics, - comments = enable_italics, - operators = false, - folds = enable_italics, - }, - strikethrough = true, - invert_selection = true, - invert_signs = false, - invert_tabline = false, - invert_intend_guides = false, - inverse = true, - overrides = { - Comment = { fg = '#81878f', italic = enable_italics, bold = true }, - Define = { link = 'GruvboxPurple' }, - Macro = { link = 'GruvboxPurple' }, - ['@constant.builtin'] = { link = 'GruvboxPurple' }, - ['@storageclass.lifetime'] = { link = 'GruvboxAqua' }, - ['@text.note'] = { link = 'TODO' }, - ['@namespace.rust'] = { link = 'Include' }, - ['@punctuation.bracket'] = { link = 'GruvboxOrange' }, - texMathDelimZoneLI = { link = 'GruvboxOrange' }, - texMathDelimZoneLD = { link = 'GruvboxOrange' }, - luaParenError = { link = 'luaParen' }, - luaError = { link = 'NONE' }, - ContextVt = { fg = '#878788' }, - CopilotSuggestion = { fg = '#878787' }, - CocCodeLens = { fg = '#878787' }, - CocWarningFloat = { fg = '#dfaf87' }, - CocInlayHint = { fg = '#ABB0B6' }, - CocPumShortcut = { fg = '#fe8019' }, - CocPumDetail = { fg = '#fe8019' }, - DiagnosticVirtualTextWarn = { fg = '#dfaf87' }, - Folded = { fg = '#fe8019', bg = '#0E1018', italic = enable_italics }, - SignColumn = { bg = '#2d2021' }, - DiffAdd = { bold = true, reverse = false, fg = '', bg = '#2a4333' }, - DiffChange = { bold = true, reverse = false, fg = '', bg = '#333841' }, - DiffDelete = { - bold = true, - reverse = false, - fg = '#442d30', - bg = '#442d30', - }, - DiffText = { bold = true, reverse = false, fg = '', bg = '#213352' }, - StLine = { bg = '#ff0000' }, - }, - dim_inactive = false, - transparent_mode = false, - }) - - -- Defer expensive highlight lookups (after colorscheme is applied) - vim.schedule(function() - vim.api.nvim_set_hl(0, 'StGitAdd', { - fg = mrl.get_hi('GitSignsAdd').fg, - bg = mrl.get_hi('Statusline').bg, - }) - local cursor_bg = mrl.get_hi('CursorLine').bg - local comment_fg = mrl.get_hi('Comment').fg - vim.api.nvim_set_hl( - 0, - 'SymbolUsageRounding', - { fg = cursor_bg, italic = enable_italics } - ) - vim.api.nvim_set_hl( - 0, - 'SymbolUsageContent', - { bg = cursor_bg, fg = comment_fg, italic = enable_italics } - ) - vim.api.nvim_set_hl(0, 'SymbolUsageRef', { - fg = mrl.get_hi('Function').fg, - bg = cursor_bg, - italic = enable_italics, - }) - vim.api.nvim_set_hl(0, 'SymbolUsageDef', { - fg = mrl.get_hi('Type').fg, - bg = cursor_bg, - italic = enable_italics, - }) - vim.api.nvim_set_hl(0, 'SymbolUsageImpl', { - fg = mrl.get_hi('@keyword').fg, - bg = cursor_bg, - italic = enable_italics, - }) - end) - - vim.cmd('colorscheme gruvbox') - end, - }, - { 'marromlam/theme-builder.nvim', lazy = false, dev = true, enabled = is_dev, cond = is_dev, - dir = '/Users/marcos/Workspaces/personal/theme-builder/generated/carbon-mist/nvim', + dir = '/Users/marcos/Workspaces/personal/theme-builder/generated/amberglow/nvim', priority = 1000, - config = function() - vim.cmd.colorscheme('carbon-mist') - -- vim.cmd.colorscheme('horizon') - -- vim.cmd.colorscheme('catppuccin') - end, + config = function() vim.cmd.colorscheme('amberglow') end, }, } From 415a9f5436dc30576bd08605f8820571b2da21b8 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 23:18:14 +0100 Subject: [PATCH 15/27] feat: remove unneded files --- extra/install_texlive.sh | 75 ------ files/.config/nvim/lua/custom/globals.lua | 107 --------- .../nvim/lua/custom/plugins/lazydev.lua | 34 --- .../.config/nvim/lua/custom/plugins/tpope.lua | 12 - files/.config/nvim/lua/health.lua | 67 ------ files/.config/nvim/queries/html/rainbow.scm | 23 -- files/.config/nvim/skeleton/skel.slides.tex | 1 - files/.config/nvim/skeleton/skel_slides.tex | 1 - files/.config/tmux/.tmux-cht-command | 39 ---- files/.config/tmux/.tmux-cht-languages | 18 -- files/.config/zsh/rc.d/70-path-extra.zsh | 2 - files/.config/zsh/rc.d/80-keychain.zsh | 4 - files/.config/zsh/rc.d/85-env-local.zsh | 2 - scripts/couscous.sh | 17 -- scripts/fzrepl | 110 --------- scripts/install-java-sonarlint.sh | 218 ------------------ scripts/jukitty | 64 ----- scripts/killer | 1 - scripts/tex-preview | 34 --- scripts/vimura | 7 - 20 files changed, 836 deletions(-) delete mode 100644 extra/install_texlive.sh delete mode 100644 files/.config/nvim/lua/custom/globals.lua delete mode 100644 files/.config/nvim/lua/custom/plugins/lazydev.lua delete mode 100644 files/.config/nvim/lua/custom/plugins/tpope.lua delete mode 100644 files/.config/nvim/lua/health.lua delete mode 100644 files/.config/nvim/queries/html/rainbow.scm delete mode 100644 files/.config/nvim/skeleton/skel.slides.tex delete mode 100644 files/.config/nvim/skeleton/skel_slides.tex delete mode 100644 files/.config/tmux/.tmux-cht-command delete mode 100644 files/.config/tmux/.tmux-cht-languages delete mode 100644 files/.config/zsh/rc.d/70-path-extra.zsh delete mode 100644 files/.config/zsh/rc.d/80-keychain.zsh delete mode 100644 files/.config/zsh/rc.d/85-env-local.zsh delete mode 100755 scripts/couscous.sh delete mode 100755 scripts/fzrepl delete mode 100755 scripts/install-java-sonarlint.sh delete mode 100755 scripts/jukitty delete mode 100755 scripts/killer delete mode 100755 scripts/tex-preview delete mode 100755 scripts/vimura diff --git a/extra/install_texlive.sh b/extra/install_texlive.sh deleted file mode 100644 index 8e4accd5..00000000 --- a/extra/install_texlive.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -# This is the fancy installer for texlive under linuxbrew, since the formule -# is broken. Since 2021 texlive has more dependencies than the 2020, and given -# we in almost all situations wont need the 2021 version, I will just install -# the latest 2020 version -# -# WARNING: 2021 actually has some big differences wrt. 2021 for some includes -# in tables/figures. Some 2020 code will fail on 2021. There is an -# easy fix for this. - -# create a install fonder on Cellar {{{ - -mkdir -p $HOMEBREW_PREFIX/Cellar/texlive/ -cd $HOMEBREW_PREFIX/Cellar/texlive/ -wget https://www.texlive.info/tlnet-archive/2020/07/15/tlnet/install-tl-unx.tar.gz -tar xf install-tl-unx.tar.gz - -rm -rf 20200715 -mv {install-tl-,}20200715 -rm -f install-tl-unx.tar.gz* - -# }}} - -# write a profile file {{{ - -cat > texlive.profile < 0 then - preserve_window(cmd.copen, silence) - end -end - -function mrl.list.loc.toggle() - if is_list_open('loclist') then - cmd.lclose(silence) - elseif #fn.getloclist(0) > 0 then - preserve_window(cmd.lopen, silence) - end -end - --- @see: https://vi.stackexchange.com/a/21255 --- using range-aware function -function mrl.list.qf.delete(buf) - buf = buf or api.nvim_get_current_buf() - local list = fn.getqflist() - local line = api.nvim_win_get_cursor(0)[1] - local mode = api.nvim_get_mode().mode - if mode:match('[vV]') then - local first_line = fn.getpos("'<")[2] - local last_line = fn.getpos("'>")[2] - list = mrl.fold(function(accum, item, i) - if i < first_line or i > last_line then accum[#accum + 1] = item end - return accum - end, list) - else - table.remove(list, line) - end - -- replace items in the current list, do not make a new copy of it; this also preserves the list title - fn.setqflist({}, 'r', { items = list }) - fn.setpos('.', { buf, line, 1, 0 }) -- restore current line -end - --- }}} ---------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- --- FILETYPE HELPERS --------------------------------------------------------------------------------- - ----@class FiletypeSettings ----@field g table ----@field bo vim.bo ----@field wo vim.wo ----@field opt vim.opt ----@field plugins {[string]: fun(module: table)} - ----@param args {[1]: string, [2]: string, [3]: string, [string]: boolean | integer}[] ----@param buf integer -local function apply_ft_mappings(args, buf) - mrl.foreach(function(m) - assert( - m[1] and m[2] and m[3], - 'map args must be a table with at least 3 items' - ) - local opts = mrl.fold(function(acc, item, key) - if type(key) == 'string' then acc[key] = item end - return acc - end, m, { buffer = buf }) - map(m[1], m[2], m[3], opts) - end, args) -end - ---- A convenience wrapper that calls the ftplugin config for a plugin if it exists ---- and warns me if the plugin is not installed ----@param configs table -function mrl.ftplugin_conf(configs) - if type(configs) ~= 'table' then return end - for name, callback in pairs(configs) do - local ok, plugin = mrl.pcall(require, name) - if ok then callback(plugin) end - end -end - --- vim: fdm=marker diff --git a/files/.config/nvim/lua/custom/plugins/lazydev.lua b/files/.config/nvim/lua/custom/plugins/lazydev.lua deleted file mode 100644 index 2108f7af..00000000 --- a/files/.config/nvim/lua/custom/plugins/lazydev.lua +++ /dev/null @@ -1,34 +0,0 @@ -return { - 'folke/lazydev.nvim', - ft = 'lua', -- only load on lua files - opts = { - library = { - -- Library paths can be absolute - -- "~/projects/my-awesome-lib", - -- Or relative, which means they will be resolved from the plugin dir. - -- "lazy.nvim", - -- It can also be a table with trigger words / mods - -- Only load luvit types when the `vim.uv` word is found - { path = '${3rd}/luv/library', words = { 'vim%.uv' } }, - -- always load the LazyVim library - 'LazyVim', - -- Only load the lazyvim library when the `LazyVim` global is found - { path = 'LazyVim', words = { 'LazyVim' } }, - -- Load the wezterm types when the `wezterm` module is required - -- Needs `justinsgithub/wezterm-types` to be installed - { path = 'wezterm-types', mods = { 'wezterm' } }, - -- Load the xmake types when opening file named `xmake.lua` - -- Needs `LelouchHe/xmake-luals-addon` to be installed - { path = 'xmake-luals-addon/library', files = { 'xmake.lua' } }, - }, - -- always enable unless `vim.g.lazydev_enabled = false` - -- This is the default - enabled = function(root_dir) - return vim.g.lazydev_enabled == nil and true or vim.g.lazydev_enabled - end, - -- disable when a .luarc.json file is found - enabled = function(root_dir) - return not vim.uv.fs_stat(root_dir .. '/.luarc.json') - end, - }, -} diff --git a/files/.config/nvim/lua/custom/plugins/tpope.lua b/files/.config/nvim/lua/custom/plugins/tpope.lua deleted file mode 100644 index d688a366..00000000 --- a/files/.config/nvim/lua/custom/plugins/tpope.lua +++ /dev/null @@ -1,12 +0,0 @@ -return { - { - 'tpope/vim-sleuth', - event = { 'BufReadPre', 'BufNewFile' }, - }, - { 'tpope/vim-surround', event = { 'BufReadPre', 'BufNewFile' } }, - - { - 'tpope/vim-repeat', - keys = { '.' }, - }, -} diff --git a/files/.config/nvim/lua/health.lua b/files/.config/nvim/lua/health.lua deleted file mode 100644 index d3e77c53..00000000 --- a/files/.config/nvim/lua/health.lua +++ /dev/null @@ -1,67 +0,0 @@ ---[[ --- --- This file is not required for your own configuration, --- but helps people determine if their system is setup correctly. --- ---]] - -local check_version = function() - local verstr = string.format( - '%s.%s.%s', - vim.version().major, - vim.version().minor, - vim.version().patch - ) - if not vim.version.cmp then - vim.health.error( - string.format( - "Neovim out of date: '%s'. Upgrade to latest stable or nightly", - verstr - ) - ) - return - end - - if vim.version.cmp(vim.version(), { 0, 9, 4 }) >= 0 then - vim.health.ok(string.format("Neovim version is: '%s'", verstr)) - else - vim.health.error( - string.format( - "Neovim out of date: '%s'. Upgrade to latest stable or nightly", - verstr - ) - ) - end -end - -local check_external_reqs = function() - -- Basic utils: `git`, `make`, `unzip` - for _, exe in ipairs({ 'git', 'make', 'unzip', 'rg' }) do - local is_executable = vim.fn.executable(exe) == 1 - if is_executable then - vim.health.ok(string.format("Found executable: '%s'", exe)) - else - vim.health.warn(string.format("Could not find executable: '%s'", exe)) - end - end - - return true -end - -return { - check = function() - vim.health.start('kickstart.nvim') - - vim.health.info([[NOTE: Not every warning is a 'must-fix' in `:checkhealth` - - Fix only warnings for plugins and languages you intend to use. - Mason will give warnings for languages that are not installed. - You do not need to install, unless you want to use those languages!]]) - - local uv = vim.uv or vim.loop - vim.health.info('System Information: ' .. vim.inspect(uv.os_uname())) - - check_version() - check_external_reqs() - end, -} diff --git a/files/.config/nvim/queries/html/rainbow.scm b/files/.config/nvim/queries/html/rainbow.scm deleted file mode 100644 index d704915e..00000000 --- a/files/.config/nvim/queries/html/rainbow.scm +++ /dev/null @@ -1,23 +0,0 @@ -;; A pair of opening and closing tag with any content in-between. Excludes -;; self-closing tags or opening tags without closing tag. - -(element - (start_tag - (tag_name) @opening) - (end_tag - (tag_name) @closing)) @container - -(style_element - (start_tag - (tag_name) @opening) - (end_tag - (tag_name) @closing)) @container - -(script_element - (start_tag - (tag_name) @opening) - (end_tag - (tag_name) @closing)) @container - -(self_closing_tag - (tag_name) @intermediate) @container diff --git a/files/.config/nvim/skeleton/skel.slides.tex b/files/.config/nvim/skeleton/skel.slides.tex deleted file mode 100644 index 2b90a60d..00000000 --- a/files/.config/nvim/skeleton/skel.slides.tex +++ /dev/null @@ -1 +0,0 @@ -dfsdf diff --git a/files/.config/nvim/skeleton/skel_slides.tex b/files/.config/nvim/skeleton/skel_slides.tex deleted file mode 100644 index 5c1b1494..00000000 --- a/files/.config/nvim/skeleton/skel_slides.tex +++ /dev/null @@ -1 +0,0 @@ -hola diff --git a/files/.config/tmux/.tmux-cht-command b/files/.config/tmux/.tmux-cht-command deleted file mode 100644 index 1296585c..00000000 --- a/files/.config/tmux/.tmux-cht-command +++ /dev/null @@ -1,39 +0,0 @@ -find -man -tldr -sed -awk -tr -cp -ls -grep -xargs -rg -ps -mv -kill -lsof -less -head -tail -tar -cp -rm -rename -jq -cat -ssh -cargo -git -git-worktree -git-status -git-commit -git-rebase -docker -docker-compose -stow -chmod -chown -make -kinit -krb5 diff --git a/files/.config/tmux/.tmux-cht-languages b/files/.config/tmux/.tmux-cht-languages deleted file mode 100644 index f4ff6807..00000000 --- a/files/.config/tmux/.tmux-cht-languages +++ /dev/null @@ -1,18 +0,0 @@ -golang -nodejs -javascript -tmux -typescript -zsh -cpp -c -lua -rust -python -bash -php -haskell -ArnoldC -css -html -fortran diff --git a/files/.config/zsh/rc.d/70-path-extra.zsh b/files/.config/zsh/rc.d/70-path-extra.zsh deleted file mode 100644 index 2d21e629..00000000 --- a/files/.config/zsh/rc.d/70-path-extra.zsh +++ /dev/null @@ -1,2 +0,0 @@ -# Extra PATHs (optional) -# export PATH="$HOME/.local/opt/sometool/bin:$PATH" diff --git a/files/.config/zsh/rc.d/80-keychain.zsh b/files/.config/zsh/rc.d/80-keychain.zsh deleted file mode 100644 index b1abe91a..00000000 --- a/files/.config/zsh/rc.d/80-keychain.zsh +++ /dev/null @@ -1,4 +0,0 @@ -# Keychain/agent bootstrap (optional) -# if command -v keychain >/dev/null; then -# eval "$(keychain --eval --quiet id_rsa)" -# fi diff --git a/files/.config/zsh/rc.d/85-env-local.zsh b/files/.config/zsh/rc.d/85-env-local.zsh deleted file mode 100644 index 9f0c251a..00000000 --- a/files/.config/zsh/rc.d/85-env-local.zsh +++ /dev/null @@ -1,2 +0,0 @@ -# Machine-specific environment (non-secret) -# export FOO=bar diff --git a/scripts/couscous.sh b/scripts/couscous.sh deleted file mode 100755 index 78dd8fff..00000000 --- a/scripts/couscous.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -THEME=$1 -KITTYTHEMES=$HOME/.config/kitty/kitty-themes - -# clone all kitty themes if needed -if [ ! -d $KITTYTHEMES ]; then - git clone --depth 1 http://github.com/dexpota/kitty-themes.git $KITTYTHEMES -fi - -# show all themes -ls $KITTYTHEMES/themes - -# Change kitty theme -kitty @ set-colors -a "$KITTYTHEMES/themes/$THEME.conf" -rm -rf $HOME/.config/kitty/theme.conf -ln -s $KITTYTHEMES/themes/$THEME.conf $HOME/.config/kitty/theme.conf diff --git a/scripts/fzrepl b/scripts/fzrepl deleted file mode 100755 index 435e5d1a..00000000 --- a/scripts/fzrepl +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash - -usage() { - less -FEXR <<'HELP' -fzrepl - interactively edit stdin using stream filters like awk, sed, jq - - -c, --cmd command used to filter input - -q, --query default command string to use - -command history can be saved to a file by setting the environment variable -FZREPL_FILE - -examples: - echo 'foo bar' | fzrepl -c 'awk {q}' -q '{print}' - echo 'hello world' | fzrepl -q p 'sed -n {q}' - FZREPL_FILE=jqhistory fzrepl jq package.json -HELP -} - -# TODO: better "wrapping", this is painful: -# fzrepl 'node -e {q}' -q "done = data => data;\nlet A='';process.stdin.on('data',x=>A=A.concat(x.toString())).on('end',()=>{let d = done(A);process.stdout.write(`${String.prototype.trim.call(typeof d==='string'?d:JSON.stringify(d,null,2))}\n`)})" - -declare tmpfile=/tmp/fzreplinput -declare cmd -declare default_query -declare output - -declare -A colors -colors[red]=$(tput setaf 1) -colors[green]=$(tput setaf 2) -colors[reset]=$(tput sgr0) - -cleanup() { - [[ -e "$tmpfile" ]] && rm "$tmpfile" -} -trap cleanup SIGHUP SIGINT SIGTERM - -color() { - local color - color="$1"; shift - printf '%s' "${colors[$color]}" "$*" "${colors[reset]}" -} - -err() { - color red "$@" >&2 - return 1 -} - -die() { - (( $# > 0 )) && err "$@" - exit 1 -} - -for arg; do - case $arg in - -q|--query) - [[ -z $2 || $2 = -* ]] && die "missing argument to $1" - default_query="$2" - shift 2 ;; - -c|--cmd) - [[ -z $2 || $2 = -* ]] && die "missing argument to $1" - cmd="$2" - shift 2 ;; - -h|--help) usage; exit ;; - *) break 2; - esac -done - -if [[ -z $cmd && -n $1 && ! -f $1 ]]; then - cmd="$1" - shift -fi - -if [[ -z $cmd ]]; then - usage - exit 1 -fi - -if [[ $cmd != *'{q}'* ]]; then - cmd+=' {q}' -fi - -if [[ -n $1 && -f $1 ]]; then - file=$1 - shift -fi - -if [[ -z $file ]]; then - file=/dev/stdin -fi - -mapfile -t REPLY < <( - tee "$tmpfile" < "$file" | fzf \ - --sync \ - --ansi \ - --height=100% \ - --disabled \ - --print-query \ - --query="$default_query" \ - ${FZREPL_FILE:+--history=$FZREPL_FILE} \ - --preview="$cmd < '$tmpfile'" -) - -q="${REPLY[0]}" -q=${q@Q} -echo "${cmd//'{q}'/$q}" - - -# vim: ft=bash diff --git a/scripts/install-java-sonarlint.sh b/scripts/install-java-sonarlint.sh deleted file mode 100755 index d82fa8e1..00000000 --- a/scripts/install-java-sonarlint.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env bash -# -# Safe Java + SonarLint Installer for macOS -# Installs everything via Homebrew to keep it clean and manageable -# -# Usage: ./install-java-sonarlint.sh - -set -euo pipefail - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Helper functions -log_info() { - echo -e "${BLUE}[INFO]${NC} $*" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $*" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $*" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $*" -} - -# Check if running on macOS -if [[ "$(uname -s)" != "Darwin" ]]; then - log_error "This script is designed for macOS only" - exit 1 -fi - -# Determine Homebrew prefix (supports both Intel and Apple Silicon) -if [[ -d "/opt/homebrew" ]]; then - BREW_PREFIX="/opt/homebrew" -elif [[ -d "/usr/local" ]]; then - BREW_PREFIX="/usr/local" -else - log_error "Homebrew installation not found" - exit 1 -fi - -log_info "Using Homebrew prefix: $BREW_PREFIX" - -# Check if Homebrew is installed -if ! command -v brew &> /dev/null; then - log_error "Homebrew is not installed. Please install it first:" - echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" - exit 1 -fi - -log_success "Homebrew is installed" - -# Update Homebrew -log_info "Updating Homebrew..." -brew update || log_warning "Failed to update Homebrew (continuing anyway)" - -# Check if Java is already installed -if command -v java &> /dev/null && java -version &> /dev/null; then - JAVA_VERSION=$(java -version 2>&1 | head -n 1) - log_success "Java is already installed: $JAVA_VERSION" - - # Ask if user wants to reinstall - read -p "Do you want to reinstall/update Java? [y/N] " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - log_info "Skipping Java installation" - SKIP_JAVA=true - fi -fi - -# Install OpenJDK via Homebrew -if [[ "${SKIP_JAVA:-false}" != "true" ]]; then - log_info "Installing OpenJDK 21 (LTS) via Homebrew..." - - # Install openjdk@21 - if brew list openjdk@21 &> /dev/null; then - log_info "OpenJDK 21 is already installed, upgrading if needed..." - brew upgrade openjdk@21 || log_warning "OpenJDK 21 is already up to date" - else - brew install openjdk@21 - fi - - log_success "OpenJDK 21 installed successfully" - - # Determine the correct openjdk path - OPENJDK_PATH="$BREW_PREFIX/opt/openjdk@21" - - if [[ ! -d "$OPENJDK_PATH" ]]; then - log_error "OpenJDK installation directory not found: $OPENJDK_PATH" - exit 1 - fi - - log_info "OpenJDK installed at: $OPENJDK_PATH" -fi - -# Determine shell config file -if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$SHELL" == *"zsh"* ]]; then - SHELL_CONFIG="$HOME/.zshrc" -elif [[ -n "${BASH_VERSION:-}" ]] || [[ "$SHELL" == *"bash"* ]]; then - SHELL_CONFIG="$HOME/.bashrc" -else - SHELL_CONFIG="$HOME/.profile" -fi - -log_info "Detected shell config: $SHELL_CONFIG" - -# Update shell configuration with JAVA_HOME and PATH -if [[ "${SKIP_JAVA:-false}" != "true" ]]; then - log_info "Configuring environment variables in $SHELL_CONFIG..." - - # Create backup - cp "$SHELL_CONFIG" "$SHELL_CONFIG.backup-$(date +%Y%m%d-%H%M%S)" - log_info "Created backup: $SHELL_CONFIG.backup-$(date +%Y%m%d-%H%M%S)" - - # Remove old Java entries if they exist - sed -i.tmp '/# Java (OpenJDK) - Homebrew/d' "$SHELL_CONFIG" 2>/dev/null || true - sed -i.tmp '/export JAVA_HOME.*openjdk/d' "$SHELL_CONFIG" 2>/dev/null || true - sed -i.tmp '/export PATH.*openjdk.*bin/d' "$SHELL_CONFIG" 2>/dev/null || true - rm -f "$SHELL_CONFIG.tmp" - - # Add new Java configuration - cat >> "$SHELL_CONFIG" << EOF - -# Java (OpenJDK) - Homebrew -export JAVA_HOME="$BREW_PREFIX/opt/openjdk@21" -export PATH="\$JAVA_HOME/bin:\$PATH" -EOF - - log_success "Environment variables configured in $SHELL_CONFIG" - - # Export for current session - export JAVA_HOME="$BREW_PREFIX/opt/openjdk@21" - export PATH="$JAVA_HOME/bin:$PATH" -fi - -# Verify Java installation -if command -v java &> /dev/null; then - JAVA_VERSION=$(java -version 2>&1 | head -n 1) - log_success "Java verification: $JAVA_VERSION" - - # Show Java path - JAVA_PATH=$(which java) - log_info "Java executable: $JAVA_PATH" - - # Show JAVA_HOME - log_info "JAVA_HOME: ${JAVA_HOME:-not set}" -else - log_error "Java installation verification failed" - log_info "Please restart your terminal and run: java -version" -fi - -# Install Mason if not already installed (for Neovim) -log_info "Checking for Neovim installation..." -if command -v nvim &> /dev/null; then - log_success "Neovim is installed" - - # Check if Mason is available - log_info "Verifying Mason installation in Neovim..." - if nvim --headless -c "lua if pcall(require, 'mason') then print('MASON_OK') else print('MASON_NOT_FOUND') end" -c "qa" 2>&1 | grep -q "MASON_OK"; then - log_success "Mason is installed in Neovim" - - # Install sonarlint-language-server via Mason - log_info "Installing sonarlint-language-server via Mason..." - nvim --headless -c "lua require('mason.api.command').MasonInstall({'sonarlint-language-server'})" -c "sleep 30000m" -c "qa" 2>&1 & - MASON_PID=$! - - log_info "Mason is installing sonarlint-language-server (PID: $MASON_PID)" - log_info "This may take a few minutes..." - - # Wait for Mason installation - wait $MASON_PID || log_warning "Mason installation may have issues, check :Mason in Neovim" - - log_success "SonarLint installation initiated via Mason" - else - log_warning "Mason not found in Neovim" - log_info "SonarLint will be installed automatically when you open Neovim" - fi -else - log_warning "Neovim is not installed" - log_info "Install Neovim first, then SonarLint will be installed automatically" -fi - -# Summary -echo "" -echo "═══════════════════════════════════════════════════════════════" -log_success "Installation Complete!" -echo "═══════════════════════════════════════════════════════════════" -echo "" -log_info "Summary:" -echo " ✓ Java (OpenJDK 21) installed via Homebrew" -echo " ✓ JAVA_HOME configured: $BREW_PREFIX/opt/openjdk@21" -echo " ✓ Java added to PATH in $SHELL_CONFIG" -echo " ✓ SonarLint installation initiated" -echo "" -log_warning "IMPORTANT: Restart your terminal or run:" -echo " source $SHELL_CONFIG" -echo "" -log_info "To verify the installation, run:" -echo " java -version" -echo " echo \$JAVA_HOME" -echo "" -log_info "In Neovim, check Mason status with:" -echo " :Mason" -echo " :LspInfo" -echo "" -log_info "Installation locations (all in Homebrew):" -echo " Java: $BREW_PREFIX/opt/openjdk@21" -echo " Mason tools: ~/.local/share/nvim/mason/" -echo "" -log_success "All done! 🎉" diff --git a/scripts/jukitty b/scripts/jukitty deleted file mode 100755 index e46abf18..00000000 --- a/scripts/jukitty +++ /dev/null @@ -1,64 +0,0 @@ -_jukitty(){ - #nvim $1 - - # 1 - First export the display - export DISPLAY=:0.0 - - # 2 - Check whether lemonade is running - ps cax | grep lemonade> /dev/null - if [ $? -eq 0 ]; then - echo "= lemonade is running. =" - else - echo "= lemonade was not running... lemonade was launched. =" - nohup lemonade server -allow 127.0.0.1 & - fi - - # 3 - Generate random string as window titles - EDITOR_ID=`xxd -l4 -ps /dev/urandom` - KERNEL_ID=`xxd -l4 -ps /dev/urandom` - - # 4 - Launch a new tab / os-window and split it - KE_ID=`kitty @ launch --type=tab --title $EDITOR_ID --keep-focus` - KK_ID=`kitty @ launch -m title:$EDITOR_ID --title $KERNEL_ID --keep-focus` - - # 5 -put juKitty as window name - kitty @ set-tab-title --match title:$EDITOR_ID "juKitty" - - echo "keditor" $KE_ID - echo "kkernel" $KK_ID - - # export KITTY_WINDOW_ID to be the one in the KKERNEL_ID window - kitty @ send-text --match title:$EDITOR_ID "export KITTY_WINDOW_ID=${KK_ID}\n" - # open nvim in first window - kitty @ send-text --match title:$EDITOR_ID "nvim $1\n" - kitty @ send-text --match title:$KERNEL_ID "alias ssh=ssh -R 2489:127.0.0.1:2489\n" - - #nvim $1 - - kitty @ focus-tab --match title:$KERNEL_ID - #LOGO=${cat << EndOfMessage - # _ _ __ _ _ _ - # (_) _ _ | |/ / (_) | |_ | |_ _ _ - # | | | | | | | ' / | | | __| | __| | | | | - # | | | |_| | | . \ | | | |_ | |_ | |_| | - # _/ | \__,_| |_|\_\ |_| \__| \__| \__, | - # |__/ |___/ - #EndOfMessage - #} - - merde=" - _ _ __ _ _ _ - (_) _ _ | |/ / (_) | |_ | |_ _ _ - | | | | | | | ' / | | | __| | __| | | | | - | | | |_| | | . \ | | | |_ | |_ | |_| | - _/ | \__,_| |_|\_\ |_| \__| \__| \__, | - |__/ |___/ - " - #echo $merde | kitty @ send-text --match title:$KERNEL_ID --stdin - - kitty @ send-text --match title:$KERNEL_ID "clc; echo juKitty kernel running at ${KERNEL_ID} with window ID = ${KK_ID}\n" - #ls | kitty @ send-text --match title:$KERNEL_ID --stdin - #kitty @ send-text --match title:$KERNEL_ID cat ls -} - -_jukitty "$@" diff --git a/scripts/killer b/scripts/killer deleted file mode 100755 index 6e0a9d3f..00000000 --- a/scripts/killer +++ /dev/null @@ -1 +0,0 @@ -ps -ax | fzf -m --layout=reverse | xargs | python -c "import sys; print('\n'.join([el.split(' ')[-1] for el in sys.stdin.read().replace('tty','?? ').split(' ??')][:-1]))" | xargs diff --git a/scripts/tex-preview b/scripts/tex-preview deleted file mode 100755 index 12c66edc..00000000 --- a/scripts/tex-preview +++ /dev/null @@ -1,34 +0,0 @@ -TEXCODE=$1 - -# %& -job-name=newfilenameialwayswanted - - -mkdir -p /tmp/tex-preview -rm -rf /tmp/tex-preview/output.tex && touch /tmp/tex-preview/output.tex - -echo "\documentclass[10pt]{standalone}" > /tmp/tex-preview/output.tex -echo "\usepackage{xcolor}" >> /tmp/tex-preview/output.tex -# echo "\usepackage[showframe]{geometry}" >> /tmp/tex-preview/output.tex -echo "\begin{document}" >> /tmp/tex-preview/output.tex -echo "\color{white}" >> /tmp/tex-preview/output.tex -echo "%%%%%" >> /tmp/tex-preview/output.tex -# echo "\centering \verb|$1|" >> /tmp/tex-preview/output.tex -echo " " >> /tmp/tex-preview/output.tex -/usr/bin/cat $1 >> /tmp/tex-preview/output.tex -echo " " >> /tmp/tex-preview/output.tex -echo "%%%%%" >> /tmp/tex-preview/output.tex -echo "\end{document}" >> /tmp/tex-preview/output.tex - - -pushd /tmp/tex-preview > /dev/null -# pdflatex output.tex > pdflatex.log 2>&1 -/home3/marcos.romero/.linuxbrew/bin/pdflatex -halt-on-error -interaction=nonstopmode output.tex > pdflatex.log 2>&1 - -if which kitty >/dev/null; then - kitty +kitten icat /tmp/tex-preview/output.pdf -else - echo "Compiled pdf at: /tmp/tex-preview/output.pdf" -fi -popd > popd - -# vim: ft=sh diff --git a/scripts/vimura b/scripts/vimura deleted file mode 100755 index 139ca556..00000000 --- a/scripts/vimura +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -if [ $1 == "neovim" ]; then - synctex_command="nvr --remote +%{line} %{input}" -else - synctex_command="vim --servername vimd --remote +%{line} %{input}" -fi -zathura -x "$synctex_command" ${@:2} From f49492969cc2921d48e0fcc89b7fbe20ff4a2fc6 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 23:26:13 +0100 Subject: [PATCH 16/27] feat: use amberglow theme --- files/.config/kitty/kitty.conf | 2 +- .../.config/nvim/after/ftplugin/markdown.lua | 1 + files/.config/nvim/after/ftplugin/tex.lua | 1 + .../nvim/lua/custom/plugins/gaming.lua | 4 + files/.config/nvim/skeleton/skel.sh | 3 +- files/.config/tmux/TODO.md | 4 + files/.config/tmux/statusbar.conf | 3 + files/.config/tmux/tmux.conf | 22 +--- files/.config/wezterm/wezterm.lua | 14 +- files/.config/zsh/aliases.d/git.sh | 120 ++++++++---------- files/.config/zsh/aliases.d/misc.sh | 1 - files/.config/zsh/common.sh | 1 - files/.config/zsh/utils.sh | 3 +- files/.config/zsh/windows.sh | 1 - 14 files changed, 85 insertions(+), 95 deletions(-) create mode 100644 files/.config/nvim/after/ftplugin/markdown.lua create mode 100644 files/.config/nvim/after/ftplugin/tex.lua create mode 100644 files/.config/nvim/lua/custom/plugins/gaming.lua create mode 100644 files/.config/tmux/TODO.md diff --git a/files/.config/kitty/kitty.conf b/files/.config/kitty/kitty.conf index d630ca34..c7dfebcf 100644 --- a/files/.config/kitty/kitty.conf +++ b/files/.config/kitty/kitty.conf @@ -1205,7 +1205,7 @@ include ./cartograph.conf # BEGIN_KITTY_THEME -include themes/horizon.conf +include themes/amberglow.conf # END_KITTY_THEME diff --git a/files/.config/nvim/after/ftplugin/markdown.lua b/files/.config/nvim/after/ftplugin/markdown.lua new file mode 100644 index 00000000..da31014c --- /dev/null +++ b/files/.config/nvim/after/ftplugin/markdown.lua @@ -0,0 +1 @@ +vim.opt_local.wrap = false diff --git a/files/.config/nvim/after/ftplugin/tex.lua b/files/.config/nvim/after/ftplugin/tex.lua new file mode 100644 index 00000000..da31014c --- /dev/null +++ b/files/.config/nvim/after/ftplugin/tex.lua @@ -0,0 +1 @@ +vim.opt_local.wrap = false diff --git a/files/.config/nvim/lua/custom/plugins/gaming.lua b/files/.config/nvim/lua/custom/plugins/gaming.lua new file mode 100644 index 00000000..6c4fa7a0 --- /dev/null +++ b/files/.config/nvim/lua/custom/plugins/gaming.lua @@ -0,0 +1,4 @@ +return { + + { 'meznaric/key-analyzer.nvim', opts = {}, cmd = 'KeyAnalyzer' }, +} diff --git a/files/.config/nvim/skeleton/skel.sh b/files/.config/nvim/skeleton/skel.sh index af6ac3a3..485e6f15 100644 --- a/files/.config/nvim/skeleton/skel.sh +++ b/files/.config/nvim/skeleton/skel.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash -@CURSOR@ set -eou pipefail +@CURSOR@ + # vim:foldmethod=marker diff --git a/files/.config/tmux/TODO.md b/files/.config/tmux/TODO.md new file mode 100644 index 00000000..7f85ca15 --- /dev/null +++ b/files/.config/tmux/TODO.md @@ -0,0 +1,4 @@ +- [ ] Consolidate helpers +- [ ] Icons should stay in statusbar +- [ ] Add pomodoro popup and calendar integration +- [ ] Themse should live in ~/.local/share diff --git a/files/.config/tmux/statusbar.conf b/files/.config/tmux/statusbar.conf index 14bfad29..31e956a6 100644 --- a/files/.config/tmux/statusbar.conf +++ b/files/.config/tmux/statusbar.conf @@ -13,6 +13,9 @@ set-option -g status-interval 10 set -g status-left-length 30 set -g status-right-length 30 +SESSION_ICON="󱂬" +BELL_ICON="⚘" + # }}} diff --git a/files/.config/tmux/tmux.conf b/files/.config/tmux/tmux.conf index 08544c7e..4a744c34 100644 --- a/files/.config/tmux/tmux.conf +++ b/files/.config/tmux/tmux.conf @@ -247,7 +247,9 @@ set -g @resurrect-capture-pane-contents 'on' set -g @resurrect-processes 'ssh nvim' # Theme: source a theme file (defines colors/variables), then the statusbar layout -source $HOME/.config/tmux/themes/carbon-mist.tmux +# BEGIN_TMUX_THEME +source-file ~/.config/tmux/themes/amberglow.tmux +# END_TMUX_THEME source $HOME/.config/tmux/statusbar.conf # Initialize plugin manager @@ -256,21 +258,3 @@ run -b '$HOME/.local/share/tmux/plugins/tpm/tpm' # }}} # vim: fdm=marker ft=tmux - - - - - - - - - - - - - - - - - - diff --git a/files/.config/wezterm/wezterm.lua b/files/.config/wezterm/wezterm.lua index 04238d08..36bfab3c 100644 --- a/files/.config/wezterm/wezterm.lua +++ b/files/.config/wezterm/wezterm.lua @@ -623,8 +623,16 @@ return { --- BEGIN_WEZTERM_THEME: carbon-mist -config.color_scheme = 'carbon-mist' --- END_WEZTERM_THEME: carbon-mist + + + + + + + + +-- BEGIN_WEZTERM_THEME: amberglow +config.color_scheme = 'amberglow' +-- END_WEZTERM_THEME: amberglow -- vim: fdm=marker ts=2 sw=2 sts=2 et diff --git a/files/.config/zsh/aliases.d/git.sh b/files/.config/zsh/aliases.d/git.sh index e93d28b1..5236fffa 100644 --- a/files/.config/zsh/aliases.d/git.sh +++ b/files/.config/zsh/aliases.d/git.sh @@ -7,18 +7,15 @@ alias g="git" alias gss="git status -s" alias gst="git status" -alias gc="git commit" -alias gd="git diff" +alias gc='git commit -v' +alias gd='git diff' alias gco="git checkout" alias ga='git add' alias gaa='git add --all' -alias gcb='git checkout -b' alias gb='git branch' alias gbD='git branch -D' alias gbl='git blame -b -w' alias gbr='git branch --remote' -alias gc='git commit -v' -alias gd='git diff' alias gf='git fetch' alias gfa='git fetch --all --prune' alias gfo='git fetch origin' @@ -32,123 +29,112 @@ alias gcl='git clone --recurse-submodules' alias gl='git pull' alias glum='git pull upstream $(git_main_branch)' alias grhh='git reset --hard' -alias groh='git reset origin/$(git_current_branch) --hard' alias grbi='git rebase -i' alias grbm='git rebase $(git_main_branch)' +alias gcb='git checkout -b' alias gcm='git checkout $(git_main_branch)' alias gcd="git checkout development" -alias gcb="git checkout -b" alias gstp="git stash pop" alias gsts="git stash show -p" function grename() { - if [[ -z "$1" || -z "$2" ]]; then - echo "Usage: $0 old_branch new_branch" - return 1 - fi - - # Rename branch locally - git branch -m "$1" "$2" - # Rename branch in origin remote - if git push origin :"$1"; then - git push --set-upstream origin "$2" - fi + if [[ -z "$1" || -z "$2" ]]; then + echo "Usage: $0 old_branch new_branch" + return 1 + fi + + # Rename branch locally + git branch -m "$1" "$2" + # Rename branch in origin remote + if git push origin :"$1"; then + git push --set-upstream origin "$2" + fi } function gdnolock() { - git diff "$@" ":(exclude)package-lock.json" ":(exclude)*.lock" + git diff "$@" ":(exclude)package-lock.json" ":(exclude)*.lock" } # compdef _git gdnolock=git-diff # Check if main exists and use instead of master function git_main_branch() { - local branch - for branch in main trunk; do - if command git show-ref -q --verify refs/heads/$branch; then - echo $branch - return - fi - done - echo master + local branch + for branch in main trunk; do + if command git show-ref -q --verify refs/heads/$branch; then + echo $branch + return + fi + done + echo master } # }}} # Github CLI {{{ function cs-list() { - gh codespace list | awk 'NR > 0 {print $1"@"$5}' + gh codespace list | awk 'NR > 0 {print $1"@"$5}' } function cs-ssh() { - # if we get one argument, we assume it's a codespace name - # else we use fzf to select one - - if [ $# -eq 1 ]; then - gh codespace ssh -c $1 - else - CODESPACE=$(gh codespace list | awk 'NR > 0 {print $1"@"$5}' | fzf | awk -F@ '{print $1}') - echo $CODESPACE - gh codespace ssh -c $CODESPACE - fi + # if we get one argument, we assume it's a codespace name + # else we use fzf to select one + + if [ $# -eq 1 ]; then + gh codespace ssh -c $1 + else + CODESPACE=$(gh codespace list | awk 'NR > 0 {print $1"@"$5}' | fzf | awk -F@ '{print $1}') + echo $CODESPACE + gh codespace ssh -c $CODESPACE + fi } - - - - - # }}} # Mounting {{{ select_partition() { - local partition - - # Use lsblk to list all partitions (excluding loops) not mounted and pipe to fzf for selection - PARTITION=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT -n | awk '$3 == "part" {gsub(/[├└]─/, "", $1); print $1}' | fzf --height 50% --reverse --prompt="Select a partition to mount: " --header="NAME SIZE TYPE") - - # Check if a partition was selected - if [ -n "$PARTITION" ]; then - echo "Selected partition: $PARTITION" - # Perform further actions with the selected partition, e.g., mount it - # Add your custom logic here - sudo mount /dev/$PARTITION /mnt/ - else - echo "No partition selected." - fi + local partition + + # Use lsblk to list all partitions (excluding loops) not mounted and pipe to fzf for selection + PARTITION=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT -n | awk '$3 == "part" {gsub(/[├└]─/, "", $1); print $1}' | fzf --height 50% --reverse --prompt="Select a partition to mount: " --header="NAME SIZE TYPE") + + # Check if a partition was selected + if [ -n "$PARTITION" ]; then + echo "Selected partition: $PARTITION" + # Perform further actions with the selected partition, e.g., mount it + # Add your custom logic here + sudo mount /dev/$PARTITION /mnt/ + else + echo "No partition selected." + fi } # }}} - # Docker {{{ -dcu(){ +dcu() { # docker-service docker compose up -d } -dcd(){ +dcd() { docker compose down } # }}} - repo() { # This will list up to 2 depth of given directory. Add/remove '*/' to your preferences. # The path should be absolute path. # Note that too many depth will slow-down the script, less depth will show fewer results. - local srchDir=/Users/marcos/Projects/*/*/ + local srchDir=$HOME/Workspaces/*/*/ # if any arguments arn't given, just list up everything else use search query - [ "$1" = false ] && cd "$(ls -d $srchDir | fzf)" || local qTerm="${@}"; cd "$(ls -d $srchDir | fzf -q "$qTerm")" + [ "$1" = false ] && cd "$(ls -d $srchDir | fzf)" || local qTerm="${@}" + cd "$(ls -d $srchDir | fzf -q "$qTerm")" } - - ocurrences() { rg -o "$@" | wc -l } - - # vim: fdm=marker et ts=4 sw=4 tw=80 diff --git a/files/.config/zsh/aliases.d/misc.sh b/files/.config/zsh/aliases.d/misc.sh index c48a3cf8..c974219a 100644 --- a/files/.config/zsh/aliases.d/misc.sh +++ b/files/.config/zsh/aliases.d/misc.sh @@ -20,7 +20,6 @@ alias x="exit" # Exit Terminal alias del="rm -rf" alias dots="cd $DOTFILES" alias coding="cd $PROJECTS_DIR" -alias lp="lsp" # ssh aliases {{{ diff --git a/files/.config/zsh/common.sh b/files/.config/zsh/common.sh index 0c6e669b..f5c702e2 100644 --- a/files/.config/zsh/common.sh +++ b/files/.config/zsh/common.sh @@ -31,7 +31,6 @@ USER_PATHS=( "$HOME/.cargo/bin" ) - # Filter paths that exist SYS_PATHS=($(for p in "${SYS_PATHS[@]}"; do path_exists "$p" && echo "$p"; done)) USER_PATHS=($(for p in "${USER_PATHS[@]}"; do path_exists "$p" && echo "$p"; done)) diff --git a/files/.config/zsh/utils.sh b/files/.config/zsh/utils.sh index 8ffc917a..5899aa7d 100644 --- a/files/.config/zsh/utils.sh +++ b/files/.config/zsh/utils.sh @@ -116,7 +116,6 @@ alias kill_jupyter="kill $(netstat -tulpn 2>&1 | pgrep jupyter)" /opt/homebrew/bin/claude --print "$query" } -# vim: fdm=marker ft=zsh # Get current environment name get_env() { @@ -128,3 +127,5 @@ get_env() { echo "syst" fi } + +# vim: fdm=marker ft=bash diff --git a/files/.config/zsh/windows.sh b/files/.config/zsh/windows.sh index a5712ce1..026ae655 100644 --- a/files/.config/zsh/windows.sh +++ b/files/.config/zsh/windows.sh @@ -68,5 +68,4 @@ sync-windows-config() { } - # vim: fdm=marker ft=bash From 6be5abfeaaec5ea1399afc175b601a55a3229fab Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 23:31:53 +0100 Subject: [PATCH 17/27] feat: update some plugin config --- files/.config/nvim/filetype.lua | 2 - files/.config/nvim/init.lua | 32 ---- files/.config/nvim/lazy-lock.json | 40 ++--- .../.config/nvim/lua/custom/plugins/debug.lua | 30 +--- .../.config/nvim/lua/custom/plugins/folke.lua | 108 ------------- .../.config/nvim/lua/custom/plugins/init.lua | 52 ++----- .../nvim/lua/custom/plugins/linting.lua | 2 +- .../.config/nvim/lua/custom/plugins/mini.lua | 31 ---- .../nvim/lua/custom/plugins/navigation.lua | 145 ++++-------------- 9 files changed, 64 insertions(+), 378 deletions(-) diff --git a/files/.config/nvim/filetype.lua b/files/.config/nvim/filetype.lua index 8f620cdf..21662145 100644 --- a/files/.config/nvim/filetype.lua +++ b/files/.config/nvim/filetype.lua @@ -1,5 +1,3 @@ -if not vim.filetype then return end - vim.filetype.add({ extension = { lock = 'yaml', diff --git a/files/.config/nvim/init.lua b/files/.config/nvim/init.lua index c20e9956..16ec7b94 100644 --- a/files/.config/nvim/init.lua +++ b/files/.config/nvim/init.lua @@ -22,39 +22,11 @@ vim.g.obsidian = vim.g.icloud .. '/iCloud~md~obsidian/Documents/Marcos' vim.g.mapleader = ' ' -- Remap leader key vim.g.maplocalleader = '\\' -- Local leader is --------------------------------------------------------------------------------- --- Global namespace {{{ --------------------------------------------------------------------------------- - -local namespace = { - ui = { - statuscolumn = { - enable = true, -- Re-enabled with caching optimizations - number_width = 3, - hide_diag_on_cursorline = true, - }, - statusline = { enable = true }, - }, - -- some vim mappings require a mixture of commandline commands and function - -- calls this table is place to store lua functions to be called in those - -- mappings - mappings = { enable = true }, -} - --- This table is a globally accessible store to facilitating accessing --- helper functions and variables throughout my config -_G.mrl = mrl or namespace -_G.map = vim.keymap.set -_G.P = vim.print - -- If opening from inside neovim terminal buffer, skip full config if vim.env.NVIM and vim.env.TERM_PROGRAM == 'nvim' then return require('lazy').setup({ { 'willothy/flatten.nvim', config = true } }) end --- }}} --------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -- Load modules {{{ -------------------------------------------------------------------------------- @@ -75,8 +47,4 @@ end, 0) -- }}} -------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- }}} --------------------------------------------------------------------------------- - -- vim: ts=2 sts=2 sw=2 et fdm=marker diff --git a/files/.config/nvim/lazy-lock.json b/files/.config/nvim/lazy-lock.json index 7987424e..3264eeac 100644 --- a/files/.config/nvim/lazy-lock.json +++ b/files/.config/nvim/lazy-lock.json @@ -3,9 +3,9 @@ "blink-cmp-avante": { "branch": "master", "commit": "4f494c6e124acbe31a8f5d58effa0c14aa38a6d5" }, "blink.cmp": { "branch": "main", "commit": "4b18c32adef2898f95cdef6192cbd5796c1a332d" }, "ccc.nvim": { "branch": "main", "commit": "9d1a256e006decc574789dfc7d628ca11644d4c2" }, - "codecompanion.nvim": { "branch": "main", "commit": "f22fe1b04e47110a26abe70bad537cb9baf9c665" }, - "conform.nvim": { "branch": "master", "commit": "40dcec5555f960b0a04340d76eabdf4efe78599d" }, - "copilot.lua": { "branch": "master", "commit": "10c1c888412f6dcc808c6a12525079d7bf9575f9" }, + "codecompanion.nvim": { "branch": "main", "commit": "5e4f7cfac58c94c61d5a3d3a99d715d5c8762db6" }, + "conform.nvim": { "branch": "master", "commit": "086a40dc7ed8242c03be9f47fbcee68699cc2395" }, + "copilot.lua": { "branch": "master", "commit": "11f6d961881783c4db4bc916bad900d26599b366" }, "crates.nvim": { "branch": "main", "commit": "ac9fa498a9edb96dc3056724ff69d5f40b898453" }, "csvview.nvim": { "branch": "main", "commit": "7022e18a0fbae9aecf99a3ba02b2a541edc2b8a1" }, "debugprint.nvim": { "branch": "main", "commit": "4f29196c5fe32752bb7a3e8bc83d4a4d2f3b7922" }, @@ -15,16 +15,13 @@ "file-line": { "branch": "main", "commit": "559088afaf10124ea663ee0f4f73b1de48fb1632" }, "fold-cycle.nvim": { "branch": "main", "commit": "6144567b3307bbcfed0e5b2dd23acb9576575d9e" }, "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, - "fugitive-difftool.nvim": { "branch": "main", "commit": "5c4b2ed39f55f5f96c8b72e5fc1a828fc26ff82b" }, - "fzf-lua": { "branch": "main", "commit": "1eba927866251bae1b61dffc5b673b8dbd0f3f48" }, + "fzf-lua": { "branch": "main", "commit": "30ba09afbc0eac5fc5ec8823418d1e45d0712b9f" }, "git-worktree.nvim": { "branch": "master", "commit": "f247308e68dab9f1133759b05d944569ad054546" }, "gitgraph.nvim": { "branch": "main", "commit": "c16daa7d7dd597caf9085644c009cfa80b75db8e" }, - "github-notifications.nvim": { "branch": "master", "commit": "a1935937ec6f71ef8e72db73c66bc4e659f2aa3c" }, "gitlinker.nvim": { "branch": "master", "commit": "cc59f732f3d043b626c8702cb725c82e54d35c25" }, - "gitsigns.nvim": { "branch": "main", "commit": "9f3c6dd7868bcc116e9c1c1929ce063b978fa519" }, + "gitsigns.nvim": { "branch": "main", "commit": "7c4faa3540d0781a28588cafbd4dd187a28ac6e3" }, "glance.nvim": { "branch": "master", "commit": "bf86d8b79dce808e65fdb6e9269d0b4ed6d2eefc" }, - "gruvbox.nvim": { "branch": "main", "commit": "a472496e1a4465a2dd574389dcf6cdb29af9bf1b" }, - "harpoon": { "branch": "harpoon2", "commit": "87b1a3506211538f460786c23f98ec63ad9af4e5" }, + "grapple.nvim": { "branch": "main", "commit": "b41ddfc1c39f87f3d1799b99c2f0f1daa524c5f7" }, "hererocks": { "branch": "master", "commit": "3db37265c3839cbd4d27fc73f92fa7b58bc3a76f" }, "image.nvim": { "branch": "master", "commit": "da2be65c153ba15a14a342b05591652a6df70d58" }, "inc-rename.nvim": { "branch": "main", "commit": "0074b551a17338ccdcd299bd86687cc651bcb33d" }, @@ -35,7 +32,7 @@ "lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" }, "lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" }, "markdown-table-mode.nvim": { "branch": "main", "commit": "bb1ea9b76c1b29e15e14806fdfbb2319df5c06f1" }, - "mason-lspconfig.nvim": { "branch": "main", "commit": "a324581a3c83fdacdb9804b79de1cbe00ce18550" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "a676ab7282da8d651e175118bcf54483ca11e46d" }, "mason-nvim-dap.nvim": { "branch": "main", "commit": "9a10e096703966335bd5c46c8c875d5b0690dade" }, "mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" }, "mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, @@ -61,27 +58,28 @@ "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "numb.nvim": { "branch": "master", "commit": "12ef3913dea8727d4632c6f2ed47957a993de627" }, - "nvim-dap": { "branch": "master", "commit": "b516f20b487b0ac6a281e376dfac1d16b5040041" }, + "nvim-dap": { "branch": "master", "commit": "a9d8cb68ee7184111dc66156c4a2ebabfbe01bc5" }, "nvim-dap-go": { "branch": "main", "commit": "b4421153ead5d726603b02743ea40cf26a51ed5f" }, "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, "nvim-dev-container": { "branch": "main", "commit": "87ea57f420b3460d0c45e239057ffc38fa32f886" }, "nvim-lightbulb": { "branch": "master", "commit": "aa3a8b0f4305b25cfe368f6c9be9923a7c9d0805" }, "nvim-lint": { "branch": "master", "commit": "606b823a57b027502a9ae00978ebf4f5d5158098" }, - "nvim-lspconfig": { "branch": "master", "commit": "ead0f5f342d8d323441e7d4b88f0fc436a81ad5f" }, + "nvim-lspconfig": { "branch": "master", "commit": "3addf05a791ea256c36438220aced91c03071712" }, "nvim-lspimport": { "branch": "main", "commit": "9c1c61a5020faeb1863bb66eb4b2a9107e641876" }, "nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" }, "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, - "nvim-treesitter-context": { "branch": "master", "commit": "64dd4cf3f6fd0ab17622c5ce15c91fc539c3f24a" }, - "nvim-treesitter-textobjects": { "branch": "main", "commit": "0bc4ef0a34d80fd6e67b59bd71fcbb0ef9ef4756" }, + "nvim-treesitter-context": { "branch": "master", "commit": "529ee357b8c03d76ff71233afed68fd0f5fe10b1" }, + "nvim-treesitter-textobjects": { "branch": "main", "commit": "4e91b5d0394329a229725b021a8ea217099826ef" }, "nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" }, "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, + "nvim-web-devicons": { "branch": "master", "commit": "d7462543c9e366c0d196c7f67a945eaaf5d99414" }, "obsidian.nvim": { "branch": "main", "commit": "14e0427bef6c55da0d63f9a313fd9941be3a2479" }, + "oil.nvim": { "branch": "master", "commit": "0fcc83805ad11cf714a949c98c605ed717e0b83e" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, "rainbow-delimiters.nvim": { "branch": "master", "commit": "01993eb20c6cdc1d33e7e98252368840309f99b9" }, - "render-markdown.nvim": { "branch": "main", "commit": "bd482f9a4827c9422231a7db1439c5cff1e69ae0" }, + "render-markdown.nvim": { "branch": "main", "commit": "e3c18ddd27a853f85a6f513a864cf4f2982b9f26" }, "sidekick.nvim": { "branch": "main", "commit": "c2bdf8cfcd87a6be5f8b84322c1b5052e78e302e" }, - "snacks.nvim": { "branch": "main", "commit": "fe7cfe9800a182274d0f868a74b7263b8c0c020b" }, "sonarqube.nvim": { "branch": "master", "commit": "811bf8f46e0aa5ed9c9b5783aaf6f87640df553a" }, "symbol-usage.nvim": { "branch": "main", "commit": "e07c07dfe7504295a369281e95a24e1afa14b243" }, "symbols-outline.nvim": { "branch": "master", "commit": "564ee65dfc9024bdde73a6621820866987cbb256" }, @@ -91,26 +89,22 @@ "toggleterm.nvim": { "branch": "main", "commit": "9a88eae817ef395952e08650b3283726786fb5fb" }, "treesj": { "branch": "main", "commit": "186084dee5e9c8eec40f6e39481c723dd567cb05" }, "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, - "undotree": { "branch": "master", "commit": "d8f99084d98c32f651860eb0baaf89759f91debc" }, + "undotree": { "branch": "master", "commit": "6fa6b57cda8459e1e4b2ca34df702f55242f4e4d" }, "vim-apathy": { "branch": "master", "commit": "27128a0f55189724c841843ba41cd33cf7186032" }, "vim-dadbod": { "branch": "master", "commit": "6d1d41da4873a445c5605f2005ad2c68c99d8770" }, "vim-dadbod-completion": { "branch": "master", "commit": "a8dac0b3cf6132c80dc9b18bef36d4cf7a9e1fe6" }, "vim-dadbod-ui": { "branch": "master", "commit": "07e92e22114cc5b1ba4938d99897d85b58e20475" }, "vim-dirdiff": { "branch": "master", "commit": "84bc8999fde4b3c2d8b228b560278ab30c7ea4c9" }, - "vim-fugitive": { "branch": "master", "commit": "61b51c09b7c9ce04e821f6cf76ea4f6f903e3cf4" }, + "vim-fugitive": { "branch": "master", "commit": "3b753cf8c6a4dcde6edee8827d464ba9b8c4a6f0" }, "vim-kitty": { "branch": "main", "commit": "cd72f2d9cfee8d6aba5a180a5ac3ca265b5d3a46" }, "vim-ledger": { "branch": "master", "commit": "4f2d93dd914f625e2d3db47d97bd9f428fedd68f" }, "vim-log-highlighting": { "branch": "master", "commit": "1037e26f3120e6a6a2c0c33b14a84336dee2a78f" }, "vim-markdown": { "branch": "master", "commit": "1bc9d0cd8e1cc3e901b0a49c2b50a843f1c89397" }, - "vim-maximizer": { "branch": "master", "commit": "2e54952fe91e140a2e69f35f22131219fcd9c5f1" }, "vim-repeat": { "branch": "master", "commit": "65846025c15494983dafe5e3b46c8f88ab2e9635" }, "vim-rzip": { "branch": "master", "commit": "f65400fed27b27c7cff7ef8d428c4e5ff749bf28" }, - "vim-skeleton": { "branch": "master", "commit": "aba9eb0f752986a98063908877289a50a7063d0e" }, "vim-sleuth": { "branch": "master", "commit": "be69bff86754b1aa5adcbb527d7fcd1635a84080" }, "vim-snakemake": { "branch": "master", "commit": "d0015a0f86150c59fb55f65b41529e6ff4469e18" }, "vim-surround": { "branch": "master", "commit": "3d188ed2113431cf8dac77be61b842acb64433d9" }, - "vim-vinegar": { "branch": "master", "commit": "bb1bcddf43cfebe05eb565a84ab069b357d0b3d6" }, "vimtex": { "branch": "master", "commit": "82d2305ff71dfb3bd91602534cc9bb9a195bcb38" }, - "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }, - "zen-mode.nvim": { "branch": "main", "commit": "8564ce6d29ec7554eb9df578efa882d33b3c23a7" } + "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } } diff --git a/files/.config/nvim/lua/custom/plugins/debug.lua b/files/.config/nvim/lua/custom/plugins/debug.lua index b67e63d5..0dfdcdcf 100644 --- a/files/.config/nvim/lua/custom/plugins/debug.lua +++ b/files/.config/nvim/lua/custom/plugins/debug.lua @@ -27,6 +27,11 @@ return { function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint', }, + { + '', + function() require('dapui').toggle() end, + desc = 'Debug: See last session result.', + }, }, dependencies = { 'rcarriga/nvim-dap-ui', -- Creates a beautiful debugger UI @@ -57,23 +62,6 @@ return { }, }) - -- Basic debugging keymaps, feel free to change to your liking! - vim.keymap.set( - 'n', - '', - dap.continue, - { desc = 'Debug: Start/Continue' } - ) - vim.keymap.set('n', '', dap.step_into, { desc = 'Debug: Step Into' }) - vim.keymap.set('n', '', dap.step_over, { desc = 'Debug: Step Over' }) - vim.keymap.set('n', '', dap.step_out, { desc = 'Debug: Step Out' }) - vim.keymap.set( - 'n', - 'B', - dap.toggle_breakpoint, - { desc = 'Debug: Toggle Breakpoint' } - ) - -- Dap UI setup -- For more information, see |:help nvim-dap-ui| dapui.setup({ @@ -96,14 +84,6 @@ return { }, }) - -- Toggle to see last session result. Without this, you can't see session output in case of unhandled exception. - vim.keymap.set( - 'n', - '', - dapui.toggle, - { desc = 'Debug: See last session result.' } - ) - dap.listeners.after.event_initialized['dapui_config'] = dapui.open dap.listeners.before.event_terminated['dapui_config'] = dapui.close dap.listeners.before.event_exited['dapui_config'] = dapui.close diff --git a/files/.config/nvim/lua/custom/plugins/folke.lua b/files/.config/nvim/lua/custom/plugins/folke.lua index 452ef5aa..97aeaddb 100644 --- a/files/.config/nvim/lua/custom/plugins/folke.lua +++ b/files/.config/nvim/lua/custom/plugins/folke.lua @@ -1,110 +1,2 @@ return { - { - 'folke/snacks.nvim', - event = { 'BufReadPre', 'BufNewFile' }, - opts = { - -- your configuration comes here - -- or leave it empty to use the default settings - -- refer to the configuration section below - bigfile = { enabled = true }, - dashboard = { enabled = false }, - explorer = { enabled = false }, - indent = { enabled = false }, - input = { enabled = false }, - picker = { enabled = false }, - notifier = { enabled = false }, - quickfile = { enabled = false }, - scope = { enabled = false }, - scroll = { enabled = false }, - statuscolumn = { enabled = false }, - words = { enabled = true }, - }, - enabled = false, - cond = false, - }, - { - 'folke/zen-mode.nvim', - enabled = false, - cond = false, - opts = { - window = { - backdrop = 0.95, -- shade the backdrop of the Zen window. Set to 1 to keep the same as Normal - -- height and width can be: - -- * an absolute number of cells when > 1 - -- * a percentage of the width / height of the editor when <= 1 - -- * a function that returns the width or the height - width = 120, -- width of the Zen window - height = 1, -- height of the Zen window - -- by default, no options are changed for the Zen window - -- uncomment any of the options below, or add other vim.wo options you want to apply - options = { - -- signcolumn = "no", -- disable signcolumn - -- number = false, -- disable number column - -- relativenumber = false, -- disable relative numbers - -- cursorline = false, -- disable cursorline - -- cursorcolumn = false, -- disable cursor column - -- foldcolumn = "0", -- disable fold column - -- list = false, -- disable whitespace characters - }, - }, - plugins = { - -- disable some global vim options (vim.o...) - -- comment the lines to not apply the options - options = { - enabled = true, - ruler = false, -- disables the ruler text in the cmd line area - showcmd = false, -- disables the command in the last line of the screen - -- you may turn on/off statusline in zen mode by setting 'laststatus' - -- statusline will be shown only if 'laststatus' == 3 - laststatus = 0, -- turn off the statusline in zen mode - }, - twilight = { enabled = true }, -- enable to start Twilight when zen mode opens - gitsigns = { enabled = false }, -- disables git signs - tmux = { enabled = false }, -- disables the tmux statusline - todo = { enabled = false }, -- if set to "true", todo-comments.nvim highlights will be disabled - -- this will change the font size on kitty when in zen mode - -- to make this work, you need to set the following kitty options: - -- - allow_remote_control socket-only - -- - listen_on unix:/tmp/kitty - kitty = { - enabled = false, - font = '+4', -- font size increment - }, - -- this will change the font size on alacritty when in zen mode - -- requires Alacritty Version 0.10.0 or higher - -- uses `alacritty msg` subcommand to change font size - alacritty = { - enabled = false, - font = '14', -- font size - }, - -- this will change the font size on wezterm when in zen mode - -- See alse also the Plugins/Wezterm section in this projects README - wezterm = { - enabled = false, - -- can be either an absolute font size or the number of incremental steps - font = '+4', -- (10% increase per step) - }, - -- this will change the scale factor in Neovide when in zen mode - -- See alse also the Plugins/Wezterm section in this projects README - neovide = { - enabled = false, - -- Will multiply the current scale factor by this number - scale = 1.2, - -- disable the Neovide animations while in Zen mode - disable_animations = { - neovide_animation_length = 0, - neovide_cursor_animate_command_line = false, - neovide_scroll_animation_length = 0, - neovide_position_animation_length = 0, - neovide_cursor_animation_length = 0, - neovide_cursor_vfx_mode = '', - }, - }, - }, - -- callback where you can add custom code when the Zen window opens - on_open = function(win) end, - -- callback where you can add custom code when the Zen window closes - on_close = function() end, - }, - }, } diff --git a/files/.config/nvim/lua/custom/plugins/init.lua b/files/.config/nvim/lua/custom/plugins/init.lua index 356d7824..40b13124 100644 --- a/files/.config/nvim/lua/custom/plugins/init.lua +++ b/files/.config/nvim/lua/custom/plugins/init.lua @@ -43,52 +43,12 @@ return { }, }, - { - 'noahfrederick/vim-skeleton', - event = 'BufNewFile', - config = function() - vim.g.skeleton_template_dir = vim.fn.expand('~/.config/nvim') - .. '/templates' - vim.cmd([[ - let g:skeleton_replacements = {} - function! g:skeleton_replacements.TITLE() - return toupper(expand("%:t:r")) - endfunction - ]]) - end, - }, - - { - 'rlch/github-notifications.nvim', - dependencies = { 'nvim-lua/plenary.nvim', 'ibhagwan/fzf-lua' }, - keys = { - { - 'gn', - function() require('custom.gh_notifications').open() end, - desc = 'github notifications (fzf)', - }, - }, - }, - - { - 'szw/vim-maximizer', - keys = { - { - 'sm', - 'MaximizerToggle', - desc = '[win] Split Maximize/minimize', - }, - }, - }, - { 'marromlam/sailor.vim', event = 'VimEnter', run = './install.sh', }, - { 'meznaric/key-analyzer.nvim', opts = {}, cmd = 'KeyAnalyzer' }, - { 'bogado/file-line', keys = { @@ -101,6 +61,14 @@ return { cmd = { 'DirDiff' }, }, - --- }}} - ------------------------------------------------------------------------ + { + 'tpope/vim-sleuth', + event = { 'BufReadPre', 'BufNewFile' }, + }, + { 'tpope/vim-surround', event = { 'BufReadPre', 'BufNewFile' } }, + + { + 'tpope/vim-repeat', + keys = { '.' }, + }, } diff --git a/files/.config/nvim/lua/custom/plugins/linting.lua b/files/.config/nvim/lua/custom/plugins/linting.lua index 50314d8f..899c1633 100644 --- a/files/.config/nvim/lua/custom/plugins/linting.lua +++ b/files/.config/nvim/lua/custom/plugins/linting.lua @@ -1,6 +1,6 @@ return { 'mfussenegger/nvim-lint', - events = { 'BufWritePost', 'BufReadPost', 'InsertLeave', 'LspAttach' }, + event = { 'BufWritePost', 'BufReadPost', 'InsertLeave', 'LspAttach' }, keys = { { 'll', diff --git a/files/.config/nvim/lua/custom/plugins/mini.lua b/files/.config/nvim/lua/custom/plugins/mini.lua index d976c5e0..ba945b59 100644 --- a/files/.config/nvim/lua/custom/plugins/mini.lua +++ b/files/.config/nvim/lua/custom/plugins/mini.lua @@ -197,37 +197,6 @@ return { -- Collection of various small independent plugins/modules }, -- Visual enhancements - { - 'echasnovski/mini.hipatterns', - event = { 'BufReadPre', 'BufNewFile' }, - config = function() - local hipatterns = require('mini.hipatterns') - hipatterns.setup({ - highlighters = { - -- Highlight standalone 'FIXME', 'HACK', 'TODO', 'NOTE' - fixme = { - pattern = '%f[%w]()FIXME()%f[%W]', - group = 'MiniHipatternsFixme', - }, - hack = { - pattern = '%f[%w]()HACK()%f[%W]', - group = 'MiniHipatternsHack', - }, - todo = { - pattern = '%f[%w]()TODO()%f[%W]', - group = 'MiniHipatternsTodo', - }, - note = { - pattern = '%f[%w]()NOTE()%f[%W]', - group = 'MiniHipatternsNote', - }, - - -- Highlight hex color strings (`#rrggbb`) using that color - hex_color = hipatterns.gen_highlighter.hex_color(), - }, - }) - end, - }, { 'echasnovski/mini.indentscope', enabled = false, diff --git a/files/.config/nvim/lua/custom/plugins/navigation.lua b/files/.config/nvim/lua/custom/plugins/navigation.lua index 3e825c34..c1c68067 100644 --- a/files/.config/nvim/lua/custom/plugins/navigation.lua +++ b/files/.config/nvim/lua/custom/plugins/navigation.lua @@ -1,124 +1,41 @@ return { { - 'tpope/vim-vinegar', - keys = { '-' }, + 'stevearc/oil.nvim', + lazy = true, + opts = { + default_file_explorer = true, + delete_to_trash = true, + skip_confirm_for_simple_edits = true, + view_options = { + show_hidden = true, + }, + keymaps = { + [''] = false, -- don't shadow split navigation + [''] = false, + }, + }, + keys = { + { '-', 'Oil', desc = 'oil: open parent directory' }, + }, }, { - 'ThePrimeagen/harpoon', - branch = 'harpoon2', - dependencies = { 'nvim-lua/plenary.nvim' }, - -- dependencies = { 'nvim-lua/plenary.nvim', 'nvim-telescope/telescope.nvim' }, + 'cbochs/grapple.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons' }, + opts = { + scope = 'git', + }, keys = { - - { - '1', - function() require('harpoon'):list():select(1) end, - 'Go to buffer 1', - }, - { - '2', - 'BufferLineGoToBuffer 2', - 'Go to buffer 2', - }, - { - '3', - 'BufferLineGoToBuffer 3', - 'Go to buffer 3', - }, - { - '4', - 'BufferLineGoToBuffer 4', - 'Go to buffer 4', - }, - { - '5', - 'BufferLineGoToBuffer 5', - 'Go to buffer 5', - }, - { - '6', - 'BufferLineGoToBuffer 6', - 'Go to buffer 6', - }, - { - '7', - 'BufferLineGoToBuffer 7', - 'Go to buffer 7', - }, - { - '8', - 'BufferLineGoToBuffer 8', - 'Go to buffer 8', - }, - { - '9', - 'BufferLineGoToBuffer 9', - 'Go to buffer 9', - }, - { - '0', - 'BufferLineGoToBuffer 10', - 'Go to buffer 10', - }, + { 'm', 'Grapple tag', desc = 'grapple: tag file' }, + { '', 'Grapple toggle_tags', desc = 'grapple: open tags' }, + { '1', 'Grapple select index=1', desc = 'grapple: select 1' }, + { '2', 'Grapple select index=2', desc = 'grapple: select 2' }, + { '3', 'Grapple select index=3', desc = 'grapple: select 3' }, + { '4', 'Grapple select index=4', desc = 'grapple: select 4' }, + { '5', 'Grapple select index=5', desc = 'grapple: select 5' }, + { '[g', 'Grapple cycle_tags prev', desc = 'grapple: prev tag' }, + { ']g', 'Grapple cycle_tags next', desc = 'grapple: next tag' }, }, - config = function() - local harpoon = require('harpoon') - harpoon:setup({}) - - -- basic telescope configuration - local function toggle_telescope(harpoon_files) - local file_paths = {} - for _, item in ipairs(harpoon_files.items) do - table.insert(file_paths, item.value) - end - - -- require('telescope.pickers') - -- .new({}, { - -- prompt_title = 'Harpoon', - -- finder = require('telescope.finders').new_table({ - -- results = file_paths, - -- }), - -- previewer = conf.file_previewer({}), - -- sorter = conf.generic_sorter({}), - -- }) - -- :find() - print(vim.inspect(file_paths)) - require('fzf-lua').buffers(file_paths, { - winopts = { - title = 'Harpoon', - height = 0.33, - row = 0.5, - }, - previewer = false, - actions = { - ['default'] = function(selected) - local session = vim.iter(file_paths):find( - function(s) return s.name == selected[1] end - ) - if not session then return end - -- persisted.load({ session = session.file_path }) - end, - ['ctrl-d'] = { - function(selected) - local session = vim.iter(file_paths):find( - function(s) return s.name == selected[1] end - ) - if not session then return end - vim.fn.delete(vim.fn.expand(session.file_path)) - end, - }, - }, - }) - end - - vim.keymap.set( - 'n', - '', - function() toggle_telescope(harpoon:list()) end, - { desc = 'Open harpoon window' } - ) - end, }, } From dbf677171c2a31e1a10cb0b48bde6db445554893 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 13 Mar 2026 23:41:35 +0100 Subject: [PATCH 18/27] feat: some updates --- .../nvim/lua/custom/plugins/whichkey.lua | 33 +++++++++++- files/.config/nvim/lua/custom/strings.lua | 15 +++--- files/.config/nvim/lua/external_grep.lua | 4 +- files/.config/nvim/lua/lazyloader.lua | 17 +------ files/.config/nvim/lua/options.lua | 8 +-- files/.config/nvim/lua/tools.lua | 51 ++++++++++--------- files/.config/nvim/plugin/root.lua | 19 ++----- 7 files changed, 75 insertions(+), 72 deletions(-) diff --git a/files/.config/nvim/lua/custom/plugins/whichkey.lua b/files/.config/nvim/lua/custom/plugins/whichkey.lua index c4a734c8..bd59258b 100644 --- a/files/.config/nvim/lua/custom/plugins/whichkey.lua +++ b/files/.config/nvim/lua/custom/plugins/whichkey.lua @@ -5,9 +5,40 @@ return { config = function() local wk = require('which-key') wk.setup({ - win = { border = false }, + win = { + border = false, + wo = { + winhl = 'Normal:StatusLine,NormalFloat:StatusLine,FloatBorder:StatusLine', + }, + }, layout = { align = 'center' }, -- preset = 'modern', }) + + -- Sync all WhichKey highlight bg to StatusLine + local function sync_hls() + local bg = + vim.api.nvim_get_hl(0, { name = 'StatusLine', link = false }).bg + if not bg then return end + bg = ('#%06x'):format(bg) + for _, name in ipairs({ + 'WhichKeyNormal', + 'WhichKey', + 'WhichKeyDesc', + 'WhichKeyGroup', + 'WhichKeySeparator', + 'WhichKeyValue', + 'WhichKeyBorder', + 'WhichKeyTitle', + }) do + local hl = vim.api.nvim_get_hl(0, { name = name, link = false }) + hl.bg = bg + hl.ctermbg = nil + vim.api.nvim_set_hl(0, name, hl) + end + end + + sync_hls() + vim.api.nvim_create_autocmd('ColorScheme', { callback = sync_hls }) end, } diff --git a/files/.config/nvim/lua/custom/strings.lua b/files/.config/nvim/lua/custom/strings.lua index 70bc2537..00c12f23 100644 --- a/files/.config/nvim/lua/custom/strings.lua +++ b/files/.config/nvim/lua/custom/strings.lua @@ -1,7 +1,8 @@ -- FORMAT STRINGS +local T = require('tools') local api, L = vim.api, vim.log.levels -local fmt, falsy = string.format, mrl.falsy +local fmt, falsy = string.format, T.falsy ---@alias StringComponent {component: string, length: integer, priority: integer} @@ -94,8 +95,8 @@ local function chunks_to_string(chunks) if type(text) ~= 'string' then text = tostring(text) end if item.max_size then text = truncate_string(text, item.max_size) end text = text:gsub('%%', '%%%1') - strings[#strings + 1] = - not falsy(hl) and ('%%#%s#%s%%*'):format(hl, text) or text + strings[#strings + 1] = not falsy(hl) and ('%%#%s#%s%%*'):format(hl, text) + or text end end return table.concat(strings, '') @@ -154,7 +155,7 @@ end ------------------------------------------------------------------------------- local function sum_lengths(list) - return mrl.fold( + return T.fold( function(acc, item) return acc + (item.length or 0) end, list, 0 @@ -200,14 +201,14 @@ end --- @param available_space number? --- @return string function M.display(sections, available_space) - local components = mrl.fold(function(acc, section, count) + local components = T.fold(function(acc, section, count) if #section == 0 then table.insert(acc, separator()) return acc end - mrl.foreach(function(args, index) + T.foreach(function(args, index) if not args then return end - local ok, str = mrl.pcall('Error creating component', component, args) + local ok, str = T.pcall('Error creating component', component, args) if not ok then return end table.insert(acc, str) if #section == index and count ~= #sections then diff --git a/files/.config/nvim/lua/external_grep.lua b/files/.config/nvim/lua/external_grep.lua index b1f6b920..d65c29ba 100644 --- a/files/.config/nvim/lua/external_grep.lua +++ b/files/.config/nvim/lua/external_grep.lua @@ -18,7 +18,7 @@ augroup quickfix augroup END ]]) -function mrl.external_grep(word, no_ignore) +local function external_grep(word, no_ignore) local word0 = (word or vim.fn.input('RG  ')) if no_ignore then vim.cmd((('silent grep ' .. word0) .. ' --no-ignore')) @@ -31,7 +31,7 @@ end vim.keymap.set( 'n', 'fS', - mrl.external_grep, + external_grep, { desc = 'Ripgrep to QuickFix' } ) diff --git a/files/.config/nvim/lua/lazyloader.lua b/files/.config/nvim/lua/lazyloader.lua index f80ebcf3..1efb1b38 100644 --- a/files/.config/nvim/lua/lazyloader.lua +++ b/files/.config/nvim/lua/lazyloader.lua @@ -15,7 +15,7 @@ end ---@diagnostic disable-next-line: undefined-field vim.opt.rtp:prepend(lazypath) -- Ensure icons are available, use defaults if not -local icons = mrl.ui and mrl.ui.icons or nil +local icons = require('custom.ui').icons require('lazy').setup({ { import = 'custom.plugins' }, @@ -90,18 +90,3 @@ require('lazy').setup({ }, }, }) - --- Disable columns in Lazy.nvim windows -vim.api.nvim_create_autocmd('FileType', { - pattern = 'lazy', - callback = function() - vim.schedule(function() - local win = vim.api.nvim_get_current_win() - vim.wo[win].statuscolumn = '' - vim.wo[win].signcolumn = 'no' - vim.wo[win].foldcolumn = '0' - vim.wo[win].number = false - vim.wo[win].relativenumber = false - end) - end, -}) diff --git a/files/.config/nvim/lua/options.lua b/files/.config/nvim/lua/options.lua index becb4db9..5675a808 100644 --- a/files/.config/nvim/lua/options.lua +++ b/files/.config/nvim/lua/options.lua @@ -29,7 +29,7 @@ vim.o.background = 'dark' -- or "light" -- }}} -- Timings {{{ -vim.opt.updatetime = 1000 -- Increased from 500 to reduce CursorHold frequency (improves scrolling perf) +vim.opt.updatetime = 1000 -- Increased from 500 to reduce CursorHold frequency (improves scrolling perf) vim.opt.timeout = true vim.opt.timeoutlen = 500 -- }}} @@ -121,11 +121,7 @@ vim.opt.breakindentopt = 'sbr' vim.opt.linebreak = true -- lines wrap at words rather than random characters -- If we render signs inside `statuscolumn`, disable the built-in signcolumn to -- avoid duplicated icons. -local statuscolumn_enabled = mrl - and mrl.ui - and mrl.ui.statuscolumn - and mrl.ui.statuscolumn.enable -vim.opt.signcolumn = statuscolumn_enabled and 'no' or 'yes' +vim.opt.signcolumn = 'no' -- statuscolumn.lua manages signs vim.opt.ruler = false vim.opt.cmdheight = 0 vim.opt.showbreak = [[↪ ]] -- Options include -> '…', '↳ ', '→','↪ ' diff --git a/files/.config/nvim/lua/tools.lua b/files/.config/nvim/lua/tools.lua index 3dc13d25..168f3919 100644 --- a/files/.config/nvim/lua/tools.lua +++ b/files/.config/nvim/lua/tools.lua @@ -1,9 +1,10 @@ -local fn, api, v, fmt = - vim.fn, vim.api, vim.v, string.format +local fn, api, fmt = vim.fn, vim.api, string.format + +local M = {} -- colors {{{ -function mrl.get_hi(name, id) +function M.get_hi(name, id) id = id or 0 local hi = vim.api.nvim_get_hl(0, { name = name }) -- hi is a table with bg and fg keys. for those we want to return the hex @@ -18,7 +19,7 @@ end -------------------------------------------------------------------------------- -- Commands {{{ -function mrl.command(name, rhs, opts) +function M.command(name, rhs, opts) opts = opts or {} api.nvim_create_user_command(name, rhs, opts) end @@ -31,7 +32,7 @@ end ---@param ... any ---@return boolean, any ---@overload fun(func: function, ...): boolean, any -function mrl.pcall(msg, func, ...) +function M.pcall(msg, func, ...) local args = { ... } if type(msg) == 'function' then local arg = func --[[@as any]] @@ -48,7 +49,7 @@ function mrl.pcall(msg, func, ...) end local LATEST_NIGHTLY_MINOR = 10 -function mrl.nightly() return vim.version().minor >= LATEST_NIGHTLY_MINOR end +function M.nightly() return vim.version().minor >= LATEST_NIGHTLY_MINOR end -- }}} @@ -58,7 +59,7 @@ function mrl.nightly() return vim.version().minor >= LATEST_NIGHTLY_MINOR end ---Determine if a value of any type is empty ---@param item any ---@return boolean? -function mrl.falsy(item) +function M.falsy(item) if not item then return true end local item_type = type(item) if item_type == 'boolean' then return not item end @@ -71,7 +72,7 @@ end ---@generic T:table ---@param callback fun(item: T, key: any) ---@param list table -function mrl.foreach(callback, list) +function M.foreach(callback, list) for k, v in pairs(list) do callback(v, k) end @@ -81,7 +82,7 @@ end ---@param target string ---@param list string[] ---@return boolean -function mrl.any(target, list) +function M.any(target, list) for _, item in ipairs(list) do if target:match(item) then return true end end @@ -93,7 +94,7 @@ end ---@param matcher fun(arg: T):boolean ---@param haystack T[] ---@return T? -function mrl.find(matcher, haystack) +function M.find(matcher, haystack) for _, needle in ipairs(haystack) do if matcher(needle) then return needle end end @@ -104,7 +105,7 @@ end ---all the table's keys for a match using `string.match` ---@param map T ---@return T -function mrl.p_table(map) +function M.p_table(map) return setmetatable(map, { __index = function(tbl, key) if not key then return end @@ -118,13 +119,13 @@ end ---check if a certain feature/version/commit exists in nvim ---@param feature string ---@return boolean -function mrl.has(feature) return fn.has(feature) > 0 end +function M.has(feature) return fn.has(feature) > 0 end -- }}} -------------------------------------------------------------------------------- -- Functional utilities {{{ -function mrl.fold(callback, list, accum) +function M.fold(callback, list, accum) accum = accum or {} for k, v in pairs(list) do accum = callback(accum, v, k) @@ -137,8 +138,8 @@ end ---@param callback fun(item: T, key: string | number, list: T[]): T ---@param list T[] ---@return T[] -function mrl.map(callback, list) - return mrl.fold(function(accum, v, k) +function M.map(callback, list) + return M.fold(function(accum, v, k) accum[#accum + 1] = callback(v, k, accum) return accum end, list, {}) @@ -150,11 +151,11 @@ end local autocmd_keys = { 'event', 'buffer', 'pattern', 'desc', 'command', 'group', 'once', 'nested' } ---- Validate the keys passed to mrl.augroup are valid +--- Validate the keys passed to M.augroup are valid ---@param name string ---@param command Autocommand local function validate_autocmd(name, command) - local incorrect = mrl.fold(function(accum, _, key) + local incorrect = M.fold(function(accum, _, key) if not vim.tbl_contains(autocmd_keys, key) then table.insert(accum, key) end return accum end, command, {}) @@ -172,7 +173,7 @@ end ---@param name string The name of the autocommand group ---@param ... Autocommand A list of autocommands to create ---@return number -function mrl.augroup(name, ...) +function M.augroup(name, ...) local commands = { ... } assert(name ~= 'User', 'The name of an augroup CANNOT be User') assert( @@ -211,7 +212,7 @@ end --- --- Will only require the module after the first index of a module. --- Only works for modules that export a table. -function mrl.require_for_later_index(require_path) +function M.require_for_later_index(require_path) return setmetatable({}, { __index = function(_, key) return require(require_path)[key] end, __newindex = function(_, key, value) require(require_path)[key] = value end, @@ -235,7 +236,7 @@ end --- ``` ---@param require_path string ---@return table -function mrl.require_for_later_call(require_path) +function M.require_for_later_call(require_path) return setmetatable({}, { __index = function(_, k) return function(...) return require(require_path)[k](...) end @@ -251,7 +252,7 @@ end --- https://vim.fandom.com/wiki/Automatically_fitting_a_quickfix_window_height ---@param min_height number ---@param max_height number -function mrl.adjust_split_height(min_height, max_height) +function M.adjust_split_height(min_height, max_height) api.nvim_win_set_height( 0, math.max(math.min(fn.line('$'), max_height), min_height) @@ -266,10 +267,12 @@ end ---@param str string ---@param max_len integer ---@return string -function mrl.truncate(str, max_len) +function M.truncate(str, max_len) assert(str and max_len, 'string and max_len must be provided') - return api.nvim_strwidth(str) > max_len - and str:sub(1, max_len) .. mrl.ui.icons.misc.ellipsis + local ellipsis = require('custom.ui').icons.misc.ellipsis + return api.nvim_strwidth(str) > max_len and str:sub(1, max_len) .. ellipsis or str end -- }}} + +return M diff --git a/files/.config/nvim/plugin/root.lua b/files/.config/nvim/plugin/root.lua index 042c2e78..7f5db439 100644 --- a/files/.config/nvim/plugin/root.lua +++ b/files/.config/nvim/plugin/root.lua @@ -1,18 +1,5 @@ -if not mrl then return end - --- Helper to safely call augroup, deferring if not available yet -local function augroup(name, ...) - local args = { ... } - if mrl and mrl.augroup then - return mrl.augroup(name, unpack(args)) - else - vim.schedule(function() - if mrl and mrl.augroup then mrl.augroup(name, unpack(args)) end - end) - end -end - -local fn, fs, api = vim.fn, vim.fs, vim.api +local augroup = require('tools').augroup +local fn, fs = vim.fn, vim.fs ------------------------------------------------------------------------------- -- Project root finder @@ -50,7 +37,7 @@ local function get_lsp_root(buf) end local function set_root_directory(args) - local path = api.nvim_buf_get_name(args.buf) + local path = vim.api.nvim_buf_get_name(args.buf) if path == '' then return end path = fs.dirname(path) From f30d407497b08d00a2229c65aca1b5d4c431c477 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Sat, 14 Mar 2026 00:08:19 +0100 Subject: [PATCH 19/27] feat(nvim): Auto-install all LSP tools via Mason Add missing formatters (shfmt, prettier, prettierd, isort) and linters (eslint_d, hadolint, jsonlint, vale, tflint) to mason-tool-installer's ensure_installed list so they are automatically installed on fresh machines. Co-Authored-By: Claude --- files/.config/nvim/lua/custom/plugins/lsp.lua | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/files/.config/nvim/lua/custom/plugins/lsp.lua b/files/.config/nvim/lua/custom/plugins/lsp.lua index 83a30a58..10b896ce 100644 --- a/files/.config/nvim/lua/custom/plugins/lsp.lua +++ b/files/.config/nvim/lua/custom/plugins/lsp.lua @@ -1,4 +1,4 @@ -local icons = mrl.ui.icons.lsp +local icons = require('custom.ui').icons.lsp return { -- LSP Configuration & Plugins { @@ -18,20 +18,6 @@ return { -- LSP Configuration & Plugins }, config = function(_, opts) require('mason').setup(opts) - -- Disable columns in Mason windows after setup - vim.api.nvim_create_autocmd('FileType', { - pattern = 'mason', - callback = function() - vim.schedule(function() - local win = vim.api.nvim_get_current_win() - vim.wo[win].statuscolumn = '' - vim.wo[win].signcolumn = 'no' - vim.wo[win].foldcolumn = '0' - vim.wo[win].number = false - vim.wo[win].relativenumber = false - end) - end, - }) end, }, { 'mason-org/mason-lspconfig.nvim', opts = {} }, @@ -40,7 +26,20 @@ return { -- LSP Configuration & Plugins -- Useful status updates for LSP. -- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})` { 'j-hui/fidget.nvim', opts = {} }, - { 'folke/lazydev.nvim' }, + { + 'folke/lazydev.nvim', + ft = 'lua', + opts = { + library = { + { path = '${3rd}/luv/library', words = { 'vim%.uv' } }, + { path = 'wezterm-types', mods = { 'wezterm' } }, + }, + enabled = function(root_dir) + return (vim.g.lazydev_enabled == nil or vim.g.lazydev_enabled) + and not vim.uv.fs_stat(root_dir .. '/.luarc.json') + end, + }, + }, { 'stevanmilic/nvim-lspimport', }, @@ -311,10 +310,25 @@ return { -- LSP Configuration & Plugins -- for you, so that they are available from within Neovim. local ensure_installed = vim.tbl_keys(servers or {}) vim.list_extend(ensure_installed, { - 'stylua', -- Used to format Lua code + -- Lua + 'stylua', + -- Python + 'black', + 'isort', 'flake8', 'mypy', - 'black', + -- Shell + 'shfmt', + -- JS/TS + 'prettier', + 'prettierd', + 'eslint_d', + -- Misc linters + 'hadolint', + 'jsonlint', + 'vale', + 'tflint', + -- Other 'sonarlint-language-server', }) require('mason-tool-installer').setup({ From 0e3f02619c50f4fe4b0784afe1bbf169340db751 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Mon, 16 Mar 2026 23:43:34 +0100 Subject: [PATCH 20/27] feat: toggle tree-sitter context --- .../nvim/lua/custom/plugins/treesitter.lua | 16 +- files/.config/nvim/lua/custom/plugins/ui.lua | 11 +- files/.config/nvim/lua/custom/strings.lua | 259 ---------- files/.config/nvim/lua/custom/ui.lua | 478 ------------------ files/.config/nvim/lua/highlight.lua | 283 +---------- files/.config/nvim/lua/keymaps.lua | 70 +-- files/.config/nvim/lua/lazyloader.lua | 2 +- files/.config/nvim/plugin/lastplace.lua | 14 +- files/.config/nvim/plugin/lsp.lua | 15 +- files/.config/nvim/plugin/statuscolumn.lua | 41 +- files/.config/wezterm/wezterm.lua | 57 +-- files/.config/zsh/rc.d/40-fzf.zsh | 7 +- 12 files changed, 71 insertions(+), 1182 deletions(-) delete mode 100644 files/.config/nvim/lua/custom/strings.lua delete mode 100644 files/.config/nvim/lua/custom/ui.lua diff --git a/files/.config/nvim/lua/custom/plugins/treesitter.lua b/files/.config/nvim/lua/custom/plugins/treesitter.lua index 401c0474..6d3563b8 100644 --- a/files/.config/nvim/lua/custom/plugins/treesitter.lua +++ b/files/.config/nvim/lua/custom/plugins/treesitter.lua @@ -1,3 +1,4 @@ +-- DONE return { { 'nvim-treesitter/nvim-treesitter', @@ -18,8 +19,7 @@ return { }, auto_install = true, highlight = { - -- enable = true, - enable = false, + enable = true, disable = { 'tex', 'latex', 'applescript' }, -- Some languages depend on vim's regex highlighting system (such as Ruby) for indent rules. -- If you are experiencing weird indenting issues, add the language to @@ -142,7 +142,7 @@ return { 'nvim-treesitter/nvim-treesitter-context', event = { 'BufReadPost', 'BufNewFile' }, init = function() - local highlight = mrl.highlight + local highlight = require('highlight') highlight.plugin('treesitter-context', { { TreesitterContextSeparator = { link = 'Dim' } }, { TreesitterContext = { inherit = 'Normal' } }, @@ -150,9 +150,17 @@ return { }) end, opts = { - multiline_threshold = 4, + enable = false, + multiline_threshold = 10, separator = '─', mode = 'cursor', }, + keys = { + { + 'sc', + 'TSContext toggle', + desc = 'Toggle [s]cope [c]ontext', + }, + }, }, } diff --git a/files/.config/nvim/lua/custom/plugins/ui.lua b/files/.config/nvim/lua/custom/plugins/ui.lua index b59a5ce8..00a37684 100644 --- a/files/.config/nvim/lua/custom/plugins/ui.lua +++ b/files/.config/nvim/lua/custom/plugins/ui.lua @@ -1,4 +1,5 @@ -local icons = mrl.ui.icons +local UI = require('tools').ui +local icons = UI.icons return { { 'lukas-reineke/indent-blankline.nvim', @@ -41,7 +42,7 @@ return { local incline = require('incline') local function set_hls() - local pal = mrl.ui.palette or {} + local pal = UI.palette or {} local bg = '#121212' vim.api.nvim_set_hl(0, 'InclineNormal', { fg = pal.whitesmoke or 'NONE', @@ -111,7 +112,7 @@ return { local buf = props.buf local ft = vim.bo[buf].ft local bt = vim.bo[buf].bt - local decor = mrl.ui.decorations.get({ + local decor = UI.decorations.get({ ft = ft, bt = bt, setting = 'winbar', @@ -167,7 +168,7 @@ return { local ccc = require('ccc') local p = ccc.picker ccc.setup({ - -- win_opts = { border = mrl.ui.border }, + -- win_opts = { border = UI.border }, highlighter = { auto_enable = false, excludes = { @@ -289,7 +290,7 @@ return { api.nvim_create_augroup('MrlRainbowDelimiters', { clear = true }) local function set_hls() - local pal = (mrl and mrl.ui and mrl.ui.palette) or {} + local pal = UI.palette or {} local function as_hex(v, fallback) if type(v) == 'string' or type(v) == 'number' then return v end if type(v) == 'table' then return v.base or fallback end diff --git a/files/.config/nvim/lua/custom/strings.lua b/files/.config/nvim/lua/custom/strings.lua deleted file mode 100644 index 00c12f23..00000000 --- a/files/.config/nvim/lua/custom/strings.lua +++ /dev/null @@ -1,259 +0,0 @@ --- FORMAT STRINGS - -local T = require('tools') -local api, L = vim.api, vim.log.levels -local fmt, falsy = string.format, T.falsy - ----@alias StringComponent {component: string, length: integer, priority: integer} - -local M = {} - -local CLICK_END = '%X' - --------------------------------------------------------------------------------- --- Components {{{ --------------------------------------------------------------------------------- - ----@return StringComponent -local function separator() return { component = '%=', length = 0, priority = 0 } end - ----@param func_name string ----@param id string ----@return string -local function get_click_start(func_name, id) - if not id then - vim.schedule(function() - local msg = - fmt('An ID is needed to enable click handler %s to work', func_name) - vim.notify_once(msg, L.ERROR, { title = 'Statusline' }) - end) - return '' - end - return ('%%%d@%s@'):format(id, func_name) -end - ---- Creates a spacer statusline component i.e. for padding ---- or to represent an empty component ---- @param size integer? ---- @param opts table? ---- @return ComponentOpts? -function M.spacer(size, opts) - opts = opts or {} - local filler = opts.filler or ' ' - local priority = opts.priority or 0 - if not size or size < 1 then return end - local spacer = string.rep(filler, size) - return { { { spacer } }, priority = priority, before = '', after = '' } -end - ---- truncate with an ellipsis or if surrounded by quotes, replace contents of quotes with ellipsis ---- @param str string ---- @param max_size integer ---- @return string -local function truncate_string(str, max_size) - if not max_size or vim.api.nvim_strwidth(str) < max_size then return str end - local match, count = str:gsub('([\'"]).*%1', '%1…%1') - return count > 0 and match or str:sub(1, max_size - 1) .. '…' -end - ----@alias Chunks {[1]: string | number, [2]: string, max_size: integer?}[] - ----@param chunks any ----@return Chunks? -local function normalize_chunks(chunks) - if type(chunks) ~= 'table' then return end - if vim.islist(chunks) then return chunks end - - -- Allow "sparse arrays" like {[1]=..., [2]=..., [7]=...} by compacting them. - local keys = {} - for k, _ in pairs(chunks) do - if type(k) == 'number' and k >= 1 and math.floor(k) == k then - keys[#keys + 1] = k - end - end - if #keys == 0 then return end - table.sort(keys) - - local dense = {} - for _, k in ipairs(keys) do - local v = chunks[k] - if v ~= nil then dense[#dense + 1] = v end - end - return dense -end - ----@param chunks Chunks ----@return string -local function chunks_to_string(chunks) - chunks = normalize_chunks(chunks) - if not chunks then return '' end - - local strings = {} - for _, item in ipairs(chunks) do - local text, hl = unpack(item) - if not falsy(text) then - if type(text) ~= 'string' then text = tostring(text) end - if item.max_size then text = truncate_string(text, item.max_size) end - text = text:gsub('%%', '%%%1') - strings[#strings + 1] = not falsy(hl) and ('%%#%s#%s%%*'):format(hl, text) - or text - end - end - return table.concat(strings, '') -end - ---- @class ComponentOpts ---- @field [1] Chunks ---- @field priority number ---- @field click string ---- @field before string ---- @field after string ---- @field id number ---- @field max_size integer ---- @field cond boolean | number | table | string, - ---- @param opts ComponentOpts ---- @return StringComponent? -local function component(opts) - assert(opts, 'component options are required') - if opts.cond ~= nil and falsy(opts.cond) then return end - - local item = normalize_chunks(opts[1]) - if not item then - error( - fmt( - 'component options are required but got %s instead', - vim.inspect(opts[1]) - ) - ) - end - - if not opts.priority then opts.priority = 10 end - local before, after = '', '' - - local item_str = chunks_to_string(item) - if vim.api.nvim_strwidth(item_str) == 0 then return end - - local click_start = opts.click - and get_click_start(opts.click, tostring(opts.id)) - or '' - local click_end = opts.click and CLICK_END or '' - local component_str = - table.concat({ click_start, before, item_str, after, click_end }) - return { - component = component_str, - length = api.nvim_eval_statusline(component_str, { maxwidth = 0 }).width, - priority = opts.priority, - } -end - --- }}} -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- --- statusline render utils {{{ -------------------------------------------------------------------------------- - -local function sum_lengths(list) - return T.fold( - function(acc, item) return acc + (item.length or 0) end, - list, - 0 - ) -end - -local function is_lowest(item, lowest) - -- if there hasn't been a lowest selected so far, then the item is the - -- lowest - if not lowest or not lowest.length then return true end - -- if the item doesn't have a priority or a length, it is likely a special - -- character so should never be the lowest - if not item.priority or not item.length then return false end - -- if the item has the same priority as the lowest, then if the item has a - -- greater length it should become the lowest - if item.priority == lowest.priority then - return item.length > lowest.length - end - return item.priority > lowest.priority -end - ---- Take the lowest priority items out of the statusline if we don't have ---- space for them. ---- Note: Currently this doesn't account for if an item that has a lower ---- priority could be fit in instead ---- @param statusline table ---- @param space number ---- @param length number -local function prioritize(statusline, space, length) - length = length or sum_lengths(statusline) - if length <= space then return statusline end - local lowest, index_to_remove - for idx, c in ipairs(statusline) do - if is_lowest(c, lowest) then - lowest, index_to_remove = c, idx - end - end - table.remove(statusline, index_to_remove) - return prioritize(statusline, space, length - lowest.length) -end - ---- @param sections ComponentOpts[][] ---- @param available_space number? ---- @return string -function M.display(sections, available_space) - local components = T.fold(function(acc, section, count) - if #section == 0 then - table.insert(acc, separator()) - return acc - end - T.foreach(function(args, index) - if not args then return end - local ok, str = T.pcall('Error creating component', component, args) - if not ok then return end - table.insert(acc, str) - if #section == index and count ~= #sections then - table.insert(acc, separator()) - end - end, section) - return acc - end, sections) - - local items = available_space and prioritize(components, available_space) - or components - local str = vim.tbl_map(function(item) return item.component end, items) - return table.concat(str) -end - ---- A helper class that allow collecting `...StringComponent` ---- into sections that can then be added to each other ---- i.e. ---- ```lua ---- section1:new(1, 2, 3) + section2:new(4, 5, 6) + section3(7, 8, 9) ---- {1, 2, 3, 4, 5, 6, 7, 8, 9} -- <-- ---- ``` ----@class Section ----@field __add fun(l:Section, r:Section): StringComponent[] ----@field __index Section ----@field new fun(...:StringComponent[]): Section -local section = {} -function section:new(...) - local o = { ... } - self.__index = self - self.__add = function(l, r) - local rt = { unpack(l) } - for _, v in ipairs(r) do - rt[#rt + 1] = v - end - return rt - end - return setmetatable(o, self) -end - -M.section = section - --- }}} --------------------------------------------------------------------------------- - -return M - --- vim:fdm=marker diff --git a/files/.config/nvim/lua/custom/ui.lua b/files/.config/nvim/lua/custom/ui.lua deleted file mode 100644 index d636e836..00000000 --- a/files/.config/nvim/lua/custom/ui.lua +++ /dev/null @@ -1,478 +0,0 @@ ----------------------------------------------------------------------------------------------------- --- Styles ----------------------------------------------------------------------------------------------------- - --- mrl.p_table is defined in tools.lua, which is loaded before this file in --- init.lua --- --- Theme palette (derived from active colorscheme). --- NOTE: we mutate this table in-place so any modules that captured a reference --- (e.g. `local P = mrl.ui.palette`) keep seeing updates after `:colorscheme`. -mrl.ui.palette = mrl.ui.palette or {} - -local function hex_from_hl(name, attr, fallback) - local ok, hl = pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) - if not ok or not hl then return fallback end - local v = hl[attr] - if not v then return fallback end - return ('#%06x'):format(v) -end - -local function tint(color, percent) - local ok = type(color) == 'string' and color:match('^#%x%x%x%x%x%x$') - if not ok then return color end - local r = tonumber(color:sub(2, 3), 16) - local g = tonumber(color:sub(4, 5), 16) - local b = tonumber(color:sub(6, 7), 16) - local function blend(component) - component = math.floor(component * (1 + percent)) - return math.min(math.max(component, 0), 255) - end - return ('#%02x%02x%02x'):format(blend(r), blend(g), blend(b)) -end - -local function get_nightfox_palette() - local ok, nightfox_palette = pcall(require, 'nightfox.palette') - if not ok or not nightfox_palette or not nightfox_palette.load then - return nil - end - return nightfox_palette.load(vim.g.colors_name or 'carbonfox') -end - --- Nightfox palette values can be "Color" objects (tables with `.base` and a --- callable metatable). Normalize everything we store in `mrl.ui.palette` to a --- hex string so other modules can safely use it. -local function as_hex(v, fallback) - if type(v) == 'string' then return v end - if type(v) == 'number' then return ('#%06x'):format(v) end - if type(v) == 'table' then - if type(v.base) == 'string' then return v.base end - if vim.is_callable(v) then - local ok, res = pcall(v) - if ok then - if type(res) == 'string' then return res end - if type(res) == 'number' then return ('#%06x'):format(res) end - end - end - end - return fallback -end - ---- Refresh palette from the active colorscheme. -function mrl.ui.refresh_palette() - -- Prior hardcoded palette as last-resort defaults - local defaults = { - green = '#98c379', - dark_green = '#10B981', - blue = '#82AAFE', - dark_blue = '#4e88ff', - bright_blue = '#51afef', - teal = '#15AABF', - pale_pink = '#b490c0', - magenta = '#c678dd', - -- "red" should track the theme's git-delete color (see derived.red below) - red = '#E06C75', - pale_red = '#E06C75', - light_red = '#c43e1f', - dark_red = '#be5046', - dark_orange = '#FF922B', - bright_yellow = '#FAB005', - light_yellow = '#e5c07b', - whitesmoke = '#9E9E9E', - light_gray = '#626262', - comment_grey = '#5c6370', - grey = '#3E4556', - } - - local pal = get_nightfox_palette() - local derived = {} - - if pal then - -- Nightfox palette naming (works across carbonfox/nightfox variants) - derived.green = as_hex(pal.green, defaults.green) - derived.blue = as_hex(pal.blue, defaults.blue) - derived.teal = as_hex(pal.cyan or pal.teal, defaults.teal) - derived.magenta = as_hex(pal.magenta, defaults.magenta) - derived.pale_pink = as_hex(pal.pink or pal.magenta, defaults.pale_pink) - derived.pale_red = as_hex(pal.red, defaults.pale_red) - -- Prefer the actual GitSignsDelete highlight if available (theme-defined) - derived.red = hex_from_hl('GitSignsDelete', 'fg', pal.red or defaults.red) - derived.dark_orange = as_hex(pal.orange, defaults.dark_orange) - derived.bright_yellow = as_hex(pal.yellow, defaults.bright_yellow) - derived.light_yellow = as_hex(pal.yellow, defaults.light_yellow) - derived.comment_grey = as_hex(pal.comment or pal.fg3, defaults.comment_grey) - derived.whitesmoke = as_hex(pal.fg1 or pal.fg0, defaults.whitesmoke) - derived.light_gray = as_hex(pal.fg3, defaults.light_gray) - derived.grey = as_hex(pal.bg3 or pal.bg2, defaults.grey) - else - -- Generic fallback: derive from highlight groups - derived.pale_red = hex_from_hl('DiagnosticError', 'fg', defaults.pale_red) - derived.red = - hex_from_hl('GitSignsDelete', 'fg', derived.pale_red or defaults.red) - derived.dark_orange = - hex_from_hl('DiagnosticWarn', 'fg', defaults.dark_orange) - derived.teal = hex_from_hl('DiagnosticInfo', 'fg', defaults.teal) - derived.bright_blue = - hex_from_hl('DiagnosticHint', 'fg', defaults.bright_blue) - derived.green = hex_from_hl('GitSignsAdd', 'fg', defaults.green) - derived.blue = hex_from_hl('Function', 'fg', defaults.blue) - derived.magenta = hex_from_hl('Statement', 'fg', defaults.magenta) - derived.pale_pink = hex_from_hl('Special', 'fg', defaults.pale_pink) - derived.bright_yellow = - hex_from_hl('WarningMsg', 'fg', defaults.bright_yellow) - derived.light_yellow = derived.bright_yellow - derived.comment_grey = hex_from_hl('Comment', 'fg', defaults.comment_grey) - derived.whitesmoke = hex_from_hl('Normal', 'fg', defaults.whitesmoke) - derived.light_gray = tint(derived.comment_grey, 0.1) - derived.grey = tint(hex_from_hl('Normal', 'bg', defaults.grey), 0.15) - end - - derived.dark_green = tint(derived.green, -0.25) - derived.dark_blue = tint(derived.blue, -0.25) - derived.light_red = tint(derived.pale_red, -0.15) - derived.dark_red = tint(derived.pale_red, -0.30) - - -- Write into the shared table in-place - for k in pairs(mrl.ui.palette) do - mrl.ui.palette[k] = nil - end - for k, v in pairs(defaults) do - mrl.ui.palette[k] = derived[k] or v - end - for k, v in pairs(derived) do - mrl.ui.palette[k] = v - end - - -- Keep LSP colors in sync with the palette (if lsp table already exists) - if mrl.ui.lsp and mrl.ui.lsp.colors then - mrl.ui.lsp.colors.error = mrl.ui.palette.pale_red - mrl.ui.lsp.colors.warn = mrl.ui.palette.dark_orange - mrl.ui.lsp.colors.hint = mrl.ui.palette.bright_blue - mrl.ui.lsp.colors.info = mrl.ui.palette.teal - end -end - --- Simplified border configuration - use "rounded" everywhere --- This is the standard Neovim border style name that works with most plugins -mrl.ui.border = 'rounded' - -mrl.ui.icons = { - separators = { - left_thin_block = '▏', - right_thin_block = '▕', - vert_bottom_half_block = '▄', - vert_top_half_block = '▀', - right_block = '🮉', - -- right_block = "▕", - light_shade_block = '░', - right_chubby_block = '▓', - }, - -- Unified scrollbar glyph (used by fzf-lua and other UIs). - -- FULL BLOCK (U+2588) to match fzf-lua preview scrollbar look. - scrollbar = '█', - lsp = { - error = '', -- '✗' - warn = '', --  - info = '󰋼', --  ℹ 󰙎  - hint = '󰌶', --  ⚑ - }, - git = { - add = '󰐗', -- ' '', -- '', - mod = '󰻂', -- '󱗜' --'', - remove = '󰍶', --'', -- '', - ignore = '', --'', - rename = '', -- '', - untracked = '', -- '', - ignored = '󰙦', -- '', - unstaged = '󰻂', --'󰄱', - staged = '', --'', - conflict = '', - diff = '', - repo = '', - logo = '󰊢', - branch = '', -- '', - }, - documents = { - file = '', - files = '', - folder = '', - open_folder = '', - }, - misc = { - --  - plus = '', - ellipsis = '…', - up = '⇡', - down = '⇣', - line = '', -- 'ℓ' - indent = 'Ξ', - tab = '⇥', - bug = '', --  '󰠭' - question = '', - clock = '', - cmd = '⌘', - lock = '', - shaded_lock = '', - circle = '', - project = '', - dashboard = '', - history = '󰄉', - comment = '󰅺', - robot = '󰚩', - copilot = '', - lightbulb = '󰌵', - search = '󰍉', - code = '', - telescope = '', - gear = '', - chat = '󰭻', - package = '', - list = '', - sign_in = '', - check = '󰄬', - fire = '', - note = '󰎞', - bookmark = '', - pencil = '', -- '󰏫', - tools = '', - arrow_right = '', - caret_right = '', - chevron_right = '', - double_chevron_right = '»', - table = '', - calendar = '', - -- block = "▌", - block = '▏', - clippy = '', - puzzle = '', - settings = '⚙', - key = '', - config = '', - box = '', - moon = '󰤄', - source = '󰈙', - sleep = '󰒲', - rocket = '', - task = '󰐃', - runtime = '', - }, -} -mrl.ui.lsp = { - colors = { - error = mrl.ui.palette.pale_red, - warn = mrl.ui.palette.dark_orange, - hint = mrl.ui.palette.bright_blue, - info = mrl.ui.palette.teal, - }, - --- This is a mapping of LSP Kinds to highlight groups. LSP Kinds come via the LSP spec - --- see: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#symbolKind - highlights = { - File = 'Directory', - Snippet = 'Label', - Text = '@string', - Method = '@method', - Function = '@function', - Constructor = '@constructor', - Field = '@field', - Variable = '@variable', - Module = '@namespace', - Property = '@property', - Unit = '@constant', - Value = '@variable', - Enum = '@type', - Keyword = '@keyword', - Reference = '@parameter.reference', - Constant = '@constant', - Struct = '@structure', - Event = '@variable', - Operator = '@operator', - Namespace = '@namespace', - Package = '@include', - String = '@string', - Number = '@number', - Boolean = '@boolean', - Array = '@repeat', - Object = '@type', - Key = '@field', - Null = '@symbol', - EnumMember = '@field', - Class = '@lsp.type.class', - Interface = '@lsp.type.interface', - TypeParameter = '@lsp.type.parameter', - }, -} - --- Keep palette synced to the active colorscheme. -vim.api.nvim_create_autocmd({ 'ColorScheme' }, { - group = vim.api.nvim_create_augroup('MrlUIPalette', { clear = true }), - callback = function() mrl.ui.refresh_palette() end, -}) -vim.schedule(mrl.ui.refresh_palette) - ----------------------------------------------------------------------------------------------------- --- UI Settings ----------------------------------------------------------------------------------------------------- ----@class Decorations { ----@field winbar 'ignore' | boolean ----@field number boolean ----@field statusline 'minimal' | boolean ----@field statuscolumn boolean ----@field colorcolumn boolean | string - ----@alias DecorationType 'statuscolumn'|'winbar'|'statusline'|'number'|'colorcolumn' - ----@class Decorations -local Preset = {} - ----@param o Decorations -function Preset:new(o) - assert(o, 'a preset must be defined') - self.__index = self - return setmetatable(o, self) -end - ---- WARNING: deep extend does not copy lua meta methods -function Preset:with(o) return vim.tbl_deep_extend('force', self, o) end - ----@type table -local presets = { - statusline_only = Preset:new({ - number = false, - winbar = false, - colorcolumn = false, - statusline = true, - statuscolumn = false, - }), - minimal_editing = Preset:new({ - number = false, - winbar = true, - colorcolumn = false, - statusline = 'minimal', - statuscolumn = false, - }), - tool_panel = Preset:new({ - number = false, - winbar = false, - colorcolumn = false, - statusline = 'minimal', - statuscolumn = false, - }), -} - -local commit_buffer = - presets.minimal_editing:with({ colorcolumn = '50,72', winbar = false }) - -local buftypes = { - ['quickfix'] = presets.tool_panel, - ['nofile'] = presets.tool_panel, - ['nowrite'] = presets.tool_panel, - ['acwrite'] = presets.tool_panel, - ['terminal'] = presets.tool_panel, - ['.*fugitive.*'] = presets.tool_panel, -} - ---- When searching through the filetypes table if a match can't be found then search ---- again but check if there is matching lua pattern. This is useful for filetypes for ---- plugins like Neogit which have a filetype of Neogit. -local filetypes = mrl.p_table({ - ['startuptime'] = presets.tool_panel, - ['checkhealth'] = presets.tool_panel, - ['log'] = presets.tool_panel, - ['help'] = presets.tool_panel, - ['^copilot.*'] = presets.tool_panel, - ['dbout'] = presets.tool_panel, - ['dbui'] = presets.tool_panel, - ['dapui'] = presets.tool_panel, - ['minimap'] = presets.tool_panel, - ['Trouble'] = presets.tool_panel, - ['tsplayground'] = presets.tool_panel, - ['list'] = presets.tool_panel, - ['netrw'] = presets.tool_panel, - ['flutter.*'] = presets.tool_panel, - ['NvimTree'] = presets.tool_panel, - ['undotree'] = presets.tool_panel, - ['dap-repl'] = presets.tool_panel:with({ winbar = 'ignore' }), - ['neo-tree'] = presets.tool_panel:with({ winbar = 'ignore' }), - ['toggleterm'] = presets.tool_panel:with({ winbar = 'ignore' }), - ['neotest.*'] = presets.tool_panel, - ['^Neogit.*'] = presets.tool_panel, - ['.*fugitive.*'] = presets.tool_panel, - ['query'] = presets.tool_panel, - ['DiffviewFiles'] = presets.tool_panel, - ['DiffviewFileHistory'] = presets.tool_panel, - ['mail'] = presets.statusline_only, - ['noice'] = presets.statusline_only, - ['diff'] = presets.statusline_only, - ['qf'] = presets.statusline_only, - ['alpha'] = presets.tool_panel:with({ statusline = false }), - ['fugitive'] = presets.statusline_only, - ['startify'] = presets.statusline_only, - ['man'] = presets.minimal_editing, - ['org'] = presets.minimal_editing:with({ winbar = false }), - ['norg'] = presets.minimal_editing:with({ winbar = false }), - ['orgagenda'] = presets.minimal_editing:with({ winbar = false }), - ['markdown'] = presets.minimal_editing, - ['himalaya'] = presets.minimal_editing, - ['gitcommit'] = commit_buffer, - ['NeogitCommitMessage'] = commit_buffer, -}) - -local filenames = mrl.p_table({ - ['option-window'] = presets.tool_panel, -}) - -mrl.ui.decorations = {} - ----@alias ui.OptionValue (boolean | string) - ----Get the mrl.ui setting for a particular filetype ----@param opts {ft: string?, bt: string?, fname: string?, setting: DecorationType} ----@return {ft: ui.OptionValue?, bt: ui.OptionValue?, fname: ui.OptionValue?} -function mrl.ui.decorations.get(opts) - local ft, bt, fname, setting = opts.ft, opts.bt, opts.fname, opts.setting - if (not ft and not bt and not fname) or not setting then return nil end - return { - ft = ft and filetypes[ft] and filetypes[ft][setting], - bt = bt and buftypes[bt] and buftypes[bt][setting], - fname = fname and filenames[fname] and filenames[fname][setting], - } -end - ----A helper to set the value of the colorcolumn option, to my preferences, this can be used ----in an autocommand to set the `vim.opt_local.colorcolumn` or by a plugin such as `virtcolumn.nvim` ----to set it's virtual column ----@param bufnr integer ----@param fn fun(virtcolumn: string) -function mrl.ui.decorations.set_colorcolumn(bufnr, fn) - local buf = vim.bo[bufnr] - local decor = mrl.ui.decorations.get({ - ft = buf.ft, - bt = buf.bt, - setting = 'colorcolumn', - }) - if buf.ft == '' or buf.bt ~= '' or decor.ft == false or decor.bt == false then - return - end - local ccol = decor.ft or decor.bt or '' - local virtcolumn = not mrl.falsy(ccol) and ccol or '+1' - if vim.is_callable(fn) then fn(virtcolumn) end -end - ----------------------------------------------------------------------------------------------------- -mrl.ui.current = { - border = 'rounded', -- Global border style - use Neovim's built-in "rounded" style - -- Float/popup background color source of truth. - -- Kept as a function so it always matches the active colorscheme's Normal bg. - float_bg = function() - -- Prefer our highlight helper (returns hex) - if mrl and mrl.highlight and mrl.highlight.get then - return mrl.highlight.get('Normal', 'bg') - end - - -- Fallback to Neovim API (also returns hex) - local ok, hl = - pcall(vim.api.nvim_get_hl, 0, { name = 'Normal', link = false }) - if ok and hl and hl.bg then return ('#%06x'):format(hl.bg) end - return 'NONE' - end, -} diff --git a/files/.config/nvim/lua/highlight.lua b/files/.config/nvim/lua/highlight.lua index 2cb8a87c..3c97ac8c 100644 --- a/files/.config/nvim/lua/highlight.lua +++ b/files/.config/nvim/lua/highlight.lua @@ -1,5 +1,7 @@ -local api, notify, fmt, augroup = - vim.api, vim.notify, string.format, mrl.augroup +local api, fmt = vim.api, string.format +local T = require('tools') +local augroup = T.augroup +local tint, blend, darken_hsl = T.tint, T.blend, T.darken_hsl ---@alias HLAttrs {from: string, attr: "fg" | "bg", alter: integer} @@ -73,267 +75,6 @@ local function get_hl_as_hex(opts, ns) return hl end ---- Change the brightness of a color, negative numbers darken and positive ones brighten ----see: ---- 1. https://stackoverflow.com/q/5560248 ---- 2. https://stackoverflow.com/a/37797380 ----@param color string A hex color ----@param percent number a negative number darkens and a positive one brightens ----@return string -local function tint(color, percent) - assert( - color and percent, - 'cannot alter a color without specifying a color and percentage' - ) - local r = tonumber(color:sub(2, 3), 16) - local g = tonumber(color:sub(4, 5), 16) - local b = tonumber(color:sub(6), 16) - if not r or not g or not b then return 'NONE' end - local blend = function(component) - component = math.floor(component * (1 + percent)) - return math.min(math.max(component, 0), 255) - end - return fmt('#%02x%02x%02x', blend(r), blend(g), blend(b)) -end - ---- Blend two hex colors using an alpha for the foreground. ---- `alpha = 0` returns bg, `alpha = 1` returns fg. ----@param bg string hex color (#RRGGBB) ----@param fg string hex color (#RRGGBB) ----@param alpha number 0..1 ----@return string -local function blend(bg, fg, alpha) - assert(bg and fg and alpha ~= nil, 'blend(bg, fg, alpha) requires 3 args') - if type(bg) ~= 'string' or type(fg) ~= 'string' then return 'NONE' end - if bg == 'NONE' or fg == 'NONE' then return 'NONE' end - if not bg:match('^#%x%x%x%x%x%x$') or not fg:match('^#%x%x%x%x%x%x$') then - return 'NONE' - end - alpha = math.min(math.max(alpha, 0), 1) - - local br, bgc, bb = - tonumber(bg:sub(2, 3), 16), - tonumber(bg:sub(4, 5), 16), - tonumber(bg:sub(6, 7), 16) - local fr, fgc, fb = - tonumber(fg:sub(2, 3), 16), - tonumber(fg:sub(4, 5), 16), - tonumber(fg:sub(6, 7), 16) - if not br or not bgc or not bb or not fr or not fgc or not fb then - return 'NONE' - end - - local function mix(b, f) return math.floor((1 - alpha) * b + alpha * f + 0.5) end - return fmt('#%02x%02x%02x', mix(br, fr), mix(bgc, fgc), mix(bb, fb)) -end - --- Blend two hex colors with alpha compositing --- @param fg_hex: foreground color in hex format (e.g., "#ff0000" or "ff0000") --- @param bg_hex: background color in hex format --- @param alpha: alpha value for foreground (0.0 to 1.0), defaults to 0.5 --- @return: blended color in hex format -local function blend_colors(fg_hex, bg_hex, alpha) - alpha = alpha or 0.5 - - if type(fg_hex) ~= 'string' or type(bg_hex) ~= 'string' then return 'NONE' end - if fg_hex == 'NONE' or bg_hex == 'NONE' then return 'NONE' end - - -- Remove '#' if present - fg_hex = fg_hex:gsub('#', '') - bg_hex = bg_hex:gsub('#', '') - - if #fg_hex ~= 6 or #bg_hex ~= 6 then return 'NONE' end - - -- Parse hex colors to RGB - local fg_r = tonumber(fg_hex:sub(1, 2), 16) - local fg_g = tonumber(fg_hex:sub(3, 4), 16) - local fg_b = tonumber(fg_hex:sub(5, 6), 16) - - local bg_r = tonumber(bg_hex:sub(1, 2), 16) - local bg_g = tonumber(bg_hex:sub(3, 4), 16) - local bg_b = tonumber(bg_hex:sub(5, 6), 16) - - if not fg_r or not fg_g or not fg_b or not bg_r or not bg_g or not bg_b then - return 'NONE' - end - - alpha = math.min(math.max(alpha, 0), 1) - - -- Alpha blend: out = fg * alpha + bg * (1 - alpha) - local out_r = math.floor(fg_r * alpha + bg_r * (1 - alpha) + 0.5) - local out_g = math.floor(fg_g * alpha + bg_g * (1 - alpha) + 0.5) - local out_b = math.floor(fg_b * alpha + bg_b * (1 - alpha) + 0.5) - - -- Convert back to hex - return string.format('#%02x%02x%02x', out_r, out_g, out_b) -end - -local function normalize_hex(hex) - if type(hex) ~= 'string' then return nil end - hex = hex:gsub('#', '') - if #hex ~= 6 then return nil end - return hex:lower() -end - --- Convert RGB (0..255) to HSL (0..1) -local function rgb_to_hsl(r, g, b) - r, g, b = r / 255, g / 255, b / 255 - local maxc, minc = math.max(r, g, b), math.min(r, g, b) - local h, s, l = 0, 0, (maxc + minc) / 2 - - if maxc ~= minc then - local d = maxc - minc - s = l > 0.5 and d / (2 - maxc - minc) or d / (maxc + minc) - - if maxc == r then - h = (g - b) / d + (g < b and 6 or 0) - elseif maxc == g then - h = (b - r) / d + 2 - else - h = (r - g) / d + 4 - end - h = h / 6 - end - - return h, s, l -end - --- Convert HSL (0..1) to RGB (0..255 ints) -local function hsl_to_rgb(h, s, l) - local function hue_to_rgb(p, q, t) - if t < 0 then t = t + 1 end - if t > 1 then t = t - 1 end - if t < 1 / 6 then return p + (q - p) * 6 * t end - if t < 1 / 2 then return q end - if t < 2 / 3 then return p + (q - p) * (2 / 3 - t) * 6 end - return p - end - - local r, g, b - if s == 0 then - r, g, b = l, l, l - else - local q = l < 0.5 and l * (1 + s) or l + s - l * s - local p = 2 * l - q - r = hue_to_rgb(p, q, h + 1 / 3) - g = hue_to_rgb(p, q, h) - b = hue_to_rgb(p, q, h - 1 / 3) - end - - return math.floor(r * 255 + 0.5), - math.floor(g * 255 + 0.5), - math.floor(b * 255 + 0.5) -end - --- Darken a color by scaling HSL lightness while preserving hue/saturation. --- `lightness_factor` in 0..1: lower is darker. -local function darken_hsl(hex, lightness_factor) - lightness_factor = lightness_factor or 0.0 - local h = normalize_hex(hex) - if not h then return 'NONE' end - local r = tonumber(h:sub(1, 2), 16) - local g = tonumber(h:sub(3, 4), 16) - local b = tonumber(h:sub(5, 6), 16) - if not r or not g or not b then return 'NONE' end - - local hh, ss, ll = rgb_to_hsl(r, g, b) - - if lightness_factor > 0 then - -- Lighten: move toward 1.0 (white) - ll = ll + (1 - ll) * lightness_factor - else - -- Darken: move toward 0.0 (black) - ll = ll * (1 + lightness_factor) - end - - ll = math.min(math.max(ll, 0), 1) - r, g, b = hsl_to_rgb(hh, ss, ll) - return fmt('#%02x%02x%02x', r, g, b) -end - ---- Compute an alpha (0..1) such that blend(bg, fg, alpha) ~= target. ---- Returns a best-effort alpha (averaged across RGB channels) and clamps to [0,1]. ----@param bg string hex color (#RRGGBB) ----@param fg string hex color (#RRGGBB) ----@param target string hex color (#RRGGBB) ----@return number -local function blend_alpha(bg, fg, target) - if - type(bg) ~= 'string' - or type(fg) ~= 'string' - or type(target) ~= 'string' - or bg == 'NONE' - or fg == 'NONE' - or target == 'NONE' - or not bg:match('^#%x%x%x%x%x%x$') - or not fg:match('^#%x%x%x%x%x%x$') - or not target:match('^#%x%x%x%x%x%x$') - then - return 0.5 - end - - local br, bgc, bb = - tonumber(bg:sub(2, 3), 16), - tonumber(bg:sub(4, 5), 16), - tonumber(bg:sub(6, 7), 16) - local fr, fgc, fb = - tonumber(fg:sub(2, 3), 16), - tonumber(fg:sub(4, 5), 16), - tonumber(fg:sub(6, 7), 16) - local tr, tgc, tb = - tonumber(target:sub(2, 3), 16), - tonumber(target:sub(4, 5), 16), - tonumber(target:sub(6, 7), 16) - if - not br - or not bgc - or not bb - or not fr - or not fgc - or not fb - or not tr - or not tgc - or not tb - then - return 0.5 - end - - local function alpha_for(b, f, t) - local denom = (f - b) - if denom == 0 then return nil end - return (t - b) / denom - end - - local alphas = { - alpha_for(br, fr, tr), - alpha_for(bgc, fgc, tgc), - alpha_for(bb, fb, tb), - } - - local sum, n = 0, 0 - for _, a in ipairs(alphas) do - if a and a == a and a ~= math.huge and a ~= -math.huge then - sum, n = sum + a, n + 1 - end - end - local a = n > 0 and (sum / n) or 0.5 - return math.min(math.max(a, 0), 1) -end - -local err_warn = vim.schedule_wrap(function(group, attribute) - notify( - fmt( - 'failed to get highlight %s for attribute %s\n%s', - group, - attribute, - debug.traceback() - ), - 'ERROR', - { - title = fmt('Highlight - get(%s)', group), - } - ) -- stylua: ignore -end) ---Get the value a highlight group whilst handling errors, fallbacks as well as returning a gui value ---If no attribute is specified return the entire highlight table @@ -386,11 +127,9 @@ local function set(ns, name, opts) opts, name, ns = name, ns, 0 end - vim.validate({ - opts = { opts, 'table' }, - name = { name, 'string' }, - ns = { ns, 'number' }, - }) + vim.validate('opts', opts, 'table') + vim.validate('name', name, 'string') + vim.validate('ns', ns, 'number') local hl = opts.clear and {} or get_hl_as_hex({ name = opts.inherit or name }) for attribute, hl_data in pairs(opts) do @@ -398,7 +137,7 @@ local function set(ns, name, opts) if attrs[attribute] then hl[attribute] = new_data end end - mrl.pcall(fmt('setting highlight "%s"', name), api.nvim_set_hl, ns, name, hl) + T.pcall(fmt('setting highlight "%s"', name), api.nvim_set_hl, ns, name, hl) end ---Apply a list of highlights @@ -454,16 +193,12 @@ local function plugin(name, opts) }) end -mrl.highlight = { +return { get = get, set = set, all = all, tint = tint, blend = blend, - blend_colors = blend_colors, - blend_alpha = blend_alpha, - rgb_to_hsl = rgb_to_hsl, - hsl_to_rgb = hsl_to_rgb, darken_hsl = darken_hsl, plugin = plugin, set_winhl = set_winhl, diff --git a/files/.config/nvim/lua/keymaps.lua b/files/.config/nvim/lua/keymaps.lua index 594b68a0..3faf4a67 100644 --- a/files/.config/nvim/lua/keymaps.lua +++ b/files/.config/nvim/lua/keymaps.lua @@ -1,37 +1,15 @@ --- TODO: move me to other place --- --- +local T = require('tools') local noremap_silent = { noremap = true, silent = true } --- Commands {{{ --- ----Create an nvim command -function mrl.command(name, rhs, opts) - opts = opts or {} - vim.api.nvim_create_user_command(name, rhs, opts) -end - ----Determine if a value of any type is empty -function mrl.falsy(item) - if not item then return true end - local item_type = type(item) - if item_type == 'boolean' then return not item end - if item_type == 'string' then return item == '' end - if item_type == 'number' then return item <= 0 end - if item_type == 'table' then return vim.tbl_isempty(item) end - return item ~= nil -end - --- }}} -- Quickfix and Location List {{{ -mrl.list = { qf = {}, loc = {} } +local list = { qf = {}, loc = {} } ---@param list_type "loclist" | "quickfix" ---@return boolean local function is_list_open(list_type) - return mrl.find( - function(win) return not mrl.falsy(win[list_type]) end, + return T.find( + function(win) return not T.falsy(win[list_type]) end, vim.fn.getwininfo() ) ~= nil end @@ -45,7 +23,7 @@ local function preserve_window(callback, ...) if win ~= vim.api.nvim_get_current_win() then vim.cmd.wincmd('p') end end -function mrl.list.qf.toggle() +function list.qf.toggle() if is_list_open('quickfix') then vim.cmd.cclose(silence) elseif #vim.fn.getqflist() > 0 then @@ -53,7 +31,7 @@ function mrl.list.qf.toggle() end end -function mrl.list.loc.toggle() +function list.loc.toggle() if is_list_open('loclist') then vim.cmd.lclose(silence) elseif #vim.fn.getloclist(0) > 0 then @@ -63,34 +41,30 @@ end -- @see: https://vi.stackexchange.com/a/21255 -- using range-aware function -function mrl.list.qf.delete(buf) +function list.qf.delete(buf) buf = buf or vim.api.nvim_get_current_buf() - local list = vim.fn.getqflist() + local qflist = vim.fn.getqflist() local line = vim.api.nvim_win_get_cursor(0)[1] local mode = vim.api.nvim_get_mode().mode if mode:match('[vV]') then local first_line = vim.fn.getpos("'<")[2] local last_line = vim.fn.getpos("'>")[2] - list = mrl.fold(function(accum, item, i) + qflist = T.fold(function(accum, item, i) if i < first_line or i > last_line then accum[#accum + 1] = item end return accum - end, list) + end, qflist) else - table.remove(list, line) + table.remove(qflist, line) end -- replace items in the current list, do not make a new copy of it; this also preserves the list title - vim.fn.setqflist({}, 'r', { items = list }) + vim.fn.setqflist({}, 'r', { items = qflist }) vim.fn.setpos('.', { buf, line, 1, 0 }) -- restore current line end -- }}} -local fn, api, uv, cmd, command, fmt = - vim.fn, vim.api, vim.loop, vim.cmd, mrl.command, string.format - -if not mrl or not mrl.mappings.enable then return end - local fn, api, uv, cmd, fmt = vim.fn, vim.api, vim.loop, vim.cmd, string.format +local command = T.command -- Credit: Justinmk vim.keymap.set('n', 'g>', [[set nomore40messagesset more]], { @@ -333,18 +307,8 @@ end) vim.keymap.set('n', 'gf', 'e ') -- quickfix list -vim.keymap.set( - 'n', - '', - mrl.list.qf.toggle, - { desc = 'toggle quickfix list' } -) -vim.keymap.set( - 'n', - '', - mrl.list.loc.toggle, - { desc = 'toggle location list' } -) +vim.keymap.set('n', '', list.qf.toggle, { desc = 'toggle quickfix list' }) +vim.keymap.set('n', '', list.loc.toggle, { desc = 'toggle location list' }) -----------------------------------------------------------------------------// -- Completion @@ -453,7 +417,7 @@ vim.keymap.set( 'Reverse', { desc = 'reverse buffer' } ) -vim.keymap.set('n', '', 'Todo', { desc = 'reverse buffer' }) +vim.keymap.set('n', '', 'Todo', { desc = 'toggle todo search' }) -----------------------------------------------------------------------------// -- References @@ -840,7 +804,7 @@ vim.keymap.set( vim.keymap.set( { 'n' }, 'ls', - 'lua vim.lsp.diagnostic.get_line_diagnostics()', + vim.diagnostic.open_float, { noremap = true, silent = true } ) diff --git a/files/.config/nvim/lua/lazyloader.lua b/files/.config/nvim/lua/lazyloader.lua index 1efb1b38..9b3c9c1e 100644 --- a/files/.config/nvim/lua/lazyloader.lua +++ b/files/.config/nvim/lua/lazyloader.lua @@ -15,7 +15,7 @@ end ---@diagnostic disable-next-line: undefined-field vim.opt.rtp:prepend(lazypath) -- Ensure icons are available, use defaults if not -local icons = require('custom.ui').icons +local icons = require('tools').ui.icons require('lazy').setup({ { import = 'custom.plugins' }, diff --git a/files/.config/nvim/plugin/lastplace.lua b/files/.config/nvim/plugin/lastplace.lua index 9197e284..9305b295 100644 --- a/files/.config/nvim/plugin/lastplace.lua +++ b/files/.config/nvim/plugin/lastplace.lua @@ -5,22 +5,10 @@ -- References: -- github.com/ethanholz/nvim-lastplace/blob/main/lua/nvim-lastplace/init.lua -if not mrl then return end - -- }}} -------------------------------------------------------------------------------- --- Helper to safely call augroup, deferring if not available yet -local function augroup(name, ...) - local args = { ... } - if mrl and mrl.augroup then - return mrl.augroup(name, unpack(args)) - else - vim.schedule(function() - if mrl and mrl.augroup then mrl.augroup(name, unpack(args)) end - end) - end -end +local augroup = require('tools').augroup local fn = vim.fn local ignore_buftype = { 'quickfix', 'nofile', 'help', 'terminal' } diff --git a/files/.config/nvim/plugin/lsp.lua b/files/.config/nvim/plugin/lsp.lua index dc93184a..e4092f62 100644 --- a/files/.config/nvim/plugin/lsp.lua +++ b/files/.config/nvim/plugin/lsp.lua @@ -1,19 +1,6 @@ -if not mrl then return end - --- Wait for icons to be available -if not mrl.ui or not mrl.ui.icons or not mrl.ui.icons.lsp then - vim.schedule(function() - if mrl and mrl.ui and mrl.ui.icons and mrl.ui.icons.lsp then - -- Re-source this file - dofile(vim.fn.expand(':p')) - end - end) - return -end - -- Optimized: Cache severity constants and icons lookup local S = vim.diagnostic.severity -local icons = mrl.ui.icons.lsp +local icons = require('tools').ui.icons.lsp -- Diagnostics configuration (optimized for startup time) vim.diagnostic.config({ diff --git a/files/.config/nvim/plugin/statuscolumn.lua b/files/.config/nvim/plugin/statuscolumn.lua index 93c5b93e..19d002a2 100644 --- a/files/.config/nvim/plugin/statuscolumn.lua +++ b/files/.config/nvim/plugin/statuscolumn.lua @@ -1,27 +1,20 @@ -if - not mrl - or not mrl.ui - or not mrl.ui.statuscolumn - or not mrl.ui.statuscolumn.enable -then - return -end - --- Custom statuscolumn inspired by akinsho: --- - separates git signs from other signs --- - shows fold marker --- - can be disabled per buffer via your "decorations" presets +local UI = require('tools').ui -mrl.ui.statuscolumn = mrl.ui.statuscolumn or {} +_G.Stlcol = {} local api, fn = vim.api, vim.fn local v = vim.v local space = ' ' -local icons = (mrl.ui and mrl.ui.icons and mrl.ui.icons.separators) or {} -local diag_icons = (mrl.ui and mrl.ui.icons and mrl.ui.icons.lsp) or {} +local icons = UI.icons.separators or {} +local diag_icons = UI.icons.lsp or {} local diag_warn_icon = diag_icons.warn or 'W' local diag_err_icon = diag_icons.error or 'E' +local cfg = { + number_width = 3, + hide_diag_on_cursorline = true, +} + local function hl(hl_group, text) if not hl_group or hl_group == '' then return text end return ('%%#%s#%s%%*'):format(hl_group, text) @@ -72,7 +65,7 @@ local function fold_mark(lnum) end local function format_number(win, lnum, relnum, virtnum, line_count) - local min_width = mrl.ui.statuscolumn.number_width or vim.o.numberwidth or 0 + local min_width = cfg.number_width or vim.o.numberwidth or 0 local col_width = math.max(api.nvim_strwidth(tostring(line_count)), min_width) if virtnum and virtnum ~= 0 then -- virtual line: show a subtle placeholder @@ -178,8 +171,8 @@ end local function should_disable(buf, ft) if vim.bo[buf].buftype ~= '' then return true end - if not mrl.ui.decorations or not mrl.ui.decorations.get then return false end - local decor = mrl.ui.decorations.get({ + if not UI.decorations or not UI.decorations.get then return false end + local decor = UI.decorations.get({ ft = ft, bt = vim.bo[buf].buftype, setting = 'statuscolumn', @@ -188,7 +181,7 @@ local function should_disable(buf, ft) and (decor.ft == false or decor.bt == false or decor.fname == false) end -function mrl.ui.statuscolumn.render() +function Stlcol.render() local win = tonumber(vim.g.statusline_winid) or api.nvim_get_current_win() local buf = api.nvim_win_get_buf(win) local ft = vim.bo[buf].filetype @@ -198,7 +191,7 @@ function mrl.ui.statuscolumn.render() local line_count = api.nvim_buf_line_count(buf) local cursor_line = api.nvim_win_get_cursor(win)[1] - local hide_diag_on_cursorline = mrl.ui.statuscolumn.hide_diag_on_cursorline + local hide_diag_on_cursorline = cfg.hide_diag_on_cursorline local diag = (hide_diag_on_cursorline and lnum == cursor_line) and ' ' or best_diag(buf, lnum - 1) local git = best_sign(buf, lnum - 1, true) @@ -216,11 +209,11 @@ function mrl.ui.statuscolumn.render() }) end -vim.o.statuscolumn = '%{%v:lua.mrl.ui.statuscolumn.render()%}' +vim.o.statuscolumn = '%{%v:lua.Stlcol.render()%}' -- Disable statuscolumn for buffers where your decorations preset says so. api.nvim_create_autocmd({ 'BufEnter', 'FileType' }, { - group = api.nvim_create_augroup('MrlStatusColumn', { clear = true }), + group = api.nvim_create_augroup('StatusColumn', { clear = true }), callback = function(args) local buf = args.buf local ft = vim.bo[buf].filetype @@ -259,7 +252,7 @@ api.nvim_create_autocmd({ 'BufEnter', 'FileType' }, { -- Clear extmark cache when signs/diagnostics update to ensure fresh data api.nvim_create_autocmd({ 'DiagnosticChanged', 'User' }, { - group = api.nvim_create_augroup('MrlStatusColumnCache', { clear = true }), + group = api.nvim_create_augroup('StatusColumnCache', { clear = true }), pattern = { '*', 'GitSignsUpdate' }, callback = function() -- Clear the cache on diagnostic/sign changes diff --git a/files/.config/wezterm/wezterm.lua b/files/.config/wezterm/wezterm.lua index 36bfab3c..59c187f3 100644 --- a/files/.config/wezterm/wezterm.lua +++ b/files/.config/wezterm/wezterm.lua @@ -580,59 +580,10 @@ return { }, -- default_gui_startup_args = { 'connect', 'wsl' }, -- }}} -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- BEGIN_WEZTERM_THEME: amberglow -config.color_scheme = 'amberglow' --- END_WEZTERM_THEME: amberglow + -- BEGIN_WEZTERM_THEME + color_scheme = 'amberglow', + -- END_WEZTERM_THEME +} -- vim: fdm=marker ts=2 sw=2 sts=2 et diff --git a/files/.config/zsh/rc.d/40-fzf.zsh b/files/.config/zsh/rc.d/40-fzf.zsh index 86d6c7a7..8f7b172c 100644 --- a/files/.config/zsh/rc.d/40-fzf.zsh +++ b/files/.config/zsh/rc.d/40-fzf.zsh @@ -5,10 +5,6 @@ export FZF_DEFAULT_OPTS=' ' export FZF_DEFAULT_OPTS=$FZF_DEFAULT_OPTS' - --color=fg:-1,fg+:3,bg:-1,bg+:0 - --color=hl:5,hl+:5,info:4,marker:6 - --color=prompt:4,spinner:6,pointer:3,header:1 - --color=gutter:-1,border:-1,label:0,query:#d9d9d9 --preview-window="border-sharp" --prompt="󰍉 " --marker="◆" @@ -18,4 +14,7 @@ export FZF_DEFAULT_OPTS=$FZF_DEFAULT_OPTS' --layout="reverse-list" --info="inline"' +# Colors sourced separately by the active theme +[ -f ~/.config/fzf/themes/amberglow.sh ] && source ~/.config/fzf/themes/amberglow.sh + [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh From a5f7f949750718d4feecd10c6e91c833d02ea882 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Tue, 17 Mar 2026 00:30:06 +0100 Subject: [PATCH 21/27] fix: add nvim tests --- .../.config/nvim/after/ftplugin/gitcommit.lua | 4 + files/.config/nvim/after/ftplugin/qf.lua | 28 +- files/.config/nvim/init.lua | 8 +- files/.config/nvim/lazy-lock.json | 15 +- .../nvim/lua/custom/plugins/bufferline.lua | 358 +++--- .../nvim/lua/custom/plugins/colorscheme.lua | 4 +- .../nvim/lua/custom/plugins/completion.lua | 3 +- files/.config/nvim/lua/custom/plugins/fzf.lua | 99 +- files/.config/nvim/lua/custom/plugins/git.lua | 55 +- files/.config/nvim/lua/custom/plugins/lsp.lua | 6 +- .../nvim/lua/custom/plugins/navigation.lua | 38 +- .../nvim/lua/custom/plugins/neotree.lua | 6 +- .../.config/nvim/lua/custom/plugins/noice.lua | 9 +- .../nvim/lua/custom/plugins/terminal.lua | 7 +- .../.config/nvim/lua/custom/plugins/todo.lua | 29 +- files/.config/nvim/lua/tools.lua | 1019 +++++++++++++++-- files/.config/nvim/plugin/autocommands.lua | 377 ++---- files/.config/nvim/plugin/colors.lua | 267 +---- files/.config/nvim/plugin/statusline.lua | 77 +- .../nvim/tests/integration/test_keymaps.lua | 57 + .../nvim/tests/integration/test_lsp.lua | 137 +++ .../nvim/tests/integration/test_options.lua | 65 ++ .../tests/integration/test_plugins_load.lua | 35 + files/.config/nvim/tests/minimal_init.lua | 33 + files/.config/nvim/tests/run_tests.sh | 23 + .../nvim/tests/unit/test_highlight.lua | 109 ++ .../.config/nvim/tests/unit/test_strings.lua | 55 + files/.config/nvim/tests/unit/test_tools.lua | 116 ++ 28 files changed, 2050 insertions(+), 989 deletions(-) create mode 100644 files/.config/nvim/after/ftplugin/gitcommit.lua create mode 100644 files/.config/nvim/tests/integration/test_keymaps.lua create mode 100644 files/.config/nvim/tests/integration/test_lsp.lua create mode 100644 files/.config/nvim/tests/integration/test_options.lua create mode 100644 files/.config/nvim/tests/integration/test_plugins_load.lua create mode 100644 files/.config/nvim/tests/minimal_init.lua create mode 100755 files/.config/nvim/tests/run_tests.sh create mode 100644 files/.config/nvim/tests/unit/test_highlight.lua create mode 100644 files/.config/nvim/tests/unit/test_strings.lua create mode 100644 files/.config/nvim/tests/unit/test_tools.lua diff --git a/files/.config/nvim/after/ftplugin/gitcommit.lua b/files/.config/nvim/after/ftplugin/gitcommit.lua new file mode 100644 index 00000000..eee2fe70 --- /dev/null +++ b/files/.config/nvim/after/ftplugin/gitcommit.lua @@ -0,0 +1,4 @@ +vim.opt_local.colorcolumn = '72' +vim.opt_local.spell = true +vim.opt_local.spelllang = 'en_us' +vim.opt_local.textwidth = 72 diff --git a/files/.config/nvim/after/ftplugin/qf.lua b/files/.config/nvim/after/ftplugin/qf.lua index 4d7624ef..e0164758 100644 --- a/files/.config/nvim/after/ftplugin/qf.lua +++ b/files/.config/nvim/after/ftplugin/qf.lua @@ -1,21 +1,43 @@ -- CUSTOM QUICKFIX WINDOW +local T = require('tools') + vim.opt_local.wrap = false vim.opt_local.number = false vim.opt_local.signcolumn = 'yes' vim.opt_local.buflisted = false vim.opt_local.winfixheight = true +-- @see: https://vi.stackexchange.ctrueom/a/21255 +local function qf_delete(buf) + buf = buf or vim.api.nvim_get_current_buf() + local qflist = vim.fn.getqflist() + local line = vim.api.nvim_win_get_cursor(0)[1] + local mode = vim.api.nvim_get_mode().mode + if mode:match('[vV]') then + local first_line = vim.fn.getpos("'<")[2] + local last_line = vim.fn.getpos("'>")[2] + qflist = T.fold(function(accum, item, i) + if i < first_line or i > last_line then accum[#accum + 1] = item end + return accum + end, qflist) + else + table.remove(qflist, line) + end + vim.fn.setqflist({}, 'r', { items = qflist }) + vim.fn.setpos('.', { buf, line, 1, 0 }) +end + vim.keymap.set( 'n', 'dd', - mrl.list.qf.delete, + qf_delete, { buffer = 0, desc = '[qf] delete current quickfix entry' } ) vim.keymap.set( 'v', 'd', - mrl.list.qf.delete, + qf_delete, { buffer = 0, desc = '[qf] delete selected quickfix entry' } ) vim.keymap.set( @@ -33,4 +55,4 @@ vim.keymap.set( -- force quickfix to open beneath all other splits vim.cmd.wincmd('J') -mrl.adjust_split_height(3, 10) +T.adjust_split_height(3, 10) diff --git a/files/.config/nvim/init.lua b/files/.config/nvim/init.lua index 16ec7b94..630803c6 100644 --- a/files/.config/nvim/init.lua +++ b/files/.config/nvim/init.lua @@ -31,18 +31,14 @@ end -- Load modules {{{ -------------------------------------------------------------------------------- -require('tools') -- has to be loaded before plugins +require('tools') -- has to be loaded before plugins (bootstraps ui, strings, colors) require('keymaps') require('options') require('highlight') -- needed by plugins for highlight tables -require('custom.ui') -- needed by lazyloader for icons require('lazyloader') -- Defer non-critical modules for faster startup -vim.defer_fn(function() - require('custom.strings') - require('external_grep') -end, 0) +vim.defer_fn(function() require('external_grep') end, 0) -- }}} -------------------------------------------------------------------------------- diff --git a/files/.config/nvim/lazy-lock.json b/files/.config/nvim/lazy-lock.json index 3264eeac..9e2ba827 100644 --- a/files/.config/nvim/lazy-lock.json +++ b/files/.config/nvim/lazy-lock.json @@ -1,11 +1,12 @@ { "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, "blink-cmp-avante": { "branch": "master", "commit": "4f494c6e124acbe31a8f5d58effa0c14aa38a6d5" }, - "blink.cmp": { "branch": "main", "commit": "4b18c32adef2898f95cdef6192cbd5796c1a332d" }, + "blink.cmp": { "branch": "main", "commit": "539053d87740e357a96fb304c9a4da2ef27b3576" }, + "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, "ccc.nvim": { "branch": "main", "commit": "9d1a256e006decc574789dfc7d628ca11644d4c2" }, "codecompanion.nvim": { "branch": "main", "commit": "5e4f7cfac58c94c61d5a3d3a99d715d5c8762db6" }, "conform.nvim": { "branch": "master", "commit": "086a40dc7ed8242c03be9f47fbcee68699cc2395" }, - "copilot.lua": { "branch": "master", "commit": "11f6d961881783c4db4bc916bad900d26599b366" }, + "copilot.lua": { "branch": "master", "commit": "8e2a91828210d6043744468f6d7027d256a41f42" }, "crates.nvim": { "branch": "main", "commit": "ac9fa498a9edb96dc3056724ff69d5f40b898453" }, "csvview.nvim": { "branch": "main", "commit": "7022e18a0fbae9aecf99a3ba02b2a541edc2b8a1" }, "debugprint.nvim": { "branch": "main", "commit": "4f29196c5fe32752bb7a3e8bc83d4a4d2f3b7922" }, @@ -15,7 +16,7 @@ "file-line": { "branch": "main", "commit": "559088afaf10124ea663ee0f4f73b1de48fb1632" }, "fold-cycle.nvim": { "branch": "main", "commit": "6144567b3307bbcfed0e5b2dd23acb9576575d9e" }, "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, - "fzf-lua": { "branch": "main", "commit": "30ba09afbc0eac5fc5ec8823418d1e45d0712b9f" }, + "fzf-lua": { "branch": "main", "commit": "65c848961e66de995052ead60970fbde101ff18f" }, "git-worktree.nvim": { "branch": "master", "commit": "f247308e68dab9f1133759b05d944569ad054546" }, "gitgraph.nvim": { "branch": "main", "commit": "c16daa7d7dd597caf9085644c009cfa80b75db8e" }, "gitlinker.nvim": { "branch": "master", "commit": "cc59f732f3d043b626c8702cb725c82e54d35c25" }, @@ -29,7 +30,7 @@ "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" }, "key-analyzer.nvim": { "branch": "main", "commit": "4e4bef34498e821bcbd5203f44db8b67e4f10e04" }, "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, - "lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" }, + "lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" }, "lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" }, "markdown-table-mode.nvim": { "branch": "main", "commit": "bb1ea9b76c1b29e15e14806fdfbb2319df5c06f1" }, "mason-lspconfig.nvim": { "branch": "main", "commit": "a676ab7282da8d651e175118bcf54483ca11e46d" }, @@ -64,12 +65,12 @@ "nvim-dev-container": { "branch": "main", "commit": "87ea57f420b3460d0c45e239057ffc38fa32f886" }, "nvim-lightbulb": { "branch": "master", "commit": "aa3a8b0f4305b25cfe368f6c9be9923a7c9d0805" }, "nvim-lint": { "branch": "master", "commit": "606b823a57b027502a9ae00978ebf4f5d5158098" }, - "nvim-lspconfig": { "branch": "master", "commit": "3addf05a791ea256c36438220aced91c03071712" }, + "nvim-lspconfig": { "branch": "master", "commit": "4d0724be90b633ddce51b328a631060e6acd7d66" }, "nvim-lspimport": { "branch": "main", "commit": "9c1c61a5020faeb1863bb66eb4b2a9107e641876" }, "nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" }, "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, - "nvim-treesitter-context": { "branch": "master", "commit": "529ee357b8c03d76ff71233afed68fd0f5fe10b1" }, + "nvim-treesitter-context": { "branch": "master", "commit": "9a8e39993e3b895601bf8227124a48ea8268149e" }, "nvim-treesitter-textobjects": { "branch": "main", "commit": "4e91b5d0394329a229725b021a8ea217099826ef" }, "nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" }, "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, @@ -87,7 +88,7 @@ "tiny-inline-diagnostic.nvim": { "branch": "main", "commit": "ba133b3e932416e4b9507095731a6d7276878fe8" }, "todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" }, "toggleterm.nvim": { "branch": "main", "commit": "9a88eae817ef395952e08650b3283726786fb5fb" }, - "treesj": { "branch": "main", "commit": "186084dee5e9c8eec40f6e39481c723dd567cb05" }, + "treesj": { "branch": "main", "commit": "26bc2a8432ba3ea79ed6aa346fba780a3d372570" }, "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, "undotree": { "branch": "master", "commit": "6fa6b57cda8459e1e4b2ca34df702f55242f4e4d" }, "vim-apathy": { "branch": "master", "commit": "27128a0f55189724c841843ba41cd33cf7186032" }, diff --git a/files/.config/nvim/lua/custom/plugins/bufferline.lua b/files/.config/nvim/lua/custom/plugins/bufferline.lua index ebe86d81..c410db42 100644 --- a/files/.config/nvim/lua/custom/plugins/bufferline.lua +++ b/files/.config/nvim/lua/custom/plugins/bufferline.lua @@ -3,188 +3,222 @@ return { { 'akinsho/bufferline.nvim', event = 'UIEnter', - dependencies = {}, - cond = false, - disable = true, + dependencies = { 'echasnovski/mini.icons' }, config = function() local bufferline = require('bufferline') - bufferline.setup({ - highlights = { - fill = { - fg = '#ffffff', - bg = '#000000', - }, - background = { - -- fg = '#000000', - bg = '#000000', - }, - tab = { - -- fg = '#000000', - bg = '#000000', - }, - tab_selected = { - fg = '#000000', - bg = '#ffaa00', - }, - tab_separator = { - fg = '#00ff00', - bg = '#00aa00', - }, - tab_separator_selected = { - fg = '#00ff00', - bg = '#0000ff', - -- sp = '', - -- underline = '', - }, - }, - options = { - debug = { logging = true }, - style_preset = { bufferline.style_preset.minimal }, - mode = 'buffers', - sort_by = 'insert_after_current', - move_wraps_at_ends = true, - right_mouse_command = 'vert sbuffer %d', - show_close_icon = false, - show_buffer_close_icons = false, - indicator = { - -- style = 'icon', - style = 'none', - icon = '▎', -- this should be omitted if indicator style is not 'icon' - }, - -- diagnostics = 'nvim_lsp', - -- diagnostics_indicator = function(count, level) - -- level = level:match('warn') and 'warn' or level - -- return (icons[level] or '?') .. ' ' .. count - -- end, - -- diagnostics_update_in_insert = false, - hover = { enabled = true, reveal = { 'close' } }, - offsets = { - { - text = 'EXPLORER', - filetype = 'neo-tree', - highlight = 'PanelHeading', - -- text_align = 'left', - separator = false, - }, - { - text = 'UNDOTREE', - filetype = 'undotree', - highlight = 'PanelHeading', - separator = false, - }, - { - text = '󰆼 DATABASE VIEWER', - filetype = 'dbui', - highlight = 'PanelHeading', - separator = false, + local UI = require('tools').ui + + -- Derive colours from live highlight groups so every theme looks great. + local function hl_hex(name, attr, fallback) + local ok, h = + pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) + if ok and h and h[attr] then return ('#%06x'):format(h[attr]) end + return fallback + end + + local NUMS = + { '➊', '➋', '➌', '➍', '➎', '➏', '➐', '➑', '➒', '➓' } + + local function setup() + local pal = UI.palette or {} + local bg = hl_hex('StatusLine', 'bg', 'NONE') + local bg_sel = hl_hex('Normal', 'bg', '#1e1e2e') + local fg_sel = hl_hex('Normal', 'fg', '#cdd6f4') + local fg_dim = pal.comment_grey or hl_hex('Comment', 'fg', '#6c7086') + local accent = pal.blue or hl_hex('Function', 'fg', '#82aaff') + + bufferline.setup({ + highlights = { + -- Tabline gutter & inactive tabs share the statusline bg + fill = { bg = bg }, + background = { bg = bg, fg = fg_dim }, + tab = { bg = bg, fg = fg_dim }, + tab_close = { bg = bg, fg = fg_dim }, + close_button = { bg = bg, fg = fg_dim }, + + -- Selected buffer pops out with normal bg + buffer_selected = { + bg = bg_sel, + fg = fg_sel, + bold = true, + italic = false, }, - { - text = ' DIFF VIEW', - filetype = 'DiffviewFiles', - highlight = 'PanelHeading', - separator = false, + close_button_selected = { bg = bg_sel, fg = fg_dim }, + tab_selected = { bg = bg, fg = fg_sel, bold = true }, + + -- Visible (non-focused split) buffer + buffer_visible = { bg = bg, fg = fg_dim }, + + -- Separators are invisible (same bg) — no clutter + separator = { bg = bg, fg = bg }, + separator_selected = { bg = bg, fg = bg }, + separator_visible = { bg = bg, fg = bg }, + offset_separator = { bg = bg, fg = bg }, + + -- Indicator line under selected tab uses the accent colour + indicator_selected = { fg = accent, bg = bg_sel }, + + -- Group labels + group_separator = { bg = bg, fg = fg_dim }, + group_label = { bg = bg, fg = accent }, + + -- Numbers + numbers = { bg = bg, fg = fg_dim }, + numbers_selected = { bg = bg_sel, fg = fg_sel }, + + -- Pick + pick = { bg = bg, fg = accent, bold = true, italic = true }, + pick_selected = { + bg = bg_sel, + fg = accent, + bold = true, + italic = true, }, }, - groups = { - options = { toggle_hidden_on_enter = true }, - items = { - bufferline.groups.builtin.pinned:with({ icon = '' }), - bufferline.groups.builtin.ungrouped, - { - name = 'Dependencies', - icon = '', - highlight = { fg = '#ECBE7B' }, - matcher = function(buf) - return vim.startswith(buf.path, vim.env.VIMRUNTIME) - end, - }, + + options = { + style_preset = bufferline.style_preset.minimal, + mode = 'buffers', + custom_areas = { + right = function() + local tabs = vim.api.nvim_list_tabpages() + if #tabs <= 1 then return {} end + local result = {} + local cur = vim.api.nvim_get_current_tabpage() + for i, tab in ipairs(tabs) do + local sym = NUMS[i] or tostring(i) + local hl = tab == cur and 'BufferLineTabSelected' + or 'BufferLineTab' + table.insert(result, { text = ' ' .. sym .. ' ', link = hl }) + end + return result + end, + }, + sort_by = 'insert_after_current', + move_wraps_at_ends = true, + right_mouse_command = 'vert sbuffer %d', + show_close_icon = false, + show_buffer_close_icons = false, + show_tab_indicators = false, + + -- Top-bar indicator for the selected buffer + -- indicator = { style = 'icon', icon = '▀' }, + + hover = { enabled = true, delay = 150, reveal = { 'close' } }, + + offsets = { { - name = 'Terraform', - matcher = function(buf) return buf.name:match('%.tf') ~= nil end, + text = ' EXPLORER', + filetype = 'neo-tree', + highlight = 'PanelHeading', + separator = false, + text_align = 'left', }, { - name = 'Kubernetes', - matcher = function(buf) - return buf.name:match('kubernetes') - and buf.name:match('%.yaml') - end, + text = ' UNDOTREE', + filetype = 'undotree', + highlight = 'PanelHeading', + separator = false, + text_align = 'left', }, { - name = 'SQL', - matcher = function(buf) return buf.name:match('%.sql$') end, + text = '󰆼 DATABASE', + filetype = 'dbui', + highlight = 'PanelHeading', + separator = false, + text_align = 'left', }, { - name = 'tests', - icon = '', - matcher = function(buf) - local name = buf.name - return name:match('[_%.]spec') or name:match('[_%.]test') - end, + text = ' DIFF VIEW', + filetype = 'DiffviewFiles', + highlight = 'PanelHeading', + separator = false, + text_align = 'left', }, - { - name = 'docs', - icon = '', - matcher = function(buf) - if - vim.bo[buf.id].filetype == 'man' or buf.path:match('man://') - then - return true - end - for _, ext in ipairs({ 'md', 'txt', 'org', 'norg', 'wiki' }) do - if ext == vim.fn.fnamemodify(buf.path, ':e') then + }, + + groups = { + options = { toggle_hidden_on_enter = true }, + items = { + bufferline.groups.builtin.pinned:with({ icon = '' }), + bufferline.groups.builtin.ungrouped, + { + name = 'Dependencies', + icon = '', + highlight = { fg = '#ECBE7B' }, + matcher = function(buf) + return vim.startswith(buf.path, vim.env.VIMRUNTIME) + end, + }, + { + name = 'Terraform', + matcher = function(buf) return buf.name:match('%.tf') ~= nil end, + }, + { + name = 'Kubernetes', + matcher = function(buf) + return buf.name:match('kubernetes') + and buf.name:match('%.yaml') + end, + }, + { + name = 'SQL', + matcher = function(buf) return buf.name:match('%.sql$') end, + }, + { + name = 'Tests', + icon = '', + matcher = function(buf) + local name = buf.name + return name:match('[_%.]spec') or name:match('[_%.]test') + end, + }, + { + name = 'Docs', + icon = '', + matcher = function(buf) + if + vim.bo[buf.id].filetype == 'man' + or buf.path:match('man://') + then return true end - end - end, - }, - { - name = 'git', - icon = ' fugitive', - matcher = function(buf) - if buf.path:match('fugitive://') then return true end - end, + for _, ext in ipairs({ 'md', 'txt', 'org', 'norg', 'wiki' }) do + if ext == vim.fn.fnamemodify(buf.path, ':e') then + return true + end + end + end, + }, + { + name = 'Git', + icon = '', + matcher = function(buf) + if buf.path:match('fugitive://') then return true end + end, + }, }, }, }, - }, + }) + end + + setup() + vim.api.nvim_create_autocmd('ColorScheme', { + group = vim.api.nvim_create_augroup('MrlBufferline', { clear = true }), + callback = setup, }) - vim.keymap.set( - 'n', - '[b', - 'BufferLineMoveNext', - { desc = 'bufferline: move next' } - ) - vim.keymap.set( - 'n', - ']b', - 'BufferLineMovePrev', - { desc = 'bufferline: move prev' } - ) - vim.keymap.set( - 'n', - 'gbb', - 'BufferLinePick', - { desc = 'bufferline: pick buffer' } - ) - vim.keymap.set( - 'n', - 'gbd', - 'BufferLinePickClose', - { desc = 'bufferline: delete buffer' } - ) - vim.keymap.set( - 'n', - '', - 'BufferLineCyclePrev', - { desc = 'bufferline: prev' } - ) - vim.keymap.set( - 'n', - '', - 'BufferLineCycleNext', - { desc = 'bufferline: next' } - ) + -- Keymaps + local map = function(lhs, rhs, desc) + vim.keymap.set('n', lhs, rhs, { desc = desc }) + end + map('[b', 'BufferLineMoveNext', 'bufferline: move next') + map(']b', 'BufferLineMovePrev', 'bufferline: move prev') + map('gbb', 'BufferLinePick', 'bufferline: pick buffer') + map('gbd', 'BufferLinePickClose', 'bufferline: delete buffer') + map('', 'BufferLineCyclePrev', 'bufferline: prev') + map('', 'BufferLineCycleNext', 'bufferline: next') end, }, } diff --git a/files/.config/nvim/lua/custom/plugins/colorscheme.lua b/files/.config/nvim/lua/custom/plugins/colorscheme.lua index 24b9bdf3..e2698a0a 100644 --- a/files/.config/nvim/lua/custom/plugins/colorscheme.lua +++ b/files/.config/nvim/lua/custom/plugins/colorscheme.lua @@ -33,14 +33,16 @@ return { vim.cmd('colorscheme vague') end, }, + -- BEGIN_NEOVIM_THEME { 'marromlam/theme-builder.nvim', lazy = false, dev = true, enabled = is_dev, cond = is_dev, - dir = '/Users/marcos/Workspaces/personal/theme-builder/generated/amberglow/nvim', priority = 1000, + dir = '/Users/marcos/Workspaces/personal/theme-builder/generated/amberglow/nvim', config = function() vim.cmd.colorscheme('amberglow') end, }, + -- END_NEOVIM_THEME } diff --git a/files/.config/nvim/lua/custom/plugins/completion.lua b/files/.config/nvim/lua/custom/plugins/completion.lua index f672ca48..4b426706 100644 --- a/files/.config/nvim/lua/custom/plugins/completion.lua +++ b/files/.config/nvim/lua/custom/plugins/completion.lua @@ -1,4 +1,5 @@ -local highlight, ui = mrl.highlight, mrl.ui +local highlight = require('highlight') +local ui = require('tools').ui return { { diff --git a/files/.config/nvim/lua/custom/plugins/fzf.lua b/files/.config/nvim/lua/custom/plugins/fzf.lua index 9f99dbf7..6264352e 100644 --- a/files/.config/nvim/lua/custom/plugins/fzf.lua +++ b/files/.config/nvim/lua/custom/plugins/fzf.lua @@ -1,15 +1,10 @@ -local mrl_ref = rawget(_G, 'mrl') or {} -local ui = mrl_ref.ui or {} -local icons = ui.icons or {} -local lsp_hls = (ui.lsp and ui.lsp.highlights) or {} +local UI = require('tools').ui +local icons = UI.icons or {} +local lsp_hls = (UI.lsp and UI.lsp.highlights) or {} -local function has_exec(bin) - return vim.fn.executable(bin) == 1 -end +local function has_exec(bin) return vim.fn.executable(bin) == 1 end -local function trim(s) - return (s:gsub('^%s+', ''):gsub('%s+$', '')) -end +local function trim(s) return (s:gsub('^%s+', ''):gsub('%s+$', '')) end local function toggle_cli_flag(args, flag) args.cmd = args.cmd or '' @@ -35,7 +30,10 @@ local function open_files_in_cwd(cwd, label) end local where = label and (' for ' .. label) or '' - vim.notify('Invalid directory' .. where .. ': ' .. tostring(dir), vim.log.levels.WARN) + vim.notify( + 'Invalid directory' .. where .. ': ' .. tostring(dir), + vim.log.levels.WARN + ) require('fzf-lua').files() end @@ -66,6 +64,7 @@ return { }, }, -- Native integration for vim.ui.select (replaces custom wrapper). + ui_select = { winopts = { split = 'botright new', @@ -83,11 +82,13 @@ return { hidden = false, no_ignore = false, follow = false, - rg_opts = [[--color=never --files -g "!.git"]], - fd_opts = [[--color=never --type f --type l --exclude .git]], + -- --hidden lets rg/fd see dotfiles; .gitignore still filters untracked ones, + -- so git-tracked hidden files are listed while junk (caches, etc.) stays out. + rg_opts = [[--color=never --files --hidden -g "!.git"]], + fd_opts = [[--color=never --type f --type l --hidden --exclude .git]], }, grep = { - rg_opts = '--column --line-number --no-heading --color=always --smart-case --max-columns=4096 -e', + rg_opts = '--column --line-number --no-heading --color=always --smart-case --max-columns=4096 --hidden -e', }, keymap = { builtin = { @@ -121,22 +122,46 @@ return { }, git = { status = { - preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' or nil, + preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' + or nil, }, bcommits = { - preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' or nil, + preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' + or nil, }, commits = { - preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' or nil, + preview_pager = has_delta and 'delta --width=$FZF_PREVIEW_COLUMNS' + or nil, }, icons = { - ['M'] = { icon = (icons.git and icons.git.mod) or 'M', color = 'yellow' }, - ['D'] = { icon = (icons.git and icons.git.remove) or 'D', color = 'red' }, - ['A'] = { icon = (icons.git and icons.git.staged) or 'A', color = 'green' }, - ['R'] = { icon = (icons.git and icons.git.rename) or 'R', color = 'yellow' }, - ['C'] = { icon = (icons.git and icons.git.conflict) or 'C', color = 'yellow' }, - ['T'] = { icon = (icons.git and icons.git.mod) or 'T', color = 'magenta' }, - ['?'] = { icon = (icons.git and icons.git.untracked) or '?', color = 'magenta' }, + ['M'] = { + icon = (icons.git and icons.git.mod) or 'M', + color = 'yellow', + }, + ['D'] = { + icon = (icons.git and icons.git.remove) or 'D', + color = 'red', + }, + ['A'] = { + icon = (icons.git and icons.git.staged) or 'A', + color = 'green', + }, + ['R'] = { + icon = (icons.git and icons.git.rename) or 'R', + color = 'yellow', + }, + ['C'] = { + icon = (icons.git and icons.git.conflict) or 'C', + color = 'yellow', + }, + ['T'] = { + icon = (icons.git and icons.git.mod) or 'T', + color = 'magenta', + }, + ['?'] = { + icon = (icons.git and icons.git.untracked) or '?', + color = 'magenta', + }, }, }, } @@ -156,11 +181,10 @@ return { -- Use defer_fn to run after fzf-lua sets its statusline vim.defer_fn(function() if vim.bo.filetype == 'fzf' then - local statusline_fn = mrl_ref.ui - and mrl_ref.ui.statusline - and mrl_ref.ui.statusline.render - if type(statusline_fn) == 'function' then - vim.opt_local.statusline = '%{%v:lua.mrl.ui.statusline.render()%}' + if + type(_G.Stl) == 'table' and type(_G.Stl.render) == 'function' + then + vim.opt_local.statusline = '%{%v:lua.Stl.render()%}' end end end, 0) @@ -174,7 +198,22 @@ return { 'FzfLua', desc = 'fzf: [f]ind [a]ll builtins', }, - { 'ff', 'FzfLua files', desc = 'fzf: [f]ind [f]iles' }, + { + 'ff', + function() + -- In a git repo: use git ls-files so tracked hidden files appear. + -- Outside a git repo: fall back to regular files picker. + local ok = vim.fn.systemlist( + 'git rev-parse --is-inside-work-tree 2>/dev/null' + )[1] + if ok == 'true' then + require('fzf-lua').git_files({ show_untracked = true }) + else + require('fzf-lua').files() + end + end, + desc = 'fzf: [f]ind [f]iles', + }, { 'fb', 'FzfLua grep_curbuf', diff --git a/files/.config/nvim/lua/custom/plugins/git.lua b/files/.config/nvim/lua/custom/plugins/git.lua index d39d7b74..ae84273f 100644 --- a/files/.config/nvim/lua/custom/plugins/git.lua +++ b/files/.config/nvim/lua/custom/plugins/git.lua @@ -1,29 +1,11 @@ -local icons = mrl.ui.icons.separators +local T = require('tools') +local icons = require('tools').ui.icons.separators -local gitlinker = mrl.require_for_later_index('gitlinker') +local gitlinker = T.require_for_later_index('gitlinker') local function browser_open() return { action_callback = require('gitlinker.actions').open_in_browser } end -local function check_main_or_develop_branch() - local main_exists = - vim.fn.system('git show-ref --verify --quiet refs/heads/main') - if vim.v.shell_error == 0 then - print('Main branch exists.') - return 'main' - end - - local develop_exists = - vim.fn.system('git show-ref --verify --quiet refs/heads/develop') - if vim.v.shell_error == 0 then - print('Develop branch exists.') - return 'develop' - end - - print('Neither main nor develop branch exists.') - return nil -end - return { ----------------------------------------------------------------------------- -- git signs {{{ @@ -304,7 +286,6 @@ return { { 'sindrets/diffview.nvim', cmd = { 'DiffviewOpen', 'DiffviewClose', 'DiffviewToggleFiles' }, - -- diff3_mixed opts = { enhanced_diff_hl = true, signs = { @@ -313,9 +294,7 @@ return { done = '✓', }, view = { - merge_tool = { - layout = 'diff3_mixed', - }, + merge_tool = { layout = 'diff3_mixed' }, }, }, }, @@ -370,32 +349,6 @@ return { config = function() require('litee.gh').setup() end, }, - { - 'jecaro/fugitive-difftool.nvim', - dependencies = { 'tpope/vim-fugitive' }, - keys = { - { - 'gPR', - function() - local current_branch = vim.fn.system('git rev-parse --abbrev-ref HEAD') - current_branch = current_branch:gsub('%s+', '') - local target_branch = check_main_or_develop_branch() - - -- print('Current branch: ' .. current_branch) - -- print('Target branch: ' .. target_branch) - - print( - 'Git! difftool --name-status ' - .. target_branch - .. '...' - .. current_branch - ) - end, - desc = 'dddd', - }, - }, - }, - -- }}} { diff --git a/files/.config/nvim/lua/custom/plugins/lsp.lua b/files/.config/nvim/lua/custom/plugins/lsp.lua index 10b896ce..570d02f5 100644 --- a/files/.config/nvim/lua/custom/plugins/lsp.lua +++ b/files/.config/nvim/lua/custom/plugins/lsp.lua @@ -1,5 +1,3 @@ -local icons = require('custom.ui').icons.lsp - return { -- LSP Configuration & Plugins { 'neovim/nvim-lspconfig', @@ -16,9 +14,7 @@ return { -- LSP Configuration & Plugins backdrop = 100, -- 100 = fully transparent (no dimming), 0 = fully opaque }, }, - config = function(_, opts) - require('mason').setup(opts) - end, + config = function(_, opts) require('mason').setup(opts) end, }, { 'mason-org/mason-lspconfig.nvim', opts = {} }, { 'WhoIsSethDaniel/mason-tool-installer.nvim', opts = {} }, diff --git a/files/.config/nvim/lua/custom/plugins/navigation.lua b/files/.config/nvim/lua/custom/plugins/navigation.lua index c1c68067..759f51a3 100644 --- a/files/.config/nvim/lua/custom/plugins/navigation.lua +++ b/files/.config/nvim/lua/custom/plugins/navigation.lua @@ -22,18 +22,42 @@ return { { 'cbochs/grapple.nvim', - dependencies = { 'nvim-tree/nvim-web-devicons' }, + dependencies = { 'echasnovski/mini.icons' }, opts = { scope = 'git', }, keys = { { 'm', 'Grapple tag', desc = 'grapple: tag file' }, - { '', 'Grapple toggle_tags', desc = 'grapple: open tags' }, - { '1', 'Grapple select index=1', desc = 'grapple: select 1' }, - { '2', 'Grapple select index=2', desc = 'grapple: select 2' }, - { '3', 'Grapple select index=3', desc = 'grapple: select 3' }, - { '4', 'Grapple select index=4', desc = 'grapple: select 4' }, - { '5', 'Grapple select index=5', desc = 'grapple: select 5' }, + { + '', + 'Grapple toggle_tags', + desc = 'grapple: open tags', + }, + { + '1', + 'Grapple select index=1', + desc = 'grapple: select 1', + }, + { + '2', + 'Grapple select index=2', + desc = 'grapple: select 2', + }, + { + '3', + 'Grapple select index=3', + desc = 'grapple: select 3', + }, + { + '4', + 'Grapple select index=4', + desc = 'grapple: select 4', + }, + { + '5', + 'Grapple select index=5', + desc = 'grapple: select 5', + }, { '[g', 'Grapple cycle_tags prev', desc = 'grapple: prev tag' }, { ']g', 'Grapple cycle_tags next', desc = 'grapple: next tag' }, }, diff --git a/files/.config/nvim/lua/custom/plugins/neotree.lua b/files/.config/nvim/lua/custom/plugins/neotree.lua index 9364d5ba..e3bea761 100644 --- a/files/.config/nvim/lua/custom/plugins/neotree.lua +++ b/files/.config/nvim/lua/custom/plugins/neotree.lua @@ -1,8 +1,8 @@ -- Neo-tree is a Neovim plugin to browse the file system -- https://github.com/nvim-neo-tree/neo-tree.nvim local fn, api = vim.fn, vim.api -local highlight = mrl.highlight -local icons = mrl.ui.icons +local highlight = require('highlight') +local icons = require('tools').ui.icons local autocmd = api.nvim_create_autocmd return { @@ -52,7 +52,7 @@ return { -- end, config = function() local symbols = require('lspkind').symbol_map - local lsp_kinds = mrl.ui.lsp.highlights + local lsp_kinds = require('tools').ui.lsp.highlights require('neo-tree').setup({ window = { diff --git a/files/.config/nvim/lua/custom/plugins/noice.lua b/files/.config/nvim/lua/custom/plugins/noice.lua index 6e05ce0d..819c4b29 100644 --- a/files/.config/nvim/lua/custom/plugins/noice.lua +++ b/files/.config/nvim/lua/custom/plugins/noice.lua @@ -1,5 +1,5 @@ local fn = vim.fn -local highlight, L = mrl.highlight, vim.log.levels +local L = vim.log.levels return { 'folke/noice.nvim', @@ -202,6 +202,13 @@ return { }, opts = { skip = true }, }) + table.insert(opts.routes, { + filter = { + event = 'msg_show', + find = 'client%.notify is deprecated', + }, + opts = { skip = true }, + }) -- Focus-aware notifications (send to notify_send when not focused) local focused = true diff --git a/files/.config/nvim/lua/custom/plugins/terminal.lua b/files/.config/nvim/lua/custom/plugins/terminal.lua index 55330d1f..0b1d34ab 100644 --- a/files/.config/nvim/lua/custom/plugins/terminal.lua +++ b/files/.config/nvim/lua/custom/plugins/terminal.lua @@ -1,3 +1,6 @@ +local T = require('tools') +local map = vim.keymap.set + return { 'akinsho/toggleterm.nvim', cmd = { 'ToggleTerm', 'ToggleTermOpenAll', 'ToggleTermCloseAll' }, @@ -31,7 +34,7 @@ return { require('toggleterm').setup(opts) local float_handler = function(term) - if not mrl.falsy(vim.fn.mapcheck('jk', 't')) then + if not T.falsy(vim.fn.mapcheck('jk', 't')) then vim.keymap.del('t', 'jk', { buffer = term.bufnr }) vim.keymap.del('t', '', { buffer = term.bufnr }) end @@ -86,6 +89,6 @@ return { map('n', 'ld', function() lazydocker:toggle() end, { desc = 'toggleterm: toggle lazydocker', }) - mrl.command('Btop', function() btop:toggle() end) + T.command('Btop', function() btop:toggle() end) end, } diff --git a/files/.config/nvim/lua/custom/plugins/todo.lua b/files/.config/nvim/lua/custom/plugins/todo.lua index 01f24cb4..df1653e9 100644 --- a/files/.config/nvim/lua/custom/plugins/todo.lua +++ b/files/.config/nvim/lua/custom/plugins/todo.lua @@ -25,33 +25,34 @@ return { 'folke/trouble.nvim', dependencies = { 'folke/todo-comments.nvim' }, cmd = { 'Trouble' }, + opts = {}, keys = { { 'xx', - 'Trouble', - desc = 'Open/close trouble list', - }, - { - 'xw', - 'TroubleToggle workspace_diagnostics', - desc = 'Open trouble workspace diagnostics', + 'Trouble diagnostics toggle', + desc = 'trouble: workspace diagnostics', }, { 'xd', - 'TroubleToggle document_diagnostics', - desc = 'Open trouble document diagnostics', + 'Trouble diagnostics toggle filter.buf=0', + desc = 'trouble: document diagnostics', }, { 'xq', - 'TroubleToggle quickfix', - desc = 'Open trouble quickfix list', + 'Trouble qflist toggle', + desc = 'trouble: quickfix list', }, { 'xl', - 'TroubleToggle loclist', - desc = 'Open trouble location list', + 'Trouble loclist toggle', + desc = 'trouble: location list', + }, + { + 'xs', + 'Trouble symbols toggle', + desc = 'trouble: document symbols', }, - { 'xt', 'TodoTrouble', desc = 'Open todos in trouble' }, + { 'xt', 'Trouble todo toggle', desc = 'trouble: todos' }, }, }, } diff --git a/files/.config/nvim/lua/tools.lua b/files/.config/nvim/lua/tools.lua index 168f3919..e3b040f2 100644 --- a/files/.config/nvim/lua/tools.lua +++ b/files/.config/nvim/lua/tools.lua @@ -2,31 +2,19 @@ local fn, api, fmt = vim.fn, vim.api, string.format local M = {} --- colors {{{ - -function M.get_hi(name, id) - id = id or 0 - local hi = vim.api.nvim_get_hl(0, { name = name }) - -- hi is a table with bg and fg keys. for those we want to return the hex - -- value with ('#%06x'):format(num) - for k, v in pairs(hi) do - if type(v) == 'number' then hi[k] = ('#%06x'):format(v) end - end - return hi -end - --- }}} +-------------------------------------------------------------------------------- +-- Helpers {{{ -------------------------------------------------------------------------------- --- Commands {{{ -function M.command(name, rhs, opts) - opts = opts or {} - api.nvim_create_user_command(name, rhs, opts) -end +---check if a certain feature/version/commit exists in nvim +---@param feature string +---@return boolean +function M.has(feature) return fn.has(feature) > 0 end ---- Call the given function and use `vim.notify` to notify of any errors ---- this function is a wrapper around `xpcall` which allows having a single ---- error handler for all errors +local LATEST_NIGHTLY_MINOR = 10 +function M.nightly() return vim.version().minor >= LATEST_NIGHTLY_MINOR end + +--- Call the given function and use `vim.notify` to notify of any errors. ---@param msg string ---@param func function ---@param ... any @@ -48,15 +36,58 @@ function M.pcall(msg, func, ...) end, unpack(args)) end -local LATEST_NIGHTLY_MINOR = 10 -function M.nightly() return vim.version().minor >= LATEST_NIGHTLY_MINOR end +--- Require on index — defers the actual require until the first key access. +function M.require_for_later_index(require_path) + return setmetatable({}, { + __index = function(_, key) return require(require_path)[key] end, + __newindex = function(_, key, value) require(require_path)[key] = value end, + }) +end + +--- Require on call — wraps each exported function so the module is only loaded +--- when that function is first invoked. +---@param require_path string +---@return table +function M.require_for_later_call(require_path) + return setmetatable({}, { + __index = function(_, k) + return function(...) return require(require_path)[k](...) end + end, + }) +end + +---Autosize a horizontal split to fit its content. +---@param min_height number +---@param max_height number +function M.adjust_split_height(min_height, max_height) + api.nvim_win_set_height( + 0, + math.max(math.min(fn.line('$'), max_height), min_height) + ) +end + +function M.get_hi(name, id) + id = id or 0 + local hi = vim.api.nvim_get_hl(0, { name = name }) + for k, v in pairs(hi) do + if type(v) == 'number' then hi[k] = ('#%06x'):format(v) end + end + return hi +end + +function M.command(name, rhs, opts) + opts = opts or {} + api.nvim_create_user_command(name, rhs, opts) +end -- }}} +-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --- finders and matchers {{{ +-- Tables & functional {{{ -------------------------------------------------------------------------------- ----Determine if a value of any type is empty + +---Determine if a value of any type is empty / falsy. ---@param item any ---@return boolean? function M.falsy(item) @@ -78,7 +109,27 @@ function M.foreach(callback, list) end end ---- Check if the target matches any item in the list. +function M.fold(callback, list, accum) + accum = accum or {} + for k, v in pairs(list) do + accum = callback(accum, v, k) + assert(accum ~= nil, 'The accumulator must be returned on each iteration') + end + return accum +end + +---@generic T +---@param callback fun(item: T, key: string | number, list: T[]): T +---@param list T[] +---@return T[] +function M.map(callback, list) + return M.fold(function(accum, v, k) + accum[#accum + 1] = callback(v, k, accum) + return accum + end, list, {}) +end + +--- Check if the target matches any item in the list (pattern-aware). ---@param target string ---@param list string[] ---@return boolean @@ -89,7 +140,7 @@ function M.any(target, list) return false end ----Find an item in a list +---Find an item in a list. ---@generic T ---@param matcher fun(arg: T):boolean ---@param haystack T[] @@ -100,9 +151,8 @@ function M.find(matcher, haystack) end end +---Return a table whose missing-key lookup falls back to pattern matching. ---@generic T ----Given a table return a new table which if the key is not found will search ----all the table's keys for a match using `string.match` ---@param map T ---@return T function M.p_table(map) @@ -116,42 +166,16 @@ function M.p_table(map) }) end ----check if a certain feature/version/commit exists in nvim ----@param feature string ----@return boolean -function M.has(feature) return fn.has(feature) > 0 end - -- }}} -------------------------------------------------------------------------------- --- Functional utilities {{{ -function M.fold(callback, list, accum) - accum = accum or {} - for k, v in pairs(list) do - accum = callback(accum, v, k) - assert(accum ~= nil, 'The accumulator must be returned on each iteration') - end - return accum -end - ----@generic T ----@param callback fun(item: T, key: string | number, list: T[]): T ----@param list T[] ----@return T[] -function M.map(callback, list) - return M.fold(function(accum, v, k) - accum[#accum + 1] = callback(v, k, accum) - return accum - end, list, {}) -end - -------------------------------------------------------------------------------- --- Autocommand group {{{ +-- Autocommands {{{ -------------------------------------------------------------------------------- local autocmd_keys = { 'event', 'buffer', 'pattern', 'desc', 'command', 'group', 'once', 'nested' } ---- Validate the keys passed to M.augroup are valid + ---@param name string ---@param command Autocommand local function validate_autocmd(name, command) @@ -168,10 +192,9 @@ local function validate_autocmd(name, command) end end ----Create an autocommand ----returns the group ID so that it can be cleared or manipulated. ----@param name string The name of the autocommand group ----@param ... Autocommand A list of autocommands to create +---Create an autocommand group and return its ID. +---@param name string +---@param ... Autocommand ---@return number function M.augroup(name, ...) local commands = { ... } @@ -184,7 +207,6 @@ function M.augroup(name, ...) for _, autocmd in ipairs(commands) do validate_autocmd(name, autocmd) local is_callback = type(autocmd.command) == 'function' - api.nvim_create_autocmd(autocmd.event, { group = name, pattern = autocmd.pattern, @@ -202,77 +224,834 @@ end -- }}} -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --- Lazy Requires {{{ -------------------------------------------------------------------------------- --- This code comes from TJ the Great: ---- source: https://github.com/tjdevries/lazy-require.nvim +-------------------------------------------------------------------------------- +-- String utilities {{{ +-------------------------------------------------------------------------------- ---- Require on index. ---- ---- Will only require the module after the first index of a module. ---- Only works for modules that export a table. -function M.require_for_later_index(require_path) - return setmetatable({}, { - __index = function(_, key) return require(require_path)[key] end, - __newindex = function(_, key, value) require(require_path)[key] = value end, - }) +---Truncate a string to a maximum display-width, appending an ellipsis. +---@param str string +---@param max_len integer +---@return string +function M.truncate(str, max_len) + assert(str and max_len, 'string and max_len must be provided') + local ellipsis = M.ui.icons.misc.ellipsis + return api.nvim_strwidth(str) > max_len and str:sub(1, max_len) .. ellipsis + or str end ---- Require when an exported method is called. ---- ---- Creates a new function. Cannot be used to compare functions, ---- set new values, etc. Only useful for waiting to do the require until you ---- actually call the code. ---- ---- ```lua ---- -- This is not loaded yet ---- local lazy_mod = lazy.require_on_exported_call('my_module') ---- local lazy_func = lazy_mod.exported_func ---- ---- -- ... some time later ---- lazy_func(42) -- <- Only loads the module now ---- ---- ``` ----@param require_path string ----@return table -function M.require_for_later_call(require_path) - return setmetatable({}, { - __index = function(_, k) - return function(...) return require(require_path)[k](...) end - end, - }) +-- }}} +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- Color utilities {{{ +-------------------------------------------------------------------------------- + +--- Change the brightness of a color, negative numbers darken and positive ones brighten. +---@param color string A hex color (#RRGGBB) +---@param percent number a negative number darkens and a positive one brightens +---@return string +function M.tint(color, percent) + assert( + color and percent, + 'cannot alter a color without specifying a color and percentage' + ) + local r = tonumber(color:sub(2, 3), 16) + local g = tonumber(color:sub(4, 5), 16) + local b = tonumber(color:sub(6), 16) + if not r or not g or not b then return 'NONE' end + local blend = function(component) + component = math.floor(component * (1 + percent)) + return math.min(math.max(component, 0), 255) + end + return fmt('#%02x%02x%02x', blend(r), blend(g), blend(b)) +end + +--- Blend two hex colors using an alpha for the foreground. +--- `alpha = 0` returns bg, `alpha = 1` returns fg. +---@param bg string hex color (#RRGGBB) +---@param fg string hex color (#RRGGBB) +---@param alpha number 0..1 +---@return string +function M.blend(bg, fg, alpha) + assert(bg and fg and alpha ~= nil, 'blend(bg, fg, alpha) requires 3 args') + if type(bg) ~= 'string' or type(fg) ~= 'string' then return 'NONE' end + if bg == 'NONE' or fg == 'NONE' then return 'NONE' end + if not bg:match('^#%x%x%x%x%x%x$') or not fg:match('^#%x%x%x%x%x%x$') then + return 'NONE' + end + alpha = math.min(math.max(alpha, 0), 1) + + local br, bgc, bb = + tonumber(bg:sub(2, 3), 16), + tonumber(bg:sub(4, 5), 16), + tonumber(bg:sub(6, 7), 16) + local fr, fgc, fb = + tonumber(fg:sub(2, 3), 16), + tonumber(fg:sub(4, 5), 16), + tonumber(fg:sub(6, 7), 16) + if not br or not bgc or not bb or not fr or not fgc or not fb then + return 'NONE' + end + + local function mix(b, f) return math.floor((1 - alpha) * b + alpha * f + 0.5) end + return fmt('#%02x%02x%02x', mix(br, fr), mix(bgc, fgc), mix(bb, fb)) +end + +---@param hex string +---@return string? +local function normalize_hex(hex) + if type(hex) ~= 'string' then return nil end + hex = hex:gsub('#', '') + if #hex ~= 6 then return nil end + return hex:lower() +end + +local function rgb_to_hsl(r, g, b) + r, g, b = r / 255, g / 255, b / 255 + local maxc, minc = math.max(r, g, b), math.min(r, g, b) + local h, s, l = 0, 0, (maxc + minc) / 2 + + if maxc ~= minc then + local d = maxc - minc + s = l > 0.5 and d / (2 - maxc - minc) or d / (maxc + minc) + if maxc == r then + h = (g - b) / d + (g < b and 6 or 0) + elseif maxc == g then + h = (b - r) / d + 2 + else + h = (r - g) / d + 4 + end + h = h / 6 + end + + return h, s, l +end + +local function hsl_to_rgb(h, s, l) + local function hue_to_rgb(p, q, t) + if t < 0 then t = t + 1 end + if t > 1 then t = t - 1 end + if t < 1 / 6 then return p + (q - p) * 6 * t end + if t < 1 / 2 then return q end + if t < 2 / 3 then return p + (q - p) * (2 / 3 - t) * 6 end + return p + end + + local r, g, b + if s == 0 then + r, g, b = l, l, l + else + local q = l < 0.5 and l * (1 + s) or l + s - l * s + local p = 2 * l - q + r = hue_to_rgb(p, q, h + 1 / 3) + g = hue_to_rgb(p, q, h) + b = hue_to_rgb(p, q, h - 1 / 3) + end + + return math.floor(r * 255 + 0.5), + math.floor(g * 255 + 0.5), + math.floor(b * 255 + 0.5) +end + +--- Darken or lighten a hex color via HSL lightness while preserving hue/saturation. +--- Positive `lightness_factor` lightens, negative darkens. +---@param hex string hex color (#RRGGBB) +---@param lightness_factor number +---@return string +function M.darken_hsl(hex, lightness_factor) + lightness_factor = lightness_factor or 0.0 + local h = normalize_hex(hex) + if not h then return 'NONE' end + local r = tonumber(h:sub(1, 2), 16) + local g = tonumber(h:sub(3, 4), 16) + local b = tonumber(h:sub(5, 6), 16) + if not r or not g or not b then return 'NONE' end + + local hh, ss, ll = rgb_to_hsl(r, g, b) + + if lightness_factor > 0 then + ll = ll + (1 - ll) * lightness_factor + else + ll = ll * (1 + lightness_factor) + end + + ll = math.min(math.max(ll, 0), 1) + r, g, b = hsl_to_rgb(hh, ss, ll) + return fmt('#%02x%02x%02x', r, g, b) end -- }}} -------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- --- Window utilities {{{ ---- Autosize horizontal split to match its minimum content ---- https://vim.fandom.com/wiki/Automatically_fitting_a_quickfix_window_height ----@param min_height number ----@param max_height number -function M.adjust_split_height(min_height, max_height) - api.nvim_win_set_height( - 0, - math.max(math.min(fn.line('$'), max_height), min_height) - ) +-------------------------------------------------------------------------------- +-- UI {{{ +-------------------------------------------------------------------------------- + +---@class UI +M.ui = {} + +-- Icons {{{ + +M.ui.icons = { + separators = { + left_thin_block = '▏', + right_thin_block = '▕', + vert_bottom_half_block = '▄', + vert_top_half_block = '▀', + right_block = '🮉', + light_shade_block = '░', + right_chubby_block = '▓', + }, + scrollbar = '█', + lsp = { + error = '', -- '✗' + warn = '', -- + info = '󰋼', -- ℹ 󰙎 + hint = '󰌶', -- ⚑ + }, + git = { + add = '󰐗', + mod = '󰻂', + remove = '󰍶', + ignore = '', + rename = '', + untracked = '', + ignored = '󰙦', + unstaged = '󰻂', + staged = '', + conflict = '', + diff = '', + repo = '', + logo = '󰊢', + branch = '', + }, + documents = { + file = '', + files = '', + folder = '', + open_folder = '', + }, + misc = { + plus = '', + ellipsis = '…', + up = '⇡', + down = '⇣', + line = '', + indent = 'Ξ', + tab = '⇥', + bug = '', + question = '', + clock = '', + cmd = '⌘', + lock = '', + shaded_lock = '', + circle = '', + project = '', + dashboard = '', + history = '󰄉', + comment = '󰅺', + robot = '󰚩', + copilot = '', + lightbulb = '󰌵', + search = '󰍉', + code = '', + telescope = '', + gear = '', + chat = '󰭻', + package = '', + list = '', + sign_in = '', + check = '󰄬', + fire = '', + note = '󰎞', + bookmark = '', + pencil = '', + tools = '', + arrow_right = '', + caret_right = '', + chevron_right = '', + double_chevron_right = '»', + table = '', + calendar = '', + block = '▏', + clippy = '', + puzzle = '', + settings = '⚙', + key = '', + config = '', + box = '', + moon = '󰤄', + source = '󰈙', + sleep = '󰒲', + rocket = '', + task = '󰐃', + runtime = '', + }, +} + +-- }}} + +-- Palette {{{ + +-- Mutated in-place so modules holding a reference keep seeing updates. +M.ui.palette = {} + +local function hex_from_hl(name, attr, fallback) + local ok, hl = pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) + if not ok or not hl then return fallback end + local v = hl[attr] + if not v then return fallback end + return ('#%06x'):format(v) +end + +local function palette_tint(color, percent) + local ok = type(color) == 'string' and color:match('^#%x%x%x%x%x%x$') + if not ok then return color end + local r = tonumber(color:sub(2, 3), 16) + local g = tonumber(color:sub(4, 5), 16) + local b = tonumber(color:sub(6, 7), 16) + local function blend(component) + component = math.floor(component * (1 + percent)) + return math.min(math.max(component, 0), 255) + end + return ('#%02x%02x%02x'):format(blend(r), blend(g), blend(b)) +end + +local function get_nightfox_palette() + local ok, nightfox_palette = pcall(require, 'nightfox.palette') + if not ok or not nightfox_palette or not nightfox_palette.load then + return nil + end + return nightfox_palette.load(vim.g.colors_name or 'carbonfox') +end + +local function as_hex(v, fallback) + if type(v) == 'string' then return v end + if type(v) == 'number' then return ('#%06x'):format(v) end + if type(v) == 'table' then + if type(v.base) == 'string' then return v.base end + if vim.is_callable(v) then + local ok, res = pcall(v) + if ok then + if type(res) == 'string' then return res end + if type(res) == 'number' then return ('#%06x'):format(res) end + end + end + end + return fallback +end + +--- Refresh the palette from the active colorscheme. +function M.ui.refresh_palette() + local palette = M.ui.palette + local defaults = { + green = '#98c379', + dark_green = '#10B981', + blue = '#82AAFE', + dark_blue = '#4e88ff', + bright_blue = '#51afef', + teal = '#15AABF', + pale_pink = '#b490c0', + magenta = '#c678dd', + red = '#E06C75', + pale_red = '#E06C75', + light_red = '#c43e1f', + dark_red = '#be5046', + dark_orange = '#FF922B', + bright_yellow = '#FAB005', + light_yellow = '#e5c07b', + whitesmoke = '#9E9E9E', + light_gray = '#626262', + comment_grey = '#5c6370', + grey = '#3E4556', + } + + local pal = get_nightfox_palette() + local derived = {} + + if pal then + derived.green = as_hex(pal.green, defaults.green) + derived.blue = as_hex(pal.blue, defaults.blue) + derived.teal = as_hex(pal.cyan or pal.teal, defaults.teal) + derived.magenta = as_hex(pal.magenta, defaults.magenta) + derived.pale_pink = as_hex(pal.pink or pal.magenta, defaults.pale_pink) + derived.pale_red = as_hex(pal.red, defaults.pale_red) + derived.red = hex_from_hl('GitSignsDelete', 'fg', pal.red or defaults.red) + derived.dark_orange = as_hex(pal.orange, defaults.dark_orange) + derived.bright_yellow = as_hex(pal.yellow, defaults.bright_yellow) + derived.light_yellow = as_hex(pal.yellow, defaults.light_yellow) + derived.comment_grey = as_hex(pal.comment or pal.fg3, defaults.comment_grey) + derived.whitesmoke = as_hex(pal.fg1 or pal.fg0, defaults.whitesmoke) + derived.light_gray = as_hex(pal.fg3, defaults.light_gray) + derived.grey = as_hex(pal.bg3 or pal.bg2, defaults.grey) + else + derived.pale_red = hex_from_hl('DiagnosticError', 'fg', defaults.pale_red) + derived.red = + hex_from_hl('GitSignsDelete', 'fg', derived.pale_red or defaults.red) + derived.dark_orange = + hex_from_hl('DiagnosticWarn', 'fg', defaults.dark_orange) + derived.teal = hex_from_hl('DiagnosticInfo', 'fg', defaults.teal) + derived.bright_blue = + hex_from_hl('DiagnosticHint', 'fg', defaults.bright_blue) + derived.green = hex_from_hl('GitSignsAdd', 'fg', defaults.green) + derived.blue = hex_from_hl('Function', 'fg', defaults.blue) + derived.magenta = hex_from_hl('Statement', 'fg', defaults.magenta) + derived.pale_pink = hex_from_hl('Special', 'fg', defaults.pale_pink) + derived.bright_yellow = + hex_from_hl('WarningMsg', 'fg', defaults.bright_yellow) + derived.light_yellow = derived.bright_yellow + derived.comment_grey = hex_from_hl('Comment', 'fg', defaults.comment_grey) + derived.whitesmoke = hex_from_hl('Normal', 'fg', defaults.whitesmoke) + derived.light_gray = palette_tint(derived.comment_grey, 0.1) + derived.grey = + palette_tint(hex_from_hl('Normal', 'bg', defaults.grey), 0.15) + end + + derived.dark_green = palette_tint(derived.green, -0.25) + derived.dark_blue = palette_tint(derived.blue, -0.25) + derived.light_red = palette_tint(derived.pale_red, -0.15) + derived.dark_red = palette_tint(derived.pale_red, -0.30) + + for k in pairs(palette) do palette[k] = nil end + for k, v in pairs(defaults) do palette[k] = derived[k] or v end + for k, v in pairs(derived) do palette[k] = v end + + -- Keep LSP colors in sync + local lsp = M.ui.lsp + if lsp and lsp.colors then + lsp.colors.error = palette.pale_red + lsp.colors.warn = palette.dark_orange + lsp.colors.hint = palette.bright_blue + lsp.colors.info = palette.teal + end +end + +vim.api.nvim_create_autocmd('ColorScheme', { + group = vim.api.nvim_create_augroup('UIPalette', { clear = true }), + callback = function() M.ui.refresh_palette() end, +}) +vim.schedule(M.ui.refresh_palette) + +-- }}} + +-- LSP {{{ + +M.ui.lsp = { + colors = { + error = M.ui.palette.pale_red, + warn = M.ui.palette.dark_orange, + hint = M.ui.palette.bright_blue, + info = M.ui.palette.teal, + }, + highlights = { + File = 'Directory', + Snippet = 'Label', + Text = '@string', + Method = '@method', + Function = '@function', + Constructor = '@constructor', + Field = '@field', + Variable = '@variable', + Module = '@namespace', + Property = '@property', + Unit = '@constant', + Value = '@variable', + Enum = '@type', + Keyword = '@keyword', + Reference = '@parameter.reference', + Constant = '@constant', + Struct = '@structure', + Event = '@variable', + Operator = '@operator', + Namespace = '@namespace', + Package = '@include', + String = '@string', + Number = '@number', + Boolean = '@boolean', + Array = '@repeat', + Object = '@type', + Key = '@field', + Null = '@symbol', + EnumMember = '@field', + Class = '@lsp.type.class', + Interface = '@lsp.type.interface', + TypeParameter = '@lsp.type.parameter', + }, +} + +-- }}} + +-- Border & floats {{{ + +M.ui.border = 'rounded' + +M.ui.current = { + border = 'rounded', + float_bg = function() + local ok, HL = pcall(require, 'highlight') + if ok and HL and HL.get then return HL.get('Normal', 'bg') end + local ok2, hl = + pcall(vim.api.nvim_get_hl, 0, { name = 'Normal', link = false }) + if ok2 and hl and hl.bg then return ('#%06x'):format(hl.bg) end + return 'NONE' + end, +} + +-- }}} + +-- Decorations {{{ + +---@class Decorations +---@field winbar 'ignore' | boolean +---@field number boolean +---@field statusline 'minimal' | boolean +---@field statuscolumn boolean +---@field colorcolumn boolean | string + +---@alias DecorationType 'statuscolumn'|'winbar'|'statusline'|'number'|'colorcolumn' + +local Preset = {} +function Preset:new(o) + assert(o, 'a preset must be defined') + self.__index = self + return setmetatable(o, self) +end +function Preset:with(o) return vim.tbl_deep_extend('force', self, o) end + +local presets = { + statusline_only = Preset:new({ + number = false, + winbar = false, + colorcolumn = false, + statusline = true, + statuscolumn = false, + }), + minimal_editing = Preset:new({ + number = false, + winbar = true, + colorcolumn = false, + statusline = 'minimal', + statuscolumn = false, + }), + tool_panel = Preset:new({ + number = false, + winbar = false, + colorcolumn = false, + statusline = 'minimal', + statuscolumn = false, + }), +} + +local commit_buffer = + presets.minimal_editing:with({ colorcolumn = '50,72', winbar = false }) + +local buftypes = { + ['quickfix'] = presets.tool_panel, + ['nofile'] = presets.tool_panel, + ['nowrite'] = presets.tool_panel, + ['acwrite'] = presets.tool_panel, + ['terminal'] = presets.tool_panel, + ['.*fugitive.*'] = presets.tool_panel, +} + +local filetypes = M.p_table({ + ['startuptime'] = presets.tool_panel, + ['checkhealth'] = presets.tool_panel, + ['log'] = presets.tool_panel, + ['help'] = presets.tool_panel, + ['^copilot.*'] = presets.tool_panel, + ['dbout'] = presets.tool_panel, + ['dbui'] = presets.tool_panel, + ['dapui'] = presets.tool_panel, + ['minimap'] = presets.tool_panel, + ['Trouble'] = presets.tool_panel, + ['tsplayground'] = presets.tool_panel, + ['list'] = presets.tool_panel, + ['netrw'] = presets.tool_panel, + ['flutter.*'] = presets.tool_panel, + ['NvimTree'] = presets.tool_panel, + ['undotree'] = presets.tool_panel, + ['dap-repl'] = presets.tool_panel:with({ winbar = 'ignore' }), + ['neo-tree'] = presets.tool_panel:with({ winbar = 'ignore' }), + ['toggleterm'] = presets.tool_panel:with({ winbar = 'ignore' }), + ['neotest.*'] = presets.tool_panel, + ['^Neogit.*'] = presets.tool_panel, + ['.*fugitive.*'] = presets.tool_panel, + ['query'] = presets.tool_panel, + ['DiffviewFiles'] = presets.tool_panel, + ['DiffviewFileHistory'] = presets.tool_panel, + ['mail'] = presets.statusline_only, + ['noice'] = presets.statusline_only, + ['diff'] = presets.statusline_only, + ['qf'] = presets.statusline_only, + ['alpha'] = presets.tool_panel:with({ statusline = false }), + ['fugitive'] = presets.statusline_only, + ['startify'] = presets.statusline_only, + ['man'] = presets.minimal_editing, + ['org'] = presets.minimal_editing:with({ winbar = false }), + ['norg'] = presets.minimal_editing:with({ winbar = false }), + ['orgagenda'] = presets.minimal_editing:with({ winbar = false }), + ['markdown'] = presets.minimal_editing, + ['himalaya'] = presets.minimal_editing, + ['gitcommit'] = commit_buffer, + ['NeogitCommitMessage'] = commit_buffer, +}) + +local filenames = M.p_table({ + ['option-window'] = presets.tool_panel, +}) + +M.ui.decorations = {} + +---Get the decoration setting for a buffer. +---@param opts {ft: string?, bt: string?, fname: string?, setting: DecorationType} +---@return {ft: any, bt: any, fname: any}? +function M.ui.decorations.get(opts) + local ft, bt, fname, setting = opts.ft, opts.bt, opts.fname, opts.setting + if (not ft and not bt and not fname) or not setting then return nil end + return { + ft = ft and filetypes[ft] and filetypes[ft][setting], + bt = bt and buftypes[bt] and buftypes[bt][setting], + fname = fname and filenames[fname] and filenames[fname][setting], + } end + +---Set the colorcolumn for a buffer according to filetype/buftype decoration rules. +---@param bufnr integer +---@param fn fun(virtcolumn: string) +function M.ui.decorations.set_colorcolumn(bufnr, fn) + local buf = vim.bo[bufnr] + local decor = M.ui.decorations.get({ + ft = buf.ft, + bt = buf.bt, + setting = 'colorcolumn', + }) + if buf.ft == '' or buf.bt ~= '' or decor.ft == false or decor.bt == false then + return + end + local ccol = decor.ft or decor.bt or '' + local virtcolumn = not M.falsy(ccol) and ccol or '+1' + if vim.is_callable(fn) then fn(virtcolumn) end +end + +-- }}} + -- }}} -------------------------------------------------------------------------------- --- String utilities {{{ +-------------------------------------------------------------------------------- +-- Statusline strings {{{ +-------------------------------------------------------------------------------- ----Truncate a string to a maximum length ----@param str string ----@param max_len integer +---@alias StringComponent {component: string, length: integer, priority: integer} +---@alias Chunks {[1]: string | number, [2]: string, max_size: integer?}[] + +local CLICK_END = '%X' + +local function sl_separator() + return { component = '%=', length = 0, priority = 0 } +end + +local function sl_get_click_start(func_name, id) + if not id then + vim.schedule( + function() + vim.notify_once( + fmt('An ID is needed to enable click handler %s to work', func_name), + vim.log.levels.ERROR, + { title = 'Statusline' } + ) + end + ) + return '' + end + return ('%%%d@%s@'):format(id, func_name) +end + +local function sl_normalize_chunks(chunks) + if type(chunks) ~= 'table' then return end + if vim.islist(chunks) then return chunks end + local keys = {} + for k in pairs(chunks) do + if type(k) == 'number' and k >= 1 and math.floor(k) == k then + keys[#keys + 1] = k + end + end + if #keys == 0 then return end + table.sort(keys) + local dense = {} + for _, k in ipairs(keys) do + local v = chunks[k] + if v ~= nil then dense[#dense + 1] = v end + end + return dense +end + +local function sl_truncate_string(str, max_size) + if not max_size or vim.api.nvim_strwidth(str) < max_size then return str end + local match, count = str:gsub('([\'"]).*%1', '%1…%1') + return count > 0 and match or str:sub(1, max_size - 1) .. '…' +end + +local function sl_chunks_to_string(chunks) + chunks = sl_normalize_chunks(chunks) + if not chunks then return '' end + local strings = {} + for _, item in ipairs(chunks) do + local text, hl = unpack(item) + if not M.falsy(text) then + if type(text) ~= 'string' then text = tostring(text) end + if item.max_size then text = sl_truncate_string(text, item.max_size) end + text = text:gsub('%%', '%%%1') + strings[#strings + 1] = not M.falsy(hl) + and ('%%#%s#%s%%*'):format(hl, text) + or text + end + end + return table.concat(strings, '') +end + +--- @class ComponentOpts +--- @field [1] Chunks +--- @field priority number +--- @field click string +--- @field before string +--- @field after string +--- @field id number +--- @field max_size integer +--- @field cond boolean | number | table | string + +local function sl_component(opts) + assert(opts, 'component options are required') + if opts.cond ~= nil and M.falsy(opts.cond) then return end + + local item = sl_normalize_chunks(opts[1]) + if not item then + error( + fmt( + 'component options are required but got %s instead', + vim.inspect(opts[1]) + ) + ) + end + + if not opts.priority then opts.priority = 10 end + + local item_str = sl_chunks_to_string(item) + if vim.api.nvim_strwidth(item_str) == 0 then return end + + local click_start = opts.click + and sl_get_click_start(opts.click, tostring(opts.id)) + or '' + local click_end = opts.click and CLICK_END or '' + local component_str = + table.concat({ click_start, '', item_str, '', click_end }) + return { + component = component_str, + length = api.nvim_eval_statusline(component_str, { maxwidth = 0 }).width, + priority = opts.priority, + } +end + +local function sl_sum_lengths(list) + return M.fold( + function(acc, item) return acc + (item.length or 0) end, + list, + 0 + ) +end + +local function sl_is_lowest(item, lowest) + if not lowest or not lowest.length then return true end + if not item.priority or not item.length then return false end + if item.priority == lowest.priority then + return item.length > lowest.length + end + return item.priority > lowest.priority +end + +local function sl_prioritize(statusline, space, length) + length = length or sl_sum_lengths(statusline) + if length <= space then return statusline end + local lowest, index_to_remove + for idx, c in ipairs(statusline) do + if sl_is_lowest(c, lowest) then + lowest, index_to_remove = c, idx + end + end + table.remove(statusline, index_to_remove) + return sl_prioritize(statusline, space, length - lowest.length) +end + +M.strings = {} + +--- Creates a spacer statusline component. +---@param size integer? +---@param opts table? +---@return ComponentOpts? +function M.strings.spacer(size, opts) + opts = opts or {} + local filler = opts.filler or ' ' + local priority = opts.priority or 0 + if not size or size < 1 then return end + return { + { { string.rep(filler, size) } }, + priority = priority, + before = '', + after = '', + } +end + +--- Render a list of sections into a statusline string, dropping lowest-priority +--- components when space is constrained. +---@param sections ComponentOpts[][] +---@param available_space number? ---@return string -function M.truncate(str, max_len) - assert(str and max_len, 'string and max_len must be provided') - local ellipsis = require('custom.ui').icons.misc.ellipsis - return api.nvim_strwidth(str) > max_len and str:sub(1, max_len) .. ellipsis - or str +function M.strings.display(sections, available_space) + local components = M.fold(function(acc, section, count) + if #section == 0 then + table.insert(acc, sl_separator()) + return acc + end + M.foreach(function(args, index) + if not args then return end + local ok, str = M.pcall('Error creating component', sl_component, args) + if not ok then return end + table.insert(acc, str) + if #section == index and count ~= #sections then + table.insert(acc, sl_separator()) + end + end, section) + return acc + end, sections) + + local items = available_space and sl_prioritize(components, available_space) + or components + local str = vim.tbl_map(function(item) return item.component end, items) + return table.concat(str) end + +--- Section helper: collects StringComponents and supports `+` concatenation. +---@class Section +---@field new fun(...:StringComponent[]): Section +local section = {} +function section:new(...) + local o = { ... } + self.__index = self + self.__add = function(l, r) + local rt = { unpack(l) } + for _, v in ipairs(r) do + rt[#rt + 1] = v + end + return rt + end + return setmetatable(o, self) +end +M.strings.section = section + -- }}} +-------------------------------------------------------------------------------- return M + +-- vim:fdm=marker diff --git a/files/.config/nvim/plugin/autocommands.lua b/files/.config/nvim/plugin/autocommands.lua index 3aec14e4..13074d71 100644 --- a/files/.config/nvim/plugin/autocommands.lua +++ b/files/.config/nvim/plugin/autocommands.lua @@ -1,78 +1,7 @@ -if not mrl then return end - --- Helper to safely call augroup, deferring if not available yet -local function augroup(name, ...) - local args = { ... } - if mrl and mrl.augroup then - return mrl.augroup(name, unpack(args)) - else - -- Defer until augroup is available - vim.schedule(function() - if mrl and mrl.augroup then mrl.augroup(name, unpack(args)) end - end) - end -end - -local fn, api, v, env, cmd, fmt = - vim.fn, vim.api, vim.v, vim.env, vim.cmd, string.format - --- ----------------------------------------------------------------------------- --- Performance-sensitive: avoid heavy Vimscript in init.lua --- ----------------------------------------------------------------------------- +local augroup = require('tools').augroup --- Highlight lines starting with '##' (headings) using a lightweight sign group. --- This replaces the old Vimscript that unplaced/placed signs across 1000 ids. --- do --- local group = api.nvim_create_augroup('HeadingLineSign', { clear = true }) --- local sign_name = 'highlightline' --- local sign_group = 'headingline' --- local debounce_ms = 120 --- local pending = {} ---@type table --- --- -- Define sign once (safe to call multiple times). --- pcall(fn.sign_define, sign_name, { linehl = 'Match' }) --- --- local function update(bufnr) --- if not api.nvim_buf_is_valid(bufnr) then return end --- if vim.bo[bufnr].buftype ~= '' then return end --- --- local ft = vim.bo[bufnr].filetype --- if ft ~= 'markdown' and ft ~= 'org' and ft ~= 'norg' then return end --- --- pcall(fn.sign_unplace, sign_group, { buffer = bufnr }) --- --- local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) --- for i, line in ipairs(lines) do --- if line:match('^%s*##') then --- -- id=0 lets Vim allocate IDs; placing is scoped to sign_group+buffer. --- pcall(fn.sign_place, 0, sign_group, sign_name, bufnr, { --- lnum = i, --- priority = 10, --- }) --- end --- end --- end --- --- local function schedule(bufnr) --- if pending[bufnr] then --- pending[bufnr]:stop() --- pending[bufnr]:close() --- pending[bufnr] = nil --- end --- pending[bufnr] = vim.defer_fn(function() --- pending[bufnr] = nil --- update(bufnr) --- end, debounce_ms) --- end --- --- api.nvim_create_autocmd( --- { 'BufWinEnter', 'TextChanged', 'TextChangedI', 'TextChangedP' }, --- { --- group = group, --- callback = function(args) schedule(args.buf) end, --- } --- ) --- end +local fn, api, v, cmd = + vim.fn, vim.api, vim.v, vim.cmd -- Better terminal UX inside fzf-lua: allow Esc to abort. api.nvim_create_autocmd('FileType', { @@ -94,7 +23,6 @@ do local function disable(buf) if not api.nvim_buf_is_valid(buf) then return end - -- Common plugin conventions: vim.b[buf].miniindentscope_disable = true vim.b[buf].ibl_disable = true vim.b[buf].indent_blankline_enabled = false @@ -105,7 +33,6 @@ do callback = function(args) disable(args.buf) end, }) - -- Some terminal plugins use a FileType instead of buftype checks. api.nvim_create_autocmd('FileType', { group = group, pattern = { 'toggleterm', 'terminal' }, @@ -130,23 +57,18 @@ do api.nvim_win_call(win, function() vim.opt_local.winhighlight:append({ - -- Force terminal windows to inherit the main editor background. Normal = 'Normal', NormalNC = 'NormalNC', - -- For floating terminal windows, ensure the float "Normal" maps to Normal too. NormalFloat = 'Normal', }) end) end - -- Apply early, but also re-apply on WinEnter since some plugins set winhighlight - -- after opening (Sidekick/toggleterm/etc). api.nvim_create_autocmd( { 'TermOpen', 'BufWinEnter', 'BufEnter', 'WinEnter' }, { group = group, callback = function(args) - -- Defer one tick so we win over later winhighlight setters. vim.schedule(function() apply_terminal_winhighlight(args.buf) end) end, } @@ -162,20 +84,15 @@ do end -- Highlight when yanking (copying) text --- Try it with `yap` in normal mode --- See `:help vim.highlight.on_yank()` vim.api.nvim_create_autocmd('TextYankPost', { desc = 'Highlight when yanking (copying) text', - group = vim.api.nvim_create_augroup( - 'kickstart-highlight-yank', - { clear = true } - ), + group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }), callback = function() vim.highlight.on_yank() end, }) local function stop_hl() if v.hlsearch == 0 or api.nvim_get_mode().mode ~= 'n' then return end - vim.api.nvim_feedkeys(vim.keycode('(StopHL)'), 'm', false) + vim.cmd('nohlsearch') end local function hl_search() @@ -184,7 +101,6 @@ local function hl_search() local ok, match = pcall(fn.matchstrpos, curr_line, fn.getreg('/'), 0) if not ok then return end local _, p_start, p_end = unpack(match) - -- if the cursor is in a search result, leave highlighting on if col < p_start or col > p_end then stop_hl() end end @@ -202,66 +118,20 @@ augroup('VimrcIncSearchHighlight', { end, }, { event = 'RecordingEnter', - command = function() vim.o.hlsearch = false end, -}, { - event = 'RecordingLeave', - command = function() vim.o.hlsearch = true end, -}) - --- Search highlighting {{{ - ----------------------------------------------------------------------------------------------------- --- HLSEARCH ----------------------------------------------------------------------------------------------------- --- In order to get hlsearch working the way I like i.e. on when using /,?,N,n,*,#, etc. and off when --- When I'm not using them, I need to set the following: --- The mappings below are essentially faked user input this is because in order to automatically turn off --- the search highlight just changing the value of 'hlsearch' inside a function does not work --- read `:h nohlsearch`. So to have this workaround I check that the current mouse position is not a search --- result, if it is we leave highlighting on, otherwise I turn it off on cursor moved by faking my input --- using the expr mappings below. --- --- This is based on the implementation discussed here: --- https://github.com/neovim/neovim/issues/5581 - -vim.keymap.set( - { 'n', 'v', 'o', 'i', 'c' }, - '(StopHL)', - 'execute("nohlsearch")[-1]', - { expr = true } -) - -local function stop_hl() - if v.hlsearch == 0 or api.nvim_get_mode().mode ~= 'n' then return end - api.nvim_feedkeys(vim.keycode('(StopHL)'), 'm', false) -end - --- REMOVED DUPLICATE: This function and autocmd group was already defined above (lines 180-209) --- Duplicate removed to fix scrolling lag caused by double CursorMoved events - --- }}} - --- Recording macro {{{ - -vim.api.nvim_create_autocmd('RecordingEnter', { - pattern = '*', - callback = function() + command = function() + vim.o.hlsearch = false vim.g.macro_recording = 'macro @' .. vim.fn.reg_recording() vim.cmd('redrawstatus') end, -}) - --- Autocmd to track the end of macro recording -vim.api.nvim_create_autocmd('RecordingLeave', { - pattern = '*', - callback = function() +}, { + event = 'RecordingLeave', + command = function() + vim.o.hlsearch = true vim.g.macro_recording = '' vim.cmd('redrawstatus') end, }) --- }}} - augroup('UpdateVim', { event = { 'FocusLost' }, pattern = { '*' }, @@ -269,7 +139,7 @@ augroup('UpdateVim', { }, { event = { 'VimResized' }, pattern = { '*' }, - command = 'wincmd =', -- Make windows equal size when vim resizes + command = 'wincmd =', }) -- ----------------------------------------------------------------------------- @@ -320,19 +190,16 @@ do if fn.winnr('$') ~= 1 then api.nvim_win_close(0, true) else - -- If it's the last window, delete the buffer instead. pcall(cmd.bdelete, { 0, bang = true }) end end - -- Auto open quickfix after grep-like commands. api.nvim_create_autocmd('QuickFixCmdPost', { group = group, pattern = '*grep*', command = 'cwindow', }) - -- Close certain filetypes by pressing q. api.nvim_create_autocmd('FileType', { group = group, callback = function(args) @@ -355,7 +222,6 @@ do end, }) - -- Close quickfix buffer if it's the only remaining window. api.nvim_create_autocmd('BufEnter', { group = group, callback = function() @@ -365,7 +231,6 @@ do end, }) - -- Close location list when quitting a window. api.nvim_create_autocmd('QuitPre', { group = group, nested = true, @@ -400,7 +265,6 @@ do local path = api.nvim_buf_get_name(bufnr) if path == '' then return false end - -- Always allow formatting in your own code/config. local allow_prefixes = { vim.g.personal_directory, vim.g.work_directory, @@ -410,20 +274,16 @@ do } for _, p in ipairs(allow_prefixes) do if p and startswith(path, p) then - -- Don't blanket-allow HOME; only use it to prevent disabling on empty vars. - -- If you want stricter behavior, remove HOME from this list. if p ~= vim.env.HOME then return false end end end - -- Disable formatting in runtime/plugin directories. if vim.env.VIMRUNTIME and startswith(path, vim.env.VIMRUNTIME) then return true end for _, dir in ipairs(vim.split(vim.o.runtimepath, ',', { plain = true })) do if dir ~= '' and startswith(path, dir) then - -- Never disable in your actual config path. if vim.g.vim_dir and startswith(path, vim.g.vim_dir) then return false end @@ -493,7 +353,7 @@ do }) end --- Auto create dir when saving a file, in case some intermediate directory does not exist +-- Auto create dir when saving a file vim.api.nvim_create_autocmd({ 'BufWritePre' }, { group = vim.api.nvim_create_augroup('AutoCreateDir', { clear = true }), callback = function(event) @@ -511,136 +371,7 @@ vim.api.nvim_create_autocmd({ 'FocusGained', 'TermClose', 'TermLeave' }, { end, }) --- LSP inline diagnostics {{{ --- --- local function best_diagnostic(diagnostics) --- if vim.tbl_isempty(diagnostics) then --- return --- end --- --- local best = nil --- local line_diagnostics = {} --- local line_nr = vim.api.nvim_win_get_cursor(0)[1] - 1 --- --- for k, v in pairs(diagnostics) do --- if v.lnum == line_nr then --- line_diagnostics[k] = v --- end --- end --- --- for _, diagnostic in ipairs(line_diagnostics) do --- if best == nil then --- best = diagnostic --- elseif diagnostic.severity < best.severity then --- best = diagnostic --- end --- end --- --- return best --- end --- --- local function current_line_diagnostics() --- local bufnr = 0 --- local line_nr = vim.api.nvim_win_get_cursor(0)[1] - 1 --- local opts = { ["lnum"] = line_nr } --- --- return vim.diagnostic.get(bufnr, opts) --- end --- --- local signs = { --- Error = " ", --- Warn = " ", --- Hint = " ", --- Info = " ", --- } --- --- local virt_handler = vim.diagnostic.handlers.virtual_text --- local ns = vim.api.nvim_create_namespace "current_line_virt" --- local severity = vim.diagnostic.severity --- local virt_options = { --- prefix = "", --- format = function(diagnostic) --- local message = vim.split(diagnostic.message, "\n")[1] --- --- if diagnostic.severity == severity.ERROR then --- return signs.Error .. message --- elseif diagnostic.severity == severity.INFO then --- return signs.Info .. message --- elseif diagnostic.severity == severity.WARN then --- return signs.Warn .. message --- elseif diagnostic.severity == severity.HINT then --- return signs.Hint .. message --- else --- return message --- end --- end, --- } --- --- vim.diagnostic.handlers.current_line_virt = { --- show = function(_, bufnr, diagnostics, _) --- local diagnostic = best_diagnostic(diagnostics) --- if not diagnostic then --- return --- end --- --- local filtered_diagnostics = { diagnostic } --- --- pcall( --- virt_handler.show, --- ns, --- bufnr, --- filtered_diagnostics, --- { virtual_text = virt_options } --- ) --- end, --- hide = function(_, bufnr) --- bufnr = bufnr or vim.api.nvim_get_current_buf() --- virt_handler.hide(ns, bufnr) --- end, --- } --- --- vim.diagnostic.config { --- float = { source = "always" }, --- signs = false, --- virtual_text = false, --- severity_sort = true, --- current_line_virt = true, --- } --- --- vim.api.nvim_create_augroup("lsp_diagnostic_current_line", { --- clear = true, --- }) --- --- --- vim.api.nvim_clear_autocmds { --- buffer = bufnr, --- group = "lsp_diagnostic_current_line", --- } --- --- vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { --- group = "lsp_diagnostic_current_line", --- buffer = bufnr, --- callback = function() --- vim.diagnostic.handlers.current_line_virt.show( --- nil, --- 0, --- current_line_diagnostics(), --- nil --- ) --- end, --- }) --- --- vim.api.nvim_create_autocmd("CursorMoved", { --- group = "lsp_diagnostic_current_line", --- buffer = bufnr, --- callback = function() --- vim.diagnostic.handlers.current_line_virt.hide(nil, nil) --- end, --- }) - --- }}} - --- Cursorline only in active window (Folke's pattern) +-- Cursorline only in active window vim.api.nvim_create_autocmd({ 'InsertLeave', 'WinEnter' }, { callback = function() if vim.w.auto_cursorline then @@ -658,34 +389,41 @@ vim.api.nvim_create_autocmd({ 'InsertEnter', 'WinLeave' }, { end, }) --- Disable statuscolumn and other columns in floating windows +-- Disable statuscolumn/signcolumn/foldcolumn in floating windows and tool panels. do local group = vim.api.nvim_create_augroup('DisableColumnsInFloats', { clear = true }) - local function disable_columns_in_float() - local win = vim.api.nvim_get_current_win() - local win_config = vim.api.nvim_win_get_config(win) - if win_config.relative ~= '' then - -- This is a floating window - disable all column decorations - vim.wo[win].statuscolumn = '' - vim.wo[win].signcolumn = 'no' - vim.wo[win].foldcolumn = '0' - vim.wo[win].number = false - vim.wo[win].relativenumber = false - end + local no_number_filetypes = { + 'lazy', 'mason', 'noice', 'notify', 'trouble', 'aerial', + 'dap-repl', 'dapui_console', 'dapui_watches', 'dapui_stacks', + 'dapui_breakpoints', 'dapui_scopes', + } + + local function disable_decoration_columns(win) + vim.wo[win].statuscolumn = '' + vim.wo[win].signcolumn = 'no' + vim.wo[win].foldcolumn = '0' end - -- Apply on multiple events to catch Mason, Lazy, and other plugin floats - vim.api.nvim_create_autocmd({ 'WinEnter', 'BufWinEnter', 'FileType' }, { + vim.api.nvim_create_autocmd('WinEnter', { group = group, - callback = disable_columns_in_float, + callback = function() + local win = vim.api.nvim_get_current_win() + if vim.api.nvim_win_get_config(win).relative ~= '' then + disable_decoration_columns(win) + end + end, }) - -- Also apply with a slight delay to override plugin settings - vim.api.nvim_create_autocmd('WinEnter', { + vim.api.nvim_create_autocmd('FileType', { group = group, + pattern = no_number_filetypes, callback = function() - vim.schedule(disable_columns_in_float) + vim.opt_local.statuscolumn = '' + vim.opt_local.signcolumn = 'no' + vim.opt_local.foldcolumn = '0' + vim.opt_local.number = false + vim.opt_local.relativenumber = false end, }) end @@ -697,7 +435,6 @@ do vim.api.nvim_create_autocmd('WinLeave', { group = group, callback = function() - -- Only dim non-floating windows local win_config = vim.api.nvim_win_get_config(0) if win_config.relative == '' then vim.cmd [[setlocal winhl=CursorLine:CursorLineNC]] @@ -713,4 +450,38 @@ do }) end --- }} +-- ----------------------------------------------------------------------------- +-- Sidebar panels: strip line numbers and remap highlights to panel groups +-- ----------------------------------------------------------------------------- + +do + local sidebar_fts = { + 'undotree', + 'diff', + 'Outline', + 'dbui', + 'neotest-summary', + 'fugitive', + 'AvanteSidebar', + 'AvanteInput', + 'Avante', + 'AvanteSelectedFiles', + } + + local function on_sidebar_enter() + vim.opt_local.number = false + vim.opt_local.relativenumber = false + vim.opt_local.winhighlight:append({ + Normal = 'PanelDarkBackground', + EndOfBuffer = 'PanelDarkBackground', + SignColumn = 'PanelDarkBackground', + WinSeparator = 'PanelWinSeparator', + }) + end + + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('SidebarPanelHighlights', { clear = true }), + pattern = sidebar_fts, + callback = on_sidebar_enter, + }) +end diff --git a/files/.config/nvim/plugin/colors.lua b/files/.config/nvim/plugin/colors.lua index 3df878ea..2cadd7b0 100644 --- a/files/.config/nvim/plugin/colors.lua +++ b/files/.config/nvim/plugin/colors.lua @@ -1,43 +1,5 @@ -if not mrl then return end - --- Helper to safely call augroup, deferring if not available yet -local function augroup(name, ...) - local args = { ... } - if mrl and mrl.augroup then - return mrl.augroup(name, unpack(args)) - else - vim.schedule(function() - if mrl and mrl.augroup then mrl.augroup(name, unpack(args)) end - end) - end -end - -local highlight = mrl.highlight - -local function blend_colors(fg_hex, bg_hex, alpha) - alpha = alpha or 0.5 - - -- Remove '#' if present - fg_hex = fg_hex:gsub('#', '') - bg_hex = bg_hex:gsub('#', '') - - -- Parse hex colors to RGB - local fg_r = tonumber(fg_hex:sub(1, 2), 16) - local fg_g = tonumber(fg_hex:sub(3, 4), 16) - local fg_b = tonumber(fg_hex:sub(5, 6), 16) - - local bg_r = tonumber(bg_hex:sub(1, 2), 16) - local bg_g = tonumber(bg_hex:sub(3, 4), 16) - local bg_b = tonumber(bg_hex:sub(5, 6), 16) - - -- Alpha blend: out = fg * alpha + bg * (1 - alpha) - local out_r = math.floor(fg_r * alpha + bg_r * (1 - alpha) + 0.5) - local out_g = math.floor(fg_g * alpha + bg_g * (1 - alpha) + 0.5) - local out_b = math.floor(fg_b * alpha + bg_b * (1 - alpha) + 0.5) - - -- Convert back to hex - return string.format('#%02x%02x%02x', out_r, out_g, out_b) -end +local highlight = require('highlight') +local augroup = require('tools').augroup local function general_overrides(dim_factor) local is_dark = vim.g.high_contrast_theme @@ -45,10 +7,11 @@ local function general_overrides(dim_factor) local normal_fg = highlight.get('Normal', 'fg', '#ffffff') local bg_color = highlight.tint(normal_bg, -dim_factor) local bg_color2 = highlight.tint(normal_bg, 0.5 * dim_factor) - local stl_bg = highlight.darken_hsl(normal_bg, -0.20) + -- local stl_bg = highlight.darken_hsl(normal_bg, -0.20) + local stl_bg = highlight.get('Statusline', 'bg') local float_bg = highlight.darken_hsl(normal_bg, 0) local popup_bg = highlight.darken_hsl(normal_bg, -0.20) - local pal = (mrl.ui and mrl.ui.palette) or {} + local pal = require('tools').ui.palette or {} -- Deleted-line diff background should track the theme's GitSignsDelete color. -- Fall back to palette red/pale_red if GitSignsDelete isn't available yet. local diff_delete_fg = highlight.get( @@ -112,10 +75,10 @@ local function general_overrides(dim_factor) -- end highlight.all({ -- Status line - { PanelSt = { link = 'StatusLine' } }, + { PanelSt = { link = 'Normal' } }, { TabLineSel = { fg = { from = 'Normal' }, bg = '#ff0000' } }, -- { StatuslineNC = { fg = { from = 'Normal', attr = 'fg' }, fg = bg_color } }, - { StatusLine = { fg = { from = 'Normal', attr = 'fg' }, bg = stl_bg } }, + -- { StatusLine = { fg = { from = 'Normal', attr = 'fg' }, bg = stl_bg } }, -- { StatusLineNC = { bg = { from = 'Normal', attr = 'fg' }, fg = bg_color } }, { StDevEnv = { @@ -562,107 +525,6 @@ local function general_overrides(dim_factor) -- {DiagnosticSignInfoLine = { bg = mrl.get_hi('SignColumn').bg }}, -- { DiagnosticSignInfo = { bg = mrl.get_hi('Normal').bg, fg = mrl.get_hi('MoreMsg').fg } }, -- }}} - -- FzfLua {{{ - -- FzfLuaNormal Normal hls.normal Main win fg/bg - { - FzfLuaNormal = { - link = 'NormalFloat', - }, - }, - { - FzfLuaTitle = { - link = 'NormalFloat', - }, - }, - { - FzfLuaTitle = { - link = 'NormalFloat', - }, - }, - { - -- Make fzf-lua borders follow FloatBorder exactly. - FzfLuaBorder = { clear = true, link = 'FloatBorder' }, - }, - { - FzfLuaPreviewNormal = { - link = 'NormalFloat', - }, - }, - { - FzfLuaPreviewBorder = { - clear = true, - link = 'FloatBorder', - }, - }, - { - FzfLuaPreviewTitle = { - link = 'NormalFloat', - }, - }, - { - FzfLuaHelpNormal = { - link = 'NormalFloat', - }, - }, - { - FzfLuaHelpBorder = { - clear = true, - link = 'FloatBorder', - }, - }, - -- FzfLuaBorder Normal hls.border Main win border - -- FzfLuaTitle FzfLuaNormal hls.title Main win title - -- FzfLuaBackdrop *bg=Black hls.backdrop Backdrop color - -- FzfLuaPreviewNormal FzfLuaNormal hls.preview_normal Builtin preview fg/bg - -- FzfLuaPreviewBorder FzfLuaBorder hls.preview_border Builtin preview border - -- FzfLuaPreviewTitle FzfLuaTitle hls.preview_title Builtin preview title - -- { FzfLuaCursor = { bg = "#ff00ff", fg = "#00ff00"} }, - -- { FzfLuaCursor = { bg = "#ff00ff" , fg = { from = 'Normal', attr = 'fg' } } }, - -- { FzfLuaCursorLine = { bg = "#ffff00", fg = "#00ff00"} }, - -- FzfLuaCursor Cursor hls.cursor Builtin preview Cursor - -- FzfLuaCursorLine CursorLine hls.cursorline Builtin preview Cursorline - -- FzfLuaCursorLineNr CursorLineNr hls.cursorlinenr Builtin preview CursorLineNr - -- FzfLuaSearch IncSearch hls.search Builtin preview search matches - -- FzfLuaScrollBorderEmpty FzfLuaBorder hls.scrollborder_e Builtin preview border scroll empty - -- FzfLuaScrollBorderFull FzfLuaBorder hls.scrollborder_f Builtin preview border scroll full - -- FzfLuaScrollFloatEmpty PmenuSbar hls.scrollfloat_e Builtin preview float scroll empty - -- FzfLuaScrollFloatFull PmenuThumb hls.scrollfloat_f Builtin preview float scroll full - -- FzfLuaHelpNormal FzfLuaNormal hls.help_normal Help win fg/bg - -- FzfLuaHelpBorder FzfLuaBorder hls.help_border Help win border - -- FzfLuaHeaderBind *BlanchedAlmond hls.header_bind Header keybind - -- FzfLuaHeaderText *Brown1 hls.header_text Header text - -- FzfLuaPathColNr *CadetBlue1 hls.path_colnr Path col nr (lines,qf,lsp,diag) - -- FzfLuaPathLineNr *LightGreen hls.path_linenr Path line nr (lines,qf,lsp,diag) - -- FzfLuaBufName *LightMagenta hls.buf_name Buffer name (lines) - -- FzfLuaBufNr *BlanchedAlmond hls.buf_nr Buffer number (all buffers) - -- FzfLuaBufFlagCur *Brown1 hls.buf_flag_cur Buffer line (buffers) - -- FzfLuaBufFlagAlt *CadetBlue1 hls.buf_flag_alt Buffer line (buffers) - -- FzfLuaTabTitle *LightSkyBlue1 hls.tab_title Tab title (tabs) - -- FzfLuaTabMarker *BlanchedAlmond hls.tab_marker Tab marker (tabs) - -- FzfLuaDirIcon Directory hls.dir_icon Paths directory icon - -- FzfLuaDirPart Comment hls.dir_part Path formatters directory hl group - -- FzfLuaFilePart @none hls.file_part Path formatters file hl group - -- FzfLuaLiveSym *Brown1 hls.live_sym LSP live symbols query match - -- fzf-lua's internal "fzf" UI groups (make sure no debug colors leak). - { FzfLuaFzfNormal = { clear = true, link = 'FzfLuaNormal' } }, - -- { FzfLuaFzfCursorLine = { bg = "#ffff00", fg = "#00ff00"} }, - -- { FzfLuaFzfPrompt = { bg = "#ff0000", fg = "#00ff00"} }, - { ['@fzf.normal'] = { clear = true, link = 'FzfLuaNormal' } }, - -- FzfLuaFzfNormal FzfLuaNormal fzf.normal fzf's fg|bg - -- FzfLuaFzfCursorLine FzfLuaCursorLine fzf.cursorline fzf's fg+|bg+ - -- FzfLuaFzfMatch Special fzf.match fzf's hl+ - -- FzfLuaFzfBorder FzfLuaBorder fzf.border fzf's border - -- FzfLuaFzfScrollbar FzfLuaFzfBorder fzf.scrollbar fzf's scrollbar - -- FzfLuaFzfSeparator FzfLuaFzfBorder fzf.separator fzf's separator - -- FzfLuaFzfGutter FzfLuaNormal fzf.gutter fzf's gutter (hl bg is used) - -- FzfLuaFzfHeader FzfLuaTitle fzf.header fzf's header - -- FzfLuaFzfInfo NonText fzf.info fzf's info - -- FzfLuaFzfPointer Special fzf.pointer fzf's pointer - -- FzfLuaFzfMarker FzfLuaFzfPointer fzf.marker fzf's marker - -- FzfLuaFzfSpinner FzfLuaFzfPointer fzf.spinner fzf's spinner - -- FzfLuaFzfPrompt Special fzf.prompt fzf's prompt - -- FzfLuaFzfQuery FzfLuaNormal fzf.query fzf's header - -- }}} }) end @@ -685,61 +547,6 @@ local function set_sidebar_highlight(dim_factor) }, { PanelStNC = { link = 'PanelWinSeparator' } }, { PanelSt = { bg = { from = 'Normal', attr = 'bg' } } }, - - -- Avanté Panel colors - { - AvanteSidebarNormal = { link = 'PanelDarkBackground' }, - }, - { - AvanteTitle = { - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - -- fg = '#00ff00', - }, - }, - { - AvanteReversedTitle = { - fg = { from = 'PanelDarkBackground', attr = 'bg' }, - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - }, - }, - { - AvanteSubtitle = { - -- fg = '#ff0000', - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - }, - }, - { - AvanteThirdTitle = { - -- fg = '#ff0000', - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - }, - }, - { - AvanteReversedSubtitle = { link = 'AvanteReversedTitle' }, - }, - { - AvanteReversedThirdTitle = { link = 'AvanteReversedTitle' }, - }, - { - AvanteSidebarWinHorizontalSeparator = { - -- bg = '#ff0000', - -- fg = '#ff0000', - fg = { from = 'PanelDarkBackground', attr = 'bg' }, - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - }, - }, - { - AvanteSidebarWinSeparator = { - -- bg = '#ff0000', - -- fg = '#ff0000', - -- fg = { from = 'PanelDarkBackground', attr = 'bg' }, - -- bg = { from = 'PanelDarkBackground', attr = 'bg' }, - bg = { from = 'PanelDarkBackground', attr = 'bg' }, - -- fg = { from = 'PanelDarkBackground', attr = 'bg' }, - -- inherit = 'PanelBackground', - fg = { from = 'WinSeparator' }, - }, - }, }) end @@ -756,19 +563,46 @@ local sidebar_fts = { 'AvanteSelectedFiles', } +local function on_diffview_enter() + local stl = vim.api.nvim_get_hl(0, { name = 'StatusLine', link = false }) + local norm = vim.api.nvim_get_hl(0, { name = 'Normal', link = false }) + local bg = stl.bg and ('#%06x'):format(stl.bg) or 'NONE' + local fg = norm.fg and ('#%06x'):format(norm.fg) or 'NONE' + local nbg = norm.bg and ('#%06x'):format(norm.bg) or 'NONE' + + -- Use our own hl groups that diffview doesn't touch + vim.api.nvim_set_hl(0, 'DvPanelBg', { bg = bg, fg = fg }) + vim.api.nvim_set_hl(0, 'DvPanelEob', { bg = bg }) + vim.api.nvim_set_hl(0, 'DvPanelCursor', { bg = nbg }) + + -- Point diffview windows directly at our groups via winhighlight + local winhl = table.concat({ + 'Normal:DvPanelBg', + 'EndOfBuffer:DvPanelEob', + 'SignColumn:DvPanelBg', + 'CursorLine:DvPanelCursor', + 'WinSeparator:DiffviewWinSeparator', + }, ',') + + -- Apply to the diffview tab (it opens in a new tabpage) + local tabpage = vim.api.nvim_get_current_tabpage() + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tabpage)) do + local buf = vim.api.nvim_win_get_buf(win) + local ft = vim.bo[buf].filetype + if ft == 'DiffviewFiles' or ft == 'DiffviewFileHistory' then + vim.api.nvim_set_option_value('winhighlight', winhl, { win = win }) + end + end +end + local function on_sidebar_enter() -- Panels should feel like sidebars: no line numbers. vim.opt_local.number = false vim.opt_local.relativenumber = false vim.opt_local.winhighlight:append({ Normal = 'PanelDarkBackground', - -- AvanteSidebarNormal = { link = 'PanelDarkBackground' }, EndOfBuffer = 'PanelDarkBackground', - -- StatusLine = 'PanelSt', - -- StatusLineNC = 'PanelStNC', SignColumn = 'PanelDarkBackground', - -- VertSplit = 'PanelVertSplit', - -- VertSplit = 'PanelWinSeparator', WinSeparator = 'PanelWinSeparator', }) end @@ -784,20 +618,15 @@ local function colorscheme_overrides(dim_factor) end local function user_highlights() - local dim_factor = 0.5 - if vim.g.colors_name == 'gruvbox' then - dim_factor = 0.25 - elseif vim.g.colors_name == 'horizon' then - dim_factor = 0.75 - elseif vim.g.colors_name == 'github_dark_default' then - dim_factor = -1 - end - + local dim_factor = 0.25 general_overrides(dim_factor) set_sidebar_highlight(dim_factor) colorscheme_overrides(dim_factor) end +------------------------------------------------------------------------------- +-- Auto-commands to apply highlights at the right time. {{{ +------------------------------------------------------------------------------- augroup('UserHighlights', { event = 'ColorScheme', command = function() user_highlights() end, @@ -811,6 +640,14 @@ augroup('UserHighlights', { event = 'FileType', pattern = sidebar_fts, command = function() on_sidebar_enter() end, +}, { + -- diffview fires this after its own hl.setup() completes, so our groups win. + event = 'User', + pattern = 'DiffviewViewPostLayout', + command = function() on_diffview_enter() end, }) --- vim: foldmethod=marker +-- }}} +------------------------------------------------------------------------------- + +-- vim: fdm=marker diff --git a/files/.config/nvim/plugin/statusline.lua b/files/.config/nvim/plugin/statusline.lua index ba65d196..4b208935 100644 --- a/files/.config/nvim/plugin/statusline.lua +++ b/files/.config/nvim/plugin/statusline.lua @@ -1,35 +1,19 @@ -if - not mrl - or not mrl.ui - or not mrl.ui.statusline - or not mrl.ui.statusline.enable -then - return -end - --- Wait for icons to be available -if not mrl.ui.icons then - vim.schedule(function() - if mrl and mrl.ui and mrl.ui.icons then - dofile(vim.fn.expand(':p')) - end - end) - return -end +local T = require('tools') +local UI = require('tools').ui +local HL = require('highlight') -mrl.ui.statusline = {} +_G.Stl = {} -- LSP clients are collapsed by default: show only 🧩 . local state = { lsp_clients_visible = false } -local str = require('custom.strings') +local str = require('tools').strings local section, spacer, display = str.section, str.spacer, str.display -local icons, lsp, highlight, decorations = - mrl.ui.icons, mrl.ui.lsp, mrl.highlight, mrl.ui.decorations +local icons, lsp, highlight, decorations = UI.icons, UI.lsp, HL, UI.decorations local api, fn, fs, fmt, strwidth = vim.api, vim.fn, vim.fs, string.format, vim.api.nvim_strwidth -local P, falsy = mrl.ui.palette, mrl.falsy +local P, falsy = UI.palette, T.falsy local sep = package.config:sub(1, 1) local space = ' ' @@ -54,7 +38,7 @@ local identifiers = { terminal = '', quickfix = '', }, - filetypes = mrl.p_table({ + filetypes = T.p_table({ ['fzf'] = '󱁴', -- '', ['fzf-lua'] = '󱁴', ['log'] = '', @@ -79,7 +63,7 @@ local identifiers = { ['toggleterm'] = '', ['Avante.*'] = icons.misc.chat, }), - names = mrl.p_table({ + names = T.p_table({ ['fzf'] = 'FZF', ['fzf-lua'] = 'FZF', ['orgagenda'] = 'Org', @@ -262,7 +246,9 @@ end local function special_buffers(ctx) if ctx.preview then return 'preview' end if ctx.buftype == 'quickfix' then return 'Quickfix List' end - if ctx.bufname and ctx.bufname:match('^fzf%-lua') then return 'FZF' end + if ctx.filetype == 'fzf' or (ctx.bufname and ctx.bufname:match('^fzf')) then + return 'FZF' + end if ctx.filetype == 'AvanteInput' then return 'Avante' end if ctx.filetype == 'AvanteSelectedFiles' then return 'Avante' end if ctx.filetype == 'Avante' then return 'Avante' end @@ -294,19 +280,10 @@ local function dir_env(directory) if not directory then return '', '' end local paths = { [vim.g.dotfiles] = '$DOTFILES', - [vim.g.projects_directory .. '/personal/dotfiles'] = '$DOTFILES', [vim.g.work_directory] = '$WORK', - ['~/Workspaces/work/'] = '$WORK', - ['/home/marcos/Workspaces/work/'] = '$WORK', [vim.env.VIMRUNTIME] = '$VIMRUNTIME', [vim.g.projects_directory] = '$WORKSPACES', - ['/home/marcos/Projects/'] = '$WORKSPACES', - ['/home/marcos/Workspaces/personal/dotfiles/'] = '$DOTFILES', - ['/Users/marcos/Workspaces/personal/dotfiles/'] = '$DOTFILES', - ['/home/marcos/Projects/personal/dotfiles/'] = '$DOTFILES', - ['~/Projects/personal/dotfiles/'] = '$DOTFILES', - ['/Users/marcos/Library/Mobile Documents/iCloud~md~obsidian'] = '$OBSIDIAN', - ['~/Library/Mobile Documents/iCloud~md~obsidian'] = '$OBSIDIAN', + [vim.g.obsidian] = '$OBSIDIAN', [vim.env.HOME] = '~', } local result, env, prev_match = directory, '', '' @@ -441,7 +418,7 @@ end local function diagnostic_info(context) local diagnostics = vim.diagnostic.get(context.bufnum) local severities = vim.diagnostic.severity - local lsp_icons = mrl.ui.icons.lsp + local lsp_icons = UI.icons.lsp if vim.tbl_isempty(diagnostics) then return { error = { count = 0, icon = lsp_icons.error }, @@ -497,7 +474,7 @@ end local LSP_COMPONENT_ID = 2000 -function mrl.ui.statusline.lsp_client_click() +function Stl.lsp_client_click() state.lsp_clients_visible = not state.lsp_clients_visible vim.cmd('redrawstatus') end @@ -618,7 +595,7 @@ end -- RENDER -------------------------------------------------------------------------------- -function mrl.ui.statusline.render() +function Stl.render() local curwin = api.nvim_get_current_win() local curbuf = api.nvim_win_get_buf(curwin) @@ -652,7 +629,6 @@ function mrl.ui.statusline.render() local l1 = section:new({ { - -- { '  ' .. vim.g.dev_environ .. '▐', 'StDevEnv' }, { '' .. vim.g.dev_environ .. '', 'StDevEnv' }, }, priority = 1, @@ -719,7 +695,7 @@ function mrl.ui.statusline.render() }, priority = 2, id = LSP_COMPONENT_ID, - click = 'v:lua.mrl.ui.statusline.lsp_client_click', + click = 'v:lua.Stl.lsp_client_click', }, } if state.lsp_clients_visible and lsp_client_count > 0 then @@ -928,9 +904,6 @@ function mrl.ui.statusline.render() priority = 6, } -- }}} - -- Space after {{{ - -- { { { space, 'StSeparator' } }, priority = 1 } - -- }}} ) -- removes 5 columns to add some padding return display({ l1 + l2, m1, r1 + r2 }, available_space - 5) @@ -940,21 +913,9 @@ end vim.g.qf_disable_statusline = 1 -- set the statusline -vim.o.statusline = '%{%v:lua.mrl.ui.statusline.render()%}' - --- Helper to safely call augroup -local function augroup(name, ...) - local args = { ... } - if mrl and mrl.augroup then - return mrl.augroup(name, unpack(args)) - else - vim.schedule(function() - if mrl and mrl.augroup then mrl.augroup(name, unpack(args)) end - end) - end -end +vim.o.statusline = '%{%v:lua.Stl.render()%}' -augroup('CustomStatusline', { +T.augroup('CustomStatusline', { event = 'FocusGained', command = function() vim.g.vim_in_focus = true end, }, { diff --git a/files/.config/nvim/tests/integration/test_keymaps.lua b/files/.config/nvim/tests/integration/test_keymaps.lua new file mode 100644 index 00000000..2f747078 --- /dev/null +++ b/files/.config/nvim/tests/integration/test_keymaps.lua @@ -0,0 +1,57 @@ +-- Integration test: verify that key bindings from keymaps.lua are registered. +-- Boots a child Neovim with the real config root on rtp, loads options.lua +-- (which patches vim.keymap.set) then keymaps.lua, and checks that specific +-- maps exist. + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +local config_root = vim.fn.fnamemodify( + debug.getinfo(1, 'S').source:sub(2), + ':h:h:h' +) + +T['keymaps'] = MiniTest.new_set({ + hooks = { + pre_once = function() + T.child = MiniTest.new_child_neovim() + T.child.start({ '-u', 'NONE' }) + T.child.lua(('vim.opt.rtp:prepend(%q)'):format(config_root)) + -- options.lua patches vim.keymap.set; keymaps.lua uses it + T.child.lua("require('options')") + T.child.lua("require('keymaps')") + end, + post_once = function() T.child.stop() end, + }, +}) + +--- Check that a keymap is registered for the given mode and lhs. +local function has_map(mode, lhs) + return T.child.lua_get( + ('(function() ' + .. 'local maps = vim.fn.maparg(%q, %q, false, true); ' + .. 'return maps ~= nil and maps.lhs ~= nil ' + .. 'end)()'):format(lhs, mode) + ) +end + +T['keymaps'][' saves in normal mode'] = function() + eq(has_map('n', ''), true) +end +T['keymaps'][']q moves to next quickfix item'] = function() + eq(has_map('n', ']q'), true) +end +T['keymaps']['[q moves to prev quickfix item'] = function() + eq(has_map('n', '[q'), true) +end +T['keymaps'][']l moves to next loclist item'] = function() + eq(has_map('n', ']l'), true) +end +T['keymaps']['[l moves to prev loclist item'] = function() + eq(has_map('n', '[l'), true) +end +T['keymaps']['g> shows messages'] = function() + eq(has_map('n', 'g>'), true) +end + +return T diff --git a/files/.config/nvim/tests/integration/test_lsp.lua b/files/.config/nvim/tests/integration/test_lsp.lua new file mode 100644 index 00000000..4aa55938 --- /dev/null +++ b/files/.config/nvim/tests/integration/test_lsp.lua @@ -0,0 +1,137 @@ +-- Integration tests for LSP / Mason setup. +-- +-- Tier 1 — Mason registry. +-- Loads mason.nvim directly in this test process (using the real lazy data +-- dir so the registry index is available) and asserts that each expected +-- package name is known to mason-registry. +-- +-- Tier 2 — On-disk binaries. +-- Checks ~/.local/share/nvim/mason/ to confirm every tool was actually +-- installed. Skipped gracefully when the directory doesn't exist. + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +-- ─── Tool / server lists (keep in sync with lua/custom/plugins/lsp.lua) ────── + +local LSP_SERVERS = { + 'basedpyright', + 'ruff', + 'lua_ls', +} + +local EXTRA_TOOLS = { + 'stylua', + 'black', + 'isort', + 'flake8', + 'mypy', + 'shfmt', + 'prettier', + 'prettierd', + 'eslint_d', + 'hadolint', + 'jsonlint', + 'vale', + 'tflint', + 'sonarlint-language-server', +} + +-- mason-lspconfig package names (differ from server names for some servers) +local SERVER_TO_PACKAGE = { + lua_ls = 'lua-language-server', + basedpyright = 'basedpyright', + ruff = 'ruff', +} + +-- ─── Tier 1: mason-registry knows every package ─────────────────────────────── +-- We load mason.nvim into this process using the real lazy install path. +-- We must temporarily override XDG_DATA_HOME so mason reads its cached registry +-- from the real data dir, not the isolated tmp dir set by minimal_init.lua. + +local real_data = vim.fn.expand('~/.local/share/nvim') +local mason_plugin_path = real_data .. '/lazy/mason.nvim' +local mason_exists = vim.loop.fs_stat(mason_plugin_path) ~= nil + +-- Load mason once, restoring the real data dir for its setup +local mason_registry +if mason_exists then + -- Temporarily point XDG_DATA_HOME at the real nvim data parent so mason + -- can find its cached registry index (under ~/.local/share/nvim/mason/) + local old_xdg = vim.env.XDG_DATA_HOME + vim.env.XDG_DATA_HOME = vim.fn.expand('~/.local/share') + + vim.opt.rtp:prepend(mason_plugin_path) + local ok, reg = pcall(function() + require('mason').setup() + return require('mason-registry') + end) + + vim.env.XDG_DATA_HOME = old_xdg + + if ok then mason_registry = reg end +end + +T['mason registry'] = MiniTest.new_set() + +local function registry_has(pkg_name) + if not mason_registry then + MiniTest.skip('mason.nvim not available — skipping registry checks') + end + local ok = pcall(function() return mason_registry.get_package(pkg_name) end) + return ok +end + +for _, tool in ipairs(EXTRA_TOOLS) do + local name = tool + T['mason registry']['tool: ' .. name] = function() + eq(registry_has(name), true) + end +end + +for _, server in ipairs(LSP_SERVERS) do + local pkg = SERVER_TO_PACKAGE[server] or server + local sname = server + T['mason registry']['lsp: ' .. sname] = function() + eq(registry_has(pkg), true) + end +end + +-- ─── Tier 2: on-disk presence ───────────────────────────────────────────────── + +local mason_root = real_data .. '/mason' +local mason_bin = mason_root .. '/bin' +local mason_pkgs = mason_root .. '/packages' +local mason_bin_exists = vim.loop.fs_stat(mason_bin) ~= nil + +T['mason installed tools'] = MiniTest.new_set() + +local function bin_exists(name) + return vim.loop.fs_stat(mason_bin .. '/' .. name) ~= nil +end +local function pkg_exists(name) + return vim.loop.fs_stat(mason_pkgs .. '/' .. name) ~= nil +end + +for _, tool in ipairs(EXTRA_TOOLS) do + local name = tool + T['mason installed tools']['bin: ' .. name] = function() + if not mason_bin_exists then + MiniTest.skip('Mason not installed on this machine') + end + eq(bin_exists(name), true) + end +end + +for _, server in ipairs(LSP_SERVERS) do + local pkg = SERVER_TO_PACKAGE[server] or server + local sname = server + T['mason installed tools']['lsp: ' .. sname] = function() + if not mason_bin_exists then + MiniTest.skip('Mason not installed on this machine') + end + eq(pkg_exists(pkg), true) + end +end + +return T diff --git a/files/.config/nvim/tests/integration/test_options.lua b/files/.config/nvim/tests/integration/test_options.lua new file mode 100644 index 00000000..97698b27 --- /dev/null +++ b/files/.config/nvim/tests/integration/test_options.lua @@ -0,0 +1,65 @@ +-- Integration test: verify vim options are set by options.lua. +-- Boots a child Neovim, loads options.lua from the config root, then asserts +-- key option values. + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +-- Resolve the config root relative to this file's location: +-- this file lives at /tests/integration/test_options.lua +local config_root = vim.fn.fnamemodify( + debug.getinfo(1, 'S').source:sub(2), -- strip leading '@' + ':h:h:h' +) + +T['options'] = MiniTest.new_set({ + hooks = { + pre_once = function() + T.child = MiniTest.new_child_neovim() + T.child.start({ '-u', 'NONE' }) + -- Put the config lua/ directory on rtp so require() works + T.child.lua( + ('vim.opt.rtp:prepend(%q)'):format(config_root) + ) + T.child.lua("require('options')") + end, + post_once = function() T.child.stop() end, + }, +}) + +local function get_opt(name) + return T.child.lua_get(('vim.o[%q]'):format(name)) +end + +T['options']['number is set'] = function() + eq(get_opt('number'), true) +end +T['options']['relativenumber is set'] = function() + eq(get_opt('relativenumber'), true) +end +T['options']['splitbelow is set'] = function() + eq(get_opt('splitbelow'), true) +end +T['options']['splitright is set'] = function() + eq(get_opt('splitright'), true) +end +T['options']['undofile is set'] = function() + eq(get_opt('undofile'), true) +end +T['options']['termguicolors is set'] = function() + eq(get_opt('termguicolors'), true) +end +T['options']['ignorecase is set'] = function() + eq(get_opt('ignorecase'), true) +end +T['options']['smartcase is set'] = function() + eq(get_opt('smartcase'), true) +end +T['options']['expandtab is set'] = function() + eq(get_opt('expandtab'), true) +end +T['options']['showtabline is 0'] = function() + eq(get_opt('showtabline'), 0) +end + +return T diff --git a/files/.config/nvim/tests/integration/test_plugins_load.lua b/files/.config/nvim/tests/integration/test_plugins_load.lua new file mode 100644 index 00000000..25ca7292 --- /dev/null +++ b/files/.config/nvim/tests/integration/test_plugins_load.lua @@ -0,0 +1,35 @@ +-- Smoke tests: verify core Lua modules can be require()'d without errors. +-- Catches import-time syntax errors or broken module structure independently +-- of whether plugins are installed. + +local T = MiniTest.new_set() + +local config_root = vim.fn.fnamemodify( + debug.getinfo(1, 'S').source:sub(2), + ':h:h:h' +) + +vim.opt.rtp:prepend(config_root) + +local function loads(mod_name) + T[mod_name .. ' loads without error'] = function() + local ok, result = pcall(require, mod_name) + MiniTest.expect.equality(ok, true) + MiniTest.expect.no_equality(result, nil) + end +end + +loads('tools') -- core utilities + ui + strings + colors +loads('highlight') -- highlight helpers (depends on tools) + +-- options.lua has side effects but returns nothing; just check it doesn't error +T['options.lua executes without error'] = function() + local ok, err = pcall(require, 'options') + if not ok then + if type(err) == 'string' and err:match('already') then return end + error(err) + end + MiniTest.expect.equality(ok, true) +end + +return T diff --git a/files/.config/nvim/tests/minimal_init.lua b/files/.config/nvim/tests/minimal_init.lua new file mode 100644 index 00000000..472fbb31 --- /dev/null +++ b/files/.config/nvim/tests/minimal_init.lua @@ -0,0 +1,33 @@ +-- Minimal init for running tests with mini.test. +-- Does NOT load the full lazy.nvim stack — only bootstraps mini.test itself. + +local tmp = vim.fn.fnamemodify('/tmp/nvim-test', ':p') +vim.env.XDG_CONFIG_HOME = tmp .. '/config' +vim.env.XDG_DATA_HOME = tmp .. '/data' +vim.env.XDG_STATE_HOME = tmp .. '/state' +vim.env.XDG_CACHE_HOME = tmp .. '/cache' + +-- Bootstrap mini.test into a dedicated data dir +local mini_test_path = tmp .. '/data/mini-test/mini.test' +if not vim.loop.fs_stat(mini_test_path) then + vim.fn.system({ + 'git', + 'clone', + '--filter=blob:none', + 'https://github.com/echasnovski/mini.test', + mini_test_path, + }) +end + +-- Add mini.test to runtimepath +vim.opt.rtp:prepend(mini_test_path) + +-- Add the nvim config root so tests can `require('tools')`, `require('highlight')`, etc. +local config_root = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h') +vim.opt.rtp:prepend(config_root) + +-- Stub out vim.notify to avoid noise during unit tests +vim.notify = function() end + +-- Set up MiniTest global so test files can reference MiniTest.* at file scope +require('mini.test').setup() diff --git a/files/.config/nvim/tests/run_tests.sh b/files/.config/nvim/tests/run_tests.sh new file mode 100755 index 00000000..44afcc07 --- /dev/null +++ b/files/.config/nvim/tests/run_tests.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Run the full Neovim test suite using mini.test. +# Usage: bash tests/run_tests.sh [directory] +# +# When run from the repo root: +# bash files/.config/nvim/tests/run_tests.sh +# Or from the nvim config root: +# bash tests/run_tests.sh + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NVIM_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +TEST_DIR="${1:-$SCRIPT_DIR}" + +cd "$NVIM_ROOT" + +echo "Running tests in: $TEST_DIR" +echo "Neovim config root: $NVIM_ROOT" + +nvim --headless --noplugin -u "$SCRIPT_DIR/minimal_init.lua" \ + -c "lua require('mini.test').run({ execute = { reporter = require('mini.test').gen_reporter.stdout({ group_depth = 2 }) } })" \ + -c "qa!" diff --git a/files/.config/nvim/tests/unit/test_highlight.lua b/files/.config/nvim/tests/unit/test_highlight.lua new file mode 100644 index 00000000..721f1e18 --- /dev/null +++ b/files/.config/nvim/tests/unit/test_highlight.lua @@ -0,0 +1,109 @@ +-- Unit tests for lua/highlight.lua exported pure functions: +-- tint, blend, darken_hsl +-- +-- These functions are pure (no Neovim UI API required at call time) so they +-- can run in a plain headless Neovim without a display. + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +-- tint, blend, darken_hsl live in tools.lua (pure, no Neovim UI API at call time). +local hl = require('tools') + +-- tint {{{ +T['tint'] = MiniTest.new_set() + +T['tint']['brightens a color with positive percent'] = function() + -- #808080 brightened by 0% should stay #808080 + local result = hl.tint('#808080', 0) + eq(result, '#808080') +end +T['tint']['darkens a color with negative percent'] = function() + -- #ffffff darkened by -50% => #7f7f7f (floor(255 * 0.5) = 127 = 0x7f) + local result = hl.tint('#ffffff', -0.5) + eq(result, '#7f7f7f') +end +T['tint']['clamps to black'] = function() + local result = hl.tint('#101010', -2.0) + eq(result, '#000000') +end +T['tint']['clamps to white'] = function() + local result = hl.tint('#f0f0f0', 100) + eq(result, '#ffffff') +end +T['tint']['returns NONE for invalid color'] = function() + local result = hl.tint('notacolor', 0.1) + eq(result, 'NONE') +end + +-- }}} + +-- blend {{{ +T['blend'] = MiniTest.new_set() + +T['blend']['alpha=0 returns bg'] = function() + eq(hl.blend('#000000', '#ffffff', 0), '#000000') +end +T['blend']['alpha=1 returns fg'] = function() + eq(hl.blend('#000000', '#ffffff', 1), '#ffffff') +end +T['blend']['50% blend of black and white is grey'] = function() + -- floor((1-0.5)*0 + 0.5*255 + 0.5) = floor(127.5 + 0.5) = 128 = 0x80 + eq(hl.blend('#000000', '#ffffff', 0.5), '#808080') +end +T['blend']['returns NONE when bg is NONE'] = function() + eq(hl.blend('NONE', '#ffffff', 0.5), 'NONE') +end +T['blend']['returns NONE when fg is NONE'] = function() + eq(hl.blend('#000000', 'NONE', 0.5), 'NONE') +end +T['blend']['returns NONE for invalid hex'] = function() + eq(hl.blend('invalid', '#ffffff', 0.5), 'NONE') +end +T['blend']['clamps alpha below 0'] = function() + eq(hl.blend('#000000', '#ffffff', -1), '#000000') +end +T['blend']['clamps alpha above 1'] = function() + eq(hl.blend('#000000', '#ffffff', 2), '#ffffff') +end + +-- }}} + +-- darken_hsl {{{ +T['darken_hsl'] = MiniTest.new_set() + +T['darken_hsl']['factor=0 returns original color'] = function() + -- darken with factor 0 => ll * (1+0) = ll unchanged + local result = hl.darken_hsl('#ff0000', 0) + eq(result, '#ff0000') +end +T['darken_hsl']['darkens a color'] = function() + -- Any negative factor should produce a darker (lower lightness) result. + -- We just check it differs from the original and is a valid hex. + local result = hl.darken_hsl('#ffffff', -0.5) + MiniTest.expect.no_equality(result, '#ffffff') + MiniTest.expect.no_equality(result, 'NONE') + eq(result:sub(1, 1), '#') + eq(#result, 7) +end +T['darken_hsl']['lightens a color'] = function() + local result = hl.darken_hsl('#333333', 0.5) + MiniTest.expect.no_equality(result, '#333333') + MiniTest.expect.no_equality(result, 'NONE') + eq(result:sub(1, 1), '#') + eq(#result, 7) +end +T['darken_hsl']['returns NONE for invalid hex'] = function() + eq(hl.darken_hsl('notacolor', -0.2), 'NONE') +end +T['darken_hsl']['pure black stays black when darkened'] = function() + eq(hl.darken_hsl('#000000', -0.5), '#000000') +end +T['darken_hsl']['pure white stays white when lightened'] = function() + -- lightness is already 1.0; moving toward 1.0 keeps it there + eq(hl.darken_hsl('#ffffff', 0.5), '#ffffff') +end + +-- }}} + +return T diff --git a/files/.config/nvim/tests/unit/test_strings.lua b/files/.config/nvim/tests/unit/test_strings.lua new file mode 100644 index 00000000..65fc8d2f --- /dev/null +++ b/files/.config/nvim/tests/unit/test_strings.lua @@ -0,0 +1,55 @@ +-- Unit tests for statusline string utilities (tools.strings). +-- spacer, section + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +local S = require('tools').strings + +-- spacer {{{ +T['spacer'] = MiniTest.new_set() + +T['spacer']['returns nil for size < 1'] = function() + eq(S.spacer(0), nil) + eq(S.spacer(-1), nil) +end +T['spacer']['returns nil when size is absent'] = function() + eq(S.spacer(), nil) +end +T['spacer']['returns a component with correct padding'] = function() + local result = S.spacer(3) + MiniTest.expect.no_equality(result, nil) + local chunks = result[1] + MiniTest.expect.no_equality(chunks, nil) + eq(chunks[1][1], ' ') +end +T['spacer']['respects custom filler'] = function() + local result = S.spacer(2, { filler = '-' }) + local chunks = result[1] + eq(chunks[1][1], '--') +end + +-- }}} + +-- section {{{ +T['section'] = MiniTest.new_set() + +T['section']['new creates a table with provided args'] = function() + local s = S.section:new('a', 'b', 'c') + eq(s[1], 'a') + eq(s[2], 'b') + eq(s[3], 'c') +end +T['section']['addition concatenates two sections'] = function() + local s1 = S.section:new('a', 'b') + local s2 = S.section:new('c', 'd') + local combined = s1 + s2 + eq(combined[1], 'a') + eq(combined[2], 'b') + eq(combined[3], 'c') + eq(combined[4], 'd') +end + +-- }}} + +return T diff --git a/files/.config/nvim/tests/unit/test_tools.lua b/files/.config/nvim/tests/unit/test_tools.lua new file mode 100644 index 00000000..44134664 --- /dev/null +++ b/files/.config/nvim/tests/unit/test_tools.lua @@ -0,0 +1,116 @@ +-- Unit tests for lua/tools.lua pure functions. +-- Requires: mini.test (bootstrapped by minimal_init.lua) + +local T = MiniTest.new_set() +local eq = MiniTest.expect.equality + +-- `tools` is on the rtp via minimal_init.lua +local tools = require('tools') + +-- falsy {{{ +T['falsy'] = MiniTest.new_set() + +T['falsy']['nil returns true'] = function() eq(tools.falsy(nil), true) end +T['falsy']['false returns true'] = function() eq(tools.falsy(false), true) end +T['falsy']['true returns false'] = function() eq(tools.falsy(true), false) end +T['falsy']['empty string returns true'] = function() + eq(tools.falsy(''), true) +end +T['falsy']['non-empty string returns false'] = function() + eq(tools.falsy('hello'), false) +end +T['falsy']['zero returns true'] = function() eq(tools.falsy(0), true) end +T['falsy']['negative number returns true'] = function() + eq(tools.falsy(-1), true) +end +T['falsy']['positive number returns false'] = function() + eq(tools.falsy(1), false) +end +T['falsy']['empty table returns true'] = function() + eq(tools.falsy({}), true) +end +T['falsy']['non-empty table returns false'] = function() + eq(tools.falsy({ 1 }), false) +end + +-- }}} + +-- any {{{ +T['any'] = MiniTest.new_set() + +T['any']['matches item in list'] = function() + eq(tools.any('foo', { 'foo', 'bar' }), true) +end +T['any']['returns false when no match'] = function() + eq(tools.any('baz', { 'foo', 'bar' }), false) +end +T['any']['supports pattern matching'] = function() + eq(tools.any('hello.lua', { '%.lua$' }), true) +end +T['any']['empty list returns false'] = function() + eq(tools.any('foo', {}), false) +end + +-- }}} + +-- find {{{ +T['find'] = MiniTest.new_set() + +T['find']['returns matching item'] = function() + local result = tools.find(function(x) return x > 2 end, { 1, 2, 3, 4 }) + eq(result, 3) +end +T['find']['returns nil when no match'] = function() + local result = tools.find(function(x) return x > 10 end, { 1, 2, 3 }) + eq(result, nil) +end +T['find']['returns first match'] = function() + local result = tools.find(function(x) return x % 2 == 0 end, { 1, 2, 4, 6 }) + eq(result, 2) +end + +-- }}} + +-- fold {{{ +T['fold'] = MiniTest.new_set() + +T['fold']['sums a list'] = function() + local sum = + tools.fold(function(acc, v) return acc + v end, { 1, 2, 3, 4 }, 0) + eq(sum, 10) +end +T['fold']['concatenates strings'] = function() + local result = + tools.fold(function(acc, v) return acc .. v end, { 'a', 'b', 'c' }, '') + eq(result, 'abc') +end +T['fold']['uses empty table as default accumulator'] = function() + local result = tools.fold(function(acc, v) + acc[#acc + 1] = v * 2 + return acc + end, { 1, 2, 3 }) + eq(result, { 2, 4, 6 }) +end + +-- }}} + +-- map {{{ +T['map'] = MiniTest.new_set() + +T['map']['doubles each element'] = function() + local result = tools.map(function(v) return v * 2 end, { 1, 2, 3 }) + eq(result, { 2, 4, 6 }) +end +T['map']['maps strings to uppercase'] = function() + local result = + tools.map(function(v) return v:upper() end, { 'a', 'b', 'c' }) + eq(result, { 'A', 'B', 'C' }) +end +T['map']['returns empty table for empty input'] = function() + local result = tools.map(function(v) return v end, {}) + eq(result, {}) +end + +-- }}} + +return T From 6aa76f7dd751e089fa1e5bcc0fbd9b408dbb7f49 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Tue, 17 Mar 2026 00:43:55 +0100 Subject: [PATCH 22/27] style: format files --- Makefile | 14 +- files/.config/nvim/colors/carbon-mist.lua | 10 +- .../.config/nvim/lua/custom/plugins/folke.lua | 3 +- files/.config/nvim/lua/dracula-pro/colors.lua | 136 +++---- files/.config/nvim/lua/dracula-pro/config.lua | 2 +- .../nvim/lua/dracula-pro/groups/base.lua | 15 +- .../nvim/lua/dracula-pro/groups/plugins.lua | 334 +++++++++--------- .../lua/dracula-pro/groups/treesitter.lua | 260 +++++++------- files/.config/nvim/lua/dracula-pro/init.lua | 56 ++- files/.config/nvim/lua/highlight.lua | 1 - files/.config/nvim/lua/tools.lua | 12 +- files/.config/nvim/plugin/autocommands.lua | 54 ++- .../nvim/tests/integration/test_keymaps.lua | 16 +- .../nvim/tests/integration/test_lsp.lua | 10 +- .../nvim/tests/integration/test_options.lua | 40 +-- .../tests/integration/test_plugins_load.lua | 10 +- files/.config/nvim/tests/minimal_init.lua | 3 +- .../.config/nvim/tests/unit/test_strings.lua | 4 +- files/.config/nvim/tests/unit/test_tools.lua | 21 +- 19 files changed, 501 insertions(+), 500 deletions(-) diff --git a/Makefile b/Makefile index d6f1b50a..26c60f22 100644 --- a/Makefile +++ b/Makefile @@ -110,4 +110,16 @@ zsh-plugins: ln -sf ${HOMEBREW_PREFIX}/Cellar/alias-tips/alias-tips.plugin.zsh ${HOMEBREW_PREFIX}/share/zsh-alias-tips -.PHONY: all homebrew install setup brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test +MASON := $(HOME)/.local/share/nvim/mason/bin + +fmt: + git diff --name-only --diff-filter=d | grep '\.lua$$' | xargs -r $(MASON)/stylua -- + git diff --name-only --diff-filter=d | grep '\.sh$$' | xargs -r $(MASON)/shfmt -w + git diff --name-only --diff-filter=d | grep '\.py$$' | xargs -r $(MASON)/isort -- + git diff --name-only --diff-filter=d | grep '\.py$$' | xargs -r $(MASON)/black -- + +commit: fmt + git add -u + git commit + +.PHONY: all homebrew install setup brew macos kitty nvim vim tmux fzf-marks private zsh-plugins test fmt commit diff --git a/files/.config/nvim/colors/carbon-mist.lua b/files/.config/nvim/colors/carbon-mist.lua index bfc6219f..f35433f1 100644 --- a/files/.config/nvim/colors/carbon-mist.lua +++ b/files/.config/nvim/colors/carbon-mist.lua @@ -3,12 +3,10 @@ -- Author: Marcos Romero Lamas -- Version: 1.0.0 -vim.cmd("hi clear") -if vim.fn.exists("syntax_on") then - vim.cmd("syntax reset") -end +vim.cmd('hi clear') +if vim.fn.exists('syntax_on') then vim.cmd('syntax reset') end vim.o.termguicolors = true -vim.g.colors_name = "carbon-mist" +vim.g.colors_name = 'carbon-mist' -require("carbon-mist").load() +require('carbon-mist').load() diff --git a/files/.config/nvim/lua/custom/plugins/folke.lua b/files/.config/nvim/lua/custom/plugins/folke.lua index 97aeaddb..a5647075 100644 --- a/files/.config/nvim/lua/custom/plugins/folke.lua +++ b/files/.config/nvim/lua/custom/plugins/folke.lua @@ -1,2 +1 @@ -return { -} +return {} diff --git a/files/.config/nvim/lua/dracula-pro/colors.lua b/files/.config/nvim/lua/dracula-pro/colors.lua index f9827dbd..a1729423 100644 --- a/files/.config/nvim/lua/dracula-pro/colors.lua +++ b/files/.config/nvim/lua/dracula-pro/colors.lua @@ -3,96 +3,96 @@ local M = {} M.default = { - none = "NONE", + none = 'NONE', -- Base colors - bg = "#22212C", - bg_dark = "#010010", - bg_darker = "#010010", - bg_light = "#43414E", - bg_highlight = "#43414E", - bg_visual = "#454158", - bg_search = "#43414E", - bg_sidebar = "#010010", - bg_statusline = "#010010", - bg_float = "#43414E", - bg_popup = "#43414E", + bg = '#22212C', + bg_dark = '#010010', + bg_darker = '#010010', + bg_light = '#43414E', + bg_highlight = '#43414E', + bg_visual = '#454158', + bg_search = '#43414E', + bg_sidebar = '#010010', + bg_statusline = '#010010', + bg_float = '#43414E', + bg_popup = '#43414E', - fg = "#F8F8F2", - fg_dark = "#CDCDC8", - fg_gutter = "#504C67", - fg_sidebar = "#F8F8F2", - fg_float = "#F8F8F2", + fg = '#F8F8F2', + fg_dark = '#CDCDC8', + fg_gutter = '#504C67', + fg_sidebar = '#F8F8F2', + fg_float = '#F8F8F2', -- Accent colors - black = "#010010", - border = "#504C67", - border_highlight = "#9580FF", - comment = "#7970A9", + black = '#010010', + border = '#504C67', + border_highlight = '#9580FF', + comment = '#7970A9', -- Syntax colors - red = "#FF9580", - orange = "#FFFF80", - yellow = "#FFFF80", - green = "#8AFF80", - cyan = "#80FFEA", - blue = "#9580FF", - blue0 = "#9580FF", - blue1 = "#9580FF", - blue2 = "#80FFEA", - blue5 = "#FF80BF", - blue6 = "#99FFEE", - blue7 = "#9580FF", - magenta = "#FF80BF", - magenta2 = "#FF99CC", - purple = "#FF80BF", - teal = "#80FFEA", - dark3 = "#CDCDC8", - dark5 = "#CDCDC8", - green1 = "#8AFF80", - green2 = "#8AFF80", - red1 = "#FF9580", + red = '#FF9580', + orange = '#FFFF80', + yellow = '#FFFF80', + green = '#8AFF80', + cyan = '#80FFEA', + blue = '#9580FF', + blue0 = '#9580FF', + blue1 = '#9580FF', + blue2 = '#80FFEA', + blue5 = '#FF80BF', + blue6 = '#99FFEE', + blue7 = '#9580FF', + magenta = '#FF80BF', + magenta2 = '#FF99CC', + purple = '#FF80BF', + teal = '#80FFEA', + dark3 = '#CDCDC8', + dark5 = '#CDCDC8', + green1 = '#8AFF80', + green2 = '#8AFF80', + red1 = '#FF9580', -- Bright variants - bright_red = "#FFAA99", - bright_green = "#A2FF99", - bright_yellow = "#FFFF99", - bright_blue = "#AA99FF", - bright_magenta = "#FF99CC", - bright_cyan = "#99FFEE", + bright_red = '#FFAA99', + bright_green = '#A2FF99', + bright_yellow = '#FFFF99', + bright_blue = '#AA99FF', + bright_magenta = '#FF99CC', + bright_cyan = '#99FFEE', -- Terminal colors - terminal_black = "#22212C", + terminal_black = '#22212C', -- Semantic syntax colors (for accurate theme reproduction) - syntax_string = "#FFFF80", - syntax_function = "#8AFF80", - syntax_variable = "#F8F8F2", - syntax_keyword = "#FF80BF", - syntax_class = "#9580FF", - syntax_constant = "#80FFEA", + syntax_string = '#FFFF80', + syntax_function = '#8AFF80', + syntax_variable = '#F8F8F2', + syntax_keyword = '#FF80BF', + syntax_class = '#9580FF', + syntax_constant = '#80FFEA', -- Diagnostic colors - error = "#FF9580", - warning = "#FFFF80", - info = "#80FFEA", - hint = "#504C67", - todo = "#9580FF", + error = '#FF9580', + warning = '#FFFF80', + info = '#80FFEA', + hint = '#504C67', + todo = '#9580FF', -- Git colors git = { - add = "#8AFF80", - change = "#FFFF80", - delete = "#FF9580", - ignore = "#504C67", + add = '#8AFF80', + change = '#FFFF80', + delete = '#FF9580', + ignore = '#504C67', }, -- Diff colors diff = { - add = "#5DD458", - delete = "#D16D5B", - change = "#D3D457", - text = "#FFFF80", + add = '#5DD458', + delete = '#D16D5B', + change = '#D3D457', + text = '#FFFF80', }, } diff --git a/files/.config/nvim/lua/dracula-pro/config.lua b/files/.config/nvim/lua/dracula-pro/config.lua index 72080476..3630fa55 100644 --- a/files/.config/nvim/lua/dracula-pro/config.lua +++ b/files/.config/nvim/lua/dracula-pro/config.lua @@ -17,7 +17,7 @@ M.defaults = { M.options = {} function M.setup(options) - M.options = vim.tbl_deep_extend("force", {}, M.defaults, options or {}) + M.options = vim.tbl_deep_extend('force', {}, M.defaults, options or {}) end M.setup() diff --git a/files/.config/nvim/lua/dracula-pro/groups/base.lua b/files/.config/nvim/lua/dracula-pro/groups/base.lua index 29dcb9e9..abb57640 100644 --- a/files/.config/nvim/lua/dracula-pro/groups/base.lua +++ b/files/.config/nvim/lua/dracula-pro/groups/base.lua @@ -36,7 +36,12 @@ function M.get(c, opts) MoreMsg = { fg = c.blue }, NonText = { fg = c.fg_dark }, Normal = { fg = c.fg, bg = opts.transparent and c.none or c.bg }, - NormalNC = { fg = c.fg, bg = opts.transparent and c.none or opts.dim_inactive and c.bg_dark or c.bg }, + NormalNC = { + fg = c.fg, + bg = opts.transparent and c.none + or opts.dim_inactive and c.bg_dark + or c.bg, + }, NormalSB = { fg = c.fg_sidebar, bg = c.bg_sidebar }, NormalFloat = { fg = c.fg_float, bg = c.bg_float }, FloatBorder = { fg = c.border_highlight, bg = c.bg_float }, @@ -49,7 +54,7 @@ function M.get(c, opts) QuickFixLine = { bg = c.bg_visual, bold = true }, Search = { bg = c.bg_search, fg = c.fg }, IncSearch = { bg = c.orange, fg = c.black }, - CurSearch = "IncSearch", + CurSearch = 'IncSearch', SpecialKey = { fg = c.fg_dark }, SpellBad = { sp = c.error, undercurl = true }, SpellCap = { sp = c.warning, undercurl = true }, @@ -66,14 +71,14 @@ function M.get(c, opts) WarningMsg = { fg = c.warning }, Whitespace = { fg = c.fg_gutter }, WildMenu = { bg = c.bg_visual }, - WinBar = "StatusLine", - WinBarNC = "StatusLineNC", + WinBar = 'StatusLine', + WinBarNC = 'StatusLineNC', Bold = { bold = true, fg = c.fg }, Character = { fg = c.syntax_string }, Constant = { fg = c.syntax_constant }, Debug = { fg = c.orange }, - Delimiter = "Special", + Delimiter = 'Special', Error = { fg = c.error }, Function = { fg = c.syntax_function, style = opts.styles.functions }, Identifier = { fg = c.syntax_variable, style = opts.styles.variables }, diff --git a/files/.config/nvim/lua/dracula-pro/groups/plugins.lua b/files/.config/nvim/lua/dracula-pro/groups/plugins.lua index 125545fe..72f7ccad 100644 --- a/files/.config/nvim/lua/dracula-pro/groups/plugins.lua +++ b/files/.config/nvim/lua/dracula-pro/groups/plugins.lua @@ -4,18 +4,18 @@ local M = {} -- Helper function to tint colors (darken/brighten) local function tint(color, percent) - if not color or color == "NONE" then return "NONE" end + if not color or color == 'NONE' then return 'NONE' end local r = tonumber(color:sub(2, 3), 16) local g = tonumber(color:sub(4, 5), 16) local b = tonumber(color:sub(6), 16) - if not r or not g or not b then return "NONE" end + if not r or not g or not b then return 'NONE' end local blend = function(component) component = math.floor(component * (1 + percent)) return math.min(math.max(component, 0), 255) end - return string.format("#%02x%02x%02x", blend(r), blend(g), blend(b)) + return string.format('#%02x%02x%02x', blend(r), blend(g), blend(b)) end function M.get(c, opts) @@ -35,40 +35,40 @@ function M.get(c, opts) -- LSP Kind highlights (for completion items, breadcrumbs, etc.) local lsp_kinds = { - ["module"] = { fg = c.orange }, - ["snippet"] = { fg = c.purple }, - ["folder"] = { fg = c.fg }, - ["color"] = { fg = c.fg }, - ["file"] = "Directory", - ["text"] = "@string", - ["method"] = "@method", - ["function"] = "@function", - ["constructor"] = "@constructor", - ["field"] = "@field", - ["variable"] = "@variable", - ["property"] = "@property", - ["unit"] = "@constant", - ["value"] = "@variable", - ["enum"] = "@type", - ["keyword"] = "@keyword", - ["reference"] = "@parameter.reference", - ["constant"] = "@constant", - ["struct"] = "@structure", - ["event"] = "@variable", - ["operator"] = "@operator", - ["namespace"] = "@namespace", - ["package"] = "@include", - ["string"] = "@string", - ["number"] = "@number", - ["boolean"] = "@boolean", - ["array"] = "@repeat", - ["object"] = "@type", - ["key"] = "@field", - ["null"] = "@symbol", - ["enumMember"] = "@field", - ["class"] = "@lsp.type.class", - ["interface"] = "@lsp.type.interface", - ["typeParameter"] = "@lsp.type.parameter", + ['module'] = { fg = c.orange }, + ['snippet'] = { fg = c.purple }, + ['folder'] = { fg = c.fg }, + ['color'] = { fg = c.fg }, + ['file'] = 'Directory', + ['text'] = '@string', + ['method'] = '@method', + ['function'] = '@function', + ['constructor'] = '@constructor', + ['field'] = '@field', + ['variable'] = '@variable', + ['property'] = '@property', + ['unit'] = '@constant', + ['value'] = '@variable', + ['enum'] = '@type', + ['keyword'] = '@keyword', + ['reference'] = '@parameter.reference', + ['constant'] = '@constant', + ['struct'] = '@structure', + ['event'] = '@variable', + ['operator'] = '@operator', + ['namespace'] = '@namespace', + ['package'] = '@include', + ['string'] = '@string', + ['number'] = '@number', + ['boolean'] = '@boolean', + ['array'] = '@repeat', + ['object'] = '@type', + ['key'] = '@field', + ['null'] = '@symbol', + ['enumMember'] = '@field', + ['class'] = '@lsp.type.class', + ['interface'] = '@lsp.type.interface', + ['typeParameter'] = '@lsp.type.parameter', } return { @@ -87,9 +87,9 @@ function M.get(c, opts) GitSignsChange = { fg = c.git.change }, GitSignsDelete = { fg = c.git.delete }, GitSignsUntracked = { fg = c.git.ignore }, - GitSignsAddInline = "DiffText", - GitSignsChangeInline = "DiffChange", - GitSignsDeleteInline = "DiffDelete", + GitSignsAddInline = 'DiffText', + GitSignsChangeInline = 'DiffChange', + GitSignsDeleteInline = 'DiffDelete', -- Telescope TelescopeSelection = { bg = custom.blue_bg }, @@ -126,11 +126,11 @@ function M.get(c, opts) NvimTreeSymlink = { fg = c.blue }, NvimTreeRootFolder = { fg = c.fg, bold = true }, NvimTreeExecFile = { fg = c.green }, - NvimTreeLspDiagnosticsError = "DiagnosticError", - NvimTreeLspDiagnosticsWarning = "DiagnosticWarn", - NvimTreeLspDiagnosticsInformation = "DiagnosticInfo", - NvimTreeLspDiagnosticsInfo = "DiagnosticInfo", - NvimTreeLspDiagnosticsHint = "DiagnosticHint", + NvimTreeLspDiagnosticsError = 'DiagnosticError', + NvimTreeLspDiagnosticsWarning = 'DiagnosticWarn', + NvimTreeLspDiagnosticsInformation = 'DiagnosticInfo', + NvimTreeLspDiagnosticsInfo = 'DiagnosticInfo', + NvimTreeLspDiagnosticsHint = 'DiagnosticHint', -- Neo-tree NeoTreeFolderIcon = { fg = custom.gold }, @@ -185,69 +185,69 @@ function M.get(c, opts) CmpItemAbbrMatch = { fg = c.blue, bold = true }, CmpItemAbbrMatchFuzzy = { fg = c.blue, italic = true }, CmpItemAbbrDeprecated = { fg = custom.gray, strikethrough = true }, - CmpItemKindVariable = lsp_kinds["variable"], - CmpItemKindModule = lsp_kinds["module"], - CmpItemKindSnippet = lsp_kinds["snippet"], - CmpItemKindFolder = lsp_kinds["folder"], - CmpItemKindColor = lsp_kinds["color"], - CmpItemKindFile = lsp_kinds["file"], - CmpItemKindText = lsp_kinds["text"], - CmpItemKindMethod = lsp_kinds["method"], - CmpItemKindFunction = lsp_kinds["function"], - CmpItemKindConstructor = lsp_kinds["constructor"], - CmpItemKindField = lsp_kinds["field"], - CmpItemKindProperty = lsp_kinds["property"], - CmpItemKindUnit = lsp_kinds["unit"], - CmpItemKindValue = lsp_kinds["value"], - CmpItemKindEnum = lsp_kinds["enum"], - CmpItemKindKeyword = lsp_kinds["keyword"], - CmpItemKindReference = lsp_kinds["reference"], - CmpItemKindConstant = lsp_kinds["constant"], - CmpItemKindStruct = lsp_kinds["struct"], - CmpItemKindEvent = lsp_kinds["event"], - CmpItemKindOperator = lsp_kinds["operator"], - CmpItemKindNamespace = lsp_kinds["namespace"], - CmpItemKindPackage = lsp_kinds["package"], - CmpItemKindString = lsp_kinds["string"], - CmpItemKindNumber = lsp_kinds["number"], - CmpItemKindBoolean = lsp_kinds["boolean"], - CmpItemKindArray = lsp_kinds["array"], - CmpItemKindObject = lsp_kinds["object"], - CmpItemKindKey = lsp_kinds["key"], - CmpItemKindNull = lsp_kinds["null"], - CmpItemKindEnumMember = lsp_kinds["enumMember"], - CmpItemKindClass = lsp_kinds["class"], - CmpItemKindInterface = lsp_kinds["interface"], - CmpItemKindTypeParameter = lsp_kinds["typeParameter"], + CmpItemKindVariable = lsp_kinds['variable'], + CmpItemKindModule = lsp_kinds['module'], + CmpItemKindSnippet = lsp_kinds['snippet'], + CmpItemKindFolder = lsp_kinds['folder'], + CmpItemKindColor = lsp_kinds['color'], + CmpItemKindFile = lsp_kinds['file'], + CmpItemKindText = lsp_kinds['text'], + CmpItemKindMethod = lsp_kinds['method'], + CmpItemKindFunction = lsp_kinds['function'], + CmpItemKindConstructor = lsp_kinds['constructor'], + CmpItemKindField = lsp_kinds['field'], + CmpItemKindProperty = lsp_kinds['property'], + CmpItemKindUnit = lsp_kinds['unit'], + CmpItemKindValue = lsp_kinds['value'], + CmpItemKindEnum = lsp_kinds['enum'], + CmpItemKindKeyword = lsp_kinds['keyword'], + CmpItemKindReference = lsp_kinds['reference'], + CmpItemKindConstant = lsp_kinds['constant'], + CmpItemKindStruct = lsp_kinds['struct'], + CmpItemKindEvent = lsp_kinds['event'], + CmpItemKindOperator = lsp_kinds['operator'], + CmpItemKindNamespace = lsp_kinds['namespace'], + CmpItemKindPackage = lsp_kinds['package'], + CmpItemKindString = lsp_kinds['string'], + CmpItemKindNumber = lsp_kinds['number'], + CmpItemKindBoolean = lsp_kinds['boolean'], + CmpItemKindArray = lsp_kinds['array'], + CmpItemKindObject = lsp_kinds['object'], + CmpItemKindKey = lsp_kinds['key'], + CmpItemKindNull = lsp_kinds['null'], + CmpItemKindEnumMember = lsp_kinds['enumMember'], + CmpItemKindClass = lsp_kinds['class'], + CmpItemKindInterface = lsp_kinds['interface'], + CmpItemKindTypeParameter = lsp_kinds['typeParameter'], -- Navic (breadcrumbs) - NavicIconsFile = lsp_kinds["file"], - NavicIconsModule = lsp_kinds["module"], - NavicIconsNamespace = lsp_kinds["namespace"], - NavicIconsPackage = lsp_kinds["package"], - NavicIconsClass = lsp_kinds["class"], - NavicIconsMethod = lsp_kinds["method"], - NavicIconsProperty = lsp_kinds["property"], - NavicIconsField = lsp_kinds["field"], - NavicIconsConstructor = lsp_kinds["constructor"], - NavicIconsEnum = lsp_kinds["enum"], - NavicIconsInterface = lsp_kinds["interface"], - NavicIconsFunction = lsp_kinds["function"], - NavicIconsVariable = lsp_kinds["variable"], - NavicIconsConstant = lsp_kinds["constant"], - NavicIconsString = lsp_kinds["string"], - NavicIconsNumber = lsp_kinds["number"], - NavicIconsBoolean = lsp_kinds["boolean"], - NavicIconsArray = lsp_kinds["array"], - NavicIconsObject = lsp_kinds["object"], - NavicIconsKey = lsp_kinds["key"], - NavicIconsKeyword = lsp_kinds["keyword"], - NavicIconsNull = lsp_kinds["null"], - NavicIconsEnumMember = lsp_kinds["enumMember"], - NavicIconsStruct = lsp_kinds["struct"], - NavicIconsEvent = lsp_kinds["event"], - NavicIconsOperator = lsp_kinds["operator"], - NavicIconsTypeParameter = lsp_kinds["typeParameter"], + NavicIconsFile = lsp_kinds['file'], + NavicIconsModule = lsp_kinds['module'], + NavicIconsNamespace = lsp_kinds['namespace'], + NavicIconsPackage = lsp_kinds['package'], + NavicIconsClass = lsp_kinds['class'], + NavicIconsMethod = lsp_kinds['method'], + NavicIconsProperty = lsp_kinds['property'], + NavicIconsField = lsp_kinds['field'], + NavicIconsConstructor = lsp_kinds['constructor'], + NavicIconsEnum = lsp_kinds['enum'], + NavicIconsInterface = lsp_kinds['interface'], + NavicIconsFunction = lsp_kinds['function'], + NavicIconsVariable = lsp_kinds['variable'], + NavicIconsConstant = lsp_kinds['constant'], + NavicIconsString = lsp_kinds['string'], + NavicIconsNumber = lsp_kinds['number'], + NavicIconsBoolean = lsp_kinds['boolean'], + NavicIconsArray = lsp_kinds['array'], + NavicIconsObject = lsp_kinds['object'], + NavicIconsKey = lsp_kinds['key'], + NavicIconsKeyword = lsp_kinds['keyword'], + NavicIconsNull = lsp_kinds['null'], + NavicIconsEnumMember = lsp_kinds['enumMember'], + NavicIconsStruct = lsp_kinds['struct'], + NavicIconsEvent = lsp_kinds['event'], + NavicIconsOperator = lsp_kinds['operator'], + NavicIconsTypeParameter = lsp_kinds['typeParameter'], NavicText = { fg = c.fg }, NavicSeparator = { fg = c.fg }, @@ -390,40 +390,40 @@ function M.get(c, opts) -- Noice NoiceCompletionItemKindDefault = { fg = c.fg_dark, bg = c.none }, - NoiceCompletionItemKindVariable = lsp_kinds["variable"], - NoiceCompletionItemKindModule = lsp_kinds["module"], - NoiceCompletionItemKindSnippet = lsp_kinds["snippet"], - NoiceCompletionItemKindFolder = lsp_kinds["folder"], - NoiceCompletionItemKindColor = lsp_kinds["color"], - NoiceCompletionItemKindFile = lsp_kinds["file"], - NoiceCompletionItemKindText = lsp_kinds["text"], - NoiceCompletionItemKindMethod = lsp_kinds["method"], - NoiceCompletionItemKindFunction = lsp_kinds["function"], - NoiceCompletionItemKindConstructor = lsp_kinds["constructor"], - NoiceCompletionItemKindField = lsp_kinds["field"], - NoiceCompletionItemKindProperty = lsp_kinds["property"], - NoiceCompletionItemKindUnit = lsp_kinds["unit"], - NoiceCompletionItemKindValue = lsp_kinds["value"], - NoiceCompletionItemKindEnum = lsp_kinds["enum"], - NoiceCompletionItemKindKeyword = lsp_kinds["keyword"], - NoiceCompletionItemKindReference = lsp_kinds["reference"], - NoiceCompletionItemKindConstant = lsp_kinds["constant"], - NoiceCompletionItemKindStruct = lsp_kinds["struct"], - NoiceCompletionItemKindEvent = lsp_kinds["event"], - NoiceCompletionItemKindOperator = lsp_kinds["operator"], - NoiceCompletionItemKindNamespace = lsp_kinds["namespace"], - NoiceCompletionItemKindPackage = lsp_kinds["package"], - NoiceCompletionItemKindString = lsp_kinds["string"], - NoiceCompletionItemKindNumber = lsp_kinds["number"], - NoiceCompletionItemKindBoolean = lsp_kinds["boolean"], - NoiceCompletionItemKindArray = lsp_kinds["array"], - NoiceCompletionItemKindObject = lsp_kinds["object"], - NoiceCompletionItemKindKey = lsp_kinds["key"], - NoiceCompletionItemKindNull = lsp_kinds["null"], - NoiceCompletionItemKindEnumMember = lsp_kinds["enumMember"], - NoiceCompletionItemKindClass = lsp_kinds["class"], - NoiceCompletionItemKindInterface = lsp_kinds["interface"], - NoiceCompletionItemKindTypeParameter = lsp_kinds["typeParameter"], + NoiceCompletionItemKindVariable = lsp_kinds['variable'], + NoiceCompletionItemKindModule = lsp_kinds['module'], + NoiceCompletionItemKindSnippet = lsp_kinds['snippet'], + NoiceCompletionItemKindFolder = lsp_kinds['folder'], + NoiceCompletionItemKindColor = lsp_kinds['color'], + NoiceCompletionItemKindFile = lsp_kinds['file'], + NoiceCompletionItemKindText = lsp_kinds['text'], + NoiceCompletionItemKindMethod = lsp_kinds['method'], + NoiceCompletionItemKindFunction = lsp_kinds['function'], + NoiceCompletionItemKindConstructor = lsp_kinds['constructor'], + NoiceCompletionItemKindField = lsp_kinds['field'], + NoiceCompletionItemKindProperty = lsp_kinds['property'], + NoiceCompletionItemKindUnit = lsp_kinds['unit'], + NoiceCompletionItemKindValue = lsp_kinds['value'], + NoiceCompletionItemKindEnum = lsp_kinds['enum'], + NoiceCompletionItemKindKeyword = lsp_kinds['keyword'], + NoiceCompletionItemKindReference = lsp_kinds['reference'], + NoiceCompletionItemKindConstant = lsp_kinds['constant'], + NoiceCompletionItemKindStruct = lsp_kinds['struct'], + NoiceCompletionItemKindEvent = lsp_kinds['event'], + NoiceCompletionItemKindOperator = lsp_kinds['operator'], + NoiceCompletionItemKindNamespace = lsp_kinds['namespace'], + NoiceCompletionItemKindPackage = lsp_kinds['package'], + NoiceCompletionItemKindString = lsp_kinds['string'], + NoiceCompletionItemKindNumber = lsp_kinds['number'], + NoiceCompletionItemKindBoolean = lsp_kinds['boolean'], + NoiceCompletionItemKindArray = lsp_kinds['array'], + NoiceCompletionItemKindObject = lsp_kinds['object'], + NoiceCompletionItemKindKey = lsp_kinds['key'], + NoiceCompletionItemKindNull = lsp_kinds['null'], + NoiceCompletionItemKindEnumMember = lsp_kinds['enumMember'], + NoiceCompletionItemKindClass = lsp_kinds['class'], + NoiceCompletionItemKindInterface = lsp_kinds['interface'], + NoiceCompletionItemKindTypeParameter = lsp_kinds['typeParameter'], -- Trouble TroubleText = { fg = c.fg_dark }, @@ -479,33 +479,33 @@ function M.get(c, opts) -- Aerial AerialLine = { bg = c.bg_visual, bold = true }, AerialGuide = { fg = c.fg_gutter }, - AerialArrayIcon = lsp_kinds["array"], - AerialBooleanIcon = lsp_kinds["boolean"], - AerialClassIcon = lsp_kinds["class"], - AerialConstantIcon = lsp_kinds["constant"], - AerialConstructorIcon = lsp_kinds["constructor"], - AerialEnumIcon = lsp_kinds["enum"], - AerialEnumMemberIcon = lsp_kinds["enumMember"], - AerialEventIcon = lsp_kinds["event"], - AerialFieldIcon = lsp_kinds["field"], - AerialFileIcon = lsp_kinds["file"], - AerialFunctionIcon = lsp_kinds["function"], - AerialInterfaceIcon = lsp_kinds["interface"], - AerialKeyIcon = lsp_kinds["key"], - AerialKeywordIcon = lsp_kinds["keyword"], - AerialMethodIcon = lsp_kinds["method"], - AerialModuleIcon = lsp_kinds["module"], - AerialNamespaceIcon = lsp_kinds["namespace"], - AerialNullIcon = lsp_kinds["null"], - AerialNumberIcon = lsp_kinds["number"], - AerialObjectIcon = lsp_kinds["object"], - AerialOperatorIcon = lsp_kinds["operator"], - AerialPackageIcon = lsp_kinds["package"], - AerialPropertyIcon = lsp_kinds["property"], - AerialStringIcon = lsp_kinds["string"], - AerialStructIcon = lsp_kinds["struct"], - AerialTypeParameterIcon = lsp_kinds["typeParameter"], - AerialVariableIcon = lsp_kinds["variable"], + AerialArrayIcon = lsp_kinds['array'], + AerialBooleanIcon = lsp_kinds['boolean'], + AerialClassIcon = lsp_kinds['class'], + AerialConstantIcon = lsp_kinds['constant'], + AerialConstructorIcon = lsp_kinds['constructor'], + AerialEnumIcon = lsp_kinds['enum'], + AerialEnumMemberIcon = lsp_kinds['enumMember'], + AerialEventIcon = lsp_kinds['event'], + AerialFieldIcon = lsp_kinds['field'], + AerialFileIcon = lsp_kinds['file'], + AerialFunctionIcon = lsp_kinds['function'], + AerialInterfaceIcon = lsp_kinds['interface'], + AerialKeyIcon = lsp_kinds['key'], + AerialKeywordIcon = lsp_kinds['keyword'], + AerialMethodIcon = lsp_kinds['method'], + AerialModuleIcon = lsp_kinds['module'], + AerialNamespaceIcon = lsp_kinds['namespace'], + AerialNullIcon = lsp_kinds['null'], + AerialNumberIcon = lsp_kinds['number'], + AerialObjectIcon = lsp_kinds['object'], + AerialOperatorIcon = lsp_kinds['operator'], + AerialPackageIcon = lsp_kinds['package'], + AerialPropertyIcon = lsp_kinds['property'], + AerialStringIcon = lsp_kinds['string'], + AerialStructIcon = lsp_kinds['struct'], + AerialTypeParameterIcon = lsp_kinds['typeParameter'], + AerialVariableIcon = lsp_kinds['variable'], -- Neotest NeotestPassed = { fg = c.green }, diff --git a/files/.config/nvim/lua/dracula-pro/groups/treesitter.lua b/files/.config/nvim/lua/dracula-pro/groups/treesitter.lua index 4ff61797..841dfc1e 100644 --- a/files/.config/nvim/lua/dracula-pro/groups/treesitter.lua +++ b/files/.config/nvim/lua/dracula-pro/groups/treesitter.lua @@ -4,137 +4,137 @@ local M = {} function M.get(c, opts) return { - ["@annotation"] = "PreProc", - ["@attribute"] = "PreProc", - ["@boolean"] = "Boolean", - ["@character"] = "Character", - ["@character.printf"] = "SpecialChar", - ["@character.special"] = "SpecialChar", - ["@comment"] = "Comment", - ["@comment.error"] = { fg = c.error }, - ["@comment.hint"] = { fg = c.hint }, - ["@comment.info"] = { fg = c.info }, - ["@comment.note"] = { fg = c.hint }, - ["@comment.todo"] = { fg = c.todo }, - ["@comment.warning"] = { fg = c.warning }, - ["@constant"] = "Constant", - ["@constant.builtin"] = "Special", - ["@constant.macro"] = "Define", - ["@constructor"] = { fg = c.magenta }, - ["@constructor.tsx"] = { fg = c.blue1 }, - ["@diff.delta"] = "DiffChange", - ["@diff.minus"] = "DiffDelete", - ["@diff.plus"] = "DiffAdd", - ["@function"] = "Function", - ["@function.builtin"] = "Special", - ["@function.call"] = "@function", - ["@function.macro"] = "Macro", - ["@function.method"] = "Function", - ["@function.method.call"] = "@function.method", - ["@keyword"] = { fg = c.purple, style = opts.styles.keywords }, - ["@keyword.conditional"] = "Conditional", - ["@keyword.coroutine"] = "@keyword", - ["@keyword.debug"] = "Debug", - ["@keyword.directive"] = "PreProc", - ["@keyword.directive.define"] = "Define", - ["@keyword.exception"] = "Exception", - ["@keyword.function"] = { fg = c.magenta, style = opts.styles.functions }, - ["@keyword.import"] = "Include", - ["@keyword.operator"] = "@operator", - ["@keyword.repeat"] = "Repeat", - ["@keyword.return"] = "@keyword", - ["@keyword.storage"] = "StorageClass", - ["@label"] = { fg = c.blue }, - ["@markup"] = "@none", - ["@markup.emphasis"] = { italic = true }, - ["@markup.environment"] = "Macro", - ["@markup.environment.name"] = "Type", - ["@markup.heading"] = "Title", - ["@markup.italic"] = { italic = true }, - ["@markup.link"] = { fg = c.teal }, - ["@markup.link.label"] = "SpecialChar", - ["@markup.link.label.symbol"] = "Identifier", - ["@markup.link.url"] = "Underlined", - ["@markup.list"] = { fg = c.blue5 }, - ["@markup.list.checked"] = { fg = c.green }, - ["@markup.list.markdown"] = { fg = c.orange, bold = true }, - ["@markup.list.unchecked"] = { fg = c.blue }, - ["@markup.math"] = "Special", - ["@markup.raw"] = "String", - ["@markup.raw.markdown_inline"] = { bg = c.terminal_black, fg = c.blue }, - ["@markup.strikethrough"] = { strikethrough = true }, - ["@markup.strong"] = { bold = true }, - ["@markup.underline"] = { underline = true }, - ["@module"] = "Include", - ["@module.builtin"] = { fg = c.red }, - ["@namespace.builtin"] = "@variable.builtin", - ["@none"] = {}, - ["@number"] = "Number", - ["@number.float"] = "Float", - ["@operator"] = { fg = c.blue5 }, - ["@property"] = { fg = c.cyan }, - ["@punctuation.bracket"] = { fg = c.fg_dark }, - ["@punctuation.delimiter"] = { fg = c.blue5 }, - ["@punctuation.special"] = { fg = c.blue5 }, - ["@string"] = "String", - ["@string.documentation"] = { fg = c.green }, - ["@string.escape"] = { fg = c.magenta }, - ["@string.regexp"] = { fg = c.blue }, - ["@tag"] = { fg = c.red }, - ["@tag.attribute"] = { fg = c.cyan }, - ["@tag.delimiter"] = { fg = c.fg_dark }, - ["@tag.delimiter.tsx"] = { fg = c.blue }, - ["@tag.tsx"] = { fg = c.red }, - ["@type"] = "Type", - ["@type.builtin"] = { fg = c.cyan }, - ["@type.definition"] = "Typedef", - ["@type.qualifier"] = "@keyword", - ["@variable"] = { fg = c.fg, style = opts.styles.variables }, - ["@variable.builtin"] = { fg = c.red }, - ["@variable.member"] = { fg = c.cyan }, - ["@variable.parameter"] = { fg = c.orange }, + ['@annotation'] = 'PreProc', + ['@attribute'] = 'PreProc', + ['@boolean'] = 'Boolean', + ['@character'] = 'Character', + ['@character.printf'] = 'SpecialChar', + ['@character.special'] = 'SpecialChar', + ['@comment'] = 'Comment', + ['@comment.error'] = { fg = c.error }, + ['@comment.hint'] = { fg = c.hint }, + ['@comment.info'] = { fg = c.info }, + ['@comment.note'] = { fg = c.hint }, + ['@comment.todo'] = { fg = c.todo }, + ['@comment.warning'] = { fg = c.warning }, + ['@constant'] = 'Constant', + ['@constant.builtin'] = 'Special', + ['@constant.macro'] = 'Define', + ['@constructor'] = { fg = c.magenta }, + ['@constructor.tsx'] = { fg = c.blue1 }, + ['@diff.delta'] = 'DiffChange', + ['@diff.minus'] = 'DiffDelete', + ['@diff.plus'] = 'DiffAdd', + ['@function'] = 'Function', + ['@function.builtin'] = 'Special', + ['@function.call'] = '@function', + ['@function.macro'] = 'Macro', + ['@function.method'] = 'Function', + ['@function.method.call'] = '@function.method', + ['@keyword'] = { fg = c.purple, style = opts.styles.keywords }, + ['@keyword.conditional'] = 'Conditional', + ['@keyword.coroutine'] = '@keyword', + ['@keyword.debug'] = 'Debug', + ['@keyword.directive'] = 'PreProc', + ['@keyword.directive.define'] = 'Define', + ['@keyword.exception'] = 'Exception', + ['@keyword.function'] = { fg = c.magenta, style = opts.styles.functions }, + ['@keyword.import'] = 'Include', + ['@keyword.operator'] = '@operator', + ['@keyword.repeat'] = 'Repeat', + ['@keyword.return'] = '@keyword', + ['@keyword.storage'] = 'StorageClass', + ['@label'] = { fg = c.blue }, + ['@markup'] = '@none', + ['@markup.emphasis'] = { italic = true }, + ['@markup.environment'] = 'Macro', + ['@markup.environment.name'] = 'Type', + ['@markup.heading'] = 'Title', + ['@markup.italic'] = { italic = true }, + ['@markup.link'] = { fg = c.teal }, + ['@markup.link.label'] = 'SpecialChar', + ['@markup.link.label.symbol'] = 'Identifier', + ['@markup.link.url'] = 'Underlined', + ['@markup.list'] = { fg = c.blue5 }, + ['@markup.list.checked'] = { fg = c.green }, + ['@markup.list.markdown'] = { fg = c.orange, bold = true }, + ['@markup.list.unchecked'] = { fg = c.blue }, + ['@markup.math'] = 'Special', + ['@markup.raw'] = 'String', + ['@markup.raw.markdown_inline'] = { bg = c.terminal_black, fg = c.blue }, + ['@markup.strikethrough'] = { strikethrough = true }, + ['@markup.strong'] = { bold = true }, + ['@markup.underline'] = { underline = true }, + ['@module'] = 'Include', + ['@module.builtin'] = { fg = c.red }, + ['@namespace.builtin'] = '@variable.builtin', + ['@none'] = {}, + ['@number'] = 'Number', + ['@number.float'] = 'Float', + ['@operator'] = { fg = c.blue5 }, + ['@property'] = { fg = c.cyan }, + ['@punctuation.bracket'] = { fg = c.fg_dark }, + ['@punctuation.delimiter'] = { fg = c.blue5 }, + ['@punctuation.special'] = { fg = c.blue5 }, + ['@string'] = 'String', + ['@string.documentation'] = { fg = c.green }, + ['@string.escape'] = { fg = c.magenta }, + ['@string.regexp'] = { fg = c.blue }, + ['@tag'] = { fg = c.red }, + ['@tag.attribute'] = { fg = c.cyan }, + ['@tag.delimiter'] = { fg = c.fg_dark }, + ['@tag.delimiter.tsx'] = { fg = c.blue }, + ['@tag.tsx'] = { fg = c.red }, + ['@type'] = 'Type', + ['@type.builtin'] = { fg = c.cyan }, + ['@type.definition'] = 'Typedef', + ['@type.qualifier'] = '@keyword', + ['@variable'] = { fg = c.fg, style = opts.styles.variables }, + ['@variable.builtin'] = { fg = c.red }, + ['@variable.member'] = { fg = c.cyan }, + ['@variable.parameter'] = { fg = c.orange }, - ["@lsp.type.boolean"] = "@boolean", - ["@lsp.type.builtinType"] = "@type.builtin", - ["@lsp.type.comment"] = "@comment", - ["@lsp.type.decorator"] = "@attribute", - ["@lsp.type.deriveHelper"] = "@attribute", - ["@lsp.type.enum"] = "@type", - ["@lsp.type.enumMember"] = "@constant", - ["@lsp.type.escapeSequence"] = "@string.escape", - ["@lsp.type.formatSpecifier"] = "@markup.list", - ["@lsp.type.generic"] = "@variable", - ["@lsp.type.interface"] = { fg = c.blue1 }, - ["@lsp.type.keyword"] = "@keyword", - ["@lsp.type.lifetime"] = "@storageclass", - ["@lsp.type.namespace"] = "@namespace", - ["@lsp.type.number"] = "@number", - ["@lsp.type.operator"] = "@operator", - ["@lsp.type.parameter"] = "@variable.parameter", - ["@lsp.type.property"] = "@property", - ["@lsp.type.selfKeyword"] = "@variable.builtin", - ["@lsp.type.selfTypeKeyword"] = "@variable.builtin", - ["@lsp.type.string"] = "@string", - ["@lsp.type.typeAlias"] = "@type.definition", - ["@lsp.type.unresolvedReference"] = { undercurl = true, sp = c.error }, - ["@lsp.type.variable"] = {}, - ["@lsp.typemod.class.defaultLibrary"] = "@type.builtin", - ["@lsp.typemod.enum.defaultLibrary"] = "@type.builtin", - ["@lsp.typemod.enumMember.defaultLibrary"] = "@constant.builtin", - ["@lsp.typemod.function.defaultLibrary"] = "@function.builtin", - ["@lsp.typemod.keyword.async"] = "@keyword.coroutine", - ["@lsp.typemod.keyword.injected"] = "@keyword", - ["@lsp.typemod.macro.defaultLibrary"] = "@function.builtin", - ["@lsp.typemod.method.defaultLibrary"] = "@function.builtin", - ["@lsp.typemod.operator.injected"] = "@operator", - ["@lsp.typemod.string.injected"] = "@string", - ["@lsp.typemod.struct.defaultLibrary"] = "@type.builtin", - ["@lsp.typemod.type.defaultLibrary"] = { fg = c.cyan }, - ["@lsp.typemod.typeAlias.defaultLibrary"] = { fg = c.cyan }, - ["@lsp.typemod.variable.callable"] = "@function", - ["@lsp.typemod.variable.defaultLibrary"] = "@variable.builtin", - ["@lsp.typemod.variable.injected"] = "@variable", - ["@lsp.typemod.variable.static"] = "@constant", + ['@lsp.type.boolean'] = '@boolean', + ['@lsp.type.builtinType'] = '@type.builtin', + ['@lsp.type.comment'] = '@comment', + ['@lsp.type.decorator'] = '@attribute', + ['@lsp.type.deriveHelper'] = '@attribute', + ['@lsp.type.enum'] = '@type', + ['@lsp.type.enumMember'] = '@constant', + ['@lsp.type.escapeSequence'] = '@string.escape', + ['@lsp.type.formatSpecifier'] = '@markup.list', + ['@lsp.type.generic'] = '@variable', + ['@lsp.type.interface'] = { fg = c.blue1 }, + ['@lsp.type.keyword'] = '@keyword', + ['@lsp.type.lifetime'] = '@storageclass', + ['@lsp.type.namespace'] = '@namespace', + ['@lsp.type.number'] = '@number', + ['@lsp.type.operator'] = '@operator', + ['@lsp.type.parameter'] = '@variable.parameter', + ['@lsp.type.property'] = '@property', + ['@lsp.type.selfKeyword'] = '@variable.builtin', + ['@lsp.type.selfTypeKeyword'] = '@variable.builtin', + ['@lsp.type.string'] = '@string', + ['@lsp.type.typeAlias'] = '@type.definition', + ['@lsp.type.unresolvedReference'] = { undercurl = true, sp = c.error }, + ['@lsp.type.variable'] = {}, + ['@lsp.typemod.class.defaultLibrary'] = '@type.builtin', + ['@lsp.typemod.enum.defaultLibrary'] = '@type.builtin', + ['@lsp.typemod.enumMember.defaultLibrary'] = '@constant.builtin', + ['@lsp.typemod.function.defaultLibrary'] = '@function.builtin', + ['@lsp.typemod.keyword.async'] = '@keyword.coroutine', + ['@lsp.typemod.keyword.injected'] = '@keyword', + ['@lsp.typemod.macro.defaultLibrary'] = '@function.builtin', + ['@lsp.typemod.method.defaultLibrary'] = '@function.builtin', + ['@lsp.typemod.operator.injected'] = '@operator', + ['@lsp.typemod.string.injected'] = '@string', + ['@lsp.typemod.struct.defaultLibrary'] = '@type.builtin', + ['@lsp.typemod.type.defaultLibrary'] = { fg = c.cyan }, + ['@lsp.typemod.typeAlias.defaultLibrary'] = { fg = c.cyan }, + ['@lsp.typemod.variable.callable'] = '@function', + ['@lsp.typemod.variable.defaultLibrary'] = '@variable.builtin', + ['@lsp.typemod.variable.injected'] = '@variable', + ['@lsp.typemod.variable.static'] = '@constant', } end diff --git a/files/.config/nvim/lua/dracula-pro/init.lua b/files/.config/nvim/lua/dracula-pro/init.lua index 34bdbfe6..9fec7d78 100644 --- a/files/.config/nvim/lua/dracula-pro/init.lua +++ b/files/.config/nvim/lua/dracula-pro/init.lua @@ -4,31 +4,29 @@ local M = {} function M.load() - local config = require("dracula-pro.config") - local colors = require("dracula-pro.colors").setup(config.options) + local config = require('dracula-pro.config') + local colors = require('dracula-pro.colors').setup(config.options) -- Clear existing highlights - if vim.g.colors_name then - vim.cmd("hi clear") - end + if vim.g.colors_name then vim.cmd('hi clear') end vim.o.termguicolors = true - vim.g.colors_name = "dracula-pro" + vim.g.colors_name = 'dracula-pro' -- Load highlight groups local groups = { - require("dracula-pro.groups.base").get(colors, config.options), - require("dracula-pro.groups.treesitter").get(colors, config.options), - require("dracula-pro.groups.plugins").get(colors, config.options), + require('dracula-pro.groups.base').get(colors, config.options), + require('dracula-pro.groups.treesitter').get(colors, config.options), + require('dracula-pro.groups.plugins').get(colors, config.options), } for _, group in ipairs(groups) do for hl, spec in pairs(group) do - if type(spec) == "string" then + if type(spec) == 'string' then vim.api.nvim_set_hl(0, hl, { link = spec }) else -- Resolve style table into highlight attributes - if type(spec.style) == "table" then + if type(spec.style) == 'table' then for k, v in pairs(spec.style) do spec[k] = v end @@ -41,27 +39,25 @@ function M.load() -- Terminal colors if config.options.terminal_colors then - vim.g.terminal_color_0 = "#22212C" - vim.g.terminal_color_1 = "#FF9580" - vim.g.terminal_color_2 = "#8AFF80" - vim.g.terminal_color_3 = "#FFFF80" - vim.g.terminal_color_4 = "#9580FF" - vim.g.terminal_color_5 = "#FF80BF" - vim.g.terminal_color_6 = "#80FFEA" - vim.g.terminal_color_7 = "#F8F8F2" - vim.g.terminal_color_8 = "#504C67" - vim.g.terminal_color_9 = "#FFAA99" - vim.g.terminal_color_10 = "#A2FF99" - vim.g.terminal_color_11 = "#FFFF99" - vim.g.terminal_color_12 = "#AA99FF" - vim.g.terminal_color_13 = "#FF99CC" - vim.g.terminal_color_14 = "#99FFEE" - vim.g.terminal_color_15 = "#FFFFFF" + vim.g.terminal_color_0 = '#22212C' + vim.g.terminal_color_1 = '#FF9580' + vim.g.terminal_color_2 = '#8AFF80' + vim.g.terminal_color_3 = '#FFFF80' + vim.g.terminal_color_4 = '#9580FF' + vim.g.terminal_color_5 = '#FF80BF' + vim.g.terminal_color_6 = '#80FFEA' + vim.g.terminal_color_7 = '#F8F8F2' + vim.g.terminal_color_8 = '#504C67' + vim.g.terminal_color_9 = '#FFAA99' + vim.g.terminal_color_10 = '#A2FF99' + vim.g.terminal_color_11 = '#FFFF99' + vim.g.terminal_color_12 = '#AA99FF' + vim.g.terminal_color_13 = '#FF99CC' + vim.g.terminal_color_14 = '#99FFEE' + vim.g.terminal_color_15 = '#FFFFFF' end end -function M.setup(opts) - require("dracula-pro.config").setup(opts) -end +function M.setup(opts) require('dracula-pro.config').setup(opts) end return M diff --git a/files/.config/nvim/lua/highlight.lua b/files/.config/nvim/lua/highlight.lua index 3c97ac8c..21bc41f3 100644 --- a/files/.config/nvim/lua/highlight.lua +++ b/files/.config/nvim/lua/highlight.lua @@ -75,7 +75,6 @@ local function get_hl_as_hex(opts, ns) return hl end - ---Get the value a highlight group whilst handling errors, fallbacks as well as returning a gui value ---If no attribute is specified return the entire highlight table ---in the right format diff --git a/files/.config/nvim/lua/tools.lua b/files/.config/nvim/lua/tools.lua index e3b040f2..a57085f4 100644 --- a/files/.config/nvim/lua/tools.lua +++ b/files/.config/nvim/lua/tools.lua @@ -612,9 +612,15 @@ function M.ui.refresh_palette() derived.light_red = palette_tint(derived.pale_red, -0.15) derived.dark_red = palette_tint(derived.pale_red, -0.30) - for k in pairs(palette) do palette[k] = nil end - for k, v in pairs(defaults) do palette[k] = derived[k] or v end - for k, v in pairs(derived) do palette[k] = v end + for k in pairs(palette) do + palette[k] = nil + end + for k, v in pairs(defaults) do + palette[k] = derived[k] or v + end + for k, v in pairs(derived) do + palette[k] = v + end -- Keep LSP colors in sync local lsp = M.ui.lsp diff --git a/files/.config/nvim/plugin/autocommands.lua b/files/.config/nvim/plugin/autocommands.lua index 13074d71..e5af403b 100644 --- a/files/.config/nvim/plugin/autocommands.lua +++ b/files/.config/nvim/plugin/autocommands.lua @@ -1,7 +1,6 @@ local augroup = require('tools').augroup -local fn, api, v, cmd = - vim.fn, vim.api, vim.v, vim.cmd +local fn, api, v, cmd = vim.fn, vim.api, vim.v, vim.cmd -- Better terminal UX inside fzf-lua: allow Esc to abort. api.nvim_create_autocmd('FileType', { @@ -55,13 +54,16 @@ do local win = api.nvim_get_current_win() if not api.nvim_win_is_valid(win) then return end - api.nvim_win_call(win, function() - vim.opt_local.winhighlight:append({ - Normal = 'Normal', - NormalNC = 'NormalNC', - NormalFloat = 'Normal', - }) - end) + api.nvim_win_call( + win, + function() + vim.opt_local.winhighlight:append({ + Normal = 'Normal', + NormalNC = 'NormalNC', + NormalFloat = 'Normal', + }) + end + ) end api.nvim_create_autocmd( @@ -86,7 +88,10 @@ end -- Highlight when yanking (copying) text vim.api.nvim_create_autocmd('TextYankPost', { desc = 'Highlight when yanking (copying) text', - group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }), + group = vim.api.nvim_create_augroup( + 'kickstart-highlight-yank', + { clear = true } + ), callback = function() vim.highlight.on_yank() end, }) @@ -391,12 +396,22 @@ vim.api.nvim_create_autocmd({ 'InsertEnter', 'WinLeave' }, { -- Disable statuscolumn/signcolumn/foldcolumn in floating windows and tool panels. do - local group = vim.api.nvim_create_augroup('DisableColumnsInFloats', { clear = true }) + local group = + vim.api.nvim_create_augroup('DisableColumnsInFloats', { clear = true }) local no_number_filetypes = { - 'lazy', 'mason', 'noice', 'notify', 'trouble', 'aerial', - 'dap-repl', 'dapui_console', 'dapui_watches', 'dapui_stacks', - 'dapui_breakpoints', 'dapui_scopes', + 'lazy', + 'mason', + 'noice', + 'notify', + 'trouble', + 'aerial', + 'dap-repl', + 'dapui_console', + 'dapui_watches', + 'dapui_stacks', + 'dapui_breakpoints', + 'dapui_scopes', } local function disable_decoration_columns(win) @@ -437,16 +452,14 @@ do callback = function() local win_config = vim.api.nvim_win_get_config(0) if win_config.relative == '' then - vim.cmd [[setlocal winhl=CursorLine:CursorLineNC]] + vim.cmd([[setlocal winhl=CursorLine:CursorLineNC]]) end end, }) vim.api.nvim_create_autocmd('WinEnter', { group = group, - callback = function() - vim.cmd [[setlocal winhl=]] - end, + callback = function() vim.cmd([[setlocal winhl=]]) end, }) end @@ -480,7 +493,10 @@ do end vim.api.nvim_create_autocmd('FileType', { - group = vim.api.nvim_create_augroup('SidebarPanelHighlights', { clear = true }), + group = vim.api.nvim_create_augroup( + 'SidebarPanelHighlights', + { clear = true } + ), pattern = sidebar_fts, callback = on_sidebar_enter, }) diff --git a/files/.config/nvim/tests/integration/test_keymaps.lua b/files/.config/nvim/tests/integration/test_keymaps.lua index 2f747078..3728123c 100644 --- a/files/.config/nvim/tests/integration/test_keymaps.lua +++ b/files/.config/nvim/tests/integration/test_keymaps.lua @@ -6,10 +6,8 @@ local T = MiniTest.new_set() local eq = MiniTest.expect.equality -local config_root = vim.fn.fnamemodify( - debug.getinfo(1, 'S').source:sub(2), - ':h:h:h' -) +local config_root = + vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h:h') T['keymaps'] = MiniTest.new_set({ hooks = { @@ -28,10 +26,12 @@ T['keymaps'] = MiniTest.new_set({ --- Check that a keymap is registered for the given mode and lhs. local function has_map(mode, lhs) return T.child.lua_get( - ('(function() ' + ( + '(function() ' .. 'local maps = vim.fn.maparg(%q, %q, false, true); ' .. 'return maps ~= nil and maps.lhs ~= nil ' - .. 'end)()'):format(lhs, mode) + .. 'end)()' + ):format(lhs, mode) ) end @@ -50,8 +50,6 @@ end T['keymaps']['[l moves to prev loclist item'] = function() eq(has_map('n', '[l'), true) end -T['keymaps']['g> shows messages'] = function() - eq(has_map('n', 'g>'), true) -end +T['keymaps']['g> shows messages'] = function() eq(has_map('n', 'g>'), true) end return T diff --git a/files/.config/nvim/tests/integration/test_lsp.lua b/files/.config/nvim/tests/integration/test_lsp.lua index 4aa55938..d377276c 100644 --- a/files/.config/nvim/tests/integration/test_lsp.lua +++ b/files/.config/nvim/tests/integration/test_lsp.lua @@ -39,9 +39,9 @@ local EXTRA_TOOLS = { -- mason-lspconfig package names (differ from server names for some servers) local SERVER_TO_PACKAGE = { - lua_ls = 'lua-language-server', + lua_ls = 'lua-language-server', basedpyright = 'basedpyright', - ruff = 'ruff', + ruff = 'ruff', } -- ─── Tier 1: mason-registry knows every package ─────────────────────────────── @@ -92,15 +92,13 @@ end for _, server in ipairs(LSP_SERVERS) do local pkg = SERVER_TO_PACKAGE[server] or server local sname = server - T['mason registry']['lsp: ' .. sname] = function() - eq(registry_has(pkg), true) - end + T['mason registry']['lsp: ' .. sname] = function() eq(registry_has(pkg), true) end end -- ─── Tier 2: on-disk presence ───────────────────────────────────────────────── local mason_root = real_data .. '/mason' -local mason_bin = mason_root .. '/bin' +local mason_bin = mason_root .. '/bin' local mason_pkgs = mason_root .. '/packages' local mason_bin_exists = vim.loop.fs_stat(mason_bin) ~= nil diff --git a/files/.config/nvim/tests/integration/test_options.lua b/files/.config/nvim/tests/integration/test_options.lua index 97698b27..6af50859 100644 --- a/files/.config/nvim/tests/integration/test_options.lua +++ b/files/.config/nvim/tests/integration/test_options.lua @@ -18,48 +18,28 @@ T['options'] = MiniTest.new_set({ T.child = MiniTest.new_child_neovim() T.child.start({ '-u', 'NONE' }) -- Put the config lua/ directory on rtp so require() works - T.child.lua( - ('vim.opt.rtp:prepend(%q)'):format(config_root) - ) + T.child.lua(('vim.opt.rtp:prepend(%q)'):format(config_root)) T.child.lua("require('options')") end, post_once = function() T.child.stop() end, }, }) -local function get_opt(name) - return T.child.lua_get(('vim.o[%q]'):format(name)) -end +local function get_opt(name) return T.child.lua_get(('vim.o[%q]'):format(name)) end -T['options']['number is set'] = function() - eq(get_opt('number'), true) -end +T['options']['number is set'] = function() eq(get_opt('number'), true) end T['options']['relativenumber is set'] = function() eq(get_opt('relativenumber'), true) end -T['options']['splitbelow is set'] = function() - eq(get_opt('splitbelow'), true) -end -T['options']['splitright is set'] = function() - eq(get_opt('splitright'), true) -end -T['options']['undofile is set'] = function() - eq(get_opt('undofile'), true) -end +T['options']['splitbelow is set'] = function() eq(get_opt('splitbelow'), true) end +T['options']['splitright is set'] = function() eq(get_opt('splitright'), true) end +T['options']['undofile is set'] = function() eq(get_opt('undofile'), true) end T['options']['termguicolors is set'] = function() eq(get_opt('termguicolors'), true) end -T['options']['ignorecase is set'] = function() - eq(get_opt('ignorecase'), true) -end -T['options']['smartcase is set'] = function() - eq(get_opt('smartcase'), true) -end -T['options']['expandtab is set'] = function() - eq(get_opt('expandtab'), true) -end -T['options']['showtabline is 0'] = function() - eq(get_opt('showtabline'), 0) -end +T['options']['ignorecase is set'] = function() eq(get_opt('ignorecase'), true) end +T['options']['smartcase is set'] = function() eq(get_opt('smartcase'), true) end +T['options']['expandtab is set'] = function() eq(get_opt('expandtab'), true) end +T['options']['showtabline is 0'] = function() eq(get_opt('showtabline'), 0) end return T diff --git a/files/.config/nvim/tests/integration/test_plugins_load.lua b/files/.config/nvim/tests/integration/test_plugins_load.lua index 25ca7292..d49271ee 100644 --- a/files/.config/nvim/tests/integration/test_plugins_load.lua +++ b/files/.config/nvim/tests/integration/test_plugins_load.lua @@ -4,10 +4,8 @@ local T = MiniTest.new_set() -local config_root = vim.fn.fnamemodify( - debug.getinfo(1, 'S').source:sub(2), - ':h:h:h' -) +local config_root = + vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h:h') vim.opt.rtp:prepend(config_root) @@ -19,8 +17,8 @@ local function loads(mod_name) end end -loads('tools') -- core utilities + ui + strings + colors -loads('highlight') -- highlight helpers (depends on tools) +loads('tools') -- core utilities + ui + strings + colors +loads('highlight') -- highlight helpers (depends on tools) -- options.lua has side effects but returns nothing; just check it doesn't error T['options.lua executes without error'] = function() diff --git a/files/.config/nvim/tests/minimal_init.lua b/files/.config/nvim/tests/minimal_init.lua index 472fbb31..88bbda8d 100644 --- a/files/.config/nvim/tests/minimal_init.lua +++ b/files/.config/nvim/tests/minimal_init.lua @@ -23,7 +23,8 @@ end vim.opt.rtp:prepend(mini_test_path) -- Add the nvim config root so tests can `require('tools')`, `require('highlight')`, etc. -local config_root = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h') +local config_root = + vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':h:h') vim.opt.rtp:prepend(config_root) -- Stub out vim.notify to avoid noise during unit tests diff --git a/files/.config/nvim/tests/unit/test_strings.lua b/files/.config/nvim/tests/unit/test_strings.lua index 65fc8d2f..c4bd2697 100644 --- a/files/.config/nvim/tests/unit/test_strings.lua +++ b/files/.config/nvim/tests/unit/test_strings.lua @@ -13,9 +13,7 @@ T['spacer']['returns nil for size < 1'] = function() eq(S.spacer(0), nil) eq(S.spacer(-1), nil) end -T['spacer']['returns nil when size is absent'] = function() - eq(S.spacer(), nil) -end +T['spacer']['returns nil when size is absent'] = function() eq(S.spacer(), nil) end T['spacer']['returns a component with correct padding'] = function() local result = S.spacer(3) MiniTest.expect.no_equality(result, nil) diff --git a/files/.config/nvim/tests/unit/test_tools.lua b/files/.config/nvim/tests/unit/test_tools.lua index 44134664..9664f0d3 100644 --- a/files/.config/nvim/tests/unit/test_tools.lua +++ b/files/.config/nvim/tests/unit/test_tools.lua @@ -13,9 +13,7 @@ T['falsy'] = MiniTest.new_set() T['falsy']['nil returns true'] = function() eq(tools.falsy(nil), true) end T['falsy']['false returns true'] = function() eq(tools.falsy(false), true) end T['falsy']['true returns false'] = function() eq(tools.falsy(true), false) end -T['falsy']['empty string returns true'] = function() - eq(tools.falsy(''), true) -end +T['falsy']['empty string returns true'] = function() eq(tools.falsy(''), true) end T['falsy']['non-empty string returns false'] = function() eq(tools.falsy('hello'), false) end @@ -26,9 +24,7 @@ end T['falsy']['positive number returns false'] = function() eq(tools.falsy(1), false) end -T['falsy']['empty table returns true'] = function() - eq(tools.falsy({}), true) -end +T['falsy']['empty table returns true'] = function() eq(tools.falsy({}), true) end T['falsy']['non-empty table returns false'] = function() eq(tools.falsy({ 1 }), false) end @@ -75,13 +71,15 @@ end T['fold'] = MiniTest.new_set() T['fold']['sums a list'] = function() - local sum = - tools.fold(function(acc, v) return acc + v end, { 1, 2, 3, 4 }, 0) + local sum = tools.fold(function(acc, v) return acc + v end, { 1, 2, 3, 4 }, 0) eq(sum, 10) end T['fold']['concatenates strings'] = function() - local result = - tools.fold(function(acc, v) return acc .. v end, { 'a', 'b', 'c' }, '') + local result = tools.fold( + function(acc, v) return acc .. v end, + { 'a', 'b', 'c' }, + '' + ) eq(result, 'abc') end T['fold']['uses empty table as default accumulator'] = function() @@ -102,8 +100,7 @@ T['map']['doubles each element'] = function() eq(result, { 2, 4, 6 }) end T['map']['maps strings to uppercase'] = function() - local result = - tools.map(function(v) return v:upper() end, { 'a', 'b', 'c' }) + local result = tools.map(function(v) return v:upper() end, { 'a', 'b', 'c' }) eq(result, { 'A', 'B', 'C' }) end T['map']['returns empty table for empty input'] = function() From 485baa18d8ad8a2e6b674a50e57228b5e29abba3 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 19 Mar 2026 01:13:09 +0100 Subject: [PATCH 23/27] feat: update plugins --- files/.config/nvim/lazy-lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/files/.config/nvim/lazy-lock.json b/files/.config/nvim/lazy-lock.json index 9e2ba827..4a2b84af 100644 --- a/files/.config/nvim/lazy-lock.json +++ b/files/.config/nvim/lazy-lock.json @@ -5,6 +5,7 @@ "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, "ccc.nvim": { "branch": "main", "commit": "9d1a256e006decc574789dfc7d628ca11644d4c2" }, "codecompanion.nvim": { "branch": "main", "commit": "5e4f7cfac58c94c61d5a3d3a99d715d5c8762db6" }, + "codediff.nvim": { "branch": "main", "commit": "93cd80c56f71af4671388c568a37d4c84c3ddefa" }, "conform.nvim": { "branch": "master", "commit": "086a40dc7ed8242c03be9f47fbcee68699cc2395" }, "copilot.lua": { "branch": "master", "commit": "8e2a91828210d6043744468f6d7027d256a41f42" }, "crates.nvim": { "branch": "main", "commit": "ac9fa498a9edb96dc3056724ff69d5f40b898453" }, @@ -53,6 +54,7 @@ "mini.starter": { "branch": "main", "commit": "cdf909e5bda577e09c61fa6d9a36bb2a88dbc636" }, "mini.surround": { "branch": "main", "commit": "d205d1741d1fcc1f3117b4e839bf00f74ad72fa2" }, "mini.trailspace": { "branch": "main", "commit": "27acb69562a4742256ab3e4b0127391fcb49dbb3" }, + "neogit": { "branch": "master", "commit": "d3890fc3cdf0859845a86b2be306bba01458df1a" }, "neotest": { "branch": "master", "commit": "deadfb1af5ce458742671ad3a013acb9a6b41178" }, "neotest-plenary": { "branch": "master", "commit": "3523adcf9ffaad1911960c5813b0136c1b63a2ec" }, "neotest-python": { "branch": "master", "commit": "b0d3a861bd85689d8ed73f0590c47963a7eb1bf9" }, @@ -74,7 +76,6 @@ "nvim-treesitter-textobjects": { "branch": "main", "commit": "4e91b5d0394329a229725b021a8ea217099826ef" }, "nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" }, "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, - "nvim-web-devicons": { "branch": "master", "commit": "d7462543c9e366c0d196c7f67a945eaaf5d99414" }, "obsidian.nvim": { "branch": "main", "commit": "14e0427bef6c55da0d63f9a313fd9941be3a2479" }, "oil.nvim": { "branch": "master", "commit": "0fcc83805ad11cf714a949c98c605ed717e0b83e" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, From f8ba4f274865013cd9d0bfabcc509ad820648946 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 26 Mar 2026 10:08:59 +0100 Subject: [PATCH 24/27] feat: better git --- files/.config/nvim/lazy-lock.json | 21 ++- .../nvim/lua/custom/plugins/bufferline.lua | 89 ++-------- files/.config/nvim/lua/custom/plugins/git.lua | 167 +++++++++++++++++- files/.config/nvim/plugin/colors.lua | 79 ++------- install/install_dependencies.sh | 2 + scripts/gwrm | 72 ++++++++ 6 files changed, 281 insertions(+), 149 deletions(-) create mode 100755 scripts/gwrm diff --git a/files/.config/nvim/lazy-lock.json b/files/.config/nvim/lazy-lock.json index 4a2b84af..d6207a9e 100644 --- a/files/.config/nvim/lazy-lock.json +++ b/files/.config/nvim/lazy-lock.json @@ -1,13 +1,12 @@ { "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, "blink-cmp-avante": { "branch": "master", "commit": "4f494c6e124acbe31a8f5d58effa0c14aa38a6d5" }, - "blink.cmp": { "branch": "main", "commit": "539053d87740e357a96fb304c9a4da2ef27b3576" }, + "blink.cmp": { "branch": "main", "commit": "451168851e8e2466bc97ee3e026c3dcb9141ce07" }, "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, "ccc.nvim": { "branch": "main", "commit": "9d1a256e006decc574789dfc7d628ca11644d4c2" }, - "codecompanion.nvim": { "branch": "main", "commit": "5e4f7cfac58c94c61d5a3d3a99d715d5c8762db6" }, - "codediff.nvim": { "branch": "main", "commit": "93cd80c56f71af4671388c568a37d4c84c3ddefa" }, + "codecompanion.nvim": { "branch": "main", "commit": "9827dbdde55217e7b4185e744eae15f74e9c5e8b" }, "conform.nvim": { "branch": "master", "commit": "086a40dc7ed8242c03be9f47fbcee68699cc2395" }, - "copilot.lua": { "branch": "master", "commit": "8e2a91828210d6043744468f6d7027d256a41f42" }, + "copilot.lua": { "branch": "master", "commit": "0552b44fceedf0c4cba2cd4953d3976633b2509a" }, "crates.nvim": { "branch": "main", "commit": "ac9fa498a9edb96dc3056724ff69d5f40b898453" }, "csvview.nvim": { "branch": "main", "commit": "7022e18a0fbae9aecf99a3ba02b2a541edc2b8a1" }, "debugprint.nvim": { "branch": "main", "commit": "4f29196c5fe32752bb7a3e8bc83d4a4d2f3b7922" }, @@ -17,7 +16,8 @@ "file-line": { "branch": "main", "commit": "559088afaf10124ea663ee0f4f73b1de48fb1632" }, "fold-cycle.nvim": { "branch": "main", "commit": "6144567b3307bbcfed0e5b2dd23acb9576575d9e" }, "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, - "fzf-lua": { "branch": "main", "commit": "65c848961e66de995052ead60970fbde101ff18f" }, + "fzf-lua": { "branch": "main", "commit": "8a79ee54d6216d10b2f153921a12b152be0c1a20" }, + "git-conflict.nvim": { "branch": "main", "commit": "a1badcd070d176172940eb55d9d59029dad1c5a6" }, "git-worktree.nvim": { "branch": "master", "commit": "f247308e68dab9f1133759b05d944569ad054546" }, "gitgraph.nvim": { "branch": "main", "commit": "c16daa7d7dd597caf9085644c009cfa80b75db8e" }, "gitlinker.nvim": { "branch": "master", "commit": "cc59f732f3d043b626c8702cb725c82e54d35c25" }, @@ -34,7 +34,7 @@ "lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" }, "lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" }, "markdown-table-mode.nvim": { "branch": "main", "commit": "bb1ea9b76c1b29e15e14806fdfbb2319df5c06f1" }, - "mason-lspconfig.nvim": { "branch": "main", "commit": "a676ab7282da8d651e175118bcf54483ca11e46d" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "a979821a975897b88493843301950c456a725982" }, "mason-nvim-dap.nvim": { "branch": "main", "commit": "9a10e096703966335bd5c46c8c875d5b0690dade" }, "mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" }, "mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, @@ -54,7 +54,6 @@ "mini.starter": { "branch": "main", "commit": "cdf909e5bda577e09c61fa6d9a36bb2a88dbc636" }, "mini.surround": { "branch": "main", "commit": "d205d1741d1fcc1f3117b4e839bf00f74ad72fa2" }, "mini.trailspace": { "branch": "main", "commit": "27acb69562a4742256ab3e4b0127391fcb49dbb3" }, - "neogit": { "branch": "master", "commit": "d3890fc3cdf0859845a86b2be306bba01458df1a" }, "neotest": { "branch": "master", "commit": "deadfb1af5ce458742671ad3a013acb9a6b41178" }, "neotest-plenary": { "branch": "master", "commit": "3523adcf9ffaad1911960c5813b0136c1b63a2ec" }, "neotest-python": { "branch": "master", "commit": "b0d3a861bd85689d8ed73f0590c47963a7eb1bf9" }, @@ -67,21 +66,21 @@ "nvim-dev-container": { "branch": "main", "commit": "87ea57f420b3460d0c45e239057ffc38fa32f886" }, "nvim-lightbulb": { "branch": "master", "commit": "aa3a8b0f4305b25cfe368f6c9be9923a7c9d0805" }, "nvim-lint": { "branch": "master", "commit": "606b823a57b027502a9ae00978ebf4f5d5158098" }, - "nvim-lspconfig": { "branch": "master", "commit": "4d0724be90b633ddce51b328a631060e6acd7d66" }, + "nvim-lspconfig": { "branch": "master", "commit": "841c6d4139aedb8a3f2baf30cef5327371385b93" }, "nvim-lspimport": { "branch": "main", "commit": "9c1c61a5020faeb1863bb66eb4b2a9107e641876" }, "nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" }, "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, "nvim-treesitter-context": { "branch": "master", "commit": "9a8e39993e3b895601bf8227124a48ea8268149e" }, - "nvim-treesitter-textobjects": { "branch": "main", "commit": "4e91b5d0394329a229725b021a8ea217099826ef" }, + "nvim-treesitter-textobjects": { "branch": "main", "commit": "7359dfcefa38db632541e1f9b5b5f291626a1d47" }, "nvim-ts-autotag": { "branch": "main", "commit": "8e1c0a389f20bf7f5b0dd0e00306c1247bda2595" }, "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, "obsidian.nvim": { "branch": "main", "commit": "14e0427bef6c55da0d63f9a313fd9941be3a2479" }, "oil.nvim": { "branch": "master", "commit": "0fcc83805ad11cf714a949c98c605ed717e0b83e" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, - "rainbow-delimiters.nvim": { "branch": "master", "commit": "01993eb20c6cdc1d33e7e98252368840309f99b9" }, + "rainbow-delimiters.nvim": { "branch": "master", "commit": "607a438d8c647a355749973fd295e33505afafde" }, "render-markdown.nvim": { "branch": "main", "commit": "e3c18ddd27a853f85a6f513a864cf4f2982b9f26" }, - "sidekick.nvim": { "branch": "main", "commit": "c2bdf8cfcd87a6be5f8b84322c1b5052e78e302e" }, + "sidekick.nvim": { "branch": "main", "commit": "8ad0ede3ff9065878a853e4b037ba6ac88231fb0" }, "sonarqube.nvim": { "branch": "master", "commit": "811bf8f46e0aa5ed9c9b5783aaf6f87640df553a" }, "symbol-usage.nvim": { "branch": "main", "commit": "e07c07dfe7504295a369281e95a24e1afa14b243" }, "symbols-outline.nvim": { "branch": "master", "commit": "564ee65dfc9024bdde73a6621820866987cbb256" }, diff --git a/files/.config/nvim/lua/custom/plugins/bufferline.lua b/files/.config/nvim/lua/custom/plugins/bufferline.lua index c410db42..fd6c035b 100644 --- a/files/.config/nvim/lua/custom/plugins/bufferline.lua +++ b/files/.config/nvim/lua/custom/plugins/bufferline.lua @@ -49,19 +49,15 @@ return { -- Visible (non-focused split) buffer buffer_visible = { bg = bg, fg = fg_dim }, - -- Separators are invisible (same bg) — no clutter - separator = { bg = bg, fg = bg }, - separator_selected = { bg = bg, fg = bg }, - separator_visible = { bg = bg, fg = bg }, - offset_separator = { bg = bg, fg = bg }, + -- Powerline triangle separators + separator = { bg = bg, fg = bg_sel }, + separator_selected = { bg = bg_sel, fg = bg }, + separator_visible = { bg = bg, fg = bg_sel }, + offset_separator = { bg = bg, fg = bg_sel }, -- Indicator line under selected tab uses the accent colour indicator_selected = { fg = accent, bg = bg_sel }, - -- Group labels - group_separator = { bg = bg, fg = fg_dim }, - group_label = { bg = bg, fg = accent }, - -- Numbers numbers = { bg = bg, fg = fg_dim }, numbers_selected = { bg = bg_sel, fg = fg_sel }, @@ -78,6 +74,7 @@ return { options = { style_preset = bufferline.style_preset.minimal, + separator_style = { '', '' }, mode = 'buffers', custom_areas = { right = function() @@ -89,7 +86,7 @@ return { local sym = NUMS[i] or tostring(i) local hl = tab == cur and 'BufferLineTabSelected' or 'BufferLineTab' - table.insert(result, { text = ' ' .. sym .. ' ', link = hl }) + table.insert(result, { text = ' ' .. sym .. ' ', link = hl }) end return result end, @@ -100,9 +97,14 @@ return { show_close_icon = false, show_buffer_close_icons = false, show_tab_indicators = false, + indicator = { style = 'none' }, - -- Top-bar indicator for the selected buffer - -- indicator = { style = 'icon', icon = '▀' }, + custom_filter = function(buf_number) + local name = vim.api.nvim_buf_get_name(buf_number) + if name:match('^fugitive://') then return false end + if name == '' then return false end + return true + end, hover = { enabled = true, delay = 150, reveal = { 'close' } }, @@ -136,69 +138,6 @@ return { text_align = 'left', }, }, - - groups = { - options = { toggle_hidden_on_enter = true }, - items = { - bufferline.groups.builtin.pinned:with({ icon = '' }), - bufferline.groups.builtin.ungrouped, - { - name = 'Dependencies', - icon = '', - highlight = { fg = '#ECBE7B' }, - matcher = function(buf) - return vim.startswith(buf.path, vim.env.VIMRUNTIME) - end, - }, - { - name = 'Terraform', - matcher = function(buf) return buf.name:match('%.tf') ~= nil end, - }, - { - name = 'Kubernetes', - matcher = function(buf) - return buf.name:match('kubernetes') - and buf.name:match('%.yaml') - end, - }, - { - name = 'SQL', - matcher = function(buf) return buf.name:match('%.sql$') end, - }, - { - name = 'Tests', - icon = '', - matcher = function(buf) - local name = buf.name - return name:match('[_%.]spec') or name:match('[_%.]test') - end, - }, - { - name = 'Docs', - icon = '', - matcher = function(buf) - if - vim.bo[buf.id].filetype == 'man' - or buf.path:match('man://') - then - return true - end - for _, ext in ipairs({ 'md', 'txt', 'org', 'norg', 'wiki' }) do - if ext == vim.fn.fnamemodify(buf.path, ':e') then - return true - end - end - end, - }, - { - name = 'Git', - icon = '', - matcher = function(buf) - if buf.path:match('fugitive://') then return true end - end, - }, - }, - }, }, }) end diff --git a/files/.config/nvim/lua/custom/plugins/git.lua b/files/.config/nvim/lua/custom/plugins/git.lua index ae84273f..c1487251 100644 --- a/files/.config/nvim/lua/custom/plugins/git.lua +++ b/files/.config/nvim/lua/custom/plugins/git.lua @@ -197,15 +197,64 @@ return { -- }}} ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- + -- git-conflict {{{ + ----------------------------------------------------------------------------- + { + 'akinsho/git-conflict.nvim', + event = 'BufReadPre', + opts = { + default_mappings = true, + default_commands = true, + disable_diagnostics = true, + list_opener = 'copen', + highlights = { + incoming = 'DiffAdd', + current = 'DiffText', + }, + }, + }, + -- }}} + ----------------------------------------------------------------------------- + ----------------------------------------------------------------------------- -- fugitive {{{ ----------------------------------------------------------------------------- { 'tpope/vim-fugitive', cmd = { 'Git' }, + event = 'BufReadPre', + init = function() + vim.api.nvim_create_autocmd('DirChanged', { + group = vim.api.nvim_create_augroup('FugitiveWorktreeDetect', { clear = true }), + callback = function() + vim.fn.FugitiveDetect(vim.fn.getcwd()) + end, + }) + end, keys = { { 'gs', - 'Git', + function() + -- FugitiveCommonDir returns the shared .bare dir for all worktrees of the same repo + local cur_common = vim.fn.FugitiveCommonDir(vim.api.nvim_get_current_buf()) + if cur_common == '' then + vim.cmd('tab Git') + return + end + for _, tabnr in ipairs(vim.api.nvim_list_tabpages()) do + for _, winnr in ipairs(vim.api.nvim_tabpage_list_wins(tabnr)) do + local buf = vim.api.nvim_win_get_buf(winnr) + local name = vim.api.nvim_buf_get_name(buf) + if name:match('^fugitive://') then + local tab_common = vim.fn.FugitiveCommonDir(buf) + if tab_common ~= '' and tab_common == cur_common then + vim.api.nvim_set_current_tabpage(tabnr) + return + end + end + end + end + vim.cmd('tab Git') + end, desc = '[git] Status', mode = 'n', }, @@ -364,6 +413,122 @@ return { 'GitWorktreeList', 'GitWorktreeSwitch', }, + keys = { + { + 'gwl', + function() + require('fzf-lua').fzf_exec(function(cb) + local lines = vim.fn.systemlist('git worktree list --porcelain') + local worktrees = {} + local cur = {} + for _, line in ipairs(lines) do + local path = line:match('^worktree (.+)') + local branch = line:match('^branch refs/heads/(.+)') + if path then cur = { path = path } end + if branch then cur.branch = branch end + if line == '' and cur.path then + table.insert(worktrees, cur) + cur = {} + end + end + if cur.path then table.insert(worktrees, cur) end + for _, wt_entry in ipairs(worktrees) do + cb(string.format('%s\t%s', wt_entry.branch or '(detached)', wt_entry.path)) + end + cb(nil) + end, { + prompt = 'Worktrees> ', + actions = { + ['default'] = function(selected) + local path = selected[1]:match('\t(.+)$') + if path then + require('git-worktree').switch_worktree(path) + end + end, + }, + }) + end, + desc = '[git] List/switch worktree', + }, + { + 'gwd', + function() + require('fzf-lua').fzf_exec(function(cb) + local lines = vim.fn.systemlist('git worktree list --porcelain') + local worktrees = {} + local cur = {} + for _, line in ipairs(lines) do + local path = line:match('^worktree (.+)') + local branch = line:match('^branch refs/heads/(.+)') + if path then cur = { path = path } end + if branch then cur.branch = branch end + if line == '' and cur.path then + table.insert(worktrees, cur) + cur = {} + end + end + if cur.path then table.insert(worktrees, cur) end + -- skip the main worktree (first entry) + for i, wt_entry in ipairs(worktrees) do + if i > 1 then + cb(string.format('%s\t%s', wt_entry.branch or '(detached)', wt_entry.path)) + end + end + cb(nil) + end, { + prompt = 'Delete worktree> ', + fzf_opts = { ['--header'] = 'CTRL-D: force delete branch | ENTER: remove worktree only' }, + actions = { + ['default'] = function(selected) + local path = selected[1]:match('\t(.+)$') + if not path then return end + vim.ui.input( + { prompt = 'Flags ([-f] [-d|-D], or enter to confirm): ' }, + function(flags) + if flags == nil then return end -- cancelled + local cmd = 'gwrm ' .. (flags ~= '' and flags .. ' ' or '') .. vim.fn.shellescape(path) + local out = vim.fn.system(cmd) + if vim.v.shell_error ~= 0 then + vim.notify(out, vim.log.levels.ERROR) + else + vim.notify('Removed worktree: ' .. path) + end + end + ) + end, + }, + }) + end, + desc = '[git] Delete worktree', + }, + { + 'gwc', + function() + vim.ui.input({ prompt = 'New branch name: ' }, function(branch) + if not branch or branch == '' then return end + vim.ui.input( + { prompt = 'Path (default: ' .. branch .. '): ' }, + function(path) + path = (path and path ~= '') and path or branch + vim.ui.input({ prompt = 'Base branch (default: HEAD): ' }, function(base) + base = (base and base ~= '') and base or 'HEAD' + require('git-worktree').create_worktree(path, branch, base) + end) + end + ) + end) + end, + desc = '[git] Create worktree', + }, + }, + config = function() + require('git-worktree').setup() + require('git-worktree').on_tree_change(function(op, metadata) + if op == require('git-worktree').Operations.Switch then + vim.fn.FugitiveDetect(metadata.path) + end + end) + end, }, } diff --git a/files/.config/nvim/plugin/colors.lua b/files/.config/nvim/plugin/colors.lua index 2cadd7b0..352af4a6 100644 --- a/files/.config/nvim/plugin/colors.lua +++ b/files/.config/nvim/plugin/colors.lua @@ -39,27 +39,6 @@ local function general_overrides(dim_factor) local popup_thumb = highlight.blend(popup_bg, highlight.get('Comment', 'fg', normal_fg), 0.30) - -- Statuscolumn git bar colors (subtle bg tint from GitSigns fg colors) - local git_bar_alpha = 0.28 - local git_add_bar_bg = highlight.blend(normal_bg, diff_add_fg, git_bar_alpha) - local git_change_bar_bg = - highlight.blend(normal_bg, diff_change_fg, git_bar_alpha) - local git_delete_bar_bg = - highlight.blend(normal_bg, diff_delete_fg, git_bar_alpha) - local git_untracked_bar_bg = - highlight.blend(normal_bg, highlight.get('Comment', 'fg', normal_fg), 0.20) - - -- VSCode-ish: keep original text colors, just tint backgrounds. - -- Use HSL darkening to keep hue/saturation consistent across themes. - local diff_bg_factor = 0.3 - local diff_add_bg = highlight.darken_hsl(diff_add_fg, -1 + 0.15) - local diff_delete_bg = highlight.darken_hsl(diff_delete_fg, -1 + 0.15) - local diff_change_bg = highlight.darken_hsl(diff_change_fg, -1 + 0.20) - local diff_text_bg = highlight.darken_hsl(diff_change_fg, -1 + 0.30) - -- Diff filler lines use the `fillchars.diff` glyph (you set it to '╱'). - -- This glyph is highlighted with `DiffDelete` and can look too bright, so - -- keep it as a subtle dark grey, just above the background. - local diff_delete_filler_fg = highlight.darken_hsl(normal_fg, -0.8) -- do -- local configured = mrl.ui.current and mrl.ui.current.float_bg -- if vim.is_callable(configured) then @@ -76,7 +55,7 @@ local function general_overrides(dim_factor) highlight.all({ -- Status line { PanelSt = { link = 'Normal' } }, - { TabLineSel = { fg = { from = 'Normal' }, bg = '#ff0000' } }, + { TabLineSel = { fg = { from = 'Normal' }, bg = stl_bg } }, -- { StatuslineNC = { fg = { from = 'Normal', attr = 'fg' }, fg = bg_color } }, -- { StatusLine = { fg = { from = 'Normal', attr = 'fg' }, bg = stl_bg } }, -- { StatusLineNC = { bg = { from = 'Normal', attr = 'fg' }, fg = bg_color } }, @@ -402,6 +381,18 @@ local function general_overrides(dim_factor) -- bg = diff_text_bg, -- }, -- }, + -- Diffview file panel background (winhighlight uses DiffviewNormal for Normal). + { DiffviewNormal = { bg = { from = 'Normal', attr = 'bg' }, fg = { from = 'Normal', attr = 'fg' } } }, + { DiffviewEndOfBuffer = { bg = { from = 'Normal', attr = 'bg' } } }, + -- Diffview file panel: status letter fg only, no bg bleed from DiffAdd/DiffDelete/etc. + { DiffviewStatusAdded = { fg = { from = 'GitSignsAdd', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewStatusUntracked = { fg = { from = 'GitSignsAdd', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewStatusModified = { fg = { from = 'GitSignsChange', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewStatusRenamed = { fg = { from = 'GitSignsChange', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewStatusDeleted = { fg = { from = 'GitSignsDelete', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewStatusUnmerged = { fg = { from = 'DiagnosticWarn', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewFilePanelInsertions = { fg = { from = 'GitSignsAdd', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, + { DiffviewFilePanelDeletions = { fg = { from = 'GitSignsDelete', attr = 'fg' }, bg = { from = 'Normal', attr = 'bg' } } }, -- these highlights are syntax groups that are set in diff.vim { diffAdded = { inherit = 'DiffAdd' } }, { diffChanged = { inherit = 'DiffChange' } }, @@ -528,7 +519,7 @@ local function general_overrides(dim_factor) }) end -local function set_sidebar_highlight(dim_factor) +local function set_sidebar_highlight() highlight.all({ { -- Panels should match the main window background. @@ -563,37 +554,6 @@ local sidebar_fts = { 'AvanteSelectedFiles', } -local function on_diffview_enter() - local stl = vim.api.nvim_get_hl(0, { name = 'StatusLine', link = false }) - local norm = vim.api.nvim_get_hl(0, { name = 'Normal', link = false }) - local bg = stl.bg and ('#%06x'):format(stl.bg) or 'NONE' - local fg = norm.fg and ('#%06x'):format(norm.fg) or 'NONE' - local nbg = norm.bg and ('#%06x'):format(norm.bg) or 'NONE' - - -- Use our own hl groups that diffview doesn't touch - vim.api.nvim_set_hl(0, 'DvPanelBg', { bg = bg, fg = fg }) - vim.api.nvim_set_hl(0, 'DvPanelEob', { bg = bg }) - vim.api.nvim_set_hl(0, 'DvPanelCursor', { bg = nbg }) - - -- Point diffview windows directly at our groups via winhighlight - local winhl = table.concat({ - 'Normal:DvPanelBg', - 'EndOfBuffer:DvPanelEob', - 'SignColumn:DvPanelBg', - 'CursorLine:DvPanelCursor', - 'WinSeparator:DiffviewWinSeparator', - }, ',') - - -- Apply to the diffview tab (it opens in a new tabpage) - local tabpage = vim.api.nvim_get_current_tabpage() - for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tabpage)) do - local buf = vim.api.nvim_win_get_buf(win) - local ft = vim.bo[buf].filetype - if ft == 'DiffviewFiles' or ft == 'DiffviewFileHistory' then - vim.api.nvim_set_option_value('winhighlight', winhl, { win = win }) - end - end -end local function on_sidebar_enter() -- Panels should feel like sidebars: no line numbers. @@ -607,7 +567,7 @@ local function on_sidebar_enter() }) end -local function colorscheme_overrides(dim_factor) +local function colorscheme_overrides() local overrides = { ['github_dark_default'] = { { TabLineSel = { link = 'Todo' } }, @@ -620,8 +580,8 @@ end local function user_highlights() local dim_factor = 0.25 general_overrides(dim_factor) - set_sidebar_highlight(dim_factor) - colorscheme_overrides(dim_factor) + set_sidebar_highlight() + colorscheme_overrides() end ------------------------------------------------------------------------------- @@ -640,11 +600,6 @@ augroup('UserHighlights', { event = 'FileType', pattern = sidebar_fts, command = function() on_sidebar_enter() end, -}, { - -- diffview fires this after its own hl.setup() completes, so our groups win. - event = 'User', - pattern = 'DiffviewViewPostLayout', - command = function() on_diffview_enter() end, }) -- }}} diff --git a/install/install_dependencies.sh b/install/install_dependencies.sh index ad4aeaca..c49b5c39 100644 --- a/install/install_dependencies.sh +++ b/install/install_dependencies.sh @@ -281,6 +281,7 @@ BREW_MAC=( "fswatch" "p7zip" "sevenzip" + "mosh" "tailscale" "timg" "ykman" @@ -323,6 +324,7 @@ BREW_LINUX=( "lsd" "exa" "gcc" + "mosh" "sshfs" "zsh-syntax-highlighting" "zsh-autosuggestions" diff --git a/scripts/gwrm b/scripts/gwrm new file mode 100755 index 00000000..6370e3ce --- /dev/null +++ b/scripts/gwrm @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# gwrm — remove a git worktree and optionally delete its branch +# Usage: gwrm [-f] [-d|-D] +# -f force remove even if worktree has uncommitted changes +# -d delete local branch after removing worktree (safe, merged only) +# -D force-delete local branch after removing worktree + +set -euo pipefail + +force='' +delete_branch='' + +while getopts 'fdD' opt; do + case $opt in + f) force='--force' ;; + d) delete_branch='-d' ;; + D) delete_branch='-D' ;; + *) echo "Usage: gwrm [-f] [-d|-D] " >&2; exit 1 ;; + esac +done +shift $((OPTIND - 1)) + +if [ $# -ne 1 ]; then + echo "Usage: gwrm [-f] [-d|-D] " >&2 + exit 1 +fi + +target=$1 + +# Resolve worktree path and branch from `git worktree list --porcelain` +wt_path='' +wt_branch='' +cur_path='' +cur_branch='' + +while IFS= read -r line; do + case $line in + 'worktree '*) cur_path="${line#worktree }" ;; + 'branch refs/heads/'*) cur_branch="${line#branch refs/heads/}" ;; + '') + # end of a worktree block — check if it matches the target + if [ "$cur_path" = "$target" ] || [ "$cur_branch" = "$target" ]; then + wt_path="$cur_path" + wt_branch="$cur_branch" + fi + cur_path=''; cur_branch='' + ;; + esac +done < <(git worktree list --porcelain; echo '') + +if [ -z "$wt_path" ]; then + echo "gwrm: no worktree found matching '$target'" >&2 + exit 1 +fi + +# Safety: refuse to remove the current worktree +if [ "$wt_path" = "$(git worktree list --porcelain | awk 'NR==2{print $2}')" ] || \ + [ "$(realpath "$wt_path")" = "$(realpath "$(pwd)")" ]; then + echo "gwrm: cannot remove the current worktree" >&2 + exit 1 +fi + +echo "Removing worktree: $wt_path" +git worktree remove $force "$wt_path" +git worktree prune + +if [ -n "$delete_branch" ] && [ -n "$wt_branch" ]; then + echo "Deleting branch: $wt_branch" + git branch "$delete_branch" "$wt_branch" +fi + +# vim: ft=bash From c8e1893e72d03cfbbb10e15fc24d17bdcaab0589 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Thu, 26 Mar 2026 10:18:08 +0100 Subject: [PATCH 25/27] ci: fix globals --- .github/workflows/ci.yml | 18 ++++++++++++++---- .luacheckrc | 3 +++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d6dc0d0..2369621d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,12 +43,22 @@ jobs: assert_link "$HOME/.zprofile" assert_link "$HOME/.gitconfig" assert_link "$HOME/.bashrc" - assert_link "$HOME/.config/nvim" - assert_link "$HOME/.config/zsh" - assert_link "$HOME/.config/tmux" - assert_link "$HOME/.config/git" + assert_link "$HOME/.config" assert_link "$HOME/.dotfiles" + assert_path() { + if [[ ! -e "$1" ]]; then + echo "FAIL: expected path at $1" + ls -la "$(dirname "$1")" || true + exit 1 + fi + echo "OK: $1" + } + assert_path "$HOME/.config/nvim" + assert_path "$HOME/.config/zsh" + assert_path "$HOME/.config/tmux" + assert_path "$HOME/.config/git" + - name: Assert zshenv sets ZDOTDIR run: | result=$(bash -c 'source "$HOME/.zshenv" 2>/dev/null; echo "$ZDOTDIR"') diff --git a/.luacheckrc b/.luacheckrc index dd2fdb4f..3d4078d7 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -2,6 +2,9 @@ globals = { "vim", "mrl", "map", + "MiniTest", + "Stl", + "Stlcol", } -- Ignore: unused variables/arguments, line too long, shadowing, value overwritten From eb7de7f0f8de156c36778e6042331692a15712a5 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 27 Mar 2026 09:43:22 +0100 Subject: [PATCH 26/27] fix: missing brew --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2369621d..90ce1d0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,8 +21,20 @@ jobs: - name: install_dependencies.sh (Homebrew + packages) run: bash install/install_dependencies.sh + - name: Persist Homebrew environment + run: | + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + { + echo "PATH=$PATH" + echo "HOMEBREW_PREFIX=$HOMEBREW_PREFIX" + echo "HOMEBREW_CELLAR=$HOMEBREW_CELLAR" + echo "HOMEBREW_REPOSITORY=$HOMEBREW_REPOSITORY" + } >> "$GITHUB_ENV" + - name: make install (symlinks) run: | + command -v brew + command -v stow mkdir -p "$HOME/Projects/personal" ln -sfn "$PWD" "$HOME/Projects/personal/dotfiles" ln -sfn "$PWD" "$HOME/.dotfiles" From 4f37235fd16eb108609eaa1a49fd3dd4feef01c8 Mon Sep 17 00:00:00 2001 From: Marcos Romero Lamas Date: Fri, 27 Mar 2026 09:58:59 +0100 Subject: [PATCH 27/27] ci: fix asserts --- .github/workflows/ci.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90ce1d0d..1d30bf1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,9 +55,22 @@ jobs: assert_link "$HOME/.zprofile" assert_link "$HOME/.gitconfig" assert_link "$HOME/.bashrc" - assert_link "$HOME/.config" assert_link "$HOME/.dotfiles" + assert_dir_or_link() { + if [[ ! -d "$1" && ! -L "$1" ]]; then + echo "FAIL: expected directory or symlink at $1" + ls -la "$(dirname "$1")" || true + exit 1 + fi + if [[ -L "$1" ]]; then + echo "OK: $1 -> $(readlink "$1")" + else + echo "OK: $1 is a directory" + fi + } + assert_dir_or_link "$HOME/.config" + assert_path() { if [[ ! -e "$1" ]]; then echo "FAIL: expected path at $1"