▍ humdrum codex / glint v1.0.2
license AGPL-3.0
2.2 KB 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
package editor

import (
	"strings"

	"glint/internal/theme"

	"github.com/alecthomas/chroma/v2"
	"github.com/alecthomas/chroma/v2/lexers"
	"github.com/charmbracelet/lipgloss"
)

// ScanCode styles source code minimally (Alabaster philosophy): only strings,
// comments, and numbers/literals get color; keywords, names, and operators stay
// at base Text; punctuation is Muted. It returns one []Span per input line whose
// concatenated text equals the line exactly (the markup-visible invariant), with
// multi-line tokens (block comments, multi-line strings) split across lines.
//
// filename selects the chroma lexer; an unrecognized file falls back to a plain
// lexer (everything base). On a tokenizer error it degrades to the prose scanner.
func ScanCode(lines []string, filename string, th theme.Theme) [][]Span {
	lexer := lexers.Match(filename)
	if lexer == nil {
		lexer = lexers.Fallback
	}
	lexer = chroma.Coalesce(lexer)

	it, err := lexer.Tokenise(nil, strings.Join(lines, "\n"))
	if err != nil {
		return ScanLines(lines, th)
	}

	out := make([][]Span, len(lines))
	li := 0
	var cur []Span
	emit := func(text string, style lipgloss.Style) {
		if text != "" {
			cur = append(cur, Span{Text: text, Style: style})
		}
	}
	for _, tok := range it.Tokens() {
		style := codeStyle(tok.Type, th)
		val := tok.Value
		for {
			nl := strings.IndexByte(val, '\n')
			if nl < 0 {
				emit(val, style)
				break
			}
			emit(val[:nl], style)
			if li < len(out) {
				out[li] = cur
			}
			cur = nil
			li++
			val = val[nl+1:]
		}
	}
	if li < len(out) {
		out[li] = cur
	}
	return out
}

// codeStyle maps a chroma token type to a theme color, coloring only strings,
// comments, and numbers/literals; everything else stays base, punctuation muted.
func codeStyle(tt chroma.TokenType, th theme.Theme) lipgloss.Style {
	switch {
	case tt.SubCategory() == chroma.String:
		return lipgloss.NewStyle().Foreground(th.Code)
	case tt.Category() == chroma.Comment:
		return lipgloss.NewStyle().Foreground(th.Comment)
	case tt.SubCategory() == chroma.Number || tt.Category() == chroma.Literal:
		return lipgloss.NewStyle().Foreground(th.Accent)
	case tt.Category() == chroma.Punctuation:
		return lipgloss.NewStyle().Foreground(th.Muted)
	default:
		return lipgloss.NewStyle().Foreground(th.Text)
	}
}