forked from alecthomas/chroma
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi.go
More file actions
135 lines (122 loc) · 3.36 KB
/
Copy pathapi.go
File metadata and controls
135 lines (122 loc) · 3.36 KB
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
package styles
import (
"embed"
"io/fs"
"slices"
"strings"
"sync"
"github.com/alecthomas/chroma/v3"
)
//go:embed *.xml
var embedded embed.FS
var (
registryMu sync.Mutex
// registry holds user-registered styles plus embedded styles that have
// already been parsed. Embedded styles are parsed lazily by lookup so
// that importing this package does not pay for parsing every style;
// see TestEmbeddedStyleNamesMatchFilenames for the invariant that makes
// the lazy filename-based lookup possible.
registry = map[string]*chroma.Style{}
)
// embeddedNames returns the lowercased names of the embedded styles, which
// by convention are also their file names.
var embeddedNames = sync.OnceValue(func() []string {
files, err := fs.ReadDir(embedded, ".")
if err != nil {
panic(err)
}
names := make([]string, 0, len(files))
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".xml") {
continue
}
names = append(names, strings.TrimSuffix(file.Name(), ".xml"))
}
return names
})
// lookup returns the style with the given lowercased name, parsing it from
// the embedded styles on first use. registryMu must be held.
func lookup(name string) (*chroma.Style, bool) {
if style, ok := registry[name]; ok {
return style, true
}
f, err := embedded.Open(name + ".xml")
if err != nil {
return nil, false
}
defer f.Close()
style, err := chroma.NewXMLStyle(f)
if err != nil {
panic(err)
}
registry[name] = style
return style, true
}
// Fallback style. Reassign to change the default fallback style.
var Fallback = func() *chroma.Style {
fallback, ok := lookup("swapoff")
if !ok {
panic(`chroma/styles: default fallback style "swapoff" is missing`)
}
return fallback
}()
// Register a chroma.Style.
func Register(style *chroma.Style) *chroma.Style {
registryMu.Lock()
defer registryMu.Unlock()
registry[strings.ToLower(style.Name)] = style
return style
}
// Names of all available styles.
func Names() []string {
registryMu.Lock()
defer registryMu.Unlock()
names := slices.Clone(embeddedNames())
for name := range registry {
if !slices.Contains(names, name) {
names = append(names, name)
}
}
slices.Sort(names)
return names
}
// Lookup a named style, returning false if not found.
func Lookup(name string) (*chroma.Style, bool) {
registryMu.Lock()
defer registryMu.Unlock()
return lookup(strings.ToLower(name))
}
// Get named style, or Fallback.
func Get(name string) *chroma.Style {
if style, ok := Lookup(name); ok {
return style
}
return Fallback
}
// GetForMode returns the named style if it already matches mode, otherwise its
// registered counterpart if one exists and matches mode. If neither matches,
// the originally-requested style is returned (or Fallback if the name is
// unknown), so callers always get something usable.
func GetForMode(name string, mode chroma.Mode) *chroma.Style {
style := Get(name)
if style.Mode() == mode {
return style
}
if style.Counterpart == "" {
return style
}
counterpart, ok := Lookup(style.Counterpart)
if !ok || counterpart.Mode() != mode {
return style
}
return counterpart
}
// RegisterPair links two styles as light/dark counterparts of each other.
//
// Both styles are also registered if they are not already present.
func RegisterPair(a, b *chroma.Style) {
Register(a)
Register(b)
a.Counterpart = strings.ToLower(b.Name)
b.Counterpart = strings.ToLower(a.Name)
}