gitstack

grindlemire/go-tui code browser

3.8 KB Go 194 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package formatter

import (
	"strings"
	"testing"
)

func TestCommentPreservationInElements(t *testing.T) {
	type tc struct {
		source string
	}

	tests := map[string]tc{
		"comments before closing tag stay inside element": {
			source: `package main

templ Foo() {
	<div>
		<span>Hello</span>
		// comment before closing
	</div>
}
`,
		},
		"comments as only children stay inside element": {
			source: `package main

templ Foo() {
	<div>
		// only comments as children
		// another comment
	</div>
}
`,
		},
		"comments at end of nested div stay inside": {
			source: `package main

templ Foo() {
	<div>
		<div>
			<span>Text</span>
			// trailing comment in inner div
		</div>
	</div>
}
`,
		},
		"comments between elements become leading comments": {
			source: `package main

templ Foo() {
	<div>
		<span>Before</span>
		// comment 1
		// comment 2
		<span>After</span>
	</div>
}
`,
		},
		"orphan in inner div and outer div stay in place": {
			source: `package main

templ Foo() {
	<div>
		<div>
			<span>Text</span>
			// orphan inside inner div
		</div>
		<span>After inner div</span>
		// orphan inside outer div
	</div>
}
`,
		},
		"comments before closing nested divs stay in place": {
			source: `package main

templ Foo() {
	<div>
		<div>
			// comment at end of inner
		</div>
		// comment at end of outer
	</div>
}
`,
		},
		"commented out closing div preserves all comments": {
			source: `package main

templ Foo() {
	<div class="flex-col">
		<span>Bold text</span>
		<span>Dim text</span>
		// <span>Italic text</span>
		// </div>
		//
		// <br />
		// <div class="flex gap-2">
		// 	<span>Red</span>
		<span>Blue</span>
		<span>White</span>
	</div>
}
`,
		},
		"orphan comments in component body preserved": {
			source: `package main

templ Foo() {
	// orphan at start
	<div>
		<span>Text</span>
	</div>
	// orphan at end
}
`,
		},
	}

	fmtr := &Formatter{
		IndentString: "\t",
		FixImports:   false,
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			formatted, err := fmtr.Format("test.gsx", tt.source)
			if err != nil {
				t.Fatalf("format error: %v", err)
			}

			// Verify no comments were lost
			sourceComments := countCommentLines(tt.source)
			formattedComments := countCommentLines(formatted)
			if formattedComments < sourceComments {
				t.Errorf("comments lost: source had %d comment lines, formatted has %d\nFormatted:\n%s",
					sourceComments, formattedComments, formatted)
			}

			// Verify comments stay at the same nesting depth
			sourceCommentDepths := commentDepths(tt.source)
			formattedCommentDepths := commentDepths(formatted)
			if sourceCommentDepths != formattedCommentDepths {
				t.Errorf("comment nesting changed:\n  source depths:    %s\n  formatted depths: %s\nFormatted:\n%s",
					sourceCommentDepths, formattedCommentDepths, formatted)
			}

			// Verify idempotency
			formatted2, err := fmtr.Format("test.gsx", formatted)
			if err != nil {
				t.Fatalf("second format error: %v", err)
			}
			if formatted2 != formatted {
				t.Errorf("not idempotent!\nFirst format:\n%s\nSecond format:\n%s", formatted, formatted2)
			}
		})
	}
}

// countCommentLines counts lines starting with // (after trimming whitespace).
func countCommentLines(s string) int {
	count := 0
	for line := range strings.SplitSeq(s, "\n") {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "//") {
			count++
		}
	}
	return count
}

// commentDepths returns a string showing the tab depth of each comment line.
// e.g., "2,2,3,3" means 4 comment lines at depths 2,2,3,3.
func commentDepths(s string) string {
	var depths []string
	for line := range strings.SplitSeq(s, "\n") {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "//") {
			tabs := 0
			for _, ch := range line {
				if ch == '\t' {
					tabs++
				} else {
					break
				}
			}
			depths = append(depths, strings.Repeat(".", tabs))
		}
	}
	return strings.Join(depths, ",")
}