Puking Rainbows
rsteube
Posted on October 14, 2023
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:
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
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)
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)'
}
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"
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
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
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" }
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
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.
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 "{...}"
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
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,
)
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
}
})
Style functions
The following functions can be passed to StyleF for common highlighting tasks:
-
ForPath highlights paths using the
LS_COLORS
environment variable.
- ForPathExt does the same, but by extension only.
- ForLogLevel highlights log levels.
- ForKeyword highlights common keywords like true and false.
That's all
Enjoy the rainbow!
Posted on October 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.