├── .gitignore
├── LICENSE
├── README.md
├── flake.lock
├── flake.nix
├── git
├── default.nix
├── gitconfig
└── gitignore
├── homies.nix
├── homies.png
├── kitty
├── Info.plist
├── default.nix
└── kitty.conf
├── neovim
├── default.nix
├── init.lua
└── lua
│ ├── README.md
│ └── fzf.lua
├── nix
├── default.nix
└── nix.conf
└── zshrc
├── default.nix
├── fzf-key-bindings.zsh
└── zshrc
/.gitignore:
--------------------------------------------------------------------------------
1 | result
2 | *.swp
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 profile install`. Update your `~/.zshrc`:
10 |
11 | ``` zsh
12 | if [ -f $HOME/.nix-profile/share/zshrc/zshrc ]; then source $HOME/.nix-profile/share/zshrc/zshrc; fi
13 | ```
14 |
15 | The homies will be available in all subsequent shells, including the
16 | customizations (vim with my favorite plugins, tmux with my customized
17 | configuration, etc). See the [introduction blog post][post] for an overview.
18 |
19 | [post]: https://nmattia.com/posts/2018-03-21-nix-reproducible-setup-linux-macos.html
20 |
21 | ## How-To
22 |
23 | Installing the package set:
24 |
25 | ``` shell
26 | $ nix profile install
27 | ```
28 |
29 | Updating the packages:
30 |
31 | ```shell
32 | $ nix flake update # alternative: nix flake lock --update-input
33 | ```
34 |
35 | Try out the new packages:
36 |
37 | ```shell
38 | $ nix develop
39 | ```
40 |
41 | Upgrading to the new profile:
42 |
43 | ``` shell
44 | $ nix profile upgrade homies # or list more with "nix profile list"
45 | ```
46 |
47 | Syncing apps for Spotlight indexing:
48 |
49 | ```
50 | $ 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
51 | ```
52 |
53 | > **Note**
54 | > We copy the app to make sure Spotlight picks it up. Creating a (Finder) alias does work too, but
55 | > the alias is given much lower priority in Spotlight search and the app appears way below e.g.
56 | > online searches, files, etc.
57 |
58 | Listing the previous and current configurations:
59 |
60 | ``` shell
61 | $ nix profile history
62 | ```
63 |
64 | Deleting old configurations:
65 |
66 | ``` shell
67 | $ nix profile wipe-history
68 | ```
69 |
70 | Ensure build is sandboxed:
71 | ```
72 | # /etc/nix/nix.conf
73 | build-users-group = nixbld
74 | # /Library: cc is installed in /Library/Developer (and used from /usr/bin
75 | /cc and others)
76 | # /System/Library: needed for system-wide Perl
77 | sandbox-paths = /bin/bash /bin /usr/bin /usr/sbin /Library /System/Library
78 | sandbox = true
79 | ```
80 |
--------------------------------------------------------------------------------
/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 | "root": {
181 | "inputs": {
182 | "bufdelete-nvim": "bufdelete-nvim",
183 | "bufferline-nvim": "bufferline-nvim",
184 | "flake-compat": "flake-compat",
185 | "flake-utils": "flake-utils",
186 | "fugitive": "fugitive",
187 | "kitty-icon": "kitty-icon",
188 | "luafun": "luafun",
189 | "multicursor-nvim": "multicursor-nvim",
190 | "nixpkgs": "nixpkgs",
191 | "nvim-tree": "nvim-tree",
192 | "nvim-web-devicons": "nvim-web-devicons",
193 | "vim-astro": "vim-astro",
194 | "vim-glsl": "vim-glsl",
195 | "vim-nix": "vim-nix",
196 | "vim-submode": "vim-submode",
197 | "vim-surround": "vim-surround",
198 | "vim-svelte": "vim-svelte",
199 | "vim-terraform": "vim-terraform"
200 | }
201 | },
202 | "systems": {
203 | "locked": {
204 | "lastModified": 1681028828,
205 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
206 | "owner": "nix-systems",
207 | "repo": "default",
208 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
209 | "type": "github"
210 | },
211 | "original": {
212 | "owner": "nix-systems",
213 | "repo": "default",
214 | "type": "github"
215 | }
216 | },
217 | "vim-astro": {
218 | "flake": false,
219 | "locked": {
220 | "lastModified": 1697864536,
221 | "narHash": "sha256-vwQ3hwSpJBCdUp5dlHuP6GPaW7EJ4Rb5kXOJ9qtrpf8=",
222 | "owner": "wuelnerdotexe",
223 | "repo": "vim-astro",
224 | "rev": "9b4674ecfe1dd84b5fb9b4de1653975de6e8e2e1",
225 | "type": "github"
226 | },
227 | "original": {
228 | "owner": "wuelnerdotexe",
229 | "repo": "vim-astro",
230 | "type": "github"
231 | }
232 | },
233 | "vim-glsl": {
234 | "flake": false,
235 | "locked": {
236 | "lastModified": 1718439280,
237 | "narHash": "sha256-d5lh5S1YQ1OzlsKmj+cB9UAdbX7haAqo3eR/4s4H7FQ=",
238 | "owner": "tikhomirov",
239 | "repo": "vim-glsl",
240 | "rev": "40dd0b143ef93f3930a8a409f60c1bb85e28b727",
241 | "type": "github"
242 | },
243 | "original": {
244 | "owner": "tikhomirov",
245 | "repo": "vim-glsl",
246 | "type": "github"
247 | }
248 | },
249 | "vim-nix": {
250 | "flake": false,
251 | "locked": {
252 | "lastModified": 1738443453,
253 | "narHash": "sha256-Hmn8EVlvMQnQF8COeb89cgl5+A83kagOjGsmvm5WNoE=",
254 | "owner": "LnL7",
255 | "repo": "vim-nix",
256 | "rev": "7235c7ce2cea530cb6b59bc3e46d4bfe917d15c8",
257 | "type": "github"
258 | },
259 | "original": {
260 | "owner": "LnL7",
261 | "repo": "vim-nix",
262 | "type": "github"
263 | }
264 | },
265 | "vim-submode": {
266 | "flake": false,
267 | "locked": {
268 | "lastModified": 1499687652,
269 | "narHash": "sha256-5zLztAk3HHi/UIZYPpcWb+q78ad422ZC4n7sJb3PwOE=",
270 | "owner": "kana",
271 | "repo": "vim-submode",
272 | "rev": "d29de4f55c40a7a03af1d8134453a703d6affbd2",
273 | "type": "github"
274 | },
275 | "original": {
276 | "owner": "kana",
277 | "repo": "vim-submode",
278 | "type": "github"
279 | }
280 | },
281 | "vim-surround": {
282 | "flake": false,
283 | "locked": {
284 | "lastModified": 1666730476,
285 | "narHash": "sha256-DZE5tkmnT+lAvx/RQHaDEgEJXRKsy56KJY919xiH1lE=",
286 | "owner": "tpope",
287 | "repo": "vim-surround",
288 | "rev": "3d188ed2113431cf8dac77be61b842acb64433d9",
289 | "type": "github"
290 | },
291 | "original": {
292 | "owner": "tpope",
293 | "repo": "vim-surround",
294 | "type": "github"
295 | }
296 | },
297 | "vim-svelte": {
298 | "flake": false,
299 | "locked": {
300 | "lastModified": 1666888764,
301 | "narHash": "sha256-sZcHLBCGvCk8px1FlIU+JwDbHS1e7neeXMMQLPoCYe8=",
302 | "owner": "evanleck",
303 | "repo": "vim-svelte",
304 | "rev": "0e93ec53c3667753237282926fec626785622c1c",
305 | "type": "github"
306 | },
307 | "original": {
308 | "owner": "evanleck",
309 | "repo": "vim-svelte",
310 | "type": "github"
311 | }
312 | },
313 | "vim-terraform": {
314 | "flake": false,
315 | "locked": {
316 | "lastModified": 1737360319,
317 | "narHash": "sha256-I2L37E2TBE3ZuK5I3xwqxmGdrwbzEMI3Keqz8k0fBdk=",
318 | "owner": "hashivim",
319 | "repo": "vim-terraform",
320 | "rev": "8912ca1be3025a1c9fab193618f3b99517e01973",
321 | "type": "github"
322 | },
323 | "original": {
324 | "owner": "hashivim",
325 | "repo": "vim-terraform",
326 | "type": "github"
327 | }
328 | }
329 | },
330 | "root": "root",
331 | "version": 7
332 | }
333 |
--------------------------------------------------------------------------------
/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 | # dependency of bufferline-nvim
44 | inputs.nvim-web-devicons.url = github:nvim-tree/nvim-web-devicons;
45 | inputs.nvim-web-devicons.flake = false;
46 |
47 | inputs.bufferline-nvim.url = github:akinsho/bufferline.nvim;
48 | inputs.bufferline-nvim.flake = false;
49 |
50 | inputs.bufdelete-nvim.url = github:famiu/bufdelete.nvim;
51 | inputs.bufdelete-nvim.flake = false;
52 |
53 | inputs.vim-submode.url = github:kana/vim-submode;
54 | inputs.vim-submode.flake = false;
55 |
56 | inputs.luafun.url = github:luafun/luafun;
57 | inputs.luafun.flake = false;
58 |
59 | outputs =
60 | inputs@{ self
61 | , nixpkgs
62 | , flake-compat
63 | , flake-utils
64 | , ...
65 | }:
66 | flake-utils.lib.eachSystem [ "x86_64-darwin" "aarch64-darwin" ] (system:
67 | let
68 | pkgs = nixpkgs.legacyPackages.${system};
69 | homies = import ./homies.nix {
70 | nixpkgs-src = nixpkgs;
71 | inherit
72 | pkgs
73 | inputs;
74 | };
75 | in
76 | {
77 | packages.default = homies;
78 | }
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/git/default.nix:
--------------------------------------------------------------------------------
1 | # Git, with a git config baked in (see ./config)
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 | git.overrideAttrs (old: {
16 |
17 | # git has three levels of config: system, global, and local. The nixpkgs build
18 | # of git already writes some stuff to the system config, so we just append our
19 | # own config. The big drawback is that we need to build git from scratch.
20 | postInstall = old.postInstall + ''
21 | cat ${gitconfig} >> $out/etc/gitconfig
22 | '';
23 | })
24 |
--------------------------------------------------------------------------------
/git/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 | [url "git@github.com:"]
28 | insteadOf = https://github.com/
29 | [init]
30 | defaultBranch = "main"
31 |
--------------------------------------------------------------------------------
/git/gitignore:
--------------------------------------------------------------------------------
1 | result
2 | result-*
3 | *.swp
4 | tags
5 |
--------------------------------------------------------------------------------
/homies.nix:
--------------------------------------------------------------------------------
1 | { pkgs, nixpkgs-src, inputs }:
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 | # Git with config baked in
16 | git = pkgs.callPackage ./git { };
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 | git
28 | kitty.wrapper
29 | kitty.bundle
30 | nix
31 | neovim
32 |
33 | pkgs.curl
34 | pkgs.direnv
35 | pkgs.entr
36 | pkgs.git-lfs
37 | pkgs.gnupg
38 | pkgs.nixpkgs-fmt
39 | pkgs.niv
40 | pkgs.fzf
41 | pkgs.htop
42 | pkgs.jq
43 | pkgs.less
44 | pkgs.mpremote
45 | pkgs.scc
46 | pkgs.shellcheck
47 | pkgs.shfmt
48 | pkgs.tree
49 | ];
50 | }
51 |
--------------------------------------------------------------------------------
/homies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nmattia/homies/b0eff88b9cd0cc3ed9c432d9e632c1185b2b056b/homies.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.41.1";
14 | sha256 = "sha256:10j445iif392l0srhlqjm1vg8vkmqmcpl7cpwm10j7dy9wlvbv4j";
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 ${kittyDmg}
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 |
--------------------------------------------------------------------------------
/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 | # open new windows in the current CWD
29 | map cmd+enter launch --cwd=last_reported
30 |
31 | # kitty sometimes inherits bash as SHELL for unknown reasons
32 | shell /bin/zsh
33 |
--------------------------------------------------------------------------------
/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 | vim-astro
12 | vim-glsl
13 | vim-nix
14 | vim-submode
15 | vim-surround
16 | vim-svelte
17 | vim-terraform;
18 | };
19 |
20 | plugins' = lib.mapAttrsToList (k: v: "${k} ${v}") plugins;
21 | plugins'' = lib.concatStringsSep "\n" plugins';
22 |
23 | pluginsDir = runCommand "mk-plugins" { nativeBuildInputs = [ neovim-unwrapped ]; }
24 | ''
25 | plugins_dir="$out/pack/nix-is-an-addiction/start"
26 |
27 | mkdir -p "$plugins_dir"
28 |
29 | # loop over plugins, using FD 10 to avoid polluting stdin
30 | # (trips nvim otherwise)
31 | while read -u 10 plug_name plugin
32 | do
33 | plug_dest="$plugins_dir/$plug_name"
34 |
35 | echo installing plugin
36 | echo " " name "'$plug_name'"
37 | echo " " source "'$plugin'"
38 | echo " " destination "'$plug_dest'"
39 |
40 | cp -a "$plugin/." "$plug_dest"
41 |
42 | # build doc/helptags if necessary
43 | pushd "$plug_dest" >/dev/null
44 | if [ -d doc ]
45 | then
46 | echo installing doc
47 | chmod -R +w .
48 | # Set home & al so that nvim can create swapfiles
49 | XDG_DATA_HOME=$PWD HOME=$PWD nvim -u NONE -c ":helptags doc" -c q
50 | fi
51 | popd >/dev/null
52 | done 10<<< $(echo -n "${plugins''}")
53 | '';
54 |
55 | luafun = runCommand "luafun" { } ''
56 | mkdir -p $out
57 | cp ${inputs.luafun}/fun.lua $out/fun.lua
58 | '';
59 |
60 | extraBins = [
61 | ripgrep # used by fzf.lua for rg
62 | coreutils
63 | ];
64 | in
65 |
66 | symlinkJoin {
67 | name = "neovim";
68 | buildInputs = [ makeWrapper ];
69 | postBuild = ''
70 | wrapProgram "$out/bin/nvim" \
71 | --add-flags "-u ${./init.lua}" \
72 | --set NEOVIM_PLUGINS_PATH '${pluginsDir}' \
73 | --set NEOVIM_LUA_PATH '${luafun}/?.lua;${./lua}/?.lua' \
74 | --prefix PATH ':' '${lib.makeBinPath extraBins}'
75 | '';
76 | paths = [ neovim-unwrapped ];
77 | }
78 |
--------------------------------------------------------------------------------
/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 | -- FZF + ripgrep on ,fr
115 |
116 | local fzf = require'fzf'
117 | vim.keymap.set('n', 'fr', fzf.rg, { noremap = true })
118 |
119 | -- Misc
120 |
121 | -- Wrap selected lines with Q
122 | vim.keymap.set('n', 'Q', 'gq', { noremap = true })
123 |
124 | -- Yank til end of line (consistent with C and D)
125 | vim.keymap.set('n', 'Y', 'y$', { noremap = true })
126 |
127 | -- Select the whole file with
128 | vim.keymap.set('n', '', 'ggVG', { noremap = true })
129 |
130 | -- Start a git command with ,g
131 | vim.keymap.set('n', 'g', ':G ', { noremap = true })
132 |
133 | -- [r]efresh buffers
134 | vim.keymap.set('n', 'r', ':checktime ', { noremap = true })
135 |
136 | -- In Visual, sort with
137 | vim.keymap.set('v', '', ':sort', { noremap = true })
138 |
139 | -- TERMINAL
140 |
141 | -- Open a terminal in the current window
142 | vim.keymap.set('n', 't', vim.cmd.terminal, { noremap = true })
143 |
144 | -- Exit terminal through Ctrl+hjkl
145 | for _,key in pairs{ 'H', 'J', 'K', 'L' } do
146 | vim.keymap.set('t', '', '', { noremap = true })
147 | end
148 |
149 | -- Close the terminal buffer if the terminal exits with 0
150 | vim.api.nvim_create_autocmd('TermClose', {
151 | command = "if !v:event.status | exe 'bdelete! '..expand('') | endif"
152 | })
153 |
154 | -- Multi-cursor edit (enabled only in normal mode to avoid clash with
155 | -- in visual mode)
156 | local mc = require("multicursor-nvim")
157 | mc.setup()
158 | -- Add a cursor and jump to the next word under cursor.
159 | vim.keymap.set({'n'}, '', function() mc.addCursor("*") end)
160 | -- Skip word under cursor
161 | vim.keymap.set({'n'}, '', function() mc.skipCursor("*") end)
162 |
163 | -- Clear all cursors
164 | vim.keymap.set({'n', 'v'}, '', function()
165 | if mc.hasCursors() then
166 | mc.clearCursors()
167 | end
168 | end)
169 |
170 | -- Set up bufferline: https://github.com/akinsho/bufferline.nvim?tab=readme-ov-file#usage
171 | require("bufferline").setup{}
172 |
173 | -- switch to previous/next buffer (and enter submode for quick repeat with h/l)
174 | vim.api.nvim_command([[
175 | call submode#enter_with('switchbuf', 'n', '', 'h', ':bprevious')
176 | call submode#enter_with('switchbuf', 'n', '', 'l', ':bnext')
177 | call submode#leave_with('switchbuf', 'n', '', '')
178 | call submode#map('switchbuf', 'n', '', 'h', ':bprevious')
179 | call submode#map('switchbuf', 'n', '', 'l', ':bnext')
180 | ]])
181 |
182 | -- Make vim sees the leaving key when exiting a submode (similar to
183 | -- e.g. opt+key when emulating Esc)
184 | vim.g.submode_keep_leaving_key = true
185 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/neovim/lua/fzf.lua:
--------------------------------------------------------------------------------
1 | local api = vim.api
2 |
3 | local function read_file(filename)
4 | local f = assert(io.open(filename, "rb"))
5 | local content = f:read("*all")
6 | f:close()
7 | return content
8 | end
9 |
10 | local rg_filename_and_lineno = function(line)
11 | local idx = string.find(line, ":")
12 | local filename = string.sub(line, 1, idx-1)
13 | line = string.sub(line, idx+1, -1)
14 |
15 | idx = string.find(line, ":")
16 | local lineno = string.sub(line, 1, idx-1)
17 |
18 | return filename, tonumber(lineno)
19 | end
20 |
21 | local mk_fzf_bindings = function(bindings)
22 | local kvs = iter(bindings):map(function(k,v) return k..":"..v end):totable()
23 | return table.concat(kvs, ",")
24 | end
25 |
26 | local rg = function()
27 |
28 | vim.cmd("new")
29 | local new_buf_nr = api.nvim_get_current_buf()
30 | local stdout_filename = os.tmpname()
31 |
32 | -- --line-number: show line numbers, useful when picking exact line
33 | -- --no-heading: don't cluster by file name (file name is printed alone above matches)
34 | -- '\S': print all lines that contain at least one non-space character
35 | -- --hidden --glob '!.git': show hidden files like `.github/` but ignore `.git/`
36 | local rg_opts = '--line-number --no-heading --color=always --hidden --glob "!.git"'
37 | local rg = 'rg '..rg_opts..' -- "\\S"'
38 |
39 | -- num[b]er all lines, and format is [l]eft justified
40 | local add_linenos = 'nl -b all -n ln'
41 |
42 | -- Match on 'start of line' + line number of selected line
43 | -- Capture rest of line in \1
44 | -- Replace everything with "line number" + + \1 +
45 | local highlight_line = [[sed "s/^"{2}" \(.*\)""/"{2}" "`tput bold`"\1"`tput sgr0`"/"]]
46 |
47 | -- fzf's preview command, that shows the file (with the selected line highlighted)
48 | local preview_cmd = 'cat {1} | '..add_linenos..' | '..highlight_line
49 |
50 | local fzf_bindings = mk_fzf_bindings{
51 | ['ctrl-p'] = 'toggle-preview',
52 | ['ctrl-c'] = 'cancel',
53 | ['ctrl-u'] = 'preview-half-page-up',
54 | ['ctrl-d'] = 'preview-half-page-down',
55 | }
56 |
57 | local fzf_opts = {
58 | "--ansi", -- works with --color=always in rg to show colors properly
59 | "--delimiter=':'", -- used by fzf to split "FIELD INDEX EXPRESSIONS" (':' is the default rg delimited)
60 | [[--preview ']]..preview_cmd..[[']], -- specify how the currently selected file should be previewed
61 | "--preview-window=hidden", -- don't open preview unless asked to
62 | [[--bind ']]..fzf_bindings..[[']],
63 | }
64 |
65 | -- Make the actual command
66 | local fzf = 'fzf '..table.concat(fzf_opts, ' ')..' >'..stdout_filename
67 | local term_cmd = rg..' | '..fzf
68 |
69 | vim.fn.termopen(term_cmd, {
70 | on_exit = function(job_id, exit_code)
71 |
72 |
73 | -- Avoid "Process exited with ..."
74 | -- (buffer is still valid iff it wasn't somehow deleted already)
75 | if api.nvim_buf_is_valid(new_buf_nr) then
76 | api.nvim_buf_delete(new_buf_nr, {})
77 | end
78 |
79 | if(exit_code ~= 0) then
80 | -- most likely ^C
81 | return
82 | end
83 |
84 | local content = read_file(stdout_filename)
85 | local filename, lineno = rg_filename_and_lineno(content)
86 |
87 | -- open file at line
88 | vim.cmd('e '..'+'..lineno..' '..filename)
89 |
90 | -- center
91 | vim.api.nvim_feedkeys("zz", "n", false)
92 |
93 | os.remove(stdout_filename)
94 | end
95 |
96 | })
97 | vim.cmd'startinsert'
98 | end
99 |
100 |
101 | return { rg = rg }
102 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/nix/nix.conf:
--------------------------------------------------------------------------------
1 | experimental-features = nix-command flakes
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------