@@ -19,6 +19,24 @@ const (
1919 No
2020)
2121
22+ // Mode indicates whether a style is intended for a light or dark background.
23+ type Mode uint8
24+
25+ // Mode values.
26+ const (
27+ Light Mode = iota
28+ Dark
29+ )
30+
31+ func (m Mode ) String () string {
32+ switch m {
33+ case Dark :
34+ return "dark"
35+ default :
36+ return "light"
37+ }
38+ }
39+
2240func (t Trilean ) String () string {
2341 switch t {
2442 case Yes :
@@ -149,15 +167,22 @@ func (s StyleEntry) IsZero() bool {
149167//
150168// Once built, a Style is immutable.
151169type StyleBuilder struct {
152- entries map [TokenType ]string
153- name string
154- parent * Style
170+ entries map [TokenType ]string
171+ name string
172+ counterpart string
173+ parent * Style
155174}
156175
157176func NewStyleBuilder (name string ) * StyleBuilder {
158177 return & StyleBuilder {name : name , entries : map [TokenType ]string {}}
159178}
160179
180+ // Counterpart sets the lowercase name of the opposite-mode style.
181+ func (s * StyleBuilder ) Counterpart (name string ) * StyleBuilder {
182+ s .counterpart = strings .ToLower (name )
183+ return s
184+ }
185+
161186func (s * StyleBuilder ) AddAll (entries StyleEntries ) * StyleBuilder {
162187 maps .Copy (s .entries , entries )
163188 return s
@@ -205,10 +230,15 @@ func (s *StyleBuilder) Transform(transform func(StyleEntry) StyleEntry) *StyleBu
205230}
206231
207232func (s * StyleBuilder ) Build () (* Style , error ) {
233+ counterpart := s .counterpart
234+ if counterpart == "" && s .parent != nil {
235+ counterpart = s .parent .Counterpart
236+ }
208237 style := & Style {
209- Name : s .name ,
210- entries : map [TokenType ]StyleEntry {},
211- parent : s .parent ,
238+ Name : s .name ,
239+ Counterpart : counterpart ,
240+ entries : map [TokenType ]StyleEntry {},
241+ parent : s .parent ,
212242 }
213243 for ttype , descriptor := range s .entries {
214244 entry , err := ParseStyleEntry (descriptor )
@@ -257,9 +287,23 @@ func MustNewStyle(name string, entries StyleEntries) *Style {
257287//
258288// See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical.
259289type Style struct {
260- Name string
261- entries map [TokenType ]StyleEntry
262- parent * Style
290+ Name string
291+ // Counterpart is the lowercase name of the style intended as this style's
292+ // opposite-mode pair (eg. "github-dark" for "github"). Resolved via
293+ // styles.GetForMode. May be empty.
294+ Counterpart string
295+ entries map [TokenType ]StyleEntry
296+ parent * Style
297+ }
298+
299+ // Mode returns Light or Dark based on the brightness of the Background entry's
300+ // background colour. Styles with an unset Background default to Light.
301+ func (s * Style ) Mode () Mode {
302+ bg := s .get (Background ).Background
303+ if bg .IsSet () && bg .Brightness () < 0.5 {
304+ return Dark
305+ }
306+ return Light
263307}
264308
265309func (s * Style ) MarshalXML (e * xml.Encoder , start xml.StartElement ) error {
@@ -268,6 +312,9 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
268312 }
269313 start .Name = xml.Name {Local : "style" }
270314 start .Attr = []xml.Attr {{Name : xml.Name {Local : "name" }, Value : s .Name }}
315+ if s .Counterpart != "" {
316+ start .Attr = append (start .Attr , xml.Attr {Name : xml.Name {Local : "counterpart" }, Value : s .Counterpart })
317+ }
271318 if err := e .EncodeToken (start ); err != nil {
272319 return err
273320 }
@@ -295,9 +342,12 @@ func (s *Style) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
295342
296343func (s * Style ) UnmarshalXML (d * xml.Decoder , start xml.StartElement ) error {
297344 for _ , attr := range start .Attr {
298- if attr .Name .Local == "name" {
345+ switch attr .Name .Local {
346+ case "name" :
299347 s .Name = attr .Value
300- } else {
348+ case "counterpart" :
349+ s .Counterpart = strings .ToLower (attr .Value )
350+ default :
301351 return fmt .Errorf ("unexpected attribute %s" , attr .Name .Local )
302352 }
303353 }
0 commit comments