├── .config ├── .color_mode ├── bash │ ├── .bash_profile │ ├── .bashrc │ └── .profile ├── composer │ └── composer.json ├── efm-langserver │ └── config.yaml ├── env │ └── .env.example ├── fish │ ├── aliases.fish │ ├── config.fish │ ├── fish_plugins │ ├── fish_prompt.fish │ ├── functions.fish │ └── fzf.fish ├── git │ ├── .gitconfig │ └── .gitignore_global ├── hammerspoon │ ├── apple-music-spotify-redirect.lua │ ├── init.lua │ ├── keymaps.lua │ ├── windows.lua │ └── zoom-killer.lua ├── mackup │ ├── .mackup.cfg │ └── .mackup │ │ └── my-files.cfg ├── mcphub │ └── servers.json ├── mise │ └── config.toml ├── nvim │ ├── .luarc.json │ ├── after │ │ └── queries │ │ │ └── html │ │ │ └── highlights.scm │ ├── ftplugin │ │ └── java.lua │ ├── init.lua │ ├── lazy-lock.json │ ├── lua │ │ ├── config │ │ │ ├── autocmds.lua │ │ │ ├── commands.lua │ │ │ ├── functions.lua │ │ │ ├── keymaps.lua │ │ │ ├── lazy.lua │ │ │ ├── options.lua │ │ │ └── util.lua │ │ ├── plugins │ │ │ ├── coding.lua │ │ │ ├── colorscheme.lua │ │ │ ├── custom │ │ │ │ └── spinner.lua │ │ │ ├── editor.lua │ │ │ ├── heirline │ │ │ │ ├── init.lua │ │ │ │ ├── statuscolumn.lua │ │ │ │ ├── statusline.lua │ │ │ │ └── winbar.lua │ │ │ ├── lsp.lua │ │ │ ├── treesitter.lua │ │ │ ├── ui.lua │ │ │ └── utilities.lua │ │ └── util │ │ │ ├── init.lua │ │ │ └── marks.lua │ └── static │ │ └── neovim.cat ├── rclone │ └── filter_list.txt ├── snippets │ ├── global.json │ ├── lua.json │ ├── package.json │ ├── python.json │ └── ruby.json ├── ssh │ └── config ├── vim │ ├── .vimrc │ ├── .vimrc.plugins │ ├── custom_snippets │ │ ├── blade.snippets │ │ ├── eruby.snippets │ │ ├── html.snippets │ │ ├── php.snippets │ │ ├── python.snippets │ │ └── ruby.snippets │ └── minimal.vim ├── vivid │ ├── onedark.yml │ └── onelight.yml └── wezterm │ ├── colors.lua │ ├── keys.lua │ ├── links.lua │ ├── tabline.lua │ ├── wezterm.lua │ └── workspaces.lua ├── .github └── workflows │ └── build.yml ├── .gitignore ├── README.md ├── Rakefile ├── bin ├── cloud-backup ├── color-mode ├── color-mode-notify ├── finance-output ├── reckon-import └── srt2txt ├── commands ├── clean_up.rb ├── color_mode.py ├── macos ├── make_separator.rb ├── seticons.py └── ssh.rb ├── dotbot.conf.yaml ├── dotbot_uninstall ├── misc ├── LaunchAgents │ ├── oli.cloud-backup.plist │ ├── oli.color-mode-notify.plist │ └── oli.finance-output.plist └── packages │ ├── app_store.txt │ ├── brew_cask.txt │ ├── brew_packages.txt │ ├── brew_taps.txt │ ├── npm_packages.txt │ ├── python_pip.txt │ ├── ruby_gems.txt │ └── rust_cargo.txt ├── stylua.toml ├── tasks ├── apps.rake ├── files.rake ├── packages.rake ├── system.rake ├── tests.rake ├── utils.rb └── work.rb └── tests └── stubs ├── .mackup.cfg ├── app_store.txt ├── brew_cask.txt ├── brew_packages.txt ├── brew_taps.txt ├── python_pip.txt └── ruby_gems.txt /.config/.color_mode: -------------------------------------------------------------------------------- 1 | dark 2 | -------------------------------------------------------------------------------- /.config/bash/.bash_profile: -------------------------------------------------------------------------------- 1 | # Load the shell dotfiles, and then some: 2 | # for file in ~/.{aliases,functions,path,env}; do 3 | # [ -r "${file}" ] && [ -f "${file}" ] && source "${file}" 4 | # done; 5 | 6 | # Load our env file - Even allow for variable expansion 7 | # As per: https://gist.github.com/mihow/9c7f559807069a03e302605691f85572#gistcomment-3928443 8 | # if [ -f .env ]; then 9 | # export $(echo $(cat .env | sed 's/#.*//g'| xargs) | envsubst) 10 | # fi 11 | 12 | # Load the z binary. 13 | # . `brew --prefix`/etc/profile.d/z.sh 14 | 15 | # Load bash completion 16 | # if [ -f $(brew --prefix)/etc/bash_completion ]; then 17 | # . $(brew --prefix)/etc/bash_completion 18 | # fi 19 | 20 | # . "$HOME/.cargo/env" 21 | -------------------------------------------------------------------------------- /.config/bash/.bashrc: -------------------------------------------------------------------------------- 1 | # [ -n "${PS1}" ] && source ~/.bash_profile; 2 | # . "$HOME/.cargo/env" 3 | -------------------------------------------------------------------------------- /.config/bash/.profile: -------------------------------------------------------------------------------- 1 | . "$HOME/.cargo/env" 2 | -------------------------------------------------------------------------------- /.config/composer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "friendsofphp/php-cs-fixer": "^2.0", 4 | "laravel/valet": "^2.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.config/efm-langserver/config.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | log-file: "/Users/Oli/.local/state/nvim/efm.log" 3 | log-level: 1 4 | root-markers: 5 | - .git/ 6 | - package.json 7 | 8 | tools: 9 | eslint: &eslint 10 | lint-command: "eslint_d -f unix --stdin" 11 | lint-ignore-exit-code: true 12 | lint-stdin: true 13 | 14 | google-java-format: &google-java-format 15 | format-command: "google-java-format -" 16 | format-stdin: true 17 | 18 | fish: &fish 19 | format-command: "fish_indent" 20 | format-stdin: true 21 | 22 | html-prettier: &html-prettier 23 | format-command: "prettier ${--tab-width:tabWidth} ${--single-quote:singleQuote} --parser html" 24 | format-stdin: true 25 | 26 | isort: &isort 27 | format-command: "isort --stdout --filename ${INPUT} -" 28 | format-stdin: true 29 | 30 | php-cs-fixer: &php-cs-fixer 31 | format-command: "php-cs-fixer fix --quiet" 32 | format-stdin: true 33 | 34 | prettier: &prettier 35 | format-command: "prettier --stdin --stdin-filepath ${INPUT} ${--range-start:charStart} ${--range-end:charEnd} ${--tab-width:tabSize} ${--use-tabs:!insertSpaces}" 36 | format-stdin: true 37 | format-can-range: true 38 | root-markers: 39 | - .prettierrc 40 | - .prettierrc.json 41 | - .prettierrc.js 42 | - .prettierrc.yml 43 | - .prettierrc.yaml 44 | - .prettierrc.json5 45 | - .prettierrc.mjs 46 | - .prettierrc.cjs 47 | - .prettierrc.toml 48 | 49 | python-black: &python-black 50 | format-command: "black --no-color -q -" 51 | format-stdin: true 52 | 53 | shfmt: &shfmt 54 | format-command: "shfmt -filename ${INPUT} -" 55 | format-stdin: true 56 | 57 | stylua: &stylua 58 | format-command: "stylua --color Never -" 59 | format-stdin: true 60 | root-markers: 61 | - stylua.toml 62 | 63 | languages: 64 | css: 65 | - <<: *prettier 66 | fish: 67 | - <<: *fish 68 | html: 69 | - <<: *html-prettier 70 | java: 71 | - <<: *google-java-format 72 | javascript: 73 | - <<: *eslint 74 | - <<: *prettier 75 | javascriptreact: 76 | - <<: *eslint 77 | - <<: *prettier 78 | json: 79 | - <<: *prettier 80 | lua: 81 | - <<: *stylua 82 | markdown: 83 | - <<: *prettier 84 | php: 85 | - <<: *php-cs-fixer 86 | python: 87 | - <<: *isort 88 | - <<: *python-black 89 | scss: 90 | - <<: *prettier 91 | sh: 92 | - <<: *shfmt 93 | typescript: 94 | - <<: *eslint 95 | - <<: *prettier 96 | typescriptreact: 97 | - <<: *eslint 98 | - <<: *prettier 99 | vue: 100 | - <<: *eslint 101 | - <<: *prettier 102 | yaml: 103 | - <<: *prettier 104 | -------------------------------------------------------------------------------- /.config/env/.env.example: -------------------------------------------------------------------------------- 1 | HOME_DIR=/Users/Oli/ 2 | DOTFILES=/Users/Oli/.dotfiles 3 | -------------------------------------------------------------------------------- /.config/fish/aliases.fish: -------------------------------------------------------------------------------- 1 | # Docker 2 | alias dl='docker ps' 3 | alias dc='docker-compose' 4 | alias dv='docker volume ls' 5 | alias dce='docker-compose exec' 6 | alias dcs='docker-compose stop' 7 | alias dcd='docker-compose down' 8 | alias dcb='docker-compose build' 9 | alias dcu='docker-compose up -d' 10 | alias dlog='docker-compose logs -f' 11 | alias dx='docker system prune -a -f' 12 | alias dub='docker-compose up -d --build' 13 | alias dclear='docker rm -fv $(docker ps -aq)' 14 | alias dcud='docker-compose -f docker-compose.dev.yml up -d' 15 | alias dcsd='docker-compose -f docker-compose.dev.yml stop' 16 | alias dcup='docker-compose -f docker-compose.prod.yml up -d' 17 | alias dcsp='docker-compose -f docker-compose.prod.yml stop' 18 | 19 | # Dotfiles 20 | alias dot='dotfile_tasks' 21 | alias ed='nvim ~/.dotfiles' 22 | alias up='cd ~/.dotfiles && rake sync' 23 | alias backup='cd ~/.dotfiles && rake backup' 24 | alias clean='ruby ~/.dotfiles/commands/clean_up.rb' 25 | alias icons='python ~/.dotfiles/commands/seticons.py' 26 | alias bf='cd ~/.dotfiles && rake backup:files' 27 | alias cleanup='ruby ~/.dotfiles/commands/clean_up.rb ~/Downloads' 28 | 29 | # Fish 30 | alias fi='fisher install' 31 | alias fl='fisher list' 32 | alias fu='fisher update' 33 | alias fr='fisher remove' 34 | 35 | # Git 36 | alias lg='lazygit' 37 | alias ga='git add' 38 | alias gp='git pull' 39 | alias gaa='git add .' 40 | alias gst='git status' 41 | alias gc='git commit -m' 42 | alias gnb='git checkout -b' 43 | alias gpu='git push origin master' 44 | alias gdm='git checkout -b dev-master' 45 | alias nah='git reset --hard && git clean -df' 46 | alias gfix='git rm -r --cached . && git add .' 47 | 48 | # hledger 49 | alias hf='hledger -f $FINANCES/transactions.journal -f $FINANCES/forecast.journal --auto' 50 | alias hfs='hledger -f $FINANCES/transactions.journal -f $FINANCES/forecast.journal --forecast="this month".. --auto bal "^(ass|liab)" --tree -H -e "1 month"' 51 | alias hb='hledger -f $FINANCES/transactions.journal -f $FINANCES/forecast.journal bal -M --tree --budget expenses' 52 | alias hg='hledger-forecast generate -t $FINANCES/transactions.journal -f $FINANCES/forecast.csv -o $FINANCES/forecast.journal --force' 53 | alias hs='hledger-forecast summarize -f $FINANCES/forecast.csv' 54 | 55 | # Homebrew 56 | alias bl='brew list' 57 | alias br='brew remove' 58 | alias bu='brew update' 59 | alias bs='brew search' 60 | alias bi='brew install' 61 | alias bupg='brew upgrade && brew cleanup' 62 | 63 | # Mac 64 | alias code='open $argv -a "Visual Studio Code"' 65 | alias reloadapps="defaults write com.apple.dock ResetLaunchPad -bool true; killall Dock" 66 | 67 | # Misc 68 | alias ls='ls --color=auto' 69 | alias fk='fuck' # Overwrite mistakes 70 | alias fck='fuck' 71 | alias etxt='extract-text' 72 | alias wifi='wifi-password' 73 | alias div='print_divider' 74 | alias dm='color-mode dark' 75 | alias lm='color-mode light' 76 | alias essh='nvim ~/.ssh/config' 77 | alias chmodall='sudo chmod -R 0777' 78 | alias copyssh='pbcopy < ~/.ssh/$1' 79 | alias rk='pgrep kitty | xargs kill -SIGUSR1' 80 | alias mssh='ruby ~/.dotfiles/commands/ssh.rb' 81 | alias sep='ruby ~/.dotfiles/commands/make_separator.rb' 82 | 83 | # Neovim / Vim 84 | alias vi='nvim' 85 | alias vim='/opt/homebrew/bin/vim' 86 | alias nvu='cd ~/.dotfiles && rake update:neovim && prevd' 87 | 88 | # PHP 89 | alias art='php artisan' 90 | alias sail='vendor/bin/sail up' 91 | alias cu='composer update' 92 | alias ci='composer install' 93 | alias p='vendor/bin/phpunit' 94 | alias tm='vendor/bin/phpunit' 95 | alias sphp='brew-php-switcher' 96 | alias csu='composer self-update' 97 | alias phpunit='vendor/bin/phpunit' 98 | alias cda='composer dump-autoload' 99 | alias cgu='composer global update' 100 | alias cor='vendor/bin/codecept run' 101 | alias clearlogs='sudo echo -n -f >' 102 | alias pc='clear && vendor/bin/phpunit' 103 | alias tf='vendor/bin/phpunit --filter=' 104 | alias phplogs='sudo tail -f /usr/local/var/log/* /usr/local/var/log/nginx/* ~/.valet/Log/* /usr/local/opt/php71/var/log/*' 105 | 106 | # Python 107 | alias jup='jupyter notebook' 108 | alias pipb='pip freeze > ~/.dotfiles/PIP.txt' 109 | alias pipi='pip install -r ~/.dotfiles/PIP.txt' 110 | alias jupr="jupyter notebook --NotebookApp.allow_origin='https://colab.research.google.com' --port=9090 --no-browser" 111 | alias pypiu_test='rm -rf dist/* && python3 -m build && python3 -m twine upload --repository testpypi dist/*' 112 | alias pypiu='rm -rf dist/* && python3 -m build && python3 -m twine upload dist/*' 113 | 114 | # Rails 115 | alias r='bin/rails' 116 | alias rr='rails routes' 117 | alias rrg='rails routes | grep' 118 | alias rd='rails destroy' 119 | alias rc='rails console' 120 | alias rdb='rails dbconsole' 121 | alias rcs='rails console --sandbox' 122 | alias rs='rails server -p 3001' 123 | alias rsd='rails server --debugger' 124 | alias rsp='rails server --port' 125 | alias rsb='rails server --bind' 126 | alias rup='rails app:update' 127 | alias rds='rails db:setup' 128 | alias rdm='rails db:migrate' 129 | alias rdmr='rails db:migrate:redo' 130 | # alias rg='rails generate' 131 | alias rgm='rails generate model' 132 | alias rgc='rails generate controller' 133 | alias rgmi='rails generate migration' 134 | alias rtest='tail -f log/test.log' 135 | alias rdev='tail -f log/development.log' 136 | alias rprod='tail -f log/production.log' 137 | 138 | # Ruby 139 | alias rt='rake test' 140 | alias sb='~/.local/share/nvim/mason/packages/solargraph/bin/solargraph bundle' 141 | alias gb='gem build' 142 | alias ug='gem update --system && gem update' 143 | 144 | # Shell 145 | alias c='clear' 146 | alias tags='ctags -R' 147 | alias ea='nvim ~/.config/fish/aliases.fish' 148 | alias src='source ~/.config/fish/config.fish && fish_logo' 149 | alias reloaddns='dscacheutil -flushcache && sudo killall -HUP mDNSResponder' 150 | 151 | # Shell navigation 152 | alias ..='cd ..' 153 | alias bk='cd -' 154 | alias home='cd ~' 155 | alias ...='cd ../..' 156 | alias desk='cd ~/Desktop' 157 | alias ....='cd ../../..' 158 | alias .....='cd ../../../..' 159 | alias ze='zoxide edit' 160 | -------------------------------------------------------------------------------- /.config/fish/config.fish: -------------------------------------------------------------------------------- 1 | set -g fish_greeting "" 2 | 3 | # Variables 4 | set -x GPG_TTY (tty) 5 | set -x EDITOR nvim 6 | set -x DOTNET_CLI_TELEMETRY_OPTOUT 1 7 | set -x HOMEBREW_NO_ANALYTICS 1 8 | set -x GOPATH "$HOME/.go" 9 | 10 | # Paths 11 | fish_add_path -p /opt/homebrew/sbin /opt/homebrew/bin "$HOME/.cargo/bin" "$HOME/.dotfiles/bin" "$HOME/.local/share/nvim/mason/bin" "$HOME/.local/share/bob/nvim-bin" "$HOME/.local/bin" "$GOPATH/bin" 12 | 13 | set -gx macOS_Theme (string trim (cat ~/.color_mode)) 14 | switch $macOS_Theme 15 | case light 16 | source $HOME/.cache/nvim/onedarkpro_dotfiles/extras/fish/onedarkpro_onelight.fish 17 | case dark 18 | source $HOME/.cache/nvim/onedarkpro_dotfiles/extras/fish/onedarkpro_vaporwave.fish 19 | end 20 | 21 | source $HOME/.config/fish/fzf.fish 22 | source $HOME/.config/fish/aliases.fish 23 | source $HOME/.config/fish/functions.fish 24 | source $HOME/.config/fish/fish_prompt.fish 25 | 26 | if status is-interactive 27 | load_env_vars ~/.env 28 | zoxide init fish | source 29 | end 30 | -------------------------------------------------------------------------------- /.config/fish/fish_plugins: -------------------------------------------------------------------------------- 1 | jorgebucaran/fisher 2 | laughedelic/fish_logo 3 | joehillen/to-fish 4 | patrickf1/fzf.fish 5 | jorgebucaran/replay.fish 6 | joehillen/ev-fish 7 | -------------------------------------------------------------------------------- /.config/fish/fish_prompt.fish: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Fish Prompt 3 | # ----------------------------------------------------------------------------- 4 | function fish_prompt 5 | set -l arrow_color (test $status -eq 0; and echo $fish_color_prefix ; or echo $fish_color_error) 6 | 7 | set -l branch (git rev-parse --abbrev-ref HEAD 2>/dev/null) 8 | set -l dirty (git status --porcelain --ignore-submodules=dirty 2>/dev/null) 9 | 10 | set -l repo_name (basename $PWD) 11 | 12 | printf "%s%s%s" (set_color --bold $fish_color_keyword) $repo_name (set_color $fish_color_normal) 13 | 14 | if test -n "$branch" 15 | printf " on %s %s" (set_color $fish_color_valid_path) $branch 16 | if test -n "$dirty" 17 | printf " %s✘" (set_color $fish_color_option) 18 | end 19 | printf "%s" (set_color normal) 20 | end 21 | 22 | printf " %s󰁔 " (set_color $arrow_color) 23 | end 24 | -------------------------------------------------------------------------------- /.config/fish/functions.fish: -------------------------------------------------------------------------------- 1 | function mkd -d "Create a directory and set CWD" 2 | command mkdir $argv 3 | if test $status = 0 4 | switch $argv[(count $argv)] 5 | case '-*' 6 | 7 | case '*' 8 | cd $argv[(count $argv)] 9 | return 10 | end 11 | end 12 | end 13 | 14 | function load_env_vars -d "Load variables in a .env file" 15 | for i in (cat $argv) 16 | set arr (echo $i |tr = \n) 17 | set -gx $arr[1] $arr[2] 18 | end 19 | end 20 | 21 | function now -d "Print the current date and time" 22 | date "+%Y-%m-%d %H:%M:%S" 23 | end 24 | 25 | function nv -d "Launch Neovim" 26 | if count $argv >/dev/null 27 | nvim $argv 28 | else 29 | nvim 30 | end 31 | end 32 | 33 | function o -d Open 34 | if count $argv >/dev/null 35 | open $argv 36 | else 37 | open . 38 | end 39 | end 40 | 41 | function which 42 | type -p $argv 43 | end 44 | 45 | function italic -d "Test if italic text is working" 46 | echo -e "\e[3mThis text should be in italics\e[23m" 47 | end 48 | 49 | function col -d "Test if color is working" 50 | curl -s https://gist.githubusercontent.com/HaleTom/89ffe32783f89f403bba96bd7bcd1263/raw/e50a28ec54188d2413518788de6c6367ffcea4f7/print256colours.sh | bash 51 | end 52 | -------------------------------------------------------------------------------- /.config/fish/fzf.fish: -------------------------------------------------------------------------------- 1 | set -x FZF_DEFAULT_OPTS 2 | set -a FZF_DEFAULT_OPTS --height=70% 3 | # set -a FZF_DEFAULT_OPTS --multi 4 | set -a FZF_DEFAULT_OPTS --border 5 | # set -a FZF_DEFAULT_OPTS --preview-window=:hidden 6 | set -a FZF_DEFAULT_OPTS --history=$HOME/.fzf_history 7 | # set -a FZF_DEFAULT_OPTS --bind=ctrl-t:top 8 | set -a FZF_DEFAULT_OPTS --marker='✓' 9 | 10 | if [ "$macOS_Theme" = light ] 11 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color bg:'#fafafa',bg+:'#fafafa',fg:'#6a6a6a' 12 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color query:'#6a6a6a',fg+:'#9a77cf',hl:'#118dc3',hl+:'#118dc3' 13 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color preview-bg:'#fafafa',preview-fg:'#6a6a6a' 14 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color border:'#6a6a6a',gutter:'#fafafa',header:'#1da912' 15 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color info:'#1da912',marker:'#e05661',pointer:'#eea825' 16 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color prompt:'#9a77cf',spinner:'#118dc3',separator:'#6a6a6a' 17 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color query:regular 18 | else if [ "$macOS_Theme" = dark ] 19 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color bg:'#282c34',bg+:'#282c34',fg:'#abb2bf' 20 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color query:'#abb2bf',fg+:'#c678dd',hl:'#61afef',hl+:'#61afef' 21 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color preview-bg:'#282c34',preview-fg:'#abb2bf' 22 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color border:'#abb2bf',gutter:'#282c34',header:'#98c379' 23 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color info:'#98c379',marker:'#e06c7f',pointer:'#e5c07b' 24 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color prompt:'#c678dd',spinner:'#61afef',separator:'#abb2bf' 25 | set FZF_DEFAULT_OPTS $FZF_DEFAULT_OPTS --color query:regular 26 | end 27 | 28 | set -g fzf_fd_opts --hidden --no-ignore --exclude=.git --exclude=Library 29 | -------------------------------------------------------------------------------- /.config/git/.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Oli Morris 3 | email = olimorris@users.noreply.github.com 4 | [alias] 5 | pr = "!f() { git fetch -fu ${2:-origin} refs/pull/$1/head:pr/$1 && git checkout pr/$1; }; f" 6 | [credential] 7 | helper = /usr/local/share/gcm-core/git-credential-manager 8 | [credential "https://github.com"] 9 | useHttpPath = true 10 | [core] 11 | excludesfile = /Users/Oli/.gitignore_global 12 | editor = nvim 13 | [init] 14 | defaultBranch = main 15 | -------------------------------------------------------------------------------- /.config/git/.gitignore_global: -------------------------------------------------------------------------------- 1 | # Mac OS X hidden files 2 | .DS_Store 3 | .DS_Store? 4 | ._* 5 | .Spotlight-V100 6 | .Trashes 7 | ehthumbs.db 8 | Thumbs.db 9 | 10 | # Vim swap files 11 | .*.sw? 12 | 13 | # CTags files 14 | tags 15 | 16 | # PhpStorm 17 | .idea 18 | 19 | # Sublime Text 20 | *.sublime-project 21 | 22 | # Node 23 | node_modules 24 | npm-debug.log -------------------------------------------------------------------------------- /.config/hammerspoon/apple-music-spotify-redirect.lua: -------------------------------------------------------------------------------- 1 | local toggle_spotify_playpause = [[ 2 | tell application "Spotify" 3 | playpause 4 | end tell 5 | ]] 6 | -- no, Apple, I didn't mean to open Apple Music instead of Spotify 7 | local function open_spotify_instead_of_apple_music(app_name, event_type, app_obj) 8 | if event_type == hs.application.watcher.launched and app_name == "Music" then 9 | -- kill Apple Music 10 | app_obj:kill() 11 | -- open Spotify instead 12 | hs.application.open("Spotify") 13 | hs.osascript.applescript(toggle_spotify_playpause) 14 | end 15 | end 16 | 17 | local apple_music_spotify_redirector = hs.application.watcher.new(open_spotify_instead_of_apple_music) 18 | apple_music_spotify_redirector:start() 19 | -------------------------------------------------------------------------------- /.config/hammerspoon/init.lua: -------------------------------------------------------------------------------- 1 | -- [[ Global Settings ]] ------------------------------------------------------ 2 | Hyper = { "cmd", "alt", "ctrl" } 3 | 4 | hs.automaticallyCheckForUpdates(true) 5 | hs.menuIcon(true) 6 | hs.dockIcon(false) 7 | 8 | -- [[ Reload Configuration ]] ------------------------------------------------- 9 | local notify = function() 10 | hs.notify.new({ title = "Hammerspoon", informativeText = "Config loaded" }):send() 11 | end 12 | hs.hotkey.bind(Hyper, "0", function() 13 | notify() 14 | hs.reload() 15 | end) 16 | 17 | local function reload_config(files) 18 | local doReload = false 19 | for _, file in pairs(files) do 20 | if file:sub(-4) == ".lua" then 21 | doReload = true 22 | end 23 | end 24 | if doReload then 25 | notify() 26 | hs.reload() 27 | end 28 | end 29 | 30 | -- Reload the config every time it changes 31 | hs.pathwatcher.new(os.getenv("HOME") .. "/.dotfiles/.config/hammerspoon/", reload_config):start() 32 | 33 | -- [[ Modules ]] -------------------------------------------------------------- 34 | require("keymaps") 35 | require("zoom-killer") 36 | require("apple-music-spotify-redirect") 37 | require("windows") 38 | -------------------------------------------------------------------------------- /.config/hammerspoon/keymaps.lua: -------------------------------------------------------------------------------- 1 | local hyper = Hyper 2 | local host = require("hs.host") 3 | local name = host.localizedName() 4 | local on_personal = (name:find("AAGB") == nil) 5 | 6 | ------------------------------- APP LAUNCH/TOGGLE ------------------------------ 7 | --[[ 8 | The list of keys and apps which enable launching and toggling 9 | Some apps have a different process name to their name on disk. To address 10 | this, a table can be passed which contains the app name followed by the filename 11 | ]] 12 | local apps = { 13 | e = "Microsoft Excel", 14 | f = "Finder", 15 | g = "Google Chrome", 16 | n = "Bear", -- Notes 17 | o = "Notion", -- Life OS 18 | r = "Reminders", 19 | t = "WezTerm", -- Terminal 20 | } 21 | 22 | if on_personal then 23 | apps.c = "Code" -- VS Code 24 | apps.b = "Safari" -- Browser 25 | apps.p = "1Password" 26 | apps.w = "Microsoft Word" 27 | else 28 | apps.c = "Teams" -- Chat 29 | apps.b = "Google Chrome" -- Browser 30 | apps.m = "Microsoft Outlook" -- Mail 31 | apps.p = "Microsoft PowerPoint" 32 | apps.w = "Windows App" 33 | apps.z = "Zoom" 34 | end 35 | 36 | local LaunchOrToggle = function(key, app_name, app_filename) 37 | hs.hotkey.bind(hyper, key, function() 38 | local app = hs.application.find(app_name) 39 | -- Toggle - show 40 | local awin = nil 41 | if app then 42 | awin = app:mainWindow() 43 | end 44 | -- Toggle - hide 45 | if awin and app and app:isFrontmost() then 46 | app:hide() 47 | else 48 | -- Launch 49 | if app_filename then 50 | return hs.application.launchOrFocus(app_filename) 51 | end 52 | 53 | app = hs.application.find(app_name) 54 | 55 | hs.application.launchOrFocus(app_name) 56 | app.setFrontmost(app) 57 | app.activate(app) 58 | end 59 | end) 60 | end 61 | 62 | for key, app_name in pairs(apps) do 63 | if type(app_name) == "table" then 64 | LaunchOrToggle(key, app_name[1], app_name[2]) 65 | else 66 | LaunchOrToggle(key, app_name) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /.config/hammerspoon/windows.lua: -------------------------------------------------------------------------------- 1 | local window = require("hs.window") 2 | 3 | -- [[ Key Bindings ]] --------------------------------------------------------- 4 | local win_keys = { "alt" } 5 | local win_shift_keys = { "alt", "shift" } 6 | 7 | -- [[ Settings ] -------------------------------------------------------------- 8 | window.animationDuration = 0.0 9 | hs.grid.setGrid("60x20") 10 | hs.grid.setMargins("0x0") 11 | 12 | -- [[ Constants ]] ------------------------------------------------------------ 13 | DISPLAYS = { 14 | internal = "Built-in Retina Display", 15 | external = "DELL U3818DW", 16 | } 17 | 18 | POSITIONS = { 19 | full = "0,0 60x20", 20 | halves = { 21 | left = "0,0 30x20", 22 | right = "30,0 30x20", 23 | }, 24 | thirds = { 25 | left = "0,0 20x20", 26 | center = "20,0 20x20", 27 | right = "40,0 20x20", 28 | }, 29 | twoThirds = { 30 | left = "0,0 40x20", 31 | right = "20,0 40x20", 32 | }, 33 | center = { 34 | large = "6,1 48x18", 35 | medium = "12,1 36x18", 36 | small = "16,2 28x16", 37 | }, 38 | } 39 | 40 | -- [[ Window Management ]] ----------------------------------------------------- 41 | 42 | -- Maximize the focused window 43 | hs.hotkey.bind(win_keys, "M", function() 44 | local win = hs.window.focusedWindow() 45 | if win then 46 | win:maximize() 47 | end 48 | end) 49 | 50 | -- Halves 51 | hs.hotkey.bind(Hyper, "Left", function() 52 | local win = hs.window.focusedWindow() 53 | if win then 54 | hs.grid.set(win, POSITIONS.halves.left) 55 | end 56 | end) 57 | hs.hotkey.bind(Hyper, "Right", function() 58 | local win = hs.window.focusedWindow() 59 | if win then 60 | hs.grid.set(win, POSITIONS.halves.right) 61 | end 62 | end) 63 | 64 | -- Thirds 65 | hs.hotkey.bind(Hyper, "1", function() 66 | local win = hs.window.focusedWindow() 67 | if win then 68 | hs.grid.set(win, POSITIONS.thirds.left) 69 | end 70 | end) 71 | hs.hotkey.bind(Hyper, "2", function() 72 | local win = hs.window.focusedWindow() 73 | if win then 74 | hs.grid.set(win, POSITIONS.thirds.center) 75 | end 76 | end) 77 | hs.hotkey.bind(Hyper, "3", function() 78 | local win = hs.window.focusedWindow() 79 | if win then 80 | hs.grid.set(win, POSITIONS.thirds.right) 81 | end 82 | end) 83 | 84 | -- Two-Thirds 85 | hs.hotkey.bind(win_shift_keys, "Left", function() 86 | local win = hs.window.focusedWindow() 87 | if win then 88 | hs.grid.set(win, POSITIONS.twoThirds.left) 89 | end 90 | end) 91 | hs.hotkey.bind(win_shift_keys, "Right", function() 92 | local win = hs.window.focusedWindow() 93 | if win then 94 | hs.grid.set(win, POSITIONS.twoThirds.right) 95 | end 96 | end) 97 | 98 | -- Center the application 99 | hs.hotkey.bind(win_keys, "C", function() 100 | local win = hs.window.focusedWindow() 101 | if win then 102 | local winFrame = win:frame() 103 | local screenFrame = win:screen():frame() 104 | 105 | local newX = screenFrame.x + (screenFrame.w - winFrame.w) / 2 106 | local newY = screenFrame.y + (screenFrame.h - winFrame.h) / 2 107 | 108 | win:setTopLeft({ x = newX, y = newY }) 109 | end 110 | end) 111 | 112 | -- Move focused window to next screen 113 | local function moveAndResizeWindow(getDestinationScreenFn) 114 | local win = hs.window.focusedWindow() 115 | if win then 116 | local destinationScreen = getDestinationScreenFn(win:screen()) 117 | if destinationScreen then 118 | win:moveToScreen(destinationScreen) 119 | 120 | -- After moving, get the screen the window is now on 121 | local newScreenOfWindow = win:screen() 122 | 123 | if newScreenOfWindow:name() == DISPLAYS.internal then 124 | hs.grid.set(win, POSITIONS.full, newScreenOfWindow) 125 | else 126 | -- If not internal, set to half. Adjust POSITIONS.halves.left if needed. 127 | hs.grid.set(win, POSITIONS.halves.left, newScreenOfWindow) 128 | end 129 | end 130 | end 131 | end 132 | hs.hotkey.bind(Hyper, "Up", function() 133 | moveAndResizeWindow(function(currentScreen) 134 | return currentScreen:next() 135 | end) 136 | end) 137 | hs.hotkey.bind(Hyper, "Down", function() 138 | moveAndResizeWindow(function(currentScreen) 139 | return currentScreen:previous() 140 | end) 141 | end) 142 | -------------------------------------------------------------------------------- /.config/hammerspoon/zoom-killer.lua: -------------------------------------------------------------------------------- 1 | -- no, Zoom, I don't want you to spy on my mic in the background 2 | local function kill_zoom(app_name, event_type, app) 3 | if 4 | ( 5 | event_type == hs.application.watcher.hidden 6 | or event_type == hs.application.watcher.terminated 7 | or event_type == hs.application.watcher.deactivated 8 | ) 9 | and app_name == "zoom.us" 10 | and #app:allWindows() == 0 11 | then 12 | -- make Zoom kill itself when I leave a meeting 13 | app:kill() 14 | end 15 | end 16 | 17 | local zoom_killer = hs.application.watcher.new(kill_zoom) 18 | zoom_killer:start() 19 | -------------------------------------------------------------------------------- /.config/mackup/.mackup.cfg: -------------------------------------------------------------------------------- 1 | [storage] 2 | engine = file_system 3 | path = .dotfiles 4 | directory = misc/mackup 5 | 6 | [applications_to_sync] 7 | my-files 8 | ColorSlurp 9 | Dash 10 | Fantastical 11 | LittleSnitch 12 | Spotify 13 | VSCode 14 | -------------------------------------------------------------------------------- /.config/mackup/.mackup/my-files.cfg: -------------------------------------------------------------------------------- 1 | [application] 2 | name = My personal synced files and dirs 3 | 4 | [configuration_files] 5 | 6 | Library/Preferences/pl.maketheweb.cleanshotx.plist 7 | Library/Preferences/pl.maketheweb.pixelsnap2.plist 8 | Library/Application Support/org.yanex.marta/Themes/Oli_Dark.theme 9 | Library/Application Support/org.yanex.marta/Themes/Oli_Light.theme 10 | -------------------------------------------------------------------------------- /.config/mcphub/servers.json: -------------------------------------------------------------------------------- 1 | { 2 | "nativeMCPServers": [], 3 | "mcpServers": { 4 | "github.com/modelcontextprotocol/servers/tree/main/src/memory": { 5 | "args": ["-y", "@modelcontextprotocol/server-memory"], 6 | "command": "npx" 7 | }, 8 | "github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking": { 9 | "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"], 10 | "command": "npx" 11 | }, 12 | "github.com/tavily-ai/tavily-mcp": { 13 | "args": ["-y", "tavily-mcp@0.1.4"], 14 | "env": { 15 | "TAVILY_API_KEY": "$:op read op://personal/Tavily_API/credential --no-newline" 16 | }, 17 | "command": "npx" 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /.config/mise/config.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | lua = '5.4' 3 | nodejs = '22' 4 | python = ['3', '2.7.18'] 5 | ruby = '3.2.0' 6 | -------------------------------------------------------------------------------- /.config/nvim/.luarc.json: -------------------------------------------------------------------------------- 1 | { 2 | "diagnostics.globals": [ 3 | "vim" 4 | ] 5 | } -------------------------------------------------------------------------------- /.config/nvim/after/queries/html/highlights.scm: -------------------------------------------------------------------------------- 1 | ;; extends 2 | 3 | ((attribute 4 | (attribute_name) @att_name (#eq? @att_name "class") 5 | (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…"))) 6 | -------------------------------------------------------------------------------- /.config/nvim/ftplugin/java.lua: -------------------------------------------------------------------------------- 1 | -- Taken from: https://github.com/mfussenegger/nvim-jdtls#configuration-quickstart 2 | -- May need the verbose configuration at some point 3 | local config = { 4 | cmd = { os.getenv("HOME_DIR") .. ".local/share/nvim/mason/bin/jdtls" }, 5 | root_dir = vim.fs.dirname(vim.fs.find({ "gradlew", ".git", "mvnw" }, { upward = true })[1]), 6 | } 7 | 8 | -- Add additional support for Java plugins (which we've installed with mason-null-ls) 9 | -- TODO: Need to test if these work 10 | local bundles = { 11 | os.getenv("HOME_DIR") 12 | .. ".local/share/nvim/mason/packages/java-debug-adapter/extension/server/com.microsoft.java.debug.plugin-*.jar", 13 | } 14 | vim.list_extend( 15 | bundles, 16 | vim.split( 17 | vim.fn.glob(os.getenv("HOME_DIR") .. ".local/share/nvim/mason/packages/java-test/extension/server/*.jar", 1), 18 | "\n" 19 | ) 20 | ) 21 | config["init_options"] = { 22 | bundles = bundles, 23 | } 24 | 25 | config["on_attach"] = function(client, bufnr) 26 | -- With `hotcodereplace = 'auto' the debug adapter will try to apply code changes 27 | -- you make during a debug session immediately. 28 | -- Remove the option if you do not want that. 29 | -- You can use the `JdtHotcodeReplace` command to trigger it manually 30 | require("jdtls").setup_dap({ hotcodereplace = "auto" }) 31 | require("jdtls.setup").add_commands() 32 | 33 | if client.server_capabilities.documentSymbolProvider then 34 | require("nvim-navic").attach(client, bufnr) 35 | end 36 | end 37 | 38 | require("jdtls").start_or_attach(config) 39 | -------------------------------------------------------------------------------- /.config/nvim/init.lua: -------------------------------------------------------------------------------- 1 | require("config.lazy") 2 | -------------------------------------------------------------------------------- /.config/nvim/lazy-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "FixCursorHold.nvim": { "branch": "master", "commit": "1900f89dc17c603eec29960f57c00bd9ae696495" }, 3 | "VectorCode": { "branch": "main", "commit": "bd09cbd9943dad3b44baf85bfeb0d842fa59fa84" }, 4 | "aerial.nvim": { "branch": "master", "commit": "2e00d1d4248f08dddfceacb8d2996e51e13e00f6" }, 5 | "blink.cmp": { "branch": "main", "commit": "022521a8910a5543b0251b21c9e1a1e989745796" }, 6 | "codecompanion-history.nvim": { "branch": "main", "commit": "66943e78bcacaa27f4ac4a398df4016eb5413ecd" }, 7 | "conform.nvim": { "branch": "master", "commit": "6feb2f28f9a9385e401857b21eeac3c1b66dd628" }, 8 | "copilot.lua": { "branch": "master", "commit": "5f726c8e6bbcd7461ee0b870d4e6c8a973b55b64" }, 9 | "csvview.nvim": { "branch": "main", "commit": "c44d9ed0664de67f3ac333fe83cb187ab241f82a" }, 10 | "edgy.nvim": { "branch": "main", "commit": "7e8dedc39abebe40c289b8012cc89b11c69aa7a0" }, 11 | "fidget.nvim": { "branch": "main", "commit": "d9ba6b7bfe29b3119a610892af67602641da778e" }, 12 | "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" }, 13 | "gitsigns.nvim": { "branch": "main", "commit": "8bdaccdb897945a3c99c1ad8df94db0ddf5c8790" }, 14 | "guess-indent.nvim": { "branch": "main", "commit": "84a4987ff36798c2fc1169cbaff67960aed9776f" }, 15 | "heirline.nvim": { "branch": "master", "commit": "fae936abb5e0345b85c3a03ecf38525b0828b992" }, 16 | "img-clip.nvim": { "branch": "main", "commit": "08a02e14c8c0d42fa7a92c30a98fd04d6993b35d" }, 17 | "lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" }, 18 | "legendary.nvim": { "branch": "master", "commit": "6de819bc285eb8c420e49e82c21d5bb696b5a727" }, 19 | "lspkind.nvim": { "branch": "master", "commit": "d79a1c3299ad0ef94e255d045bed9fa26025dab6" }, 20 | "mason-lspconfig.nvim": { "branch": "main", "commit": "60eaff7a470b8e78ddff09d847d17a011f560759" }, 21 | "mason.nvim": { "branch": "main", "commit": "8024d64e1330b86044fed4c8494ef3dcd483a67c" }, 22 | "mcphub.nvim": { "branch": "main", "commit": "eb3e1c4c802bd7279fd1ed31fb17ff32da8542bb" }, 23 | "mini.diff": { "branch": "main", "commit": "f7bcd3cb4561f7d3a02ae9afafeda899c82f7108" }, 24 | "mini.test": { "branch": "main", "commit": "0701f48de3c6af1158b9111957ff956506124c3e" }, 25 | "namu.nvim": { "branch": "main", "commit": "a3a3d81d12b61a38f131253bcd3ce5e2c6599850" }, 26 | "neotest": { "branch": "master", "commit": "ef492755730e59e1d8122c461abbd086bee4c76b" }, 27 | "neotest-plenary": { "branch": "master", "commit": "3523adcf9ffaad1911960c5813b0136c1b63a2ec" }, 28 | "neotest-python": { "branch": "master", "commit": "34c9f6f3dc53fc462ea658b5071238037f95f7aa" }, 29 | "nvim-autopairs": { "branch": "master", "commit": "4d74e75913832866aa7de35e4202463ddf6efd1b" }, 30 | "nvim-bqf": { "branch": "main", "commit": "e20417d5e589e03eaaaadc4687904528500608be" }, 31 | "nvim-colorizer.lua": { "branch": "master", "commit": "517df88cf2afb36652830df2c655df2da416a0ae" }, 32 | "nvim-dap": { "branch": "master", "commit": "b0f983507e3702f073bfe1516846e58b56d4e42f" }, 33 | "nvim-dap-python": { "branch": "master", "commit": "261ce649d05bc455a29f9636dc03f8cdaa7e0e2c" }, 34 | "nvim-dap-ruby": { "branch": "main", "commit": "93cae460d1a9bced94cf7b45748351b6cddd4ab4" }, 35 | "nvim-dap-ui": { "branch": "master", "commit": "73a26abf4941aa27da59820fd6b028ebcdbcf932" }, 36 | "nvim-dap-virtual-text": { "branch": "master", "commit": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6" }, 37 | "nvim-lspconfig": { "branch": "master", "commit": "03bc581e05e81d33808b42b2d7e76d70adb3b595" }, 38 | "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, 39 | "nvim-surround": { "branch": "main", "commit": "8dd9150ca7eae5683660ea20cec86edcd5ca4046" }, 40 | "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, 41 | "nvim-treesitter-endwise": { "branch": "master", "commit": "d6cbb83307d516ec076d17c9a33d704ef626ee8c" }, 42 | "nvim-treesitter-textobjects": { "branch": "master", "commit": "0f051e9813a36481f48ca1f833897210dbcfffde" }, 43 | "nvim-ts-autotag": { "branch": "main", "commit": "a1d526af391f6aebb25a8795cbc05351ed3620b5" }, 44 | "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, 45 | "nvim-ufo": { "branch": "main", "commit": "3c7a3570e9c9dc198a2ad4491b0b0e51c4d4ba08" }, 46 | "nvim-web-devicons": { "branch": "master", "commit": "1fb58cca9aebbc4fd32b086cb413548ce132c127" }, 47 | "oil.nvim": { "branch": "master", "commit": "685cdb4ffa74473d75a1b97451f8654ceeab0f4a" }, 48 | "overseer.nvim": { "branch": "master", "commit": "72c68aab0358c92f451168b704c411c4a3e3410e" }, 49 | "plenary.nvim": { "branch": "master", "commit": "857c5ac632080dba10aae49dba902ce3abf91b35" }, 50 | "promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" }, 51 | "refactoring.nvim": { "branch": "master", "commit": "64dbe67bf7c28c864488262d267c799f80cae9ba" }, 52 | "render-markdown.nvim": { "branch": "main", "commit": "8debb17aab2fbbf3b341e46ac032d0a6f937d8c3" }, 53 | "snacks.nvim": { "branch": "main", "commit": "bc0630e43be5699bb94dadc302c0d21615421d93" }, 54 | "sqlite.lua": { "branch": "master", "commit": "50092d60feb242602d7578398c6eb53b4a8ffe7b" }, 55 | "todo-comments.nvim": { "branch": "main", "commit": "304a8d204ee787d2544d8bc23cd38d2f929e7cc5" }, 56 | "troublesum.nvim": { "branch": "master", "commit": "b00301bb565bfcb6fcfa41f2012e43a0528da65b" }, 57 | "ts-comments.nvim": { "branch": "main", "commit": "1bd9d0ba1d8b336c3db50692ffd0955fe1bb9f0c" }, 58 | "virt-column.nvim": { "branch": "master", "commit": "b87e3e0864211a32724a2ebf3be37e24e9e2fa99" } 59 | } 60 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/autocmds.lua: -------------------------------------------------------------------------------- 1 | local autocmds = { 2 | { 3 | -- Watch for changes in ~/.color_mode 4 | -- REF: https://github.com/rktjmp/fwatch.nvim/blob/main/lua/fwatch.lua 5 | name = "Colors", 6 | { 7 | "VimEnter", 8 | function() 9 | local uv = vim.uv 10 | local handle = uv.new_fs_event() 11 | 12 | local file_to_watch = "~/.color_mode" 13 | 14 | local flags = { 15 | watch_entry = false, -- true = when dir, watch dir inode, not dir content 16 | stat = false, -- true = don't use inotify/kqueue but periodic check, not implemented 17 | recursive = false, -- true = watch dirs inside dirs 18 | } 19 | 20 | local function change_colors(mode) 21 | -- vim.opt.background = mode or "dark" 22 | 23 | if mode == "light" then 24 | vim.cmd([[colorscheme onelight]]) 25 | else 26 | vim.cmd([[colorscheme vaporwave]]) 27 | end 28 | 29 | local utils = require("heirline.utils") 30 | utils.on_colorscheme(require("onedarkpro.helpers").get_colors()) 31 | end 32 | 33 | ---Read the contents of a given file 34 | local function read_file(file) 35 | file = vim.fs.normalize(file) 36 | 37 | local fd = assert(uv.fs_open(file, "r", 438)) 38 | local stat = assert(uv.fs_fstat(fd)) 39 | local data = assert(uv.fs_read(fd, stat.size, 0)) 40 | assert(uv.fs_close(fd)) 41 | 42 | return vim.trim(data) 43 | end 44 | 45 | local event_cb = function(err, filename, _) 46 | filename = vim.fs.normalize("~/" .. filename) 47 | if not err then 48 | vim.schedule(function() 49 | local data = read_file(filename) 50 | change_colors(data) 51 | end) 52 | end 53 | end 54 | 55 | change_colors(read_file(file_to_watch)) 56 | uv.fs_event_start(handle, vim.fs.normalize(file_to_watch), flags, event_cb) 57 | end, 58 | }, 59 | }, 60 | { 61 | name = "ConcealAttributes", 62 | { 63 | { "BufEnter", "BufWritePost", "TextChanged", "InsertLeave" }, 64 | function() 65 | vim.opt.conceallevel = 2 -- Concealed text is completely hidden 66 | end, 67 | opts = { 68 | pattern = { "*.html" }, 69 | }, 70 | }, 71 | }, 72 | { 73 | name = "CodeCompanion", 74 | { 75 | "User", 76 | function(args) 77 | require("conform").format({ bufnr = args.buf }) 78 | end, 79 | opts = { pattern = "CodeCompanionInlineFinished" }, 80 | }, 81 | -- { 82 | -- "User", 83 | -- function(args) 84 | -- print(vim.inspect(args)) 85 | -- end, 86 | -- opts = { pattern = "CodeCompanionRequest*" }, 87 | -- }, 88 | }, 89 | { 90 | name = "Heirline", 91 | { 92 | "User", 93 | function(args) 94 | local buf = args.buf 95 | local buftype = vim.tbl_contains({ "prompt", "nofile", "help", "quickfix" }, vim.bo[buf].buftype) 96 | local filetype = vim.tbl_contains({ "", "alpha", "gitcommit", "fugitive" }, vim.bo[buf].filetype) 97 | if buftype or filetype then 98 | vim.opt_local.winbar = nil 99 | end 100 | end, 101 | opts = { pattern = "HeirlineInitWinbar" }, 102 | }, 103 | }, 104 | { 105 | name = "PersistedHooks", 106 | { 107 | "User", 108 | function(session) 109 | -- Prompt to save the current session 110 | if vim.fn.confirm("Save the current session?", "&Yes\n&No") == 1 then 111 | require("persisted").save({ session = vim.g.persisting_session }) 112 | end 113 | 114 | -- Clear all of the open buffers 115 | vim.api.nvim_input("silent :%bd!") 116 | 117 | -- Disable automatic session saving 118 | require("persisted").stop() 119 | end, 120 | opts = { pattern = "PersistedTelescopeLoadPre" }, 121 | }, 122 | { 123 | "User", 124 | function() 125 | -- Ensure no CodeCompanion buffers are saved into the session 126 | for _, buf in ipairs(vim.api.nvim_list_bufs()) do 127 | if vim.bo[buf].filetype == "codecompanion" then 128 | vim.api.nvim_buf_delete(buf, { force = true }) 129 | end 130 | end 131 | end, 132 | opts = { pattern = "PersistedSavePre" }, 133 | }, 134 | }, 135 | { 136 | name = "ReturnToLastEditingPosition", 137 | { 138 | "BufReadPost", 139 | function() 140 | if vim.fn.line("'\"") > 0 and vim.fn.line("'\"") <= vim.fn.line("$") then 141 | vim.fn.setpos(".", vim.fn.getpos("'\"")) 142 | vim.api.nvim_feedkeys("zz", "n", true) 143 | vim.cmd("silent! foldopen") 144 | end 145 | end, 146 | }, 147 | }, 148 | { 149 | name = "GitTrackRemoteBranch", 150 | { 151 | { "TermLeave" }, 152 | function() 153 | om.GitRemoteSync() 154 | end, 155 | opts = { 156 | pattern = { "*" }, 157 | }, 158 | }, 159 | { 160 | { "VimEnter" }, 161 | function() 162 | if _G.om.on_personal then 163 | local timer = vim.loop.new_timer() 164 | timer:start(0, 120000, function() 165 | om.GitRemoteSync() 166 | end) 167 | end 168 | end, 169 | opts = { 170 | pattern = { "*" }, 171 | }, 172 | }, 173 | }, 174 | { 175 | name = "FiletypeOptions", 176 | { 177 | "FileType", 178 | function() 179 | vim.keymap.set("n", "q", vim.cmd.close, { desc = "Close the current buffer", buffer = true }) 180 | end, 181 | opts = { 182 | pattern = { "checkhealth", "help", "lspinfo", "netrw", "qf", "query" }, 183 | }, 184 | }, 185 | { 186 | "FileType", 187 | ":setlocal shiftwidth=2 tabstop=2", 188 | opts = { 189 | pattern = { "css", "eruby", "html", "lua", "javascript", "json", "ruby", "vue" }, 190 | }, 191 | }, 192 | { 193 | "FileType", 194 | ":setlocal shiftwidth=4 tabstop=4 expandtab", 195 | opts = { 196 | pattern = { "ledger", "journal" }, 197 | }, 198 | }, 199 | { 200 | "FileType", 201 | ":setlocal wrap linebreak formatoptions-=t", 202 | opts = { pattern = "markdown" }, 203 | }, 204 | { 205 | "FileType", 206 | ":setlocal showtabline=0", 207 | opts = { pattern = "alpha" }, 208 | }, 209 | { 210 | "FileType", 211 | --Credit: 212 | --https://medium.com/scoro-engineering/5-smart-mini-snippets-for-making-text-editing-more-fun-in-neovim-b55ffb96325a 213 | function() 214 | vim.keymap.set("n", "o", function() 215 | local line = vim.api.nvim_get_current_line() 216 | 217 | local should_add_comma = string.find(line, "[^,{[]$") 218 | if should_add_comma then 219 | return "A," 220 | else 221 | return "o" 222 | end 223 | end, { buffer = true, expr = true }) 224 | end, 225 | opts = { pattern = "json" }, 226 | }, 227 | { 228 | "FileType", 229 | function() 230 | vim.keymap.set("i", "=", function() 231 | -- The cursor location does not give us the correct node in this case, so we 232 | -- need to get the node to the left of the cursor 233 | local cursor = vim.api.nvim_win_get_cursor(0) 234 | local left_of_cursor_range = { cursor[1] - 1, cursor[2] - 1 } 235 | 236 | local node = vim.treesitter.get_node({ pos = left_of_cursor_range }) 237 | local nodes_active_in = { 238 | "attribute_name", 239 | "directive_argument", 240 | "directive_name", 241 | } 242 | if not node or not vim.tbl_contains(nodes_active_in, node:type()) then 243 | -- The cursor is not on an attribute node 244 | return "=" 245 | end 246 | 247 | return '=""' 248 | end, { expr = true, buffer = true }) 249 | end, 250 | opts = { pattern = { "html", "vue" } }, 251 | }, 252 | }, 253 | { 254 | name = "QuickfixFormatting", 255 | { 256 | { "BufEnter", "WinEnter" }, 257 | ":if &buftype == 'quickfix' | setlocal nocursorline | setlocal number | endif", 258 | opts = { 259 | pattern = { "*" }, 260 | }, 261 | }, 262 | }, 263 | { 264 | name = "AddRubyFiletypes", 265 | { 266 | { "BufNewFile", "BufRead" }, 267 | ":set ft=ruby", 268 | opts = { 269 | pattern = { "*.json.jbuilder", "*.jbuilder", "*.rake" }, 270 | }, 271 | }, 272 | }, 273 | { 274 | name = "ChangeMappingsInTerminal", 275 | { 276 | "TermOpen", 277 | function() 278 | if vim.bo.filetype == "" or vim.bo.filetype == "toggleterm" then 279 | local opts = { silent = false, buffer = 0 } 280 | vim.keymap.set("t", "", [[]], opts) 281 | vim.keymap.set("t", "jk", [[]], opts) 282 | end 283 | end, 284 | opts = { 285 | pattern = "term://*", 286 | }, 287 | }, 288 | }, 289 | { 290 | name = "RemoveWhitespaceOnSave", 291 | { 292 | { "BufWritePre" }, 293 | [[%s/\s\+$//e]], 294 | opts = { 295 | pattern = { "*" }, 296 | }, 297 | }, 298 | }, 299 | { 300 | name = "HighlightYankedText", 301 | { 302 | "TextYankPost", 303 | function() 304 | vim.highlight.on_yank() 305 | end, 306 | opts = { pattern = "*" }, 307 | }, 308 | }, 309 | { 310 | name = "Telescope", 311 | { 312 | "User", 313 | ":setlocal wrap", 314 | opts = { pattern = "TelescopePreviewerLoaded" }, 315 | }, 316 | }, 317 | } 318 | 319 | for _, group in ipairs(autocmds) do 320 | local group_name = group.name 321 | local augroup = vim.api.nvim_create_augroup(group_name, { clear = true }) 322 | 323 | for _, autocmd in ipairs(group) do 324 | om.create_autocmd(autocmd, augroup, autocmd[2], { 325 | pattern = autocmd.opts and autocmd.opts.pattern or "*", 326 | }) 327 | end 328 | end 329 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/commands.lua: -------------------------------------------------------------------------------- 1 | local commands = { 2 | { 3 | "LineNumbers", 4 | function() 5 | om.ToggleLineNumbers() 6 | end, 7 | desc = "Toggle line numbers", 8 | }, 9 | { 10 | "ChangeFiletype", 11 | function() 12 | om.ChangeFiletype() 13 | end, 14 | desc = "Change filetype of current buffer", 15 | }, 16 | { 17 | "CopyMessage", 18 | function() 19 | vim.cmd([[let @+ = execute('messages')]]) 20 | end, 21 | desc = "Copy message output", 22 | }, 23 | { 24 | "FindAndReplace", 25 | function(opts) 26 | vim.api.nvim_command(string.format("silent cdo s/%s/%s", opts.fargs[1], opts.fargs[2])) 27 | vim.api.nvim_command("silent cfdo update") 28 | end, 29 | desc = "Find and Replace (after quickfix)", 30 | opts = { nargs = "*" }, 31 | }, 32 | { 33 | "FindAndReplaceUndo", 34 | function(opts) 35 | vim.api.nvim_command("silent cdo undo") 36 | end, 37 | desc = "Undo Find and Replace", 38 | }, 39 | { 40 | "GitBranchList", 41 | function() 42 | om.ListBranches() 43 | end, 44 | desc = "List the Git branches in this repo", 45 | }, 46 | { 47 | "GitRemoteSync", 48 | function() 49 | om.GitRemoteSync() 50 | end, 51 | desc = "Git sync remote repo", 52 | }, 53 | { 54 | "New", 55 | ":enew", 56 | desc = "New buffer", 57 | }, 58 | { 59 | "Snippets", 60 | function() 61 | om.EditSnippet() 62 | end, 63 | desc = "Edit Snippets", 64 | }, 65 | { 66 | "Theme", 67 | function() 68 | om.ToggleTheme() 69 | end, 70 | desc = "Toggle theme", 71 | }, 72 | { 73 | "Uuid", 74 | function() 75 | local uuid = vim.fn.system("uuidgen"):gsub("\n", ""):lower() 76 | local line = vim.fn.getline(".") 77 | vim.schedule(function() 78 | vim.fn.setline(".", vim.fn.strpart(line, 0, vim.fn.col(".")) .. uuid .. vim.fn.strpart(line, vim.fn.col("."))) 79 | end) 80 | end, 81 | desc = "Generate a UUID and insert it into the buffer", 82 | }, 83 | } 84 | 85 | for _, cmd in ipairs(commands) do 86 | om.create_user_command(cmd[1], cmd[2], cmd.desc, cmd.opts) 87 | end 88 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/functions.lua: -------------------------------------------------------------------------------- 1 | local Job = require("plenary.job") 2 | 3 | function om.ChangeFiletype() 4 | vim.ui.input({ prompt = "Change filetype to: " }, function(new_ft) 5 | if new_ft ~= nil then 6 | vim.bo.filetype = new_ft 7 | end 8 | end) 9 | end 10 | 11 | function om.ListBranches() 12 | local branches = vim.fn.systemlist([[git branch 2>/dev/null]]) 13 | local new_branch_prompt = "Create new branch" 14 | table.insert(branches, 1, new_branch_prompt) 15 | 16 | vim.ui.select(branches, { 17 | prompt = "Git branches", 18 | }, function(choice) 19 | if choice == nil then 20 | return 21 | end 22 | 23 | if choice == new_branch_prompt then 24 | local new_branch = "" 25 | vim.ui.input({ prompt = "New branch name:" }, function(branch) 26 | if branch ~= nil then 27 | vim.fn.systemlist("git checkout -b " .. branch) 28 | end 29 | end) 30 | else 31 | vim.fn.systemlist("git checkout " .. choice) 32 | end 33 | end) 34 | end 35 | 36 | function om.GitRemoteSync() 37 | if not _G.GitStatus then 38 | _G.GitStatus = { ahead = 0, behind = 0, status = nil } 39 | end 40 | 41 | -- Fetch the remote repository 42 | local git_fetch = Job:new({ 43 | command = "git", 44 | args = { "fetch" }, 45 | on_start = function() 46 | _G.GitStatus.status = "pending" 47 | end, 48 | on_exit = function() 49 | _G.GitStatus.status = "done" 50 | end, 51 | }) 52 | 53 | -- Compare local repository to upstream 54 | local git_upstream = Job:new({ 55 | command = "git", 56 | args = { "rev-list", "--left-right", "--count", "HEAD...@{upstream}" }, 57 | on_start = function() 58 | _G.GitStatus.status = "pending" 59 | vim.schedule(function() 60 | vim.api.nvim_exec_autocmds("User", { pattern = "GitStatusChanged" }) 61 | end) 62 | end, 63 | on_exit = function(job, _) 64 | local res = job:result()[1] 65 | if type(res) ~= "string" then 66 | _G.GitStatus = { ahead = 0, behind = 0, status = "error" } 67 | return 68 | end 69 | local _, ahead, behind = pcall(string.match, res, "(%d+)%s*(%d+)") 70 | 71 | _G.GitStatus = { ahead = tonumber(ahead), behind = tonumber(behind), status = "done" } 72 | vim.schedule(function() 73 | vim.api.nvim_exec_autocmds("User", { pattern = "GitStatusChanged" }) 74 | end) 75 | end, 76 | }) 77 | 78 | git_fetch:start() 79 | git_upstream:start() 80 | end 81 | 82 | local function GitPushPull(action, tense) 83 | local branch = vim.fn.systemlist("git rev-parse --abbrev-ref HEAD")[1] 84 | 85 | vim.ui.select({ "Yes", "No" }, { 86 | prompt = action:gsub("^%l", string.upper) .. " commits to/from " .. "'origin/" .. branch .. "'?", 87 | }, function(choice) 88 | if choice == "Yes" then 89 | Job:new({ 90 | command = "git", 91 | args = { action }, 92 | on_exit = function() 93 | om.GitRemoteSync() 94 | end, 95 | }):start() 96 | end 97 | end) 98 | end 99 | 100 | function om.GitPull() 101 | GitPushPull("pull", "from") 102 | end 103 | 104 | function om.GitPush() 105 | GitPushPull("push", "to") 106 | end 107 | 108 | function om.MoveToBuffer() 109 | vim.ui.input({ prompt = "Move to buffer number: " }, function(bufnr) 110 | if bufnr ~= nil then 111 | pcall(vim.cmd, "b " .. bufnr) 112 | end 113 | end) 114 | end 115 | 116 | function om.EditSnippet() 117 | local path = Homedir .. "/.config/snippets" 118 | local snippets = { "lua", "ruby", "python", "global", "package" } 119 | 120 | vim.ui.select(snippets, { prompt = "Snippet to edit" }, function(choice) 121 | if choice == nil then 122 | return 123 | end 124 | vim.cmd(":edit " .. path .. "/" .. choice .. ".json") 125 | end) 126 | end 127 | 128 | function om.ToggleLineNumbers() 129 | if vim.wo.relativenumber then 130 | vim.wo.relativenumber = false 131 | else 132 | vim.wo.relativenumber = true 133 | end 134 | end 135 | 136 | function om.ToggleTheme(mode) 137 | if vim.o.background == mode then 138 | return 139 | end 140 | 141 | if vim.o.background == "dark" then 142 | vim.cmd([[colorscheme onelight]]) 143 | else 144 | vim.cmd([[colorscheme vaporwave]]) 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/keymaps.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Some notes on how I structure my keymaps within Neovim: 3 | * All keymaps from across my configuration live in this file 4 | * The general structure of my keymaps are: 5 | 1) Ctrl - Used for your most frequent and easy to remember keymaps 6 | 2) Local Leader - Used for commands related to window or filetype/buffer options 7 | 3) Leader - Used for commands that are global or span Neovim 8 | 9 | N.B. Leader keys are set in the options.lua file. This is so that lazy.nvim doesn't corrupt mappings 10 | ]] 11 | 12 | -- Functions for multiple cursors 13 | vim.g.mc = vim.api.nvim_replace_termcodes([[y/\V=escape(@", '/')]], true, true, true) 14 | 15 | function SetupMultipleCursors() 16 | vim.keymap.set( 17 | "n", 18 | "", 19 | [[:nnoremap Enter> n@zq:let @z=strpart(@z,0,strlen(@z)-1)n@z]], 20 | { remap = true, silent = true } 21 | ) 22 | end 23 | 24 | local keymaps = { 25 | { 26 | "", 27 | "q", 28 | mode = "n", 29 | opts = { remap = true, silent = true, desc = "Quit neovim" }, 30 | }, 31 | { "", "%y+", mode = "n", opts = { desc = "Copy buffer" } }, 32 | { 33 | "", 34 | "silent write", 35 | mode = "n", 36 | opts = { desc = "Save buffer" }, 37 | }, 38 | { 39 | "", 40 | "silent write", 41 | mode = "i", 42 | opts = { desc = "Save buffer" }, 43 | }, 44 | 45 | { "", "bnext", mode = "n", opts = { noremap = false, desc = "Next buffer" } }, 46 | { "", "bprev", mode = "n", opts = { noremap = false, desc = "Previous buffer" } }, 47 | 48 | -- Editing words 49 | { ",", "norm A,", mode = "n", opts = { desc = "Append comma" } }, 50 | { ";", "norm A;", mode = "n", opts = { desc = "Append semicolon" } }, 51 | 52 | -- Wrap text 53 | { 54 | "(", 55 | [[ciw(")]], 56 | mode = "n", 57 | opts = { desc = "Wrap text in brackets ()" }, 58 | }, 59 | { 60 | "(", 61 | [[c(")]], 62 | mode = "v", 63 | opts = { desc = "Wrap text in brackets ()" }, 64 | }, 65 | { 66 | "[", 67 | [[ciw["]]], 68 | mode = "n", 69 | opts = { desc = "Wrap text in square braces []" }, 70 | }, 71 | { 72 | "[", 73 | [[c["]]], 74 | mode = "v", 75 | opts = { desc = "Wrap text in square braces []" }, 76 | }, 77 | { 78 | "{", 79 | [[ciw{"}]], 80 | mode = "n", 81 | opts = { desc = "Wrap text in curly braces {}" }, 82 | }, 83 | { 84 | "{", 85 | [[c{"}]], 86 | mode = "v", 87 | opts = { desc = "Wrap text in curly braces {}" }, 88 | }, 89 | { 90 | '"', 91 | [[ciw"""]], 92 | mode = "n", 93 | opts = { desc = 'Wrap text in quotes ""' }, 94 | }, 95 | { 96 | '"', 97 | [[c"""]], 98 | mode = "v", 99 | opts = { desc = 'Wrap text in quotes ""' }, 100 | }, 101 | 102 | -- Find and replace 103 | { 104 | "fw", 105 | [[:%s/\<=expand("")\>/]], 106 | mode = "n", 107 | opts = { desc = "Replace cursor words in buffer" }, 108 | }, 109 | { 110 | "fl", 111 | [[:s/\<=expand("")\>/]], 112 | mode = "n", 113 | opts = { desc = "Replace cursor words in line" }, 114 | }, 115 | 116 | -- Working with lines 117 | { "B", "^", mode = "n", opts = { desc = "Beginning of a line" } }, 118 | { "B", "^", mode = "v", opts = { desc = "Beginning of a line" } }, 119 | { "E", "$", mode = "n", opts = { desc = "End of a line" } }, 120 | { "E", "$", mode = "v", opts = { desc = "End of a line" } }, 121 | { "", "o", mode = "n", opts = { desc = "Insert blank line below" } }, 122 | { "", "O", mode = "n", opts = { desc = "Insert blank line above" } }, 123 | 124 | -- Moving lines 125 | { 126 | "", 127 | ":m .-2==", 128 | mode = "n", 129 | opts = { silent = true, desc = "Move selection up" }, 130 | }, 131 | { 132 | "", 133 | ":m '<-2gv=gv", 134 | mode = "v", 135 | opts = { silent = true, desc = "Move selection up" }, 136 | }, 137 | { 138 | "", 139 | ":m .+1==", 140 | mode = "n", 141 | opts = { silent = true, desc = "Move selection down" }, 142 | }, 143 | { 144 | "", 145 | ":m '>+1gv=gv", 146 | mode = "v", 147 | opts = { silent = true, desc = "Move selection down" }, 148 | }, 149 | 150 | -- Splits 151 | { "sv", "vsplit", mode = "n", opts = { desc = "Split: Create Vertical" } }, 152 | { "sh", "split", mode = "n", opts = { desc = "Split: Create Horizontal" } }, 153 | { "sc", "wincmd q", mode = "n", opts = { desc = "Split: Close" } }, 154 | { "so", "wincmd o", mode = "n", opts = { desc = "Split: Close all but current" } }, 155 | { "", "wincmd k", mode = "n", opts = { desc = "Split: Move up" } }, 156 | { "", "wincmd j", mode = "n", opts = { desc = "Split: Move down" } }, 157 | { "", "wincmd h", mode = "n", opts = { desc = "Split: Move left" } }, 158 | { "", "wincmd l", mode = "n", opts = { desc = "Split: Move right" } }, 159 | 160 | -- Surrounds 161 | { "(", "S)", mode = "x", opts = { remap = true, desc = "Surround with ()'s" } }, 162 | { ")", "S)", mode = "x", opts = { remap = true, desc = "Surround with ()'s" } }, 163 | { "{", "S}", mode = "x", opts = { remap = true, desc = "Surround with {}'s" } }, 164 | { "}", "S}", mode = "x", opts = { remap = true, desc = "Surround with {}'s" } }, 165 | { "[", "S]", mode = "x", opts = { remap = true, desc = "Surround with []'s" } }, 166 | { "]", "S]", mode = "x", opts = { remap = true, desc = "Surround with []'s" } }, 167 | 168 | -- Misc 169 | { "", ":noh", mode = "n", opts = { desc = "Clear searches" } }, 170 | { "", "set winbar=", mode = "n", opts = { desc = "Hide WinBar" } }, 171 | { "U", "gUiw`", mode = "n", opts = { desc = "Capitalize word" } }, 172 | { ">", ">gv", mode = "v", opts = { desc = "Indent" } }, 173 | { "<", "call v:lua.SetupMultipleCursors()*``qz]], 215 | mode = "n", 216 | opts = { desc = "Initiate multiple cursors with macros" }, 217 | }, 218 | { 219 | "cq", 220 | [[":\call v:lua.SetupMultipleCursors()gv" . g:mc . "``qz"]], 221 | mode = "x", 222 | opts = { expr = true, desc = "Initiate multiple cursors with macros" }, 223 | }, 224 | { 225 | "cQ", 226 | [[:\call v:lua.SetupMultipleCursors()#``qz]], 227 | mode = "n", 228 | opts = { desc = "Initiate multiple cursors with macros (backwards)" }, 229 | }, 230 | { 231 | "cQ", 232 | [[":\call v:lua.SetupMultipleCursors()gv" . substitute(g:mc, '/', '?', 'g') . "``qz"]], 233 | mode = "x", 234 | opts = { expr = true, desc = "Initiate multiple cursors with macros (backwards)" }, 235 | }, 236 | } 237 | 238 | for _, keymap in ipairs(keymaps) do 239 | om.set_keymaps(keymap[1], keymap[2], keymap.mode or "n", keymap.opts) 240 | end 241 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/lazy.lua: -------------------------------------------------------------------------------- 1 | require("config.util") 2 | require("config.options") 3 | 4 | -- Begin Lazy install and plugin setup 5 | local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" 6 | if not (vim.uv or vim.loop).fs_stat(lazypath) then 7 | local lazyrepo = "https://github.com/folke/lazy.nvim.git" 8 | local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) 9 | if vim.v.shell_error ~= 0 then 10 | vim.api.nvim_echo({ 11 | { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, 12 | { out, "WarningMsg" }, 13 | { "\nPress any key to exit..." }, 14 | }, true, {}) 15 | vim.fn.getchar() 16 | os.exit(1) 17 | end 18 | end 19 | vim.opt.rtp:prepend(lazypath) 20 | 21 | require("lazy").setup({ 22 | spec = { 23 | { import = "plugins" }, 24 | }, 25 | dev = { 26 | path = "~/Code/Neovim", 27 | -- Only load my local plugins when we're on my machine 28 | patterns = (jit.os == "OSX") and { "olimorris" } or {}, 29 | }, 30 | checker = { 31 | enabled = true, 32 | notify = false, 33 | frequency = 900, 34 | }, 35 | change_detection = { 36 | enabled = true, 37 | notify = false, 38 | }, 39 | install = { 40 | colorscheme = { "onedark", "onelight" }, 41 | }, 42 | ui = { 43 | border = "single", 44 | icons = { 45 | plugin = "", 46 | }, 47 | }, 48 | performance = { 49 | cache = { 50 | enabled = true, 51 | }, 52 | rtp = { 53 | disabled_plugins = { 54 | "gzip", 55 | "matchit", 56 | "matchparen", 57 | "netrwPlugin", 58 | "tarPlugin", 59 | "tohtml", 60 | "tutor", 61 | "zipPlugin", 62 | }, 63 | }, 64 | }, 65 | }) 66 | 67 | -- Load functions next as our plugins and autocmds require them 68 | require("config.functions") 69 | require("config.autocmds") 70 | 71 | -- Autocmds and keymaps can be loaded, lazily, after plugins 72 | vim.api.nvim_create_autocmd("User", { 73 | pattern = "VeryLazy", 74 | callback = function() 75 | require("config.commands") 76 | require("config.keymaps") 77 | require("util") 78 | end, 79 | }) 80 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/options.lua: -------------------------------------------------------------------------------- 1 | -- Documentation: https://neovim.io/doc/user/options.html 2 | 3 | local vg = vim.g 4 | local vb = vim.bo 5 | local vw = vim.wo 6 | local vo = vim.opt 7 | 8 | -- Global variables 9 | Homedir = os.getenv("HOME") 10 | Sessiondir = vim.fn.stdpath("data") .. "/sessions" 11 | 12 | -- Global options 13 | vg.mapleader = " " -- space is the leader! 14 | vg.maplocalleader = "\\" 15 | 16 | vg.neoterm_autoinsert = 0 -- Do not start terminal in insert mode 17 | vg.neoterm_autoscroll = 1 -- Autoscroll the terminal 18 | vg.loaded_perl_provider = 0 -- Do not load Perl 19 | -- if vim.fn.filereadable(os.getenv("HOME_DIR") .. ".asdf/shims/python2") then 20 | -- vg.python_host_prog = os.getenv("HOME_DIR") .. ".asdf/shims/python2" 21 | -- end 22 | -- if vim.fn.filereadable(os.getenv("HOME_DIR") .. ".local/share/mise/installs/python/3.11.0/bin/python") then 23 | -- vg.python3_host_prog = os.getenv("HOME_DIR") .. ".local/share/mise/installs/python/3.11.0/bin/python" 24 | -- end 25 | 26 | -- Buffer options 27 | vb.autoindent = true 28 | vb.expandtab = true -- Use spaces instead of tabs 29 | vb.shiftwidth = 4 -- Size of an indent 30 | vb.smartindent = true -- Insert indents automatically 31 | vb.softtabstop = 4 -- Number of spaces tabs count for 32 | vb.tabstop = 4 -- Number of spaces in a tab 33 | -- vb.wrapmargin = 1 34 | 35 | -- Vim options 36 | vo.cmdheight = 0 -- Hide the command bar 37 | vim.schedule(function() 38 | vo.clipboard = "unnamedplus" -- Use the system clipboard 39 | end) 40 | vo.completeopt = { "menuone", "noselect" } -- Completion opions for code completion 41 | vo.cursorlineopt = "screenline,number" -- Highlight the screen line of the cursor with CursorLine and the line number with CursorLineNr 42 | vo.emoji = false -- Turn off emojis 43 | vo.fillchars = { 44 | fold = " ", 45 | foldopen = "", 46 | foldclose = "", 47 | foldsep = " ", 48 | diff = " ", 49 | eob = " ", 50 | } 51 | 52 | vo.foldcolumn = "1" -- Show the fold column 53 | vo.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value 54 | vo.foldlevelstart = 99 55 | vo.foldenable = true 56 | 57 | vo.ignorecase = true -- Ignore case 58 | vo.laststatus = 3 -- Use global statusline 59 | 60 | -- Sets how Neovim will display certain whitespace characters in the editor 61 | vo.list = true 62 | vo.listchars = { tab = "» ", trail = "·", nbsp = "␣" } 63 | 64 | vo.modelines = 1 -- Only use folding settings for this file 65 | vo.mouse = "a" -- Use the mouse in all modes 66 | vo.sessionoptions = { "buffers", "curdir", "folds", "resize", "tabpages", "winpos", "winsize" } -- Session options to store in the session 67 | vo.scrolloff = 5 -- Set the cursor 5 lines down instead of directly at the top of the file 68 | --[[ 69 | ShDa (viminfo for vim): session data history 70 | -------------------------------------------- 71 | ! - Save and restore global variables (their names should be without lowercase letter). 72 | ' - Specify the maximum number of marked files remembered. It also saves the jump list and the change list. 73 | < - Maximum of lines saved for each register. All the lines are saved if this is not included, <0 to disable pessistent registers. 74 | % - Save and restore the buffer list. You can specify the maximum number of buffer stored with a number. 75 | / or : - Number of search patterns and entries from the command-line history saved. o.history is used if it’s not specified. 76 | f - Store file (uppercase) marks, use 'f0' to disable. 77 | s - Specify the maximum size of an item’s content in KiB (kilobyte). 78 | For the viminfo file, it only applies to register. 79 | For the shada file, it applies to all items except for the buffer list and header. 80 | h - Disable the effect of 'hlsearch' when loading the shada file. 81 | 82 | :oldfiles - all files with a mark in the shada file 83 | :rshada - read the shada file (:rviminfo for vim) 84 | :wshada - write the shada file (:wrviminfo for vim) 85 | ]] 86 | vo.shada = [[!,'100,<0,f100,s100,h]] 87 | 88 | vo.shell = "/opt/homebrew/bin/fish" 89 | vo.shiftround = true -- Round indent 90 | vo.shortmess = { 91 | A = true, -- ignore annoying swap file messages 92 | c = true, -- Do not show completion messages in command line 93 | F = true, -- Do not show file info when editing a file, in the command line 94 | I = true, -- Do not show the intro message 95 | W = true, -- Do not show "written" in command line when writing 96 | } 97 | -- vo.showcmd = true -- Do not show me what I'm typing 98 | vo.showmatch = true -- Show matching brackets by flickering 99 | vo.showmode = false -- Do not show the mode 100 | vo.sidescrolloff = 8 -- The minimal number of columns to keep to the left and to the right of the cursor if 'nowrap' is set 101 | vo.smartcase = true -- Don't ignore case with capitals 102 | vo.smoothscroll = true -- Smoother scrolling 103 | vo.splitbelow = true -- Put new windows below current 104 | vo.splitright = true -- Put new windows right of current 105 | vo.termguicolors = true -- True color support 106 | vo.textwidth = 120 -- Total allowed width on the screen 107 | vo.timeoutlen = 300 -- Time in milliseconds to wait for a mapped sequence to complete 108 | vo.updatetime = 250 -- If in this many milliseconds nothing is typed, the swap file will be written to disk 109 | vo.wildmode = "list:longest" -- Command-line completion mode 110 | vo.wildignore = { "*/.git/*", "*/node_modules/*" } -- Ignore these files/folders 111 | 112 | -- Create folders for our backups, undos, swaps and sessions if they don't exist 113 | vim.schedule(function() 114 | vim.cmd("silent call mkdir(stdpath('data').'/backups', 'p', '0700')") 115 | vim.cmd("silent call mkdir(stdpath('data').'/undos', 'p', '0700')") 116 | vim.cmd("silent call mkdir(stdpath('data').'/swaps', 'p', '0700')") 117 | vim.cmd("silent call mkdir(stdpath('data').'/sessions', 'p', '0700')") 118 | 119 | vo.backupdir = vim.fn.stdpath("data") .. "/backups" -- Use backup files 120 | vo.directory = vim.fn.stdpath("data") .. "/swaps" -- Use Swap files 121 | vo.undodir = vim.fn.stdpath("data") .. "/undos" -- Set the undo directory 122 | end) 123 | 124 | vo.undofile = true -- Maintain undo history between sessions 125 | vo.undolevels = 1000 -- Ensure we can undo a lot! 126 | 127 | -- Window options 128 | vw.colorcolumn = "80,120" -- Make a ruler at 80px and 120px 129 | vw.list = true -- Show some invisible characters like tabs etc 130 | vw.numberwidth = 2 -- Make the line number column thinner 131 | ---Note: Setting number and relative number gives you hybrid mode 132 | ---https://jeffkreeftmeijer.com/vim-number/ 133 | vw.number = true -- Set the absolute number 134 | vw.relativenumber = true -- Set the relative number 135 | vw.signcolumn = "yes" -- Show information next to the line numbers 136 | vw.wrap = false -- Do not display text over multiple lines 137 | -------------------------------------------------------------------------------- /.config/nvim/lua/config/util.lua: -------------------------------------------------------------------------------- 1 | -- Set the global namespace 2 | _G.om = {} 3 | 4 | ---Check if a certain feature/version/commit exists in nvim 5 | ---@param feature string 6 | ---@return boolean 7 | function om.has(feature) 8 | return vim.fn.has(feature) > 0 9 | end 10 | 11 | om.nightly = om.has("nvim-0.11") 12 | om.on_personal = vim.fn.getenv("USER") == "Oli" 13 | 14 | ---Determine if you're on an external monitor 15 | ---@return boolean 16 | function om.on_big_screen() 17 | return vim.o.columns > 150 and vim.o.lines >= 40 18 | end 19 | 20 | ---Set a keymap in Neovim 21 | ---@param lhs string The left-hand side of the keymap (the key combination) 22 | ---@param rhs string|function The right-hand side of the keymap (the command to execute) 23 | ---@param mode string The mode in which the keymap should be set (e.g., "n" for normal mode) 24 | ---@param opts? table Optional parameters for the keymap, such as silent or noremap 25 | ---@return nil 26 | function om.set_keymaps(lhs, rhs, mode, opts) 27 | opts = opts or {} 28 | vim.keymap.set(mode, lhs, rhs, opts) 29 | end 30 | 31 | ---Create a user command in Neovim 32 | ---@param name string The name of the command 33 | ---@param command string|function The command to execute, can be a string or a function 34 | ---@param desc string A description of the command 35 | ---@param opts table Optional parameters for the command, such as nargs 36 | ---@return nil 37 | function om.create_user_command(name, command, desc, opts) 38 | vim.api.nvim_create_user_command(name, command, { 39 | desc = desc, 40 | nargs = opts and opts.nargs or 0, 41 | }) 42 | end 43 | 44 | ---Create an autocommand in Neovim 45 | ---@param autocmd table A table containing the autocmd event and the command to execute 46 | ---@param augroup string The name of the augroup to which this autocmd belongs 47 | ---@param callback function|nil A callback function to execute when the autocmd is triggered 48 | ---@param opts table Optional parameters for the autocmd, such as pattern 49 | ---@return nil 50 | function om.create_autocmd(autocmd, augroup, callback, opts) 51 | opts = opts or {} 52 | 53 | vim.api.nvim_create_autocmd(autocmd[1], { 54 | group = augroup, 55 | pattern = opts.pattern or "*", 56 | callback = type(autocmd[2]) == "function" and autocmd[2] or function() 57 | vim.cmd(autocmd[2]) 58 | end, 59 | }) 60 | end 61 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/colorscheme.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "olimorris/onedarkpro.nvim", 4 | priority = 1000, 5 | build = "make extras", 6 | opts = { 7 | colors = { 8 | vaporwave = { 9 | codeblock = "require('onedarkpro.helpers').lighten('bg', 2, 'vaporwave')", 10 | statusline_bg = "require('onedarkpro.helpers').lighten('bg', 4, 'vaporwave')", -- gray 11 | statuscolumn_border = "require('onedarkpro.helpers').lighten('bg', 4, 'vaporwave')", -- gray 12 | ellipsis = "require('onedarkpro.helpers').lighten('bg', 4, 'vaporwave')", -- gray 13 | picker_results = "require('onedarkpro.helpers').darken('bg', 4, 'vaporwave')", 14 | picker_selection = "require('onedarkpro.helpers').darken('bg', 8, 'vaporwave')", 15 | copilot = "require('onedarkpro.helpers').darken('gray', 8, 'vaporwave')", 16 | breadcrumbs = "require('onedarkpro.helpers').darken('gray', 10, 'vaporwave')", 17 | light_gray = "require('onedarkpro.helpers').darken('gray', 7, 'vaporwave')", 18 | }, 19 | onedark = { 20 | codeblock = "require('onedarkpro.helpers').lighten('bg', 2, 'onedark')", 21 | statusline_bg = "#2e323b", -- gray 22 | statuscolumn_border = "#4b5160", -- gray 23 | ellipsis = "#808080", -- gray 24 | picker_results = "require('onedarkpro.helpers').darken('bg', 4, 'onedark')", 25 | picker_selection = "require('onedarkpro.helpers').darken('bg', 8, 'onedark')", 26 | copilot = "require('onedarkpro.helpers').darken('gray', 8, 'onedark')", 27 | breadcrumbs = "require('onedarkpro.helpers').darken('gray', 10, 'onedark')", 28 | light_gray = "require('onedarkpro.helpers').darken('gray', 7, 'onedark')", 29 | }, 30 | light = { 31 | codeblock = "require('onedarkpro.helpers').darken('bg', 3, 'onelight')", 32 | comment = "#bebebe", -- Revert back to original comment colors 33 | statusline_bg = "#f0f0f0", -- gray 34 | statuscolumn_border = "#e7e7e7", -- gray 35 | ellipsis = "#808080", -- gray 36 | git_add = "require('onedarkpro.helpers').get_preloaded_colors('onelight').green", 37 | git_change = "require('onedarkpro.helpers').get_preloaded_colors('onelight').yellow", 38 | git_delete = "require('onedarkpro.helpers').get_preloaded_colors('onelight').red", 39 | picker_results = "require('onedarkpro.helpers').darken('bg', 5, 'onelight')", 40 | picker_selection = "require('onedarkpro.helpers').darken('bg', 9, 'onelight')", 41 | copilot = "require('onedarkpro.helpers').lighten('gray', 8, 'onelight')", 42 | breadcrumbs = "require('onedarkpro.helpers').lighten('gray', 8, 'onelight')", 43 | light_gray = "require('onedarkpro.helpers').lighten('gray', 10, 'onelight')", 44 | }, 45 | rainbow = { 46 | "${green}", 47 | "${blue}", 48 | "${purple}", 49 | "${red}", 50 | "${orange}", 51 | "${yellow}", 52 | "${cyan}", 53 | }, 54 | }, 55 | highlights = { 56 | CodeCompanionTokens = { fg = "${gray}", italic = true }, 57 | CodeCompanionVirtualText = { fg = "${gray}", italic = true }, 58 | 59 | ["@markup.raw.block.markdown"] = { bg = "${codeblock}" }, 60 | ["@markup.quote.markdown"] = { italic = true, extend = true }, 61 | 62 | EdgyNormal = { bg = "${bg}" }, 63 | EdgyTitle = { fg = "${purple}", bold = true }, 64 | 65 | EyelinerPrimary = { fg = "${green}" }, 66 | EyelinerSecondary = { fg = "${blue}" }, 67 | 68 | NormalFloat = { bg = "${bg}" }, -- Set the terminal background to be the same as the editor 69 | FloatBorder = { fg = "${gray}", bg = "${bg}" }, 70 | 71 | CursorLineNr = { bg = "${bg}", fg = "${fg}", italic = true }, 72 | MatchParen = { fg = "${cyan}" }, 73 | ModeMsg = { fg = "${gray}" }, -- Make command line text lighter 74 | Search = { bg = "${selection}", fg = "${yellow}", underline = true }, 75 | VimLogo = { fg = { dark = "#81b766", light = "#029632" } }, 76 | 77 | -- Dashboard 78 | SnacksDashboardDesc = { fg = "${blue}", bold = true }, 79 | SnacksDashboardKey = { fg = "${orange}", bold = true, italic = true }, 80 | SnacksDashboardIcon = { fg = "${blue}" }, 81 | 82 | -- Copilot 83 | CopilotSuggestion = { fg = "${copilot}", italic = true }, 84 | 85 | -- DAP 86 | DebugBreakpoint = { fg = "${red}", italic = true }, 87 | DebugHighlightLine = { fg = "${purple}", italic = true }, 88 | NvimDapVirtualText = { fg = "${cyan}", italic = true }, 89 | 90 | -- DAP UI 91 | DapUIBreakpointsCurrentLine = { fg = "${yellow}", bold = true }, 92 | 93 | -- Heirline 94 | Heirline = { bg = "${statusline_bg}" }, 95 | HeirlineStatusColumn = { fg = "${statuscolumn_border}" }, 96 | HeirlineBufferline = { fg = { dark = "#939aa3", light = "#6a6a6a" } }, 97 | HeirlineWinbar = { fg = "${breadcrumbs}", italic = true }, 98 | HeirlineWinbarEmphasis = { fg = "${fg}", italic = true }, 99 | 100 | -- Luasnip 101 | LuaSnipChoiceNode = { fg = "${yellow}" }, 102 | LuaSnipInsertNode = { fg = "${yellow}" }, 103 | 104 | -- Neotest 105 | NeotestAdapterName = { fg = "${purple}", bold = true }, 106 | NeotestFocused = { bold = true }, 107 | NeotestNamespace = { fg = "${blue}", bold = true }, 108 | 109 | -- Nvim UFO 110 | UfoFoldedEllipsis = { fg = "${yellow}" }, 111 | 112 | -- Snacks 113 | SnacksPicker = { bg = "${picker_results}" }, 114 | SnacksPickerDir = { fg = "${gray}", italic = true }, 115 | SnacksPickerBorder = { fg = "${picker_results}", bg = "${picker_results}" }, 116 | SnacksPickerListCursorLine = { bg = "${picker_selection}" }, 117 | SnacksPickerPrompt = { bg = "${picker_results}", fg = "${purple}", bold = true }, 118 | SnacksPickerSelected = { bg = "${picker_results}", fg = "${orange}" }, 119 | SnacksPickerTitle = { bg = "${purple}", fg = "${picker_results}", bold = true }, 120 | SnacksPickerToggle = { bg = "${purple}", fg = "${picker_results}", italic = true }, 121 | SnacksPickerTotals = { bg = "${picker_results}", fg = "${purple}", bold = true }, 122 | SnacksPickerUnselected = { bg = "${picker_results}" }, 123 | 124 | SnacksPickerPreview = { bg = "${bg}" }, 125 | SnacksPickerPreviewBorder = { fg = "${bg}", bg = "${bg}" }, 126 | SnacksPickerPreviewTitle = { bg = "${green}", fg = "${bg}", bold = true }, 127 | 128 | -- Virt Column 129 | VirtColumn = { fg = "${indentline}" }, 130 | }, 131 | 132 | caching = false, 133 | cache_path = vim.fn.expand(vim.fn.stdpath("cache") .. "/onedarkpro_dotfiles"), 134 | 135 | plugins = { 136 | barbar = false, 137 | lsp_saga = false, 138 | marks = false, 139 | polygot = false, 140 | startify = false, 141 | telescope = false, 142 | trouble = false, 143 | vim_ultest = false, 144 | which_key = false, 145 | }, 146 | styles = { 147 | tags = "italic", 148 | methods = "bold", 149 | functions = "bold", 150 | keywords = "italic", 151 | comments = "italic", 152 | parameters = "italic", 153 | conditionals = "italic", 154 | virtual_text = "italic", 155 | }, 156 | options = { 157 | cursorline = true, 158 | -- transparency = true, 159 | -- highlight_inactive_windows = true, 160 | }, 161 | }, 162 | }, 163 | } 164 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/custom/spinner.lua: -------------------------------------------------------------------------------- 1 | local progress = require("fidget.progress") 2 | 3 | local M = {} 4 | 5 | function M:init() 6 | local group = vim.api.nvim_create_augroup("CodeCompanionFidgetHooks", {}) 7 | 8 | vim.api.nvim_create_autocmd({ "User" }, { 9 | pattern = "CodeCompanionRequestStarted", 10 | group = group, 11 | callback = function(request) 12 | local handle = M:create_progress_handle(request) 13 | M:store_progress_handle(request.data.id, handle) 14 | end, 15 | }) 16 | 17 | vim.api.nvim_create_autocmd({ "User" }, { 18 | pattern = "CodeCompanionRequestFinished", 19 | group = group, 20 | callback = function(request) 21 | local handle = M:pop_progress_handle(request.data.id) 22 | if handle then 23 | M:report_exit_status(handle, request) 24 | handle:finish() 25 | end 26 | end, 27 | }) 28 | end 29 | 30 | M.handles = {} 31 | 32 | function M:store_progress_handle(id, handle) 33 | M.handles[id] = handle 34 | end 35 | 36 | function M:pop_progress_handle(id) 37 | local handle = M.handles[id] 38 | M.handles[id] = nil 39 | return handle 40 | end 41 | 42 | function M:create_progress_handle(request) 43 | return progress.handle.create({ 44 | -- title = " Requesting assistance (" .. request.data.strategy .. ")", 45 | title = "", 46 | message = " Sending...", 47 | lsp_client = { 48 | name = M:llm_role_title(request.data.adapter), 49 | }, 50 | }) 51 | end 52 | 53 | function M:llm_role_title(adapter) 54 | local parts = {} 55 | table.insert(parts, adapter.formatted_name) 56 | if adapter.model and adapter.model ~= "" then 57 | table.insert(parts, "(" .. adapter.model .. ")") 58 | end 59 | return table.concat(parts, " ") 60 | end 61 | 62 | function M:report_exit_status(handle, request) 63 | if request.data.status == "success" then 64 | handle.message = "Completed" 65 | elseif request.data.status == "error" then 66 | handle.message = " Error" 67 | else 68 | handle.message = "󰜺 Cancelled" 69 | end 70 | end 71 | 72 | return M 73 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/editor.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "kevinhwang91/nvim-ufo", -- Better folds in Neovim 4 | dependencies = "kevinhwang91/promise-async", 5 | keys = { 6 | { 7 | "zR", 8 | function() 9 | require("ufo").openAllFolds() 10 | end, 11 | desc = "Open all folds", 12 | }, 13 | { 14 | "zM", 15 | function() 16 | require("ufo").closeAllFolds() 17 | end, 18 | desc = "Close all folds", 19 | }, 20 | }, 21 | }, 22 | { 23 | "stevearc/oil.nvim", -- File manager 24 | opts = { 25 | default_file_explorer = false, 26 | delete_to_trash = true, 27 | skip_confirm_for_simple_edits = true, 28 | float = { 29 | border = "none", 30 | }, 31 | is_always_hidden = function(name, bufnr) 32 | return name == ".." 33 | end, 34 | keymaps = { 35 | [""] = false, 36 | ["q"] = "actions.close", 37 | [">"] = "actions.toggle_hidden", 38 | [""] = "actions.copy_entry_path", 39 | ["gd"] = { 40 | desc = "Toggle detail view", 41 | callback = function() 42 | local oil = require("oil") 43 | local config = require("oil.config") 44 | if #config.columns == 1 then 45 | oil.set_columns({ "icon", "permissions", "size", "mtime" }) 46 | else 47 | oil.set_columns({ "icon" }) 48 | end 49 | end, 50 | }, 51 | }, 52 | buf_options = { 53 | buflisted = false, 54 | }, 55 | }, 56 | keys = { 57 | { 58 | "_", 59 | function() 60 | -- Stop me being dumb and opening a file explorer in openai.nvim 61 | if vim.bo.buftype ~= "acwrite" then 62 | require("oil").toggle_float(vim.fn.getcwd()) 63 | end 64 | end, 65 | desc = "Oil.nvim: Open File Explorer", 66 | }, 67 | { 68 | "-", 69 | function() 70 | -- Stop me being dumb and opening a file explorer in openai.nvim 71 | if vim.bo.buftype ~= "acwrite" then 72 | require("oil").toggle_float() 73 | end 74 | end, 75 | desc = "Oil.nvim: Open File Explorer to current file", 76 | }, 77 | }, 78 | }, 79 | { 80 | "bassamsdata/namu.nvim", 81 | keys = { 82 | { 83 | "", 84 | function() 85 | require("namu.namu_symbols").show() 86 | end, 87 | mode = { "n", "x", "o" }, 88 | desc = "Show symbols in current file", 89 | }, 90 | { 91 | "", 92 | function() 93 | require("namu.namu_workspace").show() 94 | end, 95 | mode = { "n", "x", "o" }, 96 | desc = "Show symbols in workspace", 97 | }, 98 | }, 99 | opts = { 100 | namu_symbols = { 101 | enable = true, 102 | options = {}, -- here you can configure namu 103 | }, 104 | ui_select = { enable = false }, -- vim.ui.select() wrapper 105 | }, 106 | }, 107 | { 108 | "stevearc/aerial.nvim", -- Toggled list of classes, methods etc in current file 109 | opts = { 110 | attach_mode = "global", 111 | close_on_select = true, 112 | layout = { 113 | min_width = 30, 114 | default_direction = "prefer_right", 115 | }, 116 | -- Use nvim-navic icons 117 | icons = { 118 | File = "󰈙 ", 119 | Module = " ", 120 | Namespace = "󰌗 ", 121 | Package = " ", 122 | Class = "󰌗 ", 123 | Method = "󰆧 ", 124 | Property = " ", 125 | Field = " ", 126 | Constructor = " ", 127 | Enum = "󰕘", 128 | Interface = "󰕘", 129 | Function = "󰊕 ", 130 | Variable = "󰆧 ", 131 | Constant = "󰏿 ", 132 | String = "󰀬 ", 133 | Number = "󰎠 ", 134 | Boolean = "◩ ", 135 | Array = "󰅪 ", 136 | Object = "󰅩 ", 137 | Key = "󰌋 ", 138 | Null = "󰟢 ", 139 | EnumMember = " ", 140 | Struct = "󰌗 ", 141 | Event = " ", 142 | Operator = "󰆕 ", 143 | TypeParameter = "󰊄 ", 144 | }, 145 | }, 146 | -- keys = { 147 | -- { "", "AerialToggle", mode = { "n", "x", "o" }, desc = "Aerial Toggle" }, 148 | -- }, 149 | }, 150 | { 151 | "folke/todo-comments.nvim", -- Highlight and search for todo comments within the codebase 152 | event = "BufEnter", 153 | keys = { 154 | { 155 | "t", 156 | function() 157 | require("snacks").picker.todo_comments() 158 | end, 159 | desc = "Todo comments", 160 | }, 161 | }, 162 | opts = { 163 | signs = false, 164 | highlight = { 165 | keyword = "bg", 166 | }, 167 | keywords = { 168 | FIX = { icon = " " }, -- Custom fix icon 169 | }, 170 | }, 171 | }, 172 | } 173 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/heirline/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | "rebelot/heirline.nvim", 3 | lazy = true, 4 | config = function() 5 | require("heirline").setup({ 6 | winbar = require("plugins.heirline.winbar"), 7 | statusline = require("plugins.heirline.statusline"), 8 | statuscolumn = require("plugins.heirline.statuscolumn"), 9 | opts = { 10 | disable_winbar_cb = function(args) 11 | local conditions = require("heirline.conditions") 12 | 13 | return conditions.buffer_matches({ 14 | buftype = { "nofile", "prompt", "help", "quickfix", "terminal" }, 15 | filetype = { "alpha", "codecompanion", "oil", "lspinfo", "snacks_dashboard", "toggleterm" }, 16 | }, args.buf) 17 | end, 18 | }, 19 | }) 20 | end, 21 | } 22 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/heirline/statuscolumn.lua: -------------------------------------------------------------------------------- 1 | local v, fn, api = vim.v, vim.fn, vim.api 2 | 3 | local conditions = require("heirline.conditions") 4 | 5 | local align = { provider = "%=" } 6 | local spacer = { provider = " ", hl = "HeirlineStatusColumn" } 7 | 8 | local git_ns = api.nvim_create_namespace("gitsigns_signs_") 9 | local function get_signs(bufnr, lnum) 10 | local signs = {} 11 | 12 | local extmarks = api.nvim_buf_get_extmarks( 13 | 0, 14 | -1, 15 | { lnum - 1, 0 }, 16 | { lnum - 1, -1 }, 17 | { details = true, type = "sign" } 18 | ) 19 | 20 | for _, extmark in pairs(extmarks) do 21 | -- Exclude gitsigns 22 | if extmark[4].ns_id ~= git_ns then 23 | signs[#signs + 1] = { 24 | name = extmark[4].sign_hl_group or "", 25 | text = extmark[4].sign_text, 26 | sign_hl_group = extmark[4].sign_hl_group, 27 | priority = extmark[4].priority, 28 | } 29 | end 30 | end 31 | 32 | table.sort(signs, function(a, b) 33 | return (a.priority or 0) > (b.priority or 0) 34 | end) 35 | 36 | return signs 37 | end 38 | 39 | return { 40 | { 41 | condition = function() 42 | return not conditions.buffer_matches({ 43 | buftype = { "nofile", "prompt", "help", "quickfix", "terminal" }, 44 | filetype = { "alpha", "codecompanion", "harpoon", "oil", "lspinfo", "snacks_dashboard", "toggleterm" }, 45 | }) 46 | end, 47 | static = { 48 | bufnr = api.nvim_win_get_buf(0), 49 | click_args = function(self, minwid, clicks, button, mods) 50 | local args = { 51 | minwid = minwid, 52 | clicks = clicks, 53 | button = button, 54 | mods = mods, 55 | mousepos = fn.getmousepos(), 56 | } 57 | local sign = fn.screenstring(args.mousepos.screenrow, args.mousepos.screencol) 58 | if sign == " " then 59 | sign = fn.screenstring(args.mousepos.screenrow, args.mousepos.screencol - 1) 60 | end 61 | args.sign = self.signs[sign] 62 | api.nvim_set_current_win(args.mousepos.winid) 63 | api.nvim_win_set_cursor(0, { args.mousepos.line, 0 }) 64 | 65 | return args 66 | end, 67 | resolve = function(self, name) 68 | for pattern, callback in pairs(self.handlers.Signs) do 69 | if name:match(pattern) then 70 | return vim.defer_fn(callback, 100) 71 | end 72 | end 73 | end, 74 | handlers = { 75 | Signs = { 76 | ["Neotest.*"] = function(self, args) 77 | require("neotest").run.run() 78 | end, 79 | ["Debug.*"] = function(self, args) 80 | require("dap").continue() 81 | end, 82 | ["Diagnostic.*"] = function(self, args) 83 | vim.diagnostic.open_float() 84 | end, 85 | ["LspLightBulb"] = function(self, args) 86 | vim.lsp.buf.code_action() 87 | end, 88 | }, 89 | Dap = function(self, args) 90 | require("dap").toggle_breakpoint() 91 | end, 92 | Fold = function(args) 93 | local line = args.mousepos.line 94 | if fn.foldlevel(line) <= fn.foldlevel(line - 1) then 95 | return 96 | end 97 | vim.cmd.execute("'" .. line .. "fold" .. (fn.foldclosed(line) == -1 and "close" or "open") .. "'") 98 | end, 99 | GitSigns = function(self, args) 100 | vim.defer_fn(function() 101 | require("snacks").git.blame_line() 102 | end, 100) 103 | end, 104 | }, 105 | }, 106 | init = function(self) 107 | self.signs = {} 108 | end, 109 | -- Signs (except for GitSigns) 110 | { 111 | init = function(self) 112 | local signs = get_signs(self.bufnr, v.lnum) 113 | self.sign = signs[1] 114 | end, 115 | provider = function(self) 116 | return self.sign and self.sign.text or " " 117 | end, 118 | hl = function(self) 119 | return self.sign and self.sign.sign_hl_group 120 | end, 121 | on_click = { 122 | name = "sc_sign_click", 123 | update = true, 124 | callback = function(self, ...) 125 | local line = self.click_args(self, ...).mousepos.line 126 | local sign = get_signs(self.bufnr, line)[1] 127 | if sign then 128 | self:resolve(sign.name) 129 | end 130 | end, 131 | }, 132 | }, 133 | align, 134 | -- Line Numbers 135 | { 136 | provider = "%=%4{v:virtnum ? '' : &nu ? (&rnu && v:relnum ? v:relnum : v:lnum) . ' ' : ''}", 137 | on_click = { 138 | name = "sc_linenumber_click", 139 | callback = function(self, ...) 140 | self.handlers.Dap(self.click_args(self, ...)) 141 | end, 142 | }, 143 | }, 144 | -- Folds 145 | { 146 | init = function(self) 147 | self.can_fold = fn.foldlevel(v.lnum) > fn.foldlevel(v.lnum - 1) 148 | end, 149 | { 150 | condition = function(self) 151 | return v.virtnum == 0 and self.can_fold 152 | end, 153 | provider = "%C", 154 | }, 155 | { 156 | condition = function(self) 157 | return not self.can_fold 158 | end, 159 | spacer, 160 | }, 161 | on_click = { 162 | name = "sc_fold_click", 163 | callback = function(self, ...) 164 | self.handlers.Fold(self.click_args(self, ...)) 165 | end, 166 | }, 167 | }, 168 | -- Git Signs 169 | { 170 | { 171 | condition = function() 172 | return conditions.is_git_repo() 173 | end, 174 | init = function(self) 175 | local extmark = api.nvim_buf_get_extmarks( 176 | 0, 177 | git_ns, 178 | { v.lnum - 1, 0 }, 179 | { v.lnum - 1, -1 }, 180 | { limit = 1, details = true } 181 | )[1] 182 | 183 | self.sign = extmark and extmark[4]["sign_hl_group"] 184 | end, 185 | { 186 | provider = "│", 187 | hl = function(self) 188 | return self.sign or { fg = "bg" } 189 | end, 190 | on_click = { 191 | name = "sc_gitsigns_click", 192 | callback = function(self, ...) 193 | self.handlers.GitSigns(self.click_args(self, ...)) 194 | end, 195 | }, 196 | }, 197 | }, 198 | { 199 | condition = function() 200 | return not conditions.is_git_repo() 201 | end, 202 | spacer, 203 | }, 204 | }, 205 | }, 206 | } 207 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/heirline/winbar.lua: -------------------------------------------------------------------------------- 1 | local conditions = require("heirline.conditions") 2 | local bit = require("bit") 3 | 4 | local sep = "  " 5 | 6 | local Spacer = { 7 | provider = " ", 8 | } 9 | 10 | local VimLogo = { 11 | provider = " ", 12 | hl = "VimLogo", 13 | } 14 | 15 | local Filepath = { 16 | static = { 17 | modifiers = { 18 | dirname = ":s?/Users/Oli/.dotfiles?dotfiles?:s?.config/nvim/lua/Oli?Neovim?:s?/Users/Oli/Code?Code?", 19 | }, 20 | }, 21 | init = function(self) 22 | self.current_dir = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":h") 23 | 24 | self.filepath = vim.fn.fnamemodify(self.current_dir, self.modifiers.dirname or nil) 25 | self.short_path = vim.fn.fnamemodify(vim.fn.expand("%:h"), self.modifiers.dirname or nil) 26 | if self.filepath == "" then 27 | self.filepath = "[No Name]" 28 | end 29 | end, 30 | hl = "HeirlineWinbar", 31 | { 32 | condition = function(self) 33 | return self.filepath ~= "." 34 | end, 35 | flexible = 2, 36 | { 37 | provider = function(self) 38 | return table.concat(vim.fn.split(self.filepath, "/"), sep) .. sep 39 | end, 40 | }, 41 | { 42 | provider = function(self) 43 | local filepath = vim.fn.pathshorten(self.short_path) 44 | return table.concat(vim.fn.split(self.short_path, "/"), sep) .. sep 45 | end, 46 | }, 47 | { 48 | provider = "", 49 | }, 50 | }, 51 | } 52 | 53 | local FileIcon = { 54 | init = function(self) 55 | self.icon, self.icon_color = 56 | require("nvim-web-devicons").get_icon_color_by_filetype(vim.bo.filetype, { default = true }) 57 | end, 58 | provider = function(self) 59 | return self.icon and (self.icon .. " ") 60 | end, 61 | hl = function(self) 62 | return { fg = self.icon_color } 63 | end, 64 | } 65 | 66 | local FileType = { 67 | condition = function() 68 | return vim.bo.filetype ~= "" 69 | end, 70 | FileIcon, 71 | } 72 | 73 | local FileName = { 74 | static = { 75 | modifiers = { 76 | dirname = ":s?/Users/Oli/.dotfiles?dotfiles?:s?.config/nvim/lua?Neovim?:s?/Users/Oli/Code?Code?", 77 | }, 78 | }, 79 | init = function(self) 80 | local filename 81 | local has_oil, oil = pcall(require, "oil") 82 | if has_oil then 83 | filename = oil.get_current_dir() 84 | end 85 | if not filename then 86 | filename = 87 | vim.fn.fnamemodify(vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":."), self.modifiers.dirname or nil) 88 | end 89 | if filename == "" then 90 | self.path = "" 91 | self.name = "[No Name]" 92 | return 93 | end 94 | -- now, if the filename would occupy more than 90% of the available 95 | -- space, we trim the file path to its initials 96 | if not conditions.width_percent_below(#filename, 0.90) then 97 | filename = vim.fn.pathshorten(filename) 98 | end 99 | 100 | self.path = filename:match("^(.*)/") 101 | self.name = filename:match("([^/]+)$") 102 | end, 103 | { 104 | provider = function(self) 105 | if self.path then 106 | return self.path .. "/" 107 | end 108 | end, 109 | hl = "HeirlineWinbar", 110 | }, 111 | { 112 | provider = function(self) 113 | return self.name 114 | end, 115 | hl = "HeirlineWinbarEmphasis", 116 | }, 117 | on_click = { 118 | callback = function(self) 119 | require("aerial").toggle() 120 | end, 121 | name = "wb_filename_click", 122 | }, 123 | } 124 | local FileFlags = { 125 | { 126 | condition = function() 127 | return vim.bo.modified 128 | end, 129 | provider = " ", 130 | hl = { fg = "red" }, 131 | }, 132 | { 133 | condition = function() 134 | return not vim.bo.modifiable or vim.bo.readonly 135 | end, 136 | provider = " ", 137 | hl = { fg = "blue" }, 138 | }, 139 | } 140 | 141 | -- Inspired by: 142 | -- https://github.com/eli-front/nvim-config/blob/5a225e1e6de3d6f1bdca2025602c3e7a4917e31b/lua/elifront/utils/status/init.lua#L32 143 | local Symbols = { 144 | init = function(self) 145 | self.symbols = require("aerial").get_location(true) or {} 146 | end, 147 | update = "CursorMoved", 148 | { 149 | condition = function(self) 150 | if vim.tbl_isempty(self.symbols) then 151 | return false 152 | end 153 | return true 154 | end, 155 | { 156 | flexible = 3, 157 | { 158 | provider = function(self) 159 | local symbols = {} 160 | 161 | table.insert(symbols, { provider = sep }) 162 | 163 | for i, d in ipairs(self.symbols) do 164 | local symbol = { 165 | -- Name 166 | { provider = string.gsub(d.name, "%%", "%%%%"):gsub("%s*->%s*", "") }, 167 | 168 | -- On-Click action 169 | on_click = { 170 | minwid = self.encode_pos(d.lnum, d.col, self.winnr), 171 | callback = function(_, minwid) 172 | local lnum, col, winnr = self.decode_pos(minwid) 173 | vim.api.nvim_win_set_cursor(vim.fn.win_getid(winnr), { lnum, col }) 174 | end, 175 | name = "wb_symbol_click", 176 | }, 177 | } 178 | 179 | -- Icon 180 | local hlgroup = string.format("Aerial%sIcon", d.kind) 181 | table.insert(symbol, 1, { 182 | provider = string.format("%s", d.icon), 183 | hl = (vim.fn.hlexists(hlgroup) == 1) and hlgroup or nil, 184 | }) 185 | 186 | if #self.symbols >= 1 and i < #self.symbols then 187 | table.insert(symbol, { provider = sep }) 188 | end 189 | 190 | table.insert(symbols, symbol) 191 | end 192 | 193 | self[1] = self:new(symbols, 1) 194 | end, 195 | }, 196 | { 197 | provider = "", 198 | }, 199 | }, 200 | hl = "Comment", 201 | }, 202 | } 203 | 204 | return { 205 | static = { 206 | encode_pos = function(line, col, winnr) 207 | return bit.bor(bit.lshift(line, 16), bit.lshift(col, 6), winnr) 208 | end, 209 | decode_pos = function(c) 210 | return bit.rshift(c, 16), bit.band(bit.rshift(c, 6), 1023), bit.band(c, 63) 211 | end, 212 | }, 213 | Spacer, 214 | -- Filepath, 215 | FileType, 216 | FileName, 217 | FileFlags, 218 | Symbols, 219 | { provider = "%=" }, 220 | VimLogo, 221 | } 222 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/lsp.lua: -------------------------------------------------------------------------------- 1 | local icons = { 2 | [vim.diagnostic.severity.ERROR] = "", 3 | [vim.diagnostic.severity.WARN] = "", 4 | [vim.diagnostic.severity.INFO] = "", 5 | [vim.diagnostic.severity.HINT] = "", 6 | } 7 | 8 | return { 9 | -- LSP 10 | { 11 | "mason-org/mason.nvim", 12 | opts = { 13 | ui = { 14 | border = "single", 15 | width = 0.9, 16 | }, 17 | }, 18 | }, 19 | { 20 | "mason-org/mason-lspconfig.nvim", 21 | dependencies = { 22 | "neovim/nvim-lspconfig", 23 | }, 24 | opts = {}, 25 | config = function(_, opts) 26 | require("lspconfig.ui.windows").default_options.border = "single" 27 | -- require("luasnip.loaders.from_vscode").lazy_load({ paths = { "~/.config/snippets" } }) 28 | 29 | require("ufo").setup() 30 | local has_blink, blink = pcall(require, "blink.cmp") 31 | local capabilities = 32 | vim.tbl_deep_extend("force", has_blink and blink.get_lsp_capabilities() or {}, opts.capabilities or {}, { 33 | textDocument = { 34 | foldingRange = { 35 | dynamicRegistration = false, 36 | lineFoldingOnly = true, 37 | }, 38 | }, 39 | }) 40 | 41 | local lspconfig_defaults = require("lspconfig").util.default_config 42 | lspconfig_defaults.capabilities = vim.tbl_deep_extend("force", lspconfig_defaults.capabilities, capabilities) 43 | 44 | -- LspAttach is where you enable features that only work 45 | -- if there is a language server active in the file 46 | vim.api.nvim_create_autocmd("LspAttach", { 47 | group = vim.api.nvim_create_augroup("lsp-attach", { clear = true }), 48 | desc = "LSP actions", 49 | callback = function(event) 50 | local bufnr = event.buf 51 | 52 | ---Shortcut function to map keys 53 | ---@param keys string|table 54 | ---@param func function|string 55 | ---@param desc string 56 | ---@param mode? string|table 57 | ---@return nil 58 | local map = function(keys, func, desc, mode) 59 | mode = mode or "n" 60 | vim.keymap.set(mode, keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) 61 | end 62 | 63 | map("gf", function() 64 | Snacks.picker.diagnostics_buffer() 65 | end, "Find Diagnostics") 66 | map("gq", function() 67 | require("conform").format({ async = true, bufnr = bufnr, lsp_format = "fallback" }) 68 | end, "Format") 69 | map("gr", function() 70 | Snacks.picker.lsp_references() 71 | end, "Find References") 72 | map("gd", function() 73 | Snacks.picker.lsp_definitions() 74 | end, "Go to definition") 75 | map( 76 | "gl", 77 | "lua vim.diagnostic.open_float(0, { border = 'single', source = 'always' })", 78 | "Show Line Diagnostics" 79 | ) 80 | map("K", "lua vim.lsp.buf.hover", "Show hover information") 81 | map("gI", function() 82 | Snacks.picker.lsp_implementations() 83 | end, "Go to [I]mplementation") 84 | map("gy", function() 85 | Snacks.picker.lsp_type_definitions() 86 | end, "Go to T[y]pe Definition") 87 | map("ga", vim.lsp.buf.code_action, "[G]oto Code [A]ction", { "n", "x" }) 88 | map("grn", vim.lsp.buf.rename, "[R]e[n]ame") 89 | map("[", function() 90 | vim.diagnostic.jump({ count = -1, float = true }) 91 | end, "Go to previous diagnostic item") 92 | map("]", function() 93 | vim.diagnostic.jump({ count = 1, float = true }) 94 | end, "Go to next diagnostic item") 95 | 96 | -- This function resolves a difference between neovim nightly (version 0.11) and stable (version 0.10) 97 | ---@param client vim.lsp.Client 98 | ---@param method vim.lsp.protocol.Method 99 | ---@param bufnr? integer some lsp support methods only in specific files 100 | ---@return boolean 101 | local function client_supports_method(client, method, bufnr) 102 | return client:supports_method(method, bufnr) 103 | end 104 | 105 | local client = vim.lsp.get_client_by_id(event.data.client_id) 106 | if 107 | client 108 | and client_supports_method(client, vim.lsp.protocol.Methods.textDocument_documentHighlight, event.buf) 109 | then 110 | local highlight_augroup = vim.api.nvim_create_augroup("lsp-highlight", { clear = false }) 111 | vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { 112 | buffer = event.buf, 113 | group = highlight_augroup, 114 | callback = vim.lsp.buf.document_highlight, 115 | }) 116 | 117 | vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, { 118 | buffer = event.buf, 119 | group = highlight_augroup, 120 | callback = vim.lsp.buf.clear_references, 121 | }) 122 | 123 | vim.api.nvim_create_autocmd("LspDetach", { 124 | group = vim.api.nvim_create_augroup("lsp-detach", { clear = true }), 125 | callback = function(event2) 126 | vim.lsp.buf.clear_references() 127 | vim.api.nvim_clear_autocmds({ group = "lsp-highlight", buffer = event2.buf }) 128 | end, 129 | }) 130 | end 131 | end, 132 | }) 133 | 134 | require("mason-lspconfig").setup({ 135 | ensure_installed = { 136 | "intelephense", 137 | "jdtls", 138 | "jsonls", 139 | "lua_ls", 140 | "pyright", 141 | "ruby_lsp", 142 | "vuels", 143 | "yamlls", 144 | }, 145 | handlers = { 146 | -- this first function is the "default handler" 147 | -- it applies to every language server without a "custom handler" 148 | function(ls) 149 | require("lspconfig")[ls].setup({ 150 | capabilities = capabilities, 151 | }) 152 | end, 153 | }, 154 | }) 155 | 156 | vim.diagnostic.config({ 157 | severity_sort = true, 158 | signs = { 159 | text = icons, 160 | }, 161 | underline = false, 162 | update_in_insert = true, 163 | virtual_text = false, 164 | -- virtual_text = { 165 | -- prefix = "", 166 | -- spacing = 0, 167 | -- }, 168 | }) 169 | end, 170 | }, 171 | 172 | -- Completion 173 | { 174 | "saghen/blink.cmp", 175 | dependencies = { 176 | "rafamadriz/friendly-snippets", 177 | "onsails/lspkind.nvim", 178 | }, 179 | version = "*", 180 | opts = { 181 | cmdline = { sources = { "cmdline" } }, 182 | sources = { 183 | default = { "lsp", "path", "snippets", "buffer" }, 184 | providers = { 185 | lsp = { 186 | min_keyword_length = 2, -- Number of characters to trigger porvider 187 | score_offset = 0, -- Boost/penalize the score of the items 188 | }, 189 | path = { 190 | min_keyword_length = 0, 191 | }, 192 | snippets = { 193 | min_keyword_length = 1, 194 | opts = { 195 | search_paths = { "~/.config/snippets" }, 196 | }, 197 | }, 198 | buffer = { 199 | min_keyword_length = 5, 200 | max_items = 5, 201 | }, 202 | }, 203 | }, 204 | 205 | appearance = { 206 | use_nvim_cmp_as_default = false, 207 | nerd_font_variant = "mono", 208 | }, 209 | 210 | completion = { 211 | accept = { auto_brackets = { enabled = true } }, 212 | 213 | documentation = { 214 | auto_show = true, 215 | auto_show_delay_ms = 250, 216 | treesitter_highlighting = true, 217 | window = { border = "rounded" }, 218 | }, 219 | 220 | list = { 221 | selection = { 222 | preselect = false, 223 | auto_insert = function(ctx) 224 | return ctx.mode == "cmdline" and "auto_insert" or "preselect" 225 | end, 226 | }, 227 | }, 228 | 229 | menu = { 230 | border = "rounded", 231 | 232 | cmdline_position = function() 233 | if vim.g.ui_cmdline_pos ~= nil then 234 | local pos = vim.g.ui_cmdline_pos -- (1, 0)-indexed 235 | return { pos[1] - 1, pos[2] } 236 | end 237 | local height = (vim.o.cmdheight == 0) and 1 or vim.o.cmdheight 238 | return { vim.o.lines - height, 0 } 239 | end, 240 | 241 | draw = { 242 | columns = { 243 | { "kind_icon", "label", gap = 1 }, 244 | { "kind" }, 245 | }, 246 | components = { 247 | kind_icon = { 248 | text = function(item) 249 | local kind = require("lspkind").symbol_map[item.kind] or "" 250 | return kind .. " " 251 | end, 252 | highlight = "CmpItemKind", 253 | }, 254 | label = { 255 | text = function(item) 256 | return item.label 257 | end, 258 | highlight = "CmpItemAbbr", 259 | }, 260 | kind = { 261 | text = function(item) 262 | return item.kind 263 | end, 264 | highlight = "CmpItemKind", 265 | }, 266 | }, 267 | }, 268 | }, 269 | }, 270 | 271 | -- My super-TAB configuration 272 | keymap = { 273 | [""] = { "show", "show_documentation", "hide_documentation" }, 274 | [""] = { "hide", "fallback" }, 275 | [""] = { "accept", "fallback" }, 276 | 277 | [""] = { 278 | function(cmp) 279 | return cmp.select_next() 280 | end, 281 | "snippet_forward", 282 | "fallback", 283 | }, 284 | [""] = { 285 | function(cmp) 286 | return cmp.select_prev() 287 | end, 288 | "snippet_backward", 289 | "fallback", 290 | }, 291 | 292 | [""] = { "select_prev", "fallback" }, 293 | [""] = { "select_next", "fallback" }, 294 | [""] = { "select_prev", "fallback" }, 295 | [""] = { "select_next", "fallback" }, 296 | [""] = { "scroll_documentation_up", "fallback" }, 297 | [""] = { "scroll_documentation_down", "fallback" }, 298 | }, 299 | 300 | -- Experimental signature help support 301 | signature = { 302 | enabled = true, 303 | window = { border = "rounded" }, 304 | }, 305 | }, 306 | }, 307 | 308 | -- Diagnostic signs 309 | { 310 | "ivanjermakov/troublesum.nvim", 311 | event = "LspAttach", 312 | opts = { 313 | enabled = function() 314 | local ft = vim.bo.filetype 315 | return ft ~= "lazy" and ft ~= "mason" and ft ~= "codecompanion" 316 | end, 317 | severity_format = vim 318 | .iter(ipairs(icons)) 319 | :map(function(sev, icon) 320 | return icon 321 | end) 322 | :totable(), 323 | }, 324 | }, 325 | 326 | -- Formatting 327 | { 328 | "stevearc/conform.nvim", 329 | event = { "BufWritePre" }, 330 | cmd = { "ConformInfo" }, 331 | opts = { 332 | format_on_save = { 333 | timeout_ms = 500, 334 | lsp_fallback = true, 335 | }, 336 | formatters_by_ft = { 337 | css = { "prettier" }, 338 | html = { "prettier" }, 339 | javascript = { "prettier" }, 340 | json = { "prettier" }, 341 | lua = { "stylua" }, 342 | python = { "isort", "black" }, 343 | ruby = { "rubocop" }, 344 | }, 345 | }, 346 | }, 347 | } 348 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/treesitter.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "nvim-treesitter/nvim-treesitter", -- Smarter code understanding like syntax Highlight and navigation 4 | build = ":TSUpdate", 5 | dependencies = { 6 | "nvim-treesitter/nvim-treesitter-textobjects", -- Syntax aware text-objects, select, move, swap, and peek support. 7 | { 8 | "JoosepAlviste/nvim-ts-context-commentstring", -- Smart commenting in multi language files - Enabled in Treesitter file 9 | config = true, 10 | }, 11 | { 12 | "windwp/nvim-ts-autotag", -- Autoclose and autorename HTML and Vue tags 13 | config = true, 14 | }, 15 | "rrethy/nvim-treesitter-endwise", -- Automatically add end keywords for Ruby, Lua, Python, and more 16 | { 17 | "windwp/nvim-autopairs", -- Autopair plugin 18 | event = "InsertEnter", 19 | opts = { 20 | check_ts = true, 21 | enable_moveright = true, 22 | fast_wrap = { 23 | map = "", 24 | }, 25 | }, 26 | config = function(_, opts) 27 | local autopairs = require("nvim-autopairs") 28 | 29 | autopairs.setup(opts) 30 | 31 | local Rule = require("nvim-autopairs.rule") 32 | local ts_conds = require("nvim-autopairs.ts-conds") 33 | 34 | autopairs.add_rules({ 35 | Rule("{{", " }", "vue"):set_end_pair_length(2):with_pair(ts_conds.is_ts_node("text")), 36 | }) 37 | end, 38 | }, 39 | }, 40 | config = function() 41 | require("nvim-treesitter.configs").setup({ 42 | ensure_installed = "all", 43 | ignore_install = { "phpdoc" }, -- list of parser which cause issues or crashes 44 | highlight = { enable = true }, 45 | incremental_selection = { 46 | enable = true, 47 | keymaps = { 48 | init_selection = "", 49 | scope_incremental = "", 50 | node_incremental = "", -- increment to the upper named parent 51 | node_decremental = "", -- decrement to the previous node 52 | }, 53 | }, 54 | indent = { enable = true }, 55 | 56 | -- nvim-treesitter-endwise plugin 57 | endwise = { enable = true }, 58 | 59 | textobjects = { 60 | select = { 61 | enable = true, 62 | lookahead = true, -- Automatically jump forward to textobj, similar to targets.vim 63 | 64 | keymaps = { 65 | -- Use v[keymap], c[keymap], d[keymap] to perform any operation 66 | ["af"] = "@function.outer", 67 | ["if"] = "@function.inner", 68 | ["ac"] = "@class.outer", 69 | }, 70 | }, 71 | }, 72 | }) 73 | end, 74 | }, 75 | } 76 | 77 | -- TODO: for when Tree-sitter makes the main branch the default 78 | 79 | -- return { 80 | -- { 81 | -- "nvim-treesitter/nvim-treesitter", -- Smarter code understanding like syntax Highlight and navigation 82 | -- lazy = false, 83 | -- branch = "main", 84 | -- build = ":TSUpdate", 85 | -- cmd = { "TSUpdateSync", "TSUpdate", "TSInstall" }, 86 | -- event = { "BufReadPost", "BufNewFile", "BufWritePre" }, 87 | -- config = function() 88 | -- require("nvim-treesitter").install({ 89 | -- "c", 90 | -- "cpp", 91 | -- "cmake", 92 | -- "diff", 93 | -- "gitcommit", 94 | -- "gitignore", 95 | -- "go", 96 | -- "html", 97 | -- "json", 98 | -- "lua", 99 | -- "markdown", 100 | -- "markdown_inline", 101 | -- "php", 102 | -- "python", 103 | -- "ruby", 104 | -- "rust", 105 | -- "toml", 106 | -- "vim", 107 | -- "vimdoc", 108 | -- "vue", 109 | -- "yaml", 110 | -- }) 111 | -- end, 112 | -- }, 113 | -- { 114 | -- "nvim-treesitter/nvim-treesitter-textobjects", -- Syntax aware text-objects, select, move, swap, and peek support. 115 | -- branch = "main", 116 | -- keys = { 117 | -- { 118 | -- "af", 119 | -- function() 120 | -- require("nvim-treesitter-textobjects.select").select_textobject("@function.outer", "textobjects") 121 | -- end, 122 | -- desc = "Select outer function", 123 | -- mode = { "x", "o" }, 124 | -- }, 125 | -- { 126 | -- "if", 127 | -- function() 128 | -- require("nvim-treesitter-textobjects.select").select_textobject("@function.inner", "textobjects") 129 | -- end, 130 | -- desc = "Select inner function", 131 | -- mode = { "x", "o" }, 132 | -- }, 133 | -- { 134 | -- "ac", 135 | -- function() 136 | -- require("nvim-treesitter-textobjects.select").select_textobject("@class.outer", "textobjects") 137 | -- end, 138 | -- desc = "Select outer class", 139 | -- mode = { "x", "o" }, 140 | -- }, 141 | -- { 142 | -- "ic", 143 | -- function() 144 | -- require("nvim-treesitter-textobjects.select").select_textobject("@class.inner", "textobjects") 145 | -- end, 146 | -- desc = "Select inner class", 147 | -- mode = { "x", "o" }, 148 | -- }, 149 | -- { 150 | -- "as", 151 | -- function() 152 | -- require("nvim-treesitter-textobjects.select").select_textobject("@local.scope", "locals") 153 | -- end, 154 | -- desc = "Select local scope", 155 | -- mode = { "x", "o" }, 156 | -- }, 157 | -- }, 158 | -- opts = {}, 159 | -- }, 160 | -- "JoosepAlviste/nvim-ts-context-commentstring", -- Smart commenting in multi language files - Enabled in Treesitter file 161 | -- "windwp/nvim-ts-autotag", -- Autoclose and autorename HTML and Vue tags 162 | -- --"rrethy/nvim-treesitter-endwise", -- Automatically add end keywords for Ruby, Lua, Python, and more 163 | -- { 164 | -- "windwp/nvim-autopairs", -- Autopair plugin 165 | -- event = "InsertEnter", 166 | -- opts = { 167 | -- check_ts = true, 168 | -- enable_moveright = true, 169 | -- fast_wrap = { 170 | -- map = "", 171 | -- }, 172 | -- }, 173 | -- config = function(_, opts) 174 | -- local autopairs = require("nvim-autopairs") 175 | -- 176 | -- autopairs.setup(opts) 177 | -- 178 | -- local Rule = require("nvim-autopairs.rule") 179 | -- local ts_conds = require("nvim-autopairs.ts-conds") 180 | -- 181 | -- autopairs.add_rules({ 182 | -- Rule("{{", " }", "vue"):set_end_pair_length(2):with_pair(ts_conds.is_ts_node("text")), 183 | -- }) 184 | -- end, 185 | -- }, 186 | -- } 187 | -- 188 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/ui.lua: -------------------------------------------------------------------------------- 1 | return { 2 | "nvim-tree/nvim-web-devicons", 3 | { 4 | "nmac427/guess-indent.nvim", -- Automatically detects which indents should be used in the current buffer 5 | opts = {}, 6 | }, 7 | { 8 | "MeanderingProgrammer/render-markdown.nvim", -- Make Markdown buffers look beautiful 9 | ft = { "markdown", "codecompanion" }, 10 | opts = { 11 | render_modes = true, -- Render in ALL modes 12 | sign = { 13 | enabled = false, -- Turn off in the status column 14 | }, 15 | }, 16 | }, 17 | { 18 | "folke/edgy.nvim", -- Create predefined window layouts 19 | event = "VeryLazy", 20 | init = function() 21 | vim.opt.laststatus = 3 22 | vim.opt.splitkeep = "screen" 23 | end, 24 | opts = { 25 | animate = { enabled = false }, 26 | options = { 27 | top = { size = 10 }, 28 | }, 29 | bottom = { 30 | { 31 | ft = "snacks_terminal", 32 | size = { height = om.on_big_screen() and 20 or 0.2 }, 33 | title = "Terminal %{b:snacks_terminal.id}", 34 | filter = function(_buf, win) 35 | return vim.w[win].snacks_win 36 | and vim.w[win].snacks_win.position == "bottom" 37 | and vim.w[win].snacks_win.relative == "editor" 38 | and not vim.w[win].trouble_preview 39 | end, 40 | }, 41 | { ft = "qf", title = "QuickFix" }, 42 | { 43 | ft = "help", 44 | size = { height = 20 }, 45 | -- only show help buffers 46 | filter = function(buf) 47 | return vim.bo[buf].buftype == "help" 48 | end, 49 | }, 50 | { title = "Neotest Output", ft = "neotest-output-panel", size = { height = 15 } }, 51 | }, 52 | right = { 53 | { ft = "aerial", title = "Symbols", size = { width = 0.3 } }, 54 | { ft = "neotest-summary", title = "Neotest Summary", size = { width = 0.3 } }, 55 | { ft = "oil", title = "File Explorer", size = { width = 0.3 } }, 56 | }, 57 | }, 58 | }, 59 | { 60 | "lukas-reineke/virt-column.nvim", -- Use characters in the color column 61 | opts = { 62 | char = "│", 63 | highlight = "VirtColumn", 64 | }, 65 | }, 66 | { 67 | "lewis6991/gitsigns.nvim", -- Git signs in the statuscolumn 68 | opts = { 69 | signs = { 70 | add = { text = "│" }, 71 | change = { text = "│" }, 72 | delete = { text = "│" }, 73 | topdelete = { text = "│" }, 74 | changedelete = { text = "│" }, 75 | untracked = { text = "│" }, 76 | }, 77 | signs_staged_enable = false, 78 | numhl = false, 79 | linehl = false, 80 | }, 81 | }, 82 | { 83 | "NvChad/nvim-colorizer.lua", -- Highlight hex and rgb colors within Neovim 84 | cmd = "ColorizerToggle", 85 | opts = { 86 | filetypes = { 87 | "css", 88 | eruby = { mode = "foreground" }, 89 | html = { mode = "foreground" }, 90 | "lua", 91 | "javascript", 92 | "vue", 93 | }, 94 | }, 95 | }, 96 | { 97 | "folke/snacks.nvim", 98 | priority = 1000, 99 | lazy = false, 100 | opts = { 101 | styles = { 102 | lazygit = { 103 | width = 0, 104 | height = 0, 105 | }, 106 | notification = { 107 | wo = { wrap = true }, -- Wrap notifications 108 | }, 109 | }, 110 | bigfile = { enabled = true }, 111 | bufdelete = { enabled = true }, 112 | dashboard = { 113 | enabled = true, 114 | preset = { 115 | keys = { 116 | { icon = " ", key = "l", desc = "Load Session", action = ":lua require('persisted').load()" }, 117 | { icon = " ", key = "n", desc = "New File", action = ":ene | startinsert" }, 118 | { icon = " ", key = "r", desc = "Recent Files", action = ":lua Snacks.dashboard.pick('oldfiles')" }, 119 | { icon = " ", key = "f", desc = "Find File", action = ":lua Snacks.dashboard.pick('files')" }, 120 | { icon = "󱘣 ", key = "s", desc = "Search Files", action = ":lua Snacks.dashboard.pick('live_grep')" }, 121 | { icon = "󰒲 ", key = "L", desc = "Lazy", action = ":Lazy", enabled = package.loaded.lazy ~= nil }, 122 | { icon = " ", key = "q", desc = "Quit", action = ":qa" }, 123 | }, 124 | }, 125 | sections = { 126 | { 127 | section = "terminal", 128 | cmd = "lolcat --seed=24 ~/.config/nvim/static/neovim.cat", 129 | indent = -5, 130 | height = 9, 131 | width = 69, 132 | padding = 1, 133 | }, 134 | { 135 | section = "keys", 136 | indent = 1, 137 | padding = 1, 138 | }, 139 | { section = "startup" }, 140 | }, 141 | }, 142 | image = { 143 | enabled = true, 144 | img_dirs = { 145 | "~/Documents/Screenshots/", 146 | }, 147 | }, 148 | indent = { 149 | enabled = true, 150 | scope = { enabled = false }, 151 | }, 152 | input = { enabled = true }, 153 | lazygit = { 154 | theme = { 155 | [241] = { fg = "Special" }, 156 | activeBorderColor = { fg = "String", bold = true }, 157 | cherryPickedCommitBgColor = { fg = "Identifier" }, 158 | cherryPickedCommitFgColor = { fg = "Function" }, 159 | defaultFgColor = { fg = "Normal" }, 160 | inactiveBorderColor = { fg = "FloatBorder" }, 161 | optionsTextColor = { fg = "Function" }, 162 | searchingActiveBorderColor = { fg = "String", bold = true }, 163 | selectedLineBgColor = { bg = "Visual" }, -- set to `default` to have no background colour 164 | unstagedChangesColor = { fg = "DiagnosticError" }, 165 | }, 166 | }, 167 | picker = { 168 | prompt = "> ", 169 | enabled = true, 170 | win = { 171 | input = { 172 | wo = { 173 | foldcolumn = "0", 174 | }, 175 | }, 176 | list = { 177 | wo = { 178 | foldcolumn = "0", 179 | }, 180 | }, 181 | preview = { 182 | wo = { 183 | foldcolumn = "0", 184 | }, 185 | }, 186 | }, 187 | }, 188 | notifier = { enabled = true }, 189 | terminal = { enabled = true }, 190 | }, 191 | keys = { 192 | { 193 | "", 194 | function() 195 | Snacks.bufdelete() 196 | end, 197 | desc = "Delete Buffer", 198 | }, 199 | { 200 | "", 201 | function() 202 | Snacks.picker.files({ hidden = true }) 203 | end, 204 | desc = "Find Files", 205 | }, 206 | { 207 | "", 208 | function() 209 | Snacks.picker.buffers() 210 | end, 211 | desc = "List Buffers", 212 | }, 213 | { 214 | "", 215 | function() 216 | Snacks.picker.grep_buffers() 217 | end, 218 | desc = "Search Open Buffers", 219 | }, 220 | { 221 | "g", 222 | function() 223 | Snacks.picker.grep() 224 | end, 225 | desc = "Search Files", 226 | }, 227 | { 228 | "", 229 | function() 230 | Snacks.picker.recent() 231 | end, 232 | desc = "List Recent Files", 233 | }, 234 | { 235 | "h", 236 | function() 237 | Snacks.picker.notifications() 238 | end, 239 | desc = "Show Notifications", 240 | }, 241 | { 242 | "gb", 243 | function() 244 | Snacks.gitbrowse() 245 | end, 246 | desc = "Git Browse", 247 | }, 248 | { 249 | "u", 250 | function() 251 | Snacks.picker.undo() 252 | end, 253 | desc = "Undo History", 254 | }, 255 | { 256 | "", 257 | function() 258 | Snacks.terminal.toggle() 259 | end, 260 | mode = { "n", "t" }, 261 | desc = "Toggle Terminal", 262 | }, 263 | { 264 | "l", 265 | function() 266 | Snacks.lazygit() 267 | end, 268 | desc = "Open LazyGit", 269 | }, 270 | { 271 | "", 272 | "", 273 | mode = "t", 274 | desc = "Escape means escape", 275 | nowait = true, 276 | }, 277 | }, 278 | }, 279 | { 280 | "hat0uma/csvview.nvim", -- Make CSV files great again 281 | opts = { 282 | parser = { comments = { "#", "//" } }, 283 | keymaps = { 284 | -- Text objects for selecting fields 285 | textobject_field_inner = { "if", mode = { "o", "x" } }, 286 | textobject_field_outer = { "af", mode = { "o", "x" } }, 287 | -- Excel-like navigation: 288 | -- Use and to move horizontally between fields. 289 | -- Use and to move vertically between rows and place the cursor at the end of the field. 290 | -- Note: In terminals, you may need to enable CSI-u mode to use and . 291 | jump_next_field_end = { "", mode = { "n", "v" } }, 292 | jump_prev_field_end = { "", mode = { "n", "v" } }, 293 | jump_next_row = { "", mode = { "n", "v" } }, 294 | jump_prev_row = { "", mode = { "n", "v" } }, 295 | }, 296 | }, 297 | cmd = { "Csv", "CsvViewEnable", "CsvViewDisable", "CsvViewToggle" }, 298 | init = function() 299 | vim.api.nvim_create_user_command("Csv", function() 300 | require("csvview").toggle(0, { 301 | parser = { 302 | delimiter = ",", 303 | quote_char = '"', 304 | comment_char = "#", 305 | }, 306 | view = { 307 | display_mode = "border", 308 | header_lnum = 1, 309 | }, 310 | }) 311 | end, { 312 | desc = "Toggle CSV view", 313 | }) 314 | end, 315 | }, 316 | } 317 | -------------------------------------------------------------------------------- /.config/nvim/lua/plugins/utilities.lua: -------------------------------------------------------------------------------- 1 | return { 2 | "nvim-lua/plenary.nvim", -- Required dependency for many plugins. Super useful Lua functions 3 | { 4 | "olimorris/persisted.nvim", -- Session management 5 | event = "BufReadPre", 6 | opts = { 7 | save_dir = Sessiondir .. "/", 8 | use_git_branch = true, 9 | autosave = true, 10 | -- autoload = true, 11 | -- allowed_dirs = { 12 | -- "~/Code", 13 | -- }, 14 | -- on_autoload_no_session = function() 15 | -- return vim.notify("No session found", vim.log.levels.WARN) 16 | -- end, 17 | should_save = function() 18 | local excluded_filetypes = { 19 | "alpha", 20 | "oil", 21 | "lazy", 22 | "", 23 | } 24 | 25 | for _, filetype in ipairs(excluded_filetypes) do 26 | if vim.bo.filetype == filetype then 27 | return false 28 | end 29 | end 30 | 31 | return true 32 | end, 33 | }, 34 | init = function() 35 | vim.api.nvim_create_user_command("Sessions", function() 36 | require("persisted").select() 37 | end, { 38 | desc = "List Sessions", 39 | }) 40 | end, 41 | }, 42 | { 43 | "kevinhwang91/nvim-bqf", -- Better quickfix window, 44 | ft = "qf", 45 | }, 46 | } 47 | -------------------------------------------------------------------------------- /.config/nvim/lua/util/init.lua: -------------------------------------------------------------------------------- 1 | local utils = { 2 | "marks", 3 | } 4 | 5 | for _, util in ipairs(utils) do 6 | pcall(require, "util." .. util) 7 | end 8 | -------------------------------------------------------------------------------- /.config/nvim/lua/util/marks.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Marks / Bookmarks / Harpoon Replacement in c.60 LOC 3 | Uses m{1-9} to set marks in a file and then '{1-9} to jump to them 4 | 5 | This is the combination of some awesome work over at: 6 | https://github.com/neovim/neovim/discussions/33335 7 | 8 | I then borrowed some simplification ideas from: 9 | https://github.com/LJFRIESE/nvim/blob/master/lua/config/autocmds.lua#L196-L340 10 | --]] 11 | 12 | -- Convert a mark number (1-9) to its corresponding character (A-I) 13 | local function mark2char(mark) 14 | if mark:match("[1-9]") then 15 | return string.char(mark + 64) 16 | end 17 | return mark 18 | end 19 | 20 | -- List bookmarks in the session 21 | local function list_marks() 22 | local snacks = require("snacks") 23 | return snacks.picker.marks({ 24 | transform = function(item) 25 | if item.label and item.label:match("^[A-I]$") and item then 26 | item.label = "" .. string.byte(item.label) - string.byte("A") + 1 .. "" 27 | return item 28 | end 29 | return false 30 | end, 31 | }) 32 | end 33 | 34 | -- Add Marks ------------------------------------------------------------------ 35 | vim.keymap.set("n", "m", function() 36 | local mark = vim.fn.getcharstr() 37 | local char = mark2char(mark) 38 | vim.cmd("mark " .. char) 39 | if mark:match("[1-9]") then 40 | vim.notify("Added mark " .. mark, vim.log.levels.INFO, { title = "Marks" }) 41 | else 42 | vim.fn.feedkeys("m" .. mark, "n") 43 | end 44 | end, { desc = "Add mark" }) 45 | 46 | -- Go To Marks ---------------------------------------------------------------- 47 | vim.keymap.set("n", "'", function() 48 | local mark = vim.fn.getcharstr() 49 | local char = mark2char(mark) 50 | local mark_pos = vim.api.nvim_get_mark(char, {}) 51 | if mark_pos[1] == 0 then 52 | return vim.notify("No mark at " .. mark, vim.log.levels.WARN, { title = "Marks" }) 53 | end 54 | 55 | vim.fn.feedkeys("'" .. mark2char(mark), "n") 56 | end, { desc = "Go to mark" }) 57 | 58 | -- List Marks ----------------------------------------------------------------- 59 | vim.keymap.set("n", "mm", function() 60 | list_marks() 61 | end, { desc = "List marks" }) 62 | 63 | -- Delete Marks --------------------------------------------------------------- 64 | vim.keymap.set("n", "md", function() 65 | local mark = vim.fn.getcharstr() 66 | vim.api.nvim_del_mark(mark2char(mark)) 67 | vim.notify("Deleted mark " .. mark, vim.log.levels.INFO, { title = "Marks" }) 68 | end, { desc = "Delete mark" }) 69 | 70 | vim.keymap.set("n", "mD", function() 71 | vim.cmd("delmarks A-I") 72 | vim.notify("Deleted all marks", vim.log.levels.INFO, { title = "Marks" }) 73 | end, { desc = "Delete all marks" }) 74 | -------------------------------------------------------------------------------- /.config/nvim/static/neovim.cat: -------------------------------------------------------------------------------- 1 |  2 | ███████████ █████ ██ 3 | ███████████ █████  4 | ████████████████ ███████████ ███ ███████ 5 | ████████████████ ████████████ █████ ██████████████ 6 | █████████████████████████████ █████ █████ ████ █████ 7 | ██████████████████████████████████ █████ █████ ████ █████ 8 | ██████ ███ █████████████████ ████ █████ █████ ████ ██████ 9 | -------------------------------------------------------------------------------- /.config/rclone/filter_list.txt: -------------------------------------------------------------------------------- 1 | - .DS_Store 2 | - node_modules/ 3 | - vendor/ 4 | - tmp/ 5 | - .git/ 6 | - .config/fish/.tofish/** 7 | - .config/fish/completions/** 8 | - .config/fish/functions/** 9 | - .config/superwhisper/recordings/** 10 | - .config/superwhisper/models/** 11 | - *.log 12 | - .cache/ 13 | - .npm/ 14 | - .yarn/ 15 | - __pycache__/ 16 | - *.pyc 17 | - dist/ 18 | - build/ 19 | - .next/ 20 | - coverage/ 21 | - .Trash/ 22 | - .Spotlight-V100/ 23 | - .fseventsd/ 24 | - .repro/ 25 | - .deps/ 26 | -------------------------------------------------------------------------------- /.config/snippets/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "lorem": { 3 | "prefix": "lorem", 4 | "body": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod\ntempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At\nvero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,\nno sea takimata sanctus est Lorem ipsum dolor sit amet.", 5 | "description": "Lorem Ipsum - 50 Words" 6 | } 7 | } -------------------------------------------------------------------------------- /.config/snippets/lua.json: -------------------------------------------------------------------------------- 1 | { 2 | "Print table": { 3 | "prefix": ["pp"], 4 | "body": ["print(vim.inspect($0))"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.config/snippets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "personal-snippets", 3 | "contributes": { 4 | "snippets": [ 5 | { 6 | "language": "global", 7 | "path": "./global.json" 8 | }, 9 | { 10 | "language": "lua", 11 | "path": "./lua.json" 12 | }, 13 | { 14 | "language": "python", 15 | "path": "./python.json" 16 | }, 17 | { 18 | "language": "ruby", 19 | "path": "./ruby.json" 20 | } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.config/snippets/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "Django Model Class": { 3 | "prefix": ["class(Django model class)"], 4 | "body": ["class ${1:classname}(models.Model):", "\t${3:pass}"], 5 | "description": "Model class for Django" 6 | }, 7 | "Django Model Timestamps": { 8 | "prefix": ["Timestamps (Django Models)"], 9 | "body": [ 10 | "created_at = models.DateTimeField(auto_now_add=True)", 11 | "modified_at = models.DateTimeField(auto_now=True)", 12 | "deleted_at = models.DateTimeField(null=True)" 13 | ], 14 | "description": "Created, Modified, Deleted timestamps for Django models" 15 | }, 16 | "Django Rest Serializers": { 17 | "prefix": ["class(Serializer)"], 18 | "body": [ 19 | "class ${1:modelname}Serializer(serializers.HyperlinkedModelSerializer):", 20 | "\tclass Meta:", 21 | "\t\tmodel = $1", 22 | "\t\tfields = [$0]" 23 | ], 24 | "description": "Serializer class for Django Rest Framework" 25 | }, 26 | "Django Rest Viewsets": { 27 | "prefix": ["class(ViewSet)"], 28 | "body": [ 29 | "class ${1:modelname}ViewSet(viewsets.ModelViewSet):", 30 | "\t\"\"\"", 31 | "\tAPI endpoint that allows $0", 32 | "\t\"\"\"", 33 | "\tqueryset = $1.objects.all().order_by('-created_at')", 34 | "\tserializer_class = $1Serializer", 35 | "\tpermission_classes = [permissions.IsAuthenticated]" 36 | ], 37 | "description": "ViewSet class for Django Rest Framework" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.config/snippets/ruby.json: -------------------------------------------------------------------------------- 1 | { 2 | "Class initialize": { 3 | "prefix": ["init"], 4 | "body": ["def initialize(${1:params})", "\t$0", "end"] 5 | }, 6 | "Methods": { 7 | "prefix": ["def"], 8 | "body": ["def ${1:name}(${2:params})", "\t$0", "end"] 9 | }, 10 | "Ruby Debug": { 11 | "prefix": ["db"], 12 | "body": ["require 'debug';binding.break"] 13 | }, 14 | "RSpec describe": { 15 | "prefix": ["describe"], 16 | "body": ["describe '${1:name}' do", "\t$0", "end"], 17 | "description": "RSpec describe block" 18 | }, 19 | "RSpec before": { 20 | "prefix": ["before"], 21 | "body": ["before do", "\t$0", "end"], 22 | "description": "RSpec before block" 23 | }, 24 | "RSpec context": { 25 | "prefix": ["context"], 26 | "body": [ 27 | "context \"${1:context}\" do", 28 | "\tit \"${2:it}\" do", 29 | "\t\t$0", 30 | "\tend", 31 | "end" 32 | ], 33 | "description": "RSpec context block" 34 | }, 35 | "factory_bot define": { 36 | "prefix": ["FactoryBot"], 37 | "body": ["FactoryBot.define do", "\t$0", "end"], 38 | "description": "factory_bot define block" 39 | }, 40 | "factory_bot factory": { 41 | "prefix": ["factory"], 42 | "body": ["factory :${1:factory} do", "\t$0", "end"], 43 | "description": "factory_bot factory block" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.config/ssh/config: -------------------------------------------------------------------------------- 1 | Host * 2 | IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" 3 | -------------------------------------------------------------------------------- /.config/vim/.vimrc.plugins: -------------------------------------------------------------------------------- 1 | " Initialise {{{ 2 | call plug#begin('~/.vim/plugged') 3 | 4 | let b:ale_enabled = 0 5 | " }}} 6 | " Plugins {{{ 7 | " General Functionality {{{ 8 | Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } 9 | Plug 'junegunn/fzf.vim' " Use FZF to search across tags and buggers 10 | Plug 'sjl/gundo.vim' " Visualise the undo tree 11 | Plug 'mileszs/ack.vim' " Search for files...fast! 12 | Plug 'majutsushi/tagbar' " Browse the tag of the current file 13 | Plug 'tpope/vim-vinegar' " Enhances netrw 14 | Plug 'qpkorr/vim-bufkill' " Close a buffer without ruining your splits 15 | Plug 'tpope/vim-obsession' " Automatically make sessions when exiting Vim 16 | Plug 'preservim/nerdtree' " Sublime like folder tree 17 | Plug 'osyo-manga/vim-over' " Highlights pattern replacement so you can see its impact 18 | Plug 'bling/vim-bufferline' " Display buffers in the command line 19 | Plug 'tpope/vim-unimpaired' " Handy bracket mappings 20 | Plug 'dbakker/vim-projectroot' " Detect the project root for a file 21 | Plug 'skywind3000/asyncrun.vim' " Run Vim commands in the background via Async 22 | Plug 'junegunn/vim-after-object' " Type 'ca=' to run 'change after = sign' - Brilliant! 23 | Plug 'ludovicchabant/vim-gutentags' " Regenerate tag files automatically 24 | " }}} 25 | " Easier coding {{{ 26 | Plug 'elzr/vim-json' " Better JSON highlighting 27 | Plug 'janko-m/vim-test' " Run Tests in any language 28 | Plug 'SirVer/ultisnips' " Code snippets 29 | Plug 'skwp/greplace.vim' " Search and replace across multiple files 30 | Plug 'tpope/vim-surround' " Surround things better 31 | Plug 'luochen1990/rainbow' " Make sets of brackets different colors 32 | Plug 'Yggdroot/indentLine' " Show indentations in the code 33 | Plug 'jiangmiao/auto-pairs' " Auto close tags and brackets 34 | Plug 'tpope/vim-commentary' " Comment stuff out 35 | Plug 'scrooloose/nerdcommenter' " Smarter commenting 36 | Plug 'Valloric/MatchTagAlways' " Use % to switch between top and bottom of HTML tags 37 | "Plug 'tyru/current-func-info.vim' " Get the method name depending on where the cursor is in the code 38 | Plug 'terryma/vim-multiple-cursors' " Sublime Text like multiple cursors 39 | Plug 'bronson/vim-trailing-whitespace' " Remove any trailing whitespace 40 | " }}} 41 | " Syntax {{{ 42 | Plug 'w0rp/ale' " Vim syntax checker 43 | Plug 'posva/vim-vue' " Vue syntax highlighting 44 | Plug 'tpope/vim-sleuth' " Auto-indent a file based on its type 45 | Plug 'othree/html5.vim' " HTML syntax support 46 | Plug 'wavded/vim-stylus' " Stylus syntax support 47 | Plug 'ap/vim-css-color' " Show Hex and RGB colors in CSS files 48 | Plug 'tpope/vim-markdown' " Markdown support for Vim 49 | Plug 'jwalton512/vim-blade' " Laravel Blade support 50 | Plug 'StanAngeloff/php.vim' " PHP syntax support 51 | Plug 'Chiel92/vim-autoformat' " Auto code format 52 | Plug 'pangloss/vim-javascript' " Javascript syntax highlighting 53 | " }}} 54 | " Git {{{ 55 | Plug 'junegunn/gv.vim' " Browse Git commits 56 | Plug 'tpope/vim-rhubarb' " Open Git links 57 | Plug 'tpope/vim-fugitive' " Required for gv.vim 58 | " }}} 59 | " Ruby {{{ 60 | Plug 'vim-ruby/vim-ruby' " Full Ruby support 61 | Plug 'tpope/vim-endwise' " End structures like methods automatically 62 | " }}} 63 | " PHP {{{ 64 | Plug 'stephpy/vim-php-cs-fixer' " Integrate PHP-CS-Fixer 65 | Plug 'arnaud-lb/vim-php-namespace' " Insert 'use' statements automatically 66 | " }}} 67 | " {{{ Python 68 | "Plug 'tweekmonster/django-plus.vim' 69 | " }}} 70 | " HTML {{{ 71 | Plug 'tpope/vim-haml' " Runtime files for Haml, Sass and SCSS 72 | Plug 'mattn/emmet-vim' " Use Emmet within Vim 73 | Plug 'alvan/vim-closetag' " Autoclose HTML tags 74 | "Plug 'gorodinskiy/vim-coloresque' " Display Hex and RGB colors in VIM 75 | " }}} 76 | " Misc {{{ 77 | Plug 'tpope/vim-repeat' " Use the '.' command on plugins 78 | Plug 'mhinz/vim-startify' " A fancy start screen for Vim 79 | " }}} 80 | " Theming {{{ 81 | Plug 'joshdick/onedark.vim' 82 | Plug 'mkarmona/materialbox' 83 | Plug 'itchyny/lightline.vim' " Lightline Status bar 84 | Plug 'farfanoide/vim-facebook' 85 | " }}} 86 | " }}} 87 | " Terminate {{{ 88 | call plug#end() 89 | " }}} 90 | " vim:foldmethod=marker:foldlevel=0 91 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/blade.snippets: -------------------------------------------------------------------------------- 1 | snippet ext 2 | @extends('layouts.app') 3 | 4 | @section('title', '${1}') 5 | 6 | @section('content') 7 | ${0} 8 | @endsection 9 | endsnippet 10 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/eruby.snippets: -------------------------------------------------------------------------------- 1 | extends html 2 | 3 | snippet form 4 | <%= form_for :${1} do |f| %> 5 | ${0} 6 | <% end %> 7 | 8 | snippet field 9 |

10 | <%= f.label :${1:} %>
11 | <%= f.text_field :$1 %> 12 |

13 | ${0} 14 | 15 | snippet area 16 |

17 | <%= f.label :${1:} %>
18 | <%= f.text_area :$1 %> 19 |

20 | ${0} 21 | 22 | snippet submit 23 | <%= f.submit %> 24 | ${0} 25 | 26 | snippet rc 27 | <%= ${0} %> 28 | 29 | snippet rce 30 | <%= ${0} %> 31 | 32 | snippet link 33 | <%= link_to '${1}', ${0} %> 34 | 35 | snippet errors 36 | <% if @${1:}.errors.any? %> 37 |
38 |

39 | <%= pluralize(@$1.errors.count, "error") %> prohibited this $1 from being saved: 40 |

41 |
    42 | <% @$1.errors.full_messages.each do |msg| %> 43 |
  • <%= msg %>
  • 44 | <% end %> 45 |
46 |
47 | <% end %> 48 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/html.snippets: -------------------------------------------------------------------------------- 1 | snippet html 2 | 3 | 4 | 5 | 6 | ${1} 7 | 8 | 9 | ${0} 10 | 11 | 12 | endsnippet 13 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/php.snippets: -------------------------------------------------------------------------------- 1 | snippet $ 2 | $this-> 3 | endsnippet 4 | 5 | snippet class 6 | ${3}('${4}'); 130 | } 131 | endsnippet 132 | 133 | snippet hm 134 | public function ${1}() 135 | { 136 | return $this->hasMany($1); 137 | } 138 | endsnippet 139 | 140 | snippet if 141 | if (${1}) { 142 | ${0} 143 | } 144 | endsnippet 145 | 146 | snippet fore 147 | foreach (${1} as ${2}) { 148 | ${0} 149 | } 150 | endsnippet 151 | 152 | snippet con 153 | public function __construct(${1}) 154 | { 155 | ${0} 156 | } 157 | endsnippet 158 | 159 | snippet asst 160 | $this->assertTrue(${0}); 161 | endsnippet 162 | 163 | snippet assf 164 | $this->assertFalse(${0}); 165 | endsnippet 166 | 167 | snippet asse 168 | $this->assertEquals(${0}); 169 | endsnippet 170 | 171 | snippet assn 172 | $this->assertNull(${0}); 173 | endsnippet 174 | 175 | snippet tmet 176 | /** @test */ 177 | public function ${1}() 178 | { 179 | ${0} 180 | } 181 | endsnippet 182 | 183 | snippet eh 184 | $this->withoutExceptionHandling(); 185 | endsnippet 186 | 187 | snippet dd 188 | eval(\Psy\sh()); 189 | endsnippet 190 | 191 | snippet factory 192 | /** @var \Illuminate\Database\Eloquent\Factory $factory */ 193 | $factory->define(${1}, function (Faker\Generator $faker) { 194 | return [ 195 | ${0} 196 | ]; 197 | }); 198 | endsnippet 199 | 200 | snippet uf 201 | $user = factory(\App\User::class)->create(); 202 | endsnippet 203 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/python.snippets: -------------------------------------------------------------------------------- 1 | snippet def 2 | def ${1}(${2}): 3 | ${3} 4 | endsnippet 5 | 6 | snippet defr 7 | def ${1}(request${2}): 8 | ${3} 9 | endsnippet 10 | 11 | snippet class 12 | class ${1}: 13 | ${0} 14 | endsnippet 15 | 16 | snippet classi 17 | class ${1}: 18 | def __init__(self${2}): 19 | ${0} 20 | endsnippet 21 | -------------------------------------------------------------------------------- /.config/vim/custom_snippets/ruby.snippets: -------------------------------------------------------------------------------- 1 | snippet d 2 | def ${1} 3 | ${0} 4 | end 5 | 6 | snippet lp 7 | $LOAD_PATH.unshift("#{File.dirname(__FILE__)}${1:/../lib}") 8 | ${0} 9 | -------------------------------------------------------------------------------- /.config/vim/minimal.vim: -------------------------------------------------------------------------------- 1 | call plug#begin("/Users/Oli/.local/share/nvim/plugged") 2 | Plug 'olimorris/onedarkpro.nvim' 3 | call plug#end() 4 | 5 | lua << EOF 6 | require("onedarkpro").load() 7 | EOF 8 | -------------------------------------------------------------------------------- /.config/vivid/onedark.yml: -------------------------------------------------------------------------------- 1 | colors: 2 | background_color: '282c34' 3 | white: 'abb2bf' 4 | red: 'e06c75' 5 | green: '98c379' 6 | yellow: 'e5c07b' 7 | blue: '61afef' 8 | purple: 'c678dd' 9 | cyan: '56b6c2' 10 | 11 | black: '000000' 12 | gray: '5c6370' 13 | darkgray: '666666' 14 | darkergray: '333333' 15 | 16 | core: 17 | normal_text: {} 18 | regular_file: {} 19 | reset_to_normal: {} 20 | 21 | directory: 22 | foreground: blue 23 | 24 | symlink: 25 | foreground: purple 26 | 27 | multi_hard_link: {} 28 | 29 | fifo: 30 | foreground: black 31 | background: blue 32 | 33 | socket: 34 | foreground: black 35 | background: purple 36 | 37 | door: 38 | foreground: black 39 | background: purple 40 | 41 | block_device: 42 | foreground: cyan 43 | background: darkergray 44 | 45 | character_device: 46 | foreground: purple 47 | background: darkergray 48 | 49 | broken_symlink: 50 | foreground: black 51 | background: red 52 | 53 | missing_symlink_target: 54 | foreground: black 55 | background: red 56 | 57 | setuid: {} 58 | 59 | setgid: {} 60 | 61 | file_with_capability: {} 62 | 63 | sticky_other_writable: {} 64 | 65 | other_writable: {} 66 | 67 | sticky: {} 68 | 69 | executable_file: 70 | foreground: red 71 | font-style: bold 72 | 73 | text: 74 | special: 75 | foreground: background_color 76 | background: yellow 77 | 78 | todo: 79 | font-style: bold 80 | 81 | licenses: 82 | foreground: gray 83 | 84 | configuration: 85 | foreground: yellow 86 | 87 | other: 88 | foreground: yellow 89 | 90 | markup: 91 | foreground: yellow 92 | 93 | programming: 94 | source: 95 | foreground: green 96 | 97 | tooling: 98 | foreground: green 99 | 100 | continuous-integration: 101 | foreground: green 102 | 103 | media: 104 | foreground: purple 105 | 106 | office: 107 | foreground: red 108 | 109 | archives: 110 | foreground: cyan 111 | font-style: underline 112 | 113 | executable: 114 | foreground: red 115 | font-style: bold 116 | 117 | unimportant: 118 | foreground: darkgray 119 | -------------------------------------------------------------------------------- /.config/vivid/onelight.yml: -------------------------------------------------------------------------------- 1 | colors: 2 | background_color: 'fafafa' 3 | white: '6a6a6a' 4 | red: 'e05661' 5 | green: '1da912' 6 | yellow: 'eea825' 7 | blue: '118dc3' 8 | purple: '9a77cf' 9 | cyan: '56b6c2' 10 | 11 | black: '000000' 12 | gray: 'bebebe' 13 | darkgray: '666666' 14 | darkergray: '333333' 15 | 16 | core: 17 | normal_text: {} 18 | regular_file: {} 19 | reset_to_normal: {} 20 | 21 | directory: 22 | foreground: blue 23 | 24 | symlink: 25 | foreground: purple 26 | 27 | multi_hard_link: {} 28 | 29 | fifo: 30 | foreground: black 31 | background: blue 32 | 33 | socket: 34 | foreground: black 35 | background: purple 36 | 37 | door: 38 | foreground: black 39 | background: purple 40 | 41 | block_device: 42 | foreground: cyan 43 | background: darkergray 44 | 45 | character_device: 46 | foreground: purple 47 | background: darkergray 48 | 49 | broken_symlink: 50 | foreground: black 51 | background: red 52 | 53 | missing_symlink_target: 54 | foreground: black 55 | background: red 56 | 57 | setuid: {} 58 | 59 | setgid: {} 60 | 61 | file_with_capability: {} 62 | 63 | sticky_other_writable: {} 64 | 65 | other_writable: {} 66 | 67 | sticky: {} 68 | 69 | executable_file: 70 | foreground: red 71 | font-style: bold 72 | 73 | text: 74 | special: 75 | foreground: background_color 76 | background: yellow 77 | 78 | todo: 79 | font-style: bold 80 | 81 | licenses: 82 | foreground: gray 83 | 84 | configuration: 85 | foreground: yellow 86 | 87 | other: 88 | foreground: yellow 89 | 90 | markup: 91 | foreground: yellow 92 | 93 | programming: 94 | source: 95 | foreground: green 96 | 97 | tooling: 98 | foreground: green 99 | 100 | continuous-integration: 101 | foreground: green 102 | 103 | media: 104 | foreground: purple 105 | 106 | office: 107 | foreground: red 108 | 109 | archives: 110 | foreground: cyan 111 | font-style: underline 112 | 113 | executable: 114 | foreground: red 115 | font-style: bold 116 | 117 | unimportant: 118 | foreground: darkgray 119 | -------------------------------------------------------------------------------- /.config/wezterm/colors.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require("wezterm") 2 | local config = wezterm.config_builder() 3 | 4 | local function scheme_for_appearance() 5 | local appearance = wezterm.gui.get_appearance() 6 | if appearance:find("Dark") then 7 | return "onedarkpro_vaporwave" 8 | end 9 | return "onedarkpro_onelight" 10 | end 11 | 12 | wezterm.on("window-config-reloaded", function(window) 13 | local new_scheme = scheme_for_appearance() 14 | window:set_config_overrides({ 15 | color_scheme = new_scheme, 16 | }) 17 | wezterm.log_info("Active window color scheme updated to " .. new_scheme) 18 | end) 19 | 20 | local function colors(config) 21 | config.color_scheme_dirs = { wezterm.home_dir .. "/.cache/nvim/onedarkpro_dotfiles/extras/wezterm" } 22 | config.color_scheme = scheme_for_appearance() 23 | end 24 | 25 | return colors 26 | -------------------------------------------------------------------------------- /.config/wezterm/keys.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require("wezterm") 2 | local act = wezterm.action --[[@type function]] 3 | 4 | local mod = "LEADER" 5 | 6 | ---@param config table 7 | local function keys(config) 8 | config.leader = { 9 | key = "z", 10 | mods = "CTRL", 11 | timeout_milliseconds = 2000, 12 | } 13 | 14 | config.keys = { 15 | 16 | -- Scrollback 17 | { key = "k", mods = "CTRL|SHIFT", action = act.ScrollByPage(-0.5) }, 18 | { key = "j", mods = "CTRL|SHIFT", action = act.ScrollByPage(0.5) }, 19 | 20 | -- Get the keys working as expected 21 | { key = "\r", mods = "CTRL", action = act({ SendString = "\x1b[13;5u" }) }, 22 | { key = "\r", mods = "SHIFT", action = act({ SendString = "\x1b[13;2u" }) }, 23 | { key = "LeftArrow", mods = "ALT", action = act({ SendString = "\x1b\x62" }) }, 24 | { key = "RightArrow", mods = "ALT", action = act({ SendString = "\x1b\x66" }) }, 25 | 26 | { key = "d", mods = mod, action = act.ShowDebugOverlay }, 27 | 28 | { key = "w", mods = mod, action = act.ShowTabNavigator }, 29 | } 30 | end 31 | 32 | return keys 33 | -------------------------------------------------------------------------------- /.config/wezterm/links.lua: -------------------------------------------------------------------------------- 1 | -- Taken from: https://github.com/folke/dot/blob/master/config/wezterm/links.lua 2 | local wezterm = require("wezterm") 3 | 4 | ---@param config table 5 | local function links(config) 6 | config.hyperlink_rules = { 7 | -- Linkify things that look like URLs and the host has a TLD name. 8 | -- 9 | -- Compiled-in default. Used if you don't specify any hyperlink_rules. 10 | { 11 | regex = "\\b\\w+://[\\w.-]+\\.[a-z]{2,15}\\S*\\b", 12 | format = "$0", 13 | }, 14 | 15 | -- linkify email addresses 16 | -- Compiled-in default. Used if you don't specify any hyperlink_rules. 17 | { 18 | regex = [[\b\w+@[\w-]+(\.[\w-]+)+\b]], 19 | format = "mailto:$0", 20 | }, 21 | 22 | -- file:// URI 23 | -- Compiled-in default. Used if you don't specify any hyperlink_rules. 24 | { 25 | regex = [[\bfile://\S*\b]], 26 | format = "$0", 27 | }, 28 | 29 | -- Linkify things that look like URLs with numeric addresses as hosts. 30 | -- E.g. http://127.0.0.1:8000 for a local development server, 31 | -- or http://192.168.1.1 for the web interface of many routers. 32 | { 33 | regex = [[\b\w+://(?:[\d]{1,3}\.){3}[\d]{1,3}\S*\b]], 34 | format = "$0", 35 | }, 36 | 37 | -- Make username/project paths clickable. This implies paths like the following are for GitHub. 38 | -- As long as a full URL hyperlink regex exists above this it should not match a full URL to 39 | -- GitHub or GitLab / BitBucket (i.e. https://gitlab.com/user/project.git is still a whole clickable URL) 40 | { 41 | regex = [[["]?([\w\d]{1}[-\w\d]+)(/){1}([-\w\d\.]+)["]?]], 42 | format = "https://www.github.com/$1/$3", 43 | }, 44 | } 45 | end 46 | 47 | return links 48 | -------------------------------------------------------------------------------- /.config/wezterm/tabline.lua: -------------------------------------------------------------------------------- 1 | -- Inspired by: https://github.com/folke/dot/blob/master/config/wezterm/tabs.lua 2 | local wezterm = require("wezterm") 3 | 4 | local function leader(window) 5 | if window:leader_is_active() then 6 | return " ! " 7 | end 8 | return "" 9 | end 10 | 11 | local function tabs(config) 12 | local tabline = wezterm.plugin.require("https://github.com/michaelbrusegard/tabline.wez") 13 | 14 | config.use_fancy_tab_bar = false 15 | config.tab_bar_at_bottom = true 16 | config.hide_tab_bar_if_only_one_tab = false 17 | 18 | local colors = wezterm.color.load_scheme(config.color_scheme_dirs[1] .. "/" .. config.color_scheme .. ".toml") 19 | 20 | tabline.setup({ 21 | options = { 22 | icons_enabled = false, 23 | component_separators = { 24 | left = "", 25 | right = "", 26 | }, 27 | section_separators = { 28 | left = "", 29 | right = "", 30 | }, 31 | tab_separators = { 32 | left = "", 33 | right = "", 34 | }, 35 | color_overrides = { 36 | normal_mode = { 37 | a = { fg = colors.background, bg = colors.ansi[5] }, 38 | b = { fg = colors.indexed[59], bg = colors.background }, 39 | c = { fg = colors.indexed[59], bg = colors.background }, 40 | }, 41 | copy_mode = { 42 | a = { fg = colors.background, bg = colors.ansi[4] }, 43 | b = { fg = colors.ansi[4], bg = colors.background }, 44 | c = { fg = colors.foreground, bg = colors.background }, 45 | }, 46 | search_mode = { 47 | a = { fg = colors.background, bg = colors.ansi[3] }, 48 | b = { fg = colors.ansi[3], bg = colors.background }, 49 | c = { fg = colors.foreground, bg = colors.background }, 50 | }, 51 | -- Defining colors for a new key table 52 | window_mode = { 53 | a = { fg = colors.background, bg = colors.ansi[6] }, 54 | b = { fg = colors.ansi[6], bg = colors.background }, 55 | c = { fg = colors.foreground, bg = colors.background }, 56 | }, 57 | -- Default tab colors 58 | tab = { 59 | active = { fg = colors.ansi[6], bg = colors.background }, 60 | inactive = { fg = colors.indexed[59], bg = colors.background }, 61 | inactive_hover = { fg = colors.ansi[6], bg = colors.background }, 62 | }, 63 | }, 64 | }, 65 | sections = { 66 | tabline_a = { leader }, 67 | tabline_b = {}, 68 | tabline_c = {}, 69 | tab_active = { "index" .. "", { "process", padding = { left = 0, right = 1 } } }, 70 | tab_inactive = { "index", { "process", padding = { left = 0, right = 1 } } }, 71 | tabline_x = { "ram", "cpu", "battery", "datetime" }, 72 | tabline_y = {}, 73 | tabline_z = {}, 74 | }, 75 | extensions = { 76 | "resurrect", 77 | }, 78 | }) 79 | end 80 | 81 | return tabs 82 | -------------------------------------------------------------------------------- /.config/wezterm/wezterm.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require("wezterm") 2 | local config = wezterm.config_builder() 3 | 4 | require("colors")(config) 5 | require("links")(config) 6 | require("keys")(config) 7 | require("tabline")(config) 8 | require("workspaces")(config) 9 | wezterm.log_info("Reloading") 10 | 11 | wezterm.add_to_config_reload_watch_list(wezterm.home_dir .. "/.color_mode") 12 | config.automatically_reload_config = true 13 | 14 | -- Graphics config 15 | config.front_end = "WebGpu" 16 | config.cursor_blink_ease_in = "Constant" 17 | config.cursor_blink_ease_out = "Constant" 18 | 19 | config.font = wezterm.font("Operator Mono", { 20 | stretch = "Normal", 21 | weight = "Book", 22 | }) 23 | config.font_size = 21 24 | 25 | config.line_height = 1.6 26 | config.mouse_bindings = { 27 | -- Open a link 28 | { 29 | event = { Up = { streak = 1, button = "Left" } }, 30 | mods = "CTRL", 31 | action = "OpenLinkAtMouseCursor", 32 | }, 33 | } 34 | config.window_close_confirmation = "NeverPrompt" 35 | config.window_decorations = "RESIZE" 36 | config.window_padding = { 37 | left = 5, 38 | right = 5, 39 | top = 0, 40 | bottom = 0, 41 | } 42 | 43 | return config 44 | -------------------------------------------------------------------------------- /.config/wezterm/workspaces.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require("wezterm") 2 | 3 | local resurrect = wezterm.plugin.require("https://github.com/MLFlexer/resurrect.wezterm") 4 | local workspace_switcher = wezterm.plugin.require("https://github.com/MLFlexer/smart_workspace_switcher.wezterm") 5 | 6 | local function workspaces(config) 7 | resurrect.set_encryption({ 8 | enable = true, 9 | method = "/opt/homebrew/bin/age", 10 | private_key = wezterm.home_dir .. "/.config/wezterm/encryption_key.txt", 11 | public_key = "age1pdme0zrvsp4rphlfuc82lnfdm6gkyuakzajkp7veclk9qckz4ccsevud0w", 12 | }) 13 | resurrect.periodic_save({ interval_seconds = 900 }) 14 | resurrect.set_max_nlines(200) 15 | 16 | workspace_switcher.workspace_formatter = function(label) 17 | return wezterm.format({ 18 | { Attribute = { Italic = true } }, 19 | { Foreground = { AnsiColor = "Purple" } }, 20 | { Background = { Color = (wezterm.gui.get_appearance() == "Dark" and "#282c34" or "#fafafa") } }, 21 | { Text = wezterm.nerdfonts.cod_terminal_tmux .. " " .. string.match(label, "[^/\\]+$") }, 22 | }) 23 | end 24 | 25 | -- Load state whenever a workspace is created 26 | wezterm.on("smart_workspace_switcher.workspace_switcher.created", function(window, path, label) 27 | local workspace_state = resurrect.workspace_state 28 | 29 | -- window:perform_action(wezterm.action.ReloadConfiguration, pane) 30 | 31 | workspace_state.restore_workspace(resurrect.load_state(label, "workspace"), { 32 | window = window, 33 | restore_text = true, 34 | on_pane_restore = resurrect.tab_state.default_on_pane_restore, 35 | }) 36 | end) 37 | 38 | -- Save state whenever a workspace is selected 39 | wezterm.on("smart_workspace_switcher.workspace_switcher.selected", function(window, path, label) 40 | local workspace_state = resurrect.workspace_state 41 | resurrect.save_state(workspace_state.get_workspace_state()) 42 | end) 43 | 44 | table.insert(config.keys, { key = "w", mods = "CTRL", action = workspace_switcher.switch_workspace() }) 45 | table.insert(config.keys, { 46 | key = "s", 47 | mods = "LEADER", 48 | action = wezterm.action_callback(function(win, pane) 49 | wezterm.log_info("Saved workspace") 50 | resurrect.save_state(resurrect.workspace_state.get_workspace_state()) 51 | end), 52 | }) 53 | table.insert(config.keys, { 54 | key = "r", 55 | mods = "LEADER", 56 | action = wezterm.action_callback(function(win, pane) 57 | resurrect.fuzzy_load(win, pane, function(id, label) 58 | local type = string.match(id, "^([^/]+)") -- match before '/' 59 | id = string.match(id, "([^/]+)$") -- match after '/' 60 | id = string.match(id, "(.+)%..+$") -- remove file extention 61 | local opts = { 62 | relative = true, 63 | restore_text = true, 64 | on_pane_restore = resurrect.tab_state.default_on_pane_restore, 65 | } 66 | if type == "workspace" then 67 | local state = resurrect.load_state(id, "workspace") 68 | resurrect.workspace_state.restore_workspace(state, opts) 69 | elseif type == "window" then 70 | local state = resurrect.load_state(id, "window") 71 | resurrect.window_state.restore_window(pane:window(), state, opts) 72 | elseif type == "tab" then 73 | local state = resurrect.load_state(id, "tab") 74 | resurrect.tab_state.restore_tab(pane:tab(), state, opts) 75 | end 76 | end) 77 | end), 78 | }) 79 | end 80 | 81 | return workspaces 82 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: ~ 5 | push: ~ 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Install rake 18 | run: gem install rake 19 | 20 | - name: Check install 21 | run: rake install 22 | env: 23 | TEST_ENV: true 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything except directories 2 | * 3 | !*/ 4 | 5 | ################################### WHITELIST ################################## 6 | 7 | # Config 8 | !.config/bash/**/* 9 | !.config/composer/composer.json 10 | !.config/efm-langserver/* 11 | !.config/env/.env.example 12 | !.config/fish/* 13 | !.config/git/**/* 14 | !.config/hammerspoon/**/* 15 | !.config/karabiner/karabiner.json 16 | !.config/mackup/**/* 17 | !.config/mcphub/**/* 18 | !.config/mise/**/* 19 | !.config/nix/**/* 20 | !.config/nvim/**/* 21 | !.config/rclone/filter_list.txt 22 | !.config/snippets/**/* 23 | !.config/ssh/config 24 | !.config/vim/**/* 25 | !.config/vivid/**/* 26 | !.config/wezterm/**/* 27 | !.config/zsh/**/* 28 | 29 | # Dotbot 30 | !dotbot.conf.yaml 31 | !dotbot_uninstall 32 | 33 | # Git 34 | !.github/**/* 35 | !.gitignore 36 | !.gitignore_global 37 | !.gitmodules 38 | 39 | # Vim 40 | !minimal.vim 41 | !vim/**/* 42 | 43 | # Other dirs 44 | !bin/**/* 45 | !commands/**/* 46 | !misc/packages/**/* 47 | !tasks/**/* 48 | !tests/**/* 49 | !misc/LaunchAgents/**/* 50 | 51 | # Other files 52 | !.tool-versions 53 | !.config/.color_mode 54 | !Rakefile 55 | !README.md 56 | !stylua.toml 57 | 58 | ################################### BLACKLIST ################################## 59 | 60 | # Config 61 | .config/nvim/plugin 62 | .config/wezterm/encryption_key.txt 63 | 64 | # Fish 65 | .config/fish/fish_variables 66 | .config/fish/fish_history 67 | 68 | # Others 69 | misc/enhance-rails-intellisense-in-solargraph 70 | .DS_Store 71 | backup 72 | .repro 73 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Dotfiles 3 |

4 | 5 |

Oli's Dotfiles

6 | 7 |

8 | 9 |

10 | 11 |

12 | My macOS dotfiles. Installation instructions are my own

13 | Regular Neovim tweaks. Occasional workflow tips worth stealing :smile: 14 |

15 | 16 | ## :computer: Setting up a new Mac 17 | 18 | - Ensure you're signed into iCloud and the App Store 19 | - Download the `~/Code` and `~/.dotfiles` folders from online storage 20 | - Open up a terminal and run: 21 | 22 | ```bash 23 | cd ~/.dotfiles && rake install 24 | ``` 25 | 26 | - Grab a coffee. This will take a while 27 | 28 | ## :wrench: What actually happens 29 | 30 | - Your [Homebrew](https://brew.sh) and macOS apps will be installed 31 | - Your application settings will be restored with [Mackup](https://github.com/lra/mackup) 32 | - Your dotfiles will be restored with [Dotbot](https://github.com/anishathalye/dotbot) 33 | - Your fonts will be installed 34 | - Your custom launch agents will be installed 35 | - Your development environments will be installed and configured 36 | 37 | ## :floppy_disk: Useful commands 38 | 39 | - To backup your dotfiles: 40 | 41 | ```bash 42 | cd ~/.dotfiles && rake backup 43 | ``` 44 | 45 | - To update and backup your dotfiles: 46 | 47 | ```bash 48 | cd ~/.dotfiles && rake sync 49 | ``` 50 | 51 | ## :keyboard: Useful keymaps 52 | 53 | I implement a [Hyperkey](https://hyperkey.app) (ctrloptcmd) to allow for faster [application toggling](https://github.com/olimorris/dotfiles/blob/main/.config/hammerspoon/keymaps.lua) and easier to remember app shortcuts. 54 | 55 |
56 | Click to see the app shortcuts 57 | 58 | - Create a temp email address Hyperkeyshifte 59 | - Hide my email Hyperkeyshifth 60 | - Lock screen Hyperkeyshiftl 61 | - Pick color with ColorSlurp Hyperkeyshiftp 62 | - Search files Hyperkeyshiftf 63 | - Search Raindrop bookmarks Hyperkeyshiftb 64 | - Search screen (uses OCR) Hyperkeyshiftr 65 | - Show Cleanshot history Hyperkeyshiftc 66 | - Show clipboard history Hyperkeyshiftv 67 | - Toggle dark mode Hyperkeyshiftd 68 | - Windows - Center optc 69 | - Windows - Maximise optm 70 | - Windows - Left half Hyperkey 71 | - Windows - Right half Hyperkey 72 | - Windows - First third Hyperkey 73 | - Windows - Last two thirds Hyperkey 74 | 75 | > Thanks to this great [Reddit post](https://www.reddit.com/r/macapps/comments/xwfp82/comment/ir6trn4) 76 | 77 |
78 | 79 | ## :clap: Thanks 80 | 81 | - [Kevin Jalbert](https://kevinjalbert.com/synchronizing-my-dotfiles) 82 | - [mathiasbynens/dotfiles](https://github.com/mathiasbynens/dotfiles) 83 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Inspiration taken from: https://github.com/kevinjalbert/dotfiles/ 2 | # This Rakefile should not be run with sudo, it will use sudo where necessary. 3 | # To perform tasks in a 'dry run' state append the following to your command: 4 | # DRY_RUN=true 5 | DOTS_FOLDER = '.dotfiles' 6 | DIRECTORY_NAME = File.dirname(__dir__) 7 | SKIP_TESTS_FOR = %w[].freeze # mas.rake brew.rake 8 | 9 | Dir.glob('./tasks/**/*').map { |file| load file } 10 | 11 | task default: [:backup] 12 | 13 | desc 'Backup Everything' 14 | task :backup do 15 | section 'Backing up' 16 | 17 | # Packages 18 | Rake::Task['backup:brew'].invoke 19 | Rake::Task['backup:app_store'].invoke 20 | Rake::Task['backup:gems'].invoke 21 | Rake::Task['backup:npm'].invoke 22 | Rake::Task['backup:pip'].invoke 23 | 24 | # Files 25 | Rake::Task['backup:app_config'].invoke 26 | Rake::Task['backup:files'].invoke 27 | end 28 | 29 | desc 'Install Everything' 30 | task :install do 31 | section 'Installing' 32 | 33 | Rake::Task['tests:setup'].invoke if testing? 34 | 35 | # Packages 36 | Rake::Task['install:xcode'].invoke 37 | Rake::Task['install:brew'].invoke 38 | Rake::Task['install:brew_packages'].invoke 39 | Rake::Task['install:brew_cask_packages'].invoke 40 | Rake::Task['install:brew_clean_up'].invoke 41 | Rake::Task['install:app_store'].invoke unless testing? 42 | 43 | Rake::Task['install:servers'].invoke 44 | 45 | # Packages 46 | Rake::Task['install:rust'].invoke unless testing? 47 | Rake::Task['install:cargo'].invoke unless testing? 48 | Rake::Task['install:gems'].invoke unless testing? 49 | Rake::Task['install:npm'].invoke unless testing? 50 | Rake::Task['install:pip'].invoke unless testing? 51 | 52 | # Files 53 | Rake::Task['install:app_config'].invoke 54 | Rake::Task['install:dotfiles'].invoke 55 | 56 | # System 57 | Rake::Task['install:chmod'].invoke 58 | Rake::Task['install:fish'].invoke 59 | Rake::Task['install:fonts'].invoke 60 | Rake::Task['install:hammerspoon'].invoke 61 | Rake::Task['install:launch_agents'].invoke 62 | 63 | # Packages 64 | Rake::Task['install:gems'].invoke 65 | Rake::Task['install:npm'].invoke 66 | Rake::Task['install:pip'].invoke 67 | 68 | # Apps 69 | Rake::Task['install:neovim'].invoke 70 | Rake::Task['install:rails'].invoke 71 | Rake::Task['install:vim'].invoke 72 | 73 | Rake::Task['install:macos'].invoke 74 | end 75 | 76 | desc 'Update Everything' 77 | task :update do 78 | section 'Updating' 79 | 80 | Rake::Task['tests:setup'].invoke if testing? 81 | 82 | # Packages 83 | Rake::Task['update:brew'].invoke 84 | Rake::Task['update:fish'].invoke 85 | Rake::Task['update:gems'].invoke 86 | Rake::Task['update:npm'].invoke 87 | Rake::Task['update:pip'].invoke 88 | 89 | # System 90 | Rake::Task['update:servers'].invoke 91 | 92 | # Apps 93 | Rake::Task['update:neovim'].invoke 94 | Rake::Task['update:rails'].invoke 95 | Rake::Task['update:vim'].invoke 96 | end 97 | 98 | desc 'Sync Everything' 99 | task :sync do 100 | section 'Syncing' 101 | 102 | Rake::Task['update'].invoke 103 | Rake::Task['backup'].invoke 104 | Rake::Task['install:brew_clean_up'].invoke 105 | end 106 | 107 | desc 'Uninstall' 108 | task :uninstall do 109 | section 'Uninstalling' 110 | 111 | Rake::Task['uninstall:dotfiles'].invoke 112 | end 113 | 114 | desc "Sync'ing personal and work settings" 115 | task :work do 116 | task :pull do 117 | section "Cloud -> Mac" 118 | 119 | Rake::Task['work:restore:files'].invoke 120 | 121 | # Install packages 122 | Rake::Task['install:brew_packages'].invoke 123 | Rake::Task['install:brew_cask_packages'].invoke 124 | Rake::Task['install:brew_clean_up'].invoke 125 | Rake::Task['install:gems'].invoke unless testing? 126 | Rake::Task['install:npm'].invoke unless testing? 127 | Rake::Task['install:pip'].invoke unless testing? 128 | 129 | # App config 130 | Rake::Task['install:app_config'].invoke 131 | Rake::Task['install:dotfiles'].invoke 132 | 133 | end 134 | task :push do 135 | section "Mac -> Cloud" 136 | 137 | Rake::Task['work:backup:files'].invoke 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /bin/cloud-backup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PATH="$HOME/.local/share/mise/shims:$PATH" 4 | 5 | . /Users/Oli/.env 6 | export STORAGE_FOLDER 7 | export STORAGE_ENCRYPTED_FOLDER 8 | 9 | cd /Users/Oli/.dotfiles && rake backup:files 10 | -------------------------------------------------------------------------------- /bin/color-mode: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export PATH="$HOME/.local/share/mise/shims:$PATH" 3 | 4 | python "$HOME/.dotfiles/commands/color_mode.py" $1 5 | -------------------------------------------------------------------------------- /bin/color-mode-notify: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/olimorris/dotfiles/7a78f080c9de70345b624cd1cfcb18993cbe91ba/bin/color-mode-notify -------------------------------------------------------------------------------- /bin/finance-output: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PATH="$HOME/.local/share/mise/shims:$PATH" 4 | 5 | . /Users/Oli/.env 6 | 7 | /opt/homebrew/bin/hledger -f $FINANCES/transactions.journal -f $FINANCES/forecast.journal --auto --forecast="this month".. bal "^(ass|liab)" --tree -H -M -b "1 month ago" -e "36 months" -O html >$FINANCES/generated.html 8 | 9 | inputFile=$FINANCES/generated.html 10 | outputFile=$FINANCES/output.html 11 | 12 | # Start of the HTML document 13 | echo ' 14 | 15 | 16 | 17 | 18 | Hledger Output 19 | 22 | 23 | ' >$outputFile 24 | 25 | # Append the table data 26 | cat $inputFile >>$outputFile 27 | 28 | # End of the HTML document 29 | echo ' 30 | ' >>$outputFile 31 | 32 | cp $outputFile /Users/Oli/Documents/finances.html 33 | rm $inputFile 34 | -------------------------------------------------------------------------------- /bin/reckon-import: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def get_reckon_command(input_file, choice) 4 | case choice 5 | when '1' # Monzo 6 | "reckon -f #{input_file} -o output_monzo.journal -c £ -a Assets:Monzo --ignore-columns 1,4,5,6,8,10,11,12 --money-column 3 --date-column 2 --contains-header 1 -t tokens.yaml -l transactions.journal" 7 | when '2' # Amex 8 | "reckon -f #{input_file} -o output_amex.journal -i -c £ -a Liabilities:Amex --money-column 3 --date-column 1 --contains-header 1 -t tokens.yaml -l transactions.journal" 9 | else 10 | raise 'Invalid choice' 11 | end 12 | end 13 | 14 | def run_reckon_command(input_file, choice) 15 | command = get_reckon_command(input_file, choice) 16 | 17 | if system(command) 18 | puts "Imported #{input_file} successfully." 19 | else 20 | puts "Error importing #{input_file}." 21 | end 22 | end 23 | 24 | def main 25 | puts "Let's import some transactions with Reckon\n\n" 26 | puts 'Choose the type of CSV file to import:' 27 | puts '1) Monzo CSV' 28 | puts '2) Amex CSV' 29 | print "\nEnter your choice (1/2): " 30 | choice = gets.chomp 31 | 32 | case choice 33 | when '1' 34 | puts 'Enter the path to the Monzo CSV file:' 35 | when '2' 36 | puts 'Enter the path to the Amex CSV file:' 37 | else 38 | puts 'Invalid choice. Exiting.' 39 | exit 40 | end 41 | 42 | input_file = File.expand_path(gets.chomp) 43 | unless File.exist?(input_file) 44 | puts 'File not found. Exiting.' 45 | exit 46 | end 47 | 48 | run_reckon_command(input_file, choice) 49 | end 50 | 51 | main 52 | -------------------------------------------------------------------------------- /bin/srt2txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'optparse' 4 | require 'fileutils' 5 | 6 | def parse_options 7 | options = { 8 | pattern: /\d+\n\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}\n/ 9 | } 10 | OptionParser.new do |opts| 11 | opts.banner = 'Usage: srt2text.rb -f INPUT_DIRECTORY -o OUTPUT_FILE [--pattern PATTERN]' 12 | 13 | opts.on('-f', '--input-directory DIRECTORY', 'Input directory containing SRT files') do |dir| 14 | options[:input_directory] = dir 15 | end 16 | 17 | opts.on('-o', '--output-file FILE', 'Output file to save the combined text') do |file| 18 | options[:output_file] = file 19 | end 20 | 21 | opts.on('--pattern PATTERN', 'Regular expression pattern for removing timestamps and sequence numbers') do |pattern| 22 | options[:pattern] = Regexp.new(pattern) 23 | end 24 | end.parse! 25 | 26 | if options[:input_directory].nil? || options[:output_file].nil? 27 | puts 'Please provide both input directory and output file.' 28 | exit(1) 29 | end 30 | 31 | options 32 | end 33 | 34 | def process_srt_files(input_directory, pattern) 35 | output_text = '' 36 | srt_files = Dir.glob("#{input_directory}/*_en.srt").sort_by { |file| File.basename(file, '.*').to_i } 37 | 38 | srt_files.each_with_index do |srt_file, index| 39 | begin 40 | text = File.read(srt_file) 41 | cleaned_text = text.gsub(pattern, '').gsub(/\n/, ' ') 42 | output_text += cleaned_text.strip + ' ' 43 | rescue StandardError => e 44 | puts "Error processing file: #{srt_file}" 45 | puts "Error message: #{e.message}" 46 | end 47 | progress = ((index + 1).to_f / srt_files.length * 100).round(2) 48 | print "\rProcessing files: #{progress}%" 49 | end 50 | 51 | puts "\nProcessing complete." 52 | output_text.strip 53 | end 54 | 55 | def save_output_file(output_file, output_text) 56 | output_dir = File.dirname(output_file) 57 | FileUtils.mkdir_p(output_dir) unless File.directory?(output_dir) 58 | 59 | File.write(output_file, output_text) 60 | puts "Combined text saved to #{output_file}" 61 | rescue StandardError => e 62 | puts "Error saving output file: #{output_file}" 63 | puts "Error message: #{e.message}" 64 | end 65 | 66 | # Main script execution 67 | options = parse_options 68 | output_text = process_srt_files(options[:input_directory], options[:pattern]) 69 | save_output_file(options[:output_file], output_text) 70 | -------------------------------------------------------------------------------- /commands/clean_up.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -w 2 | 3 | require 'optparse' 4 | require 'fileutils' 5 | 6 | module FilesToDelete 7 | FILE_TYPES = %w[.avi .flv .mkv .mov .MOV .mp4 .mpg .wmv] 8 | end 9 | 10 | # Based on a given folder pattern, recursively run a command on all 11 | # folders within the present working directory, outputting the 12 | # success of each run of the command. 13 | # 14 | # EXAMPLE: ruby ~/.dotfiles/commands/recursive.rb -p="vendor/composer" -c="composer update" 15 | class CleanUp 16 | 17 | include FilesToDelete 18 | 19 | attr_accessor :count, :failure, :folder, :success 20 | 21 | def initialize(folder) 22 | @count = 0 23 | @failure = 0 24 | @success = 0 25 | @folder = folder 26 | setup 27 | end 28 | 29 | def setup 30 | if ! @folder 31 | puts" [✖] Please supply a folder" 32 | abort 33 | end 34 | 35 | clean 36 | end 37 | 38 | def clean 39 | @folder.sub! '~', ENV['HOME'] 40 | 41 | Dir.foreach(@folder) do |item| 42 | if FILE_TYPES.include? File.extname(item) 43 | 44 | FileUtils.rm("#{@folder}/#{item}") 45 | 46 | if ! File.exist?("#{@folder}/#{item}") 47 | puts " [✔] #{item} was deleted" 48 | else 49 | puts " [✖] #{item} was not deleted" 50 | end 51 | end 52 | end 53 | 54 | end 55 | end 56 | 57 | CleanUp.new(ARGV[0].dup) 58 | -------------------------------------------------------------------------------- /commands/color_mode.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | 5 | starship_path = "~/.config/starship" 6 | 7 | # If we toggle dark mode via Alfred, we end up in a infinite loop. The dark-mode 8 | # binary changes the MacOS mode which in turn causes color-mode-notify to run 9 | # this script. This script then calls dark-mode (via the app_macos method) 10 | # which kick starts this loop all over again. We use this boolean var 11 | # to detect when we've run the command via the cmdline or alfred. 12 | ran_from_cmd_line = False 13 | 14 | # The order in which apps are changed 15 | apps = [ 16 | "macos", 17 | # "starship", 18 | "fish", 19 | ] 20 | 21 | 22 | def app_macos(mode): 23 | """ 24 | Change the macOS environment 25 | """ 26 | path_to_file = "~/.color_mode" 27 | 28 | # Open the color_mode file 29 | with open(os.path.expanduser(path_to_file), "r") as config_file: 30 | contents = config_file.read() 31 | 32 | # Change the mode to ensure on a fresh startup, the color is remembered 33 | if mode == "dark": 34 | contents = contents.replace("light", "dark") 35 | if ran_from_cmd_line: 36 | subprocess.run(["dark-mode", "on"]) 37 | 38 | if mode == "light": 39 | contents = contents.replace("dark", "light") 40 | if ran_from_cmd_line: 41 | subprocess.run(["dark-mode", "off"]) 42 | 43 | with open(os.path.expanduser(path_to_file), "w") as config_file: 44 | config_file.write(contents) 45 | 46 | 47 | def app_starship(mode): 48 | """ 49 | Change the prompt in the terminal 50 | """ 51 | if mode == "dark": 52 | return subprocess.run( 53 | [ 54 | "cp", 55 | os.path.expanduser(starship_path + "/starship_dark.toml"), 56 | os.path.expanduser(starship_path + "/starship.toml"), 57 | ] 58 | ) 59 | 60 | if mode == "light": 61 | return subprocess.run( 62 | [ 63 | "cp", 64 | os.path.expanduser(starship_path + "/starship_light.toml"), 65 | os.path.expanduser(starship_path + "/starship.toml"), 66 | ] 67 | ) 68 | 69 | 70 | 71 | def app_fish(mode): 72 | return subprocess.run(["/opt/homebrew/bin/fish"]) 73 | 74 | 75 | def run_apps(mode=None): 76 | """ 77 | Based on the apps in our list, sequentially run and trigger them 78 | """ 79 | if mode == None: 80 | mode = get_mode() 81 | 82 | for app in apps: 83 | getattr(sys.modules[__name__], "app_%s" % app)(mode) 84 | 85 | return 86 | 87 | 88 | def get_mode(): 89 | """ 90 | Determine what mode macOS is currently in 91 | """ 92 | try: 93 | subprocess.run( 94 | ["defaults", "read", "-g", "AppleInterfaceStyle"], 95 | capture_output=True, 96 | check=True, 97 | timeout=5, 98 | ) 99 | return "dark" 100 | except subprocess.CalledProcessError: 101 | return "light" 102 | 103 | 104 | if __name__ == "__main__": 105 | # If we've passed a specific mode then activate it 106 | try: 107 | if sys.argv[1]: 108 | ran_from_cmd_line = True 109 | run_apps(sys.argv[1]) 110 | except IndexError: 111 | run_apps() 112 | -------------------------------------------------------------------------------- /commands/make_separator.rb: -------------------------------------------------------------------------------- 1 | def separator(title, spacer, vim) 2 | if ! defined?(spacer) or spacer == nil or spacer.length == 0 3 | spacer = "-" 4 | end 5 | 6 | seperator_count = 80 7 | 8 | if vim == 's' 9 | vim_start = " {{{" 10 | seperator_count = seperator_count - vim_start.length 11 | end 12 | if vim == 'e' 13 | vim_end = " }}}" 14 | seperator_count = seperator_count - vim_end.length 15 | end 16 | seperator_count = (seperator_count - title.length) / 2 17 | seperator = spacer*seperator_count + title.upcase + (spacer*seperator_count + (vim_start ? vim_start : '') + (vim_end ? vim_end : '')) 18 | 19 | puts ("\n" + (seperator.length.odd? ? spacer : '') + seperator) 20 | end 21 | 22 | puts "Enter your desired title: " 23 | title = gets.chomp 24 | 25 | puts "Which character should make up the separator? (- is default): " 26 | spacer = gets.chomp 27 | 28 | puts "Include Vim fold? (s for starting fold, e for ending fold, blank for nothing): " 29 | vim = gets.chomp 30 | 31 | puts separator(title, spacer, vim) -------------------------------------------------------------------------------- /commands/seticons.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from pathlib import Path 4 | from termcolor import cprint 5 | 6 | icon_file = str(Path.home()) + '/.dotfiles/misc/ui/icons/icons.json' 7 | 8 | # Open the JSON icon file 9 | with open(icon_file) as json_file: 10 | data = json.load(json_file) 11 | 12 | # Loop through each app and begin to replace the icons 13 | for app in data.values(): 14 | 15 | # Set the icon paths 16 | icon = app['new_icon'] 17 | icon = icon.replace(" ", "\ ") 18 | icon_path = str(Path.home()) + '/.dotfiles/misc/ui/icons/icon_files/' + icon 19 | 20 | # Set the application paths 21 | app_icon = app['icon'].replace(" ", "\ ") 22 | app_escaped = app['name'].replace(" ", "\ ") 23 | app_path = '/Applications/' + app['name'] 24 | app_path_escaped = '/Applications/' + app_escaped 25 | app_new_icon = app_path_escaped + '/Contents/Resources/' + app_icon 26 | 27 | # Change the icons via the command line 28 | if os.path.exists(app_path): 29 | try: 30 | if os.system('sudo cp ' + icon_path + ' ' + app_new_icon + ' &> /dev/null') != 0: 31 | raise Exception 32 | 33 | if os.system('sudo touch ' + app_path_escaped + ' &> /dev/null') != 0: 34 | raise Exception 35 | 36 | cprint ('✔ ' + app['name'], 'green') 37 | except: 38 | cprint ('✘ ' + app['name'], 'red') 39 | else: 40 | cprint ('✘ ' + app['name'] + ' could not be found', 'red') 41 | 42 | # Reload the Finder app and the DockZ 43 | os.system('sudo killall Finder && sudo killall Dock') 44 | 45 | # Example JSON file 46 | # { 47 | # "1Password": { 48 | # "name": "1Password 7.app", 49 | # "icon": "1Password.icns", 50 | # "new_icon": "1Password.icns" 51 | # }, 52 | # "AppCleaner": { 53 | # "name": "AppCleaner.app", 54 | # "icon": "AppCleaner.icns", 55 | # "new_icon": "AppCleaner.icns" 56 | # } 57 | # } 58 | -------------------------------------------------------------------------------- /commands/ssh.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -w 2 | 3 | # This command generates a new SSH key, saves it into $HOME/.ssh, copies the 4 | # public key to the clipboard before deleting it rom the computer. The 5 | # command also adds the relevant data to the ~/.ssh/config file 6 | 7 | $LOAD_PATH.unshift(File.dirname(__FILE__).to_s) 8 | 9 | puts 'Q. What is the alias for this key? (e.g. GitHub): ' 10 | host_alias = gets.chomp 11 | 12 | puts 'Q. What is the host name or the host IP?: ' 13 | host_name = gets.chomp 14 | 15 | puts 'Q. What is the username assigned to this key?: ' 16 | user_name = gets.chomp 17 | 18 | ssh_key_content = "\n" \ 19 | "Host #{host_alias}\n" \ 20 | "HostName #{host_name}\n" \ 21 | "User #{user_name}\n" \ 22 | "IdentityFile ~/.ssh/#{host_alias}" 23 | 24 | # Now let's make the key 25 | system "(ssh-keygen -t rsa -b 4096 -C \"#{user_name}\" -f \"#{ENV['HOME']}/.ssh/#{host_alias}\" &> /dev/null)" 26 | 27 | # And let's add the ssh key to the config file 28 | open("#{ENV['HOME']}/.ssh/config", 'a') do |f| 29 | f.puts ssh_key_content 30 | puts '~> Added key to ~/.ssh/config file' 31 | end 32 | 33 | # Copy the public key to the clipboard 34 | system "(pbcopy < \"#{ENV['HOME']}/.ssh/#{host_alias}.pub\" &> /dev/null)" 35 | puts '~> Copied key to clipboard' 36 | 37 | # And remove it from the computer for security purposes 38 | system "(rm \"#{ENV['HOME']}/.ssh/#{host_alias}.pub\" &> /dev/null)" 39 | puts '~> Removed public key from ~/.ssh folder' 40 | -------------------------------------------------------------------------------- /dotbot.conf.yaml: -------------------------------------------------------------------------------- 1 | - defaults: 2 | link: 3 | create: true 4 | force: true 5 | relink: true 6 | 7 | - clean: ["~"] 8 | 9 | - link: 10 | # Config directory 11 | ~/.config/1password: .config/1password 12 | ~/.config/efm-langserver: .config/efm-langserver 13 | ~/.config/espanso: .config/espanso 14 | ~/.config/fish: .config/fish 15 | ~/.config/hammerspoon: .config/hammerspoon 16 | ~/.config/lazygit: .config/lazygit 17 | ~/.config/mise: .config/mise 18 | ~/.config/mcphub: .config/mcphub 19 | ~/.config/nvim: .config/nvim 20 | ~/.config/rclone: .config/rclone 21 | ~/.config/snippets: .config/snippets 22 | ~/.config/superwhisper: .config/superwhisper 23 | ~/.config/vivid: .config/vivid 24 | ~/.config/wezterm: .config/wezterm 25 | 26 | # Launch Agents 27 | ~/Library/LaunchAgents/oli.cloud-backup.plist: misc/LaunchAgents/oli.cloud-backup.plist 28 | ~/Library/LaunchAgents/oli.color-mode-notify.plist: misc/LaunchAgents/oli.color-mode-notify.plist 29 | ~/Library/LaunchAgents/oli.finance-output.plist: misc/LaunchAgents/oli.finance-output.plist 30 | 31 | # Others 32 | ~/.applescript: .config/applescript 33 | ~/.bash_profile: .config/bash/.bash_profile 34 | ~/.bashrc: .config/bash/.bashrc 35 | ~/.color_mode: .config/.color_mode 36 | ~/.composer: .config/composer 37 | ~/.env: .config/env/.env 38 | ~/.gitconfig: .config/git/.gitconfig 39 | ~/.gitignore_global: .config/git/.gitignore_global 40 | ~/.mackup: .config/mackup/.mackup 41 | ~/.mackup.cfg: .config/mackup/.mackup.cfg 42 | ~/.profile: .config/bash/.profile 43 | ~/.pypirc: .config/.pypirc 44 | ~/.ssh: .config/ssh 45 | ~/.tofish: .config/fish/.tofish 46 | ~/.vim/custom_snippets: .config/vim/custom_snippets 47 | ~/.vimrc: .config/vim/.vimrc 48 | ~/.vimrc.plugins: .config/vim/.vimrc.plugins 49 | ~/.local/share/fish/fish_history: .config/fish/fish_history 50 | -------------------------------------------------------------------------------- /dotbot_uninstall: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | import yaml 6 | import os 7 | 8 | CONFIG="dotbot.conf.yaml" 9 | 10 | stream = open(CONFIG, "r") 11 | conf = yaml.load(stream, yaml.FullLoader) 12 | 13 | for section in conf: 14 | if 'link' in section: 15 | for target in section['link']: 16 | realpath = os.path.expanduser(target) 17 | if os.path.islink(realpath): 18 | print("Removing ", realpath) 19 | os.unlink(realpath) 20 | -------------------------------------------------------------------------------- /misc/LaunchAgents/oli.cloud-backup.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KeepAlive 6 | 7 | NetworkState 8 | 9 | 10 | Label 11 | oli.cloud-backup 12 | Program 13 | /Users/Oli/.dotfiles/bin/cloud-backup 14 | StandardErrorPath 15 | /Users/Oli/.cloud-backup-stderr.log 16 | StandardOutPath 17 | /Users/Oli/.cloud-backup-stdout.log 18 | StartCalendarInterval 19 | 20 | 21 | Hour 22 | 9 23 | Minute 24 | 0 25 | 26 | 27 | Hour 28 | 20 29 | Minute 30 | 0 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /misc/LaunchAgents/oli.color-mode-notify.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KeepAlive 6 | 7 | Label 8 | oli.color-mode-notify 9 | ProgramArguments 10 | 11 | /Users/Oli/.dotfiles/bin/color-mode-notify 12 | /Users/Oli/.dotfiles/bin/color-mode 13 | 14 | StandardErrorPath 15 | /Users/Oli/.color-mode-notify-stderr.log 16 | StandardOutPath 17 | /Users/Oli/.color-mode-notify-stdout.log 18 | 19 | 20 | -------------------------------------------------------------------------------- /misc/LaunchAgents/oli.finance-output.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Disabled 6 | 7 | Label 8 | oli.finance-output 9 | Program 10 | /Users/Oli/.dotfiles/bin/finance-output 11 | RunAtLoad 12 | 13 | StandardErrorPath 14 | /Users/Oli/.finance-output-stderr.log 15 | StandardOutPath 16 | /Users/Oli/.finance-output-stdout.log 17 | StartCalendarInterval 18 | 19 | 20 | Hour 21 | 19 22 | Minute 23 | 45 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /misc/packages/app_store.txt: -------------------------------------------------------------------------------- 1 | 1569813296 1Password for Safari (8.10.76) 2 | 1018301773 AdBlock Pro (11.3.1) 3 | 961632517 Be Focused Pro (2.5) 4 | 1091189122 Bear (2.4.4) 5 | 1511935951 BetterJSON (2.3) 6 | 1339170533 CleanMyMac (5.0.9) 7 | 1287239339 ColorSlurp (3.9.2) 8 | 411643860 DaisyDisk (4.31) 9 | 1293083751 GSE SMART IPTV (4.4) 10 | 1502839586 Hand Mirror (3.2) 11 | 1540705431 Hover for Safari (3.5) 12 | 1544743900 Hush (1.0.17) 13 | 453114608 JSON Helper (1.20) 14 | 1357379892 Menu Bar Controller for Sonos (5.3.1) 15 | 462058435 Microsoft Excel (16.97.2) 16 | 985367838 Microsoft Outlook (16.97.2) 17 | 462062816 Microsoft PowerPoint (16.97.2) 18 | 462054704 Microsoft Word (16.97.2) 19 | 1289197285 MindNode (2023.6.4) 20 | 1592917505 Noir (2025.1.4) 21 | 1559269364 Notion Web Clipper (1.0.3) 22 | 1502111349 PDF Squeezer (4.5.4) 23 | 1289583905 Pixelmator Pro (3.6.18) 24 | 568494494 Pocket (1.8.7) 25 | 483820530 QR Journal (3.0) 26 | 1529448980 Reeder (5.5.1) 27 | 897118787 Shazam (2.11.0) 28 | 439654198 SimpleMind (2.6.1) 29 | 1176895641 Spark (2.11.55) 30 | 1087079310 Switch Sound File Converter (13.07) 31 | 1533078079 Teleprompter (3.9.25) 32 | 425424353 The Unarchiver (4.3.9) 33 | 1295203466 Windows App (11.0.6) 34 | 1470732135 Wondershare PDFelement (11.4.17) 35 | 899972312 WordService (2.8.3) 36 | -------------------------------------------------------------------------------- /misc/packages/brew_cask.txt: -------------------------------------------------------------------------------- 1 | 1password 2 | 1password-cli 3 | aldente 4 | anki 5 | appcleaner 6 | audacity 7 | cleanshot 8 | discord 9 | espanso 10 | font-dejavu-sans-mono-for-powerline 11 | git-credential-manager 12 | git-credential-manager-core 13 | google-chrome 14 | hammerspoon 15 | hazel 16 | imageoptim 17 | itsycal 18 | jordanbaird-ice 19 | launchcontrol 20 | little-snitch 21 | mitmproxy 22 | monitorcontrol 23 | notion 24 | obs 25 | openemu 26 | pdf-expert 27 | pixelsnap 28 | postman 29 | raycast 30 | rectangle-pro 31 | reminders-menubar 32 | screenflow 33 | sketch 34 | spotify 35 | steam 36 | superkey 37 | tableplus 38 | telegram 39 | textexpander 40 | typora 41 | visual-studio-code 42 | vlc 43 | wezterm 44 | xscope 45 | zed 46 | -------------------------------------------------------------------------------- /misc/packages/brew_packages.txt: -------------------------------------------------------------------------------- 1 | act 2 | age 3 | bats-core 4 | bob 5 | brew-cask-completion 6 | code-minimap 7 | composer 8 | dark-mode 9 | dash 10 | docker 11 | fd 12 | fish 13 | flux 14 | fortune 15 | fzf 16 | gh 17 | git 18 | glow 19 | go 20 | gradle 21 | hledger 22 | httpie 23 | imagemagick 24 | jython 25 | lazygit 26 | ledger 27 | librsync 28 | libvterm 29 | lolcat 30 | luajit 31 | mackup 32 | mas 33 | maven 34 | mise 35 | most 36 | mvfst 37 | neofetch 38 | ninja 39 | node 40 | opencv 41 | ossp-uuid 42 | pandoc 43 | php@8.1 44 | pinentry-mac 45 | pipx 46 | pngpaste 47 | postgresql@14 48 | pre-commit 49 | rclone 50 | reattach-to-user-namespace 51 | ripgrep 52 | shfmt 53 | shunit2 54 | spring-io/tap/spring-boot 55 | subversion 56 | tree 57 | tree-sitter 58 | typos-cli 59 | universal-ctags 60 | vim 61 | vivid 62 | watchman 63 | wget 64 | yarn 65 | zlib 66 | zoxide 67 | zsh 68 | zsh-completions 69 | -------------------------------------------------------------------------------- /misc/packages/brew_taps.txt: -------------------------------------------------------------------------------- 1 | homebrew/cask-fonts 2 | homebrew/services 3 | microsoft/git 4 | spring-io/tap 5 | -------------------------------------------------------------------------------- /misc/packages/npm_packages.txt: -------------------------------------------------------------------------------- 1 | corepack 2 | mcp-hub 3 | npm 4 | -------------------------------------------------------------------------------- /misc/packages/python_pip.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.13.4 2 | bs4==0.0.2 3 | cfgv==3.4.0 4 | distlib==0.3.9 5 | dotbot==1.21.0 6 | filelock==3.18.0 7 | identify==2.6.12 8 | iniconfig==2.1.0 9 | nodeenv==1.9.1 10 | packaging==25.0 11 | platformdirs==4.3.8 12 | pluggy==1.6.0 13 | pre_commit==4.2.0 14 | pygame==2.6.1 15 | pytest==8.3.5 16 | PyYAML==6.0.2 17 | soupsieve==2.7 18 | typing_extensions==4.13.2 19 | virtualenv==20.31.2 20 | -------------------------------------------------------------------------------- /misc/packages/ruby_gems.txt: -------------------------------------------------------------------------------- 1 | base64 2 | benchmark 3 | bigdecimal 4 | bundler 5 | cgi 6 | chronic 7 | colorize 8 | concurrent-ruby 9 | csv 10 | date 11 | debug 12 | delegate 13 | dentaku 14 | did_you_mean 15 | diff-lcs 16 | digest 17 | drb 18 | english 19 | erb 20 | error_highlight 21 | etc 22 | fcntl 23 | fiddle 24 | fileutils 25 | find 26 | forwardable 27 | getoptlong 28 | highline 29 | hledger-forecast 30 | i18n 31 | io-console 32 | io-nonblock 33 | io-wait 34 | ipaddr 35 | irb 36 | json 37 | language_server-protocol 38 | logger 39 | matrix 40 | minitest 41 | money 42 | mutex_m 43 | net-ftp 44 | net-http 45 | net-imap 46 | net-pop 47 | net-protocol 48 | net-smtp 49 | nkf 50 | observer 51 | open-uri 52 | open3 53 | openssl 54 | optparse 55 | ostruct 56 | pathname 57 | power_assert 58 | pp 59 | prettyprint 60 | prime 61 | prism 62 | pstore 63 | psych 64 | racc 65 | rake 66 | rbs 67 | rchardet 68 | rdoc 69 | readline 70 | readline-ext 71 | reckon 72 | reline 73 | resolv 74 | resolv-replace 75 | rexml 76 | rinda 77 | rspec 78 | rspec-core 79 | rspec-expectations 80 | rspec-its 81 | rspec-mocks 82 | rspec-support 83 | rss 84 | ruby-lsp 85 | ruby2_keywords 86 | rubygems-update 87 | securerandom 88 | set 89 | shellwords 90 | singleton 91 | sorbet-runtime 92 | stringio 93 | strscan 94 | syntax_suggest 95 | syslog 96 | tempfile 97 | terminal-table 98 | test-unit 99 | time 100 | timeout 101 | tmpdir 102 | tsort 103 | typeprof 104 | un 105 | unicode-display_width 106 | unicode-emoji 107 | uri 108 | weakref 109 | yaml 110 | zlib 111 | -------------------------------------------------------------------------------- /misc/packages/rust_cargo.txt: -------------------------------------------------------------------------------- 1 | bob-nvim 2 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | indent_width = 2 2 | column_width = 120 3 | indent_type = "Spaces" 4 | -------------------------------------------------------------------------------- /tasks/apps.rake: -------------------------------------------------------------------------------- 1 | namespace :install do 2 | desc 'Install Neovim' 3 | task :neovim do 4 | section 'Installing Neovim' 5 | 6 | unless testing? 7 | run %( bob install stable ) 8 | run %( bob install nightly ) 9 | run %( bob use nightly ) 10 | # time = Time.new.strftime('%s') 11 | # run %( git clone --depth 1 --branch nightly https://github.com/neovim/neovim ~/.neovim/#{time} ) 12 | # run %( rm -rf /opt/homebrew/bin/nvim ) 13 | # run %( rm -rf /usr/local/bin/nvim ) 14 | # run %( rm -rf /usr/local/share/nvim ) 15 | # run %( \(cd ~/.neovim/#{time} && make CMAKE_BUILD_TYPE=RelWithDebInfo && make install\) ) 16 | # run %( ln -s ~/.neovim/#{time} ~/.neovim/latest ) 17 | end 18 | end 19 | 20 | # As per: 21 | # https://blog.backtick.consulting/neovims-built-in-lsp-with-ruby-and-rails/ 22 | desc 'Install Rails YARD directives' 23 | task :rails do 24 | section 'Installing Rails YARD directives' 25 | 26 | run %( git clone https://gist.github.com/castwide/28b349566a223dfb439a337aea29713e ~/.dotfiles/misc/enhance-rails-intellisense-in-solargraph ) 27 | end 28 | 29 | desc 'Install Vim plugins' 30 | task :vim do 31 | section 'Installing Vim plugins' 32 | 33 | unless testing? 34 | run %( mkdir ~/.vim/swp ) 35 | run %( mkdir ~/.vim/undo ) 36 | run %( curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim ) 37 | run %( vim +PlugInstall +qall ) 38 | end 39 | end 40 | end 41 | 42 | namespace :update do 43 | desc 'Update Neovim' 44 | task :neovim do 45 | section 'Updating Neovim' 46 | 47 | unless testing? 48 | run %( bob update ) 49 | # run %( rm ~/.neovim/backup ) 50 | # run %( mv ~/.neovim/latest ~/.neovim/backup ) 51 | # Rake::Task['install:neovim'].invoke 52 | end 53 | end 54 | 55 | # desc 'Update Neovim plugins' 56 | # task :neovim_plugins do 57 | # section 'Updating Neovim plugins' 58 | # 59 | # run %( nvim --headless "+Lazy! sync" +qa ) 60 | # end 61 | 62 | desc 'Update Rails YARD directives' 63 | task :rails do 64 | section 'Updating Rails YARD directives' 65 | 66 | run %( git -C ~/.dotfiles/misc/enhance-rails-intellisense-in-solargraph pull ) 67 | end 68 | 69 | desc 'Update Vim plugins' 70 | task :vim do 71 | section 'Updating Vim plugins' 72 | 73 | run %( vim +PlugUpdate +qall ) unless testing? 74 | end 75 | end 76 | 77 | namespace :rollback do 78 | desc 'Rollback Neovim' 79 | task :neovim do 80 | section 'Rolling back Neovim' 81 | 82 | unless testing? 83 | run %( bob rollback ) 84 | # run %( rm -rf /usr/local/bin/nvim ) 85 | # run %( rm -rf /opt/homebrew/bin/nvim ) 86 | # 87 | # # Delete the most recent folder 88 | # run %( cd ~/.neovim & rm -rf .DS_Store) 89 | # run %( (cd ~/.neovim && ls -Art | tail -n 1 | xargs rm -rf) ) 90 | # 91 | # # Restore Neovim from the previous nightly build 92 | # run %( (cd ~/.neovim && ls -Art | fgrep -v .DS_Store | tail -n 1 | xargs -I{} cp -s ~/.neovim/1705399006/build/bin/nvim /usr/local/bin) ) 93 | end 94 | end 95 | end 96 | 97 | namespace :uninstall do 98 | desc 'Uninstall Neovim' 99 | task :neovim do 100 | section 'Uninstalling Neovim' 101 | 102 | unless testing? 103 | run %( bob erase ) 104 | # run %( rm ~/.neovim/backup ) 105 | # run %( mv ~/.neovim/latest ~/.neovim/backup ) 106 | # run %( rm -rf /usr/local/bin/nvim ) 107 | # run %( rm -rf /opt/homebrew/bin/nvim ) 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /tasks/files.rake: -------------------------------------------------------------------------------- 1 | namespace :backup do 2 | desc 'Backup app config' 3 | task :app_config do 4 | section 'Using Mackup to backup app configs' 5 | 6 | if ENV['DRY_RUN'] 7 | puts "~> Chill! It's a dry run" 8 | system %( mackup backup --dry-run && mackup uninstall --dry-run ) 9 | else 10 | run %( mackup backup --force && mackup uninstall --force ) 11 | end 12 | end 13 | 14 | desc 'Backup files' 15 | task :files, [:progress] do |_t, args| 16 | run %( /bin/date -u ) 17 | 18 | section 'Using RCLONE to backup files' 19 | 20 | dirs = { 21 | '.dotfiles' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:dotfiles", 22 | 'Code' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:Code", 23 | 'OliDocs' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:Documents", 24 | 'Downloads' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:Downloads", 25 | 'Documents' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:ICloud_Docs" 26 | } 27 | 28 | flag = ' -P' if args[:progress] 29 | filter = ' --filter-from ~/.config/rclone/filter_list.txt' 30 | 31 | dirs.each do |local, remote| 32 | run %( /opt/homebrew/bin/rclone sync ~/#{local} #{remote}#{flag}#{filter} ) 33 | end 34 | end 35 | end 36 | 37 | namespace :install do 38 | desc 'Install files' 39 | 40 | task :app_config do 41 | section 'Installing Mackup' 42 | 43 | if !File.file?(File.expand_path('~/.mackup.cfg')) && !ENV['DRY_RUN'] 44 | run %( ln -s #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER + File::SEPARATOR}.mackup.cfg #{'~' + File::SEPARATOR}.mackup.cfg ) 45 | run %( ln -s #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER + File::SEPARATOR}.mackup #{'~' + File::SEPARATOR}.mackup ) 46 | else 47 | puts '~> Already installed' 48 | end 49 | 50 | section 'Using Mackup to restore app configs' 51 | 52 | if ENV['DRY_RUN'] 53 | puts "~> Chill! It's a dry run" 54 | system %( mackup restore --dry-run && mackup uninstall --dry-run ) 55 | else 56 | run %( mackup restore --force && mackup uninstall --force ) 57 | end 58 | end 59 | 60 | task :dotfiles do 61 | section 'Using Dotbot to symlink dotfiles' 62 | 63 | run %( dotbot -c dotbot.conf.yaml ) 64 | end 65 | end 66 | 67 | namespace :uninstall do 68 | desc 'Uninstall dotfiles' 69 | 70 | # Don't need to uninstall Mackup as we don't use symlinks 71 | 72 | task :dotfiles do 73 | section 'Uninstall Dotbot and restoring dotfiles' 74 | 75 | run %( python dotbot_uninstall ) 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /tasks/packages.rake: -------------------------------------------------------------------------------- 1 | BREW_TAPS_FILE = File.expand_path('../misc/packages/brew_taps.txt', __dir__).gsub(/ /, '\ ') 2 | BREW_PACKAGES_FILE = File.expand_path('../misc/packages/brew_packages.txt', __dir__).gsub(/ /, '\ ') 3 | BREW_CASK_PACKAGES_FILE = File.expand_path('../misc/packages/brew_cask.txt', __dir__).gsub(/ /, '\ ') 4 | CARGO_FILE = File.expand_path('../misc/packages/rust_cargo.txt', __dir__).gsub(/ /, '\ ') 5 | GEMS_FILE = File.expand_path('../misc/packages/ruby_gems.txt', __dir__).gsub(/ /, '\ ') 6 | MAS_FILE = File.expand_path('../misc/packages/app_store.txt', __dir__).gsub(/ /, '\ ') 7 | NPM_FILE = File.expand_path('../misc/packages/npm_packages.txt', __dir__).gsub(/ /, '\ ') 8 | PIP_FILE = File.expand_path('../misc/packages/python_pip.txt', __dir__).gsub(/ /, '\ ') 9 | 10 | # HEAD_ONLY_FORMULAS = %w( neovim ) 11 | HEAD_ONLY_FORMULAS = '' 12 | 13 | namespace :backup do 14 | desc 'Backup Homebrew' 15 | task :brew do 16 | section 'Backing up Homebrew' 17 | 18 | run %( brew leaves > #{BREW_PACKAGES_FILE} ) 19 | run %( brew list --cask > #{BREW_CASK_PACKAGES_FILE} ) 20 | run %( brew tap > #{BREW_TAPS_FILE} ) 21 | end 22 | 23 | desc 'Backup App Store' 24 | task :app_store do 25 | section 'Backing up App Store apps' 26 | 27 | run %( mas list \> #{MAS_FILE} ) 28 | end 29 | 30 | desc 'Backup Ruby Gems' 31 | task :gems do 32 | section 'Backing up Ruby Gems' 33 | 34 | run %( gem list --no-versions | sed '1d' | awk '\{gsub\(/\\/.*\\//,"",$1\); print\}' \> #{GEMS_FILE} ) 35 | end 36 | 37 | desc 'Backup NPM files' 38 | task :npm do 39 | section 'Backing up NPM files' 40 | 41 | run %( npm -g upgrade ) 42 | run %( npm list --global --parseable --depth=0 | sed '1d' | awk '\{gsub\(/\\/.*\\//,"",$1\); print\}' \> #{NPM_FILE} ) 43 | end 44 | 45 | desc 'Backup PIP files' 46 | task :pip do 47 | section 'Backing up PIP files' 48 | 49 | run %( pip3 freeze \> #{PIP_FILE} ) 50 | end 51 | end 52 | 53 | namespace :install do 54 | desc 'Install XCode' 55 | task :xcode do 56 | section 'Installing XCode' 57 | 58 | run %( xcode-select --install ) 59 | end 60 | 61 | desc 'Install Homebrew' 62 | task :brew do 63 | section 'Installing Homebrew' 64 | 65 | run %( /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\" ) 66 | run %( echo >> /Users/$(whoami)/.zprofile) 67 | run %( echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/$(whoami)/.zprofile) 68 | run %( eval "$(/opt/homebrew/bin/brew shellenv)" ) 69 | 70 | # puts '~> Updating Homebrew directory permissions' 71 | # run %( sudo chown -R $(whoami) /usr/local/ ) 72 | # run %( sudo chown -R $(whoami) /opt/homebrew/ ) 73 | 74 | puts '~> Installing Homebrew taps' 75 | brew_taps.each do |tap| 76 | run %( brew tap #{tap} ) 77 | end 78 | 79 | run %( brew analytics off ) 80 | end 81 | 82 | desc 'Install Homebrew Packages' 83 | task :brew_packages do 84 | section 'Installing Homebrew Packages' 85 | 86 | brew_packages.each do |package| 87 | if HEAD_ONLY_FORMULAS.include?(package) 88 | run %( brew install --HEAD #{package} ) 89 | else 90 | run %( brew install #{package} ) 91 | end 92 | end 93 | end 94 | 95 | desc 'Install Homebrew Cask Packages' 96 | task :brew_cask_packages do 97 | section 'Installing Homebrew Cask Packages' 98 | 99 | brew_cask_packages.each do |package| 100 | run %( brew install --force --appdir="/Applications" --fontdir="/Library/Fonts" #{package} ) 101 | end 102 | end 103 | 104 | desc 'Clean up Homebrew' 105 | task :brew_clean_up do 106 | section 'Cleaning up Homebrew' 107 | 108 | run %( brew cleanup ) 109 | end 110 | 111 | desc 'Install App Store apps' 112 | task :app_store do 113 | section 'Installing App Store apps' 114 | 115 | app_store_apps.each do |app| 116 | run %( mas install #{app} ) 117 | end 118 | end 119 | 120 | desc 'Install Rust' 121 | task :rust do 122 | section 'Installing Rust' 123 | 124 | run %(curl https://sh.rustup.rs -sSf | sh) 125 | end 126 | 127 | desc 'Install Rust Cargo' 128 | task :cargo do 129 | section 'Installing Rust Cargo' 130 | 131 | cargo_apps.each do |app| 132 | run %( cargo install #{app} ) 133 | end 134 | end 135 | 136 | desc 'Install Ruby Gems' 137 | task :gems do 138 | section 'Installing Ruby Gems' 139 | 140 | run %( xargs gem install \< #{GEMS_FILE} ) 141 | end 142 | 143 | desc 'Install NPM files' 144 | task :npm do 145 | section 'Installing NPM files' 146 | 147 | run %( xargs npm install --global \< #{NPM_FILE} ) 148 | end 149 | 150 | desc 'Install PIP files' 151 | task :pip do 152 | section 'Installing PIP files' 153 | 154 | run %( pip3 install -r #{PIP_FILE} ) 155 | end 156 | end 157 | 158 | namespace :update do 159 | desc 'Update Homebrew' 160 | task :brew do 161 | section 'Updating Homebrew' 162 | 163 | run %( brew update ) 164 | run %( brew upgrade ) 165 | end 166 | 167 | desc 'Update Fish' 168 | task :fish do 169 | section 'Updating Fish plugins' 170 | 171 | run %( fisher update ) 172 | end 173 | 174 | desc 'Update Ruby Gems' 175 | task :gems do 176 | section 'Updating Ruby Gems' 177 | 178 | run %( gem update --system && gem update ) 179 | end 180 | 181 | desc 'Update NPM packages' 182 | task :npm do 183 | section 'Updating NPM' 184 | 185 | run %( npm install -g npm && npm update -g ) 186 | end 187 | 188 | desc 'Update PIP files' 189 | task :pip do 190 | section 'Updating PIP files' 191 | 192 | begin 193 | run %( pip3 install --upgrade pip ) 194 | run %( pip3 freeze \> #{PIP_FILE} ) 195 | find_replace(PIP_FILE, '==', '>=') 196 | run %( pip3 install -r #{PIP_FILE} --upgrade ) 197 | rescue StandardError 198 | puts 'PIP update failed' 199 | end 200 | end 201 | end 202 | 203 | def brew_taps 204 | File.readlines(BREW_TAPS_FILE).map(&:strip) 205 | end 206 | 207 | def brew_packages 208 | File.readlines(BREW_PACKAGES_FILE).map(&:strip) 209 | end 210 | 211 | def brew_cask_packages 212 | File.readlines(BREW_CASK_PACKAGES_FILE).map(&:strip) 213 | end 214 | 215 | def app_store_apps 216 | File.readlines(MAS_FILE).map(&:split).map(&:first) 217 | end 218 | 219 | def cargo_apps 220 | File.readlines(CARGO_FILE).map(&:split).map(&:first) 221 | end 222 | -------------------------------------------------------------------------------- /tasks/system.rake: -------------------------------------------------------------------------------- 1 | FONT_PATH = File.expand_path('misc/ui/fonts').gsub(/ /, '\ ') 2 | 3 | namespace :install do 4 | desc 'Make dotfiles/bin executable' 5 | task :chmod do 6 | section 'Making dotfiles/bin executable' 7 | 8 | run %( chmod -R +x ~/.dotfiles/bin/ ) 9 | end 10 | 11 | desc 'Install Fish shell' 12 | task :fish do 13 | section 'Installing Fish shell and plugins' 14 | 15 | # run %( echo $(which fish) | sudo tee -a /etc/shells ) 16 | # run %( chsh -s $(which fish) ) 17 | 18 | run %( curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher ) 19 | run %( fisher update ) 20 | end 21 | 22 | desc 'Install fonts' 23 | task :fonts do 24 | section 'Installing fonts' 25 | 26 | unless testing? 27 | Dir.foreach(FONT_PATH) do |font| 28 | next if ['.', '..', '.DS_Store'].include?(font) 29 | 30 | escaped_path = FONT_PATH.gsub("'") { "\\'" } 31 | escaped_path = escaped_path.gsub(' ') { '\\ ' } 32 | font = font.gsub(' ') { '\\ ' } 33 | run %( cp #{escaped_path}/#{font} ~/Library/Fonts ) 34 | end 35 | end 36 | end 37 | 38 | desc 'Change Hammerspoon directory' 39 | task :hammerspoon do 40 | section 'Changing Hammerspoon directory' 41 | 42 | run %( defaults write org.hammerspoon.Hammerspoon MJConfigFile "~/.config/hammerspoon/init.lua" ) 43 | end 44 | 45 | desc 'Install Launch Agents' 46 | task :launch_agents do 47 | section 'Installing Launch Agents' 48 | 49 | run %( launchctl load -w ~/Library/LaunchAgents/oli.cloud-backup.plist ) 50 | run %( launchctl load -w ~/Library/LaunchAgents/oli.color-mode-notify.plist ) 51 | run %( launchctl load -w ~/Library/LaunchAgents/oli.finance-output.plist ) 52 | end 53 | 54 | desc 'Install macOS Configurations' 55 | task :macos do 56 | section 'Installing macOS Configurations' 57 | 58 | run %( sh ./commands/macos ) 59 | end 60 | 61 | desc 'Install Servers' 62 | task :servers do 63 | section 'Installing servers' 64 | 65 | unless testing? 66 | run %( mise use --global lua@latest ) 67 | run %( mise install ) 68 | end 69 | end 70 | end 71 | 72 | namespace :update do 73 | desc 'Patch fonts' 74 | task :fonts do 75 | section 'Patching fonts' 76 | 77 | fontforge_dir = File.expand_path('~/.fontforge') 78 | input_dir = File.expand_path('~/fonts_to_patch') 79 | output_dir = File.expand_path('~/patched_fonts') 80 | 81 | unless testing? 82 | # Check if fontforge is installed 83 | run %( git clone https://github.com/ryanoasis/nerd-fonts ~/.fontforge ) unless File.directory?(fontforge_dir) 84 | 85 | yesno?("Have you copied fonts to the #{input_dir} folder?") 86 | 87 | run %( cd ~/.fontforge && git pull ) 88 | Dir.foreach(input_dir) do |font| 89 | run %( cd ~/.fontforge && fontforge -script font-patcher --complete --progressbars --out #{output_dir} #{input_dir}/#{font} ) 90 | end 91 | end 92 | end 93 | 94 | desc 'Update Servers' 95 | task :servers do 96 | section 'Updating servers' 97 | 98 | run %( mise upgrade ) 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /tasks/tests.rake: -------------------------------------------------------------------------------- 1 | namespace :tests do 2 | desc "Setup tests" 3 | task :setup do 4 | section "Setting up the tests" 5 | 6 | if testing? 7 | DOTS_FOLDER = 'dotfiles' 8 | end 9 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/app_store.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/app_store.txt) 10 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/python_pip.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/python_pip.txt) 11 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/ruby_gems.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/ruby_gems.txt) 12 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/brew_taps.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/brew_taps.txt) 13 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/.mackup.cfg #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/.config/mackup/.mackup.cfg) 14 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/brew_packages.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/brew_packages.txt) 15 | run %( cp #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/tests/stubs/brew_cask.txt #{DIRECTORY_NAME + File::SEPARATOR + DOTS_FOLDER}/misc/packages/brew_cask.txt) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /tasks/utils.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | 3 | def section(title, _description = '') 4 | seperator_count = (80 - title.length) / 2 5 | puts ("\n" + '=' * seperator_count) + title.upcase + ('=' * seperator_count) 6 | puts '~> Performing as dry run' if ENV['DRY_RUN'] 7 | puts '~> Performing as super user' if ENV['SUDO'] 8 | puts '~> Performing as test env user' if ENV['TEST_ENV'] 9 | end 10 | 11 | def run(cmd) 12 | puts "~>#{cmd}" 13 | 14 | calling_file = File.basename(caller_locations[0].path) 15 | if ENV['TEST_ENV'] 16 | if testable?(calling_file) 17 | system cmd unless ENV['DRY_RUN'] 18 | else 19 | puts "~> Skipped for #{calling_file}" 20 | end 21 | else 22 | system cmd unless ENV['DRY_RUN'] 23 | end 24 | end 25 | 26 | def yesno?(question) 27 | require 'highline/import' 28 | exit unless HighLine.agree(question) 29 | end 30 | 31 | def testable?(filename) 32 | !SKIP_TESTS_FOR.include?(filename) 33 | end 34 | 35 | def testing? 36 | ENV['TEST_ENV'] 37 | end 38 | 39 | def find_replace(file_name, find, replace) 40 | file_name_new = file_name.gsub('~', ENV['HOME']) 41 | text = File.read(file_name_new) 42 | new_text = text.gsub(find, replace) 43 | 44 | # To write changes to the file, use: 45 | File.open(file_name_new, 'w') { |file| file.puts new_text } 46 | end 47 | -------------------------------------------------------------------------------- /tasks/work.rb: -------------------------------------------------------------------------------- 1 | namespace :work do 2 | namespace :restore do 3 | desc 'Restore files' 4 | task :files, [:progress] do |_t, args| 5 | run %( /bin/date -u ) 6 | 7 | dirs = { 8 | '.dotfiles' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:dotfiles", 9 | 'Code' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:Code" 10 | } 11 | 12 | flag = ' -P' if args[:progress] 13 | filter = ' --filter-from ~/.config/rclone/filter_list.txt' 14 | 15 | dirs.each do |local, remote| 16 | run %( /opt/homebrew/bin/rclone copy #{remote}#{flag} ~/#{local}#{filter} ) 17 | end 18 | end 19 | end 20 | 21 | namespace :backup do 22 | desc 'Backup files' 23 | task :files, [:progress] do |_t, args| 24 | run %( /bin/date -u ) 25 | 26 | dirs = { 27 | '.dotfiles' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:dotfiles", 28 | 'Code' => "#{ENV['STORAGE_ENCRYPTED_FOLDER']}:Code" 29 | } 30 | 31 | flag = ' -P' if args[:progress] 32 | filter = ' --filter-from ~/.config/rclone/filter_list.txt' 33 | 34 | dirs.each do |local, remote| 35 | run %( /opt/homebrew/bin/rclone copy ~/#{local} #{remote}#{flag}#{filter} ) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /tests/stubs/.mackup.cfg: -------------------------------------------------------------------------------- 1 | [storage] 2 | engine = file_system 3 | path = work/dotfiles/dotfiles 4 | directory = 5 | 6 | [applications_to_sync] 7 | Mackup 8 | my-files -------------------------------------------------------------------------------- /tests/stubs/app_store.txt: -------------------------------------------------------------------------------- 1 | 1091189122 Bear (1.8.2) -------------------------------------------------------------------------------- /tests/stubs/brew_cask.txt: -------------------------------------------------------------------------------- 1 | kitty 2 | -------------------------------------------------------------------------------- /tests/stubs/brew_packages.txt: -------------------------------------------------------------------------------- 1 | asdf 2 | mackup 3 | mas 4 | fish 5 | -------------------------------------------------------------------------------- /tests/stubs/brew_taps.txt: -------------------------------------------------------------------------------- 1 | homebrew/cask -------------------------------------------------------------------------------- /tests/stubs/python_pip.txt: -------------------------------------------------------------------------------- 1 | pynvim==0.4.3 -------------------------------------------------------------------------------- /tests/stubs/ruby_gems.txt: -------------------------------------------------------------------------------- 1 | rspec 2 | --------------------------------------------------------------------------------