Removing VCS from vcs_info prompt in zsh name to use ZSH_THEME_GIT_PROMPT_DIRTY - format

I currently have this set up in my .zshrc
autoload -Uz add-zsh-hook vcs_info
setopt prompt_subst
add-zsh-hook precmd vcs_info
add-zsh-hook precmd my_precmd
zstyle ':vcs_info:git:*' formats '%b%u%c'
and in my precmd function I set my RPROMPT to be $(git_prompt_info). I also colour it based on the name of the branch (ie main might be blue, while WIP is red etc).
The issue I have is that it always prints out with a git prefix (like this git:(main)).
I checked the docs and it shows that from :vcs_info:vcs-string:user-context:repo-root-namethe relevant part I want to get rid of is vcs-string but I can't find a way to remove it.
I know I can just use ${vcs_info_msg_0_} but I'm trying to incorporate ZSH_THEME_GIT_PROMPT_CLEAN and ZSH_THEME_GIT_PROMPT_DIRTY and haven't found a way to do so with vcs_info
Any help would be appreciated.

Turns out to get access to the ZSH_THEME_GIT_PROMPT_CLEAN and ZSH_THEME_GIT_PROMPT_DIRTY parts of the prompt, you can just call directly into the zsh code
So now my prompt colouring is basically
if [[ "${vcs_info_msg_0_}" != "" ]]
then
[[ "${vcs_info_msg_0_}" == "master" || "${vcs_info_msg_0_}" == "main" ]] && git2color='196'
local git2="%B%F{${git2color}}(${vcs_info_msg_0_}$(parse_git_dirty))%f%b "
fi

Related

ZSH Equivalent to Bash `_command` completion

Given an example wrapper shell function:
app() {
setsid "$#"
}
In Bash, you can do complete -F _command app to automatically give app completions for all commands (not just binaries on the path but also the arguments afterwards).
How can you do this in ZSH? I have some leads with _normal and -command-line- but I've been unable to figure out the correct command to put in a .zshrc.
Update:
Just wanted to document something interesting, this produces a SIGSEGV:
autoload -U compinit && compinit
compdef _normal app
# Attempt tab completion of app:
# _normal:1: maximum nested function level reached; increase FUNCNEST?
FUNCNEST=999
# Attempt tab completion of app again
# !!! ZSH crashes (see coredumpctl) !!!
Figured it out:
# Set up autocomplete
_app() {
shift words
(( CURRENT-- ))
_normal
}
autoload -U compinit && compinit
compdef _app app
This was somewhat of a duplicate of this question: How do I dynamically select a completion function in zsh?
If someone knows a shorter solution like what Bash has then please answer as that would be nice.

print current directory in bash command inside cloud build yaml

So i want to print the current working directory after reading the git tag of the commit example :
/demo
/test
/prod
So when a tag contains verxxx-demo the inline script will know that it needs to switch to the specific folder.
My problem is now that i want to print the current path after the folder is switched and usually in linux you do it with pwd but in cloudbuild seems not to work or i am missing something .
if [[ $TAG_NAME == *test* ]]
then
cd org/test
echo switched "($PWD)"
elif [[ $TAG_NAME == *demo* ]]
then
cd org/demo
echo switched folder
else
echo no tag
fi
when i use this code , i get the next error in cloudbuild :
Your build failed to run: generic::invalid_argument: generic::invalid_argument: invalid value for 'build.substitutions': key in the template "PWD" is not a valid built-in substitution
Any suggestions how to tackle this one ? so i can print the current path after the change folder happens ?
I think what you want is
echo "switched `pwd`"
based on this answer.
I know the variable substitution can be tricky with cloudbuild, so please lmk if that doesn't work.

Watch for environment variable change - ZSH

Is there a way to watch for changes to an environment variable in zsh/bash? When switching my kubernetes environment, for example, I would like to be able to read the variable that's set and make changes to my terminal window if I'm in production vs. development etc.
The way we switch environments is part of our tooling. I'd like to be able to extend that on my own machine without having to update any tooling. If watching for an environment variable change isn't possible, I'm also looking for a way to use something similar to builtin.
Example: create a function of the same name as an alias, call that alias from within the function, then do some other action afterward.
Both shells provide a way to execute arbitrary code prior to displaying a prompt; you can use this to check the value of a specific variable and take an appropriate action.
In .bashrc:
# The function name doesn't matter; it's just easier
# to set PROMPT_COMMAND to the name of a function than
# to arbitrary code.
pre_prompt () {
if [[ $SOME_VAR == "prod" ]]; then
doSomething
else [[
doSomethingElse
fi
}
PROMPT_COMMAND=pre_prompt
In .zshrc:
precmd () {
if [[ $SOME_VAR == "prod" ]]; then
doSomething
else [[
doSomethingElse
fi
}

Save the old value of a function bash, so that it can be called later

Is it possible to save the old function (named foo) in such a way that you can call it within a new function (also named foo), perhaps from a different name, or such?
To make this concrete, here's specifically what I'm trying to do:
In bash, the command_not_found_handle is a function which, if defined, is called whenever bash cannot find the command which the user is trying to run. For example, Ubuntu uses this to suggest packages which can be installed:
$ pv
The program 'pv' is currently not installed. You can install it by typing:
sudo apt-get install pv
This is really nice, and I'd like to keep this behavior. However, I'd also like to add another command_not_found handle, which simply runs a given DEFAULT_COMMAND with whatever command line the user typed, like so:
$ DEFAULT_COMMAND=git
$ status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working
directory)
modified: .bashrc
modified: .gitconfig
modified: .vimrc
Untracked files:
(use "git add <file>..." to include in what will be committed)
.bash_profile
.gitglobalignore
.gvimrc
node_modules/
no changes added to commit (use "git add" and/or "git commit -a")
I already have a function which does the above, and it works like a dream; however, using it overrides the default Ubuntu command_not_found handle, which can be handy to have when I can't remember a package name for a given executable.
How do I get both? Thanks!
NB: The obvious solution is to find and copy the Ubuntu built-in command_not_found logic into my script; but that is not ideal, because it means I have to manually update it later, when Ubuntu changes the way they do things. I'm hoping for something more generic, if possible.
Edit: It would be best not to have to resort to string manipulation, as can be done by saving the text of the function to a variable, mangling it, then evaling it.
Based on Barmar's suggestion, I was able to implement a workable
solution. The following function can be used to rename an arbitrary
bash function to another name.
renameFunction () {
local oldName="$1"; shift
local newName="$1"
local definition="$(declare -f "$oldName")"
if [[ $? -gt 0 ]]; then
echo >&2 "renameFunction: $oldName is not a function"
return
fi
if declare -f "$newName" >/dev/null 2>/dev/null; then
echo >&2 "renameFunction: $newName is already defined"
return
fi
eval "$(echo "${definition/"$oldName"/"$newName"}")"
# Does not work for recursive functions (like "//" would), but also
# doesn't break if $oldName is a substring of something else
unset "$oldName"
}
Notes
The last line
unset "$oldName"
is optional — and without it, this becomes a "copy function" utility.
The pattern substitution would work for a recursive function if it were
changed to the following (note the //):
eval "$(echo "${definition//"$oldName"/"$newName"}")"
However, this fails if the function name is a substring of something
else within the definition. Since recursion is relatively rare in shell
scripts, I took the less brittle approach.
The quoting is correct, despite being too complex for the SO syntax
highlighter. (The quoting is also unnecessary, unless you like
to play with $IFS.)
For completeness' sake, here's how I'm using this function:
# The DEFAULT_CMD is the command to run if the command line could
# not be understood. Set the DEFAULT_CMD to git, once; the user can
# change it at any time
DEFAULT_CMD=git
# Save the old command_not_found_handle for reuse
renameFunction command_not_found_handle __PREVIOUS_COMMAND_NOT_FOUND_HANDLE
command_not_found_handle () {
eval '"$DEFAULT_CMD" $DEFAULT_CMD_PREFIX_ARGS "$#" $DEFAULT_CMD_POSTFIX_ARGS'
if [ $? -gt 0 ]; then
__PREVIOUS_COMMAND_NOT_FOUND_HANDLE "$#"
fi
}
export DEFAULT_CMD
command_not_found_handle is called by bash whenever it cannot find
program or command that the user specified. It receives as its arguments
the entire command-line.
This function tries to execute the command-line as a "sub command" of
the given DEFAULT_CMD. If it does not succeed, it tries the old
command_not_found_handle

can I have the PATH variable evaluate to what directory I'm in?

On linux using bash,
lets say I made two programs both called print_report.
(They are in different directories.)
Inside my .bashrc file, I have:
PATH="path/to/print_report1/:$PATH"
This allows me to type print_report anywhere and it will run one of the programs.
How can I have bash decide to use one or the other depending on the working directory?
for example,
If I'm currently in ~/project1/ and type print_report it will use /bin/foo/print_report
If I'm currently in ~/project2/ and type print_report it will use /bin/bar/print_report
You can't do that as such. Instead, write a wrapper script or function that checks the current directory and invokes the right command:
#!/bin/bash
if [[ $PWD == $HOME/project1/* ]]
then
/bin/foo/print_report "$#"
elif [[ $PWD == $HOME/project2/* ]]
then
/bin/bar/print_report "$#"
else
echo "Don't know how to print_report for $PWD"
fi
You can emulate preexec hooks à la zsh, using the DEBUG trap.
In that way, every time a command is executed, you can run a preexec hook to check $PWD, and adjust $PATH accordingly.
You can include a preexec hook doing what you want in your .bashrc.
This is a security disaster waiting to happen, (which is to say you really don't want to do this) but you can certainly do something like:
cd() {
dir=${1-.}
case $dir in)
path1) PATH=/path/for/working/in/path1;;
path2) PATH=/path/for/working/in/path2;;
*) PATH=/bin:/usr/bin;;
esac
command cd $dir
}
(Put that in your .bashrc or just define it in the current shell.)
Everything presented here so far strikes me as overly complicated and needlessly complex hackery. I would just place a Makefile in each directory with
report:
/bin/foo/print_report
in ~/project1/Makefile, and
report:
/bin/bar/print_report
in ~/project2/Makefile. This extends easily to as many directories and programs you want. And you only need to type make instead of those longwinded command names :-)

Resources