Puking Rainbows

rsteube

rsteube

Posted on October 14, 2023

Puking Rainbows

Shells support colored output. Some do so also during tab completion.

It can provide extra context like whether an issue is open (green) or closed (red).
Or reflect the color of labels:

Image description

Here is how this works and how it is done in carapace...

ANSI escape sequences

Text style is set in the shell using an escape sequence like \e[31;42m.
This instructs the terminal that the text following it is red (31) with a green background (41).

It consists of

  • \e escape.
  • [ an opening square bracket.
  • 31;42 a semicolon-separated list of escape codes.
  • m the letter m.

A common approach is to restore the default style using \e[0m afterwards.

echo "\e[1;2;4;35mdim bold underlined magenta\e[0m" # you might need `echo -e` for this
Enter fullscreen mode Exit fullscreen mode

Image description

There are basic foreground colors (30-37), background colors (40-47),
modes (1-9) and reset (0) to restore the default. 256-colors
and RGB are also possible.

You can find a full list at ANSI Escape Sequences.

Elvish

This one is easy.

Styled

Elvish provides an abstraction for escape sequences using a list of human-readable keywords.

echo (styled "dim bold underlined magenta" dim bold underlined magenta)
Enter fullscreen mode Exit fullscreen mode

Image description

You can read more about this at styled.

Complex candidate

Adding colors to tab completion is thus pretty easy with edit:complex-candidate.

set edit:completion:arg-completer[example] = {|@args|
  edit:complex-candidate true &display=(styled true green)' (true in green)'
  edit:complex-candidate false &display=(styled false red)' (false in red)'
}
Enter fullscreen mode Exit fullscreen mode

Image description

Powershell

This one is a bit tricky.

Backtick

First of all, the escape character is different in Powershell.
It uses backtick instead of backslash.

echo "`e[1;2;4;35mdim bold underlined magenta`e[0m"
Enter fullscreen mode Exit fullscreen mode

Image description

Completion result

CompletionResult allows escape sequences in the ListItemText to add color.

Function _example_completer {
    [CompletionResult]::new("false", "`e[31mfalse`e[0m", [CompletionResultType]::ParameterValue, "false in red")
    [CompletionResult]::new("true", "`e[32mtrue`e[0m", [CompletionResultType]::ParameterValue, "true in green")
}
Register-ArgumentCompleter -Native -CommandName 'example' -ScriptBlock (Get-Item "Function:_example_completer").ScriptBlock
Enter fullscreen mode Exit fullscreen mode

Image description

By default, it uses tooltips to show the descriptions.
But the value by itself often gives no sign of what it represents.

ListItemText

Luckily, we can add the description to the ListItemText ourselves.

Function _example_completer {
    [CompletionResult]::new("false", "`e[31mfalse`e[0m (false in red)", [CompletionResultType]::ParameterValue, " ")
    [CompletionResult]::new("true", "`e[32mtrue`e[0m (true in green)", [CompletionResultType]::ParameterValue, " ")
}
Register-ArgumentCompleter -Native -CommandName 'example' -ScriptBlock (Get-Item "Function:_example_completer").ScriptBlock
Enter fullscreen mode Exit fullscreen mode

Image description

Tooltip must not be empty though, so we need to set it to a single space.

Selection

Fix the highlighting by setting Selection to inverse.

Set-PSReadLineOption -Colors @{ "Selection" = "`e[7m" }
Enter fullscreen mode Exit fullscreen mode

Image description

Description

And add some finishing touches to the description.

Function _example_completer {
    [CompletionResult]::new("false", "`e[21;22;23;24;25;29m`e[31mfalse`e[21;22;23;24;25;29;39;49m`e[2m `e[2m(false in red)`e[21;22;23;24;25;29;39;49m`e[0m", [CompletionResultType]::ParameterValue, " ")
    [CompletionResult]::new("true", "`e[21;22;23;24;25;29m`e[32mtrue`e[21;22;23;24;25;29;39;49m`e[2m `e[2m(true in green)`e[21;22;23;24;25;29;39;49m`e[0m", [CompletionResultType]::ParameterValue, " ")
}
Register-ArgumentCompleter -Native -CommandName 'example' -ScriptBlock (Get-Item "Function:_example_completer").ScriptBlock
Enter fullscreen mode Exit fullscreen mode

Image description

Zsh

This one is hard.

Arrangement

First, let's have a look at how Zsh arranges values for tab completion.

  • Values without a description are arranged side-by-side.
  • Values with a description have it appended in the format -- description It is also aligned with other entries.
  • Values with the same description are also arranged side-by-side in front of the description.

Image description

Zstyle

Colors are set using the zstyle list-colors.

With ${curcontext} we can configure it for the current context during tab completion.

zstyle ":completion:${curcontext}:*" list-colors "{...}"
Enter fullscreen mode Exit fullscreen mode

List Colors

It contains a colon-separated list of patterns and formats.
Formats are the semicolon-separated escape codes from ANSI escape sequences.

There is a simple and a complex way to specify the patterns.

  • =pattern=format to apply one format.
  • =(#b)(pattern1)(pattern2)=format0=format1=format2 to apply many formats.

See here for a good explanation.

Pattern

Given the value "true", three patterns are relevant.

  • =(#b)(true)=0=32 Set "true" to green (32).
  • =(#b)(true)([ ]## -- *)=0=32=2 Set "true" to green (32) and the description to dim (2).
  • =(#b)(-- *)=0=2 Set the description to dim for everything else.

Take this with a grain of salt, but there was an edge case in regards to the arrangement.
So it's best to set both the pattern for with and without a description for each value.

Compdef

Putting it all together.

#compdef example
function _example_completion {
  zstyle ":completion:${curcontext}:*" list-colors "=(#b)(false)([ ]## -- *)=0=31=2:=(#b)(false)=0=31:=(#b)(true)([ ]## -- *)=0=32=2:=(#b)(true)=0=32:=(#b)(-- *)=0=2"

  vals=('true:true in green' 'false:false in red')
  _describe 'values' vals
}
compquote '' 2>/dev/null && _example_completion
compdef _example_completion example
Enter fullscreen mode Exit fullscreen mode

Image description

Carapace

Now to how carapace simplifies the above.

Style

Styles are adopted from Elvish.
There are colors like style.Red, modes like style.Dim and style.Of to combine them.

Action

They can be set directly with ActionStyledValues and ActionStyledValuesDescribed.

carapace.ActionStyledValues(
    "true", style.Green,
    "false", style.Red,
)

carapace.ActionStyledValuesDescribed(
    "true", "true in green", style.Green,
    "false", "false in red", style.Red,
)
Enter fullscreen mode Exit fullscreen mode

Modifier

Or with the modifiers Style, StyleR and StyleF.

carapace.ActionValues(
    "1",
    "true",
).Style(style.Green)

carapace.ActionValues(
    "0",
    "false",
).StyleR(&style.Carapace.KeywordNegative)

carapace.ActionValues(
    "true",
    "false",
    "unknown",
).StyleF(func(s string, sc style.Context) string {
    switch s {
    case "true":
        return style.Green
    case "false":
        return style.Red
    default:
        return style.Default
    }
})
Enter fullscreen mode Exit fullscreen mode

Style functions

The following functions can be passed to StyleF for common highlighting tasks:

  • ForPath highlights paths using the LS_COLORS environment variable.

Image description

  • ForPathExt does the same, but by extension only.

Image description

Image description

  • ForKeyword highlights common keywords like true and false.

Image description


That's all

Enjoy the rainbow!

Image description

💖 💪 🙅 🚩
rsteube
rsteube

Posted on October 14, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Puking Rainbows
go Puking Rainbows

October 14, 2023