zbell.plugin.zsh 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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 zbell_use_notify_send if not set
  25. (( ${+zbell_use_notify_send} )) || zbell_use_notify_send=true
  26. # initialize it because otherwise we compare a date and an empty string
  27. # the first time we see the prompt. it's fine to have lastcmd empty on the
  28. # initial run because it evaluates to an empty string, and splitting an
  29. # empty string just results in an empty array.
  30. zbell_timestamp=$EPOCHSECONDS
  31. # UI notification function
  32. # $1: command
  33. # $2: duration in seconds
  34. zbell_ui_notify() {
  35. [[ $zbell_use_notify_send != "true" ]] && return
  36. if type notify-send > /dev/null; then
  37. notify-send -i terminal "Command completed in ${2}s:" $1
  38. fi
  39. }
  40. # default notification function
  41. # $1: command
  42. # $2: duration in seconds
  43. zbell_notify() {
  44. zbell_ui_notify "${@}"
  45. print -n "\a"
  46. }
  47. # right before we begin to execute something, store the time it started at
  48. zbell_begin() {
  49. zbell_timestamp=$EPOCHSECONDS
  50. zbell_lastcmd=$1
  51. }
  52. # when it finishes, if it's been running longer than $zbell_duration,
  53. # and we dont have an ignored command in the line, then print a bell.
  54. zbell_end() {
  55. local cmd_duration=$(( $EPOCHSECONDS - $zbell_timestamp ))
  56. local ran_long=$(( $cmd_duration >= $zbell_duration ))
  57. local zbell_lastcmd_tmp="$zbell_lastcmd"
  58. regexp-replace zbell_lastcmd_tmp '^sudo ' ''
  59. [[ $zbell_last_timestamp == $zbell_timestamp ]] && return
  60. [[ $zbell_lastcmd_tmp == "" ]] && return
  61. zbell_last_timestamp=$zbell_timestamp
  62. local has_ignored_cmd=0
  63. for cmd in ${(s:;:)zbell_lastcmd_tmp//|/;}; do
  64. words=(${(z)cmd})
  65. util=${words[1]}
  66. if (( ${zbell_ignore[(i)$util]} <= ${#zbell_ignore} )); then
  67. has_ignored_cmd=1
  68. break
  69. fi
  70. done
  71. (( ! $has_ignored_cmd && ran_long )) && zbell_notify $zbell_lastcmd $cmd_duration
  72. }
  73. # register the functions as hooks
  74. add-zsh-hook preexec zbell_begin
  75. add-zsh-hook precmd zbell_end