forked from elves/elvish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
buffer.go
98 lines (87 loc) · 2.25 KB
/
buffer.go
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
package ui
// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
Text string
Width byte
Style string
}
// Pos is the position within a buffer.
type Pos struct {
Line, Col int
}
// CellsWidth returns the total width of a Cell slice.
func CellsWidth(cs []Cell) int {
w := 0
for _, c := range cs {
w += int(c.Width)
}
return w
}
// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func CompareCells(r1, r2 []Cell) (bool, int) {
for i, c := range r1 {
if i >= len(r2) || c != r2[i] {
return false, i
}
}
if len(r1) < len(r2) {
return false, len(r1)
}
return true, 0
}
// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
Width, Col int
// Lines the content of the buffer.
Lines [][]Cell
// Dot is what the user perceives as the cursor.
Dot Pos
}
// NewBuffer builds a new buffer, with one empty line.
func NewBuffer(width int) *Buffer {
return &Buffer{Width: width, Lines: [][]Cell{make([]Cell, 0, width)}}
}
func (b *Buffer) SetLines(lines ...[]Cell) *Buffer {
b.Lines = lines
b.Col = CellsWidth(lines[len(lines)-1])
return b
}
func (b *Buffer) SetDot(dot Pos) *Buffer {
b.Dot = dot
return b
}
// Cursor returns the current position of the cursor.
func (b *Buffer) Cursor() Pos {
return Pos{len(b.Lines) - 1, b.Col}
}
// BuffersHeight computes the combined height of a number of buffers.
func BuffersHeight(bufs ...*Buffer) (l int) {
for _, buf := range bufs {
if buf != nil {
l += len(buf.Lines)
}
}
return
}
// TrimToLines trims a buffer to the lines [low, high).
func (b *Buffer) TrimToLines(low, high int) {
for i := 0; i < low; i++ {
b.Lines[i] = nil
}
for i := high; i < len(b.Lines); i++ {
b.Lines[i] = nil
}
b.Lines = b.Lines[low:high]
b.Dot.Line -= low
if b.Dot.Line < 0 {
b.Dot.Line = 0
}
}