1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-11-17 18:45:12 +00:00
starship/src/init.rs
2019-08-13 22:49:47 -04:00

173 lines
6.3 KiB
Rust

use std::ffi::OsStr;
use std::path::Path;
/* We need to send execution time to the prompt for the cmd_duration module. For fish,
this is fairly straightforward. For bash and zsh, we'll need to use several
shell utilities to get the time, as well as render the prompt */
pub fn init(shell_name: &str) {
log::debug!("Shell name: {}", shell_name);
let shell_basename = Path::new(shell_name).file_stem().and_then(OsStr::to_str);
let setup_script = match shell_basename {
Some("bash") => {
let script = BASH_INIT;
Some(script)
}
Some("zsh") => {
let script = ZSH_INIT;
Some(script)
}
Some("fish") => {
let script = FISH_INIT;
Some(script)
}
None => {
println!(
"Invalid shell name provided: {}\\n\
If this issue persists, please open an \
issue in the starship repo: \\n\
https://github.com/starship/starship/issues/new\\n\"",
shell_name
);
None
}
_ => {
/* Calling unwrap() here is fine because the None case will have
already matched on the previous arm */
println!(
"printf \"\\n{0} is not yet supported by starship.\\n\
For the time being, we support bash, zsh, and fish.\\n\
Please open an issue in the starship repo if you would like to \
see support for {0}:\\nhttps://github.com/starship/starship/issues/new\"\\n\\n",
shell_basename.unwrap()
);
None
}
};
if let Some(script) = setup_script {
print!("{}", script);
}
}
/*
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.
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() {
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;
};
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;
"##;
/* For zsh: preexec_functions and precmd_functions provide preexec/precmd in a
way that lets us avoid clobbering them.
Zsh quirk: preexec() is only fired if a command is actually run (unlike in
bash, where spamming empty commands still triggers DEBUG). This means a user
spamming ENTER at an empty command line will see increasing runtime (since
preexec never actually fires to reset the start time).
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##"
starship_precmd() {
STATUS=$?;
if [[ $STARSHIP_START_TIME ]]; then
STARSHIP_END_TIME="$(date +%s)";
STARSHIP_DURATION=$((STARSHIP_END_TIME - STARSHIP_START_TIME));
PROMPT="$(starship prompt --status=$STATUS --cmd-duration=$STARSHIP_DURATION --jobs="$(jobs | wc -l)")";
unset STARSHIP_START_TIME;
else
PROMPT="$(starship prompt --status=$STATUS --jobs="$(jobs | wc -l)")";
fi
};
starship_preexec(){
STARSHIP_START_TIME="$(date +%s)"
};
if [[ ${precmd_functions[(ie)starship_precmd]} -gt ${#precmd_functions} ]]; then
precmd_functions+=(starship_precmd);
fi;
if [[ ${preexec_functions[(ie)starship_preexec]} -gt ${#preexec_functions} ]]; then
preexec_functions+=(starship_preexec);
fi;
STARSHIP_START_TIME="$(date +%s)";
"##;
/* Fish setup is simple because they give us CMD_DURATION. Just account for name
changes between 2.7/3.0 and do some math to convert ms->s and we can use it */
const FISH_INIT: &str = r##"
function fish_prompt;
set -l exit_code $status;
set -l CMD_DURATION "$CMD_DURATION$cmd_duration";
set -l starship_duration (math --scale=0 "$CMD_DURATION / 1000");
starship prompt --status=$exit_code --cmd-duration=$starship_duration --jobs=(count (jobs -p));
end;
"##;