mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 08:03:43 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			98 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package vt100
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"unicode"
 | |
| )
 | |
| 
 | |
| // Decode decodes one ANSI terminal command from s.
 | |
| //
 | |
| // s should be connected to a client program that expects an
 | |
| // ANSI terminal on the other end. It will push bytes to us that we are meant
 | |
| // to intepret as terminal control codes, or text to place onto the terminal.
 | |
| //
 | |
| // This Command alone does not actually update the terminal. You need to pass
 | |
| // it to VT100.Process().
 | |
| //
 | |
| // You should not share s with any other reader, because it could leave
 | |
| // the stream in an invalid state.
 | |
| func Decode(s io.RuneScanner) (Command, error) {
 | |
| 	r, size, err := s.ReadRune()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if r == unicode.ReplacementChar && size == 1 {
 | |
| 		return nil, fmt.Errorf("non-utf8 data from reader")
 | |
| 	}
 | |
| 
 | |
| 	if r == escape || r == monogramCsi { // At beginning of escape sequence.
 | |
| 		s.UnreadRune()
 | |
| 		return scanEscapeCommand(s)
 | |
| 	}
 | |
| 
 | |
| 	if unicode.IsControl(r) {
 | |
| 		return controlCommand(r), nil
 | |
| 	}
 | |
| 
 | |
| 	return runeCommand(r), nil
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	// There are two ways to begin an escape sequence. One is to put the escape byte.
 | |
| 	// The other is to put the single-rune control sequence indicator, which is equivalent
 | |
| 	// to putting "\u001b[".
 | |
| 	escape      = '\u001b'
 | |
| 	monogramCsi = '\u009b'
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	csEnd = &unicode.RangeTable{R16: []unicode.Range16{{Lo: 64, Hi: 126, Stride: 1}}}
 | |
| )
 | |
| 
 | |
| // scanEscapeCommand scans to the end of the current escape sequence. The scanner
 | |
| // must be positioned at an escape rune (esc or the unicode CSI).
 | |
| func scanEscapeCommand(s io.RuneScanner) (Command, error) {
 | |
| 	csi := false
 | |
| 	esc, _, err := s.ReadRune()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if esc != escape && esc != monogramCsi {
 | |
| 		return nil, fmt.Errorf("invalid content")
 | |
| 	}
 | |
| 	if esc == monogramCsi {
 | |
| 		csi = true
 | |
| 	}
 | |
| 
 | |
| 	var args bytes.Buffer
 | |
| 	quote := false
 | |
| 	for i := 0; ; i++ {
 | |
| 		r, _, err := s.ReadRune()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if i == 0 && r == '[' {
 | |
| 			csi = true
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if !csi {
 | |
| 			return escapeCommand{r, ""}, nil
 | |
| 		} else if quote == false && unicode.Is(csEnd, r) {
 | |
| 			return escapeCommand{r, args.String()}, nil
 | |
| 		}
 | |
| 
 | |
| 		if r == '"' {
 | |
| 			quote = !quote
 | |
| 		}
 | |
| 
 | |
| 		// Otherwise, we're still in the args, and this rune is one of those args.
 | |
| 		if _, err := args.WriteRune(r); err != nil {
 | |
| 			panic(err) // WriteRune cannot return an error from bytes.Buffer.
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
