zbell.plugin.zsh 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #!/usr/bin/env zsh
  2. # This script prints a bell character when a command finishes
  3. # if it has been running for longer than $zbell_duration seconds.
  4. # If there are programs that you know run long that you don't
  5. # want to bell after, then add them to $zbell_ignore.
  6. #
  7. # This script uses only zsh builtins so its fast, there's no needless
  8. # forking, and its only dependency is zsh and its standard modules
  9. #
  10. # Written by Jean-Philippe Ouellet <jpo@vt.edu>
  11. # Made available under the ISC license.
  12. # only do this if we're in an interactive shell
  13. [[ -o interactive ]] || return
  14. # get $EPOCHSECONDS. builtins are faster than date(1)
  15. zmodload zsh/datetime || return
  16. # make sure we can register hooks
  17. autoload -Uz add-zsh-hook || return
  18. # make sure we can do regexp replace
  19. autoload -Uz regexp-replace || return
  20. # initialize zbell_duration if not set
  21. (( ${+zbell_duration} )) || zbell_duration=15
  22. # initialize zbell_ignore if not set
  23. (( ${+zbell_ignore} )) || zbell_ignore=($EDITOR $PAGER)
  24. # initialize it because otherwise we compare a date and an empty string
  25. # the first time we see the prompt. it's fine to have lastcmd empty on the
  26. # initial run because it evaluates to an empty string, and splitting an
  27. # empty string just results in an empty array.
  28. zbell_timestamp=$EPOCHSECONDS
  29. # default notification function
  30. # $1: command
  31. # $2: duration in seconds
  32. zbell_notify() {
  33. type notify-send > /dev/null && \
  34. notify-send -i terminal "Command completed in ${2}s:" $1
  35. print -n "\a"
  36. }
  37. # right before we begin to execute something, store the time it started at
  38. zbell_begin() {
  39. zbell_timestamp=$EPOCHSECONDS
  40. zbell_lastcmd=$1
  41. }
  42. # when it finishes, if it's been running longer than $zbell_duration,
  43. # and we dont have an ignored command in the line, then print a bell.
  44. zbell_end() {
  45. local cmd_duration=$(( $EPOCHSECONDS - $zbell_timestamp ))
  46. local ran_long=$(( $cmd_duration >= $zbell_duration ))
  47. local zbell_lastcmd_tmp="$zbell_lastcmd"
  48. regexp-replace zbell_lastcmd_tmp '^sudo ' ''
  49. [[ $zbell_last_timestamp == $zbell_timestamp ]] && return
  50. [[ $zbell_lastcmd_tmp == "" ]] && return
  51. zbell_last_timestamp=$zbell_timestamp
  52. local has_ignored_cmd=0
  53. for cmd in ${(s:;:)zbell_lastcmd_tmp//|/;}; do
  54. words=(${(z)cmd})
  55. util=${words[1]}
  56. if (( ${zbell_ignore[(i)$util]} <= ${#zbell_ignore} )); then
  57. has_ignored_cmd=1
  58. break
  59. fi
  60. done
  61. (( ! $has_ignored_cmd && ran_long )) && zbell_notify $zbell_lastcmd $cmd_duration
  62. }
  63. # register the functions as hooks
  64. add-zsh-hook preexec zbell_begin
  65. add-zsh-hook precmd zbell_end