From c3c680ecca847b8abf0b96fea4c1e4fe9c7dd26e Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 27 Oct 2026 13:13:29 +0000 Subject: [PATCH] update --- README.md | 36 +++- aliases.bash | 21 ++ bashrc | 359 +------------------------------ bashrc.d/00-core.bash | 29 +++ bashrc.d/10-prompt.bash | 43 ++++ bashrc.d/20-greeting.bash | 75 +++++++ bashrc.d/30-integrations.bash | 16 ++ bashrc.d/40-status.bash | 90 ++++++++ bashrc.d/50-completions.bash | 7 + bashrc.d/60-directory-notes.bash | 28 +++ bashrc.d/70-development.bash | 16 ++ 11 files changed, 364 insertions(+), 356 deletions(-) create mode 100644 aliases.bash create mode 100644 bashrc.d/00-core.bash create mode 100644 bashrc.d/10-prompt.bash create mode 100644 bashrc.d/20-greeting.bash create mode 100644 bashrc.d/30-integrations.bash create mode 100644 bashrc.d/40-status.bash create mode 100644 bashrc.d/50-completions.bash create mode 100644 bashrc.d/60-directory-notes.bash create mode 100644 bashrc.d/70-development.bash diff --git a/README.md b/README.md index 41f0b0d..8bfed5e 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ This repository does not own: - Portable login environment variables or shared PATH construction. Those belong in the `profile` repository. -- Common aliases intended for more than one Bourne-family shell. Those belong - in `profile/aliases.sh`; genuinely Bash-specific aliases may live here. +- Common aliases intended for more than one Bourne-family shell. Those load + through `profile/interactive.sh`; genuinely Bash-specific aliases live here. - User services, timers or background refresh logic. Those belong in the `systemd` repository. Bash may consume their cached output or request a non-blocking service start. @@ -42,10 +42,40 @@ shell is not interactive. ## Files - `bash_profile`: connects Bash login startup to the shared profile and Bash RC. -- `bashrc`: interactive Bash configuration. +- `bashrc`: interactive guard and ordered module loader. +- `bashrc.d/`: numbered interactive Bash modules; lower numbers load first. +- `aliases.bash`: Bash-specific and warning-only transition aliases. - `gitstatus-custom.sh`: prompt integration for gitstatus. - `cheat.bash`: completion for `cheat`. - `install.sh`: installs the home-directory symlinks and gitstatus dependency. Run `install.sh` when initially installing the repository. It preserves an existing destination before creating each symlink. + +## Bash RC modules + +The loader sources every readable `bashrc.d/*.bash` file in lexical order: + +- `00-core.bash`: colors, history, terminal and Readline settings. +- `10-prompt.bash`: prompt, gitstatus and per-command history flushing. +- `20-greeting.bash`: calendar-day greeting and adjective selection. +- `30-integrations.bash`: shared aliases, NVM and z.lua. +- `40-status.bash`: command reminders, memory and cached package status. +- `50-completions.bash`: command-specific completion setup. +- `60-directory-notes.bash`: `TODO.md` and `.autonote.md` display after `cd`. +- `70-development.bash`: OpenCode and Pyenv initialization. + +Keep cross-module dependencies explicit through this numbering. New modules +should own one coherent area rather than becoming another general-purpose RC. + +## Package-status integration + +`40-status.bash` is a read-only consumer of the package cache at +`~/.cache/package-status/status`. It never runs Pacman or synchronizes package +databases during shell startup. When the cache is missing, stale or predates a +local package transaction, Bash requests a non-blocking start of +`package-status-refresh.service`, subject to a five-minute retry backoff. + +The refresh service and 30-minute timer are owned and documented by the +`systemd` repository. The Arch `update` wrapper in the `profile` repository +also requests a refresh after a successful upgrade. diff --git a/aliases.bash b/aliases.bash new file mode 100644 index 0000000..40056bc --- /dev/null +++ b/aliases.bash @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Warning-only transition aliases that rely on Bash alias naming/behaviour. + +alias disk-usage='printf "%s\n" "disk-usage is deprecated; use dusort instead." >&2' +alias debug.log='printf "%s\n" "debug.log is deprecated; use debuglog instead." >&2' + +# Destinations now managed by ~/.ssh/config. +alias mac='printf "%s\n" "mac is deprecated; use ssh mac instead." >&2' +alias pi4='printf "%s\n" "pi4 is deprecated; use ssh pi4 instead." >&2' +alias red='printf "%s\n" "red is deprecated; use ssh red instead." >&2' +alias alice='printf "%s\n" "alice is deprecated; use ssh alice instead." >&2' +alias phthalo='printf "%s\n" "phthalo is deprecated; use ssh phthalo instead." >&2' +alias zirconia='printf "%s\n" "zirconia is deprecated; use ssh zirconia instead." >&2' +alias zirconiarc='printf "%s\n" "zirconiarc is deprecated; use ssh zirconiarc instead." >&2' +alias datapasa='printf "%s\n" "datapasa is deprecated; use ssh datapasa instead." >&2' +alias ultra='printf "%s\n" "ultra is deprecated; use ssh ultra instead." >&2' +alias singsay='printf "%s\n" "singsay is deprecated; use ssh singsay instead." >&2' +alias kermit='printf "%s\n" "kermit is deprecated; use ssh kermit instead." >&2' +alias crafty='printf "%s\n" "crafty is deprecated; use ssh crafty instead." >&2' +alias lv='printf "%s\n" "lv is deprecated; use ssh lv instead." >&2' diff --git a/bashrc b/bashrc index d78a043..f4c4c75 100644 --- a/bashrc +++ b/bashrc @@ -1,359 +1,12 @@ # shellcheck shell=bash -# If not running interactively, don't do anything. +# Interactive Bash only. Login environment belongs to ~/.profile. [[ $- != *i* ]] && return -# Color definitions -_WHITE='\033[1;37m' # White -_NC='\033[0m' # No color +_bash_config_dir=${XDG_CONFIG_HOME:-$HOME/.config}/bash -_RED='\033[0;31m' # Red -_GREEN='\033[01;32m' # Bright Green -_YELLOW='\033[0;33m' # Yellow -_BLUE='\033[1;34m' # Bright Blue -_BRIGHT_GREEN='\033[1;32m' # Bright Green (for positive) -_BRIGHT_RED='\033[1;31m' # Bright Red (for negative) -_BRIGHT_WHITE='\033[1;37m' # Bright White -_DIM_RED='\033[91m' # Bright Red (for TODO files) +for _bash_module in "$_bash_config_dir"/bashrc.d/*.bash; do + [[ -r "$_bash_module" ]] && . "$_bash_module" +done -export SHELL_CONFIG="${SHELL_CONFIG:+${SHELL_CONFIG}:}.bashrc" -# -# ~/.bashrc -# - -function show_date() { - local dotfile selected_adjective adjective classification - local day_of_week day_of_month month_name year day_suffix text_color full_date - local -a adjectives - - # Define the dot file to store the selected adjective - dotfile="$HOME/.adjective_of_the_day" - - # Check if the dot file exists and was modified since yesterday - if [[ -f "$dotfile" && $(date -d "$(stat -c %y "$dotfile")" +%s) -ge $(date -d 'yesterday' +%s) ]]; then - # If the dot file exists and was modified since yesterday - # read the stored adjective from the file - selected_adjective=$(cat "$dotfile") - else - # Define an array of adjectives with classifications - adjectives=( - "beautiful:positive" - "engaging:positive" - "majestic:positive" - "distinguished:positive" - "venerable:positive" - "formal:positive" - "elegant:positive" - "serene:positive" - "graceful:positive" - "regal:positive" - "noble:positive" - - "miserable:negative" - "worthless:negative" - "depressing:negative" - "soulless:negative" - "desolate:negative" - "archaic:negative" - "grim:negative" - "dreadful:negative" - "forlorn:negative" - "wretched:negative" - - "neutral:neutral" - "ordinary:neutral" - "common:neutral" - "antique:neutral" - "ceremonial:neutral" - "timeless:neutral" - "classic:neutral" - "historic:neutral" - "ancient:neutral" - ) - - # Select a random adjective from the array - selected_adjective="${adjectives[$RANDOM % ${#adjectives[@]}]}" - - # Store the selected adjective in the dot file - echo "$selected_adjective" > "$dotfile" - fi - - # Split the selected adjective into the adjective and classification parts - IFS=':' read -r adjective classification <<< "$selected_adjective" - - # Get the current day of the week, day of the month, and year - day_of_week=$(date "+%A") - day_of_month=$(date "+%d") - month_name=$(date "+%B") - year=$(date "+%Y") - - # Determine the day suffix (th, nd, rd) - case "$day_of_month" in - 1[0-9]) day_suffix="th";; - *1) day_suffix="st";; - *2) day_suffix="nd";; - *3) day_suffix="rd";; - *) day_suffix="th";; - esac - - # Determine the color based on the classification - case "$classification" in - "positive") text_color="$_BRIGHT_GREEN";; - "negative") text_color="$_BRIGHT_RED";; - *) text_color="";; - esac - - # Create the full date format with colored parts and the selected adjective - full_date="Pray, be informed, on this ${text_color}${adjective}${_NC} day,\n${_BLUE}${day_of_week}${_NC} the ${_BLUE}${day_of_month}${day_suffix}${_NC} day of ${_BLUE}${month_name}${_NC}, in the year of our Lord ${_BLUE}${year}${_NC}!" - - # Print the full date - echo -e "$full_date" - echo "" -} - - -# ============================================================================ -# SHELL CONFIGURATION -# ============================================================================ - -export HISTCONTROL=ignoreboth:erasedups -export HISTFILE="$HOME/.histfile" -export HISTSIZE=10000 -export HISTFILESIZE=10000 -shopt -s histappend checkwinsize - -# Disable terminal flow-control keys only when stdin is a terminal. -[[ -t 0 ]] && stty -ixon - -# Optimized divider function - simple timestamp without coloring -function draw_prompt_divider() { - local timestamp prefix_length line_length i - printf -v timestamp '%(%H:%M:%S)T' -1 # Faster than $(date) - local prefix="$timestamp ├" - prefix_length=${#prefix} - line_length=$(( ${COLUMNS:-80} - prefix_length )) - - # Print timestamp, junction, then fill with box-drawing characters - printf '%s' "$prefix" - for ((i=0; i&2 - return 127 -} -_error_block='██████' -# Set PS1 with user@host, directory, git status, and prompt ($ color works!) -PS1='\[\033[01;32m\]\u@\h\[\033[00m\] \[\033[1;34m\]\w\[\033[00m\]\n${GITSTATUS_PROMPT:+$GIT_ALIGNMENT_PADDING$GITSTATUS_PROMPT\n}$([ "$?" -eq 0 ] && echo "" || echo "\[\033[01;31m\]$_error_block\[\033[00m\] ")\$ \[\e]0;\u@\h: \w\a\]' - -# If there are multiple matches for completion, Tab should cycle through them -bind TAB:menu-complete -# Display a list of the matching files -bind "set show-all-if-ambiguous on" -# Perform partial (common) completion on the first Tab press, only start -# cycling full results on the second Tab press (from bash version 5) -bind "set menu-complete-display-prefix on" - -show_date - -[ -r "$HOME/.config/profile/aliases.sh" ] && . "$HOME/.config/profile/aliases.sh" - -export NVM_DIR="$HOME/.nvm" -if [[ -s "$NVM_DIR/nvm.sh" ]]; then - \. "$NVM_DIR/nvm.sh" - nvm use default >/dev/null - [[ -s "$NVM_DIR/bash_completion" ]] && \. "$NVM_DIR/bash_completion" -fi - -# https://github.com/skywind3000/z.lua -if command -v lua ; then - eval "$(lua "$HOME/.z.lua/z.lua" --init bash enhanced once echo)" -fi > /dev/null - -_mem_available=$(awk '/MemAvailable/ { printf "%.1f \n", $2/1024/1024 }' /proc/meminfo) -_package_status_cache="${XDG_CACHE_HOME:-$HOME/.cache}/package-status/status" -_package_status_attempt="${XDG_CACHE_HOME:-$HOME/.cache}/package-status/last-attempt" -_package_status_max_age=3600 -_package_status_retry_delay=300 -_package_status_version="" -_package_status_checked_at="" -_package_status_pacman_log_signature="" -_updates_available="0" -_orphans_available="0" - -if [ -r "$_package_status_cache" ]; then - while IFS='=' read -r _package_status_key _package_status_value; do - case "$_package_status_key" in - version) _package_status_version="$_package_status_value" ;; - updates) _updates_available="$_package_status_value" ;; - orphans) _orphans_available="$_package_status_value" ;; - checked_at) _package_status_checked_at="$_package_status_value" ;; - pacman_log_signature) _package_status_pacman_log_signature="$_package_status_value" ;; - esac - done < "$_package_status_cache" -fi - -_package_status_now=$(date +%s) -_package_status_current_pacman_log_signature=$(stat -c '%Y:%s:%i' /var/log/pacman.log 2>/dev/null || echo '0:0:0') - -if ! [[ "$_package_status_version" == "1" \ - && "$_updates_available" =~ ^[0-9]+$ \ - && "$_orphans_available" =~ ^[0-9]+$ \ - && "$_package_status_checked_at" =~ ^[0-9]+$ \ - && "$_package_status_pacman_log_signature" =~ ^[0-9]+:[0-9]+:[0-9]+$ \ - && $_package_status_now -ge $_package_status_checked_at \ - && $((_package_status_now - _package_status_checked_at)) -le $_package_status_max_age \ - && "$_package_status_pacman_log_signature" == "$_package_status_current_pacman_log_signature" ]]; then - _updates_available="0" - _orphans_available="0" - - _package_status_attempt_time=$(stat -c %Y "$_package_status_attempt" 2>/dev/null || echo 0) - if ! [[ "$_package_status_attempt_time" =~ ^[0-9]+$ \ - && $_package_status_now -ge $_package_status_attempt_time \ - && $((_package_status_now - _package_status_attempt_time)) -lt $_package_status_retry_delay ]]; then - systemctl --user start --no-block package-status-refresh.service >/dev/null 2>&1 || true - fi -fi - -unset _package_status_cache _package_status_attempt _package_status_max_age -unset _package_status_retry_delay _package_status_attempt_time _package_status_version -unset _package_status_checked_at _package_status_pacman_log_signature -unset _package_status_now _package_status_current_pacman_log_signature -unset _package_status_key _package_status_value - -echo "Remember commands:" -printf '%b' "$_WHITE" -_command_reminder -printf '%b\n' "$_NC" -echo "" - -if [ ${_mem_available%.*} -lt 5 ] ; then - echo -ne "Memory available: ${_YELLOW}" - [ ${_mem_available%.*} -lt 3 ] && echo -ne "${_RED}" - echo -n "${_mem_available}" - echo -e "GB${_NC}" -fi - -if [ $_updates_available -gt 0 ] ; then - update_color="$_BRIGHT_WHITE" # default white - if [ $_updates_available -gt 29 ] ; then - update_color="$_RED" # red - elif [ $_updates_available -gt 15 ] ; then - update_color="$_YELLOW" # yellow - fi - - echo -ne "Updates available: ${update_color}${_updates_available}${_NC}" - echo -ne " [ update with: ${update_color}update${_NC} ]" - echo "" -fi - -if [ $_orphans_available -gt 0 ] ; then - echo -ne "Orphaned packages: $_BRIGHT_WHITE" - - orphan_color="$_BRIGHT_WHITE" - if [ $_orphans_available -gt 9 ] ; then - orphan_color="$_RED" - elif [ $_orphans_available -gt 4 ] ; then - orphan_color="$_YELLOW" - fi - - echo -ne "${orphan_color}${_orphans_available}${_NC}" - echo -ne " [ remove with: ${orphan_color}sudo pacman -Rns \$(pacman -Qdtq)${_NC} ]" - echo "" -fi - - -echo "" -echo -e "Remember \"${_BLUE}TODO.md${_NC}\" (${_BLUE}et${_NC}) and \"${_BLUE}autonote.md${_NC}\" (${_BLUE}ea${_NC}) files have content automatically displayed." -echo "" - -[ -r "$HOME/.config/bash/cheat.bash" ] && . "$HOME/.config/bash/cheat.bash" - -#openai codex completion -if command -v codex >/dev/null 2>&1; then - eval "$(codex completion bash)" -fi - -# Function to check for TODO.md and echo its contents -function check_todo() { - if [ -f "TODO.md" ]; then - echo - echo -e "${_DIM_RED}TODO.md${_NC}" - echo "---------------------------" - if command -v mdcat >/dev/null 2>&1; then mdcat TODO.md; else cat TODO.md; fi - echo "---------------------------" - echo - fi -} -# Function to check for .autonote.md and echo its contents -function check_autonote() { - if [ -f ".autonote.md" ]; then - echo - echo -e "${_DIM_RED}autonote.md${_NC}" - echo "---------------------------" - if command -v mdcat >/dev/null 2>&1; then mdcat .autonote.md; else cat .autonote.md; fi - echo "---------------------------" - echo - fi -} -# Hook to execute check_todo, check_autonote when changing directories -cd() { - builtin cd "$@" || return - check_todo - check_autonote - return 0 -} - -# opencode -case ":$PATH:" in - *":$HOME/.opencode/bin:"*) ;; - *) export PATH="$HOME/.opencode/bin:$PATH" ;; -esac - -#pyenv -export PYENV_ROOT="$HOME/.pyenv" -case ":$PATH:" in - *":$PYENV_ROOT/bin:"*) ;; - *) export PATH="$PYENV_ROOT/bin:$PATH" ;; -esac -if [[ -z ${PYENV_SHELL:-} ]] && command -v pyenv >/dev/null 2>&1; then - eval "$(pyenv init -)" -fi +unset _bash_module _bash_config_dir diff --git a/bashrc.d/00-core.bash b/bashrc.d/00-core.bash new file mode 100644 index 0000000..75b55f3 --- /dev/null +++ b/bashrc.d/00-core.bash @@ -0,0 +1,29 @@ +# shellcheck shell=bash + +# Shared colors used by later Bash modules. +_WHITE='\033[1;37m' +_NC='\033[0m' +_RED='\033[0;31m' +_YELLOW='\033[0;33m' +_BLUE='\033[1;34m' +_BRIGHT_GREEN='\033[1;32m' +_BRIGHT_RED='\033[1;31m' +_BRIGHT_WHITE='\033[1;37m' +_DIM_RED='\033[91m' + +export SHELL_CONFIG="${SHELL_CONFIG:+${SHELL_CONFIG}:}.bashrc" + +export HISTCONTROL=ignoreboth:erasedups +export HISTFILE="$HOME/.histfile" +export HISTSIZE=10000 +export HISTFILESIZE=10000 +shopt -s histappend checkwinsize + +# Disable terminal flow-control keys only when stdin is a terminal. +[[ -t 0 ]] && stty -ixon + +# If there are multiple completion matches, cycle through them while still +# displaying the shared prefix and full candidate list. +bind TAB:menu-complete +bind "set show-all-if-ambiguous on" +bind "set menu-complete-display-prefix on" diff --git a/bashrc.d/10-prompt.bash b/bashrc.d/10-prompt.bash new file mode 100644 index 0000000..420f30b --- /dev/null +++ b/bashrc.d/10-prompt.bash @@ -0,0 +1,43 @@ +# shellcheck shell=bash + +draw_prompt_divider() { + local timestamp prefix prefix_length line_length i + printf -v timestamp '%(%H:%M:%S)T' -1 + prefix="$timestamp ├" + prefix_length=${#prefix} + line_length=$(( ${COLUMNS:-80} - prefix_length )) + + printf '%s' "$prefix" + for ((i = 0; i < line_length; i++)); do printf '─'; done + echo +} + +_CACHED_USER_HOST="$USER@$HOSTNAME" +_CACHED_USER_HOST_LENGTH=${#_CACHED_USER_HOST} + +calculate_git_alignment() { + [[ -z "$GITSTATUS_PROMPT" ]] && { GIT_ALIGNMENT_PADDING=""; return; } + + local cleaned branch_name needed_padding + cleaned=$(echo "$GITSTATUS_PROMPT" | sed -e 's/\x01//g' -e 's/\x02//g' -e 's/\x1b\[[0-9;]*m//g') + branch_name="${cleaned%% *}" + needed_padding=$((_CACHED_USER_HOST_LENGTH - ${#branch_name})) + [[ $needed_padding -lt 8 ]] && needed_padding=8 + printf -v GIT_ALIGNMENT_PADDING '%*s' "$needed_padding" '' +} + +append_new_history() { + history -a +} + +[ -r "$HOME/.config/bash/gitstatus-custom.sh" ] && . "$HOME/.config/bash/gitstatus-custom.sh" + +PROMPT_COMMAND="append_new_history; draw_prompt_divider; my_gitstatus_prompt_update; calculate_git_alignment" + +command_not_found_handle() { + printf '\033[01;31mbash: %s: command not found\033[00m\n' "$1" >&2 + return 127 +} + +_error_block='██████' +PS1='\[\033[01;32m\]\u@\h\[\033[00m\] \[\033[1;34m\]\w\[\033[00m\]\n${GITSTATUS_PROMPT:+$GIT_ALIGNMENT_PADDING$GITSTATUS_PROMPT\n}$([ "$?" -eq 0 ] && echo "" || echo "\[\033[01;31m\]$_error_block\[\033[00m\] ")\$ \[\e]0;\u@\h: \w\a\]' diff --git a/bashrc.d/20-greeting.bash b/bashrc.d/20-greeting.bash new file mode 100644 index 0000000..289ea15 --- /dev/null +++ b/bashrc.d/20-greeting.bash @@ -0,0 +1,75 @@ +# shellcheck shell=bash + +show_date() { + local dotfile selected_adjective adjective classification + local today day_of_week day_of_month month_name year day_suffix text_color full_date + local -a adjectives + + dotfile="$HOME/.adjective_of_the_day" + today=$(date +%F) + + if [[ -f "$dotfile" && $(date -r "$dotfile" +%F) == "$today" ]]; then + selected_adjective=$(cat "$dotfile") + else + adjectives=( + "beautiful:positive" + "engaging:positive" + "majestic:positive" + "distinguished:positive" + "venerable:positive" + "formal:positive" + "elegant:positive" + "serene:positive" + "graceful:positive" + "regal:positive" + "noble:positive" + "miserable:negative" + "worthless:negative" + "depressing:negative" + "soulless:negative" + "desolate:negative" + "archaic:negative" + "grim:negative" + "dreadful:negative" + "forlorn:negative" + "wretched:negative" + "neutral:neutral" + "ordinary:neutral" + "common:neutral" + "antique:neutral" + "ceremonial:neutral" + "timeless:neutral" + "classic:neutral" + "historic:neutral" + "ancient:neutral" + ) + + selected_adjective="${adjectives[$RANDOM % ${#adjectives[@]}]}" + printf '%s\n' "$selected_adjective" > "$dotfile" + fi + + IFS=':' read -r adjective classification <<< "$selected_adjective" + day_of_week=$(date '+%A') + day_of_month=$(date '+%d') + month_name=$(date '+%B') + year=$(date '+%Y') + + case "$day_of_month" in + 1[0-9]) day_suffix="th" ;; + *1) day_suffix="st" ;; + *2) day_suffix="nd" ;; + *3) day_suffix="rd" ;; + *) day_suffix="th" ;; + esac + + case "$classification" in + positive) text_color="$_BRIGHT_GREEN" ;; + negative) text_color="$_BRIGHT_RED" ;; + *) text_color="" ;; + esac + + full_date="Pray, be informed, on this ${text_color}${adjective}${_NC} day,\n${_BLUE}${day_of_week}${_NC} the ${_BLUE}${day_of_month}${day_suffix}${_NC} day of ${_BLUE}${month_name}${_NC}, in the year of our Lord ${_BLUE}${year}${_NC}!" + printf '%b\n\n' "$full_date" +} + +show_date diff --git a/bashrc.d/30-integrations.bash b/bashrc.d/30-integrations.bash new file mode 100644 index 0000000..d950672 --- /dev/null +++ b/bashrc.d/30-integrations.bash @@ -0,0 +1,16 @@ +# shellcheck shell=bash + +[ -r "$HOME/.config/profile/interactive.sh" ] && . "$HOME/.config/profile/interactive.sh" +[ -r "$HOME/.config/bash/aliases.bash" ] && . "$HOME/.config/bash/aliases.bash" + +export NVM_DIR="$HOME/.nvm" +if [[ -s "$NVM_DIR/nvm.sh" ]]; then + \. "$NVM_DIR/nvm.sh" + nvm use default >/dev/null + [[ -s "$NVM_DIR/bash_completion" ]] && \. "$NVM_DIR/bash_completion" +fi + +# https://github.com/skywind3000/z.lua +if command -v lua >/dev/null 2>&1; then + eval "$(lua "$HOME/.z.lua/z.lua" --init bash enhanced once echo)" +fi >/dev/null diff --git a/bashrc.d/40-status.bash b/bashrc.d/40-status.bash new file mode 100644 index 0000000..733520d --- /dev/null +++ b/bashrc.d/40-status.bash @@ -0,0 +1,90 @@ +# shellcheck shell=bash + +_mem_available=$(awk '/MemAvailable/ { printf "%.1f \n", $2/1024/1024 }' /proc/meminfo) +_package_status_cache="${XDG_CACHE_HOME:-$HOME/.cache}/package-status/status" +_package_status_attempt="${XDG_CACHE_HOME:-$HOME/.cache}/package-status/last-attempt" +_package_status_max_age=3600 +_package_status_retry_delay=300 +_package_status_version="" +_package_status_checked_at="" +_package_status_pacman_log_signature="" +_updates_available="0" +_orphans_available="0" + +if [[ -r "$_package_status_cache" ]]; then + while IFS='=' read -r _package_status_key _package_status_value; do + case "$_package_status_key" in + version) _package_status_version="$_package_status_value" ;; + updates) _updates_available="$_package_status_value" ;; + orphans) _orphans_available="$_package_status_value" ;; + checked_at) _package_status_checked_at="$_package_status_value" ;; + pacman_log_signature) _package_status_pacman_log_signature="$_package_status_value" ;; + esac + done < "$_package_status_cache" +fi + +_package_status_now=$(date +%s) +_package_status_current_pacman_log_signature=$(stat -c '%Y:%s:%i' /var/log/pacman.log 2>/dev/null || echo '0:0:0') + +if ! [[ "$_package_status_version" == "1" \ + && "$_updates_available" =~ ^[0-9]+$ \ + && "$_orphans_available" =~ ^[0-9]+$ \ + && "$_package_status_checked_at" =~ ^[0-9]+$ \ + && "$_package_status_pacman_log_signature" =~ ^[0-9]+:[0-9]+:[0-9]+$ \ + && $_package_status_now -ge $_package_status_checked_at \ + && $((_package_status_now - _package_status_checked_at)) -le $_package_status_max_age \ + && "$_package_status_pacman_log_signature" == "$_package_status_current_pacman_log_signature" ]]; then + _updates_available="0" + _orphans_available="0" + + _package_status_attempt_time=$(stat -c %Y "$_package_status_attempt" 2>/dev/null || echo 0) + if ! [[ "$_package_status_attempt_time" =~ ^[0-9]+$ \ + && $_package_status_now -ge $_package_status_attempt_time \ + && $((_package_status_now - _package_status_attempt_time)) -lt $_package_status_retry_delay ]]; then + systemctl --user start --no-block package-status-refresh.service >/dev/null 2>&1 || true + fi +fi + +if declare -F _command_reminder >/dev/null 2>&1; then + printf '%s\n' 'Remember commands:' + printf '%b' "$_WHITE" + _command_reminder + printf '%b\n\n' "$_NC" +fi + +if [[ ${_mem_available%.*} -lt 5 ]]; then + printf 'Memory available: %b' "$_YELLOW" + [[ ${_mem_available%.*} -lt 3 ]] && printf '%b' "$_RED" + printf '%sGB%b\n' "$_mem_available" "$_NC" +fi + +if [[ $_updates_available -gt 0 ]]; then + update_color="$_BRIGHT_WHITE" + if [[ $_updates_available -gt 29 ]]; then + update_color="$_RED" + elif [[ $_updates_available -gt 15 ]]; then + update_color="$_YELLOW" + fi + printf 'Updates available: %b%s%b [ update with: %bupdate%b ]\n' \ + "$update_color" "$_updates_available" "$_NC" "$update_color" "$_NC" +fi + +if [[ $_orphans_available -gt 0 ]]; then + orphan_color="$_BRIGHT_WHITE" + if [[ $_orphans_available -gt 9 ]]; then + orphan_color="$_RED" + elif [[ $_orphans_available -gt 4 ]]; then + orphan_color="$_YELLOW" + fi + printf 'Orphaned packages: %b%s%b [ remove with: %bsudo pacman -Rns $(pacman -Qdtq)%b ]\n' \ + "$orphan_color" "$_orphans_available" "$_NC" "$orphan_color" "$_NC" +fi + +printf '\nRemember "%bTODO.md%b" (%bet%b) and "%bautonote.md%b" (%bea%b) files have content automatically displayed.\n\n' \ + "$_BLUE" "$_NC" "$_BLUE" "$_NC" "$_BLUE" "$_NC" "$_BLUE" "$_NC" + +unset _mem_available _updates_available _orphans_available update_color orphan_color +unset _package_status_cache _package_status_attempt _package_status_max_age +unset _package_status_retry_delay _package_status_attempt_time _package_status_version +unset _package_status_checked_at _package_status_pacman_log_signature _package_status_now +unset _package_status_current_pacman_log_signature _package_status_key _package_status_value diff --git a/bashrc.d/50-completions.bash b/bashrc.d/50-completions.bash new file mode 100644 index 0000000..b16b029 --- /dev/null +++ b/bashrc.d/50-completions.bash @@ -0,0 +1,7 @@ +# shellcheck shell=bash + +[ -r "$HOME/.config/bash/cheat.bash" ] && . "$HOME/.config/bash/cheat.bash" + +if command -v codex >/dev/null 2>&1; then + eval "$(codex completion bash)" +fi diff --git a/bashrc.d/60-directory-notes.bash b/bashrc.d/60-directory-notes.bash new file mode 100644 index 0000000..e660e9e --- /dev/null +++ b/bashrc.d/60-directory-notes.bash @@ -0,0 +1,28 @@ +# shellcheck shell=bash + +check_todo() { + if [[ -f TODO.md ]]; then + echo + printf '%bTODO.md%b\n' "$_DIM_RED" "$_NC" + printf '%s\n' '---------------------------' + if command -v mdcat >/dev/null 2>&1; then mdcat TODO.md; else cat TODO.md; fi + printf '%s\n\n' '---------------------------' + fi +} + +check_autonote() { + if [[ -f .autonote.md ]]; then + echo + printf '%bautonote.md%b\n' "$_DIM_RED" "$_NC" + printf '%s\n' '---------------------------' + if command -v mdcat >/dev/null 2>&1; then mdcat .autonote.md; else cat .autonote.md; fi + printf '%s\n\n' '---------------------------' + fi +} + +cd() { + builtin cd "$@" || return + check_todo + check_autonote + return 0 +} diff --git a/bashrc.d/70-development.bash b/bashrc.d/70-development.bash new file mode 100644 index 0000000..188c52a --- /dev/null +++ b/bashrc.d/70-development.bash @@ -0,0 +1,16 @@ +# shellcheck shell=bash + +case ":$PATH:" in + *":$HOME/.opencode/bin:"*) ;; + *) export PATH="$HOME/.opencode/bin:$PATH" ;; +esac + +export PYENV_ROOT="$HOME/.pyenv" +case ":$PATH:" in + *":$PYENV_ROOT/bin:"*) ;; + *) export PATH="$PYENV_ROOT/bin:$PATH" ;; +esac + +if [[ -z ${PYENV_SHELL:-} ]] && command -v pyenv >/dev/null 2>&1; then + eval "$(pyenv init -)" +fi