forked from hinshun/vt10x
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvt_other.go
More file actions
137 lines (125 loc) · 2.41 KB
/
vt_other.go
File metadata and controls
137 lines (125 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// +build plan9 nacl windows
package vt10x
import (
"bufio"
"bytes"
"io"
"unicode"
"unicode/utf8"
)
type terminal struct {
*State
}
func newTerminal(info TerminalInfo) *terminal {
t := &terminal{newState(info.w)}
t.init(info.cols, info.rows)
return t
}
func (t *terminal) init(cols, rows int) {
t.numlock = true
t.state = t.parse
t.cur.Attr.FG = DefaultFG
t.cur.Attr.BG = DefaultBG
t.Resize(cols, rows)
t.reset()
}
func (t *terminal) Write(p []byte) (int, error) {
var written int
r := bytes.NewReader(p)
t.lock()
defer t.unlock()
for {
c, sz, err := r.ReadRune()
if err != nil {
if err == io.EOF {
break
}
return written, err
}
written += sz
if c == unicode.ReplacementChar && sz == 1 {
if r.Len() == 0 {
// not enough bytes for a full rune
return written - 1, nil
}
t.logln("invalid utf8 sequence")
continue
}
t.put(c)
}
return written, nil
}
// WriteWithChanges writes to the terminal state and returns the line numbers that changed.
func (t *terminal) WriteWithChanges(p []byte) ([]int, error) {
var dirtyLines = make(map[int]bool)
var written int
r := bytes.NewReader(p)
t.lock()
defer t.unlock()
for {
c, sz, err := r.ReadRune()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
written += sz
if c == unicode.ReplacementChar && sz == 1 {
if r.Len() == 0 {
// not enough bytes for a full rune
return nil, nil
}
t.logln("invalid utf8 sequence")
continue
}
t.put(c)
dirtyLines[t.cur.Y] = true
}
return uniqueSorted(dirtyLines), nil
}
// TODO: add tests for expected blocking behavior
func (t *terminal) Parse(br *bufio.Reader) error {
var locked bool
defer func() {
if locked {
t.unlock()
}
}()
for {
c, sz, err := br.ReadRune()
if err != nil {
return err
}
if c == unicode.ReplacementChar && sz == 1 {
t.logln("invalid utf8 sequence")
break
}
if !locked {
t.lock()
locked = true
}
// put rune for parsing and update state
t.put(c)
// break if our buffer is empty, or if buffer contains an
// incomplete rune.
n := br.Buffered()
if n == 0 || (n < 4 && !fullRuneBuffered(br)) {
break
}
}
return nil
}
func fullRuneBuffered(br *bufio.Reader) bool {
n := br.Buffered()
buf, err := br.Peek(n)
if err != nil {
return false
}
return utf8.FullRune(buf)
}
func (t *terminal) Resize(cols, rows int) {
t.lock()
defer t.unlock()
_ = t.resize(cols, rows)
}