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
package util

import (
	avt "avt-go"
)

// TextUnwrapper joins wrapped lines back into logical lines.
type TextUnwrapper struct {
	wrapped string
}

func NewTextUnwrapper() *TextUnwrapper {
	return &TextUnwrapper{}
}

func (u *TextUnwrapper) Push(line *avt.Line) (string, bool) {
	if line.Wrapped {
		u.wrapped += line.Text()
		return "", false
	}
	u.wrapped += trimRight(line.Text())
	result := u.wrapped
	u.wrapped = ""
	return result, true
}

func (u *TextUnwrapper) Flush() (string, bool) {
	if u.wrapped == "" {
		return "", false
	}
	result := u.wrapped
	u.wrapped = ""
	return result, true
}

func trimRight(s string) string {
	end := len(s)
	for end > 0 && (s[end-1] == ' ' || s[end-1] == '\t') {
		end--
	}
	return s[:end]
}

// TextCollector wraps a Vt and collects logical lines from scrollback.
type TextCollector struct {
	vt        *avt.Vt
	unwrapper *TextUnwrapper
}

func NewTextCollector(vt *avt.Vt) *TextCollector {
	return &TextCollector{
		vt:        vt,
		unwrapper: NewTextUnwrapper(),
	}
}

func (tc *TextCollector) FeedStr(s string) []string {
	changes := tc.vt.FeedStr(s)
	var lines []string
	for i := range changes.Scrollback {
		if line, ok := tc.unwrapper.Push(&changes.Scrollback[i]); ok {
			lines = append(lines, line)
		}
	}
	return lines
}

func (tc *TextCollector) Resize(cols, rows int) []string {
	changes := tc.vt.Resize(cols, rows)
	var lines []string
	for i := range changes.Scrollback {
		if line, ok := tc.unwrapper.Push(&changes.Scrollback[i]); ok {
			lines = append(lines, line)
		}
	}
	return lines
}

func (tc *TextCollector) Flush() []string {
	unwrapper := tc.unwrapper
	var lines []string

	for _, l := range tc.vt.Lines() {
		if line, ok := unwrapper.Push(l); ok {
			lines = append(lines, line)
		}
	}

	if line, ok := unwrapper.Flush(); ok {
		lines = append(lines, line)
	}

	// trim trailing empty lines
	for len(lines) > 0 && lines[len(lines)-1] == "" {
		lines = lines[:len(lines)-1]
	}

	return lines
}