#!/usr/bin/env bash # --- # @file_name: prompt.sh # @version: 1.0.1 # @description: a prompt # @author: Jamie Albert (empty_produce) # @author_contact: # @license: GNU Affero General Public License v3.0 (Included in LICENSE) # Copyright (C) 2025, Jamie Albert # --- declare -g _cached_uh="" declare -g _uh_cache_key="" declare -g _cached_pyenv_version="" declare -g _pyenv_cache_key="" declare -g _cached_readonly="" declare -g _readonly_cache_key="" # --- # Shell options configuration # --- shopt -s histappend shopt -s cmdhist shopt -s histreedit shopt -s histverify shopt -s interactive_comments shopt -s checkwinsize shopt -s globstar shopt -s nocaseglob shopt -s dirspell shopt -s cdspell shopt -u autocd if [[ $- =~ "i" ]]; then bind "set completion-ignore-case on" bind "set show-all-if-ambiguous on" bind "set mark-symlinked-directories on" fi # --- # * Symbols # --- _prompt_symbol="❯" _git_symbol="git:" _pyenv_symbol="pyenv:" _readonly_symbol="ro:" _screen_symbol="scr:" # --- # * Colours # --- _prompt_success_colour="142" _prompt_fail_colour="203" _prompt_user_colour="109" _prompt_sudo_colour="208" _prompt_at_colour="249" _prompt_hostname_colour="132" _prompt_info_colour="172" _prompt_input_colour="254" _prompt_dir_colour="249" _git_module_colour="66" _pyenv_module_colour="66" _readonly_module_colour="196" _screen_module_colour="33" # --- # * Miscellaneous # --- _prompt_hostname="ssh" _prompt_separator=" " _prompt_wrapper="[]" # --- # // * Nerd Symbols # // OBSOLETE 2025-11-07 # --- # _git_symbol="󰘦" # _pyenv_symbol="󰌠" # _readonly_symbol="󰍁" # _screen_symbol="󰐅" # _prompt_symbol="󰥭" # --- # * Aliases # --- alias count='find . -type f | wc -l' alias cdd='cd /home/jamie/dao' alias dnf='sudo dnf' alias hist='history|grep' alias ll="ls -laFh" alias la='ls -A' alias li='la | grep -i' alias lh='ll -d .*' alias mkdir='mkdir -p' alias nano='nano -W' alias qq='exit' alias sc='sudo systemctl' alias xclipc='xclip -sel c' # --- # * Environment settings # --- declare -g HISTTIMEFORMAT='%F %T ' declare -g PROMPT_DIRTRIM="2" [[ -z "$LC_CTYPE" && -z "$LC_ALL" ]] && declare -g LC_CTYPE="${LANG%%:*}" [[ -z "$HISTFILE" ]] && declare -g HISTFILE="$HOME/.bash_history" declare -x HISTCONTROL=ignorespace declare -x HISTFILESIZE=10000 declare -x HISTSIZE=${HISTFILESIZE} # --- # * colour function # --- _prompt_colour() { [[ ${1} != "-" ]] && echo -ne "\[\033[38;5;${1}m\]" || echo -ne "\[\033[0m\]" } # --- # * Generate prompt segments # --- _prompt_generate() { declare _separator="" _text="${1%%|*}" _colour="${1##*|}" [[ ${_prompt_seg} -gt 1 ]] && _separator="${_prompt_separator}" _prompt_information+="${_separator}$(_prompt_colour "${_colour}")${_text}\[\e[0m\]" ((_prompt_seg++)) } # --- # * Screen # --- _screen_pre() { [[ ${TERM} == "screen"* ]] && _prompt_generate "${_screen_symbol}|${_screen_module_colour}" } # --- # * Git # --- _git_post() { [[ -z "$_git_dir" ]] && _git_dir="$(git rev-parse --git-dir 2>/dev/null)" [[ -n "$_git_dir" ]] || return local _unsafe_ref _unsafe_ref=$(git symbolic-ref -q HEAD 2>/dev/null) || return local _clean_ref="${_unsafe_ref##refs/heads/}" _clean_ref="${_clean_ref//[^a-zA-Z0-9\/]/-}" local _status="" [[ -n "$(git status --porcelain 2>/dev/null)" ]] && _status="*" [[ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]] && _status="${_status}+" [[ -n ${_clean_ref} ]] && _prompt_generate "${_git_symbol} ${_clean_ref}${_status}|${_git_module_colour}" } # --- # * Pyenv # --- _pyenv_post() { [[ -n ${VIRTUAL_ENV} ]] || return [[ "$VIRTUAL_ENV" != "$_pyenv_cache_key" ]] && { _cached_pyenv_version="${PYTHON_VERSION}" [[ -z ${_cached_pyenv_version} ]] && _cached_pyenv_version="$(python3 --version 2>/dev/null)" _cached_pyenv_version="${_cached_pyenv_version#Python }" _pyenv_cache_key="$VIRTUAL_ENV" } [[ -n ${_cached_pyenv_version} ]] && _prompt_generate "${_prompt_wrapper:0:1}${_pyenv_symbol} ${_cached_pyenv_version}${_prompt_wrapper:1:1}|${_pyenv_module_colour}" } # --- # * Readonly # --- _readonly_post() { [[ "$PWD" != "$_readonly_cache_key" ]] && { _cached_readonly="" [[ ! -w "$(pwd)" ]] && _cached_readonly="readonly" _readonly_cache_key="$PWD" } [[ -n "$_cached_readonly" ]] && _prompt_generate "${_prompt_wrapper:0:1}${_readonly_symbol}${_prompt_wrapper:1:1}|${_readonly_module_colour}" } # --- # * Build the main prompt # --- _prompt_build() { declare _last_status="$?" _gen_uh _cache_key="${PWD}" declare -gi _prompt_seg=1 [[ "$_cache_key" != "$_last_cache_key" ]] && { unset _git_dir _cached_pyenv_version _last_cache_key="$_cache_key" } # --- # ! Insert *pre* functions here # --- # START _screen_pre # END # --- # * User and hostname # --- declare _user_colour="${_prompt_user_colour}" sudo -nv 2>/dev/null && _user_colour="${_prompt_sudo_colour}" _gen_uh="${_cached_uh}" _uh_cache_key="${_prompt_hostname}:${SSH_CLIENT:-}:$(sudo -n -v 2>/dev/null && echo 'sudo' || echo 'nosudo')" [[ "$_uh_cache_key" != "$_last_uh_key" ]] && { _gen_uh="$(_prompt_colour "${_user_colour}")${USER}" if [[ ${_prompt_hostname} == "all" ]] || [[ ${_prompt_hostname} == "ssh" && -n ${SSH_CLIENT} ]]; then _gen_uh+="$(_prompt_colour "${_prompt_at_colour}")@$(_prompt_colour "${_prompt_hostname_colour}")${HOSTNAME}" fi _gen_uh+="\[\033[0m\]" _cached_uh="$_gen_uh" _last_uh_key="$_uh_cache_key" } _prompt_generate "${_gen_uh}|${_prompt_info_colour}" # --- # * Current directory # --- declare _current_dir="${PWD}" _current_dir="${_current_dir/#$HOME/\~}" _prompt_generate "$(_prompt_colour "${_prompt_info_colour}")${_prompt_wrapper:0:1}$(_prompt_colour "${_prompt_dir_colour}")${_current_dir}$(_prompt_colour "${_prompt_info_colour}")${_prompt_wrapper:1:1}\[\033[0m\]|${_prompt_info_colour}" # --- # * Error status # --- [[ ${_last_status} -ne 0 ]] && _prompt_generate "${_prompt_wrapper:0:1}${_last_status}${_prompt_wrapper:1:1}|${_prompt_fail_colour}" # --- # ! Insert *post* functions here # --- # START _git_post _pyenv_post _readonly_post # END # --- # *Final prompt symbol # --- _prompt_status_colour=${_prompt_success_colour} [[ ${_last_status} -ne 0 ]] && _prompt_status_colour=${_prompt_fail_colour} _prompt_information+="$(_prompt_colour "${_prompt_status_colour}")\n${_prompt_symbol}\[\e[0m\]" PS1="${_prompt_information} " unset _prompt_information _prompt_seg } # --- # * Initialize prompt # --- _prompt_init() { case "$PROMPT_COMMAND" in *"_prompt_build"*) ;; "") PROMPT_COMMAND="_prompt_build" ;; *) PROMPT_COMMAND="_prompt_build;${PROMPT_COMMAND}" ;; esac } # --- # * Initialize when sourced # --- [[ ${BASH_SOURCE[0]} != "${0}" ]] && _prompt_init