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:
parent
0dc490262b
commit
eaab95e3ef
2
moar.go
2
moar.go
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -61,7 +61,7 @@ func TestRenderLine(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
rendered, count := renderLine(row)
|
||||
rendered, count := renderLine(row, ColorType16)
|
||||
assert.Equal(t, count, 2)
|
||||
reset := "[m"
|
||||
reversed := "[7m"
|
||||
@ -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 := "[m"
|
||||
reversed := "[7m"
|
||||
@ -110,7 +110,7 @@ func TestRenderLineLastNonSpace(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
rendered, count := renderLine(row)
|
||||
rendered, count := renderLine(row, ColorType16)
|
||||
assert.Equal(t, count, 1)
|
||||
reset := "[m"
|
||||
clearToEol := "[K"
|
||||
@ -131,7 +131,7 @@ func TestRenderLineLastReversedPlusTrailingSpace(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
rendered, count := renderLine(row)
|
||||
rendered, count := renderLine(row, ColorType16)
|
||||
assert.Equal(t, count, 1)
|
||||
reset := "[m"
|
||||
reversed := "[7m"
|
||||
@ -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 := "[m"
|
||||
reversed := "[7m"
|
||||
@ -186,7 +186,7 @@ func TestRenderLineNonPrintable(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
rendered, count := renderLine(row)
|
||||
rendered, count := renderLine(row, ColorType16)
|
||||
assert.Equal(t, count, 1)
|
||||
reset := "[m"
|
||||
white := "[37m"
|
||||
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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\\")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user