To help me figure this out, I use a few shell functions. Firstly, I need to know what configuration file that was used.

function shell_init_file() { # Returns what would be your initfile
  if [[ $- == *i* ]]; then
    echo ~/.bashrc
  elif [[ -f ~/.bash_profile ]]; then
    echo ~/.bash_profile
  elif [[ -f ~/.bash_login ]]; then
    echo ~/.bash_login
  elif [[ -f ~/.profile ]]; then
    echo ~/.profile
  else
    echo "Could not find any config files.."
    return 1
  fi
}

This function checks if we’re in an interactive shell or not, and returns bashrc if we are. If not it checks for the various login files.

Thats all great, but now we need to figure out what files have been sourced from that first file.

function _sourced_files(){ # Helper for sourced_files
  sed -En 's/^[.|source]+ (.*)/\1/p' "$1" | while IFS= read -r f; do
    expanded=$(echo ${f/#\~/$HOME} | envsubst | tr -d '"')
    echo "$expanded"
    _sourced_files "$expanded"
  done
}

function sourced_files() { # Lists files which (s/w)hould have been sourced to this shell
  init_file=$(shell_init_file)
  echo "$init_file"
  _sourced_files "$init_file"
}

The function sourced_files is just a wrapper that checks the initial config file, and then calls _sourced_files to check what files are then being sourced etc..

Now we have some kind of idea about what is being configured in our shell. You might have noticed that I add # some description after my function definitions? This is so I can get these two nifty helpers:

alias halp='echo -e "Sourced files:\n$(sourced_files | sed "s#$HOME/#~/#")\n # \nFunctions:\n$(list_functions)\n # \nAliases:\n\n$(list_aliases)" | column -t -s "#"' # Show all custom aliases and functions

This is simply an alias that will list my sourced files, and show all functions, aliases and their description.

function _wat() { # Completion for wat
  local cur words
  _get_comp_words_by_ref cur
  words=$(list_aliases; list_functions | cut -d ' ' -f 1)
  COMPREPLY=( $( compgen -W "$words" -- "$cur") )
}

complete -o nospace -F _wat wat

function wat() { # show help and location of a custom function or alias
  local query pp
  query="$1"
  pp="cat"
  if [[ -n "$(type bat 2> /dev/null)" ]]; then
    pp="bat -l bash -p"
  fi

  for file in $(sourced_files); do
    awk '/^function '"$query"'\(\)/,/^}/ { i++; if(i==1){print "# " FILENAME ":" FNR RS $0;} else {print $0;}}' "$file"
    awk '/^function \_'"$query"'\(\)/,/^}/ { i++; if(i==1){print "# " FILENAME ":" FNR RS $0;} else {print $0;}}' "$file"
    awk '/^alias '"$query"'=/,/$/ {print "# " FILENAME ":" FNR RS $0 RS;}' "$file"
  done | $pp
  complete -p "$query" 2> /dev/null
}

So after looking at what helpers I have available using halp I can use wat to show me exactly where it’s defined, what it looks like and how it’s auto completed. wat also has auto completion through the _wat function. A sample output would look like this:

$> wat list_aliases
# /Users/brujoand/src/brujoand/dotfiles/bash/function.bash:66
function list_aliases() { # List all sourced aliases
  for f in $(sourced_files); do
    sed -n "s/^alias \(.*\)=['|\"].*#\(.*\)$/\1 #\2/p" "$f" | sed "s/list_aliases=.*#/list_aliases #/"
  done | sort
}

$> wat list_functions
# /Users/brujoand/src/brujoand/dotfiles/bash/function.bash:60
function list_functions() { # List all sourced functions
  for f in $(sourced_files); do
    sed -n "s/^function \(.*\)() { \(.*\)$/\1 \2/p" <(cat "$f") | grep -v "^_"
  done | sort
}

Oh, and this output is pretty printed using bat, it’s super nice :) Unfortunately I haven’t found a great way of pretty printing code on this blog though :/ (ha, finally figured it out. Pygments powered syntax highlighting)

Now, the final touch. I can figure out what helpers I have, where they are defined, and what they do. Now I’d like to edit the config file containing some helper. Enter esc, which auto completes to my sourced files, and opens it with whatever I’ve set as my $EDITOR.

function esc() { # Edit a shell config file
  local file
  file=$(grep "/$1$" <(sourced_files))
  "${EDITOR:-vi}" "$file"
}

function _esc() { # Fuzzy tabcompletion for esc
  local cur config_files
  _get_comp_words_by_ref cur
  config_files=$(for file in $(sourced_files); do echo "${file##*/}"; done)

  if [[ -z "$cur" ]]; then
    COMPREPLY=( $( compgen -W "$config_files" ) )
  else
    COMPREPLY=( $(grep -i "$cur" <<< "$config_files" ) )
  fi
}
complete -o nospace -F _esc esc

Discussion: reddit.com/r/bash - post