turbo-editors/turbo-corepublic Fork 0
28d59854361aeda8541d853093e732126f3d7bff
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

marks_test.go · 149 lines · 4.8 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 16h ago1package editor
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/gdamore/tcell/v2"
8
9 "codeberg.org/turbo-editors/turbo-core/theme"
10)
11
12// markAt returns the character in the gutter's separator column on a row, and
13// the style it is drawn in.
14func markAt(t *testing.T, view *View, screen tcell.SimulationScreen, row int) (rune, tcell.Style) {
15 t.Helper()
16
17 cells, width, _ := screen.GetContents()
18 cell := cells[row*width+view.gutterWidth()-1]
19 if len(cell.Runes) == 0 {
20 return ' ', cell.Style
21 }
22 return cell.Runes[0], cell.Style
23}
24
25func TestAMarkIsDrawnBesideItsLine(t *testing.T) {
26 view, screen, p := newTestView(t, "one\ntwo\nthree\n", 40, 8)
27 view.SetMarks(map[int]Severity{1: MarkError})
28
29 draw(t, view, screen, p)
30
31 if got, _ := markAt(t, view, screen, 1); got != '\u00d7' {
32 t.Errorf("the gutter shows %q beside line 2, want the error mark", got)
33 }
34 if got, _ := markAt(t, view, screen, 0); got != ' ' {
35 t.Errorf("line 1 shows %q and has nothing wrong with it", got)
36 }
37}
38
39func TestAMarkDoesNotMoveTheText(t *testing.T) {
40 // The whole reason the separator column is used: the text's left edge, the
41 // cursor's screen column and the column a click lands on must all stay put.
42 view, screen, p := newTestView(t, "package main\n", 40, 8)
43 before := view.gutterWidth()
44
45 view.SetMarks(map[int]Severity{0: MarkError})
46 lines := draw(t, view, screen, p)
47
48 if got := view.gutterWidth(); got != before {
49 t.Errorf("the gutter went from %d to %d columns", before, got)
50 }
51 if got := []rune(lines[0])[before]; got != 'p' {
52 t.Errorf("the text starts with %q at column %d, want it where it was", got, before)
53 }
54}
55
56func TestEachSeverityHasItsOwnGlyphAndColour(t *testing.T) {
57 // The three theme keys exist in every shipped theme and were drawn nowhere
58 // until now. A severity painted in the wrong one is invisible as a bug.
59 th, err := theme.Load("turbo-classic", "")
60 if err != nil {
61 t.Fatalf("Load() error = %v", err)
62 }
63
64 seen := map[rune]Severity{}
65 for _, severity := range []Severity{MarkError, MarkWarning, MarkInformation, MarkHint} {
66 glyph, hasGlyph := markGlyphs[severity]
67 key, hasStyle := markStyles[severity]
68 if !hasGlyph || !hasStyle {
69 t.Fatalf("severity %v has glyph=%v style=%v", severity, hasGlyph, hasStyle)
70 }
71 if other, clash := seen[glyph]; clash && severity != MarkHint {
72 t.Errorf("severities %v and %v draw the same glyph %q", other, severity, glyph)
73 }
74 seen[glyph] = severity
75
76 if fg, _, _ := th.Style(key).Decompose(); fg == 0 {
77 t.Errorf("severity %v resolves to no colour through %q", severity, key)
78 }
79 }
80
81 if markStyles[MarkError] == markStyles[MarkWarning] {
82 t.Error("an error and a warning are drawn in the same colour")
83 }
84}
85
86func TestAMarkIsDrawnInItsSeveritysColour(t *testing.T) {
87 view, screen, p := newTestView(t, "one\ntwo\n", 40, 8)
88 view.SetMarks(map[int]Severity{0: MarkError, 1: MarkWarning})
89
90 draw(t, view, screen, p)
91
92 _, errorStyle := markAt(t, view, screen, 0)
93 _, warningStyle := markAt(t, view, screen, 1)
94 if errorStyle == warningStyle {
95 t.Error("the error and the warning were drawn in the same style")
96 }
97
98 th := testTheme(t)
99 wantFg, _, _ := th.Style(theme.KeyDiagnosticError).Decompose()
100 if gotFg, _, _ := errorStyle.Decompose(); gotFg != wantFg {
101 t.Errorf("the error mark is %v, want the theme's diagnostic.error %v", gotFg, wantFg)
102 }
103}
104
105func TestHidingTheLineNumbersHidesTheMarks(t *testing.T) {
106 // The consequence of using the separator column, stated where somebody
107 // changing it will read it: there is no separator column without numbers.
108 view, screen, p := newTestView(t, "one\n", 40, 8)
109 view.SetMarks(map[int]Severity{0: MarkError})
110 view.SetLineNumbers(false)
111
112 lines := draw(t, view, screen, p)
113
114 if strings.ContainsRune(strings.Join(lines, ""), '\u00d7') {
115 t.Errorf("a mark was drawn with the gutter hidden:\n%s", strings.Join(lines, "\n"))
116 }
117}
118
119func TestClearingTheMarksClearsTheGutter(t *testing.T) {
120 view, screen, p := newTestView(t, "one\n", 40, 8)
121 view.SetMarks(map[int]Severity{0: MarkError})
122 draw(t, view, screen, p)
123
124 view.SetMarks(nil)
125 draw(t, view, screen, p)
126
127 if got, _ := markAt(t, view, screen, 0); got != ' ' {
128 t.Errorf("the gutter still shows %q after the marks were cleared", got)
129 }
130}
131
132func TestAZeroSeverityIsNotAMark(t *testing.T) {
133 // MarkNone is the zero value, so a map built by hand can hold one without
134 // meaning to say anything.
135 view, screen, p := newTestView(t, "one\n", 40, 8)
136 view.SetMarks(map[int]Severity{0: MarkNone})
137
138 // Asserted on markOf rather than on the screen: a severity with no glyph
139 // draws NUL, which a simulated screen shows as a blank — so the drawing
140 // looks right whether or not the guard is there.
141 if severity, marked := view.markOf(0); marked {
142 t.Errorf("markOf(0) = %v, true; a line marked with nothing has no mark", severity)
143 }
144
145 draw(t, view, screen, p)
146 if got, _ := markAt(t, view, screen, 0); got != ' ' {
147 t.Errorf("the gutter shows %q for a line marked with nothing", got)
148 }
149}