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
|
// Marks in the gutter: a character beside a line saying something is wrong
// with it.
package editor
import "codeberg.org/turbo-editors/turbo-core/theme"
// Severity is how serious a mark is.
//
// This is the editor's own type, not the language server's, and deliberately
// so: this package colours and draws text, and knows nothing about protocols.
// Whoever holds the diagnostics translates them into these on the way in.
//
// view.SetMarks(map[int]editor.Severity{41: editor.MarkError})
type Severity int
// The severities a mark may have, in the order a line's worst one is chosen.
const (
// MarkNone is the zero value: a line with nothing wrong with it.
MarkNone Severity = iota
MarkHint
MarkInformation
MarkWarning
MarkError
)
// markGlyphs are what each severity draws. They are single characters from the
// range every terminal font has, because the gutter has exactly one column to
// spare and a glyph that renders as a box is worse than a letter.
var markGlyphs = map[Severity]rune{
MarkError: '×',
MarkWarning: '!',
MarkInformation: 'i',
MarkHint: '·',
}
// markStyles are the theme keys each severity is drawn in.
var markStyles = map[Severity]string{
MarkError: theme.KeyDiagnosticError,
MarkWarning: theme.KeyDiagnosticWarning,
MarkInformation: theme.KeyDiagnosticInfo,
MarkHint: theme.KeyDiagnosticInfo,
}
// SetMarks says which lines have something wrong with them, keyed by
// zero-based line number.
//
// Passing nil clears them, which is what a file with nothing wrong wants.
//
// view.SetMarks(map[int]editor.Severity{0: editor.MarkWarning, 12: editor.MarkError})
func (v *View) SetMarks(marks map[int]Severity) { v.marks = marks }
// Marks returns what SetMarks was given.
func (v *View) Marks() map[int]Severity { return v.marks }
// markOf returns the severity to draw beside a line, and whether there is one.
func (v *View) markOf(line int) (Severity, bool) {
severity, marked := v.marks[line]
if !marked || severity == MarkNone {
return MarkNone, false
}
return severity, true
}
// drawMark paints the mark for a line, in the column that separates the line
// numbers from the text.
//
// That column rather than a new one: the gutter is already `digits + 1` wide,
// the +1 being the separator, so a mark costs no layout and nothing has to be
// recomputed — not the text's left edge, not the cursor's screen column, not
// the column a mouse click lands on.
//
// The consequence is that hiding the line numbers hides the marks. The
// alternative — a one-column gutter appearing when the numbers are off — would
// make the text jump sideways the moment a server reported anything.
func (v *View) drawMark(p *Painter, th *theme.Theme, row, line int) {
if !v.lineNumbers {
return
}
severity, marked := v.markOf(line)
if !marked {
return
}
p.Text(v.gutterWidth()-1, row, string(markGlyphs[severity]), th.Style(markStyles[severity]))
}
|