1
1
mirror of https://github.com/walles/moar.git synced 2024-11-22 21:50:43 +03:00

Inform ansiString() about terminal color count

So that it can downsample colors when needed.
This commit is contained in:
Johan Walles 2023-12-17 16:34:34 +01:00
parent 0dc490262b
commit eaab95e3ef
6 changed files with 44 additions and 28 deletions

View File

@ -500,7 +500,7 @@ func main() {
panic("Invariant broken: stdout is not a terminal")
}
screen, err := twin.NewScreenWithMouseMode(*mouseMode)
screen, err := twin.NewScreenWithMouseModeAndColorType(*mouseMode, *terminalColorsCount)
if err != nil {
// Ref: https://github.com/walles/moar/issues/149
log.Debug("Failed to set up screen for paging, pumping to stdout instead: ", err)

View File

@ -1,6 +1,8 @@
package twin
import "fmt"
import (
"fmt"
)
// Create using NewColor16(), NewColor256 or NewColor24Bit(), or use
// ColorDefault.
@ -83,7 +85,8 @@ func (color Color) colorValue() uint32 {
// Render color into an ANSI string.
//
// Ref: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func (color Color) ansiString(foreground bool) string {
func (color Color) ansiString(foreground bool, terminalColorCount ColorType) string {
// FIXME: Downsample colors to at most terminalColorCount colors as needed.
value := color.colorValue()
fgBgMarker := "3"
@ -124,14 +127,14 @@ func (color Color) ansiString(foreground bool) string {
panic(fmt.Errorf("unhandled color type=%d value=%#x", color.colorType(), value))
}
func (color Color) ForegroundAnsiString() string {
func (color Color) ForegroundAnsiString(terminalColorCount ColorType) string {
// FIXME: Test this function with all different color types.
return color.ansiString(true)
return color.ansiString(true, terminalColorCount)
}
func (color Color) BackgroundAnsiString() string {
func (color Color) BackgroundAnsiString(terminalColorCount ColorType) string {
// FIXME: Test this function with all different color types.
return color.ansiString(false)
return color.ansiString(false, terminalColorCount)
}
func (color Color) String() string {

View File

@ -77,6 +77,8 @@ type UnixScreen struct {
ttyOut *os.File
oldTtyOutMode uint32 //nolint Windows only
terminalColorCount ColorType
}
// Example event: "\x1b[<65;127;41M"
@ -94,18 +96,29 @@ type UnixScreen struct {
// * "M" marks the end of the mouse event.
var MOUSE_EVENT_REGEX = regexp.MustCompile("^\x1b\\[<([0-9]+);([0-9]+);([0-9]+)M")
// NewScreen() requires Close() to be called after you are done with your new
// screen, most likely somewhere in your shutdown code.
func NewScreen() (Screen, error) {
return NewScreenWithMouseMode(MouseModeAuto)
}
// NewScreen() requires Close() to be called after you are done with your new
// screen, most likely somewhere in your shutdown code.
func NewScreenWithMouseMode(mouseMode MouseMode) (Screen, error) {
terminalColorCount := ColorType24bit
if strings.Contains(os.Getenv("TERM"), "256") {
// Covers "xterm-256color" as used by the macOS Terminal
terminalColorCount = ColorType256
}
return NewScreenWithMouseModeAndColorType(mouseMode, terminalColorCount)
}
func NewScreenWithMouseModeAndColorType(mouseMode MouseMode, terminalColorCount ColorType) (Screen, error) {
if !term.IsTerminal(int(os.Stdout.Fd())) {
return nil, fmt.Errorf("stdout (fd=%d) must be a terminal for paging to work", os.Stdout.Fd())
}
screen := UnixScreen{}
screen := UnixScreen{
terminalColorCount: terminalColorCount,
}
// The number "80" here is from manual testing on my MacBook:
//
@ -505,7 +518,7 @@ func (screen *UnixScreen) Clear() {
// Returns the rendered line, plus how many information carrying cells went into
// it
func renderLine(row []Cell) (string, int) {
func renderLine(row []Cell, terminalColorCount ColorType) (string, int) {
// Strip trailing whitespace
lastSignificantCellIndex := len(row) - 1
for ; lastSignificantCellIndex >= 0; lastSignificantCellIndex-- {
@ -538,7 +551,7 @@ func renderLine(row []Cell) (string, int) {
}
if style != lastStyle {
builder.WriteString(style.RenderUpdateFrom(lastStyle))
builder.WriteString(style.RenderUpdateFrom(lastStyle, terminalColorCount))
lastStyle = style
}
@ -547,7 +560,7 @@ func renderLine(row []Cell) (string, int) {
// Clear to end of line
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences
builder.WriteString(StyleDefault.RenderUpdateFrom(lastStyle))
builder.WriteString(StyleDefault.RenderUpdateFrom(lastStyle, terminalColorCount))
builder.WriteString("\x1b[K")
return builder.String(), len(row)
@ -572,7 +585,7 @@ func (screen *UnixScreen) showNLines(height int, clearFirst bool) {
}
for row := 0; row < height; row++ {
rendered, lineLength := renderLine(screen.cells[row])
rendered, lineLength := renderLine(screen.cells[row], screen.terminalColorCount)
builder.WriteString(rendered)
wasLastLine := row == (height - 1)

View File

@ -61,7 +61,7 @@ func TestRenderLine(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 2)
reset := ""
reversed := ""
@ -76,7 +76,7 @@ func TestRenderLine(t *testing.T) {
func TestRenderLineEmpty(t *testing.T) {
row := []Cell{}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 0)
// All lines are expected to stand on their own, so we always need to clear
@ -92,7 +92,7 @@ func TestRenderLineLastReversed(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
reset := ""
reversed := ""
@ -110,7 +110,7 @@ func TestRenderLineLastNonSpace(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
reset := ""
clearToEol := ""
@ -131,7 +131,7 @@ func TestRenderLineLastReversedPlusTrailingSpace(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
reset := ""
reversed := ""
@ -153,7 +153,7 @@ func TestRenderLineOnlyTrailingSpaces(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 0)
// All lines are expected to stand on their own, so we always need to clear
@ -169,7 +169,7 @@ func TestRenderLineLastReversedSpaces(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
reset := ""
reversed := ""
@ -186,7 +186,7 @@ func TestRenderLineNonPrintable(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
reset := ""
white := ""
@ -207,7 +207,7 @@ func TestRenderHyperlinkAtEndOfLine(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 1)
assert.Equal(t,
@ -232,7 +232,7 @@ func TestMultiCharHyperlink(t *testing.T) {
},
}
rendered, count := renderLine(row)
rendered, count := renderLine(row, ColorType16)
assert.Equal(t, count, 3)
assert.Equal(t,

View File

@ -137,7 +137,7 @@ func (style Style) Foreground(color Color) Style {
// Emit an ANSI escape sequence switching from a previous style to the current
// one.
func (current Style) RenderUpdateFrom(previous Style) string {
func (current Style) RenderUpdateFrom(previous Style, terminalColorCount ColorType) string {
if current == previous {
// Shortcut for the common case
return ""
@ -150,11 +150,11 @@ func (current Style) RenderUpdateFrom(previous Style) string {
var builder strings.Builder
if current.fg != previous.fg {
builder.WriteString(current.fg.ForegroundAnsiString())
builder.WriteString(current.fg.ForegroundAnsiString(terminalColorCount))
}
if current.bg != previous.bg {
builder.WriteString(current.bg.BackgroundAnsiString())
builder.WriteString(current.bg.BackgroundAnsiString(terminalColorCount))
}
// Handle AttrDim / AttrBold changes

View File

@ -12,6 +12,6 @@ func TestHyperlinkToNormal(t *testing.T) {
style := StyleDefault.WithHyperlink(&url)
assert.Equal(t,
strings.ReplaceAll(StyleDefault.RenderUpdateFrom(style), "", "ESC"),
strings.ReplaceAll(StyleDefault.RenderUpdateFrom(style, ColorType16), "", "ESC"),
"ESC]8;;ESC\\")
}