gitstack

grindlemire/go-tui code browser

2.2 KB Go 90 lines 2026-02-27 · 3ac193e raw
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
package tui

import (
	"testing"
)

func TestScanStyledBytes(t *testing.T) {
	type token struct {
		kind  styledTokenKind
		bytes []byte
		runeW int // only meaningful for tokenRune
	}

	type tc struct {
		input  []byte
		tokens []token
	}

	tests := map[string]tc{
		"plain ascii": {
			input: []byte("abc"),
			tokens: []token{
				{kind: tokenRune, bytes: []byte("a"), runeW: 1},
				{kind: tokenRune, bytes: []byte("b"), runeW: 1},
				{kind: tokenRune, bytes: []byte("c"), runeW: 1},
			},
		},
		"newline": {
			input: []byte("a\nb"),
			tokens: []token{
				{kind: tokenRune, bytes: []byte("a"), runeW: 1},
				{kind: tokenNewline, bytes: []byte("\n")},
				{kind: tokenRune, bytes: []byte("b"), runeW: 1},
			},
		},
		"CSI sequence": {
			input: []byte("\x1b[31mhi"),
			tokens: []token{
				{kind: tokenANSI, bytes: []byte("\x1b[31m")},
				{kind: tokenRune, bytes: []byte("h"), runeW: 1},
				{kind: tokenRune, bytes: []byte("i"), runeW: 1},
			},
		},
		"wide rune": {
			input: []byte("好"),
			tokens: []token{
				{kind: tokenRune, bytes: []byte("好"), runeW: 2},
			},
		},
		"tab becomes space": {
			input: []byte("\t"),
			tokens: []token{
				{kind: tokenRune, bytes: []byte(" "), runeW: 1},
			},
		},
		"control chars dropped": {
			input: []byte("a\x01b"),
			tokens: []token{
				{kind: tokenRune, bytes: []byte("a"), runeW: 1},
				{kind: tokenRune, bytes: []byte("b"), runeW: 1},
			},
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			var scanner styledByteScanner
			scanner.reset(tt.input)

			for i, want := range tt.tokens {
				if !scanner.next() {
					t.Fatalf("token %d: scanner exhausted early", i)
				}
				if scanner.kind != want.kind {
					t.Fatalf("token %d: kind = %d, want %d", i, scanner.kind, want.kind)
				}
				if string(scanner.bytes()) != string(want.bytes) {
					t.Fatalf("token %d: bytes = %q, want %q", i, scanner.bytes(), want.bytes)
				}
				if want.kind == tokenRune && scanner.runeWidth != want.runeW {
					t.Fatalf("token %d: runeWidth = %d, want %d", i, scanner.runeWidth, want.runeW)
				}
			}

			if scanner.next() {
				t.Fatalf("expected scanner exhausted, got kind=%d bytes=%q", scanner.kind, scanner.bytes())
			}
		})
	}
}