├── bin ├── gen ├── str ├── code ├── linkify ├── subl_wait.sh ├── fishy │ ├── string-join0 │ ├── string-lower │ ├── string-upper │ ├── count │ ├── .editorconfig │ ├── math │ ├── .bcrc │ ├── count.pl │ ├── contains │ ├── contains.lua │ ├── t │ │ └── string.t │ ├── string-length │ ├── random.lua │ ├── .bcrc2 │ ├── string.lua │ ├── string-transform │ ├── contains.pl │ ├── string-join │ ├── string-repeat │ ├── string │ ├── string-match │ ├── math.lua │ └── argparse.lua ├── csv2tsv ├── reference │ ├── emacs-plus.sh │ └── git-fix-email.sh ├── palette ├── str.d │ ├── lower │ ├── upper │ ├── match │ └── match.lua ├── hello.zsh ├── fix-bom ├── whichos ├── backup ├── git │ ├── git-pushup │ ├── git-branch-cleanup │ ├── git-submodule-up │ └── git-cloner ├── context_demo.zsh ├── em ├── upallthethings ├── new_mac_secrets ├── gen.d │ └── xlcols ├── hex2rgb ├── trash ├── capsmap ├── new_mac_setup ├── dispatch ├── pathr ├── pocket2ffbookmarks ├── color-spaces.pl ├── string ├── prj ├── repo ├── pkgmgr ├── macopts.zsh ├── pocketdownload ├── otp ├── gitex └── colorschemes ├── .hammerspoon ├── Spoons │ └── .gitkeep └── readme.md ├── .vimrc ├── .bash_profile ├── .config ├── pep8 ├── nvim │ ├── .gitignore │ ├── .stylua.toml │ ├── lua │ │ ├── custom │ │ │ └── plugins │ │ │ │ └── init.lua │ │ └── kickstart │ │ │ ├── plugins │ │ │ ├── indent_line.lua │ │ │ ├── autopairs.lua │ │ │ ├── neo-tree.lua │ │ │ ├── lint.lua │ │ │ ├── gitsigns.lua │ │ │ └── debug.lua │ │ │ └── health.lua │ ├── LICENSE.md │ ├── doc │ │ └── kickstart.txt │ └── lazy-lock.json ├── pycodestyle ├── kak │ ├── plugins │ │ └── example.lua │ └── kakrc ├── wezterm │ ├── backgrounds │ │ ├── botw_corrupted_nydra.jpg │ │ ├── botw_corrupted_nydra2.png │ │ ├── botw_corrupted_nydra_dark.png │ │ └── botw_corrupted_nydra_rev.jpg │ └── README.md ├── bash │ ├── plugins │ │ ├── gam.sh │ │ ├── iwd.sh │ │ ├── nim.sh │ │ ├── fzf.sh │ │ ├── zoxide.sh │ │ ├── history-substring-search.sh │ │ ├── direnv.sh │ │ ├── confd.sh │ │ ├── gpg.sh │ │ ├── azure.sh │ │ ├── atuin.sh │ │ ├── zzz.sh │ │ ├── dotfiles.sh │ │ ├── perl.sh │ │ ├── history.sh │ │ ├── environment.sh │ │ ├── dotnet.sh │ │ ├── ble.sh │ │ ├── wezterm.sh │ │ ├── git.sh │ │ ├── editor.sh │ │ ├── gitignore.sh │ │ ├── repos.sh │ │ ├── jupyter.sh │ │ ├── completions.sh │ │ ├── directory.sh │ │ ├── colors.sh │ │ ├── homebrew.sh │ │ ├── magic-enter.sh │ │ ├── utils.sh │ │ ├── __init__.sh │ │ ├── xdg.sh │ │ ├── python.sh │ │ └── macos.sh │ ├── repos.txt │ ├── .bashrc │ ├── readme.md │ ├── conf.d │ │ └── aliases.sh │ └── themes │ │ └── bash.toml ├── npm │ └── npmrc ├── zsh │ ├── .zshenv │ └── conf.d │ │ ├── git-clone-with-cd.zsh │ │ ├── history.zsh │ │ └── myaliases.zsh ├── starship │ ├── starship.toml │ ├── pure.toml │ ├── omf.toml │ ├── fish.toml │ ├── zebrafish.toml │ ├── prezto.toml │ ├── xonsh.toml │ ├── hydro.toml │ ├── zsh.toml │ ├── powershell.toml │ ├── mmc_256.toml │ ├── mmc.toml │ └── bash.toml ├── helix │ └── config.toml ├── readline │ └── inputrc ├── tmux │ └── tmux.conf ├── homebrew │ └── init.brewfile └── vim │ └── init.vim ├── .bashrc ├── .gitmodules ├── .zshenv ├── .stow-local-ignore ├── docs ├── taskpaper.md ├── xdg.md ├── zsh │ ├── completions.md │ └── zdotdir.md ├── emacs.md ├── macos.md ├── git.md ├── readme.md └── vim.md ├── makefile ├── LICENSE ├── .editorconfig ├── .gitignore └── README.md /bin/gen: -------------------------------------------------------------------------------- 1 | ./dispatch -------------------------------------------------------------------------------- /bin/str: -------------------------------------------------------------------------------- 1 | ./dispatch -------------------------------------------------------------------------------- /.hammerspoon/Spoons/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | .config/vim/init.vim -------------------------------------------------------------------------------- /.bash_profile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . $HOME/.bashrc 3 | -------------------------------------------------------------------------------- /bin/code: -------------------------------------------------------------------------------- 1 | /Applications/Visual Studio Code.app/Contents/Resources/app/bin/code -------------------------------------------------------------------------------- /.config/pep8: -------------------------------------------------------------------------------- 1 | [pep8] 2 | ignore = E226,E731,E401,E402 3 | max-line-length = 100 4 | -------------------------------------------------------------------------------- /.config/nvim/.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | test.sh 3 | .luarc.json 4 | nvim 5 | 6 | spell/ 7 | -------------------------------------------------------------------------------- /.config/pycodestyle: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | ignore = E226,E731,E401,E402 3 | max-line-length = 100 4 | -------------------------------------------------------------------------------- /bin/linkify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | sed -E 's#(/[^\(]+)\(([0-9]+),([0-9]+)\):#file:///\1:\2:\3 :#g' 3 | -------------------------------------------------------------------------------- /.bashrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | BASH_HOME="${BASH_HOME:-$HOME/.config/bash}" 3 | . $BASH_HOME/.bashrc 4 | -------------------------------------------------------------------------------- /.config/kak/plugins/example.lua: -------------------------------------------------------------------------------- 1 | -- myplugin.lua 2 | local message = "Hello from Lua!" 3 | print(message) 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".local"] 2 | path = .local 3 | url = git@github.com:mattmc3/dotfiles.local 4 | branch = main 5 | -------------------------------------------------------------------------------- /bin/subl_wait.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # https://gist.github.com/jpalmieri/ab767f81d2ee8bce3e7d 4 | subl -w -n $* 5 | -------------------------------------------------------------------------------- /.zshenv: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | export ZDOTDIR="${ZDOTDIR:-$HOME/.config/zsh}" 3 | [[ -r $ZDOTDIR/.zshenv ]] && . $ZDOTDIR/.zshenv 4 | -------------------------------------------------------------------------------- /bin/fishy/string-join0: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 4 | "$SCRIPT_DIR"/string-join -0 "$@" 5 | -------------------------------------------------------------------------------- /bin/csv2tsv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | python3 -c "`printf '%s\n' 'import sys,csv' 'for row in csv.reader(sys.stdin):' ' print("\t".join(row))'`" 3 | -------------------------------------------------------------------------------- /bin/reference/emacs-plus.sh: -------------------------------------------------------------------------------- 1 | brew install emacs-plus@27 --without-spacemacs-icon 2 | ln -s /usr/local/opt/emacs-plus@27/Emacs.app /Applications 3 | -------------------------------------------------------------------------------- /bin/fishy/string-lower: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 4 | "$SCRIPT_DIR"/string-transform lower "$@" 5 | -------------------------------------------------------------------------------- /bin/fishy/string-upper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 4 | "$SCRIPT_DIR"/string-transform upper "$@" 5 | -------------------------------------------------------------------------------- /bin/palette: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | for i in {0..255}; do 4 | print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'} 5 | done 6 | -------------------------------------------------------------------------------- /bin/str.d/lower: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -t 0 ]; then 4 | printf '%s\n' "$@" | tr [:upper:] [:lower:] 5 | else 6 | tr [:upper:] [:lower:] 7 | fi 8 | -------------------------------------------------------------------------------- /bin/str.d/upper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -t 0 ]; then 4 | printf '%s\n' "$@" | tr [:lower:] [:upper:] 5 | else 6 | tr [:lower:] [:upper:] 7 | fi 8 | -------------------------------------------------------------------------------- /.config/wezterm/backgrounds/botw_corrupted_nydra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmc3/dotfiles/HEAD/.config/wezterm/backgrounds/botw_corrupted_nydra.jpg -------------------------------------------------------------------------------- /.config/wezterm/backgrounds/botw_corrupted_nydra2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmc3/dotfiles/HEAD/.config/wezterm/backgrounds/botw_corrupted_nydra2.png -------------------------------------------------------------------------------- /.hammerspoon/readme.md: -------------------------------------------------------------------------------- 1 | # Hammerspoon 2 | 3 | - https://github.com/evantravers/hammerspoon-config/blob/abc945264a4ec1830083c53079b2bfd5c4a4d23d/hyper.lua 4 | -------------------------------------------------------------------------------- /.config/wezterm/backgrounds/botw_corrupted_nydra_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmc3/dotfiles/HEAD/.config/wezterm/backgrounds/botw_corrupted_nydra_dark.png -------------------------------------------------------------------------------- /.config/wezterm/backgrounds/botw_corrupted_nydra_rev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattmc3/dotfiles/HEAD/.config/wezterm/backgrounds/botw_corrupted_nydra_rev.jpg -------------------------------------------------------------------------------- /.config/bash/plugins/gam.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Google Workspace manager 4 | #alias gam='$HOME/bin/gam7/gam' 5 | alias gam='$HOME/bin/gamadv-xtd3/gam' 6 | -------------------------------------------------------------------------------- /.config/bash/plugins/iwd.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Quick way to get back to your initial working directory. 4 | IWD="${IWD:-$PWD}" 5 | alias iwd='cd "$IWD"' 6 | -------------------------------------------------------------------------------- /.config/bash/plugins/nim.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | # 3 | # nim 4 | # 5 | 6 | if [ -d $HOME/.nimble/bin ]; then 7 | export PATH="$PATH:$HOME/.nimble/bin" 8 | fi 9 | -------------------------------------------------------------------------------- /.config/kak/kakrc: -------------------------------------------------------------------------------- 1 | colorscheme tomorrow-night 2 | define-command -docstring "Example Lua plugin" run-example %sh{ 3 | lua $HOME/.config/kak/plugins/example.lua 4 | } 5 | -------------------------------------------------------------------------------- /.config/npm/npmrc: -------------------------------------------------------------------------------- 1 | prefix=${XDG_DATA_HOME}/npm 2 | cache=${XDG_CACHE_HOME}/npm 3 | tmp=${XDG_RUNTIME_DIR}/npm 4 | init-module=${XDG_CONFIG_HOME}/npm/config/npm-init.js 5 | -------------------------------------------------------------------------------- /.stow-local-ignore: -------------------------------------------------------------------------------- 1 | \.DS_Store 2 | \.git 3 | \.github 4 | \.gitignore 5 | \.vscode 6 | \.stow-local-ignore 7 | docs 8 | makefile 9 | LICENSE 10 | README.md 11 | readme.md 12 | -------------------------------------------------------------------------------- /.config/nvim/.stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 160 2 | line_endings = "Unix" 3 | indent_type = "Spaces" 4 | indent_width = 2 5 | quote_style = "AutoPreferSingle" 6 | call_parentheses = "None" 7 | -------------------------------------------------------------------------------- /.config/bash/plugins/fzf.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Enable fzf bash integration. 4 | if type fzf >/dev/null 2>&1; then 5 | cached_eval fzf --bash 6 | #eval "$(fzf --bash)" 7 | fi 8 | -------------------------------------------------------------------------------- /.config/bash/plugins/zoxide.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Enable z command. 4 | if type zoxide >/dev/null 2>&1; then 5 | #eval "$(zoxide init bash)" 6 | cached_eval zoxide init bash 7 | fi 8 | -------------------------------------------------------------------------------- /.config/bash/plugins/history-substring-search.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # History substring search 4 | # bind '"\e[A": history-substring-search-backward' 5 | # bind '"\e[B": history-substring-search-forward' 6 | -------------------------------------------------------------------------------- /.config/zsh/.zshenv: -------------------------------------------------------------------------------- 1 | # Reduce key delay 2 | export KEYTIMEOUT=${KEYTIMEOUT:-1} 3 | 4 | # Make Apple Terminal behave. 5 | if [[ "$OSTYPE" == darwin* ]]; then 6 | export SHELL_SESSIONS_DISABLE=${SHELL_SESSIONS_DISABLE:-1} 7 | fi 8 | -------------------------------------------------------------------------------- /.config/bash/plugins/direnv.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Enable direnv: https://direnv.net/docs/hook.html 4 | if type direnv >/dev/null 2>&1; then 5 | cached_eval direnv hook bash 6 | #eval "$(direnv hook bash)" 7 | fi 8 | -------------------------------------------------------------------------------- /bin/hello.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | function hello() { 4 | echo "hello ${1:-world}" 5 | } 6 | 7 | # Only run if NOT sourced 8 | if [[ $ZSH_EVAL_CONTEXT != *:file ]]; then 9 | hello "$@" 10 | fi 11 | echo $ZSH_EVAL_CONTEXT 12 | -------------------------------------------------------------------------------- /docs/taskpaper.md: -------------------------------------------------------------------------------- 1 | # TaskPaper 2 | 3 | Mac App config location: 4 | ~/Library/Containers/com.hogbaysoftware.TaskPaper3/Data/Library/Application Support/TaskPaper 5 | Brew Cask App config location: 6 | ~/Library/Application Support/TaskPaper 7 | -------------------------------------------------------------------------------- /.config/bash/plugins/confd.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | # Load every script in conf.d 4 | shopt -s nullglob 5 | for _rc in "$BASH_HOME"/conf.d/*.sh; do 6 | source "$_rc" 7 | done 8 | shopt -u nullglob 9 | unset _rc 10 | -------------------------------------------------------------------------------- /.config/bash/plugins/gpg.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Tell gpg to store its keyring as data. 4 | if [[ -d "$XDG_DATA_HOME" ]]; then 5 | export GNUPGHOME="${GNUPGHOME:-$XDG_DATA_HOME/gnupg}" 6 | alias gpg='gpg --homedir "$GNUPGHOME"' 7 | fi 8 | -------------------------------------------------------------------------------- /.config/nvim/lua/custom/plugins/init.lua: -------------------------------------------------------------------------------- 1 | -- You can add your own plugins here or in other files in this directory! 2 | -- I promise not to create any merge conflicts in this directory :) 3 | -- 4 | -- See the kickstart.nvim README for more information 5 | return {} 6 | -------------------------------------------------------------------------------- /bin/fix-bom: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ $# -gt 0 ] || exit 1 4 | export LANG=C LC_ALL=C 5 | 6 | # Make 'sed -i' work for both GNU and BSD 7 | sedi() { 8 | sed --version &>/dev/null && sed -i -- "$@" || sed -i "" -- "$@" 9 | } 10 | sedi '1s/^\xEF\xBB\xBF//' "$@" 11 | -------------------------------------------------------------------------------- /.config/bash/plugins/azure.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | function azdbtok { 4 | local tok 5 | tok="$(az account get-access-token --resource https://ossrdbms-aad.database.windows.net --query accessToken --output tsv)" 6 | echo "$tok" | tee >(pbcopy) >(cat) 7 | } 8 | -------------------------------------------------------------------------------- /bin/whichos: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case "$OSTYPE" in 4 | solaris*) echo SOLARIS ;; 5 | darwin*) echo MACOS ;; 6 | linux*) echo LINUX ;; 7 | bsd*) echo BSD ;; 8 | msys*) echo WINDOWS ;; 9 | *) echo $OSTYPE ;; 10 | esac 11 | -------------------------------------------------------------------------------- /docs/xdg.md: -------------------------------------------------------------------------------- 1 | # XDG 2 | 3 | - [XDG app support][xdg-app-support]: App settings for XDG support 4 | 5 | [xdg-app-support]: https://wiki.archlinux.org/index.php/XDG_Base_Directory 6 | [xdg-basedirs]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 7 | -------------------------------------------------------------------------------- /docs/zsh/completions.md: -------------------------------------------------------------------------------- 1 | # Completions 2 | 3 | Add user contributed completions to this directory. 4 | 5 | Read more about how Zsh completions work in this [how-to][1] article. 6 | 7 | 8 | [1]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org 9 | -------------------------------------------------------------------------------- /bin/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | 0=${(%):-%x} 4 | rsync_file=${0:A:h}/backup.rsync 5 | 6 | BACKUP_DIR=${BACKUP_DIR:-$HOME/.bak} 7 | mkdir -p $BACKUP_DIR 8 | echo "backing up to ${BACKUP_DIR}..." 9 | rsync -aLv --include-from="$rsync_file" "$HOME/" ${BACKUP_DIR}/$(date +"%Y%m%d_%H%M%S") 10 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/indent_line.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { -- Add indentation guides even on blank lines 3 | 'lukas-reineke/indent-blankline.nvim', 4 | -- Enable `lukas-reineke/indent-blankline.nvim` 5 | -- See `:help ibl` 6 | main = 'ibl', 7 | opts = {}, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /bin/fishy/count: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Optionally, #!/usr/bin/env dash 3 | 4 | ##? count - counts args 5 | 6 | REPLY="$#" 7 | 8 | # count piped/redirected input 9 | if ! [ -t 0 ]; then 10 | while IFS= read -r DATA || [ -n "$DATA" ]; do 11 | REPLY=$((REPLY + 1)) 12 | done 13 | fi 14 | printf '%s\n' $REPLY 15 | -------------------------------------------------------------------------------- /.config/wezterm/README.md: -------------------------------------------------------------------------------- 1 | # WezTerm 2 | 3 | WezTerm is awesome. This is a quick place to find links and reference snippets: 4 | 5 | References: 6 | - https://alexplescan.com/posts/2024/08/10/wezterm/ 7 | - https://gist.github.com/alexpls/83d7af23426c8928402d6d79e72f9401 8 | - https://news.ycombinator.com/item?id=41223934 9 | -------------------------------------------------------------------------------- /bin/git/git-pushup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # push a new branch to the remote 4 | # Quick way to resolve the annoying error: 5 | # 'fatal: The current branch foo has no upstream branch.' 6 | # Or, you could just set `push.autoSetupRemote = true` in gitconfig... 7 | git push --set-upstream "${1:-origin}" "$(git rev-parse --abbrev-ref HEAD)" 8 | -------------------------------------------------------------------------------- /bin/fishy/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.lua] 12 | indent_style = tab 13 | indent_size = 4 14 | 15 | [*.pl] 16 | indent_style = space 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /.config/bash/plugins/atuin.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Enable atuin bash integration. 4 | if type atuin >/dev/null 2>&1; then 5 | # Disable up arrow with --disable-up-arrow. 6 | # Disabling up arrow means that we need to use C-r for search. 7 | # eval "$(atuin init bash --disable-up-arrow)" 8 | eval "$(atuin init bash --disable-up-arrow)" 9 | fi 10 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | ##? dotfiles 2 | ##? 3 | ##? Usage: make 4 | ##? 5 | ##? Commands: 6 | 7 | .DEFAULT_GOAL := help 8 | 9 | ##? submodules update all submodules 10 | submodules: 11 | git submodule update --recursive --remote 12 | .PHONY : submodules 13 | 14 | ##? help show this message 15 | help: 16 | @grep "^##?" makefile | cut -c 5- 17 | .PHONY : help 18 | -------------------------------------------------------------------------------- /bin/context_demo.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | function show_context() { 4 | echo "Inside function: ZSH_EVAL_CONTEXT = $ZSH_EVAL_CONTEXT" 5 | } 6 | 7 | echo "At toplevel: ZSH_EVAL_CONTEXT = $ZSH_EVAL_CONTEXT" 8 | 9 | show_context 10 | 11 | eval 'echo "Inside eval: ZSH_EVAL_CONTEXT = $ZSH_EVAL_CONTEXT"' 12 | 13 | trap 'echo "Inside trap: ZSH_EVAL_CONTEXT = $ZSH_EVAL_CONTEXT"' EXIT 14 | -------------------------------------------------------------------------------- /.config/bash/plugins/zzz.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Clean up '$PATH'. 4 | # PATH="$( 5 | # printf %s "$PATH" | 6 | # awk -vRS=: -vORS= '!a[$0]++ {if (NR>1) printf(":"); printf("%s", $0) }' 7 | # )" 8 | 9 | # Attach ble.sh last. 10 | [[ ${BLE_VERSION-} ]] && ble-attach 11 | 12 | # End profiling. 13 | if [ "${PROFRC:-0}" -eq 1 ]; then 14 | set +x 15 | exec 2>&3 3>&- 16 | fi 17 | -------------------------------------------------------------------------------- /bin/em: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | # https://medium.com/@bobbypriambodo/blazingly-fast-spacemacs-with-persistent-server-92260f2118b7 3 | 4 | # Checks if there's a frame open 5 | emacsclient -n -e "(if (> (length (frame-list)) 1) ‘t)" 2> /dev/null | grep t &> /dev/null 6 | if [ "$?" -eq "1" ]; then 7 | emacsclient -a '' -nqc "$@" &> /dev/null 8 | else 9 | emacsclient -nq "$@" &> /dev/null 10 | fi 11 | -------------------------------------------------------------------------------- /bin/upallthethings: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # homebrew 4 | brew update && brew upgrade && brew cleanup 5 | 6 | # python 7 | pip2 list --outdated | cut -d ' ' -f1 | xargs -n1 pip2 install -U 8 | pip3 list --outdated | cut -d ' ' -f1 | xargs -n1 pip3 install -U 9 | 10 | # node 11 | # https://docs.npmjs.com/getting-started/updating-global-packages 12 | npm update -g 13 | 14 | # ruby 15 | gem update 16 | gem cleanup 17 | -------------------------------------------------------------------------------- /.config/bash/plugins/dotfiles.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # dotfiles 4 | export DOTFILES="${DOTFILES:-$HOME/.dotfiles}" 5 | alias dotf='cd "$DOTFILES"' 6 | alias fdot="cd ~/.config/fish" 7 | alias zdot="cd ~/.config/zsh" 8 | alias bdot="cd ~/.config/bash" 9 | alias rcs='cd "${BASH_HOME:-$HOME}"' 10 | alias bashrc='"${EDITOR:-vim}" "${BASH_HOME:-$HOME}/.bashrc"' 11 | alias reload='source "${BASH_HOME:-$HOME}/.bashrc"' 12 | -------------------------------------------------------------------------------- /.config/bash/repos.txt: -------------------------------------------------------------------------------- 1 | # Zsh-like preexec 2 | rcaloras/bash-preexec 3 | 4 | # Make bash modern. 5 | akinomyoga/ble.sh 6 | 7 | # Awesome terminal, with some helpful includes. 8 | wez/wezterm 9 | 10 | # Fast git status for prompts. 11 | romkatv/gitstatus 12 | 13 | # Bash frameworks, though I likely will snag snippets rather than use them. 14 | #bash-it/bash-it 15 | #ohmybash/oh-my-bash 16 | 17 | # vim: ft=jproperties sw=2 ts=2 et 18 | -------------------------------------------------------------------------------- /.config/bash/plugins/perl.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | # 3 | # perl 4 | # 5 | 6 | if [[ "$OSTYPE" == darwin* ]]; then 7 | # eval "$(perl -I$XDG_DATA_HOME/perl5/lib/perl5 -Mlocal::lib=$XDG_DATA_HOME/perl5)" 8 | export PERL_MB_OPT="--install_base \"\$XDG_DATA_HOME\"/perl5" 9 | export PERL_MM_OPT="INSTALL_BASE=\"\$XDG_DATA_HOME\"/perl5" 10 | if [[ ! -d "$XDG_DATA_HOME/perl5" ]]; then 11 | mkdir -p "$XDG_DATA_HOME/perl5" 12 | cpan local::lib 13 | fi 14 | fi 15 | -------------------------------------------------------------------------------- /bin/fishy/math: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 5 | 6 | main() { 7 | local formula scale bcrc 8 | formula="$*" 9 | if [ "$1" = "-s" ] || [ "$1" = "--scale" ]; then 10 | shift; scale="$1"; shift 11 | formula="scale=$scale; $*" 12 | fi 13 | bcrc="$SCRIPT_DIR"/.bcrc 14 | bc -V > /dev/null 2>&1 || bcrc="$SCRIPT_DIR"/.bcrc2 15 | printf '%s\n' "$formula" | bc -l "$bcrc" 16 | } 17 | main "$@" 18 | -------------------------------------------------------------------------------- /.config/bash/plugins/history.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | shopt -s histappend # Append to history, don't overwrite it. 4 | HISTTIMEFORMAT='%F %T ' # Use standard ISO 8601 timestamp. 5 | HISTSIZE=10000 # Remember the last x commands in memory during session 6 | HISTFILESIZE=100000 # Start truncating history file after x lines 7 | HISTCONTROL=ignoreboth # ignoreboth is shorthand for ignorespace and ignoredups 8 | HISTFILE="${BASH_DATA_HOME:-$HOME/.local/share/bash}/history" 9 | -------------------------------------------------------------------------------- /bin/fishy/.bcrc: -------------------------------------------------------------------------------- 1 | # https://man.freebsd.org/cgi/man.cgi?query=bc&sektion=1 2 | 3 | define npr(n, r) { 4 | return perm(n, r) 5 | } 6 | 7 | define ncr(n, r) { 8 | return comb(n, r) 9 | } 10 | 11 | define fac(n) { 12 | return f(n); 13 | } 14 | 15 | define floor(x) { 16 | scale=0 17 | return x/1 18 | } 19 | 20 | define min(n, m) { 21 | if (n < m) return n; 22 | return m; 23 | } 24 | 25 | define max(n, m) { 26 | if (n < m) return (m); 27 | return n; 28 | } 29 | 30 | # vim: ft=bc 31 | -------------------------------------------------------------------------------- /.config/bash/plugins/environment.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Set locale. 4 | export LANG="${LANG:-en_US.UTF-8}" 5 | export TZ="${TZ:-America/New_York}" 6 | 7 | # Set preferred editors, pagers, and launchers. 8 | export EDITOR="${EDITOR:-vim}" 9 | export VISUAL="${VISUAL:-code}" 10 | export PAGER="${PAGER:-less}" 11 | export BROWSER="${BROWSER:-open}" 12 | 13 | # Set flags for less command. 14 | export LESS="-giMRSw -z-4" 15 | 16 | # Reduce key delay. 17 | export KEYTIMEOUT="${KEYTIMEOUT:-1}" 18 | -------------------------------------------------------------------------------- /bin/git/git-branch-cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | ##? branch-cleanup: Remove branches no longer on remote 5 | main() { 6 | # https://stackoverflow.com/questions/7726949/remove-tracking-branches-no-longer-on-remote 7 | local defbranch 8 | defbranch="$(git symbolic-ref --short refs/remotes/origin/HEAD | sed 's|^origin/||' 2>/dev/null)" 9 | git checkout "${defbranch:-main}" >/dev/null 2>/dev/null && git fetch -p && git branch -vv | 10 | awk '/: gone]/{print $1}' | 11 | xargs git branch -d 12 | } 13 | main "$@" 14 | -------------------------------------------------------------------------------- /.config/bash/plugins/dotnet.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete 4 | # bash parameter completion for the dotnet CLI 5 | function _dotnet_bash_complete() 6 | { 7 | local cur="${COMP_WORDS[COMP_CWORD]}" IFS=$'\n' # On Windows you may need to use use IFS=$'\r\n' 8 | local candidates 9 | read -d '' -ra candidates < <(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2>/dev/null) 10 | read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]:-}" -- "$cur") 11 | } 12 | complete -f -F _dotnet_bash_complete dotnet 13 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/autopairs.lua: -------------------------------------------------------------------------------- 1 | -- autopairs 2 | -- https://github.com/windwp/nvim-autopairs 3 | 4 | return { 5 | 'windwp/nvim-autopairs', 6 | event = 'InsertEnter', 7 | -- Optional dependency 8 | dependencies = { 'hrsh7th/nvim-cmp' }, 9 | config = function() 10 | require('nvim-autopairs').setup {} 11 | -- If you want to automatically add `(` after selecting a function or method 12 | local cmp_autopairs = require 'nvim-autopairs.completion.cmp' 13 | local cmp = require 'cmp' 14 | cmp.event:on('confirm_done', cmp_autopairs.on_confirm_done()) 15 | end, 16 | } 17 | -------------------------------------------------------------------------------- /bin/new_mac_secrets: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | setopt extended_glob interactive_comments 4 | 5 | # ssh 6 | echo "make a new ssh key..." 7 | echo "ssh-keygen -t ed25519 -C "your_email@example.com"" 8 | echo "pbcopy < ~/.ssh/github_mattmc3.pub" 9 | echo "---------" 10 | 11 | # hostname 12 | echo "Set host name?" 13 | echo "sudo scutil --set ComputerName" 14 | echo "sudo scutil --set HostName" 15 | 16 | # gpg 17 | : ${GNUPGHOME:=~/.local/share/gnupg} 18 | if [[ ! -d $GNUPGHOME ]]; then 19 | mkdir -p $GNUPGHOME 20 | chmod 700 $GNUPGHOME 21 | fi 22 | 23 | command gpg --homedir "$GNUPGHOME" --full-generate-key 24 | -------------------------------------------------------------------------------- /bin/fishy/count.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | my $total = @ARGV; 6 | $total++ while !-t *STDIN && ; 7 | 8 | print "$total\n"; 9 | exit !$total; 10 | 11 | # use strict; 12 | # use warnings; 13 | 14 | # my $stdin_count = 0; 15 | 16 | # # Only read stdin if it's not a terminal (i.e., something is piped/redirected) 17 | # unless (-t *STDIN) { 18 | # while () { 19 | # $stdin_count++; 20 | # } 21 | # } 22 | 23 | # my $arg_count = scalar @ARGV; 24 | # my $total = $arg_count + $stdin_count; 25 | 26 | # print "$total\n"; 27 | # exit($total > 0 ? 0 : 1); 28 | -------------------------------------------------------------------------------- /bin/gen.d/xlcols: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Converts a 0-based index to an Excel-style column name 4 | local function to_excel_column(n) 5 | local col = "" 6 | repeat 7 | local rem = n % 26 8 | col = string.char(65 + rem) .. col 9 | n = math.floor(n / 26) - 1 10 | until n < 0 11 | return col 12 | end 13 | 14 | -- Parse argument 15 | local count = tonumber(arg[1]) 16 | if not count or count < 1 then 17 | io.stderr:write("Usage: " .. arg[0] .. " \n") 18 | os.exit(1) 19 | end 20 | 21 | -- Output column names 22 | for i = 0, count - 1 do 23 | print(to_excel_column(i)) 24 | end 25 | 26 | -------------------------------------------------------------------------------- /.config/bash/plugins/ble.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | BLE_HOME="${BLE_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/blesh}" 4 | REPO_HOME="${REPO_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}/bash/repos}" 5 | 6 | if [[ ! -d "$BLE_HOME" ]]; then 7 | export PATH="/opt/homebrew/bin:$PATH" 8 | make -C "$REPO_HOME/akinomyoga/ble.sh" install PREFIX=~/.local 9 | fi 10 | 11 | # Initialize ble.sh for interactive shells. Do this near the beginning of .bashrc. 12 | BLE_HOME="$XDG_DATA_HOME/blesh" 13 | if [[ -d "$BLE_HOME" ]] && [[ "${PROFRC:-0}" -ne 1 ]]; then 14 | [[ $- == *i* ]] && source "$BLE_HOME/ble.sh" --noattach 15 | else 16 | unset BLE_HOME 17 | fi 18 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/neo-tree.lua: -------------------------------------------------------------------------------- 1 | -- Neo-tree is a Neovim plugin to browse the file system 2 | -- https://github.com/nvim-neo-tree/neo-tree.nvim 3 | 4 | return { 5 | 'nvim-neo-tree/neo-tree.nvim', 6 | version = '*', 7 | dependencies = { 8 | 'nvim-lua/plenary.nvim', 9 | 'nvim-tree/nvim-web-devicons', -- not strictly required, but recommended 10 | 'MunifTanjim/nui.nvim', 11 | }, 12 | cmd = 'Neotree', 13 | keys = { 14 | { '\\', ':Neotree reveal', desc = 'NeoTree reveal', silent = true }, 15 | }, 16 | opts = { 17 | filesystem = { 18 | window = { 19 | mappings = { 20 | ['\\'] = 'close_window', 21 | }, 22 | }, 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /bin/hex2rgb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ##? ~/bin/hex2rgb - convert color hex to rgb 3 | # '#C0FFEE' => 192 255 238 4 | # 'DEADBEEF' => 222 173 190 239 5 | # 'fab' => 255 170 187 6 | __hex2rgb() { 7 | # reset $1 to scrubbed hex: '#01efa9' becomes '01EFA9' 8 | set -- "$(echo "$1" | tr -d '#' | tr '[:lower:]' '[:upper:]')" 9 | START=0 10 | STR= 11 | while (( START < ${#1} )); do 12 | # double each char under len 6 : FAB => FFAABB 13 | if (( ${#1} < 6 )); then 14 | STR="$(printf "${1:${START}:1}%.0s" 1 2)" 15 | (( START += 1 )) 16 | else 17 | STR="${1:${START}:2}" 18 | (( START += 2 )) 19 | fi 20 | echo "ibase=16; ${STR}" | bc 21 | done 22 | unset START STR 23 | } 24 | __hex2rgb "$@" 25 | -------------------------------------------------------------------------------- /.config/bash/plugins/wezterm.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | [[ -n "${REPO_HOME}" ]] || return 1 4 | 5 | [[ "$TERM_PROGRAM" == "WezTerm" ]] || return 1 6 | if [[ -r "${REPO_HOME:-?}/wez/wezterm/assets/shell-integration/wezterm.sh" ]]; then 7 | source "${REPO_HOME:-?}/wez/wezterm/assets/shell-integration/wezterm.sh" 8 | else 9 | return 1 10 | fi 11 | 12 | function __wezterm_my_precmd() { 13 | __wezterm_set_user_var "TERM_CURRENT_SHELL" "bash $BASH_VERSION" 14 | } 15 | 16 | # Emit an OSC 1337 sequence to define vars that terminals like WezTerm/iTerm2 can use. 17 | if [[ -n "$BLE_VERSION" ]]; then 18 | blehook PRECMD+=__wezterm_my_precmd 19 | else 20 | precmd_functions+=(__wezterm_my_precmd) 21 | fi 22 | -------------------------------------------------------------------------------- /bin/reference/git-fix-email.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # https://help.github.com/articles/changing-author-info/ 4 | 5 | # export GITFIX_OLD_EMAIL="foo@bar.com" 6 | # export GITFIX_NEW_EMAIL="foobar@baz.com" 7 | # export GITFIX_OLD_NAME="Namey Name" 8 | # export GITFIX_NEW_NAME="New Namey" 9 | 10 | git filter-branch --env-filter ' 11 | if [ "$GIT_COMMITTER_EMAIL" = "$GITFIX_OLD_EMAIL" ] 12 | then 13 | export GIT_COMMITTER_NAME="$GITFIX_NEW_NAME" 14 | export GIT_COMMITTER_EMAIL="$GITFIX_NEW_EMAIL" 15 | fi 16 | if [ "$GIT_AUTHOR_EMAIL" = "$GITFIX_OLD_EMAIL" ] 17 | then 18 | export GIT_AUTHOR_NAME="$GITFIX_NEW_NAME" 19 | export GIT_AUTHOR_EMAIL="$GITFIX_NEW_EMAIL" 20 | fi 21 | ' --tag-name-filter cat -- --branches --tags 22 | -------------------------------------------------------------------------------- /.config/starship/starship.toml: -------------------------------------------------------------------------------- 1 | # https://github.com/starship/starship/discussions/1908 2 | 3 | [character] 4 | success_symbol = "[❯](bold green)" 5 | error_symbol = "[✗](bold red)" 6 | vicmd_symbol = "[❮](bold green)" 7 | 8 | [status] 9 | disabled = false 10 | 11 | [shell] 12 | bash_indicator = "$" 13 | zsh_indicator = "%" 14 | xonsh_indicator = "🐚" 15 | fish_indicator = "🐟" 16 | powershell_indicator = "❯_" 17 | format = "$indicator(bold green) " 18 | disabled = false 19 | 20 | [custom.zsh] 21 | symbol = "%" 22 | when = '[ "$STARSHIP_SHELL" == "zsh" ]' 23 | shell = ["bash", "--noprofile", "--norc"] 24 | 25 | [custom.xonsh] 26 | symbol = '\(🐍 xsh\)' 27 | when = '[ "$STARSHIP_SHELL" == "xonsh" ]' 28 | shell = ["bash", "--noprofile", "--norc"] 29 | -------------------------------------------------------------------------------- /.config/bash/plugins/git.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | export PATH="$PATH:$HOME/bin/git" 4 | 5 | function clone { 6 | if [[ -z "$1" ]]; then 7 | echo "What git repo do you want?" >&2 8 | return 1 9 | fi 10 | local user repo 11 | if [[ "$1" = */* ]]; then 12 | user="${1%/*}" 13 | repo="${1##*/}" 14 | else 15 | user=mattmc3 16 | repo="$1" 17 | fi 18 | 19 | local giturl="github.com" 20 | local dest="${XDG_PROJECTS_HOME:-$HOME/Projects}/$user/$repo" 21 | 22 | if [[ ! -d "$dest" ]]; then 23 | git clone --recurse-submodules "git@${giturl}:${user}/${repo}.git" "$dest" 24 | else 25 | echo "No need to clone, that directory already exists." 26 | echo "Taking you there." 27 | fi 28 | cd "$dest" || return 1 29 | } 30 | -------------------------------------------------------------------------------- /bin/trash: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | ##? trash - Move a specified file to the trash 3 | 4 | if (( $# == 0 )); then 5 | echo >&2 "Usage: trash " 6 | return 1 7 | fi 8 | 9 | if [[ $OSTYPE != darwin* ]]; then 10 | echo >&2 "trash: macOS not detected." 11 | return 1 12 | fi 13 | 14 | local file files 15 | for file in $@; do 16 | if [[ -e $file ]]; then 17 | files+=("the POSIX file \"${file:A}\"") 18 | else 19 | echo >&2 "trash: No such file or directory '$file'." 20 | return 1 21 | fi 22 | done 23 | 24 | if (( $#files == 0 )); then 25 | echo >&2 'usage: trash ' 26 | return 64 # match rm's return code 27 | fi 28 | 29 | osascript 2>&1 >/dev/null -e "tell app \"Finder\" to move { "${(pj., .)files}" } to trash" 30 | 31 | # vim: ft=zsh sw=2 ts=2 et 32 | -------------------------------------------------------------------------------- /docs/zsh/zdotdir.md: -------------------------------------------------------------------------------- 1 | # ZDOTDIR 2 | 3 | > A simple guide to moving your Zsh files out of your `$HOME` 4 | 5 | This guide shows you how to move your Zsh files out of your home directory into a dedicated Zsh config directory. 6 | 7 | ## Pick a path 8 | 9 | Assuming you want to put your Zsh config in `~/.config/zsh`, run the following from an interactive Zsh session: 10 | 11 | ```zsh 12 | export ZDOTDIR=~/.config/zsh 13 | mkdir -p $ZDOTDIR 14 | ``` 15 | 16 | These commands will set the `ZDOTDIR` variable to your new Zsh home, and then ensure that the directory is properly created. 17 | 18 | ## Move your Zsh dotfiles 19 | 20 | ```zsh 21 | zdotfiles=( 22 | .zlog{in,out} 23 | .zprofile 24 | .zsh{env,rc,_history} 25 | ) 26 | for file in $zdotfiles 27 | do 28 | [[ -f ~/$file ]] && echo mv ~/$file $ZDOTDIR 29 | done 30 | ``` 31 | -------------------------------------------------------------------------------- /bin/fishy/contains: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Optionally, #!/usr/bin/env dash 3 | 4 | ##? contains - test if a word is present in a list 5 | 6 | # This is a pure POSIX implementation of Fish's contains utility 7 | # Fish-like contains (https://fishshell.com/docs/current/cmds/contains.html) 8 | contains() {( 9 | while getopts "i" opt; do 10 | case "$opt" in 11 | i) o_index=1 ;; 12 | *) return 1 ;; 13 | esac 14 | done 15 | shift $(( OPTIND - 1 )) 16 | 17 | if [ "$#" -eq 0 ]; then 18 | echo >&2 "contains: key not specified" 19 | return 1 20 | fi 21 | 22 | key="$1"; shift 23 | index=0 24 | for val in "$@"; do 25 | index=$(( index + 1 )) 26 | if [ "$val" = "$key" ]; then 27 | [ -n "$o_index" ] && echo $index 28 | return 0 29 | fi 30 | done 31 | return 1 32 | )} 33 | contains "$@" 34 | -------------------------------------------------------------------------------- /.config/bash/plugins/editor.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Allow ctrl-S for history navigation (with ctrl-R) 4 | stty -ixon 5 | 6 | # Use vi editing mode 7 | set -o vi 8 | 9 | # Do ble.sh things from here on. 10 | [ -n "$BLE_VERSION" ] || return 1 11 | 12 | # https://github.com/akinomyoga/ble.sh/wiki/Vi-(Vim)-editing-mode 13 | #bleopt keymap_vi_mode_show= 14 | bleopt keymap_vi_mode_string_nmap:=$'\e[1m-- NORMAL --\e[m' 15 | 16 | # Strip leading dollar signs. Fixes commands pasted from markdown. 17 | # shellcheck disable=SC2016 18 | ble/function#advice around ble/widget/default/accept-line ' 19 | if [[ "${_ble_edit_str:0:2}" == "$ " ]]; then 20 | ble/widget/beginning-of-logical-line 21 | ble/widget/insert-string "${_ble_edit_str:2}" 22 | ble/widget/kill-forward-logical-line 23 | fi 24 | ble/function#advice/do 25 | ' 26 | -------------------------------------------------------------------------------- /.config/bash/plugins/gitignore.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | gi() { 4 | local IFS=, 5 | curl -fLw $'\n' "https://www.toptal.com/developers/gitignore/api/${*}" 6 | } 7 | 8 | _gi_complete() { 9 | local cur="${COMP_WORDS[COMP_CWORD]}" 10 | local prefix partial 11 | if [[ $cur == *","* ]]; then 12 | prefix="${cur%,*}" 13 | partial="${cur##*,}" 14 | else 15 | prefix="" 16 | partial="$cur" 17 | fi 18 | local list 19 | list=$(curl -sfL https://www.toptal.com/developers/gitignore/api/list) || return 20 | COMPREPLY=() 21 | local IFS=$',\n\r' 22 | for item in $list; do 23 | [[ $item == "$partial"* ]] || continue 24 | if [[ -n $prefix ]]; then 25 | COMPREPLY+=("${prefix},${item}") 26 | else 27 | COMPREPLY+=("$item") 28 | fi 29 | done 30 | } 31 | 32 | complete -F _gi_complete -o nospace gi 33 | -------------------------------------------------------------------------------- /.config/bash/.bashrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck shell=bash disable=SC2001,SC2002 3 | 4 | BASH_THEME="starship" 5 | BASH_HOME=~/.config/bash 6 | 7 | plugins=( 8 | __init__ 9 | xdg 10 | homebrew 11 | ble 12 | repos 13 | atuin 14 | azure 15 | colors 16 | completions 17 | directory 18 | direnv 19 | dotfiles 20 | dotnet 21 | editor 22 | environment 23 | fzf 24 | git 25 | gitignore 26 | gpg 27 | history 28 | iwd 29 | jupyter 30 | macos 31 | magic-enter 32 | nim 33 | prompt 34 | python 35 | utils 36 | wezterm 37 | zoxide 38 | confd 39 | zzz 40 | ) 41 | 42 | for _plugin in "${plugins[@]}"; do 43 | if [ -r "$BASH_HOME/plugins/${_plugin}.sh" ]; then 44 | source "$BASH_HOME/plugins/${_plugin}.sh" 45 | else 46 | echo >&2 "Plugin not found: '$_plugin'." 47 | fi 48 | done 49 | 50 | unset _plugin 51 | -------------------------------------------------------------------------------- /.config/bash/plugins/repos.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | : "${REPO_HOME:=${XDG_CACHE_HOME:-$HOME/.cache}/bash/repos}" 4 | declare -a repos=() 5 | 6 | # Read the file line-by-line, skipping comments and empty lines. 7 | repos_txt="$BASH_HOME/repos.txt" 8 | while IFS= read -r line; do 9 | line="${line%#*}" 10 | 11 | # Trim leading/trailing whitespace and check if the line is not empty and not a comment. 12 | if [[ -n "$line" && ! "$line" =~ ^# ]]; then 13 | repos+=("$line") # Add the line to the array. 14 | fi 15 | done < "$repos_txt" 16 | 17 | # Clone missing repos. 18 | for repo in "${repos[@]}"; do 19 | [[ -d "$REPO_HOME/$repo" ]] && continue 20 | echo "Cloning ${repo}..." 21 | git clone --quiet --depth 1 --recurse-submodules --shallow-submodules \ 22 | "https://github.com/$repo" "$REPO_HOME/$repo" & 23 | done 24 | wait 25 | 26 | # Clean up. 27 | unset line repo repos_txt 28 | -------------------------------------------------------------------------------- /bin/capsmap: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # http://homeowmorphism.com/2017/05/27/Remap-CapsLock-Backspace-Sierra 4 | # https://developer.apple.com/library/archive/technotes/tn2450/_index.html 5 | 6 | # map caps (0x39) to delete (0x2A) 7 | # hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000039,"HIDKeyboardModifierMappingDst":0x70000002A}]}' 8 | 9 | # map caps (0x39) to F18 (0x6D) 10 | hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000039,"HIDKeyboardModifierMappingDst":0x70000006D}]}' 11 | 12 | # map right command ⌘ to control ^ 13 | hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x7000000E7,"HIDKeyboardModifierMappingDst":0x7000000E0}]}' 14 | 15 | # hyper - right alt (0xE6) to F19 (0x6E) 16 | # hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x7000000E6,"HIDKeyboardModifierMappingDst":0x70000006E}]}' 17 | -------------------------------------------------------------------------------- /docs/emacs.md: -------------------------------------------------------------------------------- 1 | # Emacs 2 | 3 | ## Install 4 | 5 | ### Emacs Mac Port 6 | 7 | * [Emacs Mac port](https://github.com/railwaycat/homebrew-emacsmacport) 8 | 9 | ```sh 10 | brew tap railwaycat/emacsmacport 11 | brew install emacs-mac --with-native-comp --with-emacs-big-sur-icon --with-starter 12 | ``` 13 | 14 | Install in `/Applications`: 15 | 16 | ```sh 17 | osascript -e 'tell application "Finder" to make alias file to POSIX file "/opt/homebrew/opt/emacs-mac/Emacs.app" at POSIX file "/Applications"' 18 | ``` 19 | 20 | ## Prelude 21 | 22 | ```zsh 23 | export PRELUDE_INSTALL_DIR="$HOME/.config/emacs" && curl -L https://github.com/bbatsov/prelude/raw/master/utils/installer.sh | sh 24 | ``` 25 | 26 | ```zsh 27 | # Fixes for GPG signature errors 28 | EMACS_GPG_DIR=~/.config/emacs/elpa/gnupg 29 | mkdir -p $EMACS_GPG_DIR 30 | gpg --homedir $EMACS_GPG_DIR --list-keys 31 | chmod 700 $EMACS_GPG_DIR 32 | chmod 600 $EMACS_GPG_DIR/* 33 | ``` 34 | -------------------------------------------------------------------------------- /bin/new_mac_setup: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | : ${HOMEBREW_PREFIX:=/opt/homebrew} 4 | : ${DOTFILES:=~/.config/dotfiles} 5 | 6 | # xdg 7 | export XDG_CONFIG_HOME=~/.config 8 | export XDG_CACHE_HOME=~/.cache 9 | export XDG_DATA_HOME=~/.local/share 10 | for _zdir in XDG_{CONFIG,CACHE,DATA}_HOME; mkdir -p ${(P)_zdir} 11 | 12 | # brew 13 | if [[ -d $HOMEBREW_PREFIX ]]; then 14 | eval "$($HOMEBREW_PREFIX/bin/brew shellenv)" 15 | else 16 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 17 | 18 | eval "$($HOMEBREW_PREFIX/bin/brew shellenv)" 19 | 20 | # brew bundle 21 | # brew bundle dump --force --file=$DOTFILES/home/Brewfile 22 | brew bundle --file=$DOTFILES/config/homebrew/init.brewfile 23 | fi 24 | 25 | # install dotfiles repo 26 | if [[ ! -d $DOTFILES ]]; then 27 | git clone https://github.com/mattmc3/dotfiles $DOTFILES 28 | fi 29 | 30 | # stow 31 | cd $DOTFILES 32 | make stow 33 | 34 | # cleanup 35 | unset _zdir 36 | -------------------------------------------------------------------------------- /.config/helix/config.toml: -------------------------------------------------------------------------------- 1 | theme = "tokyonight" 2 | 3 | [editor] 4 | line-number = "relative" 5 | 6 | [editor.cursor-shape] 7 | insert = "bar" 8 | normal = "block" 9 | select = "underline" 10 | 11 | [editor.file-picker] 12 | hidden = false 13 | 14 | [keys.normal] 15 | # ------------------------- 16 | # QWERTY to Colemak - UNEI arrows 17 | # [YJ] [UL] [IU] [..] 18 | # [HH] [JN] [KE] [LI] 19 | # [NK] [..] [..] [..] 20 | # 21 | # N <=> K (K is in the QWERTY N position) 22 | n = "move_char_left" 23 | N = "keep_selections" 24 | k = "search_next" 25 | K = "search_prev" 26 | # E <=> J (J now 'jumps words') 27 | e = "move_line_down" 28 | E = "join_selections" 29 | j = "move_next_word_end" 30 | J = "move_next_long_word_end" 31 | # H <=> I (H is just a sideways I) 32 | i = "move_char_right" 33 | I = "no_op" 34 | h = "insert_mode" 35 | H = "insert_at_line_start" 36 | # U <=> L (L is in the QWERTY U position) 37 | u = "move_line_up" 38 | U = "no_op" 39 | l = "undo" 40 | L = "redo" 41 | -------------------------------------------------------------------------------- /bin/dispatch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use realpath to find the directory of the actual script 4 | BINDIR="$(dirname "$(realpath "$0")")" 5 | 6 | # Use the name used to invoke the script (symlink name) 7 | CMDGROUP="$(basename "$0")" 8 | SUBDIR="$BINDIR/${CMDGROUP}.d" 9 | 10 | print_usage() { 11 | echo "Usage: $CMDGROUP [args...]" 12 | echo 13 | echo "Subcommands:" 14 | if [ -d "$SUBDIR" ]; then 15 | # List executable files only 16 | find "$SUBDIR" -type f -perm +111 -exec basename {} \; | sort 17 | else 18 | echo " (no subcommands found)" 19 | fi 20 | } 21 | 22 | if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then 23 | print_usage 24 | exit 1 25 | fi 26 | 27 | SUBCOMMAND="$1" 28 | shift 29 | 30 | CMD="$SUBDIR/$SUBCOMMAND" 31 | 32 | if [ ! -x "$CMD" ]; then 33 | echo "$CMDGROUP: subcommand '$SUBCOMMAND' not found or not executable in ./${CMDGROUP}.d" >&2 34 | print_usage 35 | exit 1 36 | fi 37 | 38 | exec "$CMD" "$@" 39 | -------------------------------------------------------------------------------- /.config/bash/plugins/jupyter.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash disable=SC1091 2 | # 3 | # Jupyter 4 | # 5 | 6 | export JUPYTER_CONFIG_DIR="${JUPYTER_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/jupyter}" 7 | 8 | # shellcheck disable=SC2016 9 | function juno { 10 | local workon_home="${WORKON_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/venvs}" 11 | [[ -d "$workon_home" ]] || mkdir -p "$workon_home" 12 | 13 | if [[ ! -d "$workon_home/juno" ]]; then 14 | if command -v workon >/dev/null 2>&1; then 15 | workon juno 16 | else 17 | python3 -m venv "$workon_home/juno" || return 1 18 | source "$workon_home/juno/bin/activate" 19 | fi 20 | pip install --upgrade pip 21 | pip install jupyterlab 22 | pip install pandas 23 | deactivate 24 | fi 25 | 26 | local jupyter_prj=$HOME/Projects/mattmc3/jupyter 27 | if [[ ! -d "$jupyter_prj" ]]; then 28 | git clone git@github.com:mattmc3/jupyter "$jupyter_prj" 29 | fi 30 | 31 | jupyter lab "${1:-$jupyter_prj}" 32 | } 33 | -------------------------------------------------------------------------------- /.config/zsh/conf.d/git-clone-with-cd.zsh: -------------------------------------------------------------------------------- 1 | # Enhance git clone so that it will cd into the newly cloned directory 2 | autoload -Uz add-zsh-hook 3 | typeset -g last_cloned_dir 4 | 5 | # Preexec: Detect 'git clone' command and set last_cloned_dir so we can cd into it 6 | _git_clone_preexec() { 7 | if [[ "$1" == git\ clone* ]]; then 8 | local last_arg="${1##* }" 9 | if [[ "$last_arg" =~ ^(https?|git@|ssh://|git://) ]]; then 10 | last_cloned_dir=$(basename "$last_arg" .git) 11 | else 12 | last_cloned_dir="$last_arg" 13 | fi 14 | fi 15 | } 16 | 17 | # Precmd: Runs before prompt is shown, and we can cd into our last_cloned_dir 18 | _git_clone_precmd() { 19 | if [[ -n "$last_cloned_dir" ]]; then 20 | if [[ -d "$last_cloned_dir" ]]; then 21 | echo "→ cd from $PWD to $last_cloned_dir" 22 | cd "$last_cloned_dir" 23 | fi 24 | # Reset 25 | last_cloned_dir= 26 | fi 27 | } 28 | 29 | add-zsh-hook preexec _git_clone_preexec 30 | add-zsh-hook precmd _git_clone_precmd 31 | -------------------------------------------------------------------------------- /.config/readline/inputrc: -------------------------------------------------------------------------------- 1 | # inputrc is the config for GNU Readline. Anything that uses Readline 2 | # benefits from these configs (in case you're wondering why they aren't 3 | # in .bashrc). 4 | # From http://www.ukuug.org/events/linux2003/papers/bash_tips/ 5 | # https://github.com/JEG2/dotfiles/blob/master/inputrc 6 | 7 | # allow the use of the Home/End keys 8 | "\e[1~": beginning-of-line 9 | "\e[4~": end-of-line 10 | 11 | # allow the use of the Delete/Insert keys 12 | "\e[3~": delete-char 13 | "\e[2~": quoted-insert 14 | 15 | # mappings for "page up" and "page down" 16 | # to step to the beginning/end of the history 17 | "\e[5~": beginning-of-history 18 | "\e[6~": end-of-history 19 | 20 | "\e[5C": forward-word 21 | "\e[5D": backward-word 22 | "\e\e[C": forward-word 23 | "\e\e[D": backward-word 24 | 25 | set completion-ignore-case on 26 | set expand-tilde on 27 | set convert-meta off 28 | set input-meta on 29 | set output-meta on 30 | set show-all-if-ambiguous on 31 | set visible-stats on 32 | set bell-style none 33 | -------------------------------------------------------------------------------- /.config/bash/plugins/completions.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | # # Completions. 4 | # if [ -z "$BLE_VERSION" ]; then 5 | # if [ -r "${HOMEBREW_PREFIX:-/opt/homebrew}/etc/profile.d/bash_completion.sh" ]; then 6 | # . "${HOMEBREW_PREFIX:-/opt/homebrew}/etc/profile.d/bash_completion.sh" 7 | # elif [ -f /usr/share/bash-completion/bash_completion ]; then 8 | # . /usr/share/bash-completion/bash_completion 9 | # elif [ -f /etc/bash_completion ]; then 10 | # . /etc/bash_completion 11 | # fi 12 | # return 13 | # fi 14 | 15 | [[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh" 16 | 17 | function settings-for-completion { 18 | # shellcheck disable=SC2016 19 | ble/function#advice around ble/complete/auto-complete.idle ' 20 | if ble/string#match "${_ble_edit_str:_ble_edit_ind}" "^[[:space:]]|^$"; then 21 | ble/function#advice/do 22 | else 23 | ble/util/idle.wait-user-input 24 | fi 25 | ' 26 | } 27 | blehook/eval-after-load complete settings-for-completion 28 | -------------------------------------------------------------------------------- /.config/nvim/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /bin/pathr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | modifiers=() 6 | while getopts ahter opt; do 7 | modifiers+=("$opt") 8 | done 9 | shift $((OPTIND - 1)) 10 | 11 | resolve_absolute_path() { 12 | local input=$1 13 | local -a parts stack 14 | 15 | [[ "$input" != /* ]] && input="$PWD/$input" 16 | IFS='/' read -ra parts <<< "$input" 17 | 18 | for part in "${parts[@]}"; do 19 | case $part in 20 | ''|'.') continue ;; 21 | '..') [[ ${#stack[@]} -gt 0 ]] && unset "stack[$(( ${#stack[@]} - 1 ))]" ;; 22 | *) stack+=("$part") ;; 23 | esac 24 | done 25 | 26 | (IFS=/; echo "/${stack[*]}") 27 | } 28 | 29 | path="$1" 30 | 31 | for mod in "${modifiers[@]}"; do 32 | case $mod in 33 | a) 34 | path=$(resolve_absolute_path "$path") 35 | ;; 36 | h) 37 | path="${path%/*}" 38 | ;; 39 | t) 40 | path="${path##*/}" 41 | ;; 42 | e) 43 | filename="${path##*/}" 44 | [[ "$filename" == *.* ]] && path="${filename##*.}" || path="" 45 | ;; 46 | r) 47 | path="${path%.*}" 48 | ;; 49 | esac 50 | done 51 | 52 | echo "$path" 53 | -------------------------------------------------------------------------------- /bin/str.d/match: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | usage() { 4 | echo "Usage: $0 [-a] [-e] [-v] [-i] [-h] PATTERN [STR...]" >&2 5 | exit "${1:-0}" 6 | } 7 | 8 | grep_opts="-E" 9 | entire=0 10 | all=0 11 | head_n=1 12 | 13 | while getopts "aevirh" opt; do 14 | case "$opt" in 15 | r) ;; # no-op - we're always assuming regex 16 | e) entire=1 ;; 17 | v) grep_opts="$grep_opts -v" ;; 18 | i) grep_opts="$grep_opts -i" ;; 19 | a) all=1 ;; 20 | h) usage ;; 21 | *) 22 | echo "Invalid option: -$OPTARG" >&2 23 | usage 2 24 | ;; 25 | esac 26 | done 27 | shift $((OPTIND - 1)) 28 | 29 | [ "$entire" -eq 0 ] && grep_opts="$grep_opts -o" 30 | 31 | [ -z "$1" ] && usage 2 32 | 33 | pattern=$1 34 | shift 35 | 36 | # Turn piped data into arguments 37 | if [ $# -eq 0 ]; then 38 | if [ -t 0 ]; then 39 | usage 2 40 | else 41 | set -- $(cat) 42 | fi 43 | fi 44 | 45 | # Pick the number of lines to print: 1 for default, huge for -a 46 | [ "$all" -eq 1 ] && head_n=999999 47 | 48 | for arg; do 49 | # shellcheck disable=SC2086 50 | printf '%s\n' "$arg" | grep $grep_opts -- "$pattern" | head -n "$head_n" 51 | done 52 | -------------------------------------------------------------------------------- /.config/bash/plugins/directory.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Config for dirstack, globbing, special dirs, and general file system nav. 4 | # CDPATH="." 5 | set -o noclobber # Prevent file overwrite on stdout redirection; use `>|` to force 6 | set -o pipefail # Return the rightmost non-zero code for piped commands if any fail 7 | shopt -s checkwinsize 2> /dev/null # Update window size after every command 8 | shopt -s cmdhist 2> /dev/null # Save multi-line commands as one command 9 | shopt -s extglob 2> /dev/null # Turn on extended globbing 10 | shopt -s globstar 2> /dev/null # Turn on recursive globbing (enables ** to recurse all directories) 11 | shopt -s nocaseglob 2> /dev/null # Case-insensitive globbing (used in pathname expansion) 12 | shopt -s autocd 2> /dev/null # Prepend cd to directory names automatically 13 | shopt -s dirspell 2> /dev/null # Correct spelling errors during tab-completion 14 | # shopt -s cdspell 2> /dev/null # Correct spelling errors in arguments supplied to cd 15 | # shopt -s cdable_vars 2> /dev/null # CD across the filesystem as if you're in that dir 16 | -------------------------------------------------------------------------------- /.config/nvim/doc/kickstart.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | INTRODUCTION *kickstart.nvim* 3 | 4 | Kickstart.nvim is a project to help you get started on your neovim journey. 5 | 6 | *kickstart-is-not* 7 | It is not: 8 | - Complete framework for every plugin under the sun 9 | - Place to add every plugin that could ever be useful 10 | 11 | *kickstart-is* 12 | It is: 13 | - Somewhere that has a good start for the most common "IDE" type features: 14 | - autocompletion 15 | - goto-definition 16 | - find references 17 | - fuzzy finding 18 | - and hinting at what more can be done :) 19 | - A place to _kickstart_ your journey. 20 | - You should fork this project and use/modify it so that it matches your 21 | style and preferences. If you don't want to do that, there are probably 22 | other projects that would fit much better for you (and that's great!)! 23 | 24 | vim:tw=78:ts=8:ft=help:norl: 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2025 mattmc3 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/fishy/contains.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local posix = require "posix" 4 | 5 | local function contains(args) 6 | local index_flag = false 7 | local needle 8 | local start = 1 9 | 10 | -- Check for -i or --index flag 11 | if args[1] == "-i" or args[1] == "--index" then 12 | index_flag = true 13 | needle = args[2] 14 | start = 3 15 | else 16 | needle = args[1] 17 | start = 2 18 | end 19 | 20 | if not needle then 21 | error({errmsg = "contains: Key not specified", errcode = 2}) 22 | end 23 | 24 | for i = start, #args do 25 | if args[i] == needle then 26 | if index_flag then 27 | return i - (index_flag and (start - 1) or 1) 28 | else 29 | return true 30 | end 31 | end 32 | end 33 | return false 34 | end 35 | 36 | -- Run command if run as a script 37 | if debug.getinfo(1, "S").short_src == arg[0] then 38 | local ok, result = pcall(contains, {...}) 39 | if not ok then 40 | io.stderr:write(result.errmsg .. "\n") 41 | os.exit(result.errcode) 42 | elseif type(result) == "number" then 43 | print(result) 44 | elseif not result then 45 | os.exit(1) 46 | end 47 | end 48 | 49 | return contains 50 | -------------------------------------------------------------------------------- /.config/starship/pure.toml: -------------------------------------------------------------------------------- 1 | # 2 | # Refined - a Pure-like theme for Starship 3 | # 4 | 5 | format = """ 6 | $username\ 7 | $hostname\ 8 | $directory\ 9 | $git_branch\ 10 | $git_state\ 11 | ${custom.git_status_dirty}\ 12 | $git_status\ 13 | $cmd_duration\ 14 | $line_break\ 15 | $python\ 16 | $character""" 17 | 18 | [directory] 19 | style = "blue" 20 | 21 | [character] 22 | success_symbol = "[❯](purple)" 23 | error_symbol = "[❯](red)" 24 | vicmd_symbol = "[❮](cyan)" 25 | 26 | [git_branch] 27 | format = "[$branch]($style)" 28 | style = "bright-black" 29 | 30 | [git_status] 31 | format = "[($ahead_behind$stashed)]($style)" 32 | style = "cyan" 33 | stashed = "≡" 34 | 35 | [custom.git_status_dirty] 36 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 37 | symbol = "*" 38 | style = "purple" 39 | format="[$symbol]($style) " 40 | shell = ["zsh", "--no-rcs", "--no-globalrcs"] 41 | 42 | [git_state] 43 | format = '\([$state( $progress_current/$progress_total)]($style)\) ' 44 | style = "bright-black" 45 | 46 | [cmd_duration] 47 | format = "[$duration]($style) " 48 | style = "yellow" 49 | 50 | [python] 51 | format = "[$virtualenv]($style) " 52 | style = "bright-black" 53 | -------------------------------------------------------------------------------- /.config/starship/omf.toml: -------------------------------------------------------------------------------- 1 | add_newline = false 2 | 3 | # A minimal left prompt 4 | format = """$directory$character""" 5 | 6 | # move the rest of the prompt to the right 7 | right_format = """$status$time$git_branch${custom.git_status_dirty}$git_status""" 8 | 9 | [character] 10 | success_symbol = '[⋊>](cyan)' 11 | error_symbol = '[⋊>](bold red)' 12 | vicmd_symbol = '[vi ⋊>](blue)' 13 | 14 | [directory] 15 | style = "white" 16 | truncation_length = 1 17 | truncation_symbol = "" 18 | fish_style_pwd_dir_length = 1 19 | 20 | # right prompt uses left space padding 21 | [git_branch] 22 | format = ' [$branch]($style)' 23 | style = 'bold green' 24 | 25 | [git_status] 26 | format = '[$all_status$ahead_behind]($style)' 27 | ahead = '↑' 28 | behind = '↓' 29 | stashed = '≡' 30 | modified = '⨯' 31 | diverged = '⥄' 32 | style = "purple" 33 | 34 | [time] 35 | format = '[$time]($style)' 36 | disabled = false 37 | 38 | [line_break] 39 | disabled = true 40 | 41 | [status] 42 | disabled = false 43 | symbol = ' ✘' 44 | 45 | [custom.git_status_dirty] 46 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 47 | symbol = "⨯" 48 | style = "purple" 49 | format="[$symbol]($style)" 50 | shell = ["sh"] 51 | -------------------------------------------------------------------------------- /bin/fishy/t/string.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use File::Spec; 5 | 6 | my $script = './string.pl'; 7 | 8 | sub run { 9 | my ($args, $input) = @_; 10 | my $cmd = "perl $script $args"; 11 | open my $fh, "|-", $cmd or die $!; 12 | print $fh $input if defined $input; 13 | close $fh; 14 | # For output, use backticks: 15 | return `$cmd $args`; 16 | } 17 | 18 | # Uppercase 19 | is(`perl $script upper foo bar`, "FOO\nBAR\n", 'upper works'); 20 | 21 | # Lowercase 22 | is(`perl $script lower FOO BAR`, "foo\nbar\n", 'lower works'); 23 | 24 | # Trim 25 | is(`perl $script trim " foo " "bar "`, "foo\nbar\n", 'trim works'); 26 | 27 | # Length 28 | is(`perl $script length foo bar`, "3\n3\n", 'length works'); 29 | 30 | # Split 31 | is(`perl $script split , "a,b,c"`, "a\nb\nc\n", 'split works'); 32 | 33 | # Join 34 | is(`perl $script join , a b c`, "a,b,c\n", 'join works'); 35 | 36 | # Split0 (NUL-separated) 37 | my $nul_input = "a\0b\0c\0"; 38 | is(`echo -ne "$nul_input" | perl $script split0`, "a\nb\nc\n", 'split0 works'); 39 | 40 | # Join0 (NUL-separated) 41 | is(`perl $script join0 a b c`, "a\0b\0c\0", 'join0 works'); 42 | 43 | done_testing; 44 | -------------------------------------------------------------------------------- /.config/bash/plugins/colors.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Colorize man pages. 4 | export LESS_TERMCAP_md=$'\E[01;34m' # start bold 5 | export LESS_TERMCAP_mb=$'\E[01;34m' # start blink 6 | export LESS_TERMCAP_so=$'\E[00;47;30m' # start standout: white bg, black fg 7 | export LESS_TERMCAP_us=$'\E[04;35m' # start underline: underline magenta 8 | export LESS_TERMCAP_se=$'\E[0m' # end standout 9 | export LESS_TERMCAP_ue=$'\E[0m' # end underline 10 | export LESS_TERMCAP_me=$'\E[0m' # end bold/blink 11 | 12 | # Set LS_COLORS using dircolors 13 | if [[ -z "$LS_COLORS" ]]; then 14 | if type dircolors >/dev/null 2>&1; then 15 | cached_eval dircolors --sh 16 | #eval "$(dircolors --sh)" 17 | elif type gdircolors >/dev/null 2>&1; then 18 | cached_eval gdircolors --sh 19 | #eval "$(gdircolors --sh)" 20 | fi 21 | fi 22 | 23 | # Fallback, even for BSD systems 24 | export LS_COLORS="${LS_COLORS:-di=34:ln=35:so=32:pi=33:ex=31:bd=1;36:cd=1;33:su=30;41:sg=30;46:tw=30;42:ow=30;43}" 25 | 26 | # For BSD systems, set LSCOLORS instead. 27 | if ! type dircolors >/dev/null 2>&1; then 28 | export CLICOLOR=1 29 | export LSCOLORS="exfxcxdxbxGxDxabagacad" 30 | fi 31 | -------------------------------------------------------------------------------- /.config/starship/fish.toml: -------------------------------------------------------------------------------- 1 | add_newline = false 2 | 3 | # A minimal left prompt 4 | format = """$python$directory$character""" 5 | 6 | # move the rest of the prompt to the right 7 | right_format = """$status$all$git_branch$git_status""" 8 | 9 | [character] 10 | success_symbol = "[»](cyan)" 11 | error_symbol = "[»](red)" 12 | vicmd_symbol = "[⪻](yellow)" 13 | 14 | [python] 15 | format = '\($virtualenv\) ' 16 | 17 | [directory] 18 | style = "blue" 19 | truncation_length = 1 20 | truncation_symbol = "" 21 | fish_style_pwd_dir_length = 1 22 | 23 | # right prompt uses left space padding 24 | [git_branch] 25 | format = ' [$branch]($style)' 26 | style = 'bold green' 27 | 28 | # [git_status] 29 | # format = '$all_status$ahead_behind' 30 | # ahead = ' [⬆](bold purple)' 31 | # behind = ' [⬇](bold purple)' 32 | # staged = ' [✚](green)' 33 | # deleted = ' [✖](red)' 34 | # renamed = ' [➜](purple)' 35 | # stashed = ' [✭](cyan)' 36 | # untracked = ' [◼](white)' 37 | # modified = ' [✱](blue)' 38 | # conflicted = ' [═](yellow)' 39 | # diverged = ' ⇕' 40 | # up_to_date = '' 41 | 42 | [cmd_duration] 43 | format = ' [$duration]($style)' 44 | 45 | [line_break] 46 | disabled = true 47 | 48 | [status] 49 | disabled = false 50 | symbol = ' ✘' 51 | -------------------------------------------------------------------------------- /bin/fishy/string-length: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | # string length [-q | --quiet] [STRING ...] 5 | 6 | string_length() { 7 | local str len line exitcode o_help o_quiet 8 | 9 | # Parse options. 10 | while [ "$#" -gt 0 ]; do 11 | case "$1" in 12 | -h|--help) o_help=true; shift ;; 13 | -q|--quiet) o_quiet=true; shift ;; 14 | --) shift; break ;; 15 | -*) o_bad="$1"; shift ;; 16 | *) break ;; 17 | esac 18 | done 19 | 20 | # Check usage. 21 | if [ -n "$o_help" ]; then 22 | printf 'string length [-h | --help] [-q | --quiet] [STRING ...]\n' 23 | return 0 24 | elif [ -n "$o_bad" ]; then 25 | printf >&2 'string length: %s: unknown option\n' "$o_bad" 26 | return 2 27 | fi 28 | 29 | # Collect piped input 30 | if ! [ -t 0 ]; then 31 | while IFS= read -r line || [ -n "$line" ]; do 32 | set -- "$@" "$line" 33 | done 34 | fi 35 | 36 | # Print lengths 37 | exitcode=1 38 | for str in "$@"; do 39 | len="${#str}" 40 | [ "$len" -gt 0 ] && exitcode=0 41 | [ -z "$o_quiet" ] && printf '%s\n' "$len" 42 | done 43 | return "$exitcode" 44 | } 45 | string_length "$@" || exit $? 46 | -------------------------------------------------------------------------------- /.config/starship/zebrafish.toml: -------------------------------------------------------------------------------- 1 | # Zebrafish prompt (made possible with Starship) 2 | # Copyright (c) 2021-2022 mattmc3 3 | # https://github.com/mattmc3/zebrafish 4 | # License: MIT 5 | # version v0.7.0 6 | 7 | add_newline = false 8 | 9 | [character] 10 | success_symbol = "[%%](purple)" 11 | error_symbol = "[%%](red)" 12 | vicmd_symbol = "[❮](bold green)" 13 | 14 | [directory] 15 | style = "blue" 16 | repo_root_style = "blue" 17 | truncate_to_repo = false 18 | truncation_length = 0 19 | disabled = false 20 | 21 | [cmd_duration] 22 | format = "[$duration](yellow)" 23 | 24 | [git_branch] 25 | style = "bright-black" 26 | format = "[$branch]($style)" 27 | 28 | [git_status] 29 | format = "[[(*$conflicted$untracked$modified$staged$renamed$deleted)](218) ($ahead_behind$stashed)]($style) " 30 | style = "cyan" 31 | conflicted = "​" 32 | untracked = "​" 33 | modified = "​" 34 | staged = "​" 35 | renamed = "​" 36 | deleted = "​" 37 | stashed = "≡" 38 | ahead = "⇡${count}" 39 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 40 | behind = "⇣${count}" 41 | 42 | [git_state] 43 | format = '\([$state( $progress_current/$progress_total)]($style)\) ' 44 | style = "bright-black" 45 | 46 | [status] 47 | style = "red" 48 | format = '[\[$int\]]($style) ' 49 | disabled = false 50 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [bin/*] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.json] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [.{zshrc,zprofile,zshenv,zlogin,zlogout,zstyle}] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | [**/zsh/functions/*] 24 | indent_style = space 25 | indent_size = 2 26 | 27 | [**/bin/*] 28 | indent_style = space 29 | indent_size = 2 30 | 31 | [**/bin/*/*] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | [.{bashrc,bash_profile}] 36 | indent_style = space 37 | indent_size = 2 38 | 39 | [*.{bash,zsh,sh}] 40 | indent_style = space 41 | indent_size = 2 42 | 43 | [*.{md,markdown}] 44 | trim_trailing_whitespace = false 45 | 46 | [*.pl] 47 | indent_style = space 48 | indent_size = 4 49 | 50 | [*.lua] 51 | indent_style = space 52 | indent_size = 2 53 | 54 | [bin/*.lua] 55 | indent_style = tab 56 | indent_size = 4 57 | 58 | [*.sql] 59 | indent_style = space 60 | indent_size = 4 61 | 62 | [*.fish] 63 | indent_style = space 64 | 65 | [*.{py,xsh}] 66 | indent_style = space 67 | 68 | [*.vim] 69 | indent_style = space 70 | -------------------------------------------------------------------------------- /.config/starship/prezto.toml: -------------------------------------------------------------------------------- 1 | # 2 | # prezto - mimic the default prezto prompt (sorin) 3 | # 4 | 5 | add_newline = false 6 | 7 | # A minimal left prompt 8 | format = """$python$directory$character""" 9 | 10 | # move the rest of the prompt to the right 11 | right_format = """$status$git_branch$git_status""" 12 | 13 | [character] 14 | success_symbol = "[❯](red)[❯](yellow)[❯](green)" 15 | error_symbol = "[❯](red)[❯](yellow)[❯](green)" 16 | vicmd_symbol = "[❮](green)[❮](yellow)[❮](red)" 17 | 18 | [git_branch] 19 | format = '[$branch]($style) ' 20 | style = 'bold green' 21 | 22 | [python] 23 | format = '\($virtualenv\) ' 24 | 25 | [git_status] 26 | format = '$all_status$ahead_behind ' 27 | ahead = '[⬆](bold purple) ' 28 | behind = '[⬇](bold purple) ' 29 | staged = '[✚](green) ' 30 | deleted = '[✖](red) ' 31 | renamed = '[➜](purple) ' 32 | stashed = '[✭](cyan) ' 33 | untracked = '[◼](white) ' 34 | modified = '[✱](blue) ' 35 | conflicted = '[═](yellow) ' 36 | diverged = '⇕ ' 37 | up_to_date = '' 38 | 39 | [directory] 40 | style = "blue" 41 | truncation_length = 1 42 | truncation_symbol = "" 43 | fish_style_pwd_dir_length = 1 44 | 45 | [cmd_duration] 46 | format = '[$duration]($style) ' 47 | 48 | [line_break] 49 | disabled = true 50 | 51 | [status] 52 | disabled = false 53 | symbol = '✘ ' 54 | -------------------------------------------------------------------------------- /.config/tmux/tmux.conf: -------------------------------------------------------------------------------- 1 | # https://www.seanh.cc/2020/12/30/how-to-make-tmux's-windows-behave-like-browser-tabs 2 | 3 | set -g base-index 1 # Start numbering windows at 1, not 0. 4 | set -g pane-base-index 1 # Start numbering panes at 1, not 0. 5 | bind -n C-t new-window 6 | bind -n C-PgDn next-window 7 | bind -n C-PgUp previous-window 8 | bind -n C-S-Left swap-window -t -1\; select-window -t -1 9 | bind -n C-S-Right swap-window -t +1\; select-window -t +1 10 | bind -n M-1 select-window -t 1 11 | bind -n M-2 select-window -t 2 12 | bind -n M-3 select-window -t 3 13 | bind -n M-4 select-window -t 4 14 | bind -n M-5 select-window -t 5 15 | bind -n M-6 select-window -t 6 16 | bind -n M-7 select-window -t 7 17 | bind -n M-8 select-window -t 8 18 | bind -n M-9 select-window -t:$ 19 | bind -n C-M-w kill-window 20 | bind -n C-M-q confirm -p "Kill this tmux session?" kill-session 21 | bind -n F11 resize-pane -Z 22 | set -g mouse on 23 | 24 | set -g status-style "bg=default" 25 | set -g window-status-current-style "bg=default,reverse" 26 | set -g window-status-separator '' # No spaces between windows in the status bar. 27 | set -g window-status-format "#{?window_start_flag,, }#I:#W#{?window_flags,#F, } " 28 | set -g window-status-current-format "#{?window_start_flag,, }#I:#W#{?window_flags,#F, } " 29 | -------------------------------------------------------------------------------- /bin/fishy/random.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local posix = require "posix" 4 | 5 | local function random(args) 6 | local min, max 7 | 8 | if #args == 0 then 9 | min, max = 0, 32767 10 | elseif #args == 1 then 11 | min, max = 0, tonumber(args[1]) 12 | elseif #args == 2 then 13 | min, max = tonumber(args[1]), tonumber(args[2]) 14 | else 15 | error({errmsg = "random: Too many arguments", errcode = 1}) 16 | end 17 | 18 | if not min or not max then 19 | error({errmsg = "random: Arguments must be numbers", errcode = 1}) 20 | end 21 | 22 | if min > max then 23 | min, max = max, min 24 | end 25 | 26 | local val = math.random(min, max) 27 | return val 28 | end 29 | 30 | -- Run command if run as a script 31 | if debug.getinfo(1, "S").short_src == arg[0] then 32 | local pid = posix.getpid("pid") 33 | local ppid = posix.getpid("ppid") 34 | local seed = os.time() + tonumber(tostring(os.clock()):reverse()) 35 | print(string.format("ppid: %s", ppid)) 36 | print(string.format("pid: %s", pid)) 37 | print(string.format("seed: %s", seed)) 38 | math.randomseed(seed + pid) 39 | 40 | local ok, result = pcall(random, {...}) 41 | if not ok then 42 | io.stderr:write(result.errmsg .. "\n") 43 | os.exit(2) 44 | else 45 | print(result) 46 | end 47 | end 48 | 49 | return random 50 | -------------------------------------------------------------------------------- /bin/fishy/.bcrc2: -------------------------------------------------------------------------------- 1 | # https://man.freebsd.org/cgi/man.cgi?query=bc&sektion=1 2 | 3 | define npr(n, r) { 4 | return perm(n, r) 5 | } 6 | 7 | define ncr(n, r) { 8 | return comb(n, r) 9 | } 10 | 11 | define fac(n) { 12 | return f(n); 13 | } 14 | 15 | define floor(x) { 16 | scale=0 17 | return x/1 18 | } 19 | 20 | define min(n, m) { 21 | if (n < m) return n; 22 | return m; 23 | } 24 | 25 | define max(n, m) { 26 | if (n < m) return (m); 27 | return n; 28 | } 29 | 30 | ######################################################################################## 31 | 32 | define pi(x) { 33 | scale=x 34 | return 4*a(1) 35 | } 36 | 37 | define ceil(x,y) { 38 | scale=0 39 | if (x == x/1) return(x) 40 | return((x+1)/1) 41 | } 42 | 43 | define abs(n) { 44 | if (n<0) return -n 45 | return n 46 | } 47 | 48 | define f(n) { 49 | if (n <= 1) return (1); 50 | return (f(n-1) * n); 51 | } 52 | 53 | define min(x,y) { 54 | if (x/dev/null; then 14 | magic_cmd="$MAGIC_ENTER_GIT_COMMAND" 15 | else 16 | magic_cmd="$MAGIC_ENTER_OTHER_COMMAND" 17 | fi 18 | 19 | # Run the command magically. 20 | ble/widget/execute-command "$magic_cmd" 21 | 22 | # Or, run the command more explicitly. 23 | # ble/widget/insert-string "$magic_cmd" 24 | # ble/widget/accept-line 25 | } 26 | 27 | function ble/widget/blerc/magic-enter { 28 | # If no command is given, run magic-enter-command. Otherwise, run the 29 | # ble.sh default binding for C-m/RET. 30 | if [[ ! $_ble_edit_str ]]; then 31 | ble/widget/blerc/magic-enter-command 32 | else 33 | ble/widget/accept-single-line-or-newline 34 | fi 35 | } 36 | ble-bind -f C-m blerc/magic-enter # for traditional keyboard protocol 37 | ble-bind -f RET blerc/magic-enter # for advanced keyboard protocol 38 | -------------------------------------------------------------------------------- /.config/bash/readme.md: -------------------------------------------------------------------------------- 1 | # bashrc 2 | 3 | ## XDG base directories 4 | 5 | Bash does not have a built-in way to use proper XDG base directory locations for 6 | its files, so you're forced to clutter up your $HOME. But, you can minimize the 7 | content of those $HOME files and put your real config in a different location by 8 | creating stubs for the Bash runcoms. For example, create the following stub in 9 | '~/.bashrc' to allow this file to live in '~/.config/bash/.bashrc'. It also allows 10 | you to try out other configs by changing a $BASH_HOME variable, like Zsh's $ZDOTDIR. 11 | 12 | ```bash 13 | # contents of ~/.bashrc 14 | export BASH_HOME="${BASH_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}/bash}" 15 | [ -r "${BASH_HOME:-?}"/.bashrc ] && . "$BASH_HOME"/.bashrc 16 | 17 | # contents of ~/.bash_profile 18 | [ -r "${BASH_HOME:-?}"/.bash_profile ] && . "$BASH_HOME"/.bash_profile 19 | ``` 20 | 21 | ## ble.sh 22 | 23 | This config can leverage ble.sh if you have it installed. Ble.sh makes Bash a much 24 | nicer shell to use interactively. It's optional, but recommended. See here for more 25 | info: https://github.com/akinomyoga/ble.sh. 26 | 27 | You can install ble.sh by running the following commands: 28 | 29 | ```bash 30 | BLE_REPO_HOME="${XDG_CACHE_HOME:-$HOME/.cache}/repos/akinomyoga/ble.sh" 31 | git clone --depth 1 --recurse-submodules --shallow-submodules https://github.com/akinomyoga/ble.sh "$BLE_REPO_HOME" 32 | make -C "$BLE_REPO_HOME" install PREFIX=~/.local 33 | ``` 34 | -------------------------------------------------------------------------------- /.config/bash/plugins/utils.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Cross-platform support for an 'open' command. 4 | if ! type open >/dev/null 2>&1; then 5 | if [[ "$OSTYPE" == cygwin* ]]; then 6 | alias open='cygstart' 7 | elif [[ "$OSTYPE" == linux-android ]]; then 8 | alias open='termux-open' 9 | elif type explorer.exe >/dev/null 2>&1; then 10 | alias open='explorer.exe' 11 | elif type xdg-open >/dev/null 2>&1; then 12 | alias open='xdg-open' 13 | fi 14 | fi 15 | 16 | # Cross-platform support for clipboard commands (clipcopy/clippaste). 17 | if [[ "$OSTYPE" == darwin* ]]; then 18 | alias clipcopy='pbcopy' 19 | alias clippaste='pbpaste' 20 | elif [[ "$OSTYPE" == cygwin* ]]; then 21 | alias clipcopy='tee > /dev/clipboard' 22 | alias clippaste='cat /dev/clipboard' 23 | elif type clip.exe >/dev/null 2>&1 && type powershell.exe >/dev/null 2>&1; then 24 | alias clipcopy='clip.exe' 25 | alias clippaste='powershell.exe -noprofile -command Get-Clipboard' 26 | elif [[ -n "$WAYLAND_DISPLAY" ]] && type wl-copy >/dev/null 2>&1 && type wl-paste >/dev/null 2>&1; then 27 | alias clipcopy='wl-copy' 28 | alias clippaste='wl-paste' 29 | elif [[ -n "$DISPLAY" ]] && type xclip >/dev/null 2>&1; then 30 | alias clipcopy='xclip -selection clipboard -in' 31 | alias clippaste='xclip -selection clipboard -out' 32 | elif [[ -n "$DISPLAY" ]] && type xsel >/dev/null 2>&1; then 33 | alias clipcopy='xsel --clipboard --input' 34 | alias clippaste='xsel --clipboard --output' 35 | fi 36 | -------------------------------------------------------------------------------- /.config/starship/hydro.toml: -------------------------------------------------------------------------------- 1 | # 2 | # hydro - mimic Fish's hydro prompt 3 | # 4 | 5 | add_newline = false 6 | 7 | # A minimal left prompt 8 | format = """$all$directory$git_branch${custom.git_status_dirty}$git_status$cmd_duration$status$character""" 9 | 10 | # no right prompt 11 | right_format = "" 12 | 13 | # Timeout for commands executed by starship (in milliseconds) 14 | command_timeout=2000 15 | 16 | [character] 17 | success_symbol = "[❱](purple)" 18 | error_symbol = "[❱](red)" 19 | vicmd_symbol = "[❰](cyan)" 20 | 21 | [python] 22 | format = '[(\($virtualenv\) )]($style)' 23 | style = 'white' 24 | 25 | [directory] 26 | style = "blue" 27 | truncation_length = 1 28 | truncation_symbol = "" 29 | fish_style_pwd_dir_length = 1 30 | 31 | # right prompt uses left space padding 32 | [git_branch] 33 | format = '[$branch]($style)' 34 | style = 'bold green' 35 | 36 | [git_status] 37 | format = "[($ahead_behind$stashed)]($style) " 38 | style = "cyan" 39 | stashed = "≡" 40 | ahead = "⇡${count}" 41 | behind = "⇣${count}" 42 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 43 | 44 | [custom.git_status_dirty] 45 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 46 | symbol = "•" 47 | style = "white" 48 | format="[$symbol]($style)" 49 | shell = ["zsh", "--no-rcs", "--no-globalrcs"] 50 | 51 | [cmd_duration] 52 | format = '[$duration]($style) ' 53 | 54 | [line_break] 55 | disabled = true 56 | 57 | [status] 58 | disabled = false 59 | pipestatus = true 60 | format = '[$symbol$int]($style)' 61 | symbol = '✘' 62 | pipestatus_format = '\[$pipestatus\]($style)' 63 | -------------------------------------------------------------------------------- /bin/fishy/string.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local cmd = nil 4 | local quiet = false 5 | local args = {} 6 | local parsing_flags = true 7 | 8 | -- Parse arguments 9 | local i = 1 10 | while i <= #arg do 11 | local a = arg[i] 12 | if parsing_flags then 13 | if a == "--" then 14 | parsing_flags = false 15 | elseif a == "-q" or a == "--quiet" then 16 | quiet = true 17 | elseif not cmd then 18 | cmd = a 19 | else 20 | table.insert(args, a) 21 | end 22 | else 23 | table.insert(args, a) 24 | end 25 | i = i + 1 26 | end 27 | 28 | if not cmd then 29 | io.stderr:write("Usage: str [-q|--quiet] [--] [strings...]\n") 30 | os.exit(1) 31 | end 32 | 33 | local function transform(line) 34 | if cmd == "upper" then 35 | return string.upper(line) 36 | elseif cmd == "lower" then 37 | return string.lower(line) 38 | else 39 | io.stderr:write("Unknown command: " .. tostring(cmd) .. "\n") 40 | os.exit(1) 41 | end 42 | end 43 | 44 | local did_transform = false 45 | 46 | if #args > 0 then 47 | for _, original in ipairs(args) do 48 | local transformed = transform(original) 49 | if not quiet then 50 | print(transformed) 51 | end 52 | if transformed ~= original then 53 | did_transform = true 54 | end 55 | end 56 | else 57 | for line in io.lines() do 58 | local transformed = transform(line) 59 | if not quiet then 60 | print(transformed) 61 | end 62 | if transformed ~= line then 63 | did_transform = true 64 | end 65 | end 66 | end 67 | 68 | if did_transform then 69 | os.exit(0) 70 | else 71 | os.exit(1) 72 | end 73 | -------------------------------------------------------------------------------- /.config/zsh/conf.d/history.zsh: -------------------------------------------------------------------------------- 1 | # History 2 | HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/history" 3 | [ -d "${HISTFILE:h}" ] || mkdir -p "${HISTFILE:h}" 4 | 5 | ## History command configuration 6 | # OMZ settings: 7 | # setopt extended_history # record timestamp of command in HISTFILE 8 | # setopt hist_expire_dups_first # delete duplicates first when HISTFILE size exceeds HISTSIZE 9 | # setopt hist_ignore_dups # ignore duplicated commands history list 10 | # setopt hist_ignore_space # ignore commands that start with space 11 | # setopt hist_verify # show command with history expansion to user before running it 12 | # setopt share_history # share command history data 13 | 14 | # My additional settings: 15 | setopt bang_hist # Treat the '!' character specially during expansion. 16 | setopt hist_find_no_dups # Do not display a previously found event. 17 | setopt hist_ignore_all_dups # Delete an old recorded event if a new event is a duplicate. 18 | setopt hist_reduce_blanks # Remove extra blanks from commands added to the history list. 19 | setopt hist_save_no_dups # Do not write a duplicate event to the history file. 20 | setopt inc_append_history # Write to the history file immediately, not when the shell exits. 21 | setopt NO_hist_beep # Don't beep when accessing non-existent history. 22 | setopt NO_share_history # Don't share history between all sessions. 23 | 24 | # History aliases. 25 | alias hist='fc -li' 26 | alias history-stat="history 0 | awk '{print \$2}' | sort | uniq -c | sort -n -r | head" 27 | -------------------------------------------------------------------------------- /.config/homebrew/init.brewfile: -------------------------------------------------------------------------------- 1 | tap "homebrew/bundle" 2 | tap "homebrew/cask-fonts" 3 | brew "coreutils" 4 | brew "curl" 5 | brew "editorconfig" 6 | brew "fish" 7 | brew "fzf" 8 | brew "git" 9 | brew "gnupg" 10 | brew "go" 11 | brew "helix" 12 | brew "mas" 13 | brew "neovim" 14 | brew "oath-toolkit" 15 | brew "sqlite" 16 | brew "ripgrep" 17 | brew "rsync" 18 | brew "starship" 19 | brew "stow" 20 | brew "tmux" 21 | brew "tree" 22 | brew "vim" 23 | brew "wget" 24 | brew "yq" 25 | brew "zoxide" 26 | brew "zsh" 27 | cask "alfred" 28 | cask "bartender" 29 | cask "betterdisplay" 30 | cask "brave-browser" 31 | cask "font-fira-code" 32 | cask "font-fira-code-nerd-font" 33 | cask "font-hack-nerd-font" 34 | cask "font-inconsolata-nerd-font" 35 | cask "font-iosevka-nerd-font" 36 | cask "font-meslo-lg-nerd-font" 37 | cask "font-sauce-code-pro-nerd-font" 38 | cask "hammerspoon" 39 | cask "iterm2" 40 | cask "keepingyouawake" 41 | cask "slack" 42 | cask "sublime-text" 43 | cask "visual-studio-code" 44 | mas "Bitwarden", id: 1352778147 45 | mas "DaisyDisk", id: 411643860 46 | mas "GarageBand", id: 682658836 47 | mas "GoodNotes", id: 1444383602 48 | mas "iMovie", id: 408981434 49 | mas "Keynote", id: 409183694 50 | mas "Microsoft Excel", id: 462058435 51 | mas "Microsoft OneNote", id: 784801555 52 | mas "Microsoft Outlook", id: 985367838 53 | mas "Microsoft PowerPoint", id: 462062816 54 | mas "Microsoft Word", id: 462054704 55 | mas "MindNode", id: 1289197285 56 | mas "Numbers", id: 409203825 57 | mas "OneDrive", id: 823766827 58 | mas "Pages", id: 409201541 59 | mas "Pixelmator Pro", id: 1289583905 60 | mas "Tampermonkey", id: 1482490089 61 | mas "TaskPaper", id: 1090940630 62 | mas "Wipr", id: 1320666476 63 | -------------------------------------------------------------------------------- /.config/starship/zsh.toml: -------------------------------------------------------------------------------- 1 | add_newline = false 2 | 3 | # A minimal left prompt 4 | format = """$python$directory$character""" 5 | 6 | # move the rest of the prompt to the right 7 | #right_format = """$status$git_branch${custom.git_status_dirty}$git_status""" 8 | right_format = """$status$cmd_duration$git_branch$git_status${custom.git_status_dirty}""" 9 | 10 | # Set the palette. 11 | palette = "wombat" 12 | 13 | # Define custom colors 14 | [palettes.tokyo_night] 15 | black = '16' 16 | blue = '111' 17 | cyan = '117' 18 | green = '149' 19 | purple = '141' 20 | red = '210' 21 | white = '146' 22 | yellow = '179' 23 | 24 | [palettes.wombat] 25 | black = '0' 26 | blue = '75' 27 | cyan = '123' 28 | green = '149' 29 | purple = '171' 30 | red = '203' 31 | white = '188' 32 | yellow = '223' 33 | 34 | [character] 35 | success_symbol = "[❯](purple)[❯](cyan)" 36 | error_symbol = "[❯](yellow)[❯](red)" 37 | vicmd_symbol = "[❮](purple)[❮](cyan)" 38 | 39 | [python] 40 | format = '\($virtualenv\) ' 41 | 42 | [directory] 43 | style = "blue" 44 | truncation_length = 1 45 | truncation_symbol = "" 46 | fish_style_pwd_dir_length = 1 47 | 48 | # right prompt uses left space padding 49 | [git_branch] 50 | format = ' [$branch]($style)' 51 | style = 'green' 52 | 53 | [git_status] 54 | format = '([\[$ahead_behind$stashed\]]($style))' 55 | style = "cyan" 56 | stashed = "≡" 57 | ahead = "⇡${count}" 58 | behind = "⇣${count}" 59 | 60 | [custom.git_status_dirty] 61 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 62 | symbol = "•" 63 | style = "white" 64 | format="[$symbol]($style)" 65 | shell = ["sh"] 66 | 67 | [cmd_duration] 68 | format = ' [$duration]($style)' 69 | 70 | [line_break] 71 | disabled = true 72 | 73 | [status] 74 | disabled = false 75 | symbol = ' ✘' 76 | -------------------------------------------------------------------------------- /bin/pocket2ffbookmarks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # pocket2firefox_with_tags 4 | # Converts Pocket JSON to a Firefox-compatible HTML bookmarks file with tags. 5 | # 6 | 7 | INPUT_FILE="pocket.json" 8 | OUTPUT_FILE="bookmarks.html" 9 | 10 | # Check if the input file exists 11 | if [ ! -f "$INPUT_FILE" ]; then 12 | echo "Error: Input file '$INPUT_FILE' does not exist." 13 | exit 1 14 | fi 15 | 16 | echo "[+] Converting '$INPUT_FILE' to Firefox bookmarks file '$OUTPUT_FILE'..." 17 | 18 | # Generate HTML with tags 19 | jq -r ' 20 | def escape_html: 21 | gsub("&"; "&") | gsub("<"; "<") | gsub(">"; ">") | gsub("\""; """); 22 | 23 | [ 24 | .[] | { 25 | title: (if .resolved_title != null then .resolved_title else (if .given_title != null then .given_title else "Untitled" end) end) | escape_html, 26 | url: (if .resolved_url != null then .resolved_url else .given_url end), 27 | time_added: (if .time_added != null then (.time_added | tonumber | strftime("%Y-%m-%d %H:%M:%S")) else "Unknown Date" end), 28 | tags: (if .tags != null then (.tags | keys_unsorted | join(", ")) else "" end) 29 | } 30 | ] 31 | | "\n" + 32 | "\n" + 33 | "Bookmarks\n" + 34 | "

Bookmarks

\n" + 35 | "

\n" + 36 | (map("

\(.title)") | join("\n")) + 37 | "\n

" 38 | ' "$INPUT_FILE" > "$OUTPUT_FILE" 39 | 40 | if [ $? -eq 0 ]; then 41 | echo "[+] Firefox bookmarks file created successfully: $OUTPUT_FILE" 42 | else 43 | echo "[-] Error: Failed to create Firefox bookmarks file." 44 | exit 1 45 | fi 46 | -------------------------------------------------------------------------------- /bin/fishy/string-transform: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | # string upper [-q | --quiet] [STRING ...] 5 | # string lower [-q | --quiet] [STRING ...] 6 | 7 | TRANSFORM="$1" 8 | case "$TRANSFORM" in 9 | lower|upper) 10 | shift 11 | break 12 | ;; 13 | *) 14 | printf >&2 'string transform: unknown transform: %s\n' "$1" 15 | exit 2 16 | ;; 17 | esac 18 | 19 | string_transform() { 20 | local str newstr line exitcode o_help o_quiet 21 | 22 | # Parse options. 23 | while [ "$#" -gt 0 ]; do 24 | case "$1" in 25 | -h|--help) o_help=true; shift ;; 26 | -q|--quiet) o_quiet=true; shift ;; 27 | --) shift; break ;; 28 | -*) o_bad="$1"; shift ;; 29 | *) break ;; 30 | esac 31 | done 32 | 33 | # Check usage. 34 | if [ -n "$o_help" ]; then 35 | printf 'string %s [-h | --help] [-q | --quiet] [STRING ...]\n' "$TRANSFORM" 36 | return 0 37 | elif [ -n "$o_bad" ]; then 38 | printf >&2 'string %s: %s: unknown option\n' "$TRANSFORM" "$o_bad" 39 | return 2 40 | fi 41 | 42 | # Collect piped input 43 | if ! [ -t 0 ]; then 44 | while IFS= read -r line || [ -n "$line" ]; do 45 | set -- "$@" "$line" 46 | done 47 | fi 48 | 49 | # Transform strings and return success only if any were transformed. 50 | exitcode=1 51 | for str in "$@"; do 52 | if [ "$TRANSFORM" = "upper" ]; then 53 | newstr="$(printf '%s\n' "$str" | tr "[:lower:]" "[:upper:]")" 54 | else 55 | newstr="$(printf '%s\n' "$str" | tr "[:upper:]" "[:lower:]")" 56 | fi 57 | [ "$str" != "$newstr" ] && exitcode=0 58 | [ -z "$o_quiet" ] && printf '%s\n' "$newstr" 59 | done 60 | return "$exitcode" 61 | } 62 | string_transform "$@" || exit $? 63 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/health.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | -- 3 | -- This file is not required for your own configuration, 4 | -- but helps people determine if their system is setup correctly. 5 | -- 6 | --]] 7 | 8 | local check_version = function() 9 | local verstr = tostring(vim.version()) 10 | if not vim.version.ge then 11 | vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr)) 12 | return 13 | end 14 | 15 | if vim.version.ge(vim.version(), '0.10-dev') then 16 | vim.health.ok(string.format("Neovim version is: '%s'", verstr)) 17 | else 18 | vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr)) 19 | end 20 | end 21 | 22 | local check_external_reqs = function() 23 | -- Basic utils: `git`, `make`, `unzip` 24 | for _, exe in ipairs { 'git', 'make', 'unzip', 'rg' } do 25 | local is_executable = vim.fn.executable(exe) == 1 26 | if is_executable then 27 | vim.health.ok(string.format("Found executable: '%s'", exe)) 28 | else 29 | vim.health.warn(string.format("Could not find executable: '%s'", exe)) 30 | end 31 | end 32 | 33 | return true 34 | end 35 | 36 | return { 37 | check = function() 38 | vim.health.start 'kickstart.nvim' 39 | 40 | vim.health.info [[NOTE: Not every warning is a 'must-fix' in `:checkhealth` 41 | 42 | Fix only warnings for plugins and languages you intend to use. 43 | Mason will give warnings for languages that are not installed. 44 | You do not need to install, unless you want to use those languages!]] 45 | 46 | local uv = vim.uv or vim.loop 47 | vim.health.info('System Information: ' .. vim.inspect(uv.os_uname())) 48 | 49 | check_version() 50 | check_external_reqs() 51 | end, 52 | } 53 | -------------------------------------------------------------------------------- /bin/str.d/match.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local rex = require("rex_pcre") 4 | local posix = require("posix") 5 | 6 | local function usage(exit_code) 7 | io.stderr:write("Usage: " .. arg[0] .. " [-a] [-e] [-i] [-h] PATTERN [STR...]\n") 8 | os.exit(exit_code or 0) 9 | end 10 | 11 | -- Parse options using a for loop 12 | local all, entire, ignorecase = false, false, false 13 | local pattern_idx = nil 14 | for i = 1, #arg do 15 | local a = arg[i] 16 | if a == "-a" then all = true 17 | elseif a == "-e" then entire = true 18 | elseif a == "-i" then ignorecase = true 19 | elseif a == "-h" then usage(0) 20 | elseif a == "-r" then -- no-op, always regex 21 | elseif a:sub(1,1) == "-" then usage(2) 22 | else 23 | pattern_idx = i 24 | break 25 | end 26 | end 27 | 28 | if not pattern_idx then usage(2) end 29 | local pattern = arg[pattern_idx] 30 | local flags = ignorecase and "i" or "" 31 | 32 | -- Gather input strings 33 | local inputs = {} 34 | if pattern_idx < #arg then 35 | for j = pattern_idx + 1, #arg do table.insert(inputs, arg[j]) end 36 | else 37 | if posix.isatty(io.stdin) then usage(2) end 38 | for line in io.lines() do table.insert(inputs, line) end 39 | end 40 | 41 | for _, str in ipairs(inputs) do 42 | if entire then 43 | local count = 0 44 | for _ in rex.gmatch(str, pattern, flags) do count = count + 1 end 45 | if count > 0 then 46 | if all then 47 | for _ = 1, count do print(str) end 48 | else 49 | print(str) 50 | end 51 | end 52 | else 53 | local matches = {} 54 | for m in rex.gmatch(str, pattern, flags) do table.insert(matches, m) end 55 | if #matches > 0 then 56 | if all then 57 | for _, m in ipairs(matches) do print(m) end 58 | else 59 | print(matches[1]) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /bin/color-spaces.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Author: Todd Larason 3 | # $XFree86: xc/programs/xterm/vttests/256colors2.pl,v 1.1 1999/07/11 08:49:54 dawes Exp $ 4 | 5 | print "256 color mode\n\n"; 6 | 7 | # display back ground colors 8 | 9 | for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) { 10 | 11 | # first the system ones: 12 | print "System colors:\n"; 13 | for ($color = 0; $color < 8; $color++) { 14 | print "\x1b[${fgbg};5;${color}m::"; 15 | } 16 | print "\x1b[0m\n"; 17 | for ($color = 8; $color < 16; $color++) { 18 | print "\x1b[${fgbg};5;${color}m::"; 19 | } 20 | print "\x1b[0m\n\n"; 21 | 22 | # now the color cube 23 | print "Color cube, 6x6x6:\n"; 24 | for ($green = 0; $green < 6; $green++) { 25 | for ($red = 0; $red < 6; $red++) { 26 | for ($blue = 0; $blue < 6; $blue++) { 27 | $color = 16 + ($red * 36) + ($green * 6) + $blue; 28 | print "\x1b[${fgbg};5;${color}m::"; 29 | } 30 | print "\x1b[0m "; 31 | } 32 | print "\n"; 33 | } 34 | 35 | # now the grayscale ramp 36 | print "Grayscale ramp:\n"; 37 | for ($color = 232; $color < 256; $color++) { 38 | print "\x1b[${fgbg};5;${color}m::"; 39 | } 40 | print "\x1b[0m\n\n"; 41 | 42 | } 43 | 44 | print "Examples for the 3-byte color mode\n\n"; 45 | 46 | for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) { 47 | 48 | # now the color cube 49 | print "Color cube\n"; 50 | for ($green = 0; $green < 256; $green+=51) { 51 | for ($red = 0; $red < 256; $red+=51) { 52 | for ($blue = 0; $blue < 256; $blue+=51) { 53 | print "\x1b[${fgbg};2;${red};${green};${blue}m::"; 54 | } 55 | print "\x1b[0m "; 56 | } 57 | print "\n"; 58 | } 59 | 60 | # now the grayscale ramp 61 | print "Grayscale ramp:\n"; 62 | for ($gray = 8; $gray < 256; $gray+=10) { 63 | print "\x1b[${fgbg};2;${gray};${gray};${gray}m::"; 64 | } 65 | print "\x1b[0m\n\n"; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /bin/fishy/contains.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Getopt::Long qw(:config no_auto_abbrev bundling require_order); 5 | 6 | my $show_index = 0; 7 | my $help = 0; 8 | 9 | GetOptions( 10 | 'i|index' => \$show_index, 11 | 'h|help' => \$help, 12 | ) or do { 13 | print STDERR "Try --help for usage.\n"; 14 | exit 2; 15 | }; 16 | 17 | if ($help) { 18 | print <<'USAGE'; 19 | contains.pl - test if a word is present in a list (Fish-compatible) 20 | 21 | Usage: 22 | contains.pl [OPTIONS] KEY [VALUES...] 23 | contains.pl [OPTIONS] -- KEY [VALUES...] 24 | 25 | Options: 26 | -i, --index Print the 1-based index of the first matching element. 27 | -h, --help Show this help text. 28 | 29 | Behavior: 30 | - Exits with status 0 if KEY is present in VALUES, otherwise 1. 31 | - With --index and a match, prints the index and exits 0. 32 | - Arguments beginning with '-' are treated as options until a lone '--'. 33 | - Exact string match (no globbing/regex). 34 | 35 | Examples: 36 | contains.pl cat dog cat mouse # exit 0 37 | contains.pl -i cat dog cat mouse # prints "2", exit 0 38 | contains.pl -- -n -- -n -x # search for literal "-n" among "-n" "-x" 39 | USAGE 40 | exit 0; 41 | } 42 | 43 | # Need at least KEY 44 | @ARGV or do { 45 | print STDERR "contains.pl: missing KEY\n"; 46 | print STDERR "Try --help for usage.\n"; 47 | exit 2; 48 | }; 49 | 50 | my $key = shift @ARGV; 51 | # No VALUES means "not contained" 52 | if (!@ARGV) { 53 | exit 1; 54 | } 55 | 56 | my $idx = 0; 57 | for my $i (0 .. $#ARGV) { 58 | if ($ARGV[$i] eq $key) { 59 | $idx = $i + 1; # 1-based index per Fish 60 | last; 61 | } 62 | } 63 | 64 | if ($idx) { 65 | print "$idx\n" if $show_index; 66 | exit 0; 67 | } else { 68 | # No output on miss, just nonzero status (like Fish) 69 | exit 1; 70 | } 71 | -------------------------------------------------------------------------------- /bin/fishy/string-join: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | # string join [-q | --quiet] [-n | --no-empty] SEP [STRING ...] 5 | 6 | string_join() { 7 | local sep str first o_help o_quiet o_noempty o_bad o_nullsep 8 | 9 | # Parse options. 10 | while [ "$#" -gt 0 ]; do 11 | case "$1" in 12 | -h|--help) o_help=true; shift ;; 13 | -q|--quiet) o_quiet=true; shift ;; 14 | -n|--no-empty) o_noempty=true; shift ;; 15 | -0) o_nullsep=true; shift ;; 16 | --) shift; break ;; 17 | -*) o_bad="$1"; shift ;; 18 | *) break ;; 19 | esac 20 | done 21 | 22 | # Check usage. 23 | if [ -n "$o_help" ]; then 24 | printf '%s\n' "string join [-q | --quiet] [-n | --no-empty] SEP [STRING ...]" 25 | return 0 26 | elif [ -n "$o_bad" ]; then 27 | printf >&2 'string join: %s: unknown option\n' "$o_bad" 28 | return 2 29 | fi 30 | 31 | # Handle null separator differently. 32 | if [ -n "$o_nullsep" ]; then 33 | [ "$#" -eq 0 ] && return 1 34 | [ -z "$o_quiet" ] && printf '%s\0' "$@" 35 | return 0 36 | fi 37 | 38 | # Get the separator. 39 | if [ "$#" -gt 0 ]; then 40 | sep="$1" 41 | shift 42 | else 43 | printf >&2 'string join: %s\n' "missing argument" 44 | return 2 45 | fi 46 | 47 | # Return 1 if nothing was joined, or 0 if we don't want output. 48 | if [ "$#" -eq 0 ]; then 49 | return 1 50 | elif [ -n "$o_quiet" ]; then 51 | return 0 52 | fi 53 | 54 | # We have things to join and want to see output. 55 | first=1 56 | for str in "$@"; do 57 | [ "$o_noempty" = true ] && [ -z "$str" ] && continue 58 | if [ $first -eq 1 ]; then 59 | printf '%s' "$str" 60 | first=0 61 | else 62 | printf '%s%s' "$sep" "$str" 63 | fi 64 | done 65 | printf '\n' 66 | } 67 | string_join "$@" || exit $? 68 | -------------------------------------------------------------------------------- /.config/bash/conf.d/aliases.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # ls shorthand. 4 | alias ls='ls --color=auto' 5 | alias la='ls -lah' 6 | alias ll='ls -lh' 7 | alias l='ls -F' 8 | alias ldot='ls -ld .*' 9 | 10 | # Single character shortcuts - be sparing! 11 | alias _='sudo' 12 | alias h='history' 13 | alias v='vim' 14 | alias c='clear' 15 | 16 | # Mask built-ins with better defaults. 17 | alias ping='ping -c 5' 18 | alias vi=vim 19 | alias grep='grep --color=auto --exclude-dir={.git,.hg,.svn,.vscode}' 20 | 21 | # Fix typos. 22 | alias get=git 23 | alias quit='exit' 24 | alias cd..='cd ..' 25 | alias zz='exit' 26 | 27 | # Navigate directories faster. 28 | alias "dirh"="dirs -v" 29 | alias ".."="cd .." 30 | alias "..."="cd ../.." 31 | alias "...."="cd ../../.." 32 | alias "....."="cd ../../../.." 33 | 34 | # url encode/decode 35 | alias urldecode='python3 -c "import sys, urllib.parse as ul; \ 36 | print(ul.unquote_plus(sys.argv[1]))"' 37 | alias urlencode='python3 -c "import sys, urllib.parse as ul; \ 38 | print (ul.quote_plus(sys.argv[1]))"' 39 | 40 | # find 41 | #alias fd='find . -type d -name ' 42 | #alias ff='find . -type f -name ' 43 | 44 | # date/time 45 | alias timestamp="date '+%Y-%m-%d %H:%M:%S'" 46 | alias datestamp="date '+%Y-%m-%d'" 47 | alias isodate="date +%Y-%m-%dT%H:%M:%S%z" 48 | alias utc="date -u +%Y-%m-%dT%H:%M:%SZ" 49 | alias unixepoch="date +%s" 50 | 51 | # Safer way to rm. 52 | if type safe-rm >/dev/null 2>&1; then 53 | alias rm='safe-rm' 54 | alias del='safe-rm' 55 | fi 56 | 57 | # Misc aliases. 58 | alias myip="curl ifconfig.me" 59 | alias nv=nvim 60 | alias curry="xargs -I{}" # printf '%s\n' foo bar baz | curry touch {}.txt 61 | alias ppath='echo $PATH | tr ":" "\n"' 62 | alias cls="clear && printf '\e[3J'" 63 | alias bench="for i in {1..10}; do /usr/bin/time bash -ic 'echo -n'; done" 64 | alias secrets="cd \"\${XDG_DATA_HOME:-\$HOME/.local/share}\"/secrets" 65 | 66 | if [[ -n "$BASH_VERSION" ]]; then 67 | alias whence='type -a' 68 | fi 69 | -------------------------------------------------------------------------------- /.config/starship/powershell.toml: -------------------------------------------------------------------------------- 1 | # 2 | # mmc - mattmc3's starship config 3 | # 4 | 5 | add_newline = true 6 | 7 | # A minimal left prompt 8 | format = """$python$directory$status$cmd_duration$git_branch${custom.git_status_dirty}$git_status 9 | $character""" 10 | 11 | # Timeout for commands executed by starship (in milliseconds) 12 | command_timeout=2000 13 | 14 | # Set the color palette 15 | palette = "tokyonight" 16 | 17 | # Define custom colors 18 | [palettes.tokyonight] 19 | black = '#15161e' 20 | blue = '#7aa2f7' 21 | cyan = '#7dcfff' 22 | green = '#9ece6a' 23 | magenta = '#bb9af7' 24 | red = '#f7768e' 25 | white = '#a9b1d6' 26 | yellow = '#e0af68' 27 | pink = '#ff6ac1' 28 | 29 | [character] 30 | success_symbol = "[_](pink)" 31 | error_symbol = "[_](red)" 32 | vicmd_symbol = "[❰_](cyan)" 33 | 34 | [python] 35 | format = '[(\($virtualenv\) )]($style)' 36 | style = 'cyan' 37 | 38 | [directory] 39 | style = "blue" 40 | truncation_length = 1 41 | truncation_symbol = "" 42 | fish_style_pwd_dir_length = 1 43 | 44 | # Right-side prompt items use left space padding 45 | [git_branch] 46 | format = ' [$branch]($style)' 47 | style = 'white' 48 | 49 | [git_status] 50 | format = "[( $ahead_behind$stashed)]($style)" 51 | style = "cyan" 52 | stashed = "≡" 53 | ahead = "⇡${count}" 54 | behind = "⇣${count}" 55 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 56 | 57 | [custom.git_status_dirty] 58 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 59 | symbol = "*" # • * ﹡ * ✱ 60 | style = "purple" 61 | format="[$symbol]($style)" 62 | shell = ["sh"] 63 | 64 | [shell] 65 | fish_indicator = '🐟' # 󰈺 🐟 🐠 66 | powershell_indicator = '_' 67 | pwsh_indicator = '_' 68 | zsh_indicator = '󠀥%' 69 | style = 'cyan bold' 70 | disabled = false 71 | 72 | [cmd_duration] 73 | format = '[$duration]($style)' 74 | 75 | [line_break] 76 | disabled = true 77 | 78 | [status] 79 | disabled = false 80 | pipestatus = true 81 | format = '[$symbol$int]($style)' 82 | symbol = '✘' 83 | pipestatus_format = '\[$pipestatus\]($style)' 84 | -------------------------------------------------------------------------------- /bin/git/git-submodule-up: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ##? git update-submod - if there are submodules, update them, commit, and push 3 | 4 | set -euo pipefail 5 | 6 | main() { 7 | local root_git_dir submod line num_changed behind 8 | local -a submods 9 | 10 | root_git_dir="$(git rev-parse --show-toplevel || exit 1)" 11 | cd "$root_git_dir" 12 | 13 | # Make sure the repo has submodules. 14 | if [ ! -r ".gitmodules" ]; then 15 | echo >&2 "No .gitmodules found" 16 | exit 1 17 | fi 18 | 19 | # Make sure the repo has no staged changes 20 | if ! git diff --exit-code --name-only --cached >/dev/null 2>&1; then 21 | echo >&2 "update-submod: Cannot run command with changes already staged" 22 | exit 1 23 | fi 24 | 25 | # Make sure the repo is up to date. 26 | behind="$(git rev-list --count 'HEAD..@{upstream}' 2>/dev/null)" 27 | if [[ -n "$behind" ]] && (( behind > 0 )); then 28 | echo >&2 "Your repo is $behind commits behind. Run 'git pull' before using this command." 29 | exit 1 30 | fi 31 | 32 | # What are the submodules? 33 | while IFS= read -r line; do 34 | submods+=("$line") 35 | done < <(git config --file .gitmodules --get-regexp path | awk '{ print $2 }') 36 | 37 | # Update the submodules 38 | git submodule update --remote --recursive --merge 39 | 40 | # See which submodules changed 41 | num_changed=0 42 | for submod in "${submods[@]}"; do 43 | if ! git diff --exit-code --name-only "$submod" >/dev/null 2>&1; then 44 | echo "Running: git add $submod" 45 | (( num_changed += 1 )) 46 | git add "$submod" 47 | else 48 | echo "Submodule was unchanged: $submod" 49 | fi 50 | done 51 | 52 | # Commit 53 | if (( num_changed == 0 )); then 54 | return 1 55 | elif (( num_changed == 1 )); then 56 | git commit -m "Update submodule" 57 | elif (( num_changed > 1 )); then 58 | git commit -m "Update submodules" 59 | fi 60 | 61 | # Push 62 | if (( num_changed > 0 )); then 63 | echo "Submodules updated. 'git push' when ready." 64 | fi 65 | } 66 | main "$@" 67 | -------------------------------------------------------------------------------- /.config/bash/plugins/__init__.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | # Use XDG base dirs. 4 | export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" 5 | export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}" 6 | export XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" 7 | export XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" 8 | mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$XDG_DATA_HOME" "$XDG_STATE_HOME" 9 | 10 | # Support alternative BASH_HOME locations. 11 | BASH_HOME="$XDG_CONFIG_HOME"/bash 12 | BASH_DATA_HOME="$XDG_DATA_HOME"/bash 13 | BASH_CACHE_HOME="$XDG_CACHE_HOME"/bash 14 | REPO_HOME="$BASH_CACHE_HOME"/repos 15 | mkdir -p "$BASH_HOME" "$BASH_DATA_HOME" "$BASH_CACHE_HOME" "$REPO_HOME" 16 | 17 | # Setup profiling if needed. 18 | if [ "${PROFRC:-0}" -eq 1 ]; then 19 | PS4='+ $(gdate "+%s.%N")\011 ' 20 | exec 3>&2 2>"$BASH_HOME"/.bashrc.$$.log 21 | set -x 22 | fi 23 | 24 | # Declare a post_init array for post init operations. 25 | # shellcheck disable=SC2034 26 | declare -a post_init=() 27 | 28 | # Cache the results of a command used for eval init. 29 | cached_eval() { 30 | local cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/bash/cached_eval" 31 | local cache_file="$cache_dir/$1.sh" 32 | 33 | mkdir -p "$cache_dir" 34 | 35 | # Delete cache file if older than 20 hours (1200 minutes) 36 | find "$cache_dir" -type f -name "$1.sh" -mmin +1200 -delete 2>/dev/null 37 | 38 | if [[ ! -f $cache_file ]]; then 39 | "$@" > "$cache_file" || { 40 | echo "cached_eval: command failed: $*" >&2 41 | return 1 42 | } 43 | fi 44 | 45 | source "$cache_file" 46 | } 47 | 48 | # shellcheck disable=SC2139,SC2001 49 | ##? Extend an existing alias 50 | extend_alias() { 51 | local name="$1" 52 | local extra_args="$2" 53 | local alias_def 54 | alias_def=$(alias "$name" 2>/dev/null) 55 | if [[ -n "$alias_def" ]]; then 56 | # Extract current alias value 57 | local current_cmd 58 | current_cmd=$(echo "$alias_def" | sed "s/alias $name='\(.*\)'/\1/") 59 | alias "$name"="${current_cmd} ${extra_args}" 60 | else 61 | alias "$name"="$name ${extra_args}" 62 | fi 63 | } 64 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/lint.lua: -------------------------------------------------------------------------------- 1 | return { 2 | 3 | { -- Linting 4 | 'mfussenegger/nvim-lint', 5 | event = { 'BufReadPre', 'BufNewFile' }, 6 | config = function() 7 | local lint = require 'lint' 8 | lint.linters_by_ft = { 9 | markdown = { 'markdownlint' }, 10 | } 11 | 12 | -- To allow other plugins to add linters to require('lint').linters_by_ft, 13 | -- instead set linters_by_ft like this: 14 | -- lint.linters_by_ft = lint.linters_by_ft or {} 15 | -- lint.linters_by_ft['markdown'] = { 'markdownlint' } 16 | -- 17 | -- However, note that this will enable a set of default linters, 18 | -- which will cause errors unless these tools are available: 19 | -- { 20 | -- clojure = { "clj-kondo" }, 21 | -- dockerfile = { "hadolint" }, 22 | -- inko = { "inko" }, 23 | -- janet = { "janet" }, 24 | -- json = { "jsonlint" }, 25 | -- markdown = { "vale" }, 26 | -- rst = { "vale" }, 27 | -- ruby = { "ruby" }, 28 | -- terraform = { "tflint" }, 29 | -- text = { "vale" } 30 | -- } 31 | -- 32 | -- You can disable the default linters by setting their filetypes to nil: 33 | -- lint.linters_by_ft['clojure'] = nil 34 | -- lint.linters_by_ft['dockerfile'] = nil 35 | -- lint.linters_by_ft['inko'] = nil 36 | -- lint.linters_by_ft['janet'] = nil 37 | -- lint.linters_by_ft['json'] = nil 38 | -- lint.linters_by_ft['markdown'] = nil 39 | -- lint.linters_by_ft['rst'] = nil 40 | -- lint.linters_by_ft['ruby'] = nil 41 | -- lint.linters_by_ft['terraform'] = nil 42 | -- lint.linters_by_ft['text'] = nil 43 | 44 | -- Create autocommand which carries out the actual linting 45 | -- on the specified events. 46 | local lint_augroup = vim.api.nvim_create_augroup('lint', { clear = true }) 47 | vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { 48 | group = lint_augroup, 49 | callback = function() 50 | lint.try_lint() 51 | end, 52 | }) 53 | end, 54 | }, 55 | } 56 | -------------------------------------------------------------------------------- /.config/starship/mmc_256.toml: -------------------------------------------------------------------------------- 1 | # 2 | # mmc - My default prompt theme 3 | # 4 | 5 | add_newline = false 6 | 7 | # A minimal left prompt 8 | format = """$python$directory$character""" 9 | 10 | # Move the rest of the prompt to the right 11 | right_format = """$status$cmd_duration$git_branch${custom.git_status_dirty}$git_status$shell""" 12 | 13 | # Timeout for commands executed by starship (in milliseconds) 14 | command_timeout=2000 15 | 16 | # Set the palette. 17 | palette = "wombat_256" 18 | 19 | # Define custom colors 20 | [palettes.tokyo_night_256] 21 | black = '16' 22 | blue = '111' 23 | cyan = '117' 24 | green = '149' 25 | purple = '141' 26 | red = '210' 27 | white = '146' 28 | yellow = '179' 29 | 30 | [palettes.wombat_256] 31 | black = '0' 32 | blue = '75' 33 | cyan = '123' 34 | green = '149' 35 | purple = '171' 36 | red = '203' 37 | white = '188' 38 | yellow = '223' 39 | 40 | [character] 41 | success_symbol = "[❯](purple)[❯](cyan)" 42 | error_symbol = "[❯](yellow)[❯](red)" 43 | vicmd_symbol = "[❮](purple)[❮](cyan)" 44 | 45 | [python] 46 | format = '[(\($virtualenv\) )]($style)' 47 | style = 'white' 48 | 49 | [directory] 50 | style = "blue" 51 | truncation_length = 1 52 | truncation_symbol = "" 53 | fish_style_pwd_dir_length = 1 54 | 55 | # Right prompt uses left space padding. 56 | [git_branch] 57 | format = ' [$branch]($style)' 58 | style = 'green' 59 | 60 | [git_status] 61 | format = '( [\[$ahead_behind$stashed\]]($style))' 62 | style = "cyan" 63 | stashed = "≡" 64 | ahead = "⇡${count}" 65 | behind = "⇣${count}" 66 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 67 | 68 | [custom.git_status_dirty] 69 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 70 | symbol = "•" 71 | style = "purple" 72 | format="[$symbol]($style)" 73 | shell = ["sh"] 74 | 75 | [shell] 76 | format = ' [$indicator]($style)' 77 | fish_indicator = '🐟' # 󰈺 🐟 🐠 78 | powershell_indicator = '_' 79 | pwsh_indicator = '>_' 80 | zsh_indicator = '󠀥%' 81 | bash_indicator = '󠀥$' 82 | style = 'cyan bold' 83 | disabled = false 84 | 85 | [cmd_duration] 86 | format = ' [$duration]($style)' 87 | 88 | [line_break] 89 | disabled = true 90 | 91 | [status] 92 | disabled = false 93 | format = '[$symbol$int]($style)' 94 | symbol = '✘' 95 | # pipestatus = true 96 | # pipestatus_format = '\[$pipestatus\]($style)' 97 | -------------------------------------------------------------------------------- /docs/macos.md: -------------------------------------------------------------------------------- 1 | # macOS 2 | 3 | ## DefaultKeyBinding.dict 4 | 5 | ``` 6 | /* ~/Library/KeyBindings/DefaultKeyBinding.Dict 7 | 8 | https://gist.githubusercontent.com/trusktr/1e5e516df4e8032cbc3d/raw/ab7c868a8354219782b37971f984564c66a53d78/DefaultKeyBinding.dict 9 | 10 | This file remaps the key bindings of a single user on Mac OS X 10.5 to more 11 | closely match default behavior on Windows systems. This makes the Command key 12 | behave like Windows Control key. To use Control instead of Command, either swap 13 | Control and Command in Apple->System Preferences->Keyboard->Modifier Keys... 14 | or replace @ with ^ in this file. 15 | 16 | Here is a rough cheatsheet for syntax. 17 | Key Modifiers 18 | ^ : Ctrl 19 | $ : Shift 20 | ~ : Option (Alt) 21 | @ : Command (Apple) 22 | # : Numeric Keypad 23 | 24 | Non-Printable Key Codes 25 | 26 | Standard 27 | Up Arrow: \UF700 Backspace: \U0008 F1: \UF704 28 | Down Arrow: \UF701 Tab: \U0009 F2: \UF705 29 | Left Arrow: \UF702 Escape: \U001B F3: \UF706 30 | Right Arrow: \UF703 Enter: \U000A ... 31 | Insert: \UF727 Page Up: \UF72C 32 | Delete: \UF728 Page Down: \UF72D 33 | Home: \UF729 Print Screen: \UF72E 34 | End: \UF72B Scroll Lock: \UF72F 35 | Break: \UF732 Pause: \UF730 36 | SysReq: \UF731 Menu: \UF735 37 | Help: \UF746 38 | 39 | OS X 40 | delete: \U007F 41 | 42 | For a good reference see http://osxnotes.net/keybindings.html. 43 | 44 | NOTE: typically the Windows 'Insert' key is mapped to what Macs call 'Help'. 45 | Regular Mac keyboards don't even have the Insert key, but provide 'Fn' instead, 46 | which is completely different. 47 | */ 48 | 49 | { 50 | "@\UF72B" = "moveToEndOfDocument:"; /* Cmd + End */ 51 | "@\UF729" = "moveToBeginningOfDocument:"; /* Cmd + Home */ 52 | 53 | "\UF729" = "moveToBeginningOfLine:"; /* Home */ 54 | "\UF72B" = "moveToEndOfLine:"; /* End */ 55 | 56 | "$\UF729" = "moveToBeginningOfLineAndModifySelection:"; /* Shift + Home */ 57 | "$\UF72B" = "moveToEndOfLineAndModifySelection:"; /* Shift + End */ 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /.config/nvim/lazy-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "LuaSnip": { "branch": "master", "commit": "458560534a73f7f8d7a11a146c801db00b081df0" }, 3 | "blink.cmp": { "branch": "main", "commit": "bae4bae0eedd1fa55f34b685862e94a222d5c6f8" }, 4 | "conform.nvim": { "branch": "master", "commit": "a0ab60ed666c56b37fd7ed1847d2ac52f2482ce0" }, 5 | "fidget.nvim": { "branch": "main", "commit": "4d5858bd4c471c895060e1b9f3575f1551184dc5" }, 6 | "gitsigns.nvim": { "branch": "main", "commit": "6e3c66548035e50db7bd8e360a29aec6620c3641" }, 7 | "guess-indent.nvim": { "branch": "main", "commit": "84a4987ff36798c2fc1169cbaff67960aed9776f" }, 8 | "lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" }, 9 | "lazydev.nvim": { "branch": "main", "commit": "2367a6c0a01eb9edb0464731cc0fb61ed9ab9d2c" }, 10 | "mason-lspconfig.nvim": { "branch": "main", "commit": "1ec4da522fa49dcecee8d190efda273464dd2192" }, 11 | "mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" }, 12 | "mason.nvim": { "branch": "main", "commit": "7dc4facca9702f95353d5a1f87daf23d78e31c2a" }, 13 | "mini.icons": { "branch": "main", "commit": "b8f6fa6f5a3fd0c56936252edcd691184e5aac0c" }, 14 | "mini.nvim": { "branch": "main", "commit": "dee23b41ba27bd583f1d19e199738f3bd0236648" }, 15 | "nvim-lspconfig": { "branch": "master", "commit": "fa12ecf9223acf573c6a45507bd0f885070f8857" }, 16 | "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, 17 | "nvim-web-devicons": { "branch": "master", "commit": "81b37d7937953b50e5fd8d9d7dfe2c6d0088fde1" }, 18 | "oil.nvim": { "branch": "master", "commit": "07f80ad645895af849a597d1cac897059d89b686" }, 19 | "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, 20 | "telescope-fzf-native.nvim": { "branch": "main", "commit": "1f08ed60cafc8f6168b72b80be2b2ea149813e55" }, 21 | "telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" }, 22 | "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" }, 23 | "todo-comments.nvim": { "branch": "main", "commit": "304a8d204ee787d2544d8bc23cd38d2f929e7cc5" }, 24 | "tokyonight.nvim": { "branch": "main", "commit": "057ef5d260c1931f1dffd0f052c685dcd14100a3" }, 25 | "which-key.nvim": { "branch": "main", "commit": "370ec46f710e058c9c1646273e6b225acf47cbed" } 26 | } 27 | -------------------------------------------------------------------------------- /bin/fishy/string-repeat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 5 | 6 | usage() { 7 | printf '%s\n' "string repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline]" 8 | printf '%s\n' " [-q | --quiet] [STRING ...]" 9 | printf '%s\n' "string repeat [-N | --no-newline] [-q | --quiet] COUNT [STRING ...]" 10 | } 11 | 12 | string_repeat() { 13 | local o_help o_quiet o_count o_max o_nonewline 14 | local count line max str out i 15 | 16 | # Parse options. 17 | while [ "$#" -gt 0 ]; do 18 | case "$1" in 19 | -h|--help) o_help=1; shift ;; 20 | -q|--quiet) o_quiet=1; shift ;; 21 | -n|--count) o_count="$2"; shift 2 ;; 22 | -m|--max) o_max="$2"; shift 2 ;; 23 | -N|--no-newline) o_nonewline=1; shift 2 ;; 24 | --) shift; break ;; 25 | -*) o_bad="$1"; shift ;; 26 | *) break ;; 27 | esac 28 | done 29 | 30 | # Check usage. 31 | if [ -n "$o_help" ]; then 32 | usage 33 | return 0 34 | elif [ -n "$o_bad" ]; then 35 | printf >&2 'string repeat: %s: unknown option\n' "$o_bad" 36 | return 2 37 | fi 38 | 39 | # Determine count and string(s) 40 | if [ -n "$o_count" ]; then 41 | count="$o_count" 42 | elif [ $# -gt 0 ]; then 43 | count="$1" 44 | shift 45 | fi 46 | 47 | # Ensure count is a variable 48 | if ! [ "$count" -ge 0 ] 2>/dev/null; then 49 | printf >&2 "string repeat: Invalid count value '%s'\n" "$count" 50 | return 2 51 | fi 52 | 53 | # Collect piped input 54 | if ! [ -t 0 ]; then 55 | while IFS= read -r line || [ -n "$line" ]; do 56 | set -- "$@" "$line" 57 | done 58 | fi 59 | 60 | # # Default string is " " if none given 61 | # if [ $# -eq 0 ]; then 62 | # str=" " 63 | # else 64 | # str="$*" 65 | # fi 66 | 67 | # # Apply max if set 68 | # if [ -n "$o_max" ] && [ "$count" -gt "$o_max" ] 2>/dev/null; then 69 | # count=$o_max 70 | # fi 71 | 72 | # # Output 73 | # [ -n "$o_quiet" ] && return 0 74 | 75 | # out="" 76 | # i=1 77 | # while [ "$i" -le "$count" ]; do 78 | # out="$out$str" 79 | # i=$((i + 1)) 80 | # done 81 | 82 | # if [ -n "$o_nonewline" ]; then 83 | # printf '%s' "$out" 84 | # else 85 | # printf '%s\n' "$out" 86 | # fi 87 | } 88 | string_repeat "$@" || exit $? 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # use custom ignores with this command: 2 | # GIT_WORK_TREE=$HOME GIT_DIR=$HOME/.dotfiles.git git config --local core.excludesfile .gitignore.dotfiles 3 | 4 | # Ignore everything by default. 5 | 6 | * 7 | 8 | # Add back items as needed. 9 | 10 | # config 11 | !.config/ 12 | !.config/* 13 | 14 | # atuin 15 | !.config/atuin/ 16 | !.config/atuin/** 17 | 18 | # bin 19 | !bin/ 20 | !bin/** 21 | **/bin/gam7/ 22 | **/bin/gamadv-xtd3/ 23 | 24 | # bash 25 | !.bash_profile 26 | !.bashrc 27 | !.config/bash/ 28 | !.config/bash/** 29 | .config/bash/*.log 30 | 31 | # blesh 32 | !.config/blesh/ 33 | !.config/blesh/** 34 | 35 | # docs 36 | !docs/ 37 | !docs/** 38 | 39 | # editorconfig 40 | !~/.editorconfig 41 | 42 | # fish 43 | !.config/fish/ 44 | !.config/fish/* 45 | !.config/fish/** 46 | .config/fish/fish_variables 47 | 48 | # git 49 | !.gitignore 50 | !.config/git/ 51 | !.config/git/** 52 | 53 | # hammmerspoon 54 | !.hammerspoon/ 55 | !.hammerspoon/Spoons/** 56 | !.hammerspoon/init.lua 57 | !.hammerspoon/readme.md 58 | 59 | # helix 60 | !.config/helix/ 61 | !.config/helix/** 62 | 63 | # homebrew 64 | !.config/homebrew/ 65 | !.config/homebrew/** 66 | 67 | # iterm2 68 | !.config/iterm2/ 69 | !.config/iterm2/*.json 70 | 71 | # kak 72 | !.config/kak/ 73 | !.config/kak/** 74 | 75 | # npm 76 | !.config/npm/ 77 | !.config/npm/** 78 | 79 | # neovim 80 | !.config/nvim/ 81 | !.config/nvim/** 82 | 83 | # readline 84 | !.config/readline/ 85 | !.config/readline/** 86 | 87 | # shell 88 | !.config/shell/ 89 | !.config/shell/** 90 | 91 | # starship 92 | !.config/starship/ 93 | !.config/starship/** 94 | 95 | # stow 96 | !.stow-* 97 | 98 | # tmux 99 | !.config/tmux/ 100 | !.config/tmux/** 101 | 102 | # vim 103 | !~/.vimrc 104 | !.config/vim/ 105 | !.config/vim/*.vim 106 | 107 | # wezterm 108 | !.config/wezterm/ 109 | !.config/wezterm/** 110 | 111 | # zsh 112 | !.config/zsh/ 113 | !.config/zsh/** 114 | .config/zsh/.* 115 | .config/zsh/.*/ 116 | !.p10k.zsh 117 | !.zaliases 118 | !.zlogin 119 | !.zlogout 120 | !.zprofile 121 | !.zshenv 122 | !.zshrc 123 | !.zshrc.* 124 | !.zstyles 125 | !.zplugins 126 | !.config/zsh/.zshrc.d/ 127 | !.config/zsh/.zshrc.d/** 128 | .config/zsh/.zcompdump* 129 | .config/zsh/custom/ 130 | .config/zsh/ohmyzsh/ 131 | .config/zsh/.oh-my-zsh/ 132 | 133 | # re-ignore local files 134 | *.local 135 | local.* 136 | *.local/ 137 | *.local.* 138 | !.local/ 139 | !.local/** 140 | 141 | # helpers 142 | !makefile 143 | !LICENSE 144 | !README.md 145 | 146 | # final 147 | *_history* 148 | .DS_Store 149 | -------------------------------------------------------------------------------- /docs/git.md: -------------------------------------------------------------------------------- 1 | # git 2 | 3 | Reference for common stuff I never remember in git 4 | 5 | ## Fix a commit 6 | 7 | ### Accidental local commit 8 | 9 | Undo previous local commit: 10 | 11 | ```zsh 12 | git reset --soft HEAD~1 13 | ``` 14 | 15 | ## submodules 16 | 17 | ### add a submodule 18 | 19 | ```zsh 20 | cd $DOTFILES 21 | git submodule add git@github.com:mattmc3/zdotdir.git ./config/zsh 22 | git submodule add git@github.com:mattmc3/fishconf.git ./config/fish 23 | ``` 24 | 25 | ### update submodules 26 | 27 | ```zsh 28 | git submodule update --recursive --remote 29 | ``` 30 | 31 | ### remove submodules 32 | 33 | ```zsh 34 | # https://stackoverflow.com/a/36593218/8314 35 | local submodule="$1" 36 | if [[ ! -n "$submodule" || ! -d "$submodule" ]]; then 37 | >&2 echo "FAIL: No valid submodule specified" 38 | return 1 39 | fi 40 | 41 | # Remove the submodule entry from .git/config 42 | git submodule deinit -f "$submodule" 43 | 44 | # Remove the submodule directory from the superproject's .git/modules directory 45 | rm -rf ".git/modules/${submodule}" 46 | 47 | # Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule 48 | git rm -f "$submodule" 49 | ``` 50 | 51 | ## machine specific config 52 | 53 | config example: 54 | 55 | ``` 56 | # home machine 57 | [includeIf "gitdir:~/.dotfiles"] 58 | path = config_home.local 59 | # work machine 60 | [includeIf "gitdir:~/Projects/work/**"] 61 | path = config_work.local 62 | 63 | [alias] 64 | config-home = ! git config user.name "My Internet Name" && git config user.email "email@home.com" 65 | config-work = ! git config user.name "My Domain Name" && git config user.email "email@work.com" 66 | ``` 67 | 68 | config_home.local example: 69 | 70 | ``` 71 | [user] 72 | name = My Internet Name 73 | email = email@home.com 74 | ``` 75 | 76 | config_work.local example: 77 | 78 | ``` 79 | [user] 80 | name = My Domain Name 81 | email = email@work.com 82 | ``` 83 | 84 | ## branches 85 | 86 | Prune branches 87 | 88 | ```zsh 89 | local curbranch=$(git rev-parse --abbrev-ref HEAD) 90 | if [[ $curbranch != 'master' ]] || [[ $curbranch != 'main' ]]; then 91 | >&2 echo "This command requires you to be on the main branch." 92 | >&2 echo "please run: git checkout main" 93 | return 1 94 | fi 95 | git fetch -p && git branch -vv | awk '/: gone]/{print \$1}' | xargs git branch -d 96 | ``` 97 | 98 | ## Resources 99 | 100 | * [Detached HEAD](https://stackoverflow.com/questions/18770545/why-is-my-git-submodule-head-detached-from-master) 101 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/gitsigns.lua: -------------------------------------------------------------------------------- 1 | -- Adds git related signs to the gutter, as well as utilities for managing changes 2 | -- NOTE: gitsigns is already included in init.lua but contains only the base 3 | -- config. This will add also the recommended keymaps. 4 | 5 | return { 6 | { 7 | 'lewis6991/gitsigns.nvim', 8 | opts = { 9 | on_attach = function(bufnr) 10 | local gitsigns = require 'gitsigns' 11 | 12 | local function map(mode, l, r, opts) 13 | opts = opts or {} 14 | opts.buffer = bufnr 15 | vim.keymap.set(mode, l, r, opts) 16 | end 17 | 18 | -- Navigation 19 | map('n', ']c', function() 20 | if vim.wo.diff then 21 | vim.cmd.normal { ']c', bang = true } 22 | else 23 | gitsigns.nav_hunk 'next' 24 | end 25 | end, { desc = 'Jump to next git [c]hange' }) 26 | 27 | map('n', '[c', function() 28 | if vim.wo.diff then 29 | vim.cmd.normal { '[c', bang = true } 30 | else 31 | gitsigns.nav_hunk 'prev' 32 | end 33 | end, { desc = 'Jump to previous git [c]hange' }) 34 | 35 | -- Actions 36 | -- visual mode 37 | map('v', 'hs', function() 38 | gitsigns.stage_hunk { vim.fn.line '.', vim.fn.line 'v' } 39 | end, { desc = 'stage git hunk' }) 40 | map('v', 'hr', function() 41 | gitsigns.reset_hunk { vim.fn.line '.', vim.fn.line 'v' } 42 | end, { desc = 'reset git hunk' }) 43 | -- normal mode 44 | map('n', 'hs', gitsigns.stage_hunk, { desc = 'git [s]tage hunk' }) 45 | map('n', 'hr', gitsigns.reset_hunk, { desc = 'git [r]eset hunk' }) 46 | map('n', 'hS', gitsigns.stage_buffer, { desc = 'git [S]tage buffer' }) 47 | map('n', 'hu', gitsigns.undo_stage_hunk, { desc = 'git [u]ndo stage hunk' }) 48 | map('n', 'hR', gitsigns.reset_buffer, { desc = 'git [R]eset buffer' }) 49 | map('n', 'hp', gitsigns.preview_hunk, { desc = 'git [p]review hunk' }) 50 | map('n', 'hb', gitsigns.blame_line, { desc = 'git [b]lame line' }) 51 | map('n', 'hd', gitsigns.diffthis, { desc = 'git [d]iff against index' }) 52 | map('n', 'hD', function() 53 | gitsigns.diffthis '@' 54 | end, { desc = 'git [D]iff against last commit' }) 55 | -- Toggles 56 | map('n', 'tb', gitsigns.toggle_current_line_blame, { desc = '[T]oggle git show [b]lame line' }) 57 | map('n', 'tD', gitsigns.toggle_deleted, { desc = '[T]oggle git show [D]eleted' }) 58 | end, 59 | }, 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | 3 | My dotfiles 4 | 5 | ## Intro 6 | 7 | This repo is for storing my public config files, canonically called "dotfiles". Having dotfiles in a repo makes setup on a new machine just a simple `git clone` away. Some of the techniques and code are based on concepts from [this article][dotfiles-getting-started], [this article on bare repos](https://www.atlassian.com/git/tutorials/dotfiles), and the zillions of other [dotfile repos on GitHub][github-dotfiles]. 8 | 9 | ![Terminal][terminal_gif] 10 | 11 | ### Prereqs 12 | 13 | - git 14 | 15 | ## Bare repo 16 | 17 | ```zsh 18 | alias dotty='GIT_WORK_TREE=~ GIT_DIR=~/.dotfiles' 19 | alias dotfiles='git --git-dir=$HOME/.dotfiles --work-tree=$HOME' 20 | git clone --bare git@github.com:mattmc3/dotfiles $HOME/.dotfiles 21 | dotfiles checkout 22 | if [[ $? == 0 ]]; then 23 | echo "Checked out dotfiles."; 24 | else 25 | echo "Backing up pre-existing dot files."; 26 | dotfiles checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .dotfiles.bak/{} 27 | fi 28 | ``` 29 | 30 | ## Edit 31 | 32 | ```zsh 33 | IDE=${VISUAL:-${EDITOR:-vim}} 34 | dotty $IDE ~ 35 | ``` 36 | 37 | ## Git submodules 38 | 39 | Run this to make .local always track the main branch. 40 | 41 | ```sh 42 | cd .local 43 | git checkout main 44 | git pull origin main # Ensure it's up to date 45 | cd .. 46 | git config -f .gitmodules submodule..local.branch main 47 | ``` 48 | 49 | Adding these configs helps with push/pull on the .local submodule. 50 | 51 | ```sh 52 | git config push.recurseSubmodules on-demand 53 | git config submodule.recurse true 54 | ``` 55 | 56 | ## Notes 57 | 58 | Certain legacy apps don't properly use .config, so anything that doesn't has a simple wrapper in `$HOME` that then sources the real files from `~/.config`. 59 | 60 | ## Resources 61 | 62 | - [Managing dotfiles with a bare git repo](https://www.atlassian.com/git/tutorials/dotfiles) 63 | - [Managing dotfiles with GNU Stow](https://venthur.de/2021-12-19-managing-dotfiles-with-stow.html) 64 | - [Using GNU Stow to Manage Symbolic Links for Your Dotfiles](https://systemcrafters.net/managing-your-dotfiles/using-gnu-stow/) 65 | 66 | [dotfiles-getting-started]: https://medium.com/@webprolific/getting-started-with-dotfiles-43c3602fd789#.vh7hhm6th 67 | [github-dotfiles]: https://dotfiles.github.io/ 68 | [homebrew]: https://brew.sh 69 | [rsync]: http://man7.org/linux/man-pages/man1/rsync.1.html 70 | [stow]: https://www.gnu.org/software/stow/ 71 | [terminal]: https://raw.githubusercontent.com/mattmc3/dotfiles/resources/images/zsh_terminal.png 72 | [terminal_gif]: https://raw.githubusercontent.com/mattmc3/dotfiles/resources/img/zdotdir.gif 73 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | 3 | My dotfiles 4 | 5 | ## Intro 6 | 7 | This repo is for storing my public config files, canonically called "dotfiles". Having dotfiles in a repo makes setup on a new machine just a simple `git clone` away. Some of the techniques and code are based on concepts from [this article][dotfiles-getting-started], [this article on bare repos](https://www.atlassian.com/git/tutorials/dotfiles), and the zillions of other [dotfile repos on GitHub][github-dotfiles]. 8 | 9 | ![Terminal][terminal_gif] 10 | 11 | ### Prereqs 12 | 13 | - git 14 | 15 | ## Bare repo 16 | 17 | ```zsh 18 | alias dotf='GIT_WORK_TREE=~ GIT_DIR=~/.dotfiles' 19 | alias dotfiles='git --git-dir=$HOME/.dotfiles --work-tree=$HOME' 20 | git clone --bare git@github.com:mattmc3/dotfiles $HOME/.dotfiles 21 | dotfiles checkout 22 | if [[ $? == 0 ]]; then 23 | echo "Checked out dotfiles."; 24 | else 25 | echo "Backing up pre-existing dot files."; 26 | dotfiles checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .dotfiles.bak/{} 27 | fi 28 | ``` 29 | 30 | Local 31 | 32 | ```zsh 33 | alias dotloc='git --git-dir=$HOME/.dotfiles/.local --work-tree=$HOME' 34 | git clone --bare git@github.com:mattmc3/dotfiles.local $HOME/.dotfiles/.local 35 | dotloc checkout 36 | if [[ $? == 0 ]]; then 37 | echo "Checked out dotfiles."; 38 | else 39 | echo "Backing up pre-existing dot files."; 40 | dotloc checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | xargs -I{} mv {} .dotfiles.local.bak/{} 41 | fi 42 | ``` 43 | 44 | ## Edit 45 | 46 | ```zsh 47 | alias dotty="GIT_WORK_TREE=~ GIT_DIR=~/.dotfiles" 48 | IDE=${VISUAL:-${EDITOR:-vim}} 49 | dotty $IDE ~ 50 | ``` 51 | 52 | ## Notes 53 | 54 | Certain legacy apps don't properly use .config, so anything that doesn't has a simple wrapper in `$HOME` that then sources the real files from `~/.config`. 55 | 56 | ## Resources 57 | 58 | - [Managing dotfiles with a bare git repo](https://www.atlassian.com/git/tutorials/dotfiles) 59 | - [Managing dotfiles with GNU Stow](https://venthur.de/2021-12-19-managing-dotfiles-with-stow.html) 60 | - [Using GNU Stow to Manage Symbolic Links for Your Dotfiles](https://systemcrafters.net/managing-your-dotfiles/using-gnu-stow/) 61 | 62 | [dotfiles-getting-started]: https://medium.com/@webprolific/getting-started-with-dotfiles-43c3602fd789#.vh7hhm6th 63 | [github-dotfiles]: https://dotfiles.github.io/ 64 | [homebrew]: https://brew.sh 65 | [rsync]: http://man7.org/linux/man-pages/man1/rsync.1.html 66 | [stow]: https://www.gnu.org/software/stow/ 67 | [terminal]: https://raw.githubusercontent.com/mattmc3/dotfiles/resources/images/zsh_terminal.png 68 | [terminal_gif]: https://raw.githubusercontent.com/mattmc3/dotfiles/resources/img/zdotdir.gif 69 | -------------------------------------------------------------------------------- /.config/starship/mmc.toml: -------------------------------------------------------------------------------- 1 | # 2 | # mmc - My default prompt theme 3 | # 4 | 5 | add_newline = false 6 | 7 | # A minimal left prompt 8 | format = """$python$directory$character""" 9 | 10 | # Move the rest of the prompt to the right 11 | right_format = """$status$cmd_duration$git_branch${custom.git_status_dirty}$git_status$shell""" 12 | 13 | # Timeout for commands executed by starship (in milliseconds) 14 | command_timeout=2000 15 | 16 | # Set the palette. 17 | palette = "wombat_256" 18 | 19 | # Define custom colors 20 | [palettes.tokyo_night] 21 | black = '#15161e' 22 | blue = '#7aa2f7' 23 | cyan = '#7dcfff' 24 | green = '#9ece6a' 25 | purple = '#bb9af7' 26 | red = '#f7768e' 27 | white = '#a9b1d6' 28 | yellow = '#e0af68' 29 | 30 | [palettes.tokyo_night_256] 31 | black = '16' 32 | blue = '111' 33 | cyan = '117' 34 | green = '149' 35 | purple = '141' 36 | red = '210' 37 | white = '146' 38 | yellow = '179' 39 | 40 | [palettes.wombat] 41 | black = '#000000' 42 | blue = '#5da9f6' 43 | cyan = '#82fff7' 44 | green = '#b1e969' 45 | purple = '#e86aff' 46 | red = '#ff615a' 47 | white = '#dedacf' 48 | yellow = '#ebd99c' 49 | 50 | [palettes.wombat_256] 51 | black = '0' 52 | blue = '75' 53 | cyan = '123' 54 | green = '149' 55 | purple = '171' 56 | red = '203' 57 | white = '188' 58 | yellow = '223' 59 | 60 | [character] 61 | success_symbol = "[❯](purple)[❯](cyan)" 62 | error_symbol = "[❯](yellow)[❯](red)" 63 | vicmd_symbol = "[❮](purple)[❮](cyan)" 64 | 65 | [python] 66 | format = '[(\($virtualenv\) )]($style)' 67 | style = 'white' 68 | 69 | [directory] 70 | style = "blue" 71 | truncation_length = 1 72 | truncation_symbol = "" 73 | fish_style_pwd_dir_length = 1 74 | 75 | # Right prompt uses left space padding. 76 | [git_branch] 77 | format = ' [$branch]($style)' 78 | style = 'green' 79 | 80 | [git_status] 81 | format = '( [\[$ahead_behind$stashed\]]($style))' 82 | style = "cyan" 83 | stashed = "≡" 84 | ahead = "⇡${count}" 85 | behind = "⇣${count}" 86 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 87 | 88 | [custom.git_status_dirty] 89 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 90 | symbol = "•" 91 | style = "purple" 92 | format="[$symbol]($style)" 93 | shell = ["sh"] 94 | 95 | [shell] 96 | format = ' [$indicator]($style)' 97 | fish_indicator = '🐟' # 󰈺 🐟 🐠 98 | powershell_indicator = '_' 99 | pwsh_indicator = '>_' 100 | zsh_indicator = '󠀥%' 101 | bash_indicator = '󠀥\$' 102 | style = 'cyan bold' 103 | disabled = false 104 | 105 | [cmd_duration] 106 | format = ' [$duration]($style)' 107 | 108 | [line_break] 109 | disabled = true 110 | 111 | [status] 112 | disabled = false 113 | format = '[$symbol$int]($style)' 114 | symbol = '✘' 115 | # pipestatus = true 116 | # pipestatus_format = '\[$pipestatus\]($style)' 117 | -------------------------------------------------------------------------------- /.config/bash/plugins/xdg.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # 4 | # XDG base dirs - don't pollute home 5 | # 6 | 7 | # 8 | # Variables 9 | # 10 | 11 | export XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config} 12 | export XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache} 13 | export XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} 14 | export XDG_STATE_HOME=${XDG_STATE_HOME:-$HOME/.local/state} 15 | export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-$HOME/.xdg} 16 | export XDG_PROJECTS_DIR=${XDG_PROJECTS_DIR:-$HOME/Projects} 17 | mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$XDG_DATA_HOME" \ 18 | "$XDG_STATE_HOME" "$XDG_RUNTIME_DIR" "$XDG_PROJECTS_DIR" 19 | 20 | # 21 | # Shell utils 22 | # 23 | 24 | # less 25 | export LESSKEY="${LESSKEY:-$XDG_CONFIG_HOME/less/lesskey}" 26 | export LESSHISTFILE="${LESSHISTFILE:-$XDG_CACHE_HOME/less/history}" 27 | 28 | # readline 29 | export INPUTRC="${INPUTRC:-$XDG_CONFIG_HOME/readline/inputrc}" 30 | 31 | # screen 32 | export SCREENRC="${SCREENRC:-$XDG_CONFIG_HOME/screen/screenrc}" 33 | 34 | # tmux 35 | export TMUX_CONFIG="${TMUX_CONFIG:-$XDG_CONFIG_HOME/tmux/tmux.conf}" 36 | if ! alias tmux 2>/dev/null; then 37 | alias tmux='tmux -f "$TMUX_CONFIG"' 38 | fi 39 | 40 | # wget 41 | export WGETRC="${WGETRC:-$XDG_CONFIG_HOME/wget/wgetrc}" 42 | if ! alias wget 2>/dev/null; then 43 | alias wget='wget --hsts-file="$XDG_CACHE_HOME/wget/wget-hsts"' 44 | fi 45 | mkdir -p "$XDG_CONFIG_HOME/wget" "$XDG_CACHE_HOME/wget" 46 | 47 | # 48 | # Dev tools 49 | # 50 | 51 | # jupyter 52 | export JUPYTER_CONFIG_DIR="${JUPYTER_CONFIG_DIR:-$XDG_CONFIG_HOME/jupyter}" 53 | 54 | # node 55 | if [[ :$PATH: != *:/opt/homebrew/share/npm/bin:* ]]; then 56 | export PATH="/opt/homebrew/share/npm/bin:$PATH" 57 | fi 58 | export NPM_CONFIG_USERCONFIG="${NPM_CONFIG_USERCONFIG:-$XDG_CONFIG_HOME/npm/npmrc}" 59 | export NODE_REPL_HISTORY="${NODE_REPL_HISTORY:-$XDG_DATA_HOME/nodejs/repl_history}" 60 | 61 | # nuget 62 | export NUGET_PACKAGES="${NUGET_PACKAGES:-$XDG_CACHE_HOME/NuGetPackages}" 63 | 64 | # postgres 65 | export PSQLRC="${PSQLRC:-$XDG_CONFIG_HOME/pg/psqlrc}" 66 | export PSQL_HISTORY="${PSQL_HISTORY:-$XDG_CACHE_HOME/pg/psql_history}" 67 | export PGPASSFILE="${PGPASSFILE:-$XDG_CONFIG_HOME/pg/pgpass}" 68 | export PGSERVICEFILE="${PGSERVICEFILE:-$XDG_CONFIG_HOME/pg/pg_service.conf}" 69 | 70 | # ruby bundler 71 | export BUNDLE_USER_CONFIG="${BUNDLE_USER_CONFIG:-$XDG_CONFIG_HOME/bundle}" 72 | export BUNDLE_USER_CACHE="${BUNDLE_USER_CACHE:-$XDG_CACHE_HOME/bundle}" 73 | export BUNDLE_USER_PLUGIN="${BUNDLE_USER_PLUGIN:-$XDG_DATA_HOME/bundle}" 74 | 75 | # ruby gems 76 | export GEM_HOME="${GEM_HOME:-$XDG_DATA_HOME/gem}" 77 | export GEM_SPEC_CACHE="${GEM_SPEC_CACHE:-$XDG_CACHE_HOME/gem}" 78 | 79 | # rust 80 | export CARGO_HOME="${CARGO_HOME:-$XDG_DATA_HOME/cargo}" 81 | export RUSTUP_HOME="${RUSTUP_HOME:-$XDG_DATA_HOME/rustup}" 82 | -------------------------------------------------------------------------------- /.config/bash/plugins/python.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash source=/dev/null 2 | 3 | # Manage Python venvs. 4 | function venv { 5 | local workon_home="${WORKON_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/venvs}" 6 | local -a usage=( 7 | "usage: venv [--home=] [-r|--remove] [-p|--path] " 8 | " venv [-h|--help] [-l|--list]" 9 | ) 10 | local -a o_badopt o_home o_list o_remove o_path 11 | 12 | # Show help if no arguments provided. 13 | if [[ "$#" -eq 0 ]]; then 14 | printf "%s\n" "${usage[@]}"; return 0 15 | fi 16 | 17 | # Parse arguments 18 | while [[ "$#" -gt 0 ]]; do 19 | case "$1" in 20 | --) shift; break ;; 21 | -h|--help) printf "%s\n" "${usage[@]}"; return 0 ;; 22 | -l|--list) o_list+=("$1") ;; 23 | -r|--remove) o_remove+=("$1") ;; 24 | -p|--path) o_path+=("$1") ;; 25 | --home) shift; o_home+=("$1") ;; 26 | --home=*) o_home=("${1#*=}") ;; 27 | -*) o_badopt+=("$1") ;; 28 | *) break ;; 29 | esac 30 | shift 31 | done 32 | 33 | if [[ "${#o_badopt}" -gt 0 ]]; then 34 | echo >&2 "venv: Unexpected option '${o_badopt[0]}'." 35 | printf "%s\n" "${usage[@]}"; return 1 36 | fi 37 | 38 | if [[ "${#o_home}" -gt 0 ]]; then 39 | workon_home="${o_home[-1]}" 40 | fi 41 | 42 | # --list 43 | if [[ "${#o_list}" -gt 0 ]]; then 44 | shopt -s nullglob 45 | local pyvenv 46 | for pyvenv in "$workon_home"/*; do 47 | echo "${pyvenv##*/}" 48 | done 49 | shopt -u nullglob 50 | return 0 51 | fi 52 | 53 | # Expecting 54 | if [[ "$#" -eq 0 ]]; then 55 | echo >&2 "venv: Expecting argument . Try 'venv -h' for usage." 56 | return 1 57 | fi 58 | 59 | # --path/--remove 60 | if [[ "${#o_path}" -gt 0 ]] || [[ "${#o_remove}" -gt 0 ]]; then 61 | if [[ ! -d "$workon_home/$1" ]]; then 62 | echo >&2 "venv: pyvenv not found '$1'." 63 | return 1 64 | fi 65 | if [[ "${#o_remove}" -gt 0 ]]; then 66 | rm -rf -- "${workon_home:-?}/$1" 67 | else 68 | echo "$workon_home/$1" 69 | fi 70 | return 0 71 | fi 72 | 73 | # Make venv if missing and activate 74 | if [[ ! -d "$workon_home/$1" ]]; then 75 | echo "Creating pyvenv: '$1'." 76 | python3 -m venv "$workon_home/$1" || return 1 77 | fi 78 | source "$workon_home/$1/bin/activate" 79 | } 80 | 81 | # Work on Python venvs. 82 | function workon() { 83 | if [[ "$#" -eq 0 ]]; then 84 | echo >&2 "workon: Expecting name of Python venv." 85 | return 1 86 | fi 87 | local workon_home="${WORKON_HOME:-${XDG_DATA_HOME:-$HOME/.local/share}/venvs}" 88 | [[ -d "$workon_home" ]] || mkdir -p "$workon_home" 89 | if [[ ! -d "$workon_home/$1" ]]; then 90 | echo >&2 "workon: Python venv not found '$1'." 91 | return 1 92 | fi 93 | source "$workon_home/$1/bin/activate" 94 | } 95 | -------------------------------------------------------------------------------- /.config/bash/plugins/macos.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Check requirements. 4 | [[ "$OSTYPE" == darwin* ]] || return 1 5 | 6 | # Change to the current Finder directory. 7 | function cdf() { 8 | cd "$(pfd)" || return 9 | } 10 | 11 | # Flush the DNS cache. 12 | function flushdns() { 13 | dscacheutil -flushcache && sudo killall -HUP mDNSResponder 14 | } 15 | 16 | # Hide hidden dotfiles in Finder. 17 | function hidefiles() { 18 | defaults write com.apple.finder AppleShowAllFiles -bool false && killall Finder 19 | } 20 | 21 | # Show hidden dotfiles in Finder. 22 | function showfiles() { 23 | defaults write com.apple.finder AppleShowAllFiles -bool true && killall Finder 24 | } 25 | 26 | # Have Siri let me know when a process is complete. 27 | function lmk() { 28 | # eg: sleep 2 && lmk 29 | say "${*:-Process complete}" 30 | } 31 | 32 | # Read man page with Preview.app 33 | function manp() { 34 | # https://scriptingosx.com/2022/11/on-viewing-man-pages-ventura-update/ 35 | if [[ $# -eq 0 ]]; then 36 | echo >&2 'manp: You must specify the manual page you want' 37 | return 1 38 | fi 39 | mandoc -T pdf "$(/usr/bin/man -w "$*")" | open -fa Preview 40 | } 41 | 42 | # Open the current directory in Finder 43 | function ofd() { 44 | open "$PWD" 45 | } 46 | 47 | # Take a quick look at a file using an appropriate viewer 48 | function peek() { 49 | [[ $# -gt 0 ]] && qlmanage -p "$*" &>/dev/null & 50 | } 51 | 52 | # Print the frontmost Finder directory. 53 | function pfd() { 54 | osascript 2> /dev/null <&1 <" >&2 87 | return 64 88 | fi 89 | 90 | local file files=() 91 | for file; do 92 | if [ -e "$file" ]; then 93 | abs_path=$(cd "$(dirname "$file")" && pwd)/$(basename "$file") 94 | files+=("POSIX file \"$abs_path\"") 95 | else 96 | echo "trash: No such file or directory '$file'." >&2 97 | return 1 98 | fi 99 | done 100 | 101 | local IFS=", " 102 | local file_list="${files[*]}" 103 | osascript -e "tell application \"Finder\" to move { $file_list } to trash" >/dev/null 2>&1 104 | } 105 | -------------------------------------------------------------------------------- /.config/starship/bash.toml: -------------------------------------------------------------------------------- 1 | # 2 | # My default bash prompt theme 3 | # 4 | 5 | add_newline = false 6 | 7 | # A minimal left prompt 8 | format = """ 9 | $line_break\ 10 | ${custom.dirname}\ 11 | ${custom.basename}\ 12 | $git_branch\ 13 | ${custom.git_status_dirty}\ 14 | $git_status 15 | $python\ 16 | $character\ 17 | """ 18 | 19 | # Move the rest of the prompt to the right 20 | right_format = """ 21 | $all$status$cmd_duration 22 | """ 23 | 24 | # Timeout for commands executed by starship (in milliseconds) 25 | command_timeout = 2000 26 | 27 | # Set the palette. 28 | palette = "p10k" 29 | 30 | # Define custom colors 31 | [palettes.tokyo_night_256] 32 | black = '16' 33 | blue = '111' 34 | cyan = '117' 35 | green = '149' 36 | purple = '141' 37 | red = '210' 38 | white = '146' 39 | yellow = '179' 40 | 41 | [palettes.wombat_256] 42 | black = '0' 43 | blue = '75' 44 | cyan = '123' 45 | green = '149' 46 | purple = '171' 47 | red = '203' 48 | white = '188' 49 | yellow = '223' 50 | true_white = '255' 51 | 52 | [palettes.p10k] 53 | black = '0' 54 | blue = '31' 55 | cyan = '39' 56 | green = '76' 57 | purple = '205' 58 | magenta = '205' 59 | red = '204' 60 | white = '223' 61 | yellow = '226' 62 | 63 | [character] 64 | success_symbol = '[\$](green)' 65 | error_symbol = '[\$](red)' 66 | vicmd_symbol = '[vi\$](bold white)' 67 | 68 | [python] 69 | format = '[(\($virtualenv\) )]($style)' 70 | style = 'white' 71 | 72 | [directory] 73 | style = "bold cyan" 74 | truncate_to_repo = false 75 | disabled = true 76 | 77 | [git_branch] 78 | format = '[$branch]($style)' 79 | style = 'purple' 80 | 81 | [git_status] 82 | format = '( [\[$ahead_behind$stashed\]]($style))' 83 | style = "cyan" 84 | stashed = "≡" 85 | ahead = "⇡${count}" 86 | behind = "⇣${count}" 87 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 88 | 89 | [custom.git_status_dirty] 90 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 91 | symbol = "•" 92 | style = "yellow" 93 | format = "[$symbol]($style)" 94 | shell = ["sh"] 95 | 96 | [custom.dirname] 97 | when = true 98 | command = 'test "$PWD" != "$HOME" && printf "%s/\n" "$(dirname "$PWD")" | sed -E "s|$HOME/|\~/|"' 99 | format = '[$symbol($output)]($style)' 100 | style = "blue" 101 | disabled = false 102 | shell = ["sh"] 103 | 104 | [custom.basename] 105 | when = true 106 | command = 'test "$PWD" = "$HOME" && echo "~" || basename "$PWD" | sed -E "s|$HOME/|\~/|"' 107 | style = "bold cyan" 108 | disabled = false 109 | shell = ["sh"] 110 | 111 | [custom.todo] 112 | when = 'test $(todo.sh ls | wc -l) -gt 2' 113 | command = 'todo.sh ls | tail -n 1 | cut -d" " -f2' 114 | symbol = "☑" 115 | format = '[$symbol( $output)]($style)' 116 | style = "blue" 117 | disabled = false 118 | shell = ["sh"] 119 | 120 | [cmd_duration] 121 | format = ' [$duration]($style)' 122 | 123 | [line_break] 124 | disabled = false 125 | 126 | [status] 127 | disabled = false 128 | format = ' [$symbol$int]($style)' 129 | symbol = '✘' 130 | pipestatus = true 131 | pipestatus_format = '\[$pipestatus\]($style)' 132 | -------------------------------------------------------------------------------- /bin/string: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC2120,SC3043 3 | 4 | die() { local err="$1"; shift; warn "$@"; exit "$err"; } 5 | say() { printf '%s\n' "$@"; } 6 | warn() { say "$@" >&2; } 7 | noop() { :; } 8 | is_cmd() { command -v "$1" >/dev/null 2>&1; } 9 | is_cmd local || alias local=noop 10 | 11 | str_transform() { 12 | local str newstr line err o_help o_quiet o_bad 13 | 14 | # Parse options. 15 | while [ "$#" -gt 0 ]; do 16 | case "$1" in 17 | -h|--help) o_help=true; shift ;; 18 | -q|--quiet) o_quiet=true; shift ;; 19 | --) shift; break ;; 20 | -*) o_bad="$1"; shift ;; 21 | *) break ;; 22 | esac 23 | done 24 | 25 | # Check usage. 26 | if [ -n "$o_help" ]; then 27 | printf 'string %s [-h | --help] [-q | --quiet] [STRING ...]\n' "$TRANSFORM" 28 | return 0 29 | elif [ -n "$o_bad" ]; then 30 | printf >&2 'string %s: %s: unknown option\n' "$TRANSFORM" "$o_bad" 31 | return 2 32 | fi 33 | 34 | # Collect piped input 35 | if ! [ -t 0 ]; then 36 | while IFS= read -r line || [ -n "$line" ]; do 37 | set -- "$@" "$line" 38 | done 39 | fi 40 | 41 | # Transform strings and return success only if any were transformed. 42 | err=1 43 | for str in "$@"; do 44 | if [ "$TRANSFORM" = "upper" ]; then 45 | newstr="$(printf '%s\n' "$str" | tr "[:lower:]" "[:upper:]")" 46 | else 47 | newstr="$(printf '%s\n' "$str" | tr "[:upper:]" "[:lower:]")" 48 | fi 49 | [ "$str" != "$newstr" ] && err=0 50 | [ -z "$o_quiet" ] && printf '%s\n' "$newstr" 51 | done 52 | return $err 53 | } 54 | 55 | string_upper() { 56 | TRANSFORM=upper str_transform "$@" 57 | } 58 | 59 | string_lower() { 60 | TRANSFORM=lower str_transform "$@" 61 | } 62 | 63 | string_length() { 64 | local err str len line o_help o_quiet o_bad 65 | 66 | while [ "$#" -gt 0 ]; do 67 | case "$1" in 68 | -h|--help) o_help=true; shift ;; 69 | -q|--quiet) o_quiet=true; shift ;; 70 | --) shift; break ;; 71 | -*) o_bad="$1"; shift ;; 72 | *) break ;; 73 | esac 74 | done 75 | 76 | # Check usage. 77 | if [ -n "$o_help" ]; then 78 | printf '%s\n' 'string length [-h | --help] [-q | --quiet] [STRING ...]' 79 | return 0 80 | elif [ -n "$o_bad" ]; then 81 | printf >&2 'string length: %s: unknown option\n' "$o_bad" 82 | return 2 83 | fi 84 | 85 | # Collect piped input 86 | if ! [ -t 0 ]; then 87 | while IFS= read -r line || [ -n "$line" ]; do 88 | set -- "$@" "$line" 89 | done 90 | fi 91 | 92 | # Print lengths 93 | err=1 94 | for str in "$@"; do 95 | len="${#str}" 96 | [ "$len" -gt 0 ] && err=0 97 | [ -z "$o_quiet" ] && printf '%s\n' "$len" 98 | done 99 | return "$err" 100 | } 101 | 102 | string() { 103 | local cmd 104 | 105 | if [ $# -eq 0 ]; then 106 | die 1 "string: missing subcommand" 107 | elif is_cmd "string_$1"; then 108 | cmd="string_$1" 109 | shift 110 | "$cmd" "$@" 111 | else 112 | die 1 "string: invalid subcommand '$1'." 113 | fi 114 | } 115 | string "$@" 116 | -------------------------------------------------------------------------------- /bin/fishy/string: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" 2>/dev/null && pwd) 5 | 6 | usage() { 7 | printf '%s\n' "NAME" 8 | printf '%s\n' " string - manipulate strings" 9 | printf '%s\n' "" 10 | printf '%s\n' "SYNOPSIS" 11 | printf '%s\n' " string collect [-a | --allow-empty] [-N | --no-trim-newlines] [STRING ...]" 12 | printf '%s\n' " string escape [-n | --no-quoted] [--style=] [STRING ...]" 13 | printf '%s\n' " string join [-q | --quiet] [-n | --no-empty] SEP [STRING ...]" 14 | printf '%s\n' " string join0 [-q | --quiet] [STRING ...]" 15 | printf '%s\n' " string length [-q | --quiet] [STRING ...]" 16 | printf '%s\n' " string lower [-q | --quiet] [STRING ...]" 17 | printf '%s\n' " string match [-a | --all] [-e | --entire] [-i | --ignore-case]" 18 | printf '%s\n' " [-g | --groups-only] [-r | --regex] [-n | --index]" 19 | printf '%s\n' " [-q | --quiet] [-v | --invert]" 20 | printf '%s\n' " PATTERN [STRING ...]" 21 | printf '%s\n' " string pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER]" 22 | printf '%s\n' " [STRING ...]" 23 | printf '%s\n' " string repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline]" 24 | printf '%s\n' " [-q | --quiet] [STRING ...]" 25 | printf '%s\n' " string repeat [-N | --no-newline] [-q | --quiet] COUNT [STRING ...]" 26 | printf '%s\n' " string replace [-a | --all] [-f | --filter] [-i | --ignore-case]" 27 | printf '%s\n' " [-r | --regex] [-q | --quiet] PATTERN REPLACE [STRING ...]" 28 | printf '%s\n' " string shorten [(-c | --char) CHARS] [(-m | --max) INTEGER]" 29 | printf '%s\n' " [-N | --no-newline] [-l | --left] [-q | --quiet] [STRING ...]" 30 | printf '%s\n' " string split [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]" 31 | printf '%s\n' " [-q | --quiet] [-r | --right] SEP [STRING ...]" 32 | printf '%s\n' " string split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]" 33 | printf '%s\n' " [-q | --quiet] [-r | --right] [STRING ...]" 34 | printf '%s\n' " string sub [(-s | --start) START] [(-e | --end) END] [(-l | --length) LENGTH]" 35 | printf '%s\n' " [-q | --quiet] [STRING ...]" 36 | printf '%s\n' " string trim [-l | --left] [-r | --right] [(-c | --chars) CHARS]" 37 | printf '%s\n' " [-q | --quiet] [STRING ...]" 38 | printf '%s\n' " string unescape [--style=] [STRING ...]" 39 | printf '%s\n' " string upper [-q | --quiet] [STRING ...]" 40 | } 41 | 42 | string() { 43 | local strcmd 44 | 45 | # Check usage. 46 | if [ "$1" = -h ] || [ "$1" = --help ]; then 47 | usage 48 | return 0 49 | elif [ -z "$1" ]; then 50 | printf >&2 'string: missing subcommand\n' 51 | return 2 52 | fi 53 | 54 | # Run string subcommand 55 | strcmd="string-${1}" 56 | shift 57 | if [ ! -e "${SCRIPT_DIR}/${strcmd}" ]; then 58 | printf >&2 '%s\n' "string: invalid subcommand" 59 | exit 2 60 | fi 61 | "${SCRIPT_DIR}/${strcmd}" "$@" 62 | } 63 | string "$@" || exit $? 64 | -------------------------------------------------------------------------------- /.config/bash/themes/bash.toml: -------------------------------------------------------------------------------- 1 | # 2 | # My default bash prompt theme 3 | # 4 | # https://starship.rs/config 5 | 6 | add_newline = false 7 | 8 | # A minimal left prompt 9 | format = """ 10 | $line_break\ 11 | ${custom.dirname}\ 12 | ${custom.basename}\ 13 | $git_branch\ 14 | ${custom.git_status_dirty}\ 15 | $git_status 16 | $python\ 17 | $character\ 18 | """ 19 | 20 | # Move the rest of the prompt to the right 21 | right_format = """ 22 | $all$status$cmd_duration 23 | """ 24 | 25 | # Timeout for commands executed by starship (in milliseconds) 26 | command_timeout = 2000 27 | 28 | # Set the palette. 29 | palette = "wombat_256" 30 | 31 | # Define custom colors 32 | [palettes.tokyo_night_256] 33 | black = '16' 34 | blue = '111' 35 | cyan = '117' 36 | green = '149' 37 | purple = '141' 38 | red = '210' 39 | white = '146' 40 | yellow = '179' 41 | 42 | [palettes.wombat_256] 43 | black = '0' 44 | blue = '75' 45 | cyan = '123' 46 | green = '149' 47 | purple = '171' 48 | red = '203' 49 | white = '188' 50 | yellow = '223' 51 | true_white = '255' 52 | 53 | [palettes.p10k] 54 | black = '0' 55 | blue = '31' 56 | cyan = '39' 57 | green = '76' 58 | purple = '205' 59 | magenta = '205' 60 | red = '204' 61 | white = '223' 62 | yellow = '226' 63 | 64 | [c] 65 | disabled = true 66 | 67 | [character] 68 | success_symbol = '[\$](green)' 69 | error_symbol = '[\$](red)' 70 | vicmd_symbol = '[vi\$](bold white)' 71 | 72 | [dotnet] 73 | format = '[$symbol($version )]($style)' 74 | style = 'bold green' 75 | 76 | [python] 77 | format = '[(\($virtualenv\) )]($style)' 78 | style = 'white' 79 | 80 | [directory] 81 | style = "bold cyan" 82 | truncate_to_repo = false 83 | disabled = true 84 | 85 | [git_branch] 86 | format = '[$branch]($style)' 87 | style = 'purple' 88 | 89 | [git_status] 90 | format = '( [\[$ahead_behind$stashed\]]($style))' 91 | style = "cyan" 92 | stashed = "≡" 93 | ahead = "⇡${count}" 94 | behind = "⇣${count}" 95 | diverged = "⇕⇡${ahead_count}⇣${behind_count}" 96 | 97 | [golang] 98 | format = '[$symbol($version )]($style)' 99 | 100 | [custom.git_status_dirty] 101 | when = 'test -n "$(git status --porcelain 2>/dev/null)"' 102 | symbol = "•" 103 | style = "yellow" 104 | format = "[$symbol]($style)" 105 | shell = ["sh"] 106 | 107 | [custom.dirname] 108 | when = true 109 | command = 'test "$PWD" != "$HOME" && printf "%s/\n" "$(dirname "$PWD")" | sed -E "s|$HOME/|\~/|"' 110 | format = '[$symbol($output)]($style)' 111 | style = "blue" 112 | disabled = false 113 | shell = ["sh"] 114 | 115 | [custom.basename] 116 | when = true 117 | command = 'test "$PWD" = "$HOME" && echo "~" || basename "$PWD" | sed -E "s|$HOME/|\~/|"' 118 | style = "bold cyan" 119 | disabled = false 120 | shell = ["sh"] 121 | 122 | [custom.todo] 123 | when = 'test $(todo.sh ls | wc -l) -gt 2' 124 | command = 'todo.sh ls | tail -n 1 | cut -d" " -f2' 125 | symbol = "☑" 126 | format = '[$symbol($output)]($style)' 127 | style = "bold blue" 128 | disabled = false 129 | shell = ["sh"] 130 | 131 | [cmd_duration] 132 | format = ' [$duration]($style)' 133 | 134 | [line_break] 135 | disabled = false 136 | 137 | [status] 138 | disabled = false 139 | format = ' [$symbol$int]($style)' 140 | symbol = '✘' 141 | pipestatus = true 142 | pipestatus_format = '\[$pipestatus\]($style)' 143 | -------------------------------------------------------------------------------- /docs/vim.md: -------------------------------------------------------------------------------- 1 | # vim 2 | 3 | ## Colemak 4 | 5 | One of the tricky elements of using Colemak is the vim arrow movements (HJKL) 6 | are positional instead of mnemonic, and those positions change from QWERTY. 7 | 8 | There are a few approaches that I can take to address this: 9 | 10 | 1. Ignore it and leave everything as-is 11 | 1. Minimally change it to make it tolerable 12 | 1. Change it entirely to make it sane. With a subtype of doing it in such a way 13 | that it's also okay to use in QWERTY mode in case I ever have to flip back. 14 | 15 | ### Ignore it 16 | 17 | This blog post(https://sermoa.wordpress.com/2011/12/16/colemak-and-vim-but-what-about-hjkl/) 18 | is a good advocacy for leaving the default nav of hjkl. 19 | 20 | ```vim 21 | " ^ 22 | " k Hint: The `h`{normal} key is at the left and moves left. 23 | " < h l > The `l`{normal} key is at the right and moves right. 24 | " j The `j`{normal} key looks like a down arrow. 25 | " v 26 | ``` 27 | 28 | ### Minimally change keys 29 | 30 | J is north of K in Colemak which makes it feel like a flight simulator. To fix 31 | this to at least make the keys positionally make sense, do the following: 32 | 33 | Right side QWERTY: 34 | 35 | ```text 36 | Y U I O P 37 | -H- -J- -K- -L- ; 38 | N M , . / 39 | ``` 40 | 41 | Right side Colemak: 42 | 43 | ```text 44 | -J- -L- U Y ; 45 | -H- N E I O 46 | -K- M , . / 47 | ``` 48 | 49 | .vimrc 50 | 51 | ```vim 52 | " ↑ 53 | " h 54 | " ← j l → 55 | " k 56 | " ↓ 57 | 58 | set langmap=jh,kj,hk 59 | ``` 60 | 61 | ### Change entirely 62 | 63 | One option is to mix QWERTY and Colemak positional 64 | 65 | ```vim 66 | " Remap for Colemak based on https://github.com/ohjames/colemak/blob/master/vimrc 67 | " rotate some keys about to get qwerty "hjkl" back for movement 68 | noremap n j 69 | noremap e k 70 | noremap i l 71 | 72 | " move these keys to their qwerty positions because they are 73 | " in the way of hjkl (and E for J) 74 | noremap k n 75 | noremap K N 76 | noremap u i 77 | noremap U I 78 | noremap l u 79 | noremap L U 80 | noremap N J 81 | noremap E K 82 | noremap I L 83 | 84 | " this is the only key that isn't in qwerty or colemak position 85 | noremap j e 86 | noremap J E 87 | ``` 88 | 89 | Another option is NEST: 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | ```vim 98 | " ↑ 99 | " e 100 | " ← s t → 101 | " n 102 | " ↓ 103 | noremap n j|noremap n j|noremap j 104 | noremap e k|noremap e k|noremap k 105 | noremap s h 106 | noremap t l 107 | 108 | noremap f e 109 | noremap k n 110 | noremap K N 111 | ``` 112 | 113 | Or, change NEST to mean (n)orth, (e)ast, (s)outh, wes(t): 114 | 115 | ```vim 116 | " ↑ 117 | " n 118 | " ← w e → 119 | " s 120 | " ↓ 121 | noremap n k|noremap n k|noremap k 122 | noremap e l 123 | noremap s j|noremap s j|noremap j 124 | noremap t h 125 | 126 | noremap f e 127 | noremap k n 128 | noremap K N 129 | ``` 130 | -------------------------------------------------------------------------------- /bin/prj: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | ##? prj - project jumper 5 | SCRIPT_PATH="$(realpath "$0")" 6 | 7 | # Check dependencies 8 | check_dependencies() { 9 | if ! command -v fzf >/dev/null; then 10 | echo "fzf command not found" >&2 11 | exit 1 12 | elif ! command -v fd >/dev/null; then 13 | echo "fd command not found" >&2 14 | exit 1 15 | fi 16 | } 17 | 18 | # Print usage information 19 | print_usage() { 20 | echo "prj: The project jumper" 21 | echo "usage: prj [-h] [-i ] []" 22 | } 23 | 24 | # Initialize sh/bash/zsh 25 | init_sh() { 26 | cat <&2 106 | exit 2 107 | ;; 108 | esac 109 | fi 110 | 111 | # Handle tilde expansion 112 | case "$PROJECT_DIR" in 113 | ~*) PROJECT_DIR="$HOME${PROJECT_DIR#~}" ;; 114 | esac 115 | 116 | # Check project directory exists 117 | if [ ! -d "$PROJECT_DIR" ]; then 118 | echo >&2 "prj: Project home directory not found '$PROJECT_DIR'" 119 | exit 1 120 | fi 121 | 122 | # Find and select project 123 | selection=$( 124 | fd --type d --hidden --max-depth 5 '\.git$' "$PROJECT_DIR" | 125 | sed -e "s|^$PROJECT_DIR/||" -e "s|/.git/$||" | 126 | sort -u | 127 | fzf --layout=reverse-list --query="$query" 128 | ) 129 | 130 | # Output the selected project directory 131 | if [ -n "$selection" ] && [ -d "$PROJECT_DIR/$selection" ]; then 132 | echo "$PROJECT_DIR/$selection" 133 | fi 134 | } 135 | 136 | # Run the script 137 | main "$@" 138 | -------------------------------------------------------------------------------- /bin/fishy/string-match: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | usage() { 5 | printf '%s\n' "string match [-a | --all] [-e | --entire] [-i | --ignore-case]" 6 | printf '%s\n' " [-g | --groups-only] [-r | --regex] [-n | --index]" 7 | printf '%s\n' " [-q | --quiet] [-v | --invert]" 8 | printf '%s\n' " PATTERN [STRING ...]" 9 | } 10 | 11 | string_match() { 12 | local o_help o_quiet o_all o_entire o_ignorecase o_groupsonly o_regex o_index 13 | local o_invert o_bad 14 | local str grep_opts pattern 15 | 16 | # Parse options. 17 | while [ "$#" -gt 0 ]; do 18 | case "$1" in 19 | -h|--help) o_help=1; shift ;; 20 | -q|--quiet) o_quiet=1; shift ;; 21 | -a|--all) o_all=1; shift ;; 22 | -e|--entire) o_entire=1; shift ;; 23 | -i|--ignore-case) o_ignorecase=1; shift ;; 24 | -g|--groups-only) o_groupsonly=1; shift ;; 25 | -r|--regex) o_regex=1; shift ;; 26 | -n|--index) o_index=1; shift ;; 27 | -v|--invert) o_invert=1; shift ;; 28 | --) shift; break ;; 29 | -*) o_bad="$1"; shift ;; 30 | *) break ;; 31 | esac 32 | done 33 | 34 | # Check usage. 35 | if [ -n "$o_help" ]; then 36 | usage 37 | return 0 38 | elif [ -n "$o_bad" ]; then 39 | printf >&2 'string match: %s: unknown option\n' "$o_bad" 40 | return 2 41 | fi 42 | 43 | # Require at least a pattern. 44 | if [ "$#" -lt 1 ]; then 45 | usage >&2 46 | return 2 47 | fi 48 | 49 | pattern="$1" 50 | shift 51 | 52 | # Only regex mode is implemented for now. 53 | if [ -z "$o_regex" ]; then 54 | printf '%s\n' "Glob matching not implemented yet." 55 | return 3 56 | fi 57 | 58 | # If no strings, read from stdin. 59 | if [ "$#" -eq 0 ]; then 60 | set -- $(cat) 61 | fi 62 | 63 | # Compose grep options 64 | grep_opts="-E" 65 | [ -n "$o_ignorecase" ] && grep_opts="$grep_opts -i" 66 | [ -n "$o_invert" ] && grep_opts="$grep_opts -v" 67 | 68 | for str; do 69 | if [ -n "$o_groupsonly" ]; then 70 | # Not implemented for now, but could be added as above 71 | printf '%s\n' "Group extraction not implemented yet." 72 | continue 73 | fi 74 | 75 | if [ -n "$o_all" ]; then 76 | # Print all matches in the string 77 | if [ -n "$o_entire" ]; then 78 | # Print the entire string once for each match 79 | # Use grep -o to count matches, print $str for each 80 | match_count=$(printf '%s\n' "$str" | grep $grep_opts -o -- "$pattern" | wc -l | tr -d ' ') 81 | i=1 82 | while [ "$i" -le "$match_count" ]; do 83 | printf '%s\n' "$str" 84 | i=$((i+1)) 85 | done 86 | else 87 | # Print each match on its own line 88 | printf '%s\n' "$str" | grep $grep_opts -o -- "$pattern" 89 | fi 90 | else 91 | # Only the first match 92 | if printf '%s\n' "$str" | grep $grep_opts -q -- "$pattern"; then 93 | if [ -n "$o_entire" ]; then 94 | printf '%s\n' "$str" 95 | else 96 | # Print the first match only 97 | printf '%s\n' "$str" | grep $grep_opts -o -- "$pattern" | head -n1 98 | fi 99 | fi 100 | fi 101 | done 102 | 103 | return 0 104 | } 105 | string_match "$@" || exit $? 106 | -------------------------------------------------------------------------------- /bin/repo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | ##? repo - Deal with git repos 3 | 4 | : ${REPO_HOME:=${XDG_CACHE_HOME:-$HOME/.cache}/repos} 5 | 6 | function __repo_help { 7 | echo "repo - Deal with git repos" 8 | echo "" 9 | echo "commands:" 10 | echo " help print this help" 11 | echo " home print repo home" 12 | echo " ls print repo list" 13 | echo " rm remove repo" 14 | echo " in install repo" 15 | echo " up update repos" 16 | echo "" 17 | echo "examples:" 18 | echo " repo in $newsha)" 28 | fi 29 | } 30 | 31 | function __repo_clone { 32 | local repo=$1 33 | local repodir=$REPO_HOME/$repo 34 | if [ -d $repodir ]; then 35 | echo "Found $repo..." 36 | return 37 | fi 38 | 39 | echo "Cloning $repo..." 40 | command git clone --quiet --depth 1 --recursive --shallow-submodules \ 41 | https://github.com/$repo $repodir 42 | 43 | local init=$repodir/${repo:t}.plugin.zsh 44 | if [[ ! -e $init ]]; then 45 | local -a initfiles=($repodir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 46 | (( $#initfiles )) && ln -sf $initfiles[1] $init 47 | fi 48 | if [[ $repo == sorin-ionescu/prezto ]]; then 49 | for init in $repodir/modules/*/init.zsh; do 50 | ln -sf $init $init:h/${init:h:t}.plugin.zsh 51 | done 52 | fi 53 | echo "Cloned $repo." 54 | } 55 | 56 | function __repo { 57 | emulate -L zsh 58 | setopt local_options extended_glob no_monitor 59 | 60 | if [ "$#" -eq 0 ]; then 61 | __repo_help 62 | return 1 63 | fi 64 | 65 | local repo err=1 66 | local cmd=$1; shift 67 | 68 | # piped/redirected input 69 | if ! [ -t 0 ]; then 70 | local -a args=($@) 71 | local data 72 | while IFS= read -r data || [ -n "$data" ]; do 73 | data=${data%\#*} 74 | args+=($data) 75 | done 76 | set -- $args 77 | fi 78 | 79 | case "$cmd" in 80 | help|-h|--help) 81 | __repo_help 82 | ;; 83 | home) 84 | echo $REPO_HOME 85 | ;; 86 | ls|list) 87 | err=1 88 | for repo in $REPO_HOME/*/*/.git(/N); do 89 | echo ${repo:h:h:t}/${repo:h:t} 90 | err=0 91 | done 92 | return $err 93 | ;; 94 | up|update) 95 | echo "Checking for updates..." 96 | for repo in $(repo ls); do 97 | echo "$repo..." 98 | __repo_update $repo & 99 | done 100 | wait 101 | echo "Updates complete." 102 | ;; 103 | in|install) 104 | for repo in $@; do 105 | __repo_clone $repo & 106 | done 107 | wait 108 | ;; 109 | rm|del) 110 | local err=0 111 | for repo in $@; do 112 | if ! [[ "$REPO_HOME" =~ $HOME/* ]]; then 113 | echo >&2 "repo: \$REPO_HOME not set correctly '$REPO_HOME'." 114 | return 2 115 | elif ! [[ -d $REPO_HOME/$repo ]]; then 116 | echo "Repo not found '$repo'." 117 | err=1 118 | else 119 | command rm -rf -- $REPO_HOME/$repo 120 | fi 121 | done 122 | return $err 123 | ;; 124 | *) 125 | echo >&2 "repo: Unknown command '"$cmd"'" 126 | return 1 127 | ;; 128 | esac 129 | } 130 | __repo "$@" 131 | -------------------------------------------------------------------------------- /bin/git/git-cloner: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck shell=sh disable=SC3043 3 | 4 | ##? cloner: A better git cloner. 5 | # - Support short user/repo form 6 | # - Default clone to ~/repos (configurable), not $PWD unless dir arg provided 7 | # - Add flags I always forget but 99% of the time want (--recurse-submodules) 8 | 9 | # Move the last arg into the $1 position and cycle the rest forward in order. 10 | # This is accomplished by cycling each arg one-by-one from the front to the back. Right 11 | # before the final move, the last arg will be the first, and the rest will be shifted 12 | # forward one (ie: 1 2 3 4 => 4 1 2 3). This will let you `set` new args, and then 13 | # `shift` the one that was in the last position, which is conveniently now $1. 14 | pop_prep() { 15 | local i 16 | i=0 17 | while [ $((i+=1)) -lt $# ]; do 18 | set -- "$@" "$1" 19 | shift 20 | done 21 | printf '%s\n' "$@" 22 | } 23 | 24 | # Take a repo and make it into the short user/repo form. 25 | to_short_repo() { 26 | [ $# -eq 1 ] || return 1 27 | local default_user 28 | default_user="$(git config mycustom.gitUser || git config user.name || whoami)" 29 | # Prepend default git user to always make 2+ fields and print user/repo form 30 | echo "${default_user}/${1}" | awk -F '[/:]' '{ print $(NF-1) "/" $NF }' 31 | } 32 | 33 | # Take a repo and determine its clone destination. 34 | to_repo_dir() { 35 | [ $# -eq 1 ] || return 1 36 | # Get the default repo dir, replacing ~ with $HOME 37 | local dir 38 | dir="$( (git config mycustom.repoPath || echo "$HOME/repos") | sed -e "s|^~|$HOME|" )" 39 | echo "${dir}/$( to_short_repo "$1" )" 40 | } 41 | 42 | # Take a repo and determine its URL. 43 | to_repo_url() { 44 | [ $# -eq 1 ] || return 1 45 | case $1 in (https://*|git@*) echo "$1"; return ;; esac 46 | 47 | local gitdomain gitprotocol repo 48 | gitdomain="$( git config mycustom.gitDomain || echo 'github.com' )" 49 | gitprotocol="$( git config mycustom.gitProtocol || echo 'https' )" 50 | repo="$( to_short_repo "$1" )" 51 | 52 | case $gitprotocol in 53 | (ssh*|git*) 54 | echo "${gitprotocol}@${gitdomain}:${repo}.git" 55 | ;; 56 | (*) 57 | echo "${gitprotocol}://${gitdomain}/${repo}" 58 | ;; 59 | esac 60 | } 61 | 62 | cloner() { 63 | if [ $# -eq 0 ]; then 64 | echo >&2 "clone: Expecting a repo argument. See 'git clone -h'." 65 | return 1 66 | fi 67 | 68 | local dir repo addflags debug 69 | 70 | # If there are multiple args, then the last arg could either be the repo, or the 71 | # optional destination path. If the latter, then pop_prep again. A final arg starting 72 | # with any of these symbols was likely intended to be a path not a repo: ~ / . $ 73 | if [ $# -gt 1 ]; then 74 | # shellcheck disable=SC2046 75 | set -- $(pop_prep "$@") 76 | case "$1" in 77 | ([./~\$]*) 78 | dir="$1" && shift 79 | # shellcheck disable=SC2046 80 | set -- $(pop_prep "$@") 81 | ;; 82 | esac 83 | fi 84 | 85 | # Ensure we have an explicit clone destination set, and pop off the repo. 86 | [ -z "$dir" ] && dir="$( to_repo_dir "$1" )" 87 | repo="$( to_repo_url "$1" )" 88 | shift 89 | 90 | # Now, reassemble the git command, add any additional flags 91 | addflags="$( git config mycustom.cloneAddFlags )" 92 | debug="$( git config mycustom.debug | tr '[:upper:]' '[:lower:]' )" 93 | case $debug in 94 | (t|true|1) 95 | echo "clone command modified to:" 96 | echo " command git clone $addflags $* $repo $dir" 97 | ;; 98 | esac 99 | command git clone "$addflags" "$@" "$repo" "$dir" 100 | } 101 | cloner "$@" 102 | -------------------------------------------------------------------------------- /bin/pkgmgr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | app_exists() { 4 | type $1 > /dev/null 2>&1 5 | return $? 6 | } 7 | 8 | ensure_fn_exists() { 9 | declare -f -F $1 > /dev/null 10 | return $? 11 | } 12 | 13 | gem_export() { 14 | gem list --local 15 | } 16 | 17 | gem_import() { 18 | awk '{ print $1 }' "$1" | xargs gem install --conservative 19 | } 20 | 21 | brew_export() { 22 | # brew is a pain... it dumps to a forced Brewfile, and is not consistently 23 | # sorted, making version controling your Brewfile tricky. #FixedIt 24 | brew update > /dev/null 25 | brew bundle dump --force 26 | brewfile="Brewfile" 27 | brewfile_tmp="Brewfile.tmp" 28 | grep '^tap\ ' "$brewfile" | sort > "$brewfile_tmp" 29 | grep '^brew\ ' "$brewfile" | sort >> "$brewfile_tmp" 30 | grep '^cask\ ' "$brewfile" | sort >> "$brewfile_tmp" 31 | grep '^mas\ ' "$brewfile" | sort >> "$brewfile_tmp" 32 | cat "$brewfile_tmp" 33 | rm "$brewfile_tmp" "$brewfile" 34 | } 35 | 36 | brew_import() { 37 | brew bundle --file="$1" 38 | } 39 | 40 | macosapps_import() { 41 | echo "No good way currently to import macos apps" 42 | } 43 | 44 | macosapps_export() { 45 | # get a list of the mac apps installed. Apps are really directories, so 46 | # don't descend into it. 47 | find /Applications -type d -not \( -path '*.app/*' -prune \) -name '*.app' | cut -d'/' -f3- | sort -f 48 | } 49 | 50 | pip_export() { 51 | pip$1 freeze 52 | } 53 | 54 | pip_import() { 55 | pip$2 install -r "$1" 56 | } 57 | 58 | npm_export() { 59 | npm ls -g --depth=0 60 | } 61 | 62 | npm_import() { 63 | awk 'NR>1{ print $2 }' "$1" | awk -F'@' '{ print $1 }' | xargs npm install -g 64 | } 65 | 66 | code_export() { 67 | code --list-extensions 68 | } 69 | 70 | code_import() { 71 | cat "$1" | xargs -L 1 code --install-extension 72 | } 73 | 74 | azuredatastudio_export() { 75 | azuredatastudio --list-extensions 76 | } 77 | 78 | usage() { 79 | echo "pkgmgr" 80 | echo "Export/Import package lists for various utilities" 81 | echo "" 82 | echo "Usage:" 83 | echo " pkgmgr import " 84 | echo " pkgmgr export " 85 | echo " pkgmrg list" 86 | echo " pkgmrg help" 87 | } 88 | 89 | main() { 90 | local cmd="$1" 91 | 92 | if [[ "$cmd" == "" ]]; then 93 | echo "Error: expecting command" >&2 94 | usage && exit 1 95 | fi 96 | 97 | if [[ "$cmd" == "help" ]]; then 98 | usage && exit 0 99 | fi 100 | 101 | if [[ "$cmd" == "list" ]]; then 102 | echo "azuredatastudio" 103 | echo "brew" 104 | echo "code" 105 | echo "gem" 106 | echo "macosapps" 107 | echo "npm" 108 | echo "pip" 109 | exit 0 110 | fi 111 | 112 | if [[ $cmd != "import" ]] && [[ $cmd != "export" ]]; then 113 | echo "Error: unexpected command: $cmd" >&2 114 | usage && exit 1 115 | fi 116 | 117 | local app="$2" 118 | if [[ "$app" == "" ]]; then 119 | echo "Error: expecting app" >&2 120 | usage && exit 1 121 | fi 122 | 123 | # pip2/3 support... 124 | local subargs= 125 | if [[ "$app" == pip* ]]; then 126 | subargs=${app##*pip} 127 | app=pip 128 | fi 129 | 130 | applist=( azuredatastudio brew code gem npm pip ) 131 | if [[ " ${applist[@]} " =~ " ${app} " ]]; then 132 | app_exists "$app" || { 133 | echo "Cannot find app $app" >&2 134 | exit 1 135 | } 136 | fi 137 | 138 | if [[ $cmd == "import" ]]; then 139 | filepath="$3" 140 | [[ -f "$filepath" ]] || { 141 | echo "import packages file not found: $filepath" >&2 142 | exit 1 143 | } 144 | ${app}_import "$filepath" $subargs 145 | else 146 | ${app}_export $subargs 147 | fi 148 | } 149 | main $@ 150 | -------------------------------------------------------------------------------- /bin/macopts.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # dotfiles references 4 | # https://github.com/pawelgrzybek/dotfiles/blob/master/setup-macos.sh 5 | # https://github.com/mathiasbynens/dotfiles/blob/master/.macos 6 | # https://github.com/geerlingguy/dotfiles/blob/master/.osx 7 | 8 | 9 | # --- General --- 10 | # install xcode command line tools 11 | xcode-select --install 12 | 13 | # rosetta 2 for intel chipset apps on the M1 14 | softwareupdate --install-rosetta --agree-to-license 15 | 16 | 17 | # --- Symlinks --- 18 | # sublime 19 | ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl ~/bin 20 | 21 | # vscode 22 | ln -s /Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code ~/bin 23 | 24 | 25 | # --- Dock --- 26 | # https://developer.apple.com/documentation/devicemanagement/dock 27 | 28 | # System Preferences > Dock > Magnification: 29 | defaults write com.apple.dock magnification -bool true 30 | 31 | # Minimize windows into their application’s icon 32 | defaults write com.apple.dock minimize-to-application -bool true 33 | 34 | # Change minimize/maximize window effect from genie to scale 35 | defaults write com.apple.dock mineffect -string "scale" 36 | 37 | # move to left 38 | defaults write com.apple.dock orientation left 39 | 40 | 41 | # --- Keyboard --- 42 | # Disable press-and-hold for keys in favor of key repeat 43 | defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false 44 | 45 | # Or, do this for individual apps 46 | # defaults write com.sublimetext.3 ApplePressAndHoldEnabled -bool false 47 | # defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false 48 | 49 | # if you need to reset back to default 50 | # defaults delete -g ApplePressAndHoldEnabled 51 | 52 | defaults write -g InitialKeyRepeat -int 10 # normal minimum is 15 (225 ms) 53 | defaults write -g KeyRepeat -int 1 # normal minimum is 2 (30 ms) 54 | 55 | 56 | # --- Folders --- 57 | # Save screenshots to the downloads 58 | mkdir -p "$HOME/Screenshots" 59 | defaults write com.apple.screencapture location -string "$HOME/Screenshots" 60 | 61 | 62 | # --- Desktop & Screen Saver --- 63 | # Hot corners 64 | # Possible values: 65 | # 0: no-op 66 | # 2: Mission Control 67 | # 3: Show application windows 68 | # 4: Desktop 69 | # 5: Start screen saver 70 | # 6: Disable screen saver 71 | # 7: Dashboard 72 | # 10: Put display to sleep 73 | # 11: Launchpad 74 | # 12: Notification Center 75 | defaults write com.apple.dock wvous-br-corner -int 5 76 | defaults write com.apple.dock wvous-br-modifier -int 0 77 | 78 | 79 | # --- Misc --- 80 | # why does Mojave font rendering suck on external monitors? FixedIt! 81 | defaults write -g CGFontRenderingFontSmoothingDisabled -bool NO 82 | 83 | 84 | # --- Finder Preferences --- 85 | # Finder > View > Show Path Bar 86 | defaults write com.apple.finder ShowPathbar -bool true 87 | 88 | 89 | # --- Accessibility --- 90 | # Accessibility > Display > Cursor : Increase pointer size 91 | sudo defaults write com.apple.universalaccess mouseDriverCursorSize 1.8 92 | 93 | 94 | # --- Security & Privacy 95 | # Turn Firewall on 96 | sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 1 97 | 98 | 99 | # --- Restart affected apps --- 100 | 101 | for app in \ 102 | "Activity Monitor" \ 103 | "Address Book" \ 104 | "Calendar" \ 105 | "cfprefsd" \ 106 | "Contacts" \ 107 | "Dock" \ 108 | "Finder" \ 109 | "Mail" \ 110 | "Messages" \ 111 | "Photos" \ 112 | "Safari" \ 113 | "SystemUIServer" \ 114 | "Terminal" \ 115 | "iCal"; do 116 | killall "${app}" &> /dev/null 117 | done 118 | echo "Done. Note that some of these changes require a logout/restart to take effect." 119 | -------------------------------------------------------------------------------- /bin/pocketdownload: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # pocket2csv_with_paging 5 | # Adds paging to download all Pocket bookmarks. 6 | # 7 | 8 | read -r -d '' LOGO <<'EOF' 9 | ... _ _ ___ 10 | | | | | |__ \ 11 | _ __ ___ ___ | | __ ___ | |_ ) | ___ ___ __ __ 12 | | '_ \ / _ \ / __|| |/ // _ \| __| / / / __|/ __|\ \ / / 13 | | |_) || (_) || (__ | <| __/| |_ / /_| (__ \__ \ \ V / 14 | | .__/ \___/ \___||_|\_\\___| \__||____|\___||___/ \_/ 15 | | | 16 | |_| 17 | ... 18 | 19 | EOF 20 | echo "" 21 | echo "$LOGO" 22 | echo "" 23 | echo "" 24 | 25 | ## read -p '[1] Enter Your Pocket API Consumer Key: ' CONSUMER_KEY 26 | # Pocket API credentials 27 | CONSUMER_KEY="113484-c20eb68369f61b62ea28588" 28 | ## ACCESS_TOKEN="c776ede4-04a6-5e4d-7c61-e532b9&username=mattmc3%40gmail.com" 29 | 30 | read -r -d '' MY_DATA < "$OUTPUT_FILE" 79 | 80 | # Fetch bookmarks in pages 81 | OFFSET=0 82 | PAGE_SIZE=100 83 | echo "[+] Starting download of all Pocket bookmarks..." 84 | 85 | while true; do 86 | read -r -d '' MY_DATA < tmp.json && mv tmp.json "$OUTPUT_FILE" 119 | 120 | # Increment offset for the next page 121 | OFFSET=$((OFFSET + PAGE_SIZE)) 122 | done 123 | 124 | echo "[+] All bookmarks downloaded and saved to $OUTPUT_FILE." 125 | -------------------------------------------------------------------------------- /bin/otp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | function gpg 4 | set --local gpg_home $GNUPGHOME 5 | if not set -q GNUPGHOME 6 | set -q XDG_DATA_HOME 7 | and set gpg_home $XDG_DATA_HOME/gnupg 8 | or set gpg_home $HOME/.local/share/gnupg 9 | end 10 | command gpg --homedir $gpg_home $argv 11 | end 12 | 13 | function otp --description 'One-time passwords' 14 | # Set homes 15 | set --local secrets_home $SECRETS_HOME 16 | if not set -q SECRETS_HOME 17 | set -q XDG_DATA_HOME 18 | and set secrets_home $XDG_DATA_HOME/secrets 19 | or set secrets_home $HOME/.local/share/secrets 20 | end 21 | set --local otp_home $secrets_home/otp 22 | 23 | # requirements 24 | if not type -q oathtool 25 | echo >&2 "otp: 'oathtool' not found. Install oathtool or oath-toolkit, depending on your OS." 26 | return 1 27 | end 28 | if not type -q gpg 29 | echo >&2 "otp: 'gpg' not found. Install, and create a key with 'gpg --gen-key' if you don't already have one." 30 | return 1 31 | end 32 | 33 | # Set public key IDs 34 | set --local gpg_keys 35 | for pubkey in $secrets_home/pubkeys/*.asc 36 | set --append gpg_keys (gpg --show-keys --with-colons $pubkey | awk -F':' '/^pub/{print $5}') 37 | end 38 | 39 | # declare vars 40 | set --local usage "usage: otp [-h | --help] [-l | --list] [-a | --add] [-r | --remove] [--rekey] " 41 | set --local recipients 42 | for key in $gpg_keys 43 | set --append recipients --recipient $key 44 | end 45 | 46 | # parse arguments 47 | argparse --name=otp h/help l/list a/add r/remove rekey -- $argv 48 | or return 1 49 | 50 | if test -n "$_flag_help" 51 | echo "otp [OPTIONS] []" 52 | echo "flags:" 53 | echo " -h, --help Show help" 54 | echo " -l, --list List keys" 55 | echo " --rekey Rebuild encrypted OTPs" 56 | echo " -a, --add Add " 57 | echo " -r, --remove Remove " 58 | return 0 59 | else if test -n "$_flag_list" 60 | # list 61 | set --local files $otp_home/*.otp.asc 62 | if test (count $files) -eq 0 63 | echo >&2 "otp: No one-time password keys found." 64 | return 1 65 | end 66 | for file in $files 67 | path basename $file | string replace '.otp.asc' '' 68 | end 69 | return 70 | else if test -n "$_flag_rekey" 71 | # rekey 72 | set --local totpkey 73 | for file in $otp_home/*.otp.asc 74 | set totpkey (gpg --quiet --decrypt $file) 75 | echo $totpkey | gpg $recipients --armor --encrypt --output $file.new 76 | if test $status = 0 77 | mv -f $file.new $file 78 | else 79 | rm -f $file.new 80 | return 1 81 | end 82 | end 83 | else if test (count $argv) -eq 0 84 | echo >&2 "otp: Expecting argument." 85 | echo >&2 $usage 86 | return 1 87 | else if test -n "$_flag_add" 88 | read -s -l otp_secret --prompt-str="Enter the otp secret for '$argv': " 89 | command rm -f $otp_home/$argv.otp.asc 90 | echo $otp_secret | gpg $recipients --armor --encrypt --output $otp_home/$argv.otp.asc 91 | otp $argv 92 | else if not test -e $otp_home/$argv.otp.asc 93 | echo >&2 "otp: Key not found '$argv'." 94 | return 1 95 | else if test -n "$_flag_remove" 96 | command rm -f $otp_home/$argv.otp.asc 97 | else 98 | set --local totpkey (gpg $recipients --quiet --decrypt $otp_home/$argv.otp.asc) 99 | if type -q pbcopy 100 | oathtool --totp --b $totpkey | tee /dev/stderr | pbcopy 101 | else 102 | oathtool --totp --b $totpkey 103 | end 104 | end 105 | end 106 | otp $argv 107 | -------------------------------------------------------------------------------- /.config/nvim/lua/kickstart/plugins/debug.lua: -------------------------------------------------------------------------------- 1 | -- debug.lua 2 | -- 3 | -- Shows how to use the DAP plugin to debug your code. 4 | -- 5 | -- Primarily focused on configuring the debugger for Go, but can 6 | -- be extended to other languages as well. That's why it's called 7 | -- kickstart.nvim and not kitchen-sink.nvim ;) 8 | 9 | return { 10 | -- NOTE: Yes, you can install new plugins here! 11 | 'mfussenegger/nvim-dap', 12 | -- NOTE: And you can specify dependencies as well 13 | dependencies = { 14 | -- Creates a beautiful debugger UI 15 | 'rcarriga/nvim-dap-ui', 16 | 17 | -- Required dependency for nvim-dap-ui 18 | 'nvim-neotest/nvim-nio', 19 | 20 | -- Installs the debug adapters for you 21 | 'williamboman/mason.nvim', 22 | 'jay-babu/mason-nvim-dap.nvim', 23 | 24 | -- Add your own debuggers here 25 | 'leoluz/nvim-dap-go', 26 | }, 27 | keys = function(_, keys) 28 | local dap = require 'dap' 29 | local dapui = require 'dapui' 30 | return { 31 | -- Basic debugging keymaps, feel free to change to your liking! 32 | { '', dap.continue, desc = 'Debug: Start/Continue' }, 33 | { '', dap.step_into, desc = 'Debug: Step Into' }, 34 | { '', dap.step_over, desc = 'Debug: Step Over' }, 35 | { '', dap.step_out, desc = 'Debug: Step Out' }, 36 | { 'b', dap.toggle_breakpoint, desc = 'Debug: Toggle Breakpoint' }, 37 | { 38 | 'B', 39 | function() 40 | dap.set_breakpoint(vim.fn.input 'Breakpoint condition: ') 41 | end, 42 | desc = 'Debug: Set Breakpoint', 43 | }, 44 | -- Toggle to see last session result. Without this, you can't see session output in case of unhandled exception. 45 | { '', dapui.toggle, desc = 'Debug: See last session result.' }, 46 | unpack(keys), 47 | } 48 | end, 49 | config = function() 50 | local dap = require 'dap' 51 | local dapui = require 'dapui' 52 | 53 | require('mason-nvim-dap').setup { 54 | -- Makes a best effort to setup the various debuggers with 55 | -- reasonable debug configurations 56 | automatic_installation = true, 57 | 58 | -- You can provide additional configuration to the handlers, 59 | -- see mason-nvim-dap README for more information 60 | handlers = {}, 61 | 62 | -- You'll need to check that you have the required things installed 63 | -- online, please don't ask me how to install them :) 64 | ensure_installed = { 65 | -- Update this to ensure that you have the debuggers for the langs you want 66 | 'delve', 67 | }, 68 | } 69 | 70 | -- Dap UI setup 71 | -- For more information, see |:help nvim-dap-ui| 72 | dapui.setup { 73 | -- Set icons to characters that are more likely to work in every terminal. 74 | -- Feel free to remove or use ones that you like more! :) 75 | -- Don't feel like these are good choices. 76 | icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, 77 | controls = { 78 | icons = { 79 | pause = '⏸', 80 | play = '▶', 81 | step_into = '⏎', 82 | step_over = '⏭', 83 | step_out = '⏮', 84 | step_back = 'b', 85 | run_last = '▶▶', 86 | terminate = '⏹', 87 | disconnect = '⏏', 88 | }, 89 | }, 90 | } 91 | 92 | dap.listeners.after.event_initialized['dapui_config'] = dapui.open 93 | dap.listeners.before.event_terminated['dapui_config'] = dapui.close 94 | dap.listeners.before.event_exited['dapui_config'] = dapui.close 95 | 96 | -- Install golang specific config 97 | require('dap-go').setup { 98 | delve = { 99 | -- On Windows delve must be run attached or it crashes. 100 | -- See https://github.com/leoluz/nvim-dap-go/blob/main/README.md#configuring 101 | detached = vim.fn.has 'win32' == 0, 102 | }, 103 | } 104 | end, 105 | } 106 | -------------------------------------------------------------------------------- /bin/fishy/math.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local posix = require "posix" 4 | 5 | -- Memoization table for factorials 6 | local factorials = {} 7 | 8 | local custom_math = {} 9 | 10 | -- Custom math functions 11 | function custom_math.cosh(x) 12 | x = tonumber(x) 13 | if not x then error("math: cosh argument must be a number") end 14 | return (math.exp(x) + math.exp(-x)) / 2 15 | end 16 | 17 | function custom_math.log2(x) 18 | x = tonumber(x) 19 | if not x or x <= 0 then error("math: log2 argument must be a positive number") end 20 | return math.log(x, 2) 21 | end 22 | 23 | function custom_math.fac(n) 24 | n = tonumber(n) 25 | if not n or n < 0 or n ~= math.floor(n) then 26 | error("math: fac argument must be a non-negative integer") 27 | end 28 | if n == 0 then return 1 end 29 | if factorials[n] then return factorials[n] end 30 | local result = 1 31 | for i = 2, n do 32 | result = result * i 33 | end 34 | factorials[n] = result 35 | return result 36 | end 37 | 38 | -- Compute gcd for reducing fractions 39 | function custom_math.gcd(a, b) 40 | while b ~= 0 do 41 | a, b = b, a % b 42 | end 43 | return a 44 | end 45 | 46 | -- Compute nCr using multiplicative formula with fraction reduction 47 | function custom_math.ncr(n, r) 48 | n, r = tonumber(n), tonumber(r) 49 | if not n or not r or n < 0 or r < 0 or n ~= math.floor(n) or r ~= math.floor(r) or r > n then 50 | error("math: ncr arguments must be non-negative integers with r <= n") 51 | end 52 | if r > n - r then r = n - r end -- take advantage of symmetry 53 | local num, den = 1, 1 54 | for i = 1, r do 55 | num = num * (n - i + 1) 56 | den = den * i 57 | local g = custom_math.gcd(num, den) 58 | num = num // g 59 | den = den // g 60 | end 61 | return num 62 | end 63 | 64 | -- Compute nPr directly 65 | function custom_math.npr(n, r) 66 | n, r = tonumber(n), tonumber(r) 67 | if not n or not r or n < 0 or r < 0 or n ~= math.floor(n) or r ~= math.floor(r) or r > n then 68 | error("math: npr arguments must be non-negative integers with r <= n") 69 | end 70 | local result = 1 71 | for i = 0, r - 1 do 72 | result = result * (n - i) 73 | end 74 | return result 75 | end 76 | 77 | function custom_math.round(x) 78 | x = tonumber(x) 79 | if not x then error("math: round argument must be a number") end 80 | if x >= 0 then 81 | return math.floor(x + 0.5) 82 | else 83 | return math.ceil(x - 0.5) 84 | end 85 | end 86 | 87 | function custom_math.eval_infix(args) 88 | for i, v in ipairs(args) do 89 | local vl = tostring(v):lower() 90 | if vl == "x" then 91 | args[i] = "*" 92 | elseif vl == "pi" then 93 | args[i] = "3.141593" 94 | elseif vl == "tau" then 95 | args[i] = "6.283185" 96 | elseif vl == "e" then 97 | args[i] = "2.718282" 98 | end 99 | end 100 | 101 | -- Join args into a string 102 | local expr = table.concat(args, " ") 103 | -- Only allow numbers, operators, parentheses, and spaces for safety 104 | if expr:match("[^%de%^%+%-%*/%%%.%(%)%sE]") then 105 | error("math: invalid characters in expression") 106 | end 107 | -- Evaluate using load 108 | local f, err = load("return " .. expr) 109 | if not f then error("math: invalid expression: " .. err) end 110 | return f() 111 | end 112 | 113 | local function math_command(args) 114 | local funcname = args[1] 115 | local fn = math[funcname] or custom_math[funcname] 116 | if type(fn) == "function" then 117 | -- Convert remaining args to numbers 118 | local params = {} 119 | for i = 2, #args do 120 | local n = tonumber(args[i]) 121 | if not n then 122 | error("math: argument '" .. tostring(args[i]) .. "' is not a number") 123 | end 124 | params[#params+1] = n 125 | end 126 | return fn(table.unpack(params)) 127 | else 128 | -- Try to evaluate as infix expression 129 | return custom_math.eval_infix(args) 130 | end 131 | end 132 | 133 | if debug.getinfo(1, "S").short_src == arg[0] then 134 | local ok, result = pcall(math_command, {...}) 135 | if not ok then 136 | local err = result:match(":%s(.+)$") or result 137 | io.stderr:write(err, "\n") 138 | os.exit(1) 139 | end 140 | print(result) 141 | end 142 | 143 | return math_command 144 | -------------------------------------------------------------------------------- /.config/zsh/conf.d/myaliases.zsh: -------------------------------------------------------------------------------- 1 | # 2 | # aliases - Zsh and bash aliases 3 | # 4 | 5 | # References 6 | # - https://medium.com/@webprolific/getting-started-with-dotfiles-43c3602fd789#.vh7hhm6th 7 | # - https://github.com/webpro/dotfiles/blob/master/system/.alias 8 | # - https://github.com/mathiasbynens/dotfiles/blob/master/.aliases 9 | # - https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/common-aliases/common-aliases.plugin.zsh 10 | # 11 | 12 | # single character shortcuts - be sparing! 13 | alias _=sudo 14 | alias l=ls 15 | alias g=git 16 | 17 | # mask built-ins with better defaults 18 | alias ping='ping -c 5' 19 | alias vi=vim 20 | alias nv=nvim 21 | alias grep="${aliases[grep]:-grep} --exclude-dir={.git,.vscode}" 22 | 23 | # directories 24 | alias secrets="cd ${XDG_DATA_HOME:-~/.local/share}/secrets" 25 | 26 | # more ways to ls 27 | alias ll='ls -lh' 28 | alias la='ls -lAh' 29 | alias lsa="ls -aG" 30 | alias ldot='ls -ld .*' 31 | 32 | # fix typos 33 | alias get=git 34 | alias quit='exit' 35 | alias cd..='cd ..' 36 | alias zz='exit' 37 | 38 | # tar 39 | alias tarls="tar -tvf" 40 | alias untar="tar -xf" 41 | 42 | # date/time 43 | alias timestamp="date '+%Y-%m-%d %H:%M:%S'" 44 | alias datestamp="date '+%Y-%m-%d'" 45 | alias isodate="date +%Y-%m-%dT%H:%M:%S%z" 46 | alias utc="date -u +%Y-%m-%dT%H:%M:%SZ" 47 | alias unixepoch="date +%s" 48 | 49 | # find 50 | # alias fd='find . -type d -name' 51 | # alias ff='find . -type f -name' 52 | 53 | # homebrew 54 | #alias brewup="brew update && brew upgrade && brew cleanup" 55 | #alias brewinfo="brew leaves | xargs brew desc --eval-all" 56 | 57 | # disk usage 58 | alias biggest='du -s ./* | sort -nr | awk '\''{print $2}'\'' | xargs du -sh' 59 | alias dux='du -x --max-depth=1 | sort -n' 60 | alias dud='du -d 1 -h' 61 | alias duf='du -sh *' 62 | 63 | # url encode/decode 64 | alias urldecode='python3 -c "import sys, urllib.parse as ul; \ 65 | print(ul.unquote_plus(sys.argv[1]))"' 66 | alias urlencode='python3 -c "import sys, urllib.parse as ul; \ 67 | print (ul.quote_plus(sys.argv[1]))"' 68 | 69 | # misc 70 | alias please=sudo 71 | alias zshrc='${EDITOR:-vim} "${ZDOTDIR:-$HOME}"/.zshrc' 72 | alias zbench='for i in {1..10}; do /usr/bin/time zsh -lic exit; done' 73 | alias cls="clear && printf '\e[3J'" 74 | 75 | # print things 76 | alias print-fpath='for fp in $fpath; do echo $fp; done; unset fp' 77 | alias print-path='echo $PATH | tr ":" "\n"' 78 | alias print-functions='print -l ${(k)functions[(I)[^_]*]} | sort' 79 | 80 | # auto-orient images based on exif tags 81 | alias autorotate="jhead -autorot" 82 | 83 | # color 84 | alias colormap='for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+"\n"}; done' 85 | 86 | # dotfiles 87 | : ${DOTFILES:=$HOME/.dotfiles} 88 | alias dotf='cd "$DOTFILES"' 89 | alias dotfed='cd "$DOTFILES" && ${VISUAL:-${EDITOR:-vim}} .' 90 | alias dotfl="cd \$DOTFILES/local" 91 | alias fdot='cd ${XDG_CONFIG_HOME:-~/.config}/fish' 92 | alias fconf=fdot 93 | alias zdot='cd $ZDOTDIR' 94 | alias zcust='cd $ZSH_CUSTOM' 95 | 96 | # fonts 97 | alias fontlist="system_profiler SPFontsDataType | grep 'Full Name' | awk -F: '{print \$2}' | sed 's/^ *//g' | sort" 98 | 99 | # gpg 100 | export GNUPGHOME=${GNUPGHOME:=${XDG_DATA_HOME:-$HOME/.local/share}/gnupg} 101 | [[ -e $GNUPGHOME:h ]] || mkdir -p $GNUPGHOME:h 102 | alias gpg="${aliases[gpg]:-gpg} --homedir \"$GNUPGHOME\"" 103 | 104 | # iwd - initial working directory 105 | : ${IWD:=$PWD} 106 | alias iwd='cd $IWD' 107 | 108 | # java 109 | alias setjavahome="export JAVA_HOME=\`/usr/libexec/java_home\`" 110 | 111 | # dotnet 112 | alias dotnet8="/opt/homebrew/opt/dotnet@8/bin/dotnet" 113 | 114 | # todo-txt 115 | alias t="todo.sh" 116 | alias todos="$VISUAL $HOME/Desktop/todo.txt" 117 | 118 | # Ensure python commands exist. 119 | if (( $+commands[python3] )) && ! (( $+commands[python] )); then 120 | alias python=python3 121 | fi 122 | if (( $+commands[pip3] )) && ! (( $+commands[pip] )); then 123 | alias pip=pip3 124 | fi 125 | 126 | # Ensure envsubst command exists. 127 | if ! (( $+commands[envsubst] )); then 128 | alias envsubst="python -c 'import os,sys;[sys.stdout.write(os.path.expandvars(l)) for l in sys.stdin]'" 129 | fi 130 | 131 | # vim: ft=zsh sw=2 ts=2 et 132 | -------------------------------------------------------------------------------- /bin/gitex: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # optionally, use !/usr/bin/env dash to really test POSIX 3 | ##? gitex - git extensions; useful for making git aliases that don't suck 4 | ##? usage: gitx 5 | ##? 6 | 7 | # To check, run: shellcheck -e SC3043 ~/bin/gitex 8 | 9 | # For testing... 10 | gitex_foo() { 11 | echo "foo called with $# params: $*" 12 | } 13 | 14 | # POSIX test for function existance. 15 | is_function() { 16 | [ "$#" -eq 1 ] || return 1 17 | type "$1" | sed "s/$1//" | grep -qwi function 18 | } 19 | 20 | ##? help: Display gitex help comments 21 | gitex_help() { 22 | # echo "$0" 23 | grep "^##?" "$0" | cut -c 5- 24 | } 25 | 26 | ##? branch-cleanup: Remove branches no longer on remote 27 | gitex_branch_cleanup() { 28 | # https://stackoverflow.com/questions/7726949/remove-tracking-branches-no-longer-on-remote 29 | local defbranch 30 | defbranch="$(gitex_branch_main 2>/dev/null)" 31 | git checkout "${defbranch:-main}" >/dev/null 2>/dev/null && git fetch -p && git branch -vv | 32 | awk '/: gone]/{print $1}' | 33 | xargs git branch -d 34 | } 35 | 36 | ##? branch-main: Get the name of the default branch for a repo (main, master, trunk, etc) 37 | gitex_branch_main() { 38 | # https://stackoverflow.com/questions/28666357/git-how-to-get-default-branch 39 | git symbolic-ref --short refs/remotes/origin/HEAD | sed 's|^origin/||' 40 | } 41 | 42 | ##? branch-name: Get the current branch name 43 | gitex_branch_name() { 44 | git branch --show-current 45 | } 46 | 47 | ##? browse: Open web browser to git remote URL 48 | gitex_browse() { 49 | local url 50 | url=$( 51 | git config "remote.${1:-origin}.url" | 52 | sed -e 's|^.*@|https://|' -e 's|.git$||' -e 's|:|/|2' 53 | ) 54 | git web--browse "$url" 55 | } 56 | 57 | ##? checkout-branches: Checkout and track all branches 58 | gitex_checkout_branches() { 59 | # https://stackoverflow.com/questions/67699/how-to-clone-all-remote-branches-in-git 60 | git branch -a | sed -n "/\\/HEAD /d; /\\/main$/d; /\\/master$/d; /remotes/p;" | xargs -L1 git checkout --track 61 | } 62 | 63 | ##? is-clean: Is the git repo clean 64 | gitex_is_clean() { 65 | test -z "$(git status --porcelain 2>/dev/null)" 66 | } 67 | 68 | ##? is-dirty: Is the git repo dirty 69 | gitex_is_dirty() { 70 | test -n "$(git status --porcelain 2>/dev/null)" 71 | } 72 | 73 | ##? lg: Print my preferred git log view. 74 | gitex_lg() { 75 | git log --all --decorate --oneline --graph 76 | } 77 | 78 | ##? log-pretty: Print a pretty log. 79 | gitex_log_pretty() { 80 | git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' --all 81 | } 82 | 83 | ##? pushup: Push a new branch to the remote 84 | gitex_pushup() { 85 | # Quick way to resolve the annoying error: 86 | # 'fatal: The current branch foo has no upstream branch.' 87 | # Or, you could just set `push.autoSetupRemote = true` in gitconfig... 88 | git push --set-upstream "${1:-origin}" "$(git rev-parse --abbrev-ref HEAD)" 89 | } 90 | 91 | ##? repodir: Print my preferred root repo directory. 92 | gitex_repodir() { 93 | (git config gitex.repodir || echo "$HOME/repos") | sed -e "s|^~|$HOME|" 94 | } 95 | 96 | ##? sha: Print the 7-char short git SHA, with a * indicating dirty. 97 | gitex_sha() { 98 | printf '%s' "$(git rev-parse --short HEAD)" 99 | gitex_is_dirty && echo '*' || echo 100 | } 101 | 102 | ##? whoami: Print my config's username and email. 103 | gitex_whoami() { 104 | git config user.name && git config user.email 105 | } 106 | 107 | # main gitex command 108 | gitex() { 109 | if [ "$#" -eq 0 ]; then 110 | echo >&2 "gitex: expecting a subcommand." 111 | return 1 112 | fi 113 | 114 | local subcmd="$1" 115 | if [ "$subcmd" = "-h" ] || [ "$subcmd" = "--help" ]; then 116 | # Support -h/--help flags 117 | subcmd="help" 118 | else 119 | # Make kebab-case-subcommands work. 120 | subcmd=$(printf '%s' "$subcmd" | tr '-' '_') 121 | fi 122 | 123 | # Call the subcommand if it exists. 124 | if is_function "gitex_${subcmd}"; then 125 | shift 126 | "gitex_${subcmd}" "$@" 127 | else 128 | echo >&2 "gitex: subcommand not found '$subcmd'." 129 | return 1 130 | fi 131 | } 132 | gitex "$@" 133 | 134 | # vim: set sw=2 sts=2 ts=8 et: 135 | -------------------------------------------------------------------------------- /.config/vim/init.vim: -------------------------------------------------------------------------------- 1 | " 2 | " Notes: 3 | " 4 | " References 5 | " http://stevelosh.com/blog/2010/09/coming-home-to-vim/#making-vim-more-useful 6 | " https://github.com/fabi1cazenave/cua-mode.vim/blob/master/plugin/cua-mode.vim 7 | " https://github.com/tpope/vim-sensible/blob/master/plugin/sensible.vim 8 | 9 | " better defaults were introduced in vim 7, but they only load for a missing vimrc. 10 | unlet! skip_defaults_vim 11 | source $VIMRUNTIME/defaults.vim 12 | 13 | " Kickstart: 14 | source ~/.config/vim/kickstart.vim 15 | 16 | " General Settings 17 | set colorcolumn=88 " add ruler 18 | set nobackup " We have vcs, we don't need backups this way 19 | set nowritebackup " We have vcs, we don't need backups this way 20 | set noswapfile " No need for this on a modern system 21 | set autowrite " autosaving files is a nice feature 22 | set lazyredraw " don't redraw the screen on macros, or other non-typed operations 23 | set shortmess+=I " no vim welcome screen 24 | set virtualedit+=block " allow the cursor to go anywhere in visual block mode 25 | set wildmode=list:full 26 | set foldmethod=marker 27 | 28 | " https://vim.fandom.com/wiki/Disable_beeping 29 | set noerrorbells visualbell t_vb= 30 | autocmd GUIEnter * set visualbell t_vb= 31 | 32 | 33 | " Cursor 34 | " Use a line cursor within insert mode and a block cursor everywhere else. 35 | " Reference chart of values: 36 | " Ps = 0 -> blinking block. 37 | " Ps = 1 -> blinking block (default). 38 | " Ps = 2 -> steady block. 39 | " Ps = 3 -> blinking underline. 40 | " Ps = 4 -> steady underline. 41 | " Ps = 5 -> blinking bar (xterm). 42 | " Ps = 6 -> steady bar (xterm). 43 | let &t_SI = "\e[6 q" 44 | let &t_EI = "\e[2 q" 45 | 46 | " Editor 47 | 48 | set list " needed for listchars 49 | set listchars=tab:»\ ,trail:· " Display tabs and trailing spaces visually 50 | set title " Sets the terminal to show the buffer title 51 | set scrolloff=4 " when scrolling around, keep a buffer of a few lines above/below 52 | 53 | 54 | " Whitespace 55 | 56 | set expandtab " use spaces instead of tabs. 57 | set tabstop=4 " number of spaces that a tab in a file counts for 58 | set shiftwidth=4 " affects how autoindentation works 59 | set softtabstop=4 " when tab is pressed, only move to the next tab stop 60 | set shiftround " tab / shifting moves to closest tabstop. 61 | set smartindent " intelligently dedent / indent new lines based on rules. 62 | 63 | 64 | " 65 | " Key bindings 66 | " 67 | 68 | " Assign keys 69 | nnoremap ve :e $MYVIMRC 70 | nnoremap vr :source $MYVIMRC 71 | 72 | " Make U be redo. 73 | nnoremap U 74 | 75 | " Colemak 76 | " set langmap=je,JE,li,LI,nj,NJ,ek,EK,il,IL,kn,KN 77 | " Arrow with neiu, and then make H-U, J->E, K->N, and L->I. 78 | " set langmap=nh,NH,ej,EJ,il,IL,uk,UK,je,JE,li,LI,hu,HU,nh,kn,KN 79 | " Make tn get us out of insert mode because that's handy. 80 | " inoremap tn 81 | 82 | " Emacs shortcuts 83 | inoremap I 84 | nnoremap ^ 85 | inoremap A 86 | nnoremap $ 87 | inoremap Bi 88 | inoremap lWi 89 | 90 | """ CUA shortcuts 91 | " meta(alt)-left/right moves across words 92 | map B 93 | map W 94 | map { 95 | map } 96 | 97 | " inoremap E 98 | " noremap E 99 | " inoremap B 100 | " noremap B 101 | nnoremap :w 102 | nnoremap :u 103 | inoremap :u 104 | 105 | " Make search more sane 106 | set showmatch " live match highlighting 107 | set gdefault " use the `g` flag by default. 108 | " make search use normal PERL regex 109 | " nnoremap / /\v 110 | " vnoremap / /\v 111 | " This unsets the "last search pattern" register by hitting return 112 | nnoremap :noh: 113 | 114 | 115 | " Save on focus lost (breaks when unnamed) 116 | " au FocusLost * :wa 117 | 118 | " Theme 119 | " to get the font name, set it in MacVim and then run `:set guifont?` to see 120 | " the value. 121 | if has('gui_running') 122 | set guifont=HackNF-Regular:h15 123 | endif 124 | 125 | " FZF 126 | set rtp+=/usr/local/opt/fzf 127 | 128 | -------------------------------------------------------------------------------- /bin/colorschemes: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | 4 | COLOR_SCHEMES_HOME="${XDG_CACHE_HOME:-$HOME/.cache}/repos/mbadolato/iterm2-color-schemes" 5 | THIS="$0" 6 | 7 | _clone_color_schemes() { 8 | local lastupdated="$COLOR_SCHEMES_HOME/.lastupdated" 9 | 10 | if [ ! -d "$COLOR_SCHEMES_HOME" ]; then 11 | echo "Cloning mbadolato/iterm2-color-schemes..." 12 | git clone --depth 1 --quiet https://github.com/mbadolato/iterm2-color-schemes "$COLOR_SCHEMES_HOME" 13 | touch "$lastupdated" 14 | elif [ "$(find "$COLOR_SCHEMES_HOME" -name ".lastupdated" -mtime +7 | wc -l)" -gt 0 ]; then 15 | echo "Updating mbadolato/iterm2-color-schemes..." 16 | git -C "$COLOR_SCHEMES_HOME" pull --quiet 17 | touch "$lastupdated" 18 | fi 19 | } 20 | 21 | _usage() { 22 | echo "colorschemes - Show color schemes from https://iterm2colorschemes.com/" 23 | echo "usage:" 24 | echo " colorschemes [-l|-d] Fuzzy find a color scheme" 25 | echo " colorschemes -p Preview color scheme" 26 | echo "flags:" 27 | echo " -l Find only light color schemes" 28 | echo " -d Find only dark color schemes" 29 | } 30 | 31 | _isdark() { 32 | local darkness 33 | # shellcheck disable=SC2046 34 | set -- $(_hex2rgb "$1") # Reset args to RGB values from hex color 35 | darkness="$(echo "0.2126 * $1 + 0.7152 * $2 + 0.0722 * $3" | bc)" 36 | test "${darkness%.*}" -le 40 37 | } 38 | 39 | _hex2rgb() { 40 | printf "ibase=16; %s\n%s\n%s\n" \ 41 | "$(printf "%s" "$1" | cut -c1-2)" \ 42 | "$(printf "%s" "$1" | cut -c3-4)" \ 43 | "$(printf "%s" "$1" | cut -c5-6)" | 44 | bc 45 | } 46 | 47 | _preview_color() { 48 | local name hex bg fg reset 49 | name="$1" 50 | hex="$2" 51 | # shellcheck disable=SC2046 52 | set -- $(_hex2rgb "$hex") # Reset args to RGB values from hex color 53 | fg="\033[38;2;${1};${2};${3}m" 54 | bg="\033[48;2;${1};${2};${3}m" 55 | reset="\033[0m" 56 | 57 | # Output colored text 58 | printf "${bg} ${reset} %12s %s rgb(%3s,%3s,%3s)" "$name" "#${hex}" "$1" "$2" "$3" 59 | printf " ${fg}%s${reset} ${bg}%s${reset}\n" foreground background 60 | } 61 | 62 | _preview() { 63 | local jsonfile filter colorfilter key 64 | jsonfile="$COLOR_SCHEMES_HOME/vhs/${1}.json" 65 | if [ ! -f "$jsonfile" ]; then 66 | echo "No preview available for '$*'." 67 | return 1 68 | fi 69 | 70 | colorfilter='ascii_upcase | sub("#"; "")' 71 | filter='to_entries.[] | select(.key != "name") | .key' 72 | for key in $(jq -r "$filter" "$jsonfile"); do 73 | _preview_color "$key" "$(jq -r ".$key | $colorfilter" "$jsonfile")" 74 | done 75 | } 76 | 77 | _list() { 78 | local jsonfile bg 79 | for jsonfile in "$COLOR_SCHEMES_HOME"/vhs/*.json; do 80 | if [ -n "$1" ]; then 81 | bg="$(jq -r '.background | ascii_upcase | sub("#"; "")' "$jsonfile")" 82 | if [ "$1" = dark ]; then 83 | _isdark "$bg" || continue 84 | elif [ "$1" = light ]; then 85 | ! _isdark "$bg" || continue 86 | fi 87 | fi 88 | 89 | jsonfile="${jsonfile##*/}" 90 | echo "${jsonfile%.*}" 91 | done 92 | } 93 | 94 | main() { 95 | local optspec opt OPTARG OPTIND jsonfile selection theme_type fzf_opts 96 | 97 | optspec=":hldp:" 98 | while getopts "$optspec" opt; do 99 | case "$opt" in 100 | h) _usage; return 0 ;; 101 | l) theme_type="light" ;; 102 | d) theme_type="dark" ;; 103 | p) _preview "${OPTARG}"; return 0 ;; 104 | ?) echo >&2 "colorschemes: Invalid option: -${OPTARG}."; exit 1 ;; 105 | esac 106 | done 107 | shift $((OPTIND-1)) 108 | 109 | # Wish I knew how to set the preview dynamically based on the theme... 110 | if [ -n "$theme_type" ]; then 111 | if [ "$theme_type" = light ]; then 112 | fzf_opts="--color=${theme_type},preview-fg:0,preview-bg:255" 113 | elif [ "$theme_type" = dark ]; then 114 | fzf_opts="--color=${theme_type},preview-fg:255,preview-bg:0" 115 | fi 116 | fi 117 | 118 | selection="$(_list "$theme_type" | fzf $fzf_opts --layout=reverse-list --preview="$THIS -p {}" --query="$*")" 119 | # shellcheck disable=SC2181 120 | if [ "$?" -eq 0 ]; then 121 | echo "Selected Theme: $selection" 122 | _preview "$selection" 123 | fi 124 | } 125 | 126 | # Pre-reqs 127 | if ! command -v jq >/dev/null 2>&1; then 128 | echo >&2 "colorschemes: Expecting 'jq'." 129 | echo >&2 "Please use your system's package manager to install (eg: brew install jq)." 130 | return 1 131 | fi 132 | _clone_color_schemes 133 | 134 | # Run colorschemes 135 | main "$@" 136 | -------------------------------------------------------------------------------- /bin/fishy/argparse.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Helper to check if a string is nil or empty 4 | local function isempty(s) 5 | return s == nil or s == "" 6 | end 7 | 8 | local function argparse(args) 9 | -- Parse the spec 10 | local specs = {} 11 | local i = 1 12 | 13 | -- Collect specs until we hit '--' 14 | while args[i] and args[i] ~= "--" do 15 | local spec = args[i] 16 | local short, long, takes_value = nil, nil, false 17 | 18 | -- Check for '=' at the end (takes value) 19 | if spec:sub(-1) == '=' then 20 | takes_value = true 21 | spec = spec:sub(1, -2) 22 | end 23 | 24 | -- Split short/long 25 | local slash = spec:find('/') 26 | if slash then 27 | short = spec:sub(1, slash-1) 28 | long = spec:sub(slash+1) 29 | elseif #spec == 1 then 30 | short = spec 31 | else 32 | long = spec 33 | end 34 | 35 | -- Check for valid spec 36 | if isempty(short) and isempty(long) then 37 | error("argparse: An option spec must have at least a short or a long flag") 38 | end 39 | 40 | table.insert(specs, {short=short, long=long, takes_value=takes_value}) 41 | i = i + 1 42 | end 43 | 44 | -- Check for '--' separator 45 | if args[i] ~= "--" then 46 | error("argparse: Missing -- separator") 47 | end 48 | 49 | -- Skip '--' 50 | i = i + 1 51 | 52 | -- Build lookup tables 53 | local short_map, long_map = {}, {} 54 | for _, s in ipairs(specs) do 55 | if s.short then short_map[s.short] = s end 56 | if s.long then long_map[s.long] = s end 57 | end 58 | 59 | -- Parse arguments 60 | local results = {} 61 | local positionals = {} 62 | while i <= #args do 63 | local arg = args[i] 64 | if arg:sub(1,2) == "--" then 65 | local name = arg:sub(3) 66 | local spec = long_map[name] 67 | if spec then 68 | local key = not isempty(spec.long) and spec.long or spec.short 69 | if spec.takes_value then 70 | i = i + 1 71 | results[key] = args[i] 72 | else 73 | results[key] = true 74 | end 75 | else 76 | table.insert(positionals, arg) 77 | end 78 | elseif arg:sub(1,1) == "-" and #arg > 1 then 79 | local name = arg:sub(2,2) 80 | local spec = short_map[name] 81 | if spec then 82 | local key = not isempty(spec.long) and spec.long or spec.short 83 | if spec.takes_value then 84 | i = i + 1 85 | results[key] = args[i] 86 | else 87 | results[key] = true 88 | end 89 | else 90 | table.insert(positionals, arg) 91 | end 92 | else 93 | table.insert(positionals, arg) 94 | end 95 | i = i + 1 96 | end 97 | 98 | -- Build result table 99 | local flags = {} 100 | for _, spec in ipairs(specs) do 101 | local key = not isempty(spec.long) and spec.long or spec.short 102 | local value = results[key] 103 | if value ~= nil then 104 | if spec.long then flags[spec.long] = value end 105 | if spec.short then flags[spec.short] = value end 106 | end 107 | end 108 | 109 | return { 110 | flags = flags, 111 | positionals = positionals, 112 | specs = specs, 113 | } 114 | end 115 | 116 | -- If run as a script 117 | if debug.getinfo(1, "S").short_src == arg[0] then 118 | local ok, parsed = pcall(argparse, {...}) 119 | 120 | if not ok then 121 | local err = parsed:match(":%s(.+)$") or parsed 122 | io.stderr:write(err, "\n") 123 | os.exit(1) 124 | end 125 | 126 | -- Unset flag vars 127 | -- for var in $(compgen -v _flag_); do unset "$var"; done 128 | -- for var in ${(k)parameters}; do 129 | -- [[ $var == _flag_* ]] && unset $var 130 | -- done 131 | 132 | -- Print flag variables as shell assignments 133 | for _, spec in ipairs(parsed.specs) do 134 | local key = not isempty(spec.long) and spec.long or spec.short 135 | local value = parsed.flags[key] 136 | if value ~= nil then 137 | if spec.long then 138 | io.write("_flag_", spec.long, "=") 139 | if type(value) == "boolean" then 140 | io.write(value and "1" or "0", "\n") 141 | else 142 | io.write(string.format("%q", tostring(value)), "\n") 143 | end 144 | end 145 | if spec.short then 146 | io.write("_flag_", spec.short, "=") 147 | if type(value) == "boolean" then 148 | io.write(value and "1" or "0", "\n") 149 | else 150 | io.write(string.format("%q", tostring(value)), "\n") 151 | end 152 | end 153 | end 154 | end 155 | 156 | -- Print positionals as a set -- statement 157 | io.write("set --") 158 | for _, v in ipairs(parsed.positionals) do 159 | io.write(" ", string.format("%q", tostring(v))) 160 | end 161 | io.write("\n") 162 | end 163 | 164 | return argparse 165 | --------------------------------------------------------------------------------