Why is `print -P "%%"` behaviour different within library function? - zsh

On my terminal:
$ print -P "%%"
%
The equivalent code within a Spaceship prompt function:
spaceship_extension() {
unset PROMPT_PERCENT
unset PROMPT_SUBST
print -P "00%%\n"
set PROMPT_PERCENT
unset PROMPT_SUBST
print -P "01%%\n"
unset PROMPT_PERCENT
set PROMPT_SUBST
print -P "10%%\n"
set PROMPT_PERCENT
set PROMPT_SUBST
print -P "11%%\n"
}
Output:
00
01
10
11
According to the Prompt Expansion man page, those are the only relevant environment variables. Does anyone have any idea what's going on here?
Edit: Removed exports that were obviating the setting and resetting of the environment variables.

First problem is that export has no effect on either option; you are just setting the export attribute on a set of names in each case.
Second, set and unset operate on names, not shell options. You want setopt and unsetopt.
% setopt PROMPT_PERCENT
% print -P '00%%\n'
00%
% unsetopt PROMPT_PERCENT
% print -P '00%%\n'
00%%
(In practice, unsetting PROMPT_PERCENT may affect your actual prompt; I used % here as a placeholder for the prompt, not an accurate representation of what you prompt may look like after unsetting the option.)

Related

Unexpected path being shown in Prompt

On zsh shell (remote shell), I am seeing an unexpected path being displayed on the right hand side. I don't have any such component in the prompt variable. What can be the cause?
function parse_git_branch() {
git branch 2> /dev/null | sed -n -e 's/^\* \(.*\)/[\1]/p'
}
setopt PROMPT_SUBST
export PROMPT='[dev] %~ $(parse_git_branch)
# '
Prompt at the right side of terminal is displayed in zsh if the variables RPROMPT or RPS1 are set. Check if they are set by running echo $RPROMPT or echo $RPS1. If one (or both) is non empty, then it's possible that it is getting set somewhere else.
Add the lines export RPROMPT='' and export RPS1='' at the end of the file ~/.zshrc.

Disable Zsh history completely

I would like to disable Zsh history (arrow up) and zle history search (namely esc+p) completely. How can I achieve this?
My current .zshrc:
unsetopt hist_append
unsetopt hist_expand
HISTFILE=
HISTSIZE=SAVEHIST=0
Currently I have history buffer of one, but I'd like to have history of zero.
21-10-2016 Update:
I've added
bindkey -r "^[p"
bindkey -r "^Xr"
bindkey -r "^Xs"
bindkey -r "^[[A"
bindkey -r "^[[B"
bindkey -r "^[n"
to get rid of history features that I use (esc+p is deeply hardwired to my backbone - so difficult to unlearn).
I don't see anything in the zsh man page that completely disables history. Even setting HISTSIZE=0 seems to reset the value of HISTSIZE to 1.
You'll probably have better luck changing the key bindings with bindkey so that history features never occur. For example, bindkey -r "^[[A" for my up-arrow key (note that I actually typed a caret and two brackets, not an escape key).
I use following entries in my .zshrc:
alias disablehistory="function zshaddhistory() { return 1 }"
If function zshaddhistory is defined, it can control whether history line will be saved.
Therefore, alias disablehistory will just define function that always returns 1 (don't save).
alias disablehistory="function zshaddhistory() { return 1 }"
alias enablehistory="unset -f zshaddhistory"
enablehistory just unsets function set by previous alias.
If you want to disable history completely, permanently define zshaddhistory in your .zshrc
Try configuring your ZSH setup to run a postexec function that empties/deletes your .zhistory file. It's a hacky workaround but should probably work.

ZSH: Behavior on Enter

I realize, when I'm in my terminal, I would expect to press Enter on empty input to make a ls or a git status when I'm on a git repos.
How can I achieve that? I mean, have a custom behavior on Empty input -> Enter in zsh?
EDIT: Thanks for the help. Here's my take with preexec...
precmd() {
echo $0;
if ["${0}" -eq ""]; then
if [ -d .git ]; then
git status
else
ls
fi;
else
$1
fi;
}
On Enter zsh calls the accept-line widget, which causes the buffer to be executed as command.
You can write your own widget in order to implement the behaviour you want and rebind Enter:
my-accept-line () {
# check if the buffer does not contain any words
if [ ${#${(z)BUFFER}} -eq 0 ]; then
# put newline so that the output does not start next
# to the prompt
echo
# check if inside git repository
if git rev-parse --git-dir > /dev/null 2>&1 ; then
# if so, execute `git status'
git status
else
# else run `ls'
ls
fi
fi
# in any case run the `accept-line' widget
zle accept-line
}
# create a widget from `my-accept-line' with the same name
zle -N my-accept-line
# rebind Enter, usually this is `^M'
bindkey '^M' my-accept-line
While it would be sufficient to run zle accept-line only in cases where there actually was a command, zsh would not put a new prompt after the output. And while it is possible to redraw the prompt with zle redisplay, this will probably overwrite the last line(s) of the output if you are using multi-line prompts. (Of course there are workarounds for that, too, but nothing as simple as just using zle accept-line.
Warning: This redfines an (the most?) essential part of your shell. While there is nothing wrong with that per se (else I would not have posted it here), it has the very real chance to make your shell unusable if my-accept-line does not run flawlessly. For example, if zle accept-line were to be missing, you could not use Enter to confirm any command (e.g. to redefine my-accept-line or to start an editor). So please, test it before putting it into your ~/.zshrc.
Also, by default accept-line is bound to Ctrl+J, too. I would recommend to leave it that way, to have an easy way to run the default accept-line.
In my .zshrc I use a combination of precmd and preexec found here:
http://zsh.sourceforge.net/Doc/Release/Functions.html#Hook-Functions
I also find that the git-prompt is super useful:
https://github.com/olivierverdier/zsh-git-prompt

Use preexec() to evaluate entered command

I want to use preexec() to modify certain commands before they are run but I need to be able to evaluate the current entered command. Is there a variable that contains the entire command before it is executed? I know !! is the last command but I need the current line before it's saved to history.
An example of what I want to do would probably help
ls -l /root please
And then I want preexec to see I wrote "please" at the end and replace it with
sudo ls -l /root
I think something like
preexec() {
if [[ $CURRENT_LINE =~ please$ ]]; then
$CURRENT_LINE="sudo ${CURRENT_LINE% please}"
fi
Would work but I can't find a variable in zsh that gives me the correct $CURRENT_LINE
For bonus points I also want to be able to enter please on a line by itself and have it run sudo !! but I could probably do that with some form of alias.
I think it might be better to make a please function that I can pipe a command to but I don't think that'll work as well because the command will run and fail (before piping) before it is run again with sudo.
As far as I know that the preexec is not for the right place to modify the command to be executed though. We can not change the commands to be executed from inside of the preexec function…
Although the actual command to be executed are passed as $1, $2 and $3.
preexec
Executed just after a command has been read and is about to be executed. If the history mechanism is active (and the line was not discarded from the history buffer), the string that the user typed is passed as the first argument, otherwise it is an empty string. The actual command that will be executed (including expanded aliases) is passed in two different forms: the second argument is a single-line, size-limited version of the command (with things like function bodies elided); the third argument contains the full text that is being executed.
-- zshmisc(1) 9.3.1 Hook Functions
For example:
alias ls='ls -sF --color=auto'
preexec () {
print ">>>preexec<<<"
print -l ${(qqq)#}
}
If I have above in ~/.zshrc then I will get follows:
% echo test preexec<Esc-Return>
ls<Return>
;# outputs below
>>>preexec<<<
"echo test preexec
ls"
"echo test preexec; ls -sF --color=auto"
"echo test preexec
ls -sF --color=auto"
test preexec
total 1692
...
You could add your own zle widget functions to the zsh line editor for manipulating the line editor buffer. (zshzle(1))
You could add the zle widget function to change the behavior for hitting Enter.
my-accept-line () {
if [[ "$BUFFER" == *" please" ]]; then
BUFFER="sudo ${BUFFER% please}"
fi
zle .accept-line
}
zle -N accept-line my-accept-line
The above snippets changes the functionality for accept-line from the built-in behavior to my-accept-line defined here.
Adding the abbreviations also could help which is described below:
Cloning vim's abbreviation feature
-- “examples:zleiab [ZshWiki]” - http://zshwiki.org/home/examples/zleiab

setopt listambiguous not setting the option

In ZSH I run this at the command line:
$ setopt listambiguous
I then run setopt to get a list of current options:
$ setopt
It's not in the list. Why not?
The list_ambiguous option is on by default, so setopt listambiguous changes nothing unless you've previously turned it off. A plain setopt doesn't display options whose setting is the default value.

Resources