| 🛟 Updated. 28d5985 k33g 18h ago | 1 | // Marks in the gutter: a character beside a line saying something is wrong |
| 2 | // with it. |
| 3 | |
| 4 | package editor |
| 5 | |
| 📦 Turbo Core f3ade8d k33g 11h ago | 6 | import "rickub.com/turbo-editors/turbo-core/theme" |
| 🛟 Updated. 28d5985 k33g 18h ago | 7 | |
| 8 | // Severity is how serious a mark is. |
| 9 | // |
| 10 | // This is the editor's own type, not the language server's, and deliberately |
| 11 | // so: this package colours and draws text, and knows nothing about protocols. |
| 12 | // Whoever holds the diagnostics translates them into these on the way in. |
| 13 | // |
| 14 | // view.SetMarks(map[int]editor.Severity{41: editor.MarkError}) |
| 15 | type Severity int |
| 16 | |
| 17 | // The severities a mark may have, in the order a line's worst one is chosen. |
| 18 | const ( |
| 19 | // MarkNone is the zero value: a line with nothing wrong with it. |
| 20 | MarkNone Severity = iota |
| 21 | MarkHint |
| 22 | MarkInformation |
| 23 | MarkWarning |
| 24 | MarkError |
| 25 | ) |
| 26 | |
| 27 | // markGlyphs are what each severity draws. They are single characters from the |
| 28 | // range every terminal font has, because the gutter has exactly one column to |
| 29 | // spare and a glyph that renders as a box is worse than a letter. |
| 30 | var markGlyphs = map[Severity]rune{ |
| 31 | MarkError: '×', |
| 32 | MarkWarning: '!', |
| 33 | MarkInformation: 'i', |
| 34 | MarkHint: '·', |
| 35 | } |
| 36 | |
| 37 | // markStyles are the theme keys each severity is drawn in. |
| 38 | var markStyles = map[Severity]string{ |
| 39 | MarkError: theme.KeyDiagnosticError, |
| 40 | MarkWarning: theme.KeyDiagnosticWarning, |
| 41 | MarkInformation: theme.KeyDiagnosticInfo, |
| 42 | MarkHint: theme.KeyDiagnosticInfo, |
| 43 | } |
| 44 | |
| 45 | // SetMarks says which lines have something wrong with them, keyed by |
| 46 | // zero-based line number. |
| 47 | // |
| 48 | // Passing nil clears them, which is what a file with nothing wrong wants. |
| 49 | // |
| 50 | // view.SetMarks(map[int]editor.Severity{0: editor.MarkWarning, 12: editor.MarkError}) |
| 51 | func (v *View) SetMarks(marks map[int]Severity) { v.marks = marks } |
| 52 | |
| 53 | // Marks returns what SetMarks was given. |
| 54 | func (v *View) Marks() map[int]Severity { return v.marks } |
| 55 | |
| 56 | // markOf returns the severity to draw beside a line, and whether there is one. |
| 57 | func (v *View) markOf(line int) (Severity, bool) { |
| 58 | severity, marked := v.marks[line] |
| 59 | if !marked || severity == MarkNone { |
| 60 | return MarkNone, false |
| 61 | } |
| 62 | return severity, true |
| 63 | } |
| 64 | |
| 65 | // drawMark paints the mark for a line, in the column that separates the line |
| 66 | // numbers from the text. |
| 67 | // |
| 68 | // That column rather than a new one: the gutter is already `digits + 1` wide, |
| 69 | // the +1 being the separator, so a mark costs no layout and nothing has to be |
| 70 | // recomputed — not the text's left edge, not the cursor's screen column, not |
| 71 | // the column a mouse click lands on. |
| 72 | // |
| 73 | // The consequence is that hiding the line numbers hides the marks. The |
| 74 | // alternative — a one-column gutter appearing when the numbers are off — would |
| 75 | // make the text jump sideways the moment a server reported anything. |
| 76 | func (v *View) drawMark(p *Painter, th *theme.Theme, row, line int) { |
| 77 | if !v.lineNumbers { |
| 78 | return |
| 79 | } |
| 80 | severity, marked := v.markOf(line) |
| 81 | if !marked { |
| 82 | return |
| 83 | } |
| 84 | p.Text(v.gutterWidth()-1, row, string(markGlyphs[severity]), th.Style(markStyles[severity])) |
| 85 | } |