about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWilliam Carroll <wpcarro@gmail.com>2020-01-06T14·54+0000
committerWilliam Carroll <wpcarro@gmail.com>2020-01-17T10·56+0000
commit7175d230c2cb2e5d90a25bcbd012ed75eaa96860 (patch)
treeb4cad1f5b33e0b7be9b8bb77eaf8dfe5d5dcd506
parent73e988c29818dfb679e26d08aded8816b2e2f37c (diff)
Nixify fish configuration
This isn't 100% usable, but it works. You can build it with `nix-env` and then
run it with `wpcarros-fish`.
-rw-r--r--configs/shared/.config/fish/config.fish86
-rw-r--r--fish/config.fish328
-rw-r--r--fish/default.nix52
-rw-r--r--fish/functions.fish65
4 files changed, 500 insertions, 31 deletions
diff --git a/configs/shared/.config/fish/config.fish b/configs/shared/.config/fish/config.fish
index 66d7fc5f46d4..d6c8d934f51c 100644
--- a/configs/shared/.config/fish/config.fish
+++ b/configs/shared/.config/fish/config.fish
@@ -52,6 +52,9 @@
 # TODO: Decide if I prefer `abbr` or `alias` for fish. `abbr` is a new concept
 # for me.
 
+# Remove the default greeting from fish
+set fish_greeting ""
+
 function nix_find
     nix-build '<nixpkgs>' --no-build-output -A $argv[1]
 end
@@ -77,12 +80,21 @@ function fish_prompt
     echo -e "\n$suffix "
 end
 
-# 64812159184761958540
+# Setup fzf for fuzzily finding commands, files, directories
 source (fzf-share)/key-bindings.fish && fzf_key_bindings
 
+# Setup autojump for a frequency-based alternative to cd.
+# TODO: Debug why I couldn't get `fasd` to work with Nix and fish.
+source (nix_find autojump)/share/autojump/autojump.fish
+
+# TODO: What is the difference between `source` and `eval`
+# direnv
+source (direnv hook fish)
+
 # Miscellaneous
 abbr --add c xclip -selection clipboard -i
 abbr --add p xclip -selection clipboard -o
+# TODO: Depend on `cp_dwim`.
 abbr --add cp cp_dwim
 abbr --add lorem echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
 
@@ -90,9 +102,13 @@ abbr --add lorem echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit,
 abbr --add e emacsclient --no-wait --create-frame
 
 abbr --add cat bat --theme=TwoDark
-abbr --add j fasd_cd -d # Use j to emulate autojump; my muscle memory is hardened here
+# TODO: Support this once `fasd` works.
+# abbr --add j fasd_cd -d # Use j to emulate autojump; my muscle memory is hardened here
 abbr --add vim nvim
+
+# TODO: Support `blutetoothctl` with Nix.
 abbr --add btctl bluetoothctl
+
 abbr --add rg rg --ignore-case
 abbr --add rgh rg --hidden # By default, rg skips hidden files
 abbr --add fdh fd --hidden # By default, rg skips hidden files
@@ -108,26 +124,30 @@ abbr --add stopx sudo service lightdm stop # stop X server session
 abbr --add please 'eval sudo $history[1]'
 abbr --add plz please
 
-# TODO: Consider packaging this with Nix.
-abbr --add simple_vim vim -u ~/.config/nvim/simple.vim # vim without a zero-dependency vimrc
+# TODO: Package this with Nix.
+abbr --add simple_vim vim -u ~/.config/nvim/simple.vim
 
 # Filesystem
+# TODO: Depend on `mkdir_cd`.
 abbr --add mdd mkdir_cd
 abbr --add mdp mkdir --parents
 abbr --add ls exa --sort=type
 abbr --add ll exa --long --sort=type
 abbr --add la exa --long --all --sort=type
+
+# TODO: Depend on these functions once they're defined.
 abbr --add files laf
-abbr --add dirs lad
+abbr --add dirs  lad
 abbr --add links lal
 
 # Device and power management
-abbr --add off shutdown now
-abbr --add suspend systemctl suspend
+abbr --add off       shutdown now
+abbr --add suspend   systemctl suspend
 abbr --add hibernate systemctl hibernate
 
 abbr --add pscp pass show --clip
 
+# TODO: Debug `Error: No interface specified.`.
 abbr --add wfls nmcli device wifi
 abbr --add wfls nmcli device connect
 
@@ -138,20 +158,20 @@ abbr --add td  tmux detach
 
 # Chrome
 abbr --add chrome google-chrome
-abbr --add cssh chrome --app-id=pnhechapfaindjhompbnflcldabbghjo # Secure Shell
-abbr --add crd chrome --app-id=gbchcmhmhahfdphkhkmpfmihenigjmpp  # Chrome Remote Desktop
+abbr --add cssh   chrome --app-id=pnhechapfaindjhompbnflcldabbghjo # Secure Shell
+abbr --add crd    chrome --app-id=gbchcmhmhahfdphkhkmpfmihenigjmpp # Chrome Remote Desktop
 
 # Dropbox
 abbr --add drst dropbox.py status
 
 # Docker
-abbr --add dk docker
-abbr --add dkps docker ps
+abbr --add dk    docker
+abbr --add dkps  docker ps
 abbr --add dkpsa docker ps -a
-abbr --add dkrm docker rm
+abbr --add dkrm  docker rm
 abbr --add dkrmi docker rmi
-abbr --add dkrd docker run -d
-abbr --add dki docker images
+abbr --add dkrd  docker run -d
+abbr --add dki   docker images
 
 # Java
 # TODO: Consider packaging this idea with Nix instead
@@ -166,6 +186,7 @@ abbr --add tism MIX_ENV=test iex -S mix
 abbr --add mdg mix deps.get
 abbr --add mdu mix deps.update
 abbr --add mdup mix docker.up
+# TODO: Support `repl_ex` and company as Nix-built programs.
 abbr --add repl_ex dkish elixir iex
 
 # Clojure
@@ -173,8 +194,8 @@ abbr --add cljsh dkish clojure lein repl
 
 # GPG
 abbr --add gpged gpg --edit-key wpcarro@gmail.com
-abbr --add gpge gpg --encrypt
-abbr --add gpgd gpg --decrypt
+abbr --add gpge  gpg --encrypt
+abbr --add gpgd  gpg --decrypt
 abbr --add gpgls gpg --list-keys
 
 # Git
@@ -206,28 +227,29 @@ abbr --add gst   'git status && hub pr list'
 # Mercurial counterparts. Some may map 1:1, others may be like putting a square
 # peg into a round hole. I will try and use my best judgement in these cases
 # while erring on the side of unifying the two APIs.
-abbr --add hgst PAGER="" hg status
-abbr --add hglp hg xl
-abbr --add hgp hg uploadchain # this is like `git push`
-abbr --add hga hg add
-abbr --add hgc hg commit
+abbr --add hgst  PAGER="" hg status
+abbr --add hglp  hg xl
+abbr --add hgp   hg uploadchain # this is like `git push`
+abbr --add hga   hg add
+abbr --add hgc   hg commit
 abbr --add hgcan hg amend # like `git commit --amend --no-edit'
-abbr --add hgpr hg mail -r . -m # this may be similar to `hub pull-request`
-abbr --add hgd hg diff
-abbr --add hgsh hg export
-abbr --add hgco hg update
-abbr --add hgls hg citc --list # should have different output from `pils`
-abbr --add hgrc hg rebase --continue
-abbr --add hgra hg rebase --abort
+abbr --add hgpr  hg mail -r . -m # this may be similar to `hub pull-request`
+abbr --add hgd   hg diff
+abbr --add hgsh  hg export
+abbr --add hgco  hg update
+abbr --add hgls  hg citc --list # should have different output from `pils`
+abbr --add hgrc  hg rebase --continue
+abbr --add hgra  hg rebase --abort
+abbr --add hgrm  hg citc -d # delete a CitC client created with Fig
 abbr --add hgconflicts hg resolve --list 'set:unresolved()' # much like `gconflicts`
-abbr --add hgrm hg citc -d # delete a CitC client created with Fig
 
 # Haskell
 abbr --add sb stack build
 abbr --add se stack exec --
 abbr --add sc stack clean
-# alias st="stack test" # blocks suckless-terminal
+abbr --add st stack test
 abbr --add haddocks open (stack path --local-doc-root)/index.html
+# TODO: Remove `dkish` in favor of a Nix-built solution.
 abbr --add hksh 'dkish haskell ghci'
 
 # Kubernetes
@@ -240,6 +262,8 @@ abbr --add kedit kubectl edit deployments
 abbr --add kswitch gcloud container clusters get-credentials
 
 # Nix
+# TODO: Ensure that this depends on `nix_introspect` as defined in
+# functions.fish.
 abbr --add nq nix_introspect
 abbr --add nsh nix-shell
 abbr --add nshp nix-shell --pure
@@ -250,7 +274,7 @@ abbr --add nls nix-env --query
 abbr --add nrs sudo nixos-rebuild switch
 
 # Aptitude (apt)
-abbr --add apti sudo apt-get install --assume-yes
+abbr --add apti  sudo apt-get install --assume-yes
 abbr --add aptrm sudo apt remove
 
 # Pacman
diff --git a/fish/config.fish b/fish/config.fish
new file mode 100644
index 000000000000..948339314a15
--- /dev/null
+++ b/fish/config.fish
@@ -0,0 +1,328 @@
+# While I work use a variety of programs, below of some of my more commonly used
+# programs that I have decided to support with aliases.
+# Applications
+#   java:       jv
+#   tmux:       t
+#   $EDITOR:    e
+#   vim:        v
+#   GnuPG:      gpg
+#   blaze:      bz
+#   borgcfg:    br
+#   piper:      pi
+#   pass:       ps
+#   pastebin:   pb
+#   pacman:     pm
+#   codesearch: cs
+#   git:        g
+#   mercurial:  hg
+#   aptitude:   apt
+#   chrome:     c
+#   elixir:     ex
+#   haskell:    hk
+#   wifi:       wf
+#   piper:      pp
+#   g4:         pp
+#   g4d:        pp
+#   cci:        circleci
+#
+# Below are some common modifiers or flags that programs support.
+# Supported qualifiers:
+#   hidden:      h
+#   ignore-case: i
+#
+# I've found that much of my time is spent working with programs that support
+# some many of the following actions.
+# Supported verbs:
+#   source:  s
+#   install: i
+#   test:    t
+#   build:   b
+#   list:    ls
+#   shell:   REPL
+#
+# Commentary:
+# Ideally a file like this would be either unnecessary in the case of a fully
+# embraced Emacs workflow or compiled from some high-level language like
+# Elisp.
+#
+# Most of this was ported from my aliases.zsh file, which I accumulated over a
+# two to three year period. If some of the fish code herein is not idiomatic, it
+# is most likely because I'm new to the ecosystem.
+
+# TODO: Decide if I prefer `abbr` or `alias` for fish. `abbr` is a new concept
+# for me.
+
+# Remove the default greeting from fish
+set fish_greeting ""
+
+# TODO: This installs $1, which is most likely not desirable.
+function nix_find
+    nix-build '<nixpkgs>' --no-build-output -A $argv[1]
+end
+
+# Prompt
+function fish_prompt
+    set -l color_cwd
+    set -l suffix
+    switch "$USER"
+        case root toor
+            if set -q fish_color_cwd_root
+                set color_cwd $fish_color_cwd_root
+            else
+                set color_cwd $fish_color_cwd
+            end
+            set suffix '#'
+        case '*'
+            set color_cwd $fish_color_cwd
+            set suffix '>'
+    end
+
+    echo -n -s "$USER" @ (prompt_hostname) ' ' (set_color $color_cwd) (pwd) (set_color normal)
+    echo -e "\n$suffix "
+end
+
+# Setup fzf for fuzzily finding commands, files, directories
+source (fzf-share)/key-bindings.fish && fzf_key_bindings
+
+# Setup autojump for a frequency-based alternative to cd.
+# TODO: Debug why I couldn't get `fasd` to work with Nix and fish.
+source (nix_find autojump)/share/autojump/autojump.fish
+
+# TODO: What is the difference between `source` and `eval`
+# direnv
+source (direnv hook fish)
+
+# Miscellaneous
+abbr --add c xclip -selection clipboard -i
+abbr --add p xclip -selection clipboard -o
+# TODO: Depend on `cp_dwim`.
+abbr --add cp cp_dwim
+abbr --add lorem echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+
+# TODO: Ensure this works as expect with current EXWM setup.
+abbr --add e emacsclient --no-wait --create-frame
+
+abbr --add cat bat --theme=TwoDark
+# TODO: Support this once `fasd` works.
+# abbr --add j fasd_cd -d # Use j to emulate autojump; my muscle memory is hardened here
+abbr --add vim nvim
+
+# TODO: Support `blutetoothctl` with Nix.
+abbr --add btctl bluetoothctl
+
+abbr --add rg rg --ignore-case
+abbr --add rgh rg --hidden # By default, rg skips hidden files
+abbr --add fdh fd --hidden # By default, rg skips hidden files
+abbr --add define sdcv # uses stardict to lookup a word
+abbr --add intellij nohup /opt/intellij-ce-stable/bin/idea.sh >/dev/null 2>&1 &
+abbr --add tpr tput reset
+abbr --add nordvpn sudo openvpn /etc/openvpn/ovpn_tcp/us3559.nordvpn.com.tcp.ovpn # connects to the nordvpn servers in USA
+abbr --add perms ls -ld # list the permissions of a directory
+abbr --add rmrf rm -rf # sometimes the space and dash are too much...
+abbr --add open xdg-open
+abbr --add o open
+abbr --add stopx sudo service lightdm stop # stop X server session
+abbr --add please 'eval sudo $history[1]'
+abbr --add plz please
+
+# TODO: Package this with Nix.
+abbr --add simple_vim vim -u ~/.config/nvim/simple.vim
+
+# Filesystem
+# TODO: Depend on `mkdir_cd`.
+abbr --add mdd mkdir_cd
+abbr --add mdp mkdir --parents
+abbr --add ls exa --sort=type
+abbr --add ll exa --long --sort=type
+abbr --add la exa --long --all --sort=type
+
+# TODO: Depend on these functions once they're defined.
+abbr --add files laf
+abbr --add dirs  lad
+abbr --add links lal
+
+# Device and power management
+abbr --add off       shutdown now
+abbr --add suspend   systemctl suspend
+abbr --add hibernate systemctl hibernate
+
+abbr --add pscp pass show --clip
+
+# TODO: Debug `Error: No interface specified.`.
+abbr --add wfls nmcli device wifi
+abbr --add wfls nmcli device connect
+
+# Tmux
+abbr --add tls tmux list-sessions
+abbr --add ta  tmux attach
+abbr --add td  tmux detach
+
+# Chrome
+abbr --add chrome google-chrome
+abbr --add cssh   chrome --app-id=pnhechapfaindjhompbnflcldabbghjo # Secure Shell
+abbr --add crd    chrome --app-id=gbchcmhmhahfdphkhkmpfmihenigjmpp  # Chrome Remote Desktop
+
+# Dropbox
+abbr --add drst dropbox.py status
+
+# Docker
+abbr --add dk    docker
+abbr --add dkps  docker ps
+abbr --add dkpsa docker ps -a
+abbr --add dkrm  docker rm
+abbr --add dkrmi docker rmi
+abbr --add dkrd  docker run -d
+abbr --add dki   docker images
+
+# Java
+# TODO: Consider packaging this idea with Nix instead
+abbr --add jvsh env CLASSPATH=(fd '\\.jar$' ~/Dropbox/programming/jars | tr \\n :) jshell
+
+# Elixir
+abbr --add m mix
+abbr --add mc mix compile
+abbr --add mcf mix compile --force
+abbr --add ism iex -S mix
+abbr --add tism MIX_ENV=test iex -S mix
+abbr --add mdg mix deps.get
+abbr --add mdu mix deps.update
+abbr --add mdup mix docker.up
+# TODO: Support `repl_ex` and company as Nix-built programs.
+abbr --add repl_ex dkish elixir iex
+
+# Clojure
+abbr --add cljsh dkish clojure lein repl
+
+# GPG
+abbr --add gpged gpg --edit-key wpcarro@gmail.com
+abbr --add gpge  gpg --encrypt
+abbr --add gpgd  gpg --decrypt
+abbr --add gpgls gpg --list-keys
+
+# Git
+abbr --add glp   git log --graph --pretty='format:"%Cred%h%Creset -%Cblue %an %Creset - %C(yellow)%d%Creset %s %Cgreen(%cr)%Creset" --abbrev-commit --date=relative'
+abbr --add g     hub
+abbr --add git   hub
+abbr --add ga    git add
+abbr --add gc    git commit
+abbr --add gco   git checkout
+abbr --add gd    git diff
+abbr --add gp    git push
+abbr --add grbi  git rebase --interactive
+abbr --add grba  git rebase --abort
+abbr --add grbc  git rebase --continue
+abbr --add gprom git pull --rebase origin master
+abbr --add gca   git commit --amend
+abbr --add gcan  git commit --amend --no-edit
+abbr --add gpf   git push --force
+abbr --add gpff  git push --force --no-verify
+abbr --add gds   git diff --staged
+abbr --add gfx   git commit --fixup
+abbr --add gsh   git show
+abbr --add gwip  'git add . && git commit -m wip'
+abbr --add gpr   git pull-request
+abbr --add gst   'git status && hub pr list'
+
+# Mercurial
+# The attempt here is to map my well-known, existing `git` aliases to their
+# Mercurial counterparts. Some may map 1:1, others may be like putting a square
+# peg into a round hole. I will try and use my best judgement in these cases
+# while erring on the side of unifying the two APIs.
+abbr --add hgst  PAGER="" hg status
+abbr --add hglp  hg xl
+abbr --add hgp   hg uploadchain # this is like `git push`
+abbr --add hga   hg add
+abbr --add hgc   hg commit
+abbr --add hgcan hg amend # like `git commit --amend --no-edit'
+abbr --add hgpr  hg mail -r . -m # this may be similar to `hub pull-request`
+abbr --add hgd   hg diff
+abbr --add hgsh  hg export
+abbr --add hgco  hg update
+abbr --add hgls  hg citc --list # should have different output from `pils`
+abbr --add hgrc  hg rebase --continue
+abbr --add hgra  hg rebase --abort
+abbr --add hgrm  hg citc -d # delete a CitC client created with Fig
+abbr --add hgconflicts hg resolve --list 'set:unresolved()' # much like `gconflicts`
+
+# Haskell
+abbr --add sb stack build
+abbr --add se stack exec --
+abbr --add sc stack clean
+abbr --add st stack test
+abbr --add haddocks open (stack path --local-doc-root)/index.html
+# TODO: Remove `dkish` in favor of a Nix-built solution.
+abbr --add hksh 'dkish haskell ghci'
+
+# Kubernetes
+abbr --add kc kubectl
+abbr --add kpods kubectl get pods
+abbr --add knodes kubectl get nodes
+abbr --add kdeploys kubectl get deployments
+abbr --add kdns kubectl get ing
+abbr --add kedit kubectl edit deployments
+abbr --add kswitch gcloud container clusters get-credentials
+
+# Nix
+# TODO: Ensure that this depends on `nix_introspect` as defined in
+# functions.fish.
+abbr --add nq nix_introspect
+abbr --add nsh nix-shell
+abbr --add nshp nix-shell --pure
+abbr --add nr nix repl
+abbr --add ni nix-env --install
+abbr --add nrm nix-env --uninstall
+abbr --add nls nix-env --query
+abbr --add nrs sudo nixos-rebuild switch
+
+# Aptitude (apt)
+abbr --add apti  sudo apt-get install --assume-yes
+abbr --add aptrm sudo apt remove
+
+# Pacman
+abbr --add pmi sudo pacman -S --noconfirm
+abbr --add pms pacman -Ss
+abbr --add pmrm sudo pacman -Rs
+
+# Couple the e* aliases to the <leader>e* kbds in vim.
+abbr --add ev e ~/.config/nvim/init.vim
+abbr --add ee e ~/.emacs.d/init.el
+abbr --add ez e ~/.zshrc
+abbr --add ea e ~/aliases.zsh
+abbr --add ef e ~/functions.zsh
+abbr --add el e ~/variables.zsh
+abbr --add ex e ~/.Xresources
+abbr --add em e ~/.tmux.conf
+abbr --add er e ~/Dropbox/dotfiles/README.md
+# TODO: consider DRYing this up with `e`. Unfortunately, `sudo` won't support
+# aliases.
+abbr --add en sudo ALTERNATE_EDITOR=nvim emacsclient --no-wait --create-frame /etc/nixos/configuration.nix
+
+# Couple the s* aliases to the <leader>s* kbds in vim.
+abbr --add sz source ~/.zshrc
+abbr --add sa source ~/aliases.zsh
+abbr --add sf source ~/functions.zsh
+abbr --add sl source ~/variables.zsh
+abbr --add sx xrdb ~/.Xresources
+abbr --add sm tmux source-file ~/.tmux.conf
+abbr --add sn sudo nixos-rebuild switch
+
+# CircleCI
+abbr --add cci    circleci local
+abbr --add ccijob circleci local execute --job
+
+# Google stuff
+abbr --add bzb      blaze build
+abbr --add bzt      blaze test --test_output=all
+abbr --add br       borgcfg
+abbr --add pils     p4 listclients
+abbr --add pirm     p4 citc -d
+abbr --add ppls     'g4 listclients | sed \'s/^Client wpcarro://\' | sed \'s/:[0-9]*:citc.*$//g\''
+abbr --add pprm     p4 citc -d -f # WARNING: This will forcefully delete a CitC client even if contains pending changes.
+abbr --add flagpick /google/data/ro/users/sk/skaushik/www/public-tools/flagpick
+abbr --add jaze     /google/data/ro/projects/devtools/javascript/jaze
+abbr --add aclcheck /google/data/ro/projects/ganpati/aclcheck
+abbr --add g3python /google/data/ro/projects/g3python/g3python
+abbr --add pb       /google/src/head/depot/eng/tools/pastebin
+abbr --add pbc      'pb --private --title $(date +$date_fmt)| tee >(c && chrome (p))' # create a private gPaste from your clipboard's content; open the result in a browser
+abbr --add pbcp     'p | pb --private --title (date +$date_fmt)| tee >(c && chrome (p))' # create a private gPaste from your clipboard's content; open the result in a browser
+abbr --add pbls     $BROWSER https://paste.googleplex.com/(whoami)
diff --git a/fish/default.nix b/fish/default.nix
new file mode 100644
index 000000000000..d5ec372a5dea
--- /dev/null
+++ b/fish/default.nix
@@ -0,0 +1,52 @@
+{ pkgs ? import <nixpkgs> {} , ... }:
+
+# TODO: Is it appropriate to put programming language dependencies here? Should
+# I have a bin dependency for every fish `abbr` and `alias` that I use? What
+# makes the most sense?
+
+# TODO: Some of the abbreviations / aliases depend on binaries and some depend
+# on files (e.g. ~/.config/nvim/init.vim). How should I handle the file
+# dependencies?
+
+# TODO: Support symlinking config.fish to ~/.config/fish/config.fish using Nix.
+
+let
+  fishBinPath = pkgs.lib.strings.makeBinPath (with pkgs; [
+    # TODO: Support fasd instead of autojump.
+    # fasd
+    direnv
+    autojump
+    fzf
+    fd
+    xclip
+    bat
+    neovim
+    ripgrep
+    sdcv
+    exa
+    pass
+    networkmanager
+    google-chrome
+    docker
+    elixir
+    clojure
+    gnupg
+    git
+    tmux
+    # This is not that same as `hub`.
+    # git-hub
+    mercurial
+    stack
+    kubernetes
+    circleci-cli
+    nix # Really?
+    apt # Really?
+    pacman # Really?
+  ]);
+# TODO: It's difficult to test if the `--init-command` is working since fish
+# persists functions, abbreviations, aliases between sessions so it's easy to
+# get tricked by false-positives.
+in pkgs.writeShellScriptBin "wpcarros-fish" ''
+  export PATH="${fishBinPath}:$PATH"
+  exec ${pkgs.fish}/bin/fish --init-command 'source ${ ./functions.fish }'
+''
diff --git a/fish/functions.fish b/fish/functions.fish
new file mode 100644
index 000000000000..d7e320c9a6f5
--- /dev/null
+++ b/fish/functions.fish
@@ -0,0 +1,65 @@
+# TODO: Consider a `rm` that behaves like this as well. Would then be useful to
+# support something like a "Trash" folder so that I can undo unintentional
+# deletions.
+function cp_dwim -d "Copy files and directories similarly."
+    # TODO: Where do I put documentation like this?
+    # Calls `cp -r` when a directory is specified, otherwise uses `cp`.
+    # This is closer to the UX you expect in GUIs when you copy-and-paste files.
+    if test -d $argv[1]
+        command cp -r $argv[1..-1]
+    else
+        command cp $argv[1..-1]
+    end
+end
+
+function mkdir_cd -d "Make a directory and `cd` into it."
+    mkdir -p $argv[1] && cd $argv[1]
+end
+
+function lt -d "Like tree, except using `exa`."
+    # Convenience wrapper around `exa --tree`.
+    # Optionally accepts a number for the max-depth and a directory to list.
+    # Usage: lt 2 ./scripts
+
+    # lt
+    if test (count $argv) -eq 0
+        exa --tree --all
+
+    else if test (count $argv) -eq 1
+        # lt 2
+        if string match --quiet --regex '^[0-9]+$' $argv[1]
+            exa --tree --all --level $argv[1]
+
+        # lt path/to/directory
+        else if test -d $argv[1]
+            exa --tree --all $argv[1]
+        end
+
+    # lt 2 path/to/directory
+    else if test (count $argv) -eq 2
+        exa --tree --all --level $argv[1] $argv[2]
+    end
+
+end
+
+function lad -d "List only the directories within a directory."
+    # TODO: Support $argv[1], which is currently broken here. See functions.zsh
+    # for a reference.
+    fd --hidden --maxdepth 1 --type d
+end
+
+function laf -d "List only the files within a directory."
+    # TODO: Support $argv[1], which is currently broken here. See functions.zsh
+    # for a reference.
+    fd --hidden --maxdepth 1 --type f
+end
+
+function lal -d "List only the links within a directory."
+    # TODO: Support $argv[1], which is currently broken here. See functions.zsh
+    # for a reference.
+    fd --hidden --maxdepth 1 --type l
+end
+
+function nix_introspect -d "Search through local nixpkgs repository."
+    rg --after-context 5 "\\b$argv[1]\\b\\s*=" (nix-instantiate --find-file nixpkgs)
+end