├── .gitignore ├── gitconfig ├── gitignore ├── default.nix └── gitconfig ├── nix ├── nix.conf └── default.nix ├── homies.png ├── neovim ├── lua │ ├── README.md │ └── fzf.lua ├── default.nix └── init.lua ├── kitty ├── Info.plist ├── kitty.conf └── default.nix ├── LICENSE ├── homies.nix ├── zshrc ├── default.nix ├── zshrc └── fzf-key-bindings.zsh ├── README.md ├── flake.nix └── flake.lock /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | *.swp 3 | -------------------------------------------------------------------------------- /gitconfig/gitignore: -------------------------------------------------------------------------------- 1 | result 2 | result-* 3 | *.swp 4 | tags 5 | -------------------------------------------------------------------------------- /nix/nix.conf: -------------------------------------------------------------------------------- 1 | experimental-features = nix-command flakes 2 | -------------------------------------------------------------------------------- /homies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmattia/homies/HEAD/homies.png -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | # Nix, with a nix.conf baked in 2 | { nix, symlinkJoin, makeWrapper }: 3 | 4 | symlinkJoin { 5 | name = "nix"; 6 | nativeBuildInputs = [ makeWrapper ]; 7 | paths = [ nix ]; 8 | postBuild = '' 9 | wrapProgram $out/bin/nix \ 10 | --set NIX_USER_CONF_FILES ${./nix.conf} 11 | ''; 12 | } 13 | -------------------------------------------------------------------------------- /gitconfig/default.nix: -------------------------------------------------------------------------------- 1 | # gitconfig file 2 | { runCommand, git, symlinkJoin, writeTextFile }: 3 | let 4 | 5 | gitconfig = writeTextFile 6 | { 7 | name = "git-config"; 8 | text = 9 | builtins.replaceStrings 10 | [ "SUBSTITUTE_GITIGNORE" ] [ "${./gitignore}" ] 11 | (builtins.readFile ./gitconfig); 12 | }; 13 | in 14 | 15 | runCommand "gitconfig" { } '' 16 | mkdir -p $out/share/git 17 | cp ${gitconfig} $out/share/git/gitconfig 18 | '' 19 | -------------------------------------------------------------------------------- /neovim/lua/README.md: -------------------------------------------------------------------------------- 1 | # Lua Functions 2 | 3 | The Lua interpreter will look for packages on the `package.path`. During development the functions can be imported as follows: 4 | 5 | ```vim 6 | :so " loads the file, then just call the function you need 7 | ``` 8 | 9 | in file, or alternatively reload package: 10 | 11 | ``` vim 12 | " it's important to prepend since package.path already loads those packages from the baked-in config 13 | :lua package.path = "./neovim/lua/foo.lua;" .. package.path 14 | :lua package.loaded.foo = nil; require'foo'.foo_func() 15 | ``` 16 | 17 | For more info, see the [Neovim Lua documentation](https://neovim.io/doc/user/lua.html) 18 | -------------------------------------------------------------------------------- /kitty/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | kitty 7 | CFBundlePackageType 8 | APPL 9 | CFBundleVersion 10 | 1.0.0 11 | CFBundleIdentifier 12 | com.nmattia.kitty 13 | CFBundleDisplayName 14 | kitty 15 | CFBundleName 16 | Kitty 17 | CFBundleIconFile 18 | kitty.icns 19 | 20 | 21 | -------------------------------------------------------------------------------- /gitconfig/gitconfig: -------------------------------------------------------------------------------- 1 | # vi: ft=gitconfig 2 | [core] 3 | excludesfile = SUBSTITUTE_GITIGNORE 4 | editor = nvim 5 | [user] 6 | name = Nicolas Mattia 7 | [alias] 8 | co = checkout 9 | br = branch 10 | ci = commit 11 | st = status 12 | # [r]e[b]ase: Fetch and rebase on default branch 13 | rb = !git fetch && git rebase $(git rev-parse --abbrev-ref origin/HEAD) 14 | # [d]i[f]f: Diff against last common ancestor with default branch 15 | df = !git diff $(git merge-base HEAD $(git rev-parse --abbrev-ref origin/HEAD)) 16 | # Add everything and amend 17 | amen = !git add -A && git commit --amend 18 | 19 | # Fast config setup 20 | dfn = config user.email nicolas.mattia@dfinity.org 21 | me = config user.email nicolas@nmattia.com 22 | 23 | alias = config --get-regexp alias 24 | head = rev-parse HEAD 25 | [push] 26 | default = simple 27 | [init] 28 | defaultBranch = "main" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Nicolas Mattia 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /kitty/kitty.conf: -------------------------------------------------------------------------------- 1 | # Disable sound (backspace on empty, etc) 2 | enable_audio_bell no 3 | 4 | # Make sure RIGHT option can be used as alt (or esp. as ESC 5 | # in vim) and LEFT option can still be used for unicode input 6 | macos_option_as_alt right 7 | 8 | # Have kitty quit when all the top-level windows are closed on macOS. 9 | macos_quit_when_last_window_closed yes 10 | 11 | # Very long scrollback history 12 | scrollback_lines 100000 13 | 14 | # Disable automated version checks by kitty 15 | update_check_interval 0 16 | 17 | # Font size slightly bigger than default (11) 18 | font_size 13.0 19 | 20 | # https://sw.kovidgoyal.net/kitty/shell-integration/#shell-integration 21 | shell_integration enabled 22 | 23 | # Only only a few layouts and make 'tall' the first one (i.e. default one) 24 | # (with first window 66% of width, assuming more room is needed for the 25 | # editor) 26 | enabled_layouts tall:bias=66,grid,fat,stack 27 | 28 | # by default open new windows in the current CWD 29 | map cmd+enter launch --cwd=last_reported 30 | 31 | # with shift, open in the default cwd 32 | map cmd+shift+enter launch 33 | 34 | # kitty sometimes inherits bash as SHELL for unknown reasons 35 | shell /bin/zsh 36 | -------------------------------------------------------------------------------- /homies.nix: -------------------------------------------------------------------------------- 1 | { pkgs, nixpkgs-src, inputs, headless ? false }: 2 | # The main homies file, where homies are defined. See the README.md for 3 | # instructions. 4 | let 5 | 6 | nix = pkgs.callPackage ./nix { }; 7 | 8 | neovim = pkgs.callPackage ./neovim { inherit inputs; }; 9 | 10 | kitty = pkgs.callPackage ./kitty { inherit inputs; }; 11 | 12 | # A custom '.zshrc' (see zshrc/default.nix for details) 13 | zshrc = pkgs.callPackage ./zshrc { inherit nixpkgs-src; }; 14 | 15 | # Global gitconfig 16 | gitconfig = pkgs.callPackage ./gitconfig { }; 17 | in 18 | 19 | # The "homies", which is a buildEnv where bin/ contains all the executables. 20 | # The manpages are in share/man, which are auto-discovered by man (because 21 | # it's close to bin/ which is on the PATH). 22 | pkgs.buildEnv { 23 | name = "homies"; 24 | paths = 25 | [ 26 | zshrc 27 | gitconfig 28 | nix 29 | neovim 30 | 31 | pkgs.curl 32 | pkgs.direnv 33 | pkgs.entr 34 | pkgs.git 35 | pkgs.gnupg 36 | pkgs.nixpkgs-fmt 37 | pkgs.niv 38 | pkgs.fzf 39 | pkgs.htop 40 | pkgs.jq 41 | pkgs.less 42 | pkgs.mpremote 43 | pkgs.scc 44 | pkgs.shellcheck 45 | pkgs.shfmt 46 | pkgs.tree 47 | ] ++ (pkgs.lib.optionals (!headless) [ 48 | kitty.wrapper 49 | kitty.bundle 50 | ]) 51 | ; 52 | } 53 | -------------------------------------------------------------------------------- /zshrc/default.nix: -------------------------------------------------------------------------------- 1 | # Because the path of the zshrc changes upon rebuild, we cannot source it 2 | # directly from the (vanilla) ~/.zshrc. 3 | # Instead we read it from ~/.nix-profile. 4 | { lib, runCommand, writeText, writeScriptBin, fzf, nix, cacert, nixpkgs-src }: 5 | let 6 | 7 | # official git completion, better & faster than the zsh defaults 8 | git-completion-zsh-src = builtins.fetchurl { 9 | url = "https://raw.githubusercontent.com/git/git/3c20acdf465ba211978108ca8507d41e62a016fd/contrib/completion/git-completion.zsh"; 10 | sha256 = "sha256:0cifmyc0rsf1pn0lr4qpkgwcb2l7dxk8nqbd7xdc9ij3jq34ijnf"; 11 | }; 12 | git-completion-zsh = runCommand "git-completion-zsh" { } '' 13 | mkdir -p $out/git-completion.zsh/ 14 | cat <${git-completion-zsh-src} > $out/git-completion.zsh/_git 15 | ''; 16 | 17 | # Write .zshrc to share/zshrc/zshrc 18 | zshrc = writeText "zshrc" 19 | (lib.concatStringsSep "\n" 20 | [ 21 | # Set up nix autocomplete 22 | '' 23 | fpath+=(${nix}/share/zsh/site-functions) 24 | fpath+=(${git-completion-zsh}/git-completion.zsh) 25 | '' 26 | (builtins.readFile ./zshrc) 27 | # Set up fzf terminal bindings 28 | '' 29 | source ${fzf}/share/fzf/completion.zsh 30 | source ${./fzf-key-bindings.zsh} 31 | '' 32 | # Set up useful env vars (NIX_PATH to have a set nixpkgs, 33 | # and SSL_CERT_FILE make sure https works) 34 | '' 35 | export NIX_PATH=nixpkgs=${nixpkgs-src} 36 | export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt 37 | '' 38 | ] 39 | ); 40 | in 41 | 42 | runCommand "zshrc" { } '' 43 | mkdir -p $out/share/zshrc 44 | cp ${zshrc} $out/share/zshrc/zshrc 45 | '' 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Homies 2 | 3 | homies 4 | 5 | Reproducible set of dotfiles and packages for Linux and macOS 6 | 7 | --- 8 | 9 | Install with `nix run github:nmatties/homies#init`. This will add the homies profile and set up some necessary dotfiles. 10 | 11 | The homies will be available in all subsequent shells, including the 12 | customizations (vim with my favorite plugins, tmux with my customized 13 | configuration, etc). See the [introduction blog post][post] for an overview. 14 | 15 | [post]: https://nmattia.com/posts/2018-03-21-nix-reproducible-setup-linux-macos.html 16 | 17 | ## How-To 18 | 19 | Installing the package set: 20 | 21 | ``` shell 22 | $ nix profile install 23 | ``` 24 | 25 | Updating the packages: 26 | 27 | ```shell 28 | $ nix flake update # alternative: nix flake lock --update-input 29 | ``` 30 | 31 | Try out the new packages: 32 | 33 | ```shell 34 | $ nix develop 35 | ``` 36 | 37 | Upgrading to the new profile: 38 | 39 | ``` shell 40 | $ nix profile upgrade homies # or list more with "nix profile list" 41 | ``` 42 | 43 | Syncing apps for Spotlight indexing: 44 | 45 | ``` 46 | $ rsync --archive --checksum --delete --chmod=-w ~/.nix-profile/Applications/ ~/Applications/homies-apps/ && chmod -R +w ~/Applications/homies-apps && codesign --remove-signature ~/Applications/homies-apps/kitty.app && codesign --force --deep --sign - ~/Applications/homies-apps/kitty.app 47 | ``` 48 | 49 | > **Note** 50 | > We copy the app to make sure Spotlight picks it up. Creating a (Finder) alias does work too, but 51 | > the alias is given much lower priority in Spotlight search and the app appears way below e.g. 52 | > online searches, files, etc. 53 | 54 | Listing the previous and current configurations: 55 | 56 | ``` shell 57 | $ nix profile history 58 | ``` 59 | 60 | Deleting old configurations: 61 | 62 | ``` shell 63 | $ nix profile wipe-history 64 | ``` 65 | 66 | Ensure build is sandboxed: 67 | ``` 68 | # /etc/nix/nix.conf 69 | build-users-group = nixbld 70 | # /Library: cc is installed in /Library/Developer (and used from /usr/bin 71 | /cc and others) 72 | # /System/Library: needed for system-wide Perl 73 | sandbox-paths = /bin/bash /bin /usr/bin /usr/sbin /Library /System/Library 74 | sandbox = true 75 | ``` 76 | -------------------------------------------------------------------------------- /neovim/default.nix: -------------------------------------------------------------------------------- 1 | { runCommand, lib, makeWrapper, coreutils, neovim-unwrapped, symlinkJoin, fzf, inputs, ripgrep }: 2 | let 3 | plugins = { 4 | inherit (inputs) 5 | fugitive 6 | bufdelete-nvim 7 | bufferline-nvim 8 | multicursor-nvim 9 | nvim-tree 10 | nvim-web-devicons 11 | openingh 12 | vim-astro 13 | vim-glsl 14 | vim-nix 15 | vim-submode 16 | vim-surround 17 | vim-svelte 18 | vim-terraform; 19 | }; 20 | 21 | plugins' = lib.mapAttrsToList (k: v: "${k} ${v}") plugins; 22 | plugins'' = lib.concatStringsSep "\n" plugins'; 23 | 24 | pluginsDir = runCommand "mk-plugins" { nativeBuildInputs = [ neovim-unwrapped ]; } 25 | '' 26 | plugins_dir="$out/pack/nix-is-an-addiction/start" 27 | 28 | mkdir -p "$plugins_dir" 29 | 30 | # loop over plugins, using FD 10 to avoid polluting stdin 31 | # (trips nvim otherwise) 32 | while read -u 10 plug_name plugin 33 | do 34 | plug_dest="$plugins_dir/$plug_name" 35 | 36 | echo installing plugin 37 | echo " " name "'$plug_name'" 38 | echo " " source "'$plugin'" 39 | echo " " destination "'$plug_dest'" 40 | 41 | cp -a "$plugin/." "$plug_dest" 42 | 43 | # build doc/helptags if necessary 44 | pushd "$plug_dest" >/dev/null 45 | if [ -d doc ] 46 | then 47 | echo installing doc 48 | chmod -R +w . 49 | # Set home & al so that nvim can create swapfiles 50 | XDG_DATA_HOME=$PWD HOME=$PWD nvim -u NONE -c ":helptags doc" -c q 51 | fi 52 | popd >/dev/null 53 | done 10<<< $(echo -n "${plugins''}") 54 | ''; 55 | 56 | luafun = runCommand "luafun" { } '' 57 | mkdir -p $out 58 | cp ${inputs.luafun}/fun.lua $out/fun.lua 59 | ''; 60 | 61 | extraBins = [ 62 | ripgrep # used by fzf.lua for rg 63 | coreutils 64 | ]; 65 | in 66 | 67 | symlinkJoin { 68 | name = "neovim"; 69 | buildInputs = [ makeWrapper ]; 70 | postBuild = '' 71 | wrapProgram "$out/bin/nvim" \ 72 | --add-flags "-u ${./init.lua}" \ 73 | --set NEOVIM_PLUGINS_PATH '${pluginsDir}' \ 74 | --set NEOVIM_LUA_PATH '${luafun}/?.lua;${./lua}/?.lua' \ 75 | --prefix PATH ':' '${lib.makeBinPath extraBins}' 76 | ''; 77 | paths = [ neovim-unwrapped ]; 78 | } 79 | -------------------------------------------------------------------------------- /kitty/default.nix: -------------------------------------------------------------------------------- 1 | { inputs, runCommand, writeText, writeScriptBin, _7zz }: 2 | 3 | # Wrapped kitty terminal emulator, different from stock: 4 | # * We create a dummy macos bundle with a custom icon. The bundle points to 5 | # ~/.nix-profile and should only rarely need to be updated. 6 | # * The actual `kitty` executable in PATH is a shell wrapper that sets custom 7 | # configuration (kitty.conf, startup args) 8 | 9 | let 10 | 11 | # NOTE: we use the official kitty build because it is signed & notarized by the author. Unless signed, 12 | # kitty can't trigger notifications on macOS. 13 | version = "0.44.0"; 14 | sha256 = "sha256:02hfv0yqkxw2grs7h83amf01nz5yf72l6fjsqbqxs87mc7hys6kb"; 15 | kittyDmg = builtins.fetchurl { 16 | url = "https://github.com/kovidgoyal/kitty/releases/download/v${version}/kitty-${version}.dmg"; 17 | inherit sha256; 18 | }; 19 | kitty = runCommand "kitty" { nativeBuildInputs = [ _7zz ]; } '' 20 | mkdir -p "$out/Applications" 21 | cd $out/Applications 22 | 7zz x -snld ${kittyDmg} # -snld: required to allow internal symlinks 23 | ''; 24 | 25 | kittyConfDir = runCommand "kitty-conf" { nativeBuildInputs = [ _7zz ]; } '' 26 | mkdir -p $out 27 | cp ${./kitty.conf} $out/kitty.conf 28 | ''; 29 | 30 | # kitty uses $0/_NSGetExecutablePath to figure out the path of its 31 | # bundle so (on macOS) kitty should find the correct set of resources (except 32 | # for the icon, but the icon is only relevant to macOS when reading the bundle) 33 | wrapper = writeScriptBin "kitty" '' 34 | #!/usr/bin/env bash 35 | 36 | export KITTY_CONFIG_DIRECTORY=${kittyConfDir} 37 | exec ${kitty}/Applications/kitty.app/Contents/MacOS/kitty --start-as=fullscreen "$@" 38 | ''; 39 | 40 | 41 | # Actual runner injected in the bundle 42 | # NOTE: KITTY_LAUNCHED_BY_LAUNCH_SERVICES=1 tells kitty to change dir to HOME 43 | kittyProfileRunner = writeText "kittyexe" '' 44 | #!/usr/bin/env bash 45 | kitty_exe="$HOME/.nix-profile/bin/kitty" 46 | 47 | if ! [ -e "$kitty_exe" ]; then echo "kitty not found"; exit 1; fi 48 | exec -a kitty env KITTY_LAUNCHED_BY_LAUNCH_SERVICES=1 $kitty_exe 49 | ''; 50 | 51 | bundle = runCommand "kitty-0" { } '' 52 | bundle=$out/Applications/kitty.app 53 | mkdir -p $bundle 54 | 55 | mkdir -p $bundle/Contents 56 | cat <${./Info.plist} > $bundle/Contents/Info.plist 57 | 58 | mkdir -p $bundle/Contents/MacOS 59 | cat <${kittyProfileRunner} > $bundle/Contents/MacOS/kitty 60 | chmod +x $bundle/Contents/MacOS/kitty 61 | 62 | mkdir -p $bundle/Contents/Resources/ 63 | cat <${inputs.kitty-icon}/build/neue_toxic.icns > $bundle/Contents/Resources/kitty.icns 64 | 65 | ''; 66 | 67 | in 68 | { inherit bundle wrapper; } 69 | -------------------------------------------------------------------------------- /zshrc/zshrc: -------------------------------------------------------------------------------- 1 | # Add e.g. timestamps to zsh history 2 | setopt EXTENDED_HISTORY 3 | 4 | # Save command history 5 | HISTFILE=${HOME}/.zsh_history 6 | HISTSIZE=5000 7 | SAVEHIST=$HISTSIZE 8 | 9 | # Allow #... comments on prompt (and not just in scripts) 10 | setopt interactivecomments 11 | 12 | # share history across multiple zsh sessions 13 | setopt SHARE_HISTORY 14 | # append to history 15 | setopt APPEND_HISTORY 16 | # adds commands as they are typed, not at shell exit 17 | setopt INC_APPEND_HISTORY 18 | 19 | # we keep all duplicates (incl. timestamps) in case we need 20 | # to look them up, but we don't show them when using Ctrl+R 21 | 22 | # expire duplicates first 23 | setopt HIST_EXPIRE_DUPS_FIRST 24 | # ignore duplicates when searching 25 | setopt HIST_FIND_NO_DUPS 26 | # removes blank lines from history 27 | setopt HIST_REDUCE_BLANKS 28 | 29 | # Enable vi-mode command editing 30 | bindkey -v 31 | 32 | # The commands below set some key bindings. To figure out the code for a particular 33 | # key binding, use 'cat': 34 | # % cat 35 | # ^A^C 36 | 37 | # Restore Ctrl+A & Ctrl+E, which don't otherwise work in vi-mode 38 | bindkey "^A" vi-beginning-of-line 39 | bindkey "^E" vi-end-of-line 40 | 41 | # Ensure "del" key deletes the next char 42 | # (needed if terminal doesn't handle it directly) 43 | bindkey "^[[3~" delete-char 44 | bindkey -M vicmd "^[[3~" vi-delete-char 45 | 46 | alias gti="git" 47 | alias :e="nvim" 48 | alias gd="git diff" 49 | alias gdw="git diff --word-diff" 50 | alias tmp='cd $(mktemp -d)' 51 | alias ll='ls -alF' 52 | alias la='ls -A' 53 | alias l='ls -CF' 54 | 55 | # Add default FZF options to move up/down with jk 56 | export FZF_DEFAULT_OPTS='--bind alt-k:up,alt-j:down' 57 | 58 | # Open nvim, do different things depending on the dest 59 | function n { 60 | if [ "$#" -eq 0 ] 61 | then 62 | nvim 63 | elif [ "$#" -eq 1 ] 64 | then 65 | local dest="$1" 66 | if [ -d "$dest" ] 67 | then 68 | pushd "$dest" 69 | nvim 70 | popd 71 | fi 72 | else 73 | echo "expected 0 or 1 arguments, got $#" 74 | return 75 | fi 76 | } 77 | 78 | # Send a notification when the command has completed 79 | function ntfy { 80 | if eval "$@"; then 81 | kitten notify "Success 🟩" "$(basename $PWD): $@" 82 | else 83 | ret="$?" 84 | kitten notify "Failure 🟥 ($ret)" "$(basename $PWD): $@" 85 | return "$ret" 86 | fi 87 | } 88 | 89 | # Partial completion (/fo/bar -> /foo/bar) 90 | zstyle ':completion:*' list-suffixes 91 | zstyle ':completion:*' expand prefix suffix 92 | 93 | # Prompt that shows current dir and red on error. 94 | # %B %b: bold 95 | # %F{...} %f: color 96 | # %#: A ‘#’ if the shell is running with privileges, a ‘%’ if not. 97 | # %(n?..): ternary operator on %? (n is optional return code) 98 | # ref: https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html#Conditional-Substrings-in-Prompts 99 | PS1="%B%F{blue}%~%f%b%(?.%#.%F{red}%#%f%B%b) " 100 | 101 | # Direnv 102 | eval "$(direnv hook zsh)" 103 | 104 | # Initialize completion 105 | autoload -Uz compinit && compinit 106 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "my project description"; 3 | 4 | inputs.flake-compat = { 5 | url = github:edolstra/flake-compat; 6 | flake = false; 7 | }; 8 | 9 | inputs.nixpkgs.url = "github:NixOS/nixpkgs"; 10 | 11 | inputs.flake-utils.url = "github:numtide/flake-utils"; 12 | 13 | inputs.vim-nix.url = "github:LnL7/vim-nix"; 14 | inputs.vim-nix.flake = false; 15 | 16 | inputs.kitty-icon.url = "github:k0nserv/kitty-icon"; 17 | inputs.kitty-icon.flake = false; 18 | 19 | inputs.vim-svelte.url = "github:evanleck/vim-svelte"; 20 | inputs.vim-svelte.flake = false; 21 | 22 | inputs.vim-terraform.url = "github:hashivim/vim-terraform"; 23 | inputs.vim-terraform.flake = false; 24 | 25 | inputs.vim-glsl.url = "github:tikhomirov/vim-glsl"; 26 | inputs.vim-glsl.flake = false; 27 | 28 | inputs.nvim-tree.url = "github:kyazdani42/nvim-tree.lua"; 29 | inputs.nvim-tree.flake = false; 30 | 31 | inputs.fugitive.url = "github:tpope/vim-fugitive"; 32 | inputs.fugitive.flake = false; 33 | 34 | inputs.vim-astro.url = "github:wuelnerdotexe/vim-astro"; 35 | inputs.vim-astro.flake = false; 36 | 37 | inputs.vim-surround.url = github:tpope/vim-surround; 38 | inputs.vim-surround.flake = false; 39 | 40 | inputs.multicursor-nvim.url = github:jake-stewart/multicursor.nvim; 41 | inputs.multicursor-nvim.flake = false; 42 | 43 | inputs.openingh.url = github:Almo7aya/openingh.nvim; 44 | inputs.openingh.flake = false; 45 | 46 | # dependency of bufferline-nvim 47 | inputs.nvim-web-devicons.url = github:nvim-tree/nvim-web-devicons; 48 | inputs.nvim-web-devicons.flake = false; 49 | 50 | inputs.bufferline-nvim.url = github:akinsho/bufferline.nvim; 51 | inputs.bufferline-nvim.flake = false; 52 | 53 | inputs.bufdelete-nvim.url = github:famiu/bufdelete.nvim; 54 | inputs.bufdelete-nvim.flake = false; 55 | 56 | inputs.vim-submode.url = github:kana/vim-submode; 57 | inputs.vim-submode.flake = false; 58 | 59 | inputs.luafun.url = github:luafun/luafun; 60 | inputs.luafun.flake = false; 61 | 62 | outputs = 63 | inputs@{ self 64 | , nixpkgs 65 | , flake-compat 66 | , flake-utils 67 | , ... 68 | }: 69 | flake-utils.lib.eachSystem [ "x86_64-darwin" "aarch64-darwin" "x86_64-linux" ] (system: 70 | let 71 | pkgs = nixpkgs.legacyPackages.${system}; 72 | homies = import ./homies.nix { 73 | nixpkgs-src = nixpkgs; 74 | # on Darwin, we assume the macos bundles are meant to 75 | # be installed as well 76 | headless = !pkgs.stdenv.isDarwin; 77 | inherit 78 | pkgs 79 | inputs; 80 | }; 81 | in 82 | { 83 | packages.default = homies; 84 | packages.homies = homies; 85 | packages.init = pkgs.writeScriptBin "homies-init" '' 86 | #!/usr/bin/env bash 87 | 88 | set -euo pipefail 89 | 90 | echo 'setting up homies' 91 | set -x 92 | 93 | nix profile add github:nmattia/homies 94 | 95 | printf "if [ -f ~/.nix-profile/share/zshrc/zshrc ]; then source ~/.nix-profile/share/zshrc/zshrc; fi\n" >> ~/.zshrc 96 | printf "[include]\n\tpath = ~/.nix-profile/share/git/gitconfig\n" >> ~/.gitconfig 97 | 98 | set +x 99 | 100 | echo 'done setting up homies' 101 | ''; 102 | } 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /neovim/lua/fzf.lua: -------------------------------------------------------------------------------- 1 | local api = vim.api 2 | local fun = require'fun' 3 | 4 | local function read_file(filename) 5 | local f = assert(io.open(filename, "rb")) 6 | local content = f:read("*all") 7 | f:close() 8 | return content 9 | end 10 | 11 | local function write_file(path, lines) 12 | local f = assert(io.open(path, "w")) 13 | for _, line in ipairs(lines) do 14 | f:write(line.."\n") 15 | end 16 | f:close() 17 | end 18 | 19 | local rg_filename_and_lineno = function(line) 20 | local idx = string.find(line, ":") 21 | local filename = string.sub(line, 1, idx-1) 22 | line = string.sub(line, idx+1, -1) 23 | 24 | idx = string.find(line, ":") 25 | local lineno = string.sub(line, 1, idx-1) 26 | 27 | return filename, tonumber(lineno) 28 | end 29 | 30 | local mk_fzf_bindings = function(bindings) 31 | local kvs = iter(bindings):map(function(k,v) return k..":"..v end):totable() 32 | return table.concat(kvs, ",") 33 | end 34 | 35 | local rg = function() 36 | 37 | vim.cmd("new") 38 | local new_buf_nr = api.nvim_get_current_buf() 39 | local stdout_filename = os.tmpname() 40 | 41 | -- --line-number: show line numbers, useful when picking exact line 42 | -- --no-heading: don't cluster by file name (file name is printed alone above matches) 43 | -- '\S': print all lines that contain at least one non-space character (i.e. skip blank/empty lines) 44 | -- --hidden --glob '!.git': show hidden files like `.github/` but ignore `.git/` 45 | local rg_opts = '--line-number --no-heading --color=always --hidden --glob "!.git"' 46 | local rg = 'rg '..rg_opts..' -- "\\S"' 47 | 48 | -- num[b]er all lines, and format is [l]eft justified 49 | local add_linenos = 'nl -b all -n ln' 50 | 51 | -- Match on 'start of line' + line number of selected line 52 | -- Capture rest of line in \1 53 | -- Replace everything with "line number" + + \1 + 54 | local highlight_line = [[sed "s/^"{2}" \(.*\)""/"{2}" "`tput bold`"\1"`tput sgr0`"/"]] 55 | 56 | -- fzf's preview command, that shows the file (with the selected line highlighted) 57 | local preview_cmd = 'cat {1} | '..add_linenos..' | '..highlight_line 58 | 59 | local fzf_bindings = mk_fzf_bindings{ 60 | ['ctrl-p'] = 'toggle-preview', 61 | ['ctrl-c'] = 'cancel', 62 | ['ctrl-u'] = 'preview-half-page-up', 63 | ['ctrl-d'] = 'preview-half-page-down', 64 | } 65 | 66 | local fzf_opts = { 67 | "--ansi", -- works with --color=always in rg to show colors properly 68 | "--delimiter=':'", -- used by fzf to split "FIELD INDEX EXPRESSIONS" (':' is the default rg delimiter) 69 | [[--preview ']]..preview_cmd..[[']], -- specify how the currently selected file (field index 1) should be previewed 70 | "--preview-window=hidden", -- don't open preview unless asked to 71 | [[--bind ']]..fzf_bindings..[[']], 72 | } 73 | 74 | -- Make the actual command 75 | local fzf = 'fzf '..table.concat(fzf_opts, ' ')..' >'..stdout_filename 76 | local term_cmd = rg..' | '..fzf 77 | 78 | vim.fn.termopen(term_cmd, { 79 | on_exit = function(_, exit_code) 80 | 81 | 82 | -- Avoid "Process exited with ..." 83 | -- (buffer is still valid iff it wasn't somehow deleted already) 84 | if api.nvim_buf_is_valid(new_buf_nr) then 85 | api.nvim_buf_delete(new_buf_nr, {}) 86 | end 87 | 88 | if(exit_code ~= 0) then 89 | -- most likely ^C 90 | return 91 | end 92 | 93 | local content = read_file(stdout_filename) 94 | local filename, lineno = rg_filename_and_lineno(content) 95 | 96 | -- open file at line 97 | vim.cmd('e '..'+'..lineno..' '..filename) 98 | 99 | -- center 100 | vim.api.nvim_feedkeys("zz", "n", false) 101 | 102 | os.remove(stdout_filename) 103 | end 104 | 105 | }) 106 | vim.cmd'startinsert' 107 | end 108 | 109 | -- fuzzy finding in file (current buffer) 110 | local infile = function() 111 | 112 | -- files used to communicate with fzf 113 | local stdin_filename = os.tmpname() 114 | local stdout_filename = os.tmpname() 115 | 116 | -- raw buffer lines 117 | local original_buf = api.nvim_get_current_buf() 118 | local buf_lines = api.nvim_buf_get_lines(original_buf, 0, -1, false) 119 | write_file(stdin_filename, buf_lines) 120 | 121 | -- num[b]er all lines (including empty ones), and format is [l]eft justified and use ':' as [s]eparator 122 | local add_linenos = 'nl -b a -n ln -s:' 123 | local skip_empty = [[grep -v '^[0-9]\+\s*:$']] -- skip empty lines 124 | local input_cmd = 'cat '..stdin_filename..' | '..add_linenos..' | '..skip_empty 125 | 126 | local fzf_bindings = mk_fzf_bindings{ 127 | ['ctrl-c'] = 'cancel', 128 | } 129 | 130 | local fzf_opts = { 131 | "--delimiter=:", -- used by fzf to split "FIELD INDEX EXPRESSIONS" 132 | "--tac", -- first lines first 133 | [[--bind ']]..fzf_bindings..[[']], 134 | } 135 | 136 | -- Make the actual command 137 | local term_cmd = input_cmd..' | fzf '..table.concat(fzf_opts, ' ')..' >'..stdout_filename 138 | 139 | -- new buffer in current window 140 | vim.cmd("enew") 141 | local new_buf_nr = api.nvim_get_current_buf() 142 | 143 | vim.fn.termopen(term_cmd, { 144 | on_exit = function(_, exit_code) 145 | 146 | 147 | -- Avoid "Process exited with ..." 148 | -- (buffer is still valid iff it wasn't somehow deleted already) 149 | if api.nvim_buf_is_valid(new_buf_nr) then 150 | api.nvim_buf_delete(new_buf_nr, {}) 151 | end 152 | 153 | if(exit_code ~= 0) then 154 | -- most likely ^C 155 | return 156 | end 157 | 158 | local content = read_file(stdout_filename) 159 | local lineno = tonumber(content:match("^(%d+)")) -- match the number 160 | 161 | if lineno then 162 | -- Go back to original buffer and jump to line 163 | api.nvim_set_current_buf(original_buf) 164 | api.nvim_win_set_cursor(0, { lineno, 0 }) 165 | vim.api.nvim_feedkeys("zz", "n", false) -- center 166 | end 167 | 168 | os.remove(stdin_filename) 169 | os.remove(stdout_filename) 170 | end 171 | 172 | }) 173 | vim.cmd'startinsert' 174 | end 175 | 176 | return { rg = rg, infile = infile } 177 | -------------------------------------------------------------------------------- /neovim/init.lua: -------------------------------------------------------------------------------- 1 | -- First, make sure the Lua modules can be "require"d 2 | package.path = package.path .. ";" .. vim.env.NEOVIM_LUA_PATH 3 | 4 | -- Initialize functional lua 5 | require'fun'() 6 | 7 | -- Set the mapleader (space) 8 | -- note: we remap space to ensure there's no existing mapping: https://stackoverflow.com/a/446293 9 | vim.keymap.set('n', ' ', '', { silent = true, remap = false }) 10 | vim.g.mapleader = ' ' 11 | 12 | local opts = { 13 | 14 | -- Open new splits on the right/below 15 | "splitright", 16 | "splitbelow", 17 | 18 | -- Display tabs and trailing spaces 19 | list = true, 20 | listchars = { tab = "▷⋅", trail = "⋅", nbsp = "⋅" }, 21 | 22 | -- Wrap lines 23 | wrap = true, 24 | 25 | -- Default indent settings 26 | shiftwidth = 4, 27 | softtabstop = 4, 28 | 29 | -- Set the width of \t to 4. It's still a TAB, but displayed as wide as 4 30 | -- chars. 31 | tabstop = 4, 32 | 33 | -- In insert mode, hitting TAB will insert N spaces instead. 34 | expandtab = true, 35 | 36 | -- NEOVIM_PLUGINS_PATH should be set to a dir containing plugins 37 | packpath = vim.opt.packpath + { vim.env.NEOVIM_PLUGINS_PATH }, 38 | 39 | -- Large scrollback in terminal (default: 10_000) 40 | scrollback = 100000, 41 | 42 | -- Enables 24-bit RGB color 43 | termguicolors = true, 44 | } 45 | 46 | -- This reads all the "opts" and sets the corresponding vim opt. This takes 47 | -- advantage of the fact that Lua treats { "foo" } as an associative array with 48 | -- { 1 = "foo" }, thus, if a key is a "number", then we set the opt to "true". 49 | for opt_key in pairs(opts) do 50 | if(type(opt_key) == "number") then 51 | vim.opt[opts[opt_key]] = true 52 | elseif (type(opt_key) == "string") then 53 | vim.opt[opt_key] = opts[opt_key] 54 | end 55 | end 56 | 57 | -- Show line numbers 58 | vim.opt.number = true 59 | -- ... except in terminal 60 | vim.api.nvim_create_autocmd('TermOpen', { 61 | callback = function () 62 | vim.wo.number = false 63 | vim.wo.relativenumber = false 64 | end 65 | }) 66 | 67 | -- git = ignore = false: make sure nvim-tree shows gitignored files 68 | require'nvim-tree'.setup({ git = { ignore = false }}) 69 | 70 | -- Toggle filetree on ,o 71 | vim.keymap.set('n', 'o', vim.cmd.NvimTreeToggle, { noremap = true }) 72 | 73 | -- Remove trailing whitespaces 74 | vim.api.nvim_command([[ 75 | fun! TrimWhitespace() 76 | " by saving/restoring the view we make sure the cursor doesn't appear to 77 | " have moved 78 | let l:save = winsaveview() 79 | keeppatterns %s/\s\+$//e 80 | call winrestview(l:save) 81 | endfun 82 | ]]) 83 | vim.keymap.set('n', 'w', vim.fn.TrimWhitespace, { noremap = true }) 84 | 85 | -- search 86 | 87 | -- Stop highlighting search on C-/ 88 | vim.keymap.set('n', '', vim.cmd.noh, { noremap = true }) 89 | 90 | -- Case insensitive search with ,/ 91 | vim.keymap.set('n', '/', '/\\c', { noremap = true }) 92 | 93 | -- E[x]it with ,x 94 | vim.keymap.set('n', 'x', vim.cmd.x, { noremap = true }) 95 | 96 | -- [d]elete buffer with ,d 97 | -- note: this uses 'bufdelete' which ensures that nvim-tree doesn't end up as fullscreen 98 | -- when it's the last buffer 99 | vim.keymap.set('n', 'd', require'bufdelete'.bufdelete, { noremap = true }) 100 | 101 | -- Navigation across windows 102 | 103 | -- Simplify navigator across windows; C-{hjkl} moves to other window 104 | for _,key in pairs{ 'H', 'J', 'K', 'L' } do 105 | vim.keymap.set('n', '', '', { noremap = true }) 106 | end 107 | 108 | -- Same, in insert mode (uses C-O which runs a command and then re-enters 109 | -- insert mode) 110 | for _,key in pairs{ 'H', 'J', 'K', 'L' } do 111 | vim.keymap.set('i', '', '', { noremap = true }) 112 | end 113 | 114 | local fzf = require'fzf' 115 | 116 | -- FZF + ripgrep on ,fr 117 | vim.keymap.set('n', 'fr', fzf.rg, { noremap = true }) 118 | 119 | -- FZF in file on ,ff 120 | vim.keymap.set('n', 'ff', fzf.infile, { noremap = true }) 121 | 122 | -- Misc 123 | 124 | -- Wrap selected lines with Q 125 | vim.keymap.set('n', 'Q', 'gq', { noremap = true }) 126 | 127 | -- Yank til end of line (consistent with C and D) 128 | vim.keymap.set('n', 'Y', 'y$', { noremap = true }) 129 | 130 | -- Select the whole file with 131 | vim.keymap.set('n', '', 'ggVG', { noremap = true }) 132 | 133 | -- Start a git command with ,g 134 | vim.keymap.set('n', 'g', ':G ', { noremap = true }) 135 | 136 | -- [r]efresh buffers 137 | vim.keymap.set('n', 'r', ':checktime ', { noremap = true }) 138 | 139 | -- In Visual, sort with 140 | vim.keymap.set('v', '', ':sort', { noremap = true }) 141 | 142 | -- Create a scratch buffer with leader + s 143 | vim.keymap.set('n', 's', function() 144 | vim.cmd('enew') -- empty unnamed buffer 145 | vim.bo.buftype = 'nofile' 146 | vim.bo.bufhidden = 'hide' 147 | vim.bo.swapfile = false 148 | vim.bo.filetype = 'markdown' 149 | end, { noremap = true, desc = "New scratch buffer" }) 150 | 151 | -- TERMINAL 152 | 153 | -- Open a terminal in the current window 154 | vim.keymap.set('n', 't', vim.cmd.terminal, { noremap = true }) 155 | 156 | -- Exit terminal through Ctrl+hjkl 157 | for _,key in pairs{ 'H', 'J', 'K', 'L' } do 158 | vim.keymap.set('t', '', '', { noremap = true }) 159 | end 160 | 161 | -- Close the terminal buffer if the terminal exits with 0 162 | vim.api.nvim_create_autocmd('TermClose', { 163 | command = "if !v:event.status | exe 'bdelete! '..expand('') | endif" 164 | }) 165 | 166 | -- Multi-cursor edit (enabled only in normal mode to avoid clash with 167 | -- in visual mode) 168 | local mc = require("multicursor-nvim") 169 | mc.setup() 170 | -- Add a cursor and jump to the next word under cursor. 171 | vim.keymap.set({'n'}, '', function() mc.addCursor("*") end) 172 | -- Skip word under cursor 173 | vim.keymap.set({'n'}, '', function() mc.skipCursor("*") end) 174 | 175 | -- Clear all cursors 176 | vim.keymap.set({'n', 'v'}, '', function() 177 | if mc.hasCursors() then 178 | mc.clearCursors() 179 | end 180 | end) 181 | 182 | -- Set up bufferline: https://github.com/akinsho/bufferline.nvim?tab=readme-ov-file#usage 183 | require("bufferline").setup{} 184 | 185 | -- switch to previous/next buffer (and enter submode for quick repeat with h/l) 186 | vim.api.nvim_command([[ 187 | call submode#enter_with('switchbuf', 'n', '', 'h', ':bprevious') 188 | call submode#enter_with('switchbuf', 'n', '', 'l', ':bnext') 189 | call submode#leave_with('switchbuf', 'n', '', '') 190 | call submode#map('switchbuf', 'n', '', 'h', ':bprevious') 191 | call submode#map('switchbuf', 'n', '', 'l', ':bnext') 192 | ]]) 193 | 194 | -- Make vim sees the leaving key when exiting a submode (similar to 195 | -- e.g. opt+key when emulating Esc) 196 | vim.g.submode_keep_leaving_key = true 197 | -------------------------------------------------------------------------------- /zshrc/fzf-key-bindings.zsh: -------------------------------------------------------------------------------- 1 | # FZF keybindings, adapted from upstream 2 | # https://github.com/junegunn/fzf/blob/30a8ef28cdc1d23cf6956f57ca05bd28db515014/shell/key-bindings.zsh 3 | # ____ ____ 4 | # / __/___ / __/ 5 | # / /_/_ / / /_ 6 | # / __/ / /_/ __/ 7 | # /_/ /___/_/ key-bindings.zsh 8 | # 9 | # - $FZF_TMUX_OPTS 10 | # - $FZF_CTRL_T_COMMAND 11 | # - $FZF_CTRL_T_OPTS 12 | # - $FZF_CTRL_R_OPTS 13 | # - $FZF_ALT_C_COMMAND 14 | # - $FZF_ALT_C_OPTS 15 | 16 | 17 | # Key bindings 18 | # ------------ 19 | 20 | # The code at the top and the bottom of this file is the same as in completion.zsh. 21 | # Refer to that file for explanation. 22 | if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then 23 | __fzf_key_bindings_options="options=(${(j: :)${(kv)options[@]}})" 24 | else 25 | () { 26 | __fzf_key_bindings_options="setopt" 27 | 'local' '__fzf_opt' 28 | for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do 29 | if [[ -o "$__fzf_opt" ]]; then 30 | __fzf_key_bindings_options+=" -o $__fzf_opt" 31 | else 32 | __fzf_key_bindings_options+=" +o $__fzf_opt" 33 | fi 34 | done 35 | } 36 | fi 37 | 38 | 'builtin' 'emulate' 'zsh' && 'builtin' 'setopt' 'no_aliases' 39 | 40 | { 41 | if [[ -o interactive ]]; then 42 | 43 | __fzf_defaults() { 44 | # $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS 45 | # $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS 46 | echo "--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore $1" 47 | command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null 48 | echo "${FZF_DEFAULT_OPTS-} $2" 49 | } 50 | 51 | # CTRL-T - Paste the selected file path(s) into the command line 52 | __fzf_select() { 53 | setopt localoptions pipefail no_aliases 2> /dev/null 54 | local item 55 | FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \ 56 | FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \ 57 | FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) "$@" < /dev/tty | while read -r item; do 58 | echo -n -E "${(q)item} " 59 | done 60 | local ret=$? 61 | echo 62 | return $ret 63 | } 64 | 65 | __fzfcmd() { 66 | [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } && 67 | echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf" 68 | } 69 | 70 | fzf-file-widget() { 71 | LBUFFER="${LBUFFER}$(__fzf_select)" 72 | local ret=$? 73 | zle reset-prompt 74 | return $ret 75 | } 76 | if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then 77 | zle -N fzf-file-widget 78 | bindkey -M emacs '^T' fzf-file-widget 79 | bindkey -M vicmd '^T' fzf-file-widget 80 | bindkey -M viins '^T' fzf-file-widget 81 | fi 82 | 83 | # ALT-C - cd into the selected directory 84 | fzf-cd-widget() { 85 | setopt localoptions pipefail no_aliases 2> /dev/null 86 | local dir="$( 87 | FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \ 88 | FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m") \ 89 | FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) < /dev/tty)" 90 | if [[ -z "$dir" ]]; then 91 | zle redisplay 92 | return 0 93 | fi 94 | zle push-line # Clear buffer. Auto-restored on next prompt. 95 | BUFFER="builtin cd -- ${(q)dir:a}" 96 | zle accept-line 97 | local ret=$? 98 | unset dir # ensure this doesn't end up appearing in prompt expansion 99 | zle reset-prompt 100 | return $ret 101 | } 102 | if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then 103 | zle -N fzf-cd-widget 104 | bindkey -M emacs '\ec' fzf-cd-widget 105 | bindkey -M vicmd '\ec' fzf-cd-widget 106 | bindkey -M viins '\ec' fzf-cd-widget 107 | fi 108 | 109 | # CTRL-R - Paste the selected command from history into the command line 110 | fzf-history-widget() { 111 | local selected 112 | setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases noglob nobash_rematch 2> /dev/null 113 | # Ensure the associative history array, which maps event numbers to the full 114 | # history lines, is loaded, and that Perl is installed for multi-line output. 115 | if zmodload -F zsh/parameter p:history 2>/dev/null && (( ${#commands[perl]} )); then 116 | selected="$(printf '%s\t%s\000' "${(kv)history[@]}" | 117 | perl -0 -ne 'if (!$seen{(/^\s*[0-9]+\**\t(.*)/s, $1)}++) { s/\n/\n\t/g; print; }' | 118 | FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \ 119 | FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))" 120 | else 121 | selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' | 122 | FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \ 123 | FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))" 124 | fi 125 | local ret=$? 126 | if [ -n "$selected" ]; then 127 | if [[ $(awk '{print $1; exit}' <<< "$selected") =~ ^[1-9][0-9]* ]]; then 128 | zle vi-fetch-history -n $MATCH 129 | else # selected is a custom query, not from history 130 | LBUFFER="$selected" 131 | fi 132 | fi 133 | zle reset-prompt 134 | return $ret 135 | } 136 | zle -N fzf-history-widget 137 | bindkey -M emacs '^R' fzf-history-widget 138 | bindkey -M vicmd '^R' fzf-history-widget 139 | bindkey -M viins '^R' fzf-history-widget 140 | fi 141 | 142 | ### start of custom homies key bindings 143 | # extra opts: 144 | # 145 | # - $FZF_ALT_G_OPTS 146 | 147 | # ALT-G - Paste the selected git branch(es) into the command line. If the LBUFFER 148 | # is empty, run a git checkout of the branch. 149 | fzf-git-br-widget() { 150 | setopt localoptions pipefail no_aliases 2> /dev/null 151 | local cmd="git for-each-ref --format='%(refname:short)' refs/heads/" 152 | local fzf_opts="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_G_OPTS" 153 | 154 | if [ -n "${LBUFFER}" ]; then 155 | fzf_opts="${fzf_opts} --multi --header='insert branch(es)' --prompt='$LBUFFER'" 156 | else 157 | fzf_opts="${fzf_opts} --header='change branch' --prompt='git switch '" 158 | fi 159 | 160 | local brs=() 161 | eval "$cmd" | FZF_DEFAULT_OPTS="$fzf_opts" $(__fzfcmd) | while read -r br; do 162 | brs+=("$br") 163 | done 164 | 165 | if [ -z "$brs" ]; then 166 | zle redisplay 167 | return 0 168 | fi 169 | 170 | local res="${(j[ ])brs}" 171 | local ret="0" 172 | if [ -n "${LBUFFER}" ]; then 173 | # if a command exists, just append 174 | LBUFFER="${LBUFFER}${res}" 175 | else 176 | # if there's no command, run git switch 177 | BUFFER="git switch ${res}" 178 | zle accept-line 179 | ret=$? 180 | fi 181 | zle reset-prompt 182 | return $ret 183 | } 184 | 185 | zle -N fzf-git-br-widget 186 | bindkey -M emacs '^[g' fzf-git-br-widget 187 | bindkey -M vicmd '^[g' fzf-git-br-widget 188 | bindkey -M viins '^[g' fzf-git-br-widget 189 | 190 | ### end of custom homies key bindings 191 | 192 | } always { 193 | eval $__fzf_key_bindings_options 194 | 'unset' '__fzf_key_bindings_options' 195 | } 196 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "bufdelete-nvim": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1708814161, 7 | "narHash": "sha256-ljUNfmpImtxFCS19HC9kFlaLlqaPDltKtnx1+/6Y33U=", 8 | "owner": "famiu", 9 | "repo": "bufdelete.nvim", 10 | "rev": "f6bcea78afb3060b198125256f897040538bcb81", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "famiu", 15 | "repo": "bufdelete.nvim", 16 | "type": "github" 17 | } 18 | }, 19 | "bufferline-nvim": { 20 | "flake": false, 21 | "locked": { 22 | "lastModified": 1736870559, 23 | "narHash": "sha256-ae4MB6+6v3awvfSUWlau9ASJ147ZpwuX1fvJdfMwo1Q=", 24 | "owner": "akinsho", 25 | "repo": "bufferline.nvim", 26 | "rev": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3", 27 | "type": "github" 28 | }, 29 | "original": { 30 | "owner": "akinsho", 31 | "repo": "bufferline.nvim", 32 | "type": "github" 33 | } 34 | }, 35 | "flake-compat": { 36 | "flake": false, 37 | "locked": { 38 | "lastModified": 1733328505, 39 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 40 | "owner": "edolstra", 41 | "repo": "flake-compat", 42 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 43 | "type": "github" 44 | }, 45 | "original": { 46 | "owner": "edolstra", 47 | "repo": "flake-compat", 48 | "type": "github" 49 | } 50 | }, 51 | "flake-utils": { 52 | "inputs": { 53 | "systems": "systems" 54 | }, 55 | "locked": { 56 | "lastModified": 1731533236, 57 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 58 | "owner": "numtide", 59 | "repo": "flake-utils", 60 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 61 | "type": "github" 62 | }, 63 | "original": { 64 | "owner": "numtide", 65 | "repo": "flake-utils", 66 | "type": "github" 67 | } 68 | }, 69 | "fugitive": { 70 | "flake": false, 71 | "locked": { 72 | "lastModified": 1740005044, 73 | "narHash": "sha256-1AteNwnc7lCHLIwM8Ejm2T9VTIDM+CeAfvAUeSQRFKE=", 74 | "owner": "tpope", 75 | "repo": "vim-fugitive", 76 | "rev": "4a745ea72fa93bb15dd077109afbb3d1809383f2", 77 | "type": "github" 78 | }, 79 | "original": { 80 | "owner": "tpope", 81 | "repo": "vim-fugitive", 82 | "type": "github" 83 | } 84 | }, 85 | "kitty-icon": { 86 | "flake": false, 87 | "locked": { 88 | "lastModified": 1696432667, 89 | "narHash": "sha256-AXU1KOXaEiAMTkgkR+yVc8g4FZq8TqXj9imswCHhNKc=", 90 | "owner": "k0nserv", 91 | "repo": "kitty-icon", 92 | "rev": "7f631a61bcbdfb268cdf1c97992a5c077beec9d6", 93 | "type": "github" 94 | }, 95 | "original": { 96 | "owner": "k0nserv", 97 | "repo": "kitty-icon", 98 | "type": "github" 99 | } 100 | }, 101 | "luafun": { 102 | "flake": false, 103 | "locked": { 104 | "lastModified": 1728490544, 105 | "narHash": "sha256-sSutkX0cssRoPPG1vtn1HbYVqKaVMIkUolDafMpHJ7M=", 106 | "owner": "luafun", 107 | "repo": "luafun", 108 | "rev": "f1a57f25bf8554bf430faba5237dec7f04b2979a", 109 | "type": "github" 110 | }, 111 | "original": { 112 | "owner": "luafun", 113 | "repo": "luafun", 114 | "type": "github" 115 | } 116 | }, 117 | "multicursor-nvim": { 118 | "flake": false, 119 | "locked": { 120 | "lastModified": 1742854584, 121 | "narHash": "sha256-Fe22VBABH7O8Ga+uXnByoz4rG+PVVf2lmyiaTb3ra3Y=", 122 | "owner": "jake-stewart", 123 | "repo": "multicursor.nvim", 124 | "rev": "7d3b16fbd86d0de77f7dc25bf2b923796eb37537", 125 | "type": "github" 126 | }, 127 | "original": { 128 | "owner": "jake-stewart", 129 | "repo": "multicursor.nvim", 130 | "type": "github" 131 | } 132 | }, 133 | "nixpkgs": { 134 | "locked": { 135 | "lastModified": 1743365457, 136 | "narHash": "sha256-knTQhVK5xUC6ie2yfmwuONn5V08B8ggkD9Ern28uC84=", 137 | "owner": "NixOS", 138 | "repo": "nixpkgs", 139 | "rev": "fd9f17ef491ffacc24fee30e94491b5b46ba27f0", 140 | "type": "github" 141 | }, 142 | "original": { 143 | "owner": "NixOS", 144 | "repo": "nixpkgs", 145 | "type": "github" 146 | } 147 | }, 148 | "nvim-tree": { 149 | "flake": false, 150 | "locked": { 151 | "lastModified": 1742694377, 152 | "narHash": "sha256-YKt5yYkgNvCWbnyaDHsF/vnLBEuLTE29LHjl5n0iyj8=", 153 | "owner": "kyazdani42", 154 | "repo": "nvim-tree.lua", 155 | "rev": "44d9b58f11d5a426c297aafd0be1c9d45617a849", 156 | "type": "github" 157 | }, 158 | "original": { 159 | "owner": "kyazdani42", 160 | "repo": "nvim-tree.lua", 161 | "type": "github" 162 | } 163 | }, 164 | "nvim-web-devicons": { 165 | "flake": false, 166 | "locked": { 167 | "lastModified": 1742215722, 168 | "narHash": "sha256-JKOvXJr1s2lpP5aeRE7OC3IeOrF5uJxg/Tal3eScd6g=", 169 | "owner": "nvim-tree", 170 | "repo": "nvim-web-devicons", 171 | "rev": "4c3a5848ee0b09ecdea73adcd2a689190aeb728c", 172 | "type": "github" 173 | }, 174 | "original": { 175 | "owner": "nvim-tree", 176 | "repo": "nvim-web-devicons", 177 | "type": "github" 178 | } 179 | }, 180 | "openingh": { 181 | "flake": false, 182 | "locked": { 183 | "lastModified": 1746139196, 184 | "narHash": "sha256-/FlNLWOSIrOYiWzAcgOdu9//QTorCDV1KWb+h6eqLwk=", 185 | "owner": "Almo7aya", 186 | "repo": "openingh.nvim", 187 | "rev": "7cc8c897cb6b34d8ed28e99d95baccef609ed251", 188 | "type": "github" 189 | }, 190 | "original": { 191 | "owner": "Almo7aya", 192 | "repo": "openingh.nvim", 193 | "type": "github" 194 | } 195 | }, 196 | "root": { 197 | "inputs": { 198 | "bufdelete-nvim": "bufdelete-nvim", 199 | "bufferline-nvim": "bufferline-nvim", 200 | "flake-compat": "flake-compat", 201 | "flake-utils": "flake-utils", 202 | "fugitive": "fugitive", 203 | "kitty-icon": "kitty-icon", 204 | "luafun": "luafun", 205 | "multicursor-nvim": "multicursor-nvim", 206 | "nixpkgs": "nixpkgs", 207 | "nvim-tree": "nvim-tree", 208 | "nvim-web-devicons": "nvim-web-devicons", 209 | "openingh": "openingh", 210 | "vim-astro": "vim-astro", 211 | "vim-glsl": "vim-glsl", 212 | "vim-nix": "vim-nix", 213 | "vim-submode": "vim-submode", 214 | "vim-surround": "vim-surround", 215 | "vim-svelte": "vim-svelte", 216 | "vim-terraform": "vim-terraform" 217 | } 218 | }, 219 | "systems": { 220 | "locked": { 221 | "lastModified": 1681028828, 222 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 223 | "owner": "nix-systems", 224 | "repo": "default", 225 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 226 | "type": "github" 227 | }, 228 | "original": { 229 | "owner": "nix-systems", 230 | "repo": "default", 231 | "type": "github" 232 | } 233 | }, 234 | "vim-astro": { 235 | "flake": false, 236 | "locked": { 237 | "lastModified": 1697864536, 238 | "narHash": "sha256-vwQ3hwSpJBCdUp5dlHuP6GPaW7EJ4Rb5kXOJ9qtrpf8=", 239 | "owner": "wuelnerdotexe", 240 | "repo": "vim-astro", 241 | "rev": "9b4674ecfe1dd84b5fb9b4de1653975de6e8e2e1", 242 | "type": "github" 243 | }, 244 | "original": { 245 | "owner": "wuelnerdotexe", 246 | "repo": "vim-astro", 247 | "type": "github" 248 | } 249 | }, 250 | "vim-glsl": { 251 | "flake": false, 252 | "locked": { 253 | "lastModified": 1718439280, 254 | "narHash": "sha256-d5lh5S1YQ1OzlsKmj+cB9UAdbX7haAqo3eR/4s4H7FQ=", 255 | "owner": "tikhomirov", 256 | "repo": "vim-glsl", 257 | "rev": "40dd0b143ef93f3930a8a409f60c1bb85e28b727", 258 | "type": "github" 259 | }, 260 | "original": { 261 | "owner": "tikhomirov", 262 | "repo": "vim-glsl", 263 | "type": "github" 264 | } 265 | }, 266 | "vim-nix": { 267 | "flake": false, 268 | "locked": { 269 | "lastModified": 1738443453, 270 | "narHash": "sha256-Hmn8EVlvMQnQF8COeb89cgl5+A83kagOjGsmvm5WNoE=", 271 | "owner": "LnL7", 272 | "repo": "vim-nix", 273 | "rev": "7235c7ce2cea530cb6b59bc3e46d4bfe917d15c8", 274 | "type": "github" 275 | }, 276 | "original": { 277 | "owner": "LnL7", 278 | "repo": "vim-nix", 279 | "type": "github" 280 | } 281 | }, 282 | "vim-submode": { 283 | "flake": false, 284 | "locked": { 285 | "lastModified": 1499687652, 286 | "narHash": "sha256-5zLztAk3HHi/UIZYPpcWb+q78ad422ZC4n7sJb3PwOE=", 287 | "owner": "kana", 288 | "repo": "vim-submode", 289 | "rev": "d29de4f55c40a7a03af1d8134453a703d6affbd2", 290 | "type": "github" 291 | }, 292 | "original": { 293 | "owner": "kana", 294 | "repo": "vim-submode", 295 | "type": "github" 296 | } 297 | }, 298 | "vim-surround": { 299 | "flake": false, 300 | "locked": { 301 | "lastModified": 1666730476, 302 | "narHash": "sha256-DZE5tkmnT+lAvx/RQHaDEgEJXRKsy56KJY919xiH1lE=", 303 | "owner": "tpope", 304 | "repo": "vim-surround", 305 | "rev": "3d188ed2113431cf8dac77be61b842acb64433d9", 306 | "type": "github" 307 | }, 308 | "original": { 309 | "owner": "tpope", 310 | "repo": "vim-surround", 311 | "type": "github" 312 | } 313 | }, 314 | "vim-svelte": { 315 | "flake": false, 316 | "locked": { 317 | "lastModified": 1666888764, 318 | "narHash": "sha256-sZcHLBCGvCk8px1FlIU+JwDbHS1e7neeXMMQLPoCYe8=", 319 | "owner": "evanleck", 320 | "repo": "vim-svelte", 321 | "rev": "0e93ec53c3667753237282926fec626785622c1c", 322 | "type": "github" 323 | }, 324 | "original": { 325 | "owner": "evanleck", 326 | "repo": "vim-svelte", 327 | "type": "github" 328 | } 329 | }, 330 | "vim-terraform": { 331 | "flake": false, 332 | "locked": { 333 | "lastModified": 1737360319, 334 | "narHash": "sha256-I2L37E2TBE3ZuK5I3xwqxmGdrwbzEMI3Keqz8k0fBdk=", 335 | "owner": "hashivim", 336 | "repo": "vim-terraform", 337 | "rev": "8912ca1be3025a1c9fab193618f3b99517e01973", 338 | "type": "github" 339 | }, 340 | "original": { 341 | "owner": "hashivim", 342 | "repo": "vim-terraform", 343 | "type": "github" 344 | } 345 | } 346 | }, 347 | "root": "root", 348 | "version": 7 349 | } 350 | --------------------------------------------------------------------------------