turbo-editors/turbo-corepublic Fork 0
main
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.

🛟 Updated. 28d5985 · on main · k33g · 4h ago
marks_test.go · 149 lines · 4.8 KBGo Blame HistoryRaw
  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
package editor

import (
	"strings"
	"testing"

	"github.com/gdamore/tcell/v2"

	"codeberg.org/turbo-editors/turbo-core/theme"
)

// markAt returns the character in the gutter's separator column on a row, and
// the style it is drawn in.
func markAt(t *testing.T, view *View, screen tcell.SimulationScreen, row int) (rune, tcell.Style) {
	t.Helper()

	cells, width, _ := screen.GetContents()
	cell := cells[row*width+view.gutterWidth()-1]
	if len(cell.Runes) == 0 {
		return ' ', cell.Style
	}
	return cell.Runes[0], cell.Style
}

func TestAMarkIsDrawnBesideItsLine(t *testing.T) {
	view, screen, p := newTestView(t, "one\ntwo\nthree\n", 40, 8)
	view.SetMarks(map[int]Severity{1: MarkError})

	draw(t, view, screen, p)

	if got, _ := markAt(t, view, screen, 1); got != '\u00d7' {
		t.Errorf("the gutter shows %q beside line 2, want the error mark", got)
	}
	if got, _ := markAt(t, view, screen, 0); got != ' ' {
		t.Errorf("line 1 shows %q and has nothing wrong with it", got)
	}
}

func TestAMarkDoesNotMoveTheText(t *testing.T) {
	// The whole reason the separator column is used: the text's left edge, the
	// cursor's screen column and the column a click lands on must all stay put.
	view, screen, p := newTestView(t, "package main\n", 40, 8)
	before := view.gutterWidth()

	view.SetMarks(map[int]Severity{0: MarkError})
	lines := draw(t, view, screen, p)

	if got := view.gutterWidth(); got != before {
		t.Errorf("the gutter went from %d to %d columns", before, got)
	}
	if got := []rune(lines[0])[before]; got != 'p' {
		t.Errorf("the text starts with %q at column %d, want it where it was", got, before)
	}
}

func TestEachSeverityHasItsOwnGlyphAndColour(t *testing.T) {
	// The three theme keys exist in every shipped theme and were drawn nowhere
	// until now. A severity painted in the wrong one is invisible as a bug.
	th, err := theme.Load("turbo-classic", "")
	if err != nil {
		t.Fatalf("Load() error = %v", err)
	}

	seen := map[rune]Severity{}
	for _, severity := range []Severity{MarkError, MarkWarning, MarkInformation, MarkHint} {
		glyph, hasGlyph := markGlyphs[severity]
		key, hasStyle := markStyles[severity]
		if !hasGlyph || !hasStyle {
			t.Fatalf("severity %v has glyph=%v style=%v", severity, hasGlyph, hasStyle)
		}
		if other, clash := seen[glyph]; clash && severity != MarkHint {
			t.Errorf("severities %v and %v draw the same glyph %q", other, severity, glyph)
		}
		seen[glyph] = severity

		if fg, _, _ := th.Style(key).Decompose(); fg == 0 {
			t.Errorf("severity %v resolves to no colour through %q", severity, key)
		}
	}

	if markStyles[MarkError] == markStyles[MarkWarning] {
		t.Error("an error and a warning are drawn in the same colour")
	}
}

func TestAMarkIsDrawnInItsSeveritysColour(t *testing.T) {
	view, screen, p := newTestView(t, "one\ntwo\n", 40, 8)
	view.SetMarks(map[int]Severity{0: MarkError, 1: MarkWarning})

	draw(t, view, screen, p)

	_, errorStyle := markAt(t, view, screen, 0)
	_, warningStyle := markAt(t, view, screen, 1)
	if errorStyle == warningStyle {
		t.Error("the error and the warning were drawn in the same style")
	}

	th := testTheme(t)
	wantFg, _, _ := th.Style(theme.KeyDiagnosticError).Decompose()
	if gotFg, _, _ := errorStyle.Decompose(); gotFg != wantFg {
		t.Errorf("the error mark is %v, want the theme's diagnostic.error %v", gotFg, wantFg)
	}
}

func TestHidingTheLineNumbersHidesTheMarks(t *testing.T) {
	// The consequence of using the separator column, stated where somebody
	// changing it will read it: there is no separator column without numbers.
	view, screen, p := newTestView(t, "one\n", 40, 8)
	view.SetMarks(map[int]Severity{0: MarkError})
	view.SetLineNumbers(false)

	lines := draw(t, view, screen, p)

	if strings.ContainsRune(strings.Join(lines, ""), '\u00d7') {
		t.Errorf("a mark was drawn with the gutter hidden:\n%s", strings.Join(lines, "\n"))
	}
}

func TestClearingTheMarksClearsTheGutter(t *testing.T) {
	view, screen, p := newTestView(t, "one\n", 40, 8)
	view.SetMarks(map[int]Severity{0: MarkError})
	draw(t, view, screen, p)

	view.SetMarks(nil)
	draw(t, view, screen, p)

	if got, _ := markAt(t, view, screen, 0); got != ' ' {
		t.Errorf("the gutter still shows %q after the marks were cleared", got)
	}
}

func TestAZeroSeverityIsNotAMark(t *testing.T) {
	// MarkNone is the zero value, so a map built by hand can hold one without
	// meaning to say anything.
	view, screen, p := newTestView(t, "one\n", 40, 8)
	view.SetMarks(map[int]Severity{0: MarkNone})

	// Asserted on markOf rather than on the screen: a severity with no glyph
	// draws NUL, which a simulated screen shows as a blank — so the drawing
	// looks right whether or not the guard is there.
	if severity, marked := view.markOf(0); marked {
		t.Errorf("markOf(0) = %v, true; a line marked with nothing has no mark", severity)
	}

	draw(t, view, screen, p)
	if got, _ := markAt(t, view, screen, 0); got != ' ' {
		t.Errorf("the gutter shows %q for a line marked with nothing", got)
	}
}