github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/pkg/terminal/glint_term.go (about)

     1  package terminal
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"strings"
     8  	"sync"
     9  	"unicode"
    10  
    11  	"github.com/lab47/vterm/parser"
    12  	"github.com/lab47/vterm/screen"
    13  	"github.com/lab47/vterm/state"
    14  	"github.com/mitchellh/go-glint"
    15  )
    16  
    17  type glintTerm struct {
    18  	mu sync.Mutex
    19  
    20  	w      io.Writer
    21  	scr    *screen.Screen
    22  	ctx    context.Context
    23  	cancel func()
    24  
    25  	output        [][]rune
    26  	height, width int
    27  
    28  	wg       sync.WaitGroup
    29  	parseErr error
    30  
    31  	scrollback [][]rune
    32  
    33  	full bool
    34  }
    35  
    36  func (t *glintTerm) Body(ctx context.Context) glint.Component {
    37  	t.mu.Lock()
    38  	defer t.mu.Unlock()
    39  
    40  	var cs []glint.Component
    41  
    42  	if t.full {
    43  		for _, row := range t.scrollback {
    44  			s := strings.TrimRightFunc(string(row), unicode.IsSpace)
    45  			cs = append(cs, glint.Layout(glint.Text(" │ "), glint.Text(s)).Row())
    46  		}
    47  	}
    48  
    49  	for _, row := range t.output {
    50  		cs = append(cs, glint.Layout(
    51  			glint.Text(" │ "),
    52  			glint.Style(
    53  				glint.Text(strings.TrimRightFunc(string(row), unicode.IsSpace)),
    54  				glint.Color("lightBlue"),
    55  			),
    56  		).Row())
    57  	}
    58  
    59  	return glint.Fragment(cs...)
    60  }
    61  
    62  func (t *glintTerm) DamageDone(r state.Rect, cr screen.CellReader) error {
    63  	t.mu.Lock()
    64  	defer t.mu.Unlock()
    65  
    66  	for row := r.Start.Row; row <= r.End.Row; row++ {
    67  		for col := r.Start.Col; col <= r.End.Col; col++ {
    68  			cell := cr.GetCell(row, col)
    69  
    70  			for len(t.output) <= row {
    71  				t.output = append(t.output, make([]rune, t.width))
    72  			}
    73  
    74  			if cell == nil {
    75  				t.output[row][col] = ' '
    76  			} else {
    77  				val, _ := cell.Value()
    78  
    79  				if val == 0 {
    80  					t.output[row][col] = ' '
    81  				} else {
    82  					t.output[row][col] = val
    83  				}
    84  			}
    85  		}
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  func (t *glintTerm) MoveCursor(p state.Pos) error {
    92  	// Ignore it.
    93  	return nil
    94  }
    95  
    96  func (t *glintTerm) SetTermProp(attr state.TermAttr, val interface{}) error {
    97  	// Ignore it.
    98  	return nil
    99  }
   100  
   101  func (t *glintTerm) Output(data []byte) error {
   102  	// Ignore it.
   103  	return nil
   104  }
   105  
   106  func (t *glintTerm) StringEvent(kind string, data []byte) error {
   107  	// Ignore them.
   108  	return nil
   109  }
   110  
   111  func newGlintTerm(ctx context.Context, height, width int) (*glintTerm, error) {
   112  	term := &glintTerm{
   113  		height: height,
   114  		width:  width,
   115  	}
   116  
   117  	scr, err := screen.NewScreen(height, width, term)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	term.scr = scr
   123  
   124  	st, err := state.NewState(height, width, scr)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	r, w, err := os.Pipe()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	term.w = w
   135  
   136  	prs, err := parser.NewParser(r, st)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	term.ctx, term.cancel = context.WithCancel(ctx)
   142  
   143  	term.wg.Add(1)
   144  	go func() {
   145  		defer term.wg.Done()
   146  
   147  		err := prs.Drive(term.ctx)
   148  		if err != nil && err != context.Canceled {
   149  			term.parseErr = err
   150  		}
   151  	}()
   152  
   153  	return term, nil
   154  }
   155  
   156  func (t *glintTerm) Write(b []byte) (int, error) {
   157  	return t.w.Write(b)
   158  }
   159  
   160  func (t *glintTerm) AddScrollBack(line []rune) error {
   161  	t.mu.Lock()
   162  	defer t.mu.Unlock()
   163  
   164  	t.scrollback = append(t.scrollback, line)
   165  	return nil
   166  }
   167  
   168  func (t *glintTerm) showFull() {
   169  	t.mu.Lock()
   170  	defer t.mu.Unlock()
   171  
   172  	t.full = true
   173  }
   174  
   175  var _ screen.ScrollBack = (*glintTerm)(nil)
   176  
   177  func (t *glintTerm) Close() error {
   178  	t.cancel()
   179  	t.wg.Wait()
   180  	return t.parseErr
   181  }