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 }