Skip to content

Commit

Permalink
feat: Use zsh-auto-notify plugin (#55)
Browse files Browse the repository at this point in the history
- use existing plugin which has several improvements and bugfixes
- store as link instead of downloading the plugin since it is seems
somewhat abandoned and isn't accepting new fixes to the repo
- modify default settings and enable sound
- see: https://github.com/MichaelAquilina/zsh-auto-notify

## Summary by Sourcery

Integrate the zsh-auto-notify plugin to enhance user experience by
providing notifications for long-running terminal commands. Modify
default settings to enable sound and adjust notification thresholds for
better usability.

New Features:
- Integrate the zsh-auto-notify plugin to provide notifications for
long-running terminal commands.

Enhancements:
- Modify default settings of the zsh-auto-notify plugin to enable sound
notifications and adjust notification thresholds.
  • Loading branch information
martimlobao authored Nov 10, 2024
1 parent a7d86a7 commit 476a0ce
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 56 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/lint_pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
env:
SHELLCHECK_OPTS: -x
with:
ignore_names: .zshrc .zprofile
ignore_names: .zshrc .zprofile *.zsh
shfmt:
name: Run shfmt
runs-on: ubuntu-latest
Expand All @@ -28,6 +28,7 @@ jobs:
SHFMT_OPTS: -s
with:
sh_checker_checkbashisms_enable: true
sh_checker_exclude: .zshrc .zprofile .zsh
yamllint:
name: Run yamllint
runs-on: ubuntu-latest
Expand Down
207 changes: 207 additions & 0 deletions linkme/.oh-my-zsh/custom/plugins/auto-notify/auto-notify.plugin.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# modified from https://github.com/MichaelAquilina/zsh-auto-notify

# Time it takes for a notification to expire
[[ -z "$AUTO_NOTIFY_EXPIRE_TIME" ]] &&
export AUTO_NOTIFY_EXPIRE_TIME=8000
# Threshold in seconds for when to automatically show a notification
[[ -z "$AUTO_NOTIFY_THRESHOLD" ]] &&
export AUTO_NOTIFY_THRESHOLD=30
# Threshold in seconds for when to automatically show a notification
[[ -z "$AUTO_NOTIFY_TRUNCATE_COMMAND" ]] &&
export AUTO_NOTIFY_TRUNCATE_COMMAND=40
# Sound to play when a notification is shown
[[ -z "$AUTO_NOTIFY_SOUND" ]] &&
export AUTO_NOTIFY_SOUND='Frog'

# List of commands/programs to ignore sending notifications for
[[ -z "$AUTO_NOTIFY_IGNORE" ]] &&
export AUTO_NOTIFY_IGNORE=(
'vim'
'nvim'
'less'
'more'
'man'
'tig'
'watch'
'git commit'
'top'
'htop'
'ssh'
'nano'
'ipython'
'python'
)

function _auto_notify_format() {
local MESSAGE="$1"
local command="$2"
local elapsed="$3"
local exit_code="$4"
if [[ ${#command} -gt $AUTO_NOTIFY_TRUNCATE_COMMAND ]]; then
command="${command:0:$AUTO_NOTIFY_TRUNCATE_COMMAND - 1}"
fi
MESSAGE="${MESSAGE//\%command/$command}"
MESSAGE="${MESSAGE//\%elapsed/$elapsed}"
MESSAGE="${MESSAGE//\%exit_code/$exit_code}"
printf "%s" "$MESSAGE"
}

function _auto_notify_message() {
local command="$1"
local elapsed="$2"
local exit_code="$3"
local platform="$(uname)"
# Run using echo -e in order to make sure notify-send picks up new line
local DEFAULT_TITLE="Terminal Command Completed"
local DEFAULT_BODY="'%command' finished after %elapseds with exit code %exit_code"

local title="${AUTO_NOTIFY_TITLE:-$DEFAULT_TITLE}"
local text="${AUTO_NOTIFY_BODY:-$DEFAULT_BODY}"

title="$(_auto_notify_format "$title" "$command" "$elapsed" "$exit_code")"
body="$(_auto_notify_format "$text" "$command" "$elapsed" "$exit_code")"

if [[ "$platform" == "Linux" ]]; then
local urgency="normal"
local transient="--hint=int:transient:1"
local icon=${AUTO_NOTIFY_ICON_SUCCESS:-""}
# Exit code 130 is returned when a process is terminated with SIGINT.
# Since the user is already interacting with the program, there is no
# need to make the notification persistent.
if [[ "$exit_code" != "0" ]] && [[ "$exit_code" != "130" ]]; then
urgency="critical"
transient=""
icon=${AUTO_NOTIFY_ICON_FAILURE:-""}
fi

local arguments=("$title" "$body" "--app-name=zsh" "$transient" "--urgency=$urgency" "--expire-time=$AUTO_NOTIFY_EXPIRE_TIME")

if [[ -n "$icon" ]]; then
arguments+=("--icon=$icon")
fi
notify-send ${arguments[@]}
if [[ -n "$AUTO_NOTIFY_SOUND" ]]; then
paplay "$AUTO_NOTIFY_SOUND"
fi

elif [[ "$platform" == "Darwin" ]]; then
notification_command="display notification (item 1 of argv) with title (item 2 of argv)"
notification_body=($body $title)
if [[ ! -z "$AUTO_NOTIFY_SOUND" ]]; then
notification_command+=" sound name (item 3 of argv)"
notification_body+=("$AUTO_NOTIFY_SOUND")
fi
osascript \
-e 'on run argv' \
-e ${notification_command[@]} \
-e 'end run' \
${notification_body[@]}
else
printf "Unknown platform for sending notifications: $platform\n"
fi
}

function _is_auto_notify_ignored() {
local command="$1"
# split the command if its been piped one or more times
local command_list=("${(@s/|/)command}")
local target_command="${command_list[-1]}"
# Remove leading whitespace
target_command="$(echo "$target_command" | sed -e 's/^ *//')"

# If the command is being run over SSH, then ignore it
if [[ -n ${SSH_CLIENT-} || -n ${SSH_TTY-} || -n ${SSH_CONNECTION-} ]]; then
print "yes"
return
fi

# Remove sudo prefix from command if detected
if [[ "$target_command" == "sudo "* ]]; then
target_command="${target_command/sudo /}"
fi

# If AUTO_NOTIFY_WHITELIST is defined, then auto-notify will ignore
# any item not defined in the white list
# Otherwise - the alternative (default) approach is used where the
# AUTO_NOTIFY_IGNORE blacklist is used to ignore commands

if [[ -n "$AUTO_NOTIFY_WHITELIST" ]]; then
for allowed in $AUTO_NOTIFY_WHITELIST; do
if [[ "$target_command" == "$allowed"(| *) ]]; then
print "no"
return
fi
done
print "yes"
else
for ignore in $AUTO_NOTIFY_IGNORE; do
if [[ "$target_command" == "$ignore"(| *) ]]; then
print "yes"
return
fi
done
print "no"
fi
}

function _auto_notify_send() {
# Immediately store the exit code before it goes away
local exit_code="$?"

if [[ -z "$AUTO_COMMAND" && -z "$AUTO_COMMAND_START" ]]; then
return
fi

if [[ "$(_is_auto_notify_ignored "$AUTO_COMMAND_FULL")" == "no" ]]; then
local current="$(date +"%s")"
let "elapsed = current - AUTO_COMMAND_START"

if [[ $elapsed -gt $AUTO_NOTIFY_THRESHOLD ]]; then
_auto_notify_message "$AUTO_COMMAND" "$elapsed" "$exit_code"
fi
fi

# Empty tracking so that notifications are not
# triggered for any commands not run (e.g ctrl+C when typing)
_auto_notify_reset_tracking
}

function _auto_notify_track() {
# $1 is the string the user typed, but only when history is enabled
# $2 is a single-line, size-limited version of the command that is always available
# To still do something useful when history is disabled, although with reduced functionality, fall back to $2 when $1 is empty
AUTO_COMMAND="${1:-$2}"
AUTO_COMMAND_FULL="$3"
AUTO_COMMAND_START="$(date +"%s")"
}

function _auto_notify_reset_tracking() {
# Command start time in seconds since epoch
unset AUTO_COMMAND_START
# Full command that the user has executed after alias expansion
unset AUTO_COMMAND_FULL
# Command that the user has executed
unset AUTO_COMMAND
}

function disable_auto_notify() {
add-zsh-hook -D preexec _auto_notify_track
add-zsh-hook -D precmd _auto_notify_send
}

function enable_auto_notify() {
autoload -Uz add-zsh-hook
add-zsh-hook preexec _auto_notify_track
add-zsh-hook precmd _auto_notify_send
}

_auto_notify_reset_tracking


platform="$(uname)"
if [[ "$platform" == "Linux" ]] && ! type notify-send > /dev/null; then
printf "'notify-send' must be installed for zsh-auto-notify to work\n"
printf "Please install it with your relevant package manager\n"
else
enable_auto_notify
fi
56 changes: 1 addition & 55 deletions linkme/.zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ source $(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zs
# Add wisely, as too many plugins slow down shell startup.
plugins=(
1password
auto-notify
autojump
git
thefuck
Expand All @@ -107,58 +108,3 @@ eval "$(starship init zsh)"
# Shell completion for uv and uvx
eval "$(uv generate-shell-completion zsh)"
eval "$(uvx --generate-shell-completion zsh)"

# Notify for long running commands
# https://dev.to/kniraj/macos-the-long-running-task-notifier-35o1
# https://github.com/MichaelAquilina/zsh-auto-notify
function command_start {
zsh_command_start_time=$SECONDS
zsh_last_command=$1
}

function command_end {
local duration=$((SECONDS - zsh_command_start_time))
if (( duration > 30 )); then
# Split the command if its been piped one or more times
local command_list=("${(@s/|/)${zsh_last_command}}")
local command="${command_list[-1]}"
# Remove leading whitespace
command="$(echo "$command" | sed -e 's/^ *//')"
# Remove sudo prefix from command if detected
if [[ "$command" == "sudo "* ]]; then
command="${command/sudo /}"
fi

# Don't notify for SSH commands
if [[ -n ${SSH_CLIENT-} || -n ${SSH_TTY-} || -n ${SSH_CONNECTION-} ]]; then
return
fi
# Don't notify for these commands
local notify_exclude=(
"git diff"
"htop"
"ipython"
"less"
"man"
"more"
"nano"
"python"
"ssh"
"top"
"vim"
"watch"
)
for exclude in "${notify_exclude[@]}"; do
if [[ "$command" == "$exclude"* ]]; then
return
fi
done
local message="'${zsh_last_command}' finished after ${duration}s"
local title="Terminal Command Completed"
osascript -e "display notification \"$message\" with title \"$title\""
fi
}

autoload -U add-zsh-hook
add-zsh-hook preexec command_start
add-zsh-hook precmd command_end

0 comments on commit 476a0ce

Please sign in to comment.