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
	 Todd Gamblin
					Todd Gamblin