github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration/internal/termtest/stripansi.go (about)

     1  package termtest // import "github.com/docker/docker/integration/internal/termtest"
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  
     7  	"github.com/Azure/go-ansiterm"
     8  )
     9  
    10  var stripOSC = regexp.MustCompile(`\x1b\][^\x1b\a]*(\x1b\\|\a)`)
    11  
    12  // StripANSICommands attempts to strip ANSI console escape and control sequences
    13  // from s, returning a string containing only the final printed characters which
    14  // would be visible onscreen if the string was to be processed by a terminal
    15  // emulator. Basic cursor positioning and screen erase control sequences are
    16  // parsed and processed such that the output of simple CLI commands passed
    17  // through a Windows Pseudoterminal and then this function yields the same
    18  // string as if the output of those commands was redirected to a file.
    19  //
    20  // The only correct way to represent the result of processing ANSI console
    21  // output would be a two-dimensional array of an emulated terminal's display
    22  // buffer. That would be awkward to test against, so this function instead
    23  // attempts to render to a one-dimensional string without extra padding. This is
    24  // an inherently lossy process, and any attempts to render a string containing
    25  // many cursor positioning commands are unlikely to yield satisfactory results.
    26  // Handlers for several ANSI control sequences are also unimplemented; attempts
    27  // to parse a string containing one will panic.
    28  func StripANSICommands(s string, opts ...ansiterm.Option) (string, error) {
    29  	// Work around https://github.com/Azure/go-ansiterm/issues/34
    30  	s = stripOSC.ReplaceAllLiteralString(s, "")
    31  
    32  	var h stringHandler
    33  	p := ansiterm.CreateParser("Ground", &h, opts...)
    34  	_, err := p.Parse([]byte(s))
    35  	return h.String(), err
    36  }
    37  
    38  type stringHandler struct {
    39  	ansiterm.AnsiEventHandler
    40  	cursor int
    41  	b      []byte
    42  }
    43  
    44  func (h *stringHandler) Print(b byte) error {
    45  	if h.cursor == len(h.b) {
    46  		h.b = append(h.b, b)
    47  	} else {
    48  		h.b[h.cursor] = b
    49  	}
    50  	h.cursor++
    51  	return nil
    52  }
    53  
    54  func (h *stringHandler) Execute(b byte) error {
    55  	switch b {
    56  	case '\b':
    57  		if h.cursor > 0 {
    58  			if h.cursor == len(h.b) && h.b[h.cursor-1] == ' ' {
    59  				h.b = h.b[:len(h.b)-1]
    60  			}
    61  			h.cursor--
    62  		}
    63  	case '\r', '\n':
    64  		h.Print(b)
    65  	}
    66  	return nil
    67  }
    68  
    69  // Erase Display
    70  func (h *stringHandler) ED(v int) error {
    71  	switch v {
    72  	case 1: // Erase from start to cursor.
    73  		for i := 0; i < h.cursor; i++ {
    74  			h.b[i] = ' '
    75  		}
    76  	case 2, 3: // Erase whole display.
    77  		h.b = make([]byte, h.cursor)
    78  		for i := range h.b {
    79  			h.b[i] = ' '
    80  		}
    81  	default: // Erase from cursor to end of display.
    82  		h.b = h.b[:h.cursor+1]
    83  	}
    84  	return nil
    85  }
    86  
    87  // CUrsor Position
    88  func (h *stringHandler) CUP(x, y int) error {
    89  	if x > 1 {
    90  		return errors.New("termtest: cursor position not supported for X > 1")
    91  	}
    92  	if y > len(h.b) {
    93  		for n := len(h.b) - y; n > 0; n-- {
    94  			h.b = append(h.b, ' ')
    95  		}
    96  	}
    97  	h.cursor = y - 1
    98  	return nil
    99  }
   100  
   101  func (h stringHandler) DECTCEM(bool) error      { return nil } // Text Cursor Enable
   102  func (h stringHandler) SGR(v []int) error       { return nil } // Set Graphics Rendition
   103  func (h stringHandler) DA(attrs []string) error { return nil }
   104  func (h stringHandler) Flush() error            { return nil }
   105  
   106  func (h *stringHandler) String() string {
   107  	return string(h.b)
   108  }