- dash as the system shell (because it’s faster)
- fish as my interactive shell
- bash for some scripts because it’s more convenient than dash and more POSIX compliant than fish
Using org-tangle, I output the configs to the files they need to be in:
~/.profile
is sourced by login, making everything in it available to all shells.~/.bashrc
is sourced by bash and fish (using the bass extension).
Note: I keep fish aliases in Aliases.
set fish_greeting ''
function __haris_init_key_bindings --on-event fish_prompt
# If I don't postpone it, something overrides it in certain setups
[ -z "$EMACS_VTERM_PATH" ] && fish_vi_key_bindings || fish_default_key_bindings
functions --erase __haris_init_key_bindings
end
# Disable path shortening
set fish_prompt_pwd_dir_length 0
if type --quiet bass
# I'm setting HOME to prevent errors when running inside tuterm
HOME="/home/$USER" bass source ~/.bashrc # Load bash's config
if status is-login
bass source ~/.profile
end
end
set -gx MANPATH ":$__fish_data_dir/man" # Add fish manpages to MANPATH
set -gx MANWIDTH 80
set -gx EDITOR 'editor'
set -gx VISUAL 'visual-editor'
set -gx PAGER 'less'
function fish_prompt --description 'Write out the prompt'
set -l last_pipestatus $pipestatus
set -l normal (set_color normal)
# Color the prompt differently when we're root
set -l color_cwd $fish_color_cwd
set -l prefix
set -l suffix '$'
if contains -- $USER root toor
if set -q fish_color_cwd_root
set color_cwd $fish_color_cwd_root
end
set suffix '#'
end
# If we're running via SSH, change the host color.
set -l color_host $fish_color_host
if set -q SSH_TTY
set color_host $fish_color_host_remote
end
set -l prompt_status (__fish_print_pipestatus " [" "]" "|" \
(set_color brred) (set_color --bold brred) $last_pipestatus)
# Using a timeout here prevents hang on remotely mounted filesystems
set -l vcs_prompt (timeout 0.2 fish --no-config -c fish_vcs_prompt 2>/dev/null)
set -l vcs_prompt (
echo "$vcs_prompt" |
head -c 40 |
string replace -r '([^\)])$' '$1)'
)
set -l host
if [ -n "$SSH_CONNECTION" ]
set host "$normal"(set_color white)"[$hostname]"
end
set -l sudo_privilege
if sudo -n true 2>/dev/null
set sudo_privilege " (#)"
end
set -l private
if [ -n "$fish_private_mode" ]
set private "(private) "
end
echo -n -s (set_color --bold brblue) "$USER$host " \
(set_color bryellow) "$private" $normal \
(set_color --bold magenta) (prompt_pwd) $normal \
(set_color brmagenta) "$vcs_prompt" \
(set_color brmagenta) $prompt_status \
(set_color white) $sudo_privilege \
(set_color yellow) \n" $suffix "
# These are needed because some apps (docker-compose) don't clean up properly
begin
# Exit AppMode
echo -ne "\e[?1l"
# Show the cursor again
echo -e "\e[?25h"
end
end
function fish_mode_prompt
if [ -z "$EMACS_VTERM_PATH" ]
switch $fish_bind_mode
case default
set_color --bold brred
echo '[N] '
case insert
# Show nothing
case replace_one
set_color --bold green
echo '[r] '
case replace
set_color --bold blue
echo '[R] '
case visual
set_color --bold magenta
echo '[V] '
case '*'
set_color --bold red
echo '?'
end
end
set_color normal
end
function fish_right_prompt
set_color yellow
set -l any_content
if [ -n "$WORKTREE" ]
echo -n "WT: $WORKTREE"
set any_content true
end
if [ "$COMPOSE_PROJECT_NAME" ]
if [ -n "$any_content" ]
echo -n " | "
end
echo -n "COMPOSE: $COMPOSE_PROJECT_NAME"
set any_content true
end
if [ -n "$EXPOSED_IP" ]
if [ -n "$any_content" ]
echo -n " | "
end
echo -n "IP: $EXPOSED_IP"
set any_content true
end
set_color normal
end
This function prints a horizontal line spanning the terminal screen, that serves as a visual separator of command outputs. It helps me to inspect the scrollback buffer more easily.
It is bound to the fish_postexec
event, so it is printed any time a command
finishes; unless the command was clear
in which case we don’t print it in order
to prevent the line to be visible after clearing.
function __haris_draw_output_separator --on-event fish_postexec
set -l _pipestatus "$fish_pipestatus"
# When clearing the screen using 'clear' (without -x), the output separator
# should not be drawn. Otherwise the terminal would contain a line on the top.
if echo "$argv" | grep -v -- '-x' | grep -qE '^clear($|\s)'
return
end
# Clear any ^C text (due to pressing Ctrl+C to quit the previous process)
if string match -rq 'SIGINT$' (__fish_print_pipestatus '' '' ' ' '' '' $pipestatus)
echo -ne '\b\b'
end
bash -c 'echo -ne "\e[38;5;238m"; printf "%*s\n" "${COLUMNS:-$(tput cols)}" " " | sed "s: :—:g"'
end
Sends a graphical notification when a command running in a non-visible context finishes. This applies to commands that are:
- running in a terminal emulator that is minimized or in a different workspace
- running without a terminal that can be accessed, e.g. launched via dmenu
This applies only to graphical sessions, the linux TTY is unaffected.
function __haris_notify_bg_task_done --on-event fish_postexec
set -l _status "$status"
[ -n "$HARIS_BACKGROUND_TASKS_SILENT" ] && return
[ -n "$DISPLAY" ] || return
if ! type --quiet xdotool
return 0
end
# Check if terminal window is hidden
if xdotool search --all --onlyvisible "" 2>/dev/null | ! grep -q "$WINDOWID"
set -l exit_message
if [ "$_status" = 0 ]
set exit_message "exited successfully"
else
set exit_message "exited with $_status"
end
# Timeout is so the command doesn't hang when daemon is not running
timeout 0.8 notify-send (echo "Command "(history | tac | tail -n +"$__haris_history_count" 2>/dev/null)"" "$exit_message")
if [ "$status" = "124" ]
echo ERR: NOTIFICATION TIMED OUT
end
end
set -g __haris_history_count (history | wc -l)
end
set -l actual_editor
if which emacs >/dev/null
set actual_editor emacs
else
set actual_editor nvim
end
functions edit_command_buffer |
sed -E -e 's/^(function edit_command_buffer)/\1_custom/' \
-e "s/(case.* $actual_editor\b)/\1 visual-editor /" \
| source
function bind_all_modes
for mode in (bind --list-modes)
bind -M "$mode" $argv
end
end
function fish_user_key_bindings # Start bindings
bind_all_modes \el forward-char
bind_all_modes \eh backward-char
bind_all_modes \cp up-or-search
bind_all_modes \cn down-or-search
bind_all_modes \er src_fish
# Some terminals like linux TTY and emacs vterm send Ctrl+P as \cP, so I enable
# it only for alacritty which has been proven to work. I can add other terminals
# here as needed.
if [ "$TERM" = "alacritty" ]
function __haris_toggle_private_mode
set -g fish_private_mode ([ -z "$fish_private_mode" ] && echo true)
commandline -f repaint
end
bind_all_modes \cP __haris_toggle_private_mode
end
bind_all_modes \eL 'clear; commandline -f repaint'
# Copy the current contents of the command line
bind_all_modes \ec 'fish_clipboard_copy'
# Run the current command in bash
bind_all_modes \eb __haris_run_in_bash
# Prepend o in front of current command
bind_all_modes \eo '__haris_cmdline_prepend o'
# Prepend man in front of current command
bind_all_modes \em '__haris_show_man'
# Append --help to the end of the command line and submit
bind_all_modes \eH '__haris_cmdline_append_or_toggle --help'
# Append --version to the end of the command line and submit
bind_all_modes \eV '__haris_cmdline_append_or_toggle --version'
bind_all_modes \et term
bind_all_modes \eg 'dragon (command ls | fzf --multi) >/dev/null 2>&1'
bind_all_modes \ee 'edit_command_buffer_custom'
bind_all_modes \eE 'myemacs-float .'
bind_all_modes \ea 'commandline -i "(adhoc)"'
bind_all_modes \eA 'commandline -i "(adhoc - | string collect)"'
bind_all_modes \eG 'magit'
bind_all_modes \e\cd 'disown'
bind_all_modes \eC 'edocker-compose'
bind_all_modes \eT '__haris_test_eval'
bind_all_modes \e\? '__haris_cmdline_prepend whatis'
bind_all_modes \ey '__haris_copy_cmdline_and_output'
# Ctrl+Alt+L
# TODO: Add sol command installation instructions
bind_all_modes \e\f 'commandline --replace (commandline | sol -b -s -a -jqobj)'
end # End fish_user_key_bindings
function __haris_run_in_bash
set -l cmd (commandline -b)
echo
eval bash -c "'source ~/.bashrc; $cmd'"
commandline -f repaint
commandline -r ''
end
function __haris_cmdline_prepend
commandline --cursor 0
commandline --insert "$argv "
commandline --function end-of-line
end
function __haris_show_man
eman (commandline --current-process --tokenize | grep -v '^sudo$' | head -1)
end
function __haris_cmdline_append_or_toggle -a text
if [ (commandline -o | tail -1) = "$text" ]
commandline -r (commandline | string replace -r -- "\s+$text\s*\$" '')
return
end
commandline --append " $text"
commandline -f execute
end
function __haris_test_eval
eval "$(adhoc - (mktemp -t XXXXXXXX.fish) 2>/dev/null)"
end
function __haris_copy_cmdline_and_output
set -l commandline "$(commandline)"
if string match -qr __haris_copy_cmdline_and_output "$commandline"
echo >&2
echo "Error: __haris_copy_cmdline_and_output is not allowed to be used recursively" >&2
commandline -f repaint
return 1
end
echo
begin
echo "\$ $commandline"
eval "$commandline" 2>&1 | tee /dev/tty
end | fish_clipboard_copy
end
set -U fish_color_command brblue
set -U fish_color_quote brgreen
set -U fish_color_param brcyan
set -U fish_color_autosuggestion brblack
set -U fish_color_cancel -r red
set -U fish_color_error red
set -U fish_color_comment green
set -U fish_color_operator normal
set -U fish_color_redirection brmagenta
set -U fish_pager_color_progress brgreen
set -U fish_pager_color_description green
set -U fish_color_end yellow
jorgebucaran/fisher
edc/bass
oh-my-fish/plugin-pj
PatrickF1/fzf.fish
jorgebucaran/nvm.fish
evanlucas/fish-kubectl-completions
# Fixes some vterm issues
pymander/vfish
brgmnn/fish-docker-compose
oh-my-fish/plugin-pyenv@df70a41
# pj plugin
set -g PROJECT_PATHS ~/proj ~/proj/drytoe
# z.lua
set _ZL_CMD z
if type --quiet lua && [ -f /usr/share/z.lua/z.lua ]
lua /usr/share/z.lua/z.lua --init fish | source
end
set -gx _ZL_CD cd
# tem
if [ "$(type --type tem)" = "function" ]
tem fish-init
end
# fzf bindings
if functions | grep --quiet fzf_configure_bindings
fzf_configure_bindings
else if [ -e ~/.config/fish/conf.d/fzf.fish ]
function __haris_load_fzf --on-event fish_prompt
source ~/.config/fish/conf.d/fzf.fish
functions --erase __haris_load_fzf
end
end
System packages:
fisher
fish
# Dependencies for fzf.fish
fd bat
This code block installs all system dependencies and all plugins in fish based on the plugin list above.
touch ~/.config/fish/{private,tmp}.fish
fish -c "fisher update"
function whatis --wraps type --description "Print what something is, regardless of type" --argument-names what
type "$what"
if [ "$(type --type "$what")" = 'file' ]
catcmd "$what"
end
end
# Create a new dir and cd
function ndir; mkdir -p "$argv"; cd "$argv"; end
# Print first argument
function 1; echo $argv[1]; end
# Run z through fzf
function a
z -l $argv | read -z choices
set -l count (echo "$choices" | sed '/^$/d' | wc -l)
set dest (echo "$choices" | sed '/^$/d' | tac | fzf --select-1)
cd (echo "$dest" | sed -E -e '/^$/d' -e 's/^\S+\s+//')
end
# Open a GUI app and disown
function open; for file in $argv; o xdg-open "$file"; end; end
# Wrapper around imount script so I can cd to the mount directory
function imount
command imount $argv
cd (cat /tmp/imount_directory)
end
# Vim help
function vh; vim -c ":h $argv | only"; end
# Save the path of the argument to the clipboard
function copypath; realpath $argv | xsel -b; end
# When you ls, save the argument so you can quickly cd to that folder.
# It's not fool-proof, but it works in most situations and it's safe.
function ls
if [ -z "$EMACS_VTERM_PATH" ] && type --quiet lsd
# In emacs vterm, lsd outputs additional whitespace which is annoying
lsd --color=auto $argv
else
command ls --color=auto $argv
end
set -g __last_ls_arg "$argv"
end
# cd the last directory you have ls-ed
function cdls
[ -n "$__last_ls_arg" ] && cd "$__last_ls_arg"
end
# vim the last file you have ls-ed
function vils
[ -n "$__last_ls_arg" ] && vim "$__last_ls_arg"
end
function chbg
set -l path (
ls 2>/dev/null \
~/.local/share/backgrounds/"$argv[1]" \
/usr/local/share/backgrounds/"$argv[1]" \
/usr/share/backgrounds/"$argv[1]" |
head -1
)
feh --bg-fill "$path"
rm ~/.wallpaper
ln -s "$path" ~/.wallpaper
end
complete --command chbg --no-files --arguments="(
begin
ls -1 2>/dev/null ~/.local/share/backgrounds
ls -1 2>/dev/null /usr/local/share/backgrounds
ls -1 2>/dev/null /usr/share/backgrounds
end |
sort |
uniq
)"
function vicmd
set -l file (fcmd "$argv");
if [ -f "$file" ]
vim "$file"
else
read -n 1 -P "Create new script? [y/N]: " choice
if [ "$choice" = 'y' ]
myemacs-float ~/.haris/scripts.org
else
echo 'Aborting...'
return 1
end
end
end
# Run a command and disown. Put it into a tmux session. Notify the user when done.
# Persist a shell for a small time so the user can follow up on the command. If
# the user doesn't follow up within 20 minutes, the background shell will exit
# automatically.
function o
set -l __o_args $argv
<<o/argparse>>
set -l _status "$status"
if [ "$_status" != 0 ]
return "$_status"
end
if [ -n "$_flag_h" ]
echo "Run a command in the background in a tmux session so its output can be inspected on-demand."
echo
echo "A GUI notification is sent when the command completes."
echo "After the command completes, plus a certain linger period, the tmux session is killed."
echo "The linger period can be adjusted with --linger-period or disabled with --persist."
echo
echo "Usage: o [OPTIONS] COMMAND..."
echo
echo " -h, --help" \t\t"Print this help message."
echo " -p, --persist" \t"Persist the tmux session until manually killed."
echo " -l, --linger-period" \t"How long the tmux session should linger after the command completes."
echo \t\t\t"Same format as for the sleep command. (default: 20m)"
echo " -s, --silent" \t\t"Do not send a GUI notification when the command completes."
echo " -a, --attach" \t\t"Immediately attach to the created tmux session"
return
end
# The remaining args contain only the command to be run (argparse options have been extracted)
set -l __cmdline (string escape -- $argv)
# The arguments are forwarded to the helper script, so it can parse them
set -l __o_args (string escape -- $__o_args)
set -l tempfile (mktemp)
echo >"$tempfile" "
rm -f '$tempfile'
set HARIS_BACKGROUND_TASKS_SILENT $HARIS_BACKGROUND_TASKS_SILENT;
set __o_args $__o_args;
set __cmdline $__cmdline;"'
<<o/background-task>>
'
set -l tmux_session "$(echo "$__cmdline" | head -c20) [$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 5)]"
tmux new -d \
-s "$tmux_session" \
fish --private -C "$(env | grep -v '\s' | tr '\n' ' ') source $tempfile"
if [ -n "$_flag_attach" ]
tmux attach -t "$tmux_session"
end
end
Test the function by executing this code block:
<<o>>
o --linger-period=8s fish -C '
echo Simulating long command...;
sleep 3s; echo Done.;
echo The terminal window should close in a few seconds;
echo "The exit code (\$status) should be 1!";
false'
alacritty-float -e tmux attach
argparse \
--stop-nonopt \
--exclusive linger-period,persist \
h/help \
p/persist \
l/linger-period= \
s/silent \
a/attach \
-- $__o_args
Note: This code block must not contain any apostrophes!
# Convert o options to local variables
<<o/argparse>>
set linger_period 20m
if [ -n "$_flag_linger_period" ]
set linger_period "$_flag_linger_period"
end
set persist "$_flag_persist"
set silent "$HARIS_BACKGROUND_TASKS_SILENT"
if [ -n "$_flag_silent" ]
set silent "$_flag_silent"
end
functions -e fish_greeting
# Run the command
$__cmdline
set __status "$status"
set -l tmux_session (tmux display-message -p "#S")
if [ -z "$silent" ] && [ -z "$(tmux list-clients -t "$tmux_session")" ]
# Send a notification and wait for it to close. The reason we wait for it is
# because if the user is AFK, then the follow-up shell would exit prematurely.
# This way, if the user has set up notiication persistence while AFK, we
# leverage that feature.
# If a tmux session is already attached, the notification is not displayed.
set -l notif_options
if [ "$__status" != 0 ]; set -a notif_options "--urgency=critical"; end
set -l notif_id_file (mktemp)
# If a client attaches, the notification is no longer necessary and is immediately closed
set -l hook (string escape run-shell "sh -c \"[ -n \"$notif_id_file\" ] && dunstify --close=\$(cat $notif_id_file)\" || true")
tmux set-hook client-attached "$hook"
unbuffer \
notify-send "Background task done" \
"$__cmdline exited with code $__status" \
--wait --print-id $notif_options \
2>/dev/null >"$notif_id_file"
rm -f "$notif_id_file"
end
if [ -z "$persist" ]
# Start a timeout for the shell to close if the user does not perform any
# follow-up commands
sh -c "sleep $linger_period; kill $fish_pid" &
jobs --last --pid | read _timeout_pid
disown
function __haris_on_stop_interaction_timeout --on-event fish_preexec
kill "$_timeout_pid"
functions --erase __haris_on_stop_interaction_timeout
end
end
function __haris_reproduce_status_code
functions --erase __haris_reproduce_status_code
return $__status
end
__haris_reproduce_status_code
complete -c o --wraps command
function enved --argument envvar --description "Edit an environment variable by name"
set tmp (mktemp /tmp/envvar-XXXXXXXXX)
bass echo \$"$envvar" > "$tmp"
"$EDITOR" "$tmp"
bass export "$envvar"=(cat "$tmp")
/usr/bin/rm -f "$tmp"
end
function funcat
type $argv[1] | sed '1,3d;$d' | fish_indent
end
function '^' -d "cd to the first directory in the hierarchy by specified name" -a name
pushd "$PWD"
while [ "$PWD" != "/" ]
if [ (basename "$PWD") = "$name" ]
set -l dir "$PWD"
popd
cd "$dir"
return
end
cd ..
end
popd
return 1
end
complete -c '^' -a '(pwd | tr "/" "\n")' -f
function forever
while :
$argv
sleep 2s
end
end
complete -c forever --wraps command --wraps builtin
function wt --argument-names wt_index subpath
if [ (count $argv) = 1 ] && ! string match -qr '^[0-9]+$' "$wt_index"
set subpath $wt_index
set wt_index
end
set -l worktree_root
if [ -n "$wt_index" ]
set worktree_root (git worktree list | cut -f1 -d' ' | grep "wt$wt_index\$")
else
set worktree_root (git worktree list | fzf | cut -f1 -d' ')
end
if [ -z "$worktree_root" ]
return 1
end
cd "$worktree_root/$subpath"
end
function __haris_complete_wt
set -l args (commandline -op)
set -l git_root (git root)
set -l current_subpath (realpath (pwd) --relative-to "$git_root")/
set -l new_git_root "$git_root"
# The worktree index was provided
if [ (count $args) -gt 1 ]
set -l wt_index {$args[2]}
set new_git_root (git worktree list | cut -f1 -d' ' | grep "wt$wt_index\$")
if [ -z "$new_git_root" ]
return
end
end
if [ -d "$new_git_root/$current_subpath" ]
echo "$current_subpath"
end
set -l arg (commandline -ot)
pushd "$new_git_root"
complete -C"cd $arg"
popd
end
complete -c wt -fk -a '(__haris_complete_wt)'
function wt_setup_compose --argument-names project worktree_index
begin [ -z "$project" ] || [ -z "$worktree_index" ]; end && echo "Usage: wt_setup_compose PROJECT WORKTREE_INDEX" && return 1
set -gx WORKTREE "$worktree_index"
set -gx COMPOSE_PROJECT_NAME "$project$worktree_index"
set -gx EXPOSED_IP (dig +short "$COMPOSE_PROJECT_NAME" | grep -v '^;;')
if [ -z "$EXPOSED_IP" ]
set_color yellow
echo "EXPOSED_IP set to localhost. Consider aliasing $COMPOSE_PROJECT_NAME to a unique IP under the 127.x.x.x subnet in /etc/hosts"
set_color normal
echo
set -gx EXPOSED_IP (dig +short localhost | grep -v '^;;')
end
echo "Variables:"
env | grep -P '^(WORKTREE|COMPOSE_PROJECT_NAME|EXPOSED_IP)=' | cat
end
function cdcf; set -l file (cf "$argv"); test -f "$file" && cd (dirname "$file"); end
function catcf; set -l file (cf "$argv"); test -f "$file" && cat "$file" ; end
function vicf; set -l file (cf "$argv"); test -e "$file" && vim "$file" ; end
function ecf; set -l file (cf "$argv"); test -e "$file" && myemacs-float "$file" ; end
function cdcmd; set -l file (fcmd "$argv"); test -f "$file" && cd (dirname "$file"); end
function catcmd; set -l file (fcmd "$argv"); test -f "$file" && cat "$file" ; end
function ecmd; set -l file (fcmd "$argv"); test -f "$file" && myemacs "$file" ; end
function rmcmd; set -l file (fcmd "$argv"); rm "$file"; end
function eman
if [ -n "$DISPLAY" ] && emacsclient -s emacs --eval nil >/dev/null 2>&1
command eman $argv
else
man $argv
end
end
if type --query uuidgen
function tmp_func
set -g __haris_latest_tmp_func_name "__tmp_func_$(uuidgen | tr '-' '_')"
echo "$__haris_latest_tmp_func_name"
end
end
if type --query tmp_func
function tmp_func_consume_content
funcat "$__haris_latest_tmp_func_name"
functions --erase "$__haris_latest_tmp_func_name"
end
end
Function(s) that will be used by many completions. Completions for the functions defined in Functions.
# Return success if the command line contains no positional arguments
function no_positional_args
set -l -- args (commandline -po) # cmdline broken up into list
set -l -- cmdline (commandline -p) # single string
set -l -- n (count $args) # number of cmdline tokens
for i in (seq 2 $n)
set -l arg $args[$i]
[ -z "$arg" ] && continue # can be caused by '--' argument
# If the the last token is a positional argument and there is no
# trailing space, we ignore it
[ "$i" = "$n" ] && [ (string sub -s -1 "$cmdline") != ' ' ] && break
if string match -rvq '^-' -- "$arg" # doesn't start with -
return 1
end
end
# contains a '--' argument
string match -r -- '\s--\s' "$cmdline" && return 1
return 0
end
complete -c snip -f -a \
"(pushd ~/.vim/snips; command ls | sed 's_\(.*\)\.snippets_\1_g'; popd)"
complete -c adhoc -f \
-a "(complete -C'adsfadadflasdjflasdflnasdflasdu /tmp/adhoc-files/' | string replace /tmp/adhoc-files/ \"\")"
source /usr/share/fish/vendor_completions.d/pass.fish
complete -c pass -a 'add' -n "no_positional_args"
complete -c pass -a '(complete -C "pass show ")' -f -n '__fish_seen_subcommand_from add'
complete -c otp -a '(fd "otp-secret.gpg" ~/.password-store -x echo {//} | sed "s:^.*/\.password-store/\?::")' -f
Some commands provide a completion
subcommand (maybe under a slightly different
name) which generates the completion script for a specific shell. If you source
this script from that shell, you will get completions for that command. In this
section I take advantage of that for some programs.
Each self-bootstrapping completion script should set the source
variable to the
desired final content of the completion script.
[ "$status" != 0 ] && return 1
echo "$source" | tee "$(status filename)" | source
set -l source "$(bufls completion fish 2>/dev/null)"
«completions/self-bootstrapping/common»
set -l source "$(gh completion -s fish 2>/dev/null)"
«completions/self-bootstrapping/common»
# *cf and *cmd style commands
for cmd in {,cd,vi,cat,e}cf
complete --command $cmd --no-files -a '(lscf)'
end
for cmd in {f,cd,vi,cat,e, rm}cmd
complete -c $cmd -f \
-a '(command ls -1 $PATH 2>/dev/null | grep -v "/")'
end
Note: ~/.bashrc
is sourced by fish as well.
PS1='\[\e[1;36m\]\u\[\e[1;31m\]@\[\e[1;34m\]\h \[\e[1;32m\]\W \[\e[1;31m\]\$ \[\e[0;32m\]\[\e[0m\]'
# Shell options
shopt -s extglob
shopt -s autocd
shopt -s globstar
{
bind '"\C-p":previous-history'
bind '"\C-k":previous-history'
bind '"\C-n":next-history'
bind '"\C-j":next-history'
} 2>/dev/null
export SHELL='fish'
export MPD_HOST="localhost"
export MPD_PORT="6601"
if ! [[ "$PATH" =~ ~/\.local/bin ]]; then
export PATH=~/.local/bin:"$PATH"
fi
if which startx >/dev/null; then
alias x='startx'
fi
if which nvim >/dev/null; then
alias vim='nvim'
fi
alias src_fish 'source ~/.config/fish/config.fish'
alias term 'term & disown'
abbr stage 'ndir /tmp/stage-$USER'
if command --quiet docker
alias alpine 'docker run -it --rm --name alpine alpine'
alias debian 'docker run -it --name debian debian:bookworm-slim'
end
function vrg --wraps rg; vim (rg -l $argv); end
function vfd --wraps fd; vim (fd $argv); end
function dragon --wraps dragon-drop --wraps dragon-drag-and-drop
if contains -- --help $argv
command dragon --help
else
o "$(which dragon)" $argv
end
end
Create an abbreviation of a callable (command, function or builting), optionally with arguments, but only if the callable actually exists.
function conditional_abbr
set -l command (echo $argv[-1] | cut -f1 -d' ')
if type --quiet "$command"
_abbr -g $argv
end
end
A wrapper around built-in abbr
that adds the --set-cursor
option, to reduce
verbosity of abbreviation definitions.
function _abbr
builtin abbr --set-cursor $argv
end
_abbr -g rm 'rm -i'
_abbr -g mv 'mv -i'
conditional_abbr -g claer 'clear'
conditional_abbr -g pas 'pass'
conditional_abbr -g gs 'git status'
I also sometimes use Ubuntu, but since I’m so used to pacman, I use the same abbreviations for equivalent apt operations.
function _if_pacman_else
if command --quiet pacman
echo "$argv[1]"
else
echo "$argv[2]"
end
end
if command --quiet pacman || command --quiet apt
_abbr -g p (_if_pacman_else 'pacman' 'apt')
_abbr -g pq (_if_pacman_else 'pacman -Q' 'apt list --installed')
_abbr -g pqq (_if_pacman_else 'pacman -Qq' 'apt list --installed')
_abbr -g pqi (_if_pacman_else 'pacman -Qi' 'dpkg --status')
_abbr -g pql (_if_pacman_else 'pacman -Ql' 'dpkg --listfiles')
_abbr -g pqm (_if_pacman_else 'pacman -Qm')
_abbr -g pqe (_if_pacman_else 'pacman -Qe')
_abbr -g pqo (_if_pacman_else 'pacman -Qo' 'apt-file search --regexp')
_abbr -g pqs (_if_pacman_else 'pacman -Qs')
_abbr -g psi (_if_pacman_else 'pacman -Si' 'apt-cache show')
_abbr -g pss (_if_pacman_else 'pacman -Ss' 'apt search')
_abbr -g pqdtq (_if_pacman_else 'pacman -Qdtq')
_abbr -g sp (_if_pacman_else 'sudo pacman' 'sudo apt')
_abbr -g sps (_if_pacman_else 'sudo pacman -S' 'sudo apt install')
_abbr -g spr (_if_pacman_else 'sudo pacman -R' 'sudo apt remove')
_abbr -g sprq (_if_pacman_else 'sudo pacman -R (pacman -Qdtq)' 'sudo apt autoremove')
end
functions --erase _if_pacman_else
if command --quiet systemctl
_abbr -g ctl 'sudo systemctl'
_abbr -g start 'sudo systemctl start'
_abbr -g stop 'sudo systemctl stop'
_abbr -g en 'sudo systemctl enable'
_abbr -g dis 'sudo systemctl disable'
_abbr -g sts 'systemctl status'
_abbr -g drel 'sudo systemctl daemon-reload'
_abbr -g rel 'sudo systemctl reload'
_abbr -g res 'sudo systemctl restart'
_abbr -g dres 'sudo systemctl daemon-reload --user && systemctl restart --user'
_abbr -g sus 'systemctl suspend'
_abbr -g j 'journalctl -f -u'
_abbr -g ctlu 'systemctl --user'
_abbr -g startu 'systemctl start --user'
_abbr -g stopu 'systemctl stop --user'
_abbr -g enu 'systemctl enable --user'
_abbr -g disu 'systemctl disable --user'
_abbr -g stsu 'systemctl status --user'
_abbr -g drelu 'systemctl daemon-reload --user'
_abbr -g relu 'systemctl reload --user'
_abbr -g resu 'systemctl restart --user'
_abbr -g dresu 'systemctl daemon-reload --user && systemctl restart --user'
_abbr -g ju 'journalctl --user -f -u'
end
if command --quiet docker
set -g __haris_docker docker
else if command --quiet podman
set -g __haris_docker podman
end
if [ -n "$__haris_docker" ]
_abbr -g d "$__haris_docker"
_abbr -g db "$__haris_docker build"
_abbr -g dbn "$__haris_docker build --no-cache"
_abbr -g dr "$__haris_docker run --rm"
_abbr -g drit "$__haris_docker run -it --rm"
_abbr -g drite "$__haris_docker run -it --rm --entrypoint"
_abbr -g deit "$__haris_docker exec -it"
_abbr -g drm "$__haris_docker rm -f"
_abbr -g di "$__haris_docker image"
_abbr -g dil "$__haris_docker image ls"
_abbr -g dip "$__haris_docker image prune"
_abbr -g dii "$__haris_docker image inspect"
_abbr -g dir "$__haris_docker image rm"
_abbr -g dc "$__haris_docker container"
_abbr -g dcl "$__haris_docker container ls"
_abbr -g dcp "$__haris_docker container prune"
_abbr -g dci "$__haris_docker container inspect"
_abbr -g dv "$__haris_docker volume"
_abbr -g dvl "$__haris_docker volume ls"
_abbr -g dvc "$__haris_docker volume create"
_abbr -g dvr "$__haris_docker volume rm"
_abbr -g dvi "$__haris_docker volume inspect"
_abbr -g dvp "$__haris_docker volume prune"
_abbr -g dn "$__haris_docker network"
_abbr -g dnl "$__haris_docker network ls"
_abbr -g dni "$__haris_docker network inspect"
_abbr -g dnp "$__haris_docker network prune"
_abbr -g dnr "$__haris_docker network rm"
_abbr -g dnc "$__haris_docker network connect"
_abbr -g dnd "$__haris_docker network disconnect"
_abbr -g dnC "$__haris_docker network create"
_abbr -g ds "$__haris_docker system"
_abbr -g dsp "$__haris_docker system prune"
_abbr -g dsd "$__haris_docker system df -v"
_abbr -g dm "$__haris_docker manifest"
_abbr -g dmi "$__haris_docker manifest inspect"
_abbr -g dhh "$__haris_docker history"
_abbr -g dl "$__haris_docker logs -f"
# Misc
_abbr -g ENT --position anywhere -- '--entrypoint env%'
end
if [ "$__haris_docker" = "docker" ] &&
begin
docker compose version ||
docker-compose version
end &>/dev/null
_abbr -g D 'docker-compose'
_abbr -g Db 'docker-compose build'
_abbr -g Du 'docker-compose up'
_abbr -g Dub 'docker-compose up --build'
_abbr -g Dud 'docker-compose up -d'
_abbr -g Dubd 'docker-compose up --build -d'
_abbr -g Dubt 'docker-compose up --build --timestamps'
_abbr -g Dr 'docker-compose run --rm'
_abbr -g Drit 'docker-compose run -it --rm'
_abbr -g Dritd 'docker-compose run -it -d --rm'
_abbr -g Dritb 'docker-compose run -it --build --rm'
_abbr -g Dritn 'docker-compose run -it --no-deps --rm'
_abbr -g Dritbn 'docker-compose run -it --build --no-deps --rm'
_abbr -g Drite 'docker-compose run -it --rm --entrypoint'
_abbr -g Ds 'docker-compose start'
_abbr -g DS 'docker-compose stop'
_abbr -g DR 'docker-compose restart'
_abbr -g Dd 'docker-compose down'
_abbr -g Ddo 'docker-compose down --remove-orphans'
_abbr -g Ddv 'docker-compose down --volumes'
_abbr -g Ddov 'docker-compose down --remove-orphans --volumes'
_abbr -g Deit 'docker-compose exec -it'
_abbr -g Drm 'docker-compose rm -sf'
_abbr -g Dl 'docker-compose logs -f'
_abbr -g Dc 'docker-compose config'
# Misc
_abbr -g Dsh 'docker-compose exec -it % sh'
_abbr -g Dbash 'docker-compose exec -it % bash'
_abbr -g Denv 'docker-compose exec -i % env'
_abbr -g Denvr 'docker-compose run --rm -i % env'
_abbr -g Dp 'export COMPOSE_PROFILES=%'
_abbr -g DP 'export COMPOSE_PROJECT_NAME=%'
# Restart using rm + up
if type --query tmp_func
function (tmp_func)
begin
set -l container (docker-compose config --services | fzf)
docker-compose rm -sf "$container"
docker-compose up -d "$container"
end
end
_abbr -g DRR "$(tmp_func_consume_content)"
end
end
if command --quiet pass
conditional_abbr -g pn 'pass insert'
conditional_abbr -g pg 'pass generate --clip'
conditional_abbr -g pe 'pass edit'
conditional_abbr -g pc 'pass show --clip'
conditional_abbr -g pon 'pass otp insert'
end
_abbr -g w0 'watch -n0.1'
_abbr -g w1 'watch -n1'
_abbr -g w2 'watch -n2'
_abbr ufw 'sudo ufw'
_abbr ufws 'sudo ufw status numbered'
_abbr sub 'mosquitto_sub -t'
_abbr pub 'mosquitto_pub -t'
conditional_abbr -g g 'git'
if [ -n "$DISPLAY" ]
conditional_abbr -g e 'myemacs -c'
else
conditional_abbr -g e 'myemacs'
end
conditional_abbr -g E 'myemacs'
conditional_abbr -g s 'sudo'
conditional_abbr -g py 'python'
conditional_abbr -g ipy 'ipython'
conditional_abbr -g copy 'fish_clipboard_copy'
conditional_abbr -g Copy 'tee /dev/tty | fish_clipboard_copy'
conditional_abbr -g C 'tee /dev/tty | fish_clipboard_copy'
conditional_abbr -g CC 'clipctl tmp-disable | fish_clipboard_copy'
conditional_abbr -g paste 'xsel -b -o'
conditional_abbr -g oct 'octave'
conditional_abbr -g octb 'OCTAVE_BASIC=true command octave'
conditional_abbr -g va 'vagrant'
conditional_abbr -g M 'sudo mount -o uid=(id -u),gid=(id -g) /dev/%'
conditional_abbr -g u 'fusermount -u'
conditional_abbr -g um 'fusermount -u ~/mnt/%'
conditional_abbr -g U 'sudo umount'
conditional_abbr -g Um 'sudo umount ~/mnt/%'
conditional_abbr -g UM 'sudo umount ~/mnt/%'
conditional_abbr -g cmd 'command'
conditional_abbr -g w 'which'
conditional_abbr -g P "pgrep -af"
conditional_abbr -g Pu "pgrep -afu "(whoami)
conditional_abbr -g yt 'ytfzf -t -s'
conditional_abbr -g t 'tem'
conditional_abbr -g v 'vim'
conditional_abbr -g vf 'vim (fzf)'
conditional_abbr -g fm 'vifm'
conditional_abbr -g fb 'facebook-cli'
conditional_abbr -g fl 'flameshot'
conditional_abbr -g ff 'firefox'
conditional_abbr -g tb 'nc termbin.com 9999'
conditional_abbr -g asc 'asciinema'
conditional_abbr -g hk 'heroku'
conditional_abbr -g mhc 'man http-codes'
conditional_abbr -g rgh 'rg --hidden'
conditional_abbr -g px 'ping x'
conditional_abbr -g ii 'curl ipinfo.io'
conditional_abbr -g bt 'bluetoothctl'
conditional_abbr -g sshk 'ssh-copy-id -i ~/.ssh/%'
conditional_abbr -g sshkm 'ssh-copy-id -i ~/.ssh/main.pub'
_abbr -g ek 'sudo etckeeper'
if [ "$TERM" != "linux" ]
conditional_abbr -g man 'eman'
end
conditional_abbr -g R 'reset'
conditional_abbr -g pa 'printarg'
conditional_abbr -g hosts 'myemacs -c /etc/hosts'
_abbr -g WT 'export WORKTREE=%'
_abbr -g sni 'sudo npm install -g'
_abbr -g c 'cut -d " " -f'
_abbr -g .. 'cd ../%'
_abbr -g ... 'cd ../../%'
conditional_abbr -g gce 'gcloud compute'
# Tmux
conditional_abbr -g x 'tmux'
if type --query tmp_func
function (tmp_func)
tmux attach -t (tmux list-sessions -F "#{session_created} #{session_name}" | sort | tac | grep -Po "(?<= ).*" | fzf -s)
end
conditional_abbr -g X "$(tmp_func_consume_content)"
end
# VirtualBox
conditional_abbr -g vb 'vboxmanage'
functions --erase conditional_abbr
set aur '[email protected]'
if [ -f ~/.config/fish/local.fish ]
source ~/.config/fish/local.fish
end
if [ -f ~/.config/fish/tmp.fish ]
source ~/.config/fish/tmp.fish
end
if [ -w "$XDG_RUNTIME_DIR/ephemeral.config.fish" ]
source "$XDG_RUNTIME_DIR/ephemeral.config.fish"
end
# Clipmenu
# (ref:CM_DIR)
CM_DIR=$HOME/.local/share/haris/clipmenu
RCLONE_PASSWORD_COMMAND="pass rclone/config"
VAGRANT_HOME=/usr/local/share/haris/vagrant
Make the environment variables available for shells as well:
eval "$(cat ~/.config/environment.d/90-haris.conf | grep -vP '^\s*#' | sed 's/^/export /')"
mkdir -p ~/.go
export GOPATH="$HOME"/.go
export PATH="$CLOUDSDK_ROOT_DIR"/bin:"$PATH"
export PATH=~/.go/bin:"$PATH"
gempath="$(gem environment gempath)"
if [ -n "$gempath" ]; then
export PATH="$(echo "$gempath" | sed -E 's_(:|$)_/bin:_g')$PATH"
fi
export PATH=~/vm/.tem/path:"$PATH"
export PATH=~/.local/bin:"$PATH"
systemctl start --user ssh-agent.service
eval "$(ssh-agent)" >/dev/null
export MAKEFLAGS='-j6'
export GPG_TTY=$(tty)
export PYTHONSTARTUP=~/.startup.py
export RUSTC_WRAPPER=sccache
export MOZ_USE_XINPUT2=1
[ "$(get-os-type)" = "arch" ] && export QT_QPA_PLATFORMTHEME=gtk2
# If rootless docker is used, configure the DOCKER_HOST variable
if systemctl is-enabled --user --quiet docker.service; then
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
fi
# Better pager for psql command
if which pspg >/dev/null 2>&1; then
# '-s 5' chooses the mutt theme
export PSQL_PAGER='pspg -s 5'
fi
mkdir -p /tmp/stage-"$USER"
if [ -f ~/.local.profile ]; then . ~/.local.profile; fi
These are optional and must be provided separately.
Local configurations that I don’t want to keep under version control. Click to edit.
Temporary configurations that I don’t want to keep under version control, but also don’t want to use for a long time. Mostly useful for debugging. Click to edit
Configuration that lives until reboot. Click to edit
Code block that is evaluated in order to edit the ephemeral configuration file.
(spacemacs/find-file-split
(format "%s/ephemeral.config.fish"
(getenv "XDG_RUNTIME_DIR" (selected-frame))))
dash fish