├── .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 |
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 |
--------------------------------------------------------------------------------