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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
package terminal
import "avt-go/types"
// Snapshot is a comparable dump of all terminal state used in equality checks.
type Snapshot struct {
ActiveBufferType BufferType
Cursor Cursor
Pen types.Pen
Charsets [2]types.Charset
ActiveCharset int
Tabs types.Tabs
InsertMode bool
OriginMode bool
AutoWrapMode bool
NewLineMode bool
CursorKeysAppMode bool
TopMargin int
BottomMargin int
SavedCtx SavedCtxSnapshot
AltSavedCtx SavedCtxSnapshot
// PrimaryView is the visible lines of the primary buffer (text + wrapped flag).
PrimaryView []LineSnapshot
// AltView is set only when alternate buffer is active.
AltView []LineSnapshot
}
type SavedCtxSnapshot struct {
CursorCol int
CursorRow int
Pen types.Pen
OriginMode bool
AutoWrapMode bool
}
type LineSnapshot struct {
Text string
Wrapped bool
}
func (t *Terminal) Snapshot() Snapshot {
var primaryCtx, altCtx *savedCtx
if t.activeBufferType == BufferTypePrimary {
primaryCtx = &t.savedCtx
altCtx = &t.alternateSavedCtx
} else {
primaryCtx = &t.alternateSavedCtx
altCtx = &t.savedCtx
}
snap := Snapshot{
ActiveBufferType: t.activeBufferType,
Cursor: t.cursor,
Pen: t.pen,
Charsets: t.charsets,
ActiveCharset: t.activeCharset,
Tabs: t.tabs,
InsertMode: t.insertMode,
OriginMode: t.originMode,
AutoWrapMode: t.autoWrapMode,
NewLineMode: t.newLineMode,
CursorKeysAppMode: t.cursorKeysMode == cursorKeysModeApplication,
TopMargin: t.topMargin,
BottomMargin: t.bottomMargin,
SavedCtx: snapshotCtx(primaryCtx),
AltSavedCtx: snapshotCtx(altCtx),
}
for _, l := range t.primaryBuffer().View() {
snap.PrimaryView = append(snap.PrimaryView, LineSnapshot{Text: l.Text(), Wrapped: l.Wrapped})
}
if t.activeBufferType == BufferTypeAlternate {
for _, l := range t.alternateBuffer().View() {
snap.AltView = append(snap.AltView, LineSnapshot{Text: l.Text(), Wrapped: l.Wrapped})
}
}
return snap
}
// Verify checks internal consistency invariants (mirrors Rust's terminal.verify()).
func (t *Terminal) Verify() []string {
var errs []string
if t.cursor.Row >= t.rows {
errs = append(errs, "cursor.Row >= rows")
}
if t.cursor.Col > t.cols {
errs = append(errs, "cursor.Col > cols")
}
for i, l := range t.buffer.Lines() {
if l.Len() != t.cols {
errs = append(errs, "line "+itoa(i)+" len "+itoa(l.Len())+" != cols "+itoa(t.cols))
}
}
lines := t.buffer.Lines()
if len(lines) > 0 && lines[len(lines)-1].Wrapped {
errs = append(errs, "last line is wrapped")
}
for _, l := range t.buffer.Lines() {
for i := 0; i < t.cols; i++ {
c := l.At(i)
if c.Occupancy() == types.OccupancyWideTail {
if i == 0 {
errs = append(errs, "WideTail at col 0")
} else if l.At(i-1).Occupancy() != types.OccupancyWideHead {
errs = append(errs, "WideTail not preceded by WideHead at col "+itoa(i))
}
} else if c.Occupancy() == types.OccupancyWideHead {
if i+1 >= t.cols || l.At(i+1).Occupancy() != types.OccupancyWideTail {
errs = append(errs, "WideHead not followed by WideTail at col "+itoa(i))
}
}
}
}
return errs
}
func snapshotCtx(c *savedCtx) SavedCtxSnapshot {
return SavedCtxSnapshot{
CursorCol: c.cursorCol,
CursorRow: c.cursorRow,
Pen: c.pen,
OriginMode: c.originMode,
AutoWrapMode: c.autoWrapMode,
}
}
func itoa(n int) string {
if n == 0 {
return "0"
}
neg := false
if n < 0 {
neg = true
n = -n
}
buf := [20]byte{}
i := len(buf)
for n > 0 {
i--
buf[i] = byte('0' + n%10)
n /= 10
}
if neg {
i--
buf[i] = '-'
}
return string(buf[i:])
}
|