...
1 package parser
2
3 import (
4 "fmt"
5 "strings"
6
7 "github.com/yuin/goldmark/ast"
8 "github.com/yuin/goldmark/text"
9 "github.com/yuin/goldmark/util"
10 )
11
12
13
14 type DelimiterProcessor interface {
15
16 IsDelimiter(byte) bool
17
18
19 CanOpenCloser(opener, closer *Delimiter) bool
20
21
22
23 OnMatch(consumes int) ast.Node
24 }
25
26
27 type Delimiter struct {
28 ast.BaseInline
29
30 Segment text.Segment
31
32
33
34 CanOpen bool
35
36
37
38 CanClose bool
39
40
41 Length int
42
43
44 OriginalLength int
45
46
47 Char byte
48
49
50 PreviousDelimiter *Delimiter
51
52
53 NextDelimiter *Delimiter
54
55
56 Processor DelimiterProcessor
57 }
58
59
60 func (d *Delimiter) Inline() {}
61
62
63 func (d *Delimiter) Dump(source []byte, level int) {
64 fmt.Printf("%sDelimiter: \"%s\"\n", strings.Repeat(" ", level), string(d.Text(source)))
65 }
66
67 var kindDelimiter = ast.NewNodeKind("Delimiter")
68
69
70 func (d *Delimiter) Kind() ast.NodeKind {
71 return kindDelimiter
72 }
73
74
75 func (d *Delimiter) Text(source []byte) []byte {
76 return d.Segment.Value(source)
77 }
78
79
80 func (d *Delimiter) ConsumeCharacters(n int) {
81 d.Length -= n
82 d.Segment = d.Segment.WithStop(d.Segment.Start + d.Length)
83 }
84
85
86
87 func (d *Delimiter) CalcComsumption(closer *Delimiter) int {
88 if (d.CanClose || closer.CanOpen) && (d.OriginalLength+closer.OriginalLength)%3 == 0 && closer.OriginalLength%3 != 0 {
89 return 0
90 }
91 if d.Length >= 2 && closer.Length >= 2 {
92 return 2
93 }
94 return 1
95 }
96
97
98 func NewDelimiter(canOpen, canClose bool, length int, char byte, processor DelimiterProcessor) *Delimiter {
99 c := &Delimiter{
100 BaseInline: ast.BaseInline{},
101 CanOpen: canOpen,
102 CanClose: canClose,
103 Length: length,
104 OriginalLength: length,
105 Char: char,
106 PreviousDelimiter: nil,
107 NextDelimiter: nil,
108 Processor: processor,
109 }
110 return c
111 }
112
113
114 func ScanDelimiter(line []byte, before rune, min int, processor DelimiterProcessor) *Delimiter {
115 i := 0
116 c := line[i]
117 j := i
118 if !processor.IsDelimiter(c) {
119 return nil
120 }
121 for ; j < len(line) && c == line[j]; j++ {
122 }
123 if (j - i) >= min {
124 after := rune(' ')
125 if j != len(line) {
126 after = util.ToRune(line, j)
127 }
128
129 var canOpen, canClose bool
130 beforeIsPunctuation := util.IsPunctRune(before)
131 beforeIsWhitespace := util.IsSpaceRune(before)
132 afterIsPunctuation := util.IsPunctRune(after)
133 afterIsWhitespace := util.IsSpaceRune(after)
134
135 isLeft := !afterIsWhitespace &&
136 (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation)
137 isRight := !beforeIsWhitespace &&
138 (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation)
139
140 if line[i] == '_' {
141 canOpen = isLeft && (!isRight || beforeIsPunctuation)
142 canClose = isRight && (!isLeft || afterIsPunctuation)
143 } else {
144 canOpen = isLeft
145 canClose = isRight
146 }
147 return NewDelimiter(canOpen, canClose, j-i, c, processor)
148 }
149 return nil
150 }
151
152
153
154
155
156
157 func ProcessDelimiters(bottom ast.Node, pc Context) {
158 lastDelimiter := pc.LastDelimiter()
159 if lastDelimiter == nil {
160 return
161 }
162 var closer *Delimiter
163 if bottom != nil {
164 if bottom != lastDelimiter {
165 for c := lastDelimiter.PreviousSibling(); c != nil && c != bottom; {
166 if d, ok := c.(*Delimiter); ok {
167 closer = d
168 }
169 c = c.PreviousSibling()
170 }
171 }
172 } else {
173 closer = pc.FirstDelimiter()
174 }
175 if closer == nil {
176 pc.ClearDelimiters(bottom)
177 return
178 }
179 for closer != nil {
180 if !closer.CanClose {
181 closer = closer.NextDelimiter
182 continue
183 }
184 consume := 0
185 found := false
186 maybeOpener := false
187 var opener *Delimiter
188 for opener = closer.PreviousDelimiter; opener != nil && opener != bottom; opener = opener.PreviousDelimiter {
189 if opener.CanOpen && opener.Processor.CanOpenCloser(opener, closer) {
190 maybeOpener = true
191 consume = opener.CalcComsumption(closer)
192 if consume > 0 {
193 found = true
194 break
195 }
196 }
197 }
198 if !found {
199 next := closer.NextDelimiter
200 if !maybeOpener && !closer.CanOpen {
201 pc.RemoveDelimiter(closer)
202 }
203 closer = next
204 continue
205 }
206 opener.ConsumeCharacters(consume)
207 closer.ConsumeCharacters(consume)
208
209 node := opener.Processor.OnMatch(consume)
210
211 parent := opener.Parent()
212 child := opener.NextSibling()
213
214 for child != nil && child != closer {
215 next := child.NextSibling()
216 node.AppendChild(node, child)
217 child = next
218 }
219 parent.InsertAfter(parent, opener, node)
220
221 for c := opener.NextDelimiter; c != nil && c != closer; {
222 next := c.NextDelimiter
223 pc.RemoveDelimiter(c)
224 c = next
225 }
226
227 if opener.Length == 0 {
228 pc.RemoveDelimiter(opener)
229 }
230
231 if closer.Length == 0 {
232 next := closer.NextDelimiter
233 pc.RemoveDelimiter(closer)
234 closer = next
235 }
236 }
237 pc.ClearDelimiters(bottom)
238 }
239
View as plain text