PSReadLine icon indicating copy to clipboard operation
PSReadLine copied to clipboard

PowerShell on osx - how to force to be completely monochrome

Open rhubarb-geek-nz opened this issue 1 year ago • 19 comments
trafficstars

Prerequisites

  • [X] Write a descriptive title.
  • [X] Make sure you are able to repro it on the latest released version
  • [X] Search the existing issues, especially the pinned issues.

Exception report

See PowerShell on osx how to force to be completely monochrome

Should be able to easily use PowerShell on a monochrome terminal, ideally respecting the TERM environment variable.

Screenshot

Image 14-01-24 at 08 39

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.1
PSEdition                      Core
GitCommitId                    7.4.1
OS                             Darwin 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:34 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T8103
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Steps to reproduce

Open a terminal on macOS of type "Man Page", use pwsh

Typed text will be yellow on a yellow background

Expected behavior

Typed text should be visible no matter the type of terminal or colours used by the terminal

Actual behavior

Typed text is always yellow, hard to read on white or yellow backgrounds.

rhubarb-geek-nz avatar Jan 13 '24 22:01 rhubarb-geek-nz

+1, but please reformat your post to provide a proper link to https://github.com/PowerShell/PowerShell/issues/21066, and provide an actual screenshot.

mklement0 avatar Jan 14 '24 03:01 mklement0

PSReadLine abide by terminfo

One proposal is to have the PowerShell core's $PSStyle.OutputRendering controlled by NO_COLOR, and then PSReadLine governed by terminfo.

Given .bashrc on Debian systems uses 'xterm-256color' as the determination on whether to use colours, then PSReadLine could do similar and if terminfo says the current terminal has more than 16 colours it could use colours, if 16 or less then it should be monochrome.

We note that macOS does not support "xterm-mono" in its terminfo database. Setting TERM is global for a session so affects other programs including 'pico' and 'nano'.

% TERM=xterm-mono nano
zsh: can't find terminal definition for xterm-mono
'xterm-mono': unknown terminal type.
% TERM=xterm infocmp | grep colors 
	colors#8, cols#80, it#8, lines#24, pairs#64,

rhubarb-geek-nz avatar Jan 14 '24 08:01 rhubarb-geek-nz

@rhubarb-geek-nz, I just noticed that you can use xtermm on macOS, which is the xterm-mono equivalent and is also honored by PowerShell (but not PSReadLine yet).

Note that making PSReadline respect TERM also makes sense for supporting a value of dumb, which currently breaks the interactive experience.

Apart from that, it would be great if PSReadLine - in the absence of explicit color configuration for it (i.e. Set-PSReadLineOption -Colors ... having been called) - could automatically and dynamically - pick colors that work well with the terminal foreground and background color (at least as in effect at session startup). I imagine that won't be trivial, though.

This would more directly give you what you want - readability - while not having to forgo color support.

mklement0 avatar Jan 14 '24 20:01 mklement0

I would certainly like a monochrome option somehow. I often remove the default alias to "ls" in bash because I find it hard to read blue text on a black background, let alone yellow on white.

The great thing about monochrome is that it is (a) high contrast (b) the user's preference of colours.

rhubarb-geek-nz avatar Jan 14 '24 20:01 rhubarb-geek-nz

Understood. I think that both things are worth implementing:

  • Automatic switch to monochrome depending on the TERM value, and possibly also NO_COLOR
  • With color support enabled, intelligent choice of default colors based on the terminal's effective background and foreground color - if feasible.

mklement0 avatar Jan 14 '24 21:01 mklement0

I think that PSReadline should understand $PSStyle.OutputRendering = "PlainText", since unlike NO_COLOR it is cross-platform.

237dmitry avatar Jan 15 '24 14:01 237dmitry

Good point, @237dmitry.

What NO_COLOR and $PSStyle.OutputRendering = "PlainText" share (the former translates into the latter), if set directly, is that - unlike TERM, which implies specific terminal capabilities on Unix - they are pure expressions of user preference: the desire not to use color.

My only question is: Would users ever want to control this preference separately for output (already implemented in PowerShell) vs. interactive input?

mklement0 avatar Jan 15 '24 14:01 mklement0

My only question is: Would users ever want to control this preference separately for output (already implemented in PowerShell) vs. interactive input?

Perhaps, they may want NO_COLOR for all rendering of output because they are copying and pasting output tables and results into a document or a report, but want the colour during editing of commands to assist with syntax highlighting and auto-completion.

rhubarb-geek-nz avatar Jan 15 '24 15:01 rhubarb-geek-nz

NO_COLOR is the first for syntax like ENV=value command And it does not work with aliases like alias ls="ls --color"

237dmitry avatar Jan 15 '24 16:01 237dmitry

NO_COLOR is the first for syntax like ENV=value command And it does not work with aliases like alias ls="ls --color"

I am not sure what you mean.

Environment variables like LANG and TZ have traditionally affected the output of commands. Likewise TERM affects how editors drive the terminal console.

Aliases are purely internal to shells themselves and do not affect actual programs. It is the shell interpreting an alias that appends the argument --color to an invocation of ls, not the executing of /bin/ls or /usr/bin/ls.

Debian's .bashrc has similar to

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac

So it determines the prompt based on the TERM environment variable. Likewise it conditionally sets up the alias of ls based on the executability of /usr/bin/dircolors

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'

rhubarb-geek-nz avatar Jan 15 '24 16:01 rhubarb-geek-nz

I am not sure what you mean.

I meant that this variable is not for global use so that the terminal becomes completely monochrome, but for launching individual utilities.

If I add export NO_COLOR="true" in ~/.bashrc then launched bash is still colored.

And not of all utilities understand $NO_COLOR in single-line syntax. For example, NO_COLOR="true" micro will start colored micro, and micro is not the only application.

As for pwsh, not everything is so smooth with it, because many users, unlike you, use profiles for shell configuration. And this is normal use, that's what $profile is for.

237dmitry avatar Jan 15 '24 18:01 237dmitry

I certainly agree that NO_COLOR is not supported by every application, no-color-.org shows a list of a hundred or so programs that do (PowerShell is listed!). Likewise many applications ignore TERM. NO_COLOR is new and TERM is relayed by ssh.

One of the principles of UNIX software is mechanism, not policy.

Do we provide a mechanism to support monochrome, and if so is it a standard?

The policy is something different which we cannot enforce, who sets the variable (if that is the mechanism) in the first place and who else also abides by it.

Having the mechanism of supporting NO_COLOR does mean that I could change my .bashrc to set it depending on the TERM variable value, along with not setting the aliases for ls. That would be me implementing my policy.

In reality I don't customise machines .bashrc or profiles because life is really too short and I deal with hundreds of real and virtual machines and don't have time for customising each. However setting my TERM to be a simple "xterm" or similar does allow me to get by.

rhubarb-geek-nz avatar Jan 15 '24 18:01 rhubarb-geek-nz

That's why we need to target pwsh so that the core and modules understand each other. To prevent PSReadline from being colorful when $PSStyle.OutputRendering is set to "PlainText". This is a single directive and works fine, even though it is primarily intended to output the results of a command to a file so that there are no esc-sequences in it.

For me, until this is implemented, I would just write a function that defines the colors in Set-PSReadlineOption

237dmitry avatar Jan 15 '24 19:01 237dmitry

I can't set the colours in Set-PSReadlineOption because they are not static, the macOS terminal changes automatically between dark mode and light mode depending on the time of day, and I might have one window open as "Man Page" and the other as "Homebrew".

rhubarb-geek-nz avatar Jan 15 '24 19:01 rhubarb-geek-nz

I can't set the colours in Set-PSReadlineOption because they are not static,

Base 16 console colors are not absolute, and they depend on terminal settings.

Just enter in different (dark and light) modes:

"`e[37m Some text `e[0m"

Screenshot 2024-01-15 235750

237dmitry avatar Jan 15 '24 21:01 237dmitry

On MacOS Terminal in the different themes `e[37m just gave white text as per the ANSI escape sequence specification. So when on a yellow background, it gave white text. When Terminal is in light mode it is a very pale grey, eg the background is true bright white and the text is off-white.

rhubarb-geek-nz avatar Jan 15 '24 21:01 rhubarb-geek-nz

So when on a yellow background, it gave white text

You have to configure terminal

Screenshot 2024-01-16 005550

function set-mono {

    $PSStyle.OutputRendering = "PlainText"

    Set-PSReadlineOption -Color @{

        Command = "`e[37m"
        Comment = "`e[37m"
        ContinuationPrompt = "`e[37m"
        Default = "`e[37m"
        Emphasis = "`e[37m"
        Error = "`e[37m"
        InlinePrediction = "`e[37m"
        Keyword = "`e[37m"
        ListPrediction = "`e[37m"
        ListPredictionSelected = "`e[7m"
        ListPredictionTooltip = "`e[37m"
        Member = "`e[37m"
        Number = "`e[37m"
        Operator = "`e[37m"
        Parameter = "`e[37m"
        Selection = "`e[7m"
        String = "`e[37m"
        Type = "`e[37m"
        Variable = "`e[37m"
    }
}

237dmitry avatar Jan 15 '24 21:01 237dmitry

You have to configure terminal

Or just use a shell that works monochrome out of the box, eg bash or zsh, and only use PowerShell to execute scripts. The default terminal on MacOS gives you ( during the day ) black text on a white background. You should not have to reconfigure that to run PowerShell.

rhubarb-geek-nz avatar Jan 15 '24 22:01 rhubarb-geek-nz