diff options
author | Kevin Song <chipbuster@users.noreply.github.com> | 2019-08-13 19:49:47 -0700 |
---|---|---|
committer | Matan Kushner <hello@matchai.me> | 2019-08-13 22:49:47 -0400 |
commit | 22c8c3459f9583cd23098d76736f8af0fbbbeab3 (patch) | |
tree | bebdb1aa092806a36fad03d93f18f65bcc87e0c1 | |
parent | 56f4797a2583dfe4a15fea9790b8c4cb0f5883f9 (diff) |
feat: implement `cmd_duration` for bash (#144)
-rw-r--r-- | docs/config/README.md | 11 | ||||
-rw-r--r-- | src/init.rs | 74 |
2 files changed, 72 insertions, 13 deletions
diff --git a/docs/config/README.md b/docs/config/README.md index 0c92e9361..e3d759fff 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -119,11 +119,16 @@ The `cmd_duration` module shows how long the last command took to execute. The module will be shown only if the command took longer than two seconds, or the `min_time` config value, if it exists. -::: warning NOTE -Command duration is currently not supported in `bash`. See -[this issue](https://github.com/starship/starship/issues/124) for more details. +::: warning Do Not Hook the DEBUG trap in Bash +If you are running Starship in `bash`, do not hook the `DEBUG` trap after running +`eval $(starship init $0)`, or this module **will** break. ::: +Bash users who need preexec-like functionality can use +[rcaloras's bash_preexec framework](https://github.com/rcaloras/bash-preexec). +Simply define the arrays `preexec_functions` and `precmd_functions` before +running `eval $(starship init $0)`, and then proceed as normal. + ### Options | Variable | Default | Description | diff --git a/src/init.rs b/src/init.rs index 16d466df2..d366bc1b8 100644 --- a/src/init.rs +++ b/src/init.rs @@ -52,23 +52,74 @@ pub fn init(shell_name: &str) { } } -/* Bash does not currently support command durations (see issue #124) for details -https://github.com/starship/starship/issues/124 +/* + For bash: we need to manually hook functions ourself: PROMPT_COMMAND will exec + right before the prompt is drawn, and any function trapped by DEBUG will exec + before a command is run. -We need to quote the output of `$(jobs -p | wc -l)` since MacOS `wc` leaves -giant spaces in front of the number (e.g. " 3"), which messes up the -word-splitting. Instead, quote the whole thing, then let Rust do the whitespace -trimming within the jobs module. + There is a preexec/precmd framework for bash out there: if we find the + appropriate variables set, assume we are using that framework: + https://github.com/rcaloras/bash-preexec + + Bash quirk: DEBUG is triggered whenever a command is executed, even if that + command is part of a pipeline. To avoid only timing the last part of a pipeline, + we only start the timer if no timer has been started since the last prompt draw, + tracked by the variable PREEXEC_READY. Similarly, only draw timing info if + STARSHIP_START_TIME is defined, in case preexec was interrupted. + + Finally, to work around existing DEBUG traps in the absence of a preexec-like, + we parse out the name of the old DEBUG hook, then make a new function which + calls both that function and our starship hooks. We don't do this for + PROMPT_COMMAND because that would probably result in two prompts. + + We need to quote the output of `$(jobs -p | wc -l)` since MacOS `wc` leaves + giant spaces in front of the number (e.g. " 3"), which messes up the + word-splitting. Instead, quote the whole thing, then let Rust do the whitespace + trimming within the jobs module */ +/* +Note to programmers: this and the zsh init will be evaluated on a single line. +Use semicolons, avoid comments, and generally think like all newlines will be +deleted. +*/ const BASH_INIT: &str = r##" +starship_preexec() { + if [ "$PREEXEC_READY" = "true" ]; then + PREEXEC_READY=false; + STARSHIP_START_TIME=$(date +%s); + fi +}; starship_precmd() { - PS1="$(starship prompt --status=$? --jobs="$(jobs -p | wc -l)")"; + STATUS=$?; + if [[ $STARSHIP_START_TIME ]]; then + STARSHIP_END_TIME=$(date +%s); + STARSHIP_DURATION=$((STARSHIP_END_TIME - STARSHIP_START_TIME)); + PS1="$(starship prompt --status=$STATUS --jobs="$(jobs -p | wc -l)" --cmd-duration=$STARSHIP_DURATION)"; + unset STARSHIP_START_TIME; + else + PS1="$(starship prompt --status=$STATUS --jobs="$(jobs -p | wc -l)")"; + fi; + PREEXEC_READY=true; }; -PROMPT_COMMAND=starship_precmd; +if [[ $preexec_functions ]]; then + preexec_functions+=(starship_preexec); + precmd_functions+=(starship_precmd); + STARSHIP_START_TIME=$(date +%s); +else + dbg_trap="$(trap -p DEBUG | cut -d' ' -f3 | tr -d \')"; + if [[ -z "$dbg_trap" ]]; then + trap starship_preexec DEBUG; + elif [[ "$dbg_trap" != "starship_preexec" && "$dbg_trap" != "starship_preexec_all" ]]; then + function starship_preexec_all(){ + $dbg_trap; starship_preexec; + }; + trap starship_preexec_all DEBUG; + fi; + PROMPT_COMMAND=starship_precmd; + STARSHIP_START_TIME=$(date +%s); +fi; "##; -/* TODO: Once warning/error system is implemented in starship, print a warning -if starship will not be printing timing due to DEBUG clobber error */ /* For zsh: preexec_functions and precmd_functions provide preexec/precmd in a way that lets us avoid clobbering them. @@ -80,6 +131,9 @@ if starship will not be printing timing due to DEBUG clobber error */ To fix this, only pass the time if STARSHIP_START_TIME is defined, and unset it after passing the time, so that we only measure actual commands. + + We need to quote the output of the jobs command for the same reason as + bash. */ const ZSH_INIT: &str = r##" |