shell: fix zsh color formatting for PS1 in environments (#39497)
* shell: fix zsh color formatting for PS1 in environments The `colorize` function in `llnl.util.tty.color` only applies proper formatting for Bash ANSI and for console output, but this is not what zsh expects for environment variables. In particular, when using `zsh`, `spack env activate -p` produces a `PS1` prompt that looks like this: ``` \[\033[0;92m\][ENVIRONMENT]\[\033[0m\] ``` For zsh the formatting should be: ``` \e[0;92m[ENVIRONMENT]\e0;m ``` - [x] Add a `zsh` option to `colorize()` to enable zsh color formatting - [x] Add conditional to choose the right `PS1` for `zsh`, `bash`, and `sh` - [x] Don't use color escapes for `sh`, as they don't print properly * convert lots of += lines to triple quotes
This commit is contained in:
		@@ -204,17 +204,23 @@ def color_when(value):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class match_to_ansi:
 | 
					class match_to_ansi:
 | 
				
			||||||
    def __init__(self, color=True, enclose=False):
 | 
					    def __init__(self, color=True, enclose=False, zsh=False):
 | 
				
			||||||
        self.color = _color_when_value(color)
 | 
					        self.color = _color_when_value(color)
 | 
				
			||||||
        self.enclose = enclose
 | 
					        self.enclose = enclose
 | 
				
			||||||
 | 
					        self.zsh = zsh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def escape(self, s):
 | 
					    def escape(self, s):
 | 
				
			||||||
        """Returns a TTY escape sequence for a color"""
 | 
					        """Returns a TTY escape sequence for a color"""
 | 
				
			||||||
        if self.color:
 | 
					        if self.color:
 | 
				
			||||||
            if self.enclose:
 | 
					            if self.zsh:
 | 
				
			||||||
                return r"\[\033[%sm\]" % s
 | 
					                result = rf"\e[0;{s}m"
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                return "\033[%sm" % s
 | 
					                result = f"\033[{s}m"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if self.enclose:
 | 
				
			||||||
 | 
					                result = rf"\[{result}\]"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return result
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return ""
 | 
					            return ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -261,9 +267,11 @@ def colorize(string, **kwargs):
 | 
				
			|||||||
            codes, for output to non-console devices.
 | 
					            codes, for output to non-console devices.
 | 
				
			||||||
        enclose (bool): If True, enclose ansi color sequences with
 | 
					        enclose (bool): If True, enclose ansi color sequences with
 | 
				
			||||||
            square brackets to prevent misestimation of terminal width.
 | 
					            square brackets to prevent misestimation of terminal width.
 | 
				
			||||||
 | 
					        zsh (bool): If True, use zsh ansi codes instead of bash ones (for variables like PS1)
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    color = _color_when_value(kwargs.get("color", get_color_when()))
 | 
					    color = _color_when_value(kwargs.get("color", get_color_when()))
 | 
				
			||||||
    string = re.sub(color_re, match_to_ansi(color, kwargs.get("enclose")), string)
 | 
					    zsh = kwargs.get("zsh", False)
 | 
				
			||||||
 | 
					    string = re.sub(color_re, match_to_ansi(color, kwargs.get("enclose")), string, zsh)
 | 
				
			||||||
    string = string.replace("}}", "}")
 | 
					    string = string.replace("}}", "}")
 | 
				
			||||||
    return string
 | 
					    return string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
					# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					import textwrap
 | 
				
			||||||
from typing import Optional
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import llnl.util.tty as tty
 | 
					import llnl.util.tty as tty
 | 
				
			||||||
@@ -54,22 +55,35 @@ def activate_header(env, shell, prompt=None, view: Optional[str] = None):
 | 
				
			|||||||
        if view:
 | 
					        if view:
 | 
				
			||||||
            cmds += "$Env:SPACK_ENV_VIEW='%s'\n" % view
 | 
					            cmds += "$Env:SPACK_ENV_VIEW='%s'\n" % view
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        if "color" in os.getenv("TERM", "") and prompt:
 | 
					        bash_color_prompt = colorize(f"@G{{{prompt}}}", color=True, enclose=True)
 | 
				
			||||||
            prompt = colorize("@G{%s}" % prompt, color=True, enclose=True)
 | 
					        zsh_color_prompt = colorize(f"@G{{{prompt}}}", color=True, enclose=False, zsh=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cmds += "export SPACK_ENV=%s;\n" % env.path
 | 
					        cmds += "export SPACK_ENV=%s;\n" % env.path
 | 
				
			||||||
        if view:
 | 
					        if view:
 | 
				
			||||||
            cmds += "export SPACK_ENV_VIEW=%s;\n" % view
 | 
					            cmds += "export SPACK_ENV_VIEW=%s;\n" % view
 | 
				
			||||||
        cmds += "alias despacktivate='spack env deactivate';\n"
 | 
					        cmds += "alias despacktivate='spack env deactivate';\n"
 | 
				
			||||||
        if prompt:
 | 
					        if prompt:
 | 
				
			||||||
            cmds += "if [ -z ${SPACK_OLD_PS1+x} ]; then\n"
 | 
					            cmds += textwrap.dedent(
 | 
				
			||||||
            cmds += "    if [ -z ${PS1+x} ]; then\n"
 | 
					                rf"""
 | 
				
			||||||
            cmds += "        PS1='$$$$';\n"
 | 
					                if [ -z ${{SPACK_OLD_PS1+x}} ]; then
 | 
				
			||||||
            cmds += "    fi;\n"
 | 
					                    if [ -z ${{PS1+x}} ]; then
 | 
				
			||||||
            cmds += '    export SPACK_OLD_PS1="${PS1}";\n'
 | 
					                        PS1='$$$$';
 | 
				
			||||||
            cmds += "fi;\n"
 | 
					                    fi;
 | 
				
			||||||
            cmds += 'export PS1="%s ${PS1}";\n' % prompt
 | 
					                    export SPACK_OLD_PS1="${{PS1}}";
 | 
				
			||||||
 | 
					                fi;
 | 
				
			||||||
 | 
					                if [ -n "${{TERM:-}}" ] && [ "${{TERM#*color}}" != "${{TERM}}" ] && \
 | 
				
			||||||
 | 
					                   [ -n "${{BASH:-}}" ];
 | 
				
			||||||
 | 
					                then
 | 
				
			||||||
 | 
					                    export PS1="{bash_color_prompt} ${{PS1}}";
 | 
				
			||||||
 | 
					                elif [ -n "${{TERM:-}}" ] && [ "${{TERM#*color}}" != "${{TERM}}" ] && \
 | 
				
			||||||
 | 
					                     [ -n "${{ZSH_NAME:-}}" ];
 | 
				
			||||||
 | 
					                then
 | 
				
			||||||
 | 
					                    export PS1="{zsh_color_prompt} ${{PS1}}";
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    export PS1="{prompt} ${{PS1}}";
 | 
				
			||||||
 | 
					                fi
 | 
				
			||||||
 | 
					                """
 | 
				
			||||||
 | 
					            ).lstrip("\n")
 | 
				
			||||||
    return cmds
 | 
					    return cmds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user