-
-
Notifications
You must be signed in to change notification settings - Fork 506
Expand file tree
/
Copy pathdelegate.go
More file actions
150 lines (137 loc) · 3.42 KB
/
Copy pathdelegate.go
File metadata and controls
150 lines (137 loc) · 3.42 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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package chroma
import (
"iter"
"strings"
)
type delegatingLexer struct {
root Lexer
language Lexer
}
// DelegatingLexer combines two lexers to handle the common case of a language embedded inside another, such as PHP
// inside HTML or PHP inside plain text.
//
// It takes two lexers as arguments: a root lexer and a language lexer. First everything is scanned using the language
// lexer, which must return "Other" for unrecognised tokens. Then all "Other" tokens are lexed using the root lexer.
// Finally, these two sets of tokens are merged.
//
// The lexers from the template lexer package use this base lexer.
func DelegatingLexer(root Lexer, language Lexer) Lexer {
return &delegatingLexer{
root: root,
language: language,
}
}
func (d *delegatingLexer) SetTracing(enable bool) {
if l, ok := d.language.(TracingLexer); ok {
l.SetTracing(enable)
}
if l, ok := d.root.(TracingLexer); ok {
l.SetTracing(enable)
}
}
func (d *delegatingLexer) AnalyseText(text string) float32 {
return d.root.AnalyseText(text)
}
func (d *delegatingLexer) SetAnalyser(analyser func(text string) float32) Lexer {
d.root.SetAnalyser(analyser)
return d
}
func (d *delegatingLexer) SetRegistry(r *LexerRegistry) Lexer {
d.root.SetRegistry(r)
d.language.SetRegistry(r)
return d
}
func (d *delegatingLexer) Config() *Config {
return d.language.Config()
}
func (d *delegatingLexer) Tokenise(options *TokeniseOptions, text string) (iter.Seq[Token], error) { // nolint: gocognit
languageTokens, err := Tokenise(Coalesce(d.language), options, text)
if err != nil {
return nil, err
}
var rootText strings.Builder
hasLanguageTokens := false
for token := range languageTokens {
if token.Type == Other {
rootText.WriteString(token.Value)
} else {
hasLanguageTokens = true
}
}
if !hasLanguageTokens {
return d.root.Tokenise(options, text)
}
rootTokens, err := Tokenise(Coalesce(d.root), options, rootText.String())
if err != nil {
return nil, err
}
languageTokens, err = Tokenise(Coalesce(d.language), options, text)
if err != nil {
return nil, err
}
return func(yield func(Token) bool) {
nextRoot, stopRoot := iter.Pull(rootTokens)
defer stopRoot()
var root Token
var pendingRoot Token
for language := range languageTokens {
if language.Value == "" {
continue
}
if language.Type != Other {
if pendingRoot.Value != "" {
if !yield(pendingRoot) {
return
}
pendingRoot = Token{}
}
if !yield(language) {
return
}
continue
}
// Consume only Other spans because embedded language spans were removed from the root input.
remaining := len(language.Value)
for remaining > 0 {
if root.Value == "" {
var ok bool
root, ok = nextRoot()
if !ok {
break
}
continue
}
consumed, rest := splitToken(root, min(remaining, len(root.Value)))
root = rest
remaining -= len(consumed.Value)
if pendingRoot.Value == "" {
pendingRoot = consumed
} else {
pendingRoot.Value += consumed.Value
}
if root.Value == "" {
if !yield(pendingRoot) {
return
}
pendingRoot = Token{}
}
}
}
if pendingRoot.Value != "" {
yield(pendingRoot)
}
}, nil
}
func splitToken(t Token, offset int) (l Token, r Token) {
if offset <= 0 {
return Token{}, t
}
if offset >= len(t.Value) {
return t, Token{}
}
l = t.Clone()
r = t.Clone()
l.Value = l.Value[:offset]
r.Value = r.Value[offset:]
return
}