ZSH prompt issue : commands shifted to the right on one hundred space characters when doing completions, i.e using TAB key

auto-completecatalinaterminalzsh

From this post, Get Function Into PS1 (Zsh)? , it seems to be possible to execute a function inside a ZSH prompt`.

Here how to use a function into PROMPT :

setopt PROMPT_SUBST
slash_color () { dirs | awk -F "/" ' {for (i=1; i<=NF; i++) printf "\033[38;5;75m"$i"\033[38;5;206m/"} '; }

PS1='%F{13}|%F{green}%n@%F{cyan}%m%F{13}|%f%T%F{13}|$(slash_color)%F{13}|%F{7} '

Below the result :

Prompt result

ISSUE : But now, I am faced to another problem : when I am inside a directory, the first completion with TAB, for example "vim" ⇨Tab shift all the display to roughly one hundred of space characters to the right.

To illustrate, here is a screen capture :

vim tab screen capture

As you can see, just after typing vim te and after press ⇨Tab key, the command vim te is pushed to the right: I don't understand where this shifting could come from.

I am using zsh-5.8 from MacPorts and I also could reproduce the issue by compiling the sources of zsh-5.8 on macOS Catalina.

If someone could have an explanation/suggestion/clue, this would be fine to tell it.

PS: I suspect that using a simple ZSH environment variable could fix this strange behavior.

I have done some research and find these 2 interesting links :

It seems the shifting that occurs in my case is caused by the interpretation of ANSI escape characters but I didn't understand all the details.

However, i tried to modify slash_color() function like this :

slash_color () {
dirs | awk -F "/" '{ blue="\e[38;5;75m"; \
                     pink="\e[38;5;206m"; \
                     for (i=1; i<NF; i++) \
                       printf blue $i pink "/"; \
                     printf blue $NF pink; \
                     printf "\n"
                   }'
}

But this doesn't work, I get the following error about awk:

awk: cmd. line:1: warning: escape sequence `\e' treated as plain `e'

In my original issue (shifting to the right), everything happens like ZSH was badly computing the length of the real visible PATH of PROMPT and overestimating it? That would explain why I am shifted to the right.

Could anyone take a look please at the two links that I gave above, it could contain the solution in my case.

UPDATE 1: @Allan, here you can see my last attempt :

# Prompt for zsh : path separated by %F{13} slash to better see where we are
setopt PROMPT_SUBST

slash_color() {
dirs | awk -F "/" '{ blue="\033[38;5;75m"; \
                     pink="\033[38;5;206m"; \
                     for (i=1; i<NF; i++) \
                      printf blue $i pink "/"; \
                     printf blue $NF pink; \
                     printf "\n";
                   }';
}

# Last method using my_precmd_hook_function
my_precmd_hook_function() {
  slash_path=$(slash_color)
}

autoload -U add-zsh-hook
add-zsh-hook precmd my_precmd_hook_function

PROMPT='%F{13}|%F{green}%n@%F{cyan}%m%F{13}|%f%T%F{13}|''$slash_path''%F{13}|%F{7} '

Rendering of PATH with pink forward slash is good but the issue of shifting to the right remains when I type cd+⇨Tab or ls+⇨Tab. The presence of printf "\n"; doesn't change anything on the result.

Below a capture illustrating this :

shifting to the right

SOLUTION FINALLY FOUND : my perseverance has paid off, everyting is working fine, no more shifting and colorized forward slash in PATH, with :

# Prompt for zsh : path separated by %F{13} slash to better see where we are
setopt PROMPT_SUBST

# Path with colorized forward slash
slash_color() {
dirs | awk -F "/" '{ blue="%{\033[38;5;75m%}"; \
                     pink="%{\033[38;5;206m%}"; \
                     for (i=1; i<NF; i++) \
                      printf blue $i pink "/"; \
                     printf blue $NF pink; \
                   }';
}

# Prompt final
PROMPT=$'%F{13}|%F{green}%n@%F{cyan}%m%F{13}|%f%T%F{13}|$(slash_color)%F{13}|%F{7} '

# Zsh reverse auto-completion
bindkey '^[[Z' reverse-menu-complete

Thanks a lot for all your advices.

Best Answer

To be candid, I think your making this prompt overly complex and attempting to introduce additional, complex "layers" to what's supposed to be a simple prompt is having unforeseen and unpredictable issues.

In my previous answer to your related question, which what you based your slash_color function on, I cautioned against this from a usability standpoint. In the comments, I mentioned that I wasn't amenable to putting a function within a prompt for security reasons.

Ok...disclaimers are out of the way.


My first observation is that you have a newline character being generated by awk.

slash_color code

So, in your prompt, you're telling it to output a new line in something that's supposed to be contained to a single line. I don't now how PROMPT handles things within ZSH, but I doubt it handles new line chars very well.

Secondly, you seem to be mixing up all the different things that ZSH, awk, and functions in general have to deal with. I'm still a bit foggy as to the actual logic here, but it seems like you're trying to push "normal" escape codes through two layers of functions within ZSH by using their color conventions and ZSH's loadable modules.

Could anyone take a look please at the 2 links that I gave above, it could contain the solution in my case?

Why? What in those links lead you to believe that? The first link is about variable expansion within the prompt. The second show you how to colorize your prompt using both escape sequences and the preferred braces method in ZSH.

I believe the issue is the over-complexity.

You're shoehorning a function that not only has the ANSI escape codes, which are supported, but calling another function that creates a pipe, which means it creates a sub shell to call yet another function! (This is what I was referring to re: layers and complexity).

This is why I suggested using precmd(). Not only can you nest functions, but you can call multiple functions and then generate your prompt. I advise you to explore that more and not fixate on PROMPT being the sole method to skin this cat.

My other piece of advice is to articulate what you'd ultimately like to accomplish and post that as a new question because there's probably a solution you're not aware of. IMO, this sounds like an XY Problem