dot-bashrc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. # This file is sourced by all *interactive* bash shells on startup, including
  2. # some apparently interactive shells such as scp and rcp that can't tolerate any
  3. # output. So make sure this doesn't display anything or bad things will happen.
  4. # Test for an interactive shell. There is no need to set anything past this
  5. # point for scp and rcp, and it's important to refrain from outputting anything
  6. # in those cases.
  7. if [[ $- != *i* ]] ; then
  8. # Shell is non-interactive. Be done now!
  9. return
  10. fi
  11. #------------------------------------------------------------------------------
  12. # If not running tmux, attach to an existing session or launch one
  13. if [[ $TERM == xterm* ]] ; then
  14. tmux attach || tmux
  15. fi
  16. #------------------------------------------------------------------------------
  17. # Enviroment variables
  18. #------------------------------------------------------------------------------
  19. EDITOR=vim
  20. # Remove message from lesspipe.sh
  21. export LESSQUIET=1
  22. # Guard against npm messing with portage files.
  23. # Also, allows 'npm install -g' without root.
  24. export NPM_CONFIG_PREFIX=$HOME/.local/
  25. export PATH="/home/$USER/go/bin:/home/$USER/.local/bin:$NPM_CONFIG_PREFIX/bin:$PATH"
  26. # Add ~/.local/bin to PATH
  27. export PATH="$HOME/.local/bin:$PATH"
  28. # Ripgrep config path
  29. export RIPGREP_CONFIG_PATH=~/.ripgreprc
  30. # Android configuration
  31. export ANDROID_HOME="${HOME}/.android-tooling"
  32. export PATH="${PATH}:${ANDROID_HOME}/platform-tools";
  33. export PATH="${PATH}:${ANDROID_HOME}/cmdline-tools/bin";
  34. #------------------------------------------------------------------------------
  35. # Aliases, functions and misc configurations
  36. #------------------------------------------------------------------------------
  37. # Aliases
  38. #------------------------------------------------------------------------------
  39. # Coreutils
  40. alias ll='ls -l'
  41. alias la='ls -a'
  42. alias lla='ls -al'
  43. alias egrep='grep -E'
  44. alias fgrep='grep -F'
  45. alias Pgrep='grep -P'
  46. alias gad='git add . && git commit'
  47. # Push and pull to notabug repository using passgit. Merely add 'push' or 'pull'
  48. # to the end of string
  49. alias notabug='passgit -n token_notabug'
  50. # Use eman for showing manual pages
  51. alias m='eman'
  52. # Gentoo specific
  53. alias update='doas emerge-webrsync && doas emerge -auD @world && doas emerge -c'
  54. alias equ="equery u"
  55. alias eqh="equery h"
  56. # Start the X server from any tty as a normal user
  57. alias startx="startx -- vt$(tty | sed -e 's|/dev/tty||')"
  58. # Default stow usage to managing dotfiles
  59. alias stow='stow --dotfiles'
  60. # Dowload music playlists with format '$index-title.format'
  61. alias dl_album='yt-dlp -f "ba" --audio-quality 10 -o "%(playlist_index)s-%(title)s.%(ext)s"'
  62. # Opens mixer TUI for pulseaudio or alsa
  63. alias mix='[[ $(type pulsemixer 2>/dev/null) ]] && pulsemixer || alsamixer'
  64. # Misc
  65. alias pdoc='perldoc'
  66. alias e=$EDITOR
  67. # Surfraw aliases
  68. alias mr="sr marginalia"
  69. alias yt="sr invidious"
  70. alias go="sr google"
  71. alias g="sr duckduckgo"
  72. # Functions
  73. #------------------------------------------------------------------------------
  74. #------------------------------------------------------------------------------
  75. # Simple functions
  76. #------------------------------------------------------------------------------
  77. # Fork zathura by default
  78. zat() { zathura --fork "$@" & disown ;}
  79. #-------------------------------------------------------------------------------
  80. # Wrapper around some REPLS
  81. # requires:
  82. # - rlwrap
  83. # Currently supported: perl, node
  84. #-----------------------------------------------------------------------------
  85. replwrap() {
  86. ! command -v rlwrap &>/dev/null &&\
  87. echo "${FUNCNAME[-1]}: rlwrap not found in PATH" &&\
  88. return 1
  89. local usage="$(cat <<-HELP_MSG
  90. Usage: repl [OPTION]
  91. Simple interface for launching some repls.
  92. perl Launches a perl repl
  93. node Launches a node repl
  94. HELP_MSG
  95. )"
  96. (($# != 1)) && echo "$usage" && return 1
  97. case "${1}" in
  98. perl)
  99. rlwrap -pBlack -A -S "$ " perl -wnE 'say eval()//$@'
  100. shift
  101. ;;
  102. node)
  103. NODE_NO_READLINE=1 rlwrap node
  104. shift
  105. ;;
  106. *) echo "${FUNCNAME[-1]}: Invalid option '$1'" && return 1 ;;
  107. esac
  108. }
  109. #-------------------------------------------------------------------------------
  110. # Watch typescript files
  111. # Args: files to test
  112. # TODO:
  113. # - Try to find ts files to watch if none are supplied
  114. #-----------------------------------------------------------------------------
  115. watchts() {
  116. if [[ -n "$@" ]] ; then
  117. compiled -s -c "make && node ${@//ts/js}"
  118. else
  119. echo "Please supply a file to be watched."
  120. return 1
  121. fi
  122. }
  123. #-------------------------------------------------------------------------------
  124. # Cleans up file names to make them more command-line friendly
  125. # Args: files to be renamed
  126. #-------------------------------------------------------------------------------
  127. norm_name() {
  128. for arg in "$@" ; do
  129. local name=$arg
  130. local ext=${name##*.}
  131. name=${name%.*}
  132. name=${name//[\{(\[]*[\})\]]/}
  133. name=${name@L}
  134. name=${name//[[:blank:]]/_}
  135. name+=.$ext
  136. name=${name/[[:blank:]]\./\.}
  137. name=${name/_\./\.}
  138. mv "$arg" "$name"
  139. done
  140. }
  141. #-------------------------------------------------------------------------------
  142. # Open man pages as HTML inside elinks. Each page will be opened in its own tab.
  143. #-------------------------------------------------------------------------------
  144. eman() {
  145. # Dir we are storing the converted files
  146. local -r base_dir='/tmp/autoless'
  147. local -r convert='groff -spte -mandoc -Thtml'
  148. local -a pages_arr
  149. mkdir $base_dir 2>/dev/null
  150. # Treat arguments as potential pages
  151. for page in "$@" ; do
  152. # Find the path to the page and format its name
  153. local page=$(man -w $page)
  154. (($? != 0)) && continue # Skip missing pages
  155. # Format the page name
  156. local page_base=$(basename $page)
  157. local page_path=$base_dir/${page_base/%.*/.html}
  158. pages_arr+=($page_path)
  159. # Convert to HTML
  160. if [[ $page_base == *bz2* ]] ; then
  161. bzcat $page | $convert >${page_path} 2>/dev/null
  162. else
  163. # Handle uncompressed files
  164. $convert $page >${page_path} 2>/dev/null
  165. fi
  166. done
  167. elinks -force-html ${pages_arr[@]}
  168. # Cleaunp
  169. rm -rf $base_dir
  170. }
  171. #-------------------------------------------------------------------------------
  172. # Shorten the PS1 directory names in the prompt.
  173. # Note: This is needs to be hooked on the cd built-in and be called in main().
  174. # TODO: Make it more portable, currently it hardcodes a style for the prompt.
  175. #-----------------------------------------------------------------------------
  176. shorten_PS1() {
  177. local shorten_depth=3
  178. local path="$(pwd)"
  179. [[ "$path" == *"$HOME"* ]] && path="~${path#$HOME}"
  180. depth="#${path//[^\/]}"
  181. depth="${#depth}"
  182. if [[ $depth -ge $shorten_depth ]] ; then
  183. newpath=""
  184. OLDIFS=$IFS
  185. IFS=/
  186. i=1
  187. for dir in $path ; do
  188. if [[ $i -eq $depth ]] ; then
  189. newpath+=$dir
  190. break
  191. fi
  192. char=${dir:0:1}
  193. [[ $char == '.' ]] && char+=${dir:1:1}
  194. newpath+=$char/
  195. ((i++))
  196. done
  197. IFS=$OLDIFS
  198. path=$newpath
  199. fi
  200. PS1="\[\e[30;0;1m\]$path $\[\e[0m\] "
  201. }
  202. #-----------------------------------------------------------------------------
  203. # Hack to silence autocd
  204. # Note: Call silence_autocd in main()
  205. #-----------------------------------------------------------------------------
  206. silence_autocd() {
  207. exec {BASH_XTRACEFD}>/dev/null
  208. }
  209. # There are situations that it needs to be unsilenced
  210. unsilence_autocd() {
  211. exec {BASH_XTRACEFD}>/dev/stdout
  212. }
  213. # Custom set
  214. set() {
  215. # if calling "set -x", undo the silencing of autocd
  216. if [[ "$#" == 1 && "$1" == "-x" ]]; then
  217. command set -x;
  218. unsilence_autocd;
  219. elif [[ "$#" == 1 && "$1" == "+x" ]]; then
  220. silence_autocd;
  221. command set +x;
  222. else
  223. command set "$@";
  224. fi;
  225. }
  226. #-----------------------------------------------------------------------------
  227. # Changes to built-in commands
  228. #-----------------------------------------------------------------------------
  229. cd() {
  230. builtin cd $@
  231. shorten_PS1
  232. ls
  233. }
  234. # Completion set-up
  235. #------------------------------------------------------------------------------
  236. # doas completion
  237. # Requires 'bash-completion'
  238. complete -F _root_command doas
  239. # Use 'man' completion on with 'eman' and 'm' alias
  240. source /usr/share/bash-completion/completions/man
  241. complete -F _comp_cmd_man eman m
  242. #------------------------------------------------------------------------------
  243. # Binding scripts
  244. #------------------------------------------------------------------------------
  245. # Surround
  246. # TODO:
  247. # - Error handling
  248. #------------------------------------------------------------------------------
  249. # Automatically surround text with passed characters. Meant to be bound to the
  250. # character themselves.
  251. # $1: 'all' or 'word'
  252. # surround only a single word or all the text suceding
  253. # cursor.
  254. # $2: opening character
  255. # $3: closing character
  256. _surround() {
  257. local line=$READLINE_LINE point=$READLINE_POINT
  258. local -r action=$1 open=$2 close=$3
  259. local -r start_to_cursor=${line:0:$point} cursor_to_end="${line:$point}"
  260. if [[ -z "$cursor_to_end" ]] ; then
  261. line="$line$open$close"
  262. point=$((${#line} - 1))
  263. elif [[ "${line:$point:1}" == ' ' ]] ; then
  264. line="$start_to_cursor$open$close$cursor_to_end"
  265. ((point++))
  266. else
  267. case $action in
  268. all)
  269. line="$start_to_cursor$open$cursor_to_end$close"
  270. point=$((${#line} - 1))
  271. ;;
  272. word)
  273. local word=${cursor_to_end% *}
  274. line="$start_to_cursor${cursor_to_end/#$word/$open$word$close}"
  275. point=$(($point + ${#word} + 1))
  276. ;;
  277. esac
  278. fi
  279. READLINE_LINE=$line
  280. READLINE_POINT=$point
  281. }
  282. # Bindings using the '_surround' function
  283. bind -m vi-insert -x '"(":"_surround all \( \)"'
  284. bind -m vi-insert -x '"\"":"_surround all \" \""'
  285. bind -m vi-insert -x "\"'\": \"_surround all \' \'\""
  286. bind -m vi-insert -x '"{":"_surround word \{ \}"'
  287. #-----------------------------------------------------------------------------
  288. # FZF
  289. #-----------------------------------------------------------------------------
  290. # Obsolete since version 0.48.0
  291. #source /usr/share/fzf/{completion.bash,key-bindings.bash}
  292. # Set up fzf key bindings and fuzzy completion
  293. eval "$(fzf --bash)"
  294. # Force __fzf_cd__ to use my modified cd (defined in .bashrc)
  295. __fzf_cd__() {
  296. local dir
  297. dir=$(
  298. FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \
  299. FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m
  300. ") \
  301. FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd)
  302. ) && printf 'cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
  303. }
  304. # Alt-c binding respects gitignore
  305. export FZF_ALT_C_COMMAND="fd --ignore-file ~/.gitignore --hidden --follow --type directory"
  306. # General styling. Open in a tmux popup, otherwise use heigh mode
  307. export FZF_DEFAULT_OPTS='--height 40% --tmux bottom,40% --border top
  308. --color=fg:black,fg+:white,bg:#ffffd7,bg+:black
  309. --color=hl:black:bold,hl+:bright-blue,info:black,marker:white
  310. --color=prompt:black:regular,spinner:black,pointer:white,header:#727272
  311. --color=border:black,preview-label:black,label:black,query:black:regular
  312. --preview-window="border-sharp" --prompt=">" --marker=">" --pointer=">"
  313. --separator="─" --scrollbar="│" --layout="reverse"'
  314. # Use fd to generate path
  315. _fzf_compgen_path() {
  316. fd --ignore-file ~/.gitignore --hidden --follow --exclude ".git" . "$1"
  317. }
  318. # Use fd to generate directory list
  319. _fzf_compgen_dir() {
  320. fd --type d --ignore-file ~/.gitignore --hidden --follow --exclude ".git" . "$1"
  321. }
  322. # Commands that should work with **<TAB>
  323. _fzf_setup_completion path mkdir vim zat mpv e
  324. # Rebind fuzzy search on history
  325. bind -x '"\C-h": __fzf_history__'
  326. bind -x '"\C-f": fzf_with_rg'
  327. # Workaround Alt-C conflict in tmux
  328. bind '"\ec": nop'
  329. bind -m emacs-standard '"\C-r": " \C-b\C-k \C-u$(__fzf_cd__)\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
  330. bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
  331. bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
  332. # Switch between Ripgrep mode and fzf filtering mode with C-f
  333. fzf_with_rg() {
  334. rm -f /tmp/rg-fzf-{r,f}
  335. # Color customization is in ~/.ripgreprc
  336. local RG_PREFIX="rg --column --color=always "
  337. local INITIAL_QUERY="${*:-}"
  338. fzf --ansi --disabled --query "$INITIAL_QUERY" \
  339. --bind "start:reload:$RG_PREFIX {q}" \
  340. --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
  341. --bind 'ctrl-f:transform:[[ ! $FZF_PROMPT == ripgrep* ]] &&
  342. echo "rebind(change)+change-prompt(1. ripgrep> )+disable-search+transform-query:echo \{q} > /tmp/rg-fzf-f; cat /tmp/rg-fzf-r" ||
  343. echo "unbind(change)+change-prompt(2. fzf> )+enable-search+transform-query:echo \{q} > /tmp/rg-fzf-r; cat /tmp/rg-fzf-f"' \
  344. --color "hl:-1:underline,hl+:-1:underline:reverse" \
  345. --prompt 'ripgrep> ' \
  346. --delimiter : \
  347. --header 'C-f: Switch between ripgrep/fzf' \
  348. --bind 'enter:become(vim {1} +{2})'
  349. }
  350. #-----------------------------------------------------------------------------
  351. # Main
  352. #-----------------------------------------------------------------------------
  353. main() {
  354. silence_autocd
  355. shopt -s autocd
  356. shorten_PS1 # Shorten PS1 on startup
  357. }
  358. main