gitstack

grindlemire/go-tui code browser

3.6 KB Go 130 lines 2026-06-03 · d15bb9f 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
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
package tuigen

import (
	"testing"
)

func TestLexer_GoExpressions(t *testing.T) {
	type tc struct {
		input   string
		literal string
	}

	tests := map[string]tc{
		"simple":          {input: "{x}", literal: "x"},
		"with spaces":     {input: "{ x + y }", literal: " x + y "},
		"nested braces":   {input: "{map[string]int{}}", literal: "map[string]int{}"},
		"deeply nested":   {input: "{func() { if true { x } }()}", literal: "func() { if true { x } }()"},
		"with string":     {input: `{fmt.Sprintf("%d", x)}`, literal: `fmt.Sprintf("%d", x)`},
		"with raw string": {input: "{`hello`}", literal: "`hello`"},
		"function call":   {input: "{onClick()}", literal: "onClick()"},
		"method call":     {input: "{s.Method(a, b)}", literal: "s.Method(a, b)"},
		"struct literal":  {input: "{Point{X: 1, Y: 2}}", literal: "Point{X: 1, Y: 2}"},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			l := NewLexer("test.gsx", tt.input)
			// First, consume the opening brace via Next()
			// ReadGoExpr expects to be called after { was tokenized
			brace := l.Next()
			if brace.Type != TokenLBrace {
				t.Fatalf("expected TokenLBrace, got %v", brace.Type)
			}
			tok := l.ReadGoExpr()
			if tok.Type != TokenGoExpr {
				t.Errorf("Type = %v, want TokenGoExpr", tok.Type)
			}
			if tok.Literal != tt.literal {
				t.Errorf("Literal = %q, want %q", tok.Literal, tt.literal)
			}
		})
	}
}

func TestLexer_ReadBalancedBracesFrom(t *testing.T) {
	type tc struct {
		input    string
		startPos int
		expected string
		hasError bool
	}

	tests := map[string]tc{
		"simple":          {input: "{x}", startPos: 0, expected: "x"},
		"with spaces":     {input: "{ x + y }", startPos: 0, expected: " x + y "},
		"nested braces":   {input: "{map[string]int{}}", startPos: 0, expected: "map[string]int{}"},
		"with string":     {input: `{fmt.Sprintf("%d", x)}`, startPos: 0, expected: `fmt.Sprintf("%d", x)`},
		"with raw string": {input: "{`hello`}", startPos: 0, expected: "`hello`"},
		"deeply nested":   {input: "{func() { if true { x } }()}", startPos: 0, expected: "func() { if true { x } }()"},
		"not at brace":    {input: "abc{x}", startPos: 0, hasError: true},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			l := NewLexer("test.tui", tt.input)
			result, err := l.ReadBalancedBracesFrom(tt.startPos)

			if tt.hasError {
				if err == nil {
					t.Error("expected error, got nil")
				}
				return
			}

			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}

			if result != tt.expected {
				t.Errorf("ReadBalancedBracesFrom(%d) = %q, want %q", tt.startPos, result, tt.expected)
			}
		})
	}
}

func TestLexer_ComponentCall(t *testing.T) {
	type tc struct {
		input       string
		wantType    TokenType
		wantLiteral string
	}

	tests := map[string]tc{
		"simple call": {
			input:       "@Card",
			wantType:    TokenAtCall,
			wantLiteral: "Card",
		},
		"multi-word name": {
			input:       "@MyCustomComponent",
			wantType:    TokenAtCall,
			wantLiteral: "MyCustomComponent",
		},
		"header component": {
			input:       "@Header",
			wantType:    TokenAtCall,
			wantLiteral: "Header",
		},
		"lowercase is component expr": {
			input:       "@unknown",
			wantType:    TokenAtExpr,
			wantLiteral: "unknown",
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			l := NewLexer("test.gsx", tt.input)
			tok := l.Next()

			if tok.Type != tt.wantType {
				t.Errorf("token type = %v, want %v", tok.Type, tt.wantType)
			}
			if tt.wantLiteral != "" && tok.Literal != tt.wantLiteral {
				t.Errorf("token literal = %q, want %q", tok.Literal, tt.wantLiteral)
			}
		})
	}
}