mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
109 lines
3.1 KiB
Go
109 lines
3.1 KiB
Go
![]() |
package termtest // import "github.com/docker/docker/integration/internal/termtest"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"regexp"
|
||
|
|
||
|
"github.com/Azure/go-ansiterm"
|
||
|
)
|
||
|
|
||
|
var stripOSC = regexp.MustCompile(`\x1b\][^\x1b\a]*(\x1b\\|\a)`)
|
||
|
|
||
|
// StripANSICommands attempts to strip ANSI console escape and control sequences
|
||
|
// from s, returning a string containing only the final printed characters which
|
||
|
// would be visible onscreen if the string was to be processed by a terminal
|
||
|
// emulator. Basic cursor positioning and screen erase control sequences are
|
||
|
// parsed and processed such that the output of simple CLI commands passed
|
||
|
// through a Windows Pseudoterminal and then this function yields the same
|
||
|
// string as if the output of those commands was redirected to a file.
|
||
|
//
|
||
|
// The only correct way to represent the result of processing ANSI console
|
||
|
// output would be a two-dimensional array of an emulated terminal's display
|
||
|
// buffer. That would be awkward to test against, so this function instead
|
||
|
// attempts to render to a one-dimensional string without extra padding. This is
|
||
|
// an inherently lossy process, and any attempts to render a string containing
|
||
|
// many cursor positioning commands are unlikely to yield satisfactory results.
|
||
|
// Handlers for several ANSI control sequences are also unimplemented; attempts
|
||
|
// to parse a string containing one will panic.
|
||
|
func StripANSICommands(s string, opts ...ansiterm.Option) (string, error) {
|
||
|
// Work around https://github.com/Azure/go-ansiterm/issues/34
|
||
|
s = stripOSC.ReplaceAllLiteralString(s, "")
|
||
|
|
||
|
var h stringHandler
|
||
|
p := ansiterm.CreateParser("Ground", &h, opts...)
|
||
|
_, err := p.Parse([]byte(s))
|
||
|
return h.String(), err
|
||
|
}
|
||
|
|
||
|
type stringHandler struct {
|
||
|
ansiterm.AnsiEventHandler
|
||
|
cursor int
|
||
|
b []byte
|
||
|
}
|
||
|
|
||
|
func (h *stringHandler) Print(b byte) error {
|
||
|
if h.cursor == len(h.b) {
|
||
|
h.b = append(h.b, b)
|
||
|
} else {
|
||
|
h.b[h.cursor] = b
|
||
|
}
|
||
|
h.cursor++
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h *stringHandler) Execute(b byte) error {
|
||
|
switch b {
|
||
|
case '\b':
|
||
|
if h.cursor > 0 {
|
||
|
if h.cursor == len(h.b) && h.b[h.cursor-1] == ' ' {
|
||
|
h.b = h.b[:len(h.b)-1]
|
||
|
}
|
||
|
h.cursor--
|
||
|
}
|
||
|
case '\r', '\n':
|
||
|
h.Print(b)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Erase Display
|
||
|
func (h *stringHandler) ED(v int) error {
|
||
|
switch v {
|
||
|
case 1: // Erase from start to cursor.
|
||
|
for i := 0; i < h.cursor; i++ {
|
||
|
h.b[i] = ' '
|
||
|
}
|
||
|
case 2, 3: // Erase whole display.
|
||
|
h.b = make([]byte, h.cursor)
|
||
|
for i := range h.b {
|
||
|
h.b[i] = ' '
|
||
|
}
|
||
|
default: // Erase from cursor to end of display.
|
||
|
h.b = h.b[:h.cursor+1]
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// CUrsor Position
|
||
|
func (h *stringHandler) CUP(x, y int) error {
|
||
|
if x > 1 {
|
||
|
return errors.New("termtest: cursor position not supported for X > 1")
|
||
|
}
|
||
|
if y > len(h.b) {
|
||
|
for n := len(h.b) - y; n > 0; n-- {
|
||
|
h.b = append(h.b, ' ')
|
||
|
}
|
||
|
}
|
||
|
h.cursor = y - 1
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (h stringHandler) DECTCEM(bool) error { return nil } // Text Cursor Enable
|
||
|
func (h stringHandler) SGR(v []int) error { return nil } // Set Graphics Rendition
|
||
|
func (h stringHandler) DA(attrs []string) error { return nil }
|
||
|
func (h stringHandler) Flush() error { return nil }
|
||
|
|
||
|
func (h *stringHandler) String() string {
|
||
|
return string(h.b)
|
||
|
}
|