Files
byre/internal/terminal/text.go
T

111 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package terminal
import (
"fmt"
"strings"
"unicode/utf8"
)
const textLimit = 64 << 10
func Text(b []byte) string {
s := plainText(b)
if len(s) > textLimit {
s = s[len(s)-textLimit:]
s = s[strings.IndexByte(s, '\n')+1:]
}
return s
}
// Lines reads a range of the retained output. Positive offsets are one-based;
// negative offsets count back from the last line.
func Lines(b []byte, offset, limit int) string {
lines := strings.Split(strings.TrimSuffix(plainText(b), "\n"), "\n")
start := 0
if offset < 0 {
start = len(lines) + offset
} else {
start = offset - 1
}
start = min(max(0, start), len(lines))
end := start + min(limit, len(lines)-start)
if start == end {
return fmt.Sprintf("[no lines in range; %d retained lines]", len(lines))
}
return fmt.Sprintf("[lines %d–%d of %d retained lines]\n", min(start+1, end), end, len(lines)) + strings.Join(lines[start:end], "\n")
}
func plainText(b []byte) string {
out := strings.Builder{}
line := []rune{}
col := 0
flush := func() {
out.WriteString(strings.TrimRight(string(line), " "))
out.WriteByte('\n')
line = line[:0]
col = 0
}
for i := 0; i < len(b); {
c := b[i]
switch {
case c == 0x1b:
end, final := skipEscape(b, i)
if final == 'K' {
line = line[:min(col, len(line))]
}
i = end + 1
continue
case c == '\n':
flush()
case c == '\r':
col = 0
case c == '\b':
col = max(0, col-1)
case c == '\t' || c >= 0x20 && c != 0x7f:
r, n := utf8.DecodeRune(b[i:])
for len(line) < col {
line = append(line, ' ')
}
if col < len(line) {
line[col] = r
} else {
line = append(line, r)
}
col++
i += n
continue
}
i++
}
return out.String() + strings.TrimRight(string(line), " ")
}
func skipEscape(b []byte, i int) (int, byte) {
if i+1 >= len(b) {
return i, 0
}
i++
switch b[i] {
case '[':
for i++; i < len(b); i++ {
if b[i] >= 0x40 && b[i] <= 0x7e {
return i, b[i]
}
}
return len(b) - 1, 0
case ']', 'P', '_', '^':
for i++; i < len(b); i++ {
if b[i] == 0x07 {
return i, 0
}
if b[i] == 0x1b && i+1 < len(b) && b[i+1] == '\\' {
return i + 1, 0
}
}
return len(b) - 1, 0
case '(', ')', '*', '+', '#', '%':
return min(i+1, len(b)-1), 0
}
return i, 0
}