11package chroma
22
33import (
4- "bytes"
54 "iter"
6- "slices "
5+ "strings "
76)
87
98type delegatingLexer struct {
@@ -14,7 +13,7 @@ type delegatingLexer struct {
1413// DelegatingLexer combines two lexers to handle the common case of a language embedded inside another, such as PHP
1514// inside HTML or PHP inside plain text.
1615//
17- // It takes two lexer as arguments: a root lexer and a language lexer. First everything is scanned using the language
16+ // It takes two lexers as arguments: a root lexer and a language lexer. First everything is scanned using the language
1817// lexer, which must return "Other" for unrecognised tokens. Then all "Other" tokens are lexed using the root lexer.
1918// Finally, these two sets of tokens are merged.
2019//
@@ -54,79 +53,86 @@ func (d *delegatingLexer) Config() *Config {
5453 return d .language .Config ()
5554}
5655
57- // An insertion is the character range where language tokens should be inserted.
58- type insertion struct {
59- start , end int
60- tokens []Token
61- }
62-
6356func (d * delegatingLexer ) Tokenise (options * TokeniseOptions , text string ) (iter.Seq [Token ], error ) { // nolint: gocognit
64- tokens , err := Tokenise (Coalesce (d .language ), options , text )
57+ languageTokens , err := Tokenise (Coalesce (d .language ), options , text )
6558 if err != nil {
6659 return nil , err
6760 }
68- // Compute insertions and gather "Other" tokens.
69- others := & bytes.Buffer {}
70- insertions := []* insertion {}
71- var insert * insertion
72- offset := 0
73- first := true
74- var lastType TokenType
75- for t := range tokens {
76- if t .Type == Other {
77- if ! first && insert != nil && lastType != Other {
78- insert .end = offset
79- }
80- others .WriteString (t .Value )
61+ var rootText strings.Builder
62+ hasLanguageTokens := false
63+ for token := range languageTokens {
64+ if token .Type == Other {
65+ rootText .WriteString (token .Value )
8166 } else {
82- if first || lastType == Other {
83- insert = & insertion {start : offset }
84- insertions = append (insertions , insert )
85- }
86- insert .tokens = append (insert .tokens , t )
67+ hasLanguageTokens = true
8768 }
88- first = false
89- lastType = t .Type
90- offset += len (t .Value )
9169 }
92-
93- if len (insertions ) == 0 {
70+ if ! hasLanguageTokens {
9471 return d .root .Tokenise (options , text )
9572 }
9673
97- // Lex the other tokens.
98- rootIt , err := Tokenise (Coalesce (d .root ), options , others .String ())
74+ rootTokens , err := Tokenise (Coalesce (d .root ), options , rootText .String ())
9975 if err != nil {
10076 return nil , err
10177 }
102- rootTokens := slices .Collect (rootIt )
78+ languageTokens , err = Tokenise (Coalesce (d .language ), options , text )
79+ if err != nil {
80+ return nil , err
81+ }
82+ return func (yield func (Token ) bool ) {
83+ nextRoot , stopRoot := iter .Pull (rootTokens )
84+ defer stopRoot ()
85+
86+ var root Token
87+ var pendingRoot Token
88+ for language := range languageTokens {
89+ if language .Value == "" {
90+ continue
91+ }
92+ if language .Type != Other {
93+ if pendingRoot .Value != "" {
94+ if ! yield (pendingRoot ) {
95+ return
96+ }
97+ pendingRoot = Token {}
98+ }
99+ if ! yield (language ) {
100+ return
101+ }
102+ continue
103+ }
103104
104- // Interleave the two sets of tokens.
105- var out []Token
106- offset = 0
107- ti := 0
108- ii := 0
109- for ti < len (rootTokens ) || ii < len (insertions ) {
110- if ti >= len (rootTokens ) || (ii < len (insertions ) && insertions [ii ].start < offset + len (rootTokens [ti ].Value )) {
111- ins := insertions [ii ]
112- ii ++
113- if ti < len (rootTokens ) {
114- l , r := splitToken (rootTokens [ti ], ins .start - offset )
115- if l .Value != "" {
116- out = append (out , l )
117- offset += len (l .Value )
105+ // Consume only Other spans because embedded language spans were removed from the root input.
106+ remaining := len (language .Value )
107+ for remaining > 0 {
108+ if root .Value == "" {
109+ var ok bool
110+ root , ok = nextRoot ()
111+ if ! ok {
112+ break
113+ }
114+ continue
115+ }
116+ consumed , rest := splitToken (root , min (remaining , len (root .Value )))
117+ root = rest
118+ remaining -= len (consumed .Value )
119+ if pendingRoot .Value == "" {
120+ pendingRoot = consumed
121+ } else {
122+ pendingRoot .Value += consumed .Value
123+ }
124+ if root .Value == "" {
125+ if ! yield (pendingRoot ) {
126+ return
127+ }
128+ pendingRoot = Token {}
118129 }
119- rootTokens [ti ] = r
120130 }
121- out = append (out , ins .tokens ... )
122- offset += ins .end - ins .start
123- } else {
124- out = append (out , rootTokens [ti ])
125- offset += len (rootTokens [ti ].Value )
126- ti ++
127131 }
128- }
129- return slices .Values (out ), nil
132+ if pendingRoot .Value != "" {
133+ yield (pendingRoot )
134+ }
135+ }, nil
130136}
131137
132138func splitToken (t Token , offset int ) (l Token , r Token ) {
0 commit comments