@peaceroad/markdown-it-strong-ja 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -13
- package/index.js +44 -2488
- package/package.json +4 -3
- package/src/token-compat.js +226 -0
- package/src/token-core.js +439 -0
- package/src/token-link-utils.js +774 -0
- package/src/token-postprocess.js +340 -0
- package/src/token-utils.js +166 -0
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
import Token from 'markdown-it/lib/token.mjs'
|
|
2
|
+
import { parseLinkDestination, parseLinkTitle } from 'markdown-it/lib/helpers/index.mjs'
|
|
3
|
+
import { isSpace, isWhiteSpace } from 'markdown-it/lib/common/utils.mjs'
|
|
4
|
+
|
|
5
|
+
const CHAR_OPEN_BRACKET = 0x5B // [
|
|
6
|
+
const CHAR_CLOSE_BRACKET = 0x5D // ]
|
|
7
|
+
const CHAR_OPEN_PAREN = 0x28 // (
|
|
8
|
+
const CHAR_CLOSE_PAREN = 0x29 // )
|
|
9
|
+
|
|
10
|
+
const isWhitespaceToken = (token) => {
|
|
11
|
+
if (!token || token.type !== 'text') return false
|
|
12
|
+
const content = token.content
|
|
13
|
+
if (!content) return true
|
|
14
|
+
for (let i = 0; i < content.length; i++) {
|
|
15
|
+
if (!isWhiteSpace(content.charCodeAt(i))) return false
|
|
16
|
+
}
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Collapsed reference helpers
|
|
21
|
+
const buildReferenceLabelRange = (tokens, startIdx, endIdx) => {
|
|
22
|
+
if (startIdx > endIdx) return ''
|
|
23
|
+
let label = ''
|
|
24
|
+
for (let idx = startIdx; idx <= endIdx; idx++) {
|
|
25
|
+
const token = tokens[idx]
|
|
26
|
+
if (!token) continue
|
|
27
|
+
if (token.type === 'text' || token.type === 'code_inline') {
|
|
28
|
+
label += token.content
|
|
29
|
+
} else if (token.type === 'softbreak' || token.type === 'hardbreak') {
|
|
30
|
+
label += ' '
|
|
31
|
+
} else if (token.type && token.type.endsWith('_open') && token.markup) {
|
|
32
|
+
label += token.markup
|
|
33
|
+
} else if (token.type && token.type.endsWith('_close') && token.markup) {
|
|
34
|
+
label += token.markup
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return label
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cleanLabelText = (label) => {
|
|
41
|
+
if (label.indexOf('*') === -1 && label.indexOf('_') === -1) return label
|
|
42
|
+
return label.replace(/^[*_]+/, '').replace(/[*_]+$/, '')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const normalizeReferenceCandidate = (state, text, { useClean = false } = {}) => {
|
|
46
|
+
const source = useClean ? cleanLabelText(text) : text
|
|
47
|
+
return normalizeRefKey(state, source)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const getNormalizeRef = (state) => {
|
|
51
|
+
if (state.__strongJaNormalizeRef) return state.__strongJaNormalizeRef
|
|
52
|
+
const normalize = state.md && state.md.utils && state.md.utils.normalizeReference
|
|
53
|
+
? state.md.utils.normalizeReference
|
|
54
|
+
: (str) => str.trim().replace(/\s+/g, ' ').toUpperCase()
|
|
55
|
+
state.__strongJaNormalizeRef = normalize
|
|
56
|
+
return normalize
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const normalizeRefKey = (state, label) => {
|
|
60
|
+
return getNormalizeRef(state)(label)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const adjustTokenLevels = (tokens, startIdx, endIdx, delta) => {
|
|
64
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
65
|
+
if (tokens[i]) tokens[i].level += delta
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const cloneMap = (map) => {
|
|
70
|
+
if (!map || !Array.isArray(map)) return null
|
|
71
|
+
return [map[0], map[1]]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const getMapFromTokenRange = (tokens, startIdx, endIdx) => {
|
|
75
|
+
if (!tokens || startIdx > endIdx) return null
|
|
76
|
+
let startLine = null
|
|
77
|
+
let endLine = null
|
|
78
|
+
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
79
|
+
const token = tokens[i]
|
|
80
|
+
if (!token || !token.map || !Array.isArray(token.map)) continue
|
|
81
|
+
const map = token.map
|
|
82
|
+
if (startLine === null || map[0] < startLine) startLine = map[0]
|
|
83
|
+
if (endLine === null || map[1] > endLine) endLine = map[1]
|
|
84
|
+
}
|
|
85
|
+
if (startLine === null || endLine === null) return null
|
|
86
|
+
return [startLine, endLine]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const getNearbyMap = (tokens, startIdx, endIdx) => {
|
|
90
|
+
if (!tokens) return null
|
|
91
|
+
for (let i = startIdx - 1; i >= 0; i--) {
|
|
92
|
+
if (tokens[i] && tokens[i].map) return cloneMap(tokens[i].map)
|
|
93
|
+
}
|
|
94
|
+
for (let i = endIdx + 1; i < tokens.length; i++) {
|
|
95
|
+
if (tokens[i] && tokens[i].map) return cloneMap(tokens[i].map)
|
|
96
|
+
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const cloneTextToken = (source, content) => {
|
|
101
|
+
const newToken = new Token('text', '', 0)
|
|
102
|
+
Object.assign(newToken, source)
|
|
103
|
+
newToken.content = content
|
|
104
|
+
if (source.meta) newToken.meta = { ...source.meta }
|
|
105
|
+
if (source.map) newToken.map = source.map
|
|
106
|
+
return newToken
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Split only text tokens that actually contain bracket characters
|
|
110
|
+
const splitBracketToken = (tokens, index, options) => {
|
|
111
|
+
const token = tokens[index]
|
|
112
|
+
if (!token || token.type !== 'text') return false
|
|
113
|
+
if (token.__strongJaBracketAtomic) return false
|
|
114
|
+
if (token.__strongJaHasBracket === false) return false
|
|
115
|
+
const content = token.content
|
|
116
|
+
if (!content) {
|
|
117
|
+
token.__strongJaHasBracket = false
|
|
118
|
+
token.__strongJaBracketAtomic = false
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
if (token.__strongJaHasBracket !== true) {
|
|
122
|
+
if (content.indexOf('[') === -1 && content.indexOf(']') === -1) {
|
|
123
|
+
token.__strongJaHasBracket = false
|
|
124
|
+
token.__strongJaBracketAtomic = false
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
token.__strongJaHasBracket = true
|
|
128
|
+
}
|
|
129
|
+
const splitEmptyPair = options && options.splitEmptyPair
|
|
130
|
+
const segments = []
|
|
131
|
+
let buffer = ''
|
|
132
|
+
let pos = 0
|
|
133
|
+
while (pos < content.length) {
|
|
134
|
+
if (!splitEmptyPair &&
|
|
135
|
+
content.charCodeAt(pos) === CHAR_OPEN_BRACKET &&
|
|
136
|
+
content.charCodeAt(pos + 1) === CHAR_CLOSE_BRACKET) {
|
|
137
|
+
if (buffer) {
|
|
138
|
+
segments.push(buffer)
|
|
139
|
+
buffer = ''
|
|
140
|
+
}
|
|
141
|
+
segments.push('[]')
|
|
142
|
+
pos += 2
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
const ch = content[pos]
|
|
146
|
+
if (ch === '[' || ch === ']') {
|
|
147
|
+
if (buffer) {
|
|
148
|
+
segments.push(buffer)
|
|
149
|
+
buffer = ''
|
|
150
|
+
}
|
|
151
|
+
segments.push(ch)
|
|
152
|
+
pos++
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
buffer += ch
|
|
156
|
+
pos++
|
|
157
|
+
}
|
|
158
|
+
if (buffer) segments.push(buffer)
|
|
159
|
+
if (segments.length <= 1) {
|
|
160
|
+
if (segments.length === 0) {
|
|
161
|
+
token.__strongJaHasBracket = false
|
|
162
|
+
token.__strongJaBracketAtomic = false
|
|
163
|
+
} else {
|
|
164
|
+
const seg = segments[0]
|
|
165
|
+
if (seg === '[' || seg === ']') {
|
|
166
|
+
token.__strongJaHasBracket = true
|
|
167
|
+
token.__strongJaBracketAtomic = true
|
|
168
|
+
} else if (seg === '[]') {
|
|
169
|
+
token.__strongJaHasBracket = true
|
|
170
|
+
token.__strongJaBracketAtomic = false
|
|
171
|
+
} else {
|
|
172
|
+
token.__strongJaHasBracket = false
|
|
173
|
+
token.__strongJaBracketAtomic = false
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
token.content = segments[0]
|
|
179
|
+
if (token.content === '[' || token.content === ']') {
|
|
180
|
+
token.__strongJaHasBracket = true
|
|
181
|
+
token.__strongJaBracketAtomic = true
|
|
182
|
+
} else if (token.content === '[]') {
|
|
183
|
+
token.__strongJaHasBracket = true
|
|
184
|
+
token.__strongJaBracketAtomic = false
|
|
185
|
+
} else {
|
|
186
|
+
token.__strongJaHasBracket = false
|
|
187
|
+
token.__strongJaBracketAtomic = false
|
|
188
|
+
}
|
|
189
|
+
let insertIdx = index + 1
|
|
190
|
+
for (let s = 1; s < segments.length; s++) {
|
|
191
|
+
const newToken = cloneTextToken(token, segments[s])
|
|
192
|
+
if (segments[s] === '[' || segments[s] === ']') {
|
|
193
|
+
newToken.__strongJaHasBracket = true
|
|
194
|
+
newToken.__strongJaBracketAtomic = true
|
|
195
|
+
} else if (segments[s] === '[]') {
|
|
196
|
+
newToken.__strongJaHasBracket = true
|
|
197
|
+
newToken.__strongJaBracketAtomic = false
|
|
198
|
+
} else {
|
|
199
|
+
newToken.__strongJaHasBracket = false
|
|
200
|
+
newToken.__strongJaBracketAtomic = false
|
|
201
|
+
}
|
|
202
|
+
tokens.splice(insertIdx, 0, newToken)
|
|
203
|
+
insertIdx++
|
|
204
|
+
}
|
|
205
|
+
return true
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const isBracketToken = (token, bracket) => {
|
|
209
|
+
return token && token.type === 'text' && token.content === bracket
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const findLinkCloseIndex = (tokens, startIdx) => {
|
|
213
|
+
let depth = 0
|
|
214
|
+
for (let idx = startIdx; idx < tokens.length; idx++) {
|
|
215
|
+
const token = tokens[idx]
|
|
216
|
+
if (token.type === 'link_open') depth++
|
|
217
|
+
if (token.type === 'link_close') {
|
|
218
|
+
depth--
|
|
219
|
+
if (depth === 0) return idx
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return -1
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const consumeCharactersFromTokens = (tokens, startIdx, count) => {
|
|
226
|
+
let remaining = count
|
|
227
|
+
let idx = startIdx
|
|
228
|
+
while (idx < tokens.length && remaining > 0) {
|
|
229
|
+
const token = tokens[idx]
|
|
230
|
+
if (!token || token.type !== 'text') {
|
|
231
|
+
return false
|
|
232
|
+
}
|
|
233
|
+
const len = token.content.length
|
|
234
|
+
if (remaining >= len) {
|
|
235
|
+
remaining -= len
|
|
236
|
+
tokens.splice(idx, 1)
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
239
|
+
token.content = token.content.slice(remaining)
|
|
240
|
+
remaining = 0
|
|
241
|
+
}
|
|
242
|
+
return remaining === 0
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const wrapLabelTokensWithLink = (tokens, labelStartIdx, labelEndIdx, linkOpenToken, linkCloseToken, labelSource) => {
|
|
246
|
+
const wrapperPairs = []
|
|
247
|
+
let startIdx = labelStartIdx
|
|
248
|
+
let endIdx = labelEndIdx
|
|
249
|
+
while (startIdx > 0) {
|
|
250
|
+
const prevToken = tokens[startIdx - 1]
|
|
251
|
+
const nextToken = tokens[endIdx + 1]
|
|
252
|
+
if (!prevToken || !nextToken) break
|
|
253
|
+
if (!/_close$/.test(prevToken.type)) break
|
|
254
|
+
const expectedOpen = prevToken.type.replace('_close', '_open')
|
|
255
|
+
if (nextToken.type !== expectedOpen) break
|
|
256
|
+
wrapperPairs.push({
|
|
257
|
+
base: prevToken.type.replace('_close', ''),
|
|
258
|
+
tag: prevToken.tag,
|
|
259
|
+
markup: prevToken.markup,
|
|
260
|
+
openMap: cloneMap(nextToken.map),
|
|
261
|
+
closeMap: cloneMap(prevToken.map)
|
|
262
|
+
})
|
|
263
|
+
tokens.splice(endIdx + 1, 1)
|
|
264
|
+
tokens.splice(startIdx - 1, 1)
|
|
265
|
+
startIdx -= 1
|
|
266
|
+
endIdx -= 1
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (startIdx > endIdx) {
|
|
270
|
+
if (labelSource !== undefined && labelSource !== null) {
|
|
271
|
+
const placeholder = new Token('text', '', 0)
|
|
272
|
+
placeholder.content = labelSource
|
|
273
|
+
placeholder.level = linkOpenToken.level + 1
|
|
274
|
+
tokens.splice(startIdx, 0, placeholder)
|
|
275
|
+
endIdx = startIdx
|
|
276
|
+
} else {
|
|
277
|
+
return startIdx
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let labelLength = endIdx - startIdx + 1
|
|
282
|
+
const firstLabelToken = tokens[startIdx]
|
|
283
|
+
const linkLevel = firstLabelToken ? Math.max(firstLabelToken.level - 1, 0) : 0
|
|
284
|
+
linkOpenToken.level = linkLevel
|
|
285
|
+
linkCloseToken.level = linkLevel
|
|
286
|
+
const labelMap = getMapFromTokenRange(tokens, startIdx, endIdx) || getNearbyMap(tokens, startIdx, endIdx)
|
|
287
|
+
if (labelMap) {
|
|
288
|
+
if (!linkOpenToken.map) linkOpenToken.map = cloneMap(labelMap)
|
|
289
|
+
if (!linkCloseToken.map) linkCloseToken.map = cloneMap(labelMap)
|
|
290
|
+
}
|
|
291
|
+
tokens.splice(startIdx, 0, linkOpenToken)
|
|
292
|
+
tokens.splice(startIdx + labelLength + 1, 0, linkCloseToken)
|
|
293
|
+
|
|
294
|
+
adjustTokenLevels(tokens, startIdx + 1, startIdx + labelLength + 1, 1)
|
|
295
|
+
|
|
296
|
+
if (wrapperPairs.length > 0) {
|
|
297
|
+
let insertIdx = startIdx + 1
|
|
298
|
+
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
299
|
+
const pair = wrapperPairs[wp]
|
|
300
|
+
const innerOpen = new Token(pair.base + '_open', pair.tag, 1)
|
|
301
|
+
innerOpen.markup = pair.markup
|
|
302
|
+
innerOpen.level = linkLevel + 1 + wp
|
|
303
|
+
if (pair.openMap && !innerOpen.map) innerOpen.map = cloneMap(pair.openMap)
|
|
304
|
+
tokens.splice(insertIdx, 0, innerOpen)
|
|
305
|
+
insertIdx++
|
|
306
|
+
labelLength++
|
|
307
|
+
}
|
|
308
|
+
let linkClosePos = startIdx + labelLength + 1
|
|
309
|
+
for (let wp = wrapperPairs.length - 1; wp >= 0; wp--) {
|
|
310
|
+
const pair = wrapperPairs[wp]
|
|
311
|
+
const innerClose = new Token(pair.base + '_close', pair.tag, -1)
|
|
312
|
+
innerClose.markup = pair.markup
|
|
313
|
+
innerClose.level = linkLevel + 1 + wp
|
|
314
|
+
if (pair.closeMap && !innerClose.map) innerClose.map = cloneMap(pair.closeMap)
|
|
315
|
+
tokens.splice(linkClosePos, 0, innerClose)
|
|
316
|
+
labelLength++
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return startIdx + labelLength + 2
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const parseInlineLinkTail = (content, md) => {
|
|
324
|
+
if (!content || content.charCodeAt(0) !== CHAR_OPEN_PAREN) return null
|
|
325
|
+
const max = content.length
|
|
326
|
+
let pos = 1
|
|
327
|
+
while (pos < max) {
|
|
328
|
+
const code = content.charCodeAt(pos)
|
|
329
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
330
|
+
pos++
|
|
331
|
+
}
|
|
332
|
+
if (pos >= max) return null
|
|
333
|
+
|
|
334
|
+
let href = ''
|
|
335
|
+
let destPos = pos
|
|
336
|
+
if (pos < max && content.charCodeAt(pos) === CHAR_CLOSE_PAREN) {
|
|
337
|
+
href = ''
|
|
338
|
+
} else {
|
|
339
|
+
const dest = parseLinkDestination(content, pos, max)
|
|
340
|
+
if (!dest.ok) return null
|
|
341
|
+
href = md.normalizeLink(dest.str)
|
|
342
|
+
if (!md.validateLink(href)) {
|
|
343
|
+
return null
|
|
344
|
+
}
|
|
345
|
+
pos = dest.pos
|
|
346
|
+
destPos = dest.pos
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
while (pos < max) {
|
|
350
|
+
const code = content.charCodeAt(pos)
|
|
351
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
352
|
+
pos++
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let title = ''
|
|
356
|
+
const titleRes = parseLinkTitle(content, pos, max)
|
|
357
|
+
if (pos < max && pos !== destPos && titleRes.ok) {
|
|
358
|
+
title = titleRes.str
|
|
359
|
+
pos = titleRes.pos
|
|
360
|
+
while (pos < max) {
|
|
361
|
+
const code = content.charCodeAt(pos)
|
|
362
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
363
|
+
pos++
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (pos >= max || content.charCodeAt(pos) !== CHAR_CLOSE_PAREN) {
|
|
368
|
+
return null
|
|
369
|
+
}
|
|
370
|
+
pos++
|
|
371
|
+
return { href, title, consumed: pos }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const INLINE_LINK_BRACKET_SPLIT_OPTIONS = { splitEmptyPair: true }
|
|
375
|
+
|
|
376
|
+
const removeGhostLabelText = (tokens, linkCloseIndex, labelText) => {
|
|
377
|
+
if (!labelText) return
|
|
378
|
+
if (linkCloseIndex === null || linkCloseIndex === undefined) return
|
|
379
|
+
if (linkCloseIndex < 0 || linkCloseIndex >= tokens.length) return
|
|
380
|
+
const closeToken = tokens[linkCloseIndex]
|
|
381
|
+
if (!closeToken || closeToken.type !== 'link_close') return
|
|
382
|
+
let idx = linkCloseIndex + 1
|
|
383
|
+
while (idx < tokens.length) {
|
|
384
|
+
const token = tokens[idx]
|
|
385
|
+
if (!token) {
|
|
386
|
+
idx++
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
if (token.type === 'text') {
|
|
390
|
+
if (token.content.startsWith(labelText)) {
|
|
391
|
+
if (token.content.length === labelText.length) {
|
|
392
|
+
tokens.splice(idx, 1)
|
|
393
|
+
} else {
|
|
394
|
+
token.content = token.content.slice(labelText.length)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
break
|
|
398
|
+
}
|
|
399
|
+
if (!/_close$/.test(token.type)) {
|
|
400
|
+
break
|
|
401
|
+
}
|
|
402
|
+
idx++
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const restoreLabelWhitespace = (tokens, labelSources) => {
|
|
407
|
+
if (!tokens || !labelSources || labelSources.length === 0) return
|
|
408
|
+
let labelIdx = 0
|
|
409
|
+
for (let i = 0; i < tokens.length && labelIdx < labelSources.length; i++) {
|
|
410
|
+
if (tokens[i].type !== 'link_open') continue
|
|
411
|
+
const closeIdx = findLinkCloseIndex(tokens, i)
|
|
412
|
+
if (closeIdx === -1) continue
|
|
413
|
+
const labelSource = labelSources[labelIdx] || ''
|
|
414
|
+
if (!labelSource) {
|
|
415
|
+
labelIdx++
|
|
416
|
+
continue
|
|
417
|
+
}
|
|
418
|
+
let cursor = 0
|
|
419
|
+
for (let pos = i + 1; pos < closeIdx; pos++) {
|
|
420
|
+
const t = tokens[pos]
|
|
421
|
+
const markup = t.markup || ''
|
|
422
|
+
const text = t.content || ''
|
|
423
|
+
const startPos = cursor
|
|
424
|
+
if (t.type === 'text') {
|
|
425
|
+
cursor += text.length
|
|
426
|
+
} else if (t.type === 'code_inline') {
|
|
427
|
+
cursor += markup.length + text.length + markup.length
|
|
428
|
+
} else if (markup) {
|
|
429
|
+
cursor += markup.length
|
|
430
|
+
}
|
|
431
|
+
if ((t.type === 'strong_open' || t.type === 'em_open') && startPos > 0) {
|
|
432
|
+
const prevToken = tokens[pos - 1]
|
|
433
|
+
if (prevToken && prevToken.type === 'text' && prevToken.content && !prevToken.content.endsWith(' ')) {
|
|
434
|
+
const hasSpaceBefore = startPos - 1 >= 0 && startPos - 1 < labelSource.length && labelSource[startPos - 1] === ' '
|
|
435
|
+
const hasSpaceAt = startPos >= 0 && startPos < labelSource.length && labelSource[startPos] === ' '
|
|
436
|
+
if (hasSpaceBefore || hasSpaceAt) {
|
|
437
|
+
prevToken.content += ' '
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
labelIdx++
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const convertInlineLinks = (tokens, state) => {
|
|
447
|
+
if (!tokens || tokens.length === 0) return
|
|
448
|
+
let labelSources = tokens.__strongJaInlineLabelSources
|
|
449
|
+
if ((!labelSources || labelSources.length === 0) && state && state.env && Array.isArray(state.env.__strongJaInlineLabelSourceList) && state.env.__strongJaInlineLabelSourceList.length > 0) {
|
|
450
|
+
labelSources = state.env.__strongJaInlineLabelSourceList.shift()
|
|
451
|
+
}
|
|
452
|
+
let labelSourceIndex = tokens.__strongJaInlineLabelIndex || 0
|
|
453
|
+
let i = 0
|
|
454
|
+
while (i < tokens.length) {
|
|
455
|
+
if (splitBracketToken(tokens, i, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
456
|
+
continue
|
|
457
|
+
}
|
|
458
|
+
if (!isBracketToken(tokens[i], '[')) {
|
|
459
|
+
i++
|
|
460
|
+
continue
|
|
461
|
+
}
|
|
462
|
+
let closeIdx = i + 1
|
|
463
|
+
let invalid = false
|
|
464
|
+
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
465
|
+
if (splitBracketToken(tokens, closeIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
466
|
+
continue
|
|
467
|
+
}
|
|
468
|
+
if (tokens[closeIdx].type === 'link_open') {
|
|
469
|
+
invalid = true
|
|
470
|
+
break
|
|
471
|
+
}
|
|
472
|
+
closeIdx++
|
|
473
|
+
}
|
|
474
|
+
if (invalid || closeIdx >= tokens.length) {
|
|
475
|
+
i++
|
|
476
|
+
continue
|
|
477
|
+
}
|
|
478
|
+
const currentLabelSource = labelSources && labelSourceIndex < labelSources.length
|
|
479
|
+
? labelSources[labelSourceIndex]
|
|
480
|
+
: undefined
|
|
481
|
+
|
|
482
|
+
const labelLength = closeIdx - i - 1
|
|
483
|
+
const needsPlaceholder = labelLength <= 0
|
|
484
|
+
if (needsPlaceholder && !currentLabelSource) {
|
|
485
|
+
i++
|
|
486
|
+
continue
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let tailIdx = closeIdx + 1
|
|
490
|
+
let tailContent = ''
|
|
491
|
+
let parsedTail = null
|
|
492
|
+
let tailHasCloseParen = false
|
|
493
|
+
while (tailIdx < tokens.length) {
|
|
494
|
+
if (splitBracketToken(tokens, tailIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
495
|
+
continue
|
|
496
|
+
}
|
|
497
|
+
const tailToken = tokens[tailIdx]
|
|
498
|
+
if (tailToken.type !== 'text' || !tailToken.content) {
|
|
499
|
+
break
|
|
500
|
+
}
|
|
501
|
+
tailContent += tailToken.content
|
|
502
|
+
if (!tailHasCloseParen) {
|
|
503
|
+
if (tailToken.content.indexOf(')') === -1) {
|
|
504
|
+
tailIdx++
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
tailHasCloseParen = true
|
|
508
|
+
}
|
|
509
|
+
parsedTail = parseInlineLinkTail(tailContent, state.md)
|
|
510
|
+
if (parsedTail) break
|
|
511
|
+
tailIdx++
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!parsedTail) {
|
|
515
|
+
i++
|
|
516
|
+
continue
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (!consumeCharactersFromTokens(tokens, closeIdx + 1, parsedTail.consumed)) {
|
|
520
|
+
i++
|
|
521
|
+
continue
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
tokens.splice(closeIdx, 1)
|
|
525
|
+
tokens.splice(i, 1)
|
|
526
|
+
|
|
527
|
+
const linkOpenToken = new Token('link_open', 'a', 1)
|
|
528
|
+
linkOpenToken.attrs = [['href', parsedTail.href]]
|
|
529
|
+
if (parsedTail.title) linkOpenToken.attrPush(['title', parsedTail.title])
|
|
530
|
+
linkOpenToken.markup = '[]()'
|
|
531
|
+
linkOpenToken.info = 'auto'
|
|
532
|
+
const linkCloseToken = new Token('link_close', 'a', -1)
|
|
533
|
+
linkCloseToken.markup = '[]()'
|
|
534
|
+
linkCloseToken.info = 'auto'
|
|
535
|
+
|
|
536
|
+
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken, currentLabelSource)
|
|
537
|
+
if (nextIndex === i) {
|
|
538
|
+
i++
|
|
539
|
+
continue
|
|
540
|
+
}
|
|
541
|
+
if (currentLabelSource) {
|
|
542
|
+
const linkCloseIdx = findLinkCloseIndex(tokens, i)
|
|
543
|
+
if (linkCloseIdx !== -1) {
|
|
544
|
+
let cursor = 0
|
|
545
|
+
for (let pos = i + 1; pos < linkCloseIdx; pos++) {
|
|
546
|
+
const t = tokens[pos]
|
|
547
|
+
const markup = t.markup || ''
|
|
548
|
+
const text = t.content || ''
|
|
549
|
+
const startPos = cursor
|
|
550
|
+
if (t.type === 'text') {
|
|
551
|
+
cursor += text.length
|
|
552
|
+
} else if (t.type === 'code_inline') {
|
|
553
|
+
cursor += markup.length + text.length + markup.length
|
|
554
|
+
} else if (markup) {
|
|
555
|
+
cursor += markup.length
|
|
556
|
+
}
|
|
557
|
+
if ((t.type === 'strong_open' || t.type === 'em_open') && startPos > 0) {
|
|
558
|
+
const prevToken = tokens[pos - 1]
|
|
559
|
+
if (prevToken && prevToken.type === 'text' && prevToken.content && !prevToken.content.endsWith(' ')) {
|
|
560
|
+
const labelHasSpaceBefore = startPos - 1 >= 0 && startPos - 1 < currentLabelSource.length && currentLabelSource[startPos - 1] === ' '
|
|
561
|
+
const labelHasSpaceAt = startPos >= 0 && startPos < currentLabelSource.length && currentLabelSource[startPos] === ' '
|
|
562
|
+
if (labelHasSpaceBefore || labelHasSpaceAt) {
|
|
563
|
+
prevToken.content += ' '
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (needsPlaceholder && currentLabelSource) {
|
|
571
|
+
removeGhostLabelText(tokens, nextIndex - 1, currentLabelSource)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (labelSources && labelSources.length > 0) {
|
|
575
|
+
if (labelSourceIndex < labelSources.length) {
|
|
576
|
+
labelSourceIndex++
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
i = nextIndex
|
|
580
|
+
}
|
|
581
|
+
if (labelSources) {
|
|
582
|
+
tokens.__strongJaInlineLabelIndex = labelSourceIndex
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const convertCollapsedReferenceLinks = (tokens, state) => {
|
|
587
|
+
const references = state.env && state.env.references
|
|
588
|
+
if (!references) return
|
|
589
|
+
const referenceCount = state.__strongJaReferenceCount
|
|
590
|
+
if (referenceCount !== undefined) {
|
|
591
|
+
if (referenceCount === 0) return
|
|
592
|
+
} else if (Object.keys(references).length === 0) {
|
|
593
|
+
return
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
let i = 0
|
|
597
|
+
while (i < tokens.length) {
|
|
598
|
+
if (splitBracketToken(tokens, i)) {
|
|
599
|
+
continue
|
|
600
|
+
}
|
|
601
|
+
if (!isBracketToken(tokens[i], '[')) {
|
|
602
|
+
i++
|
|
603
|
+
continue
|
|
604
|
+
}
|
|
605
|
+
let closeIdx = i + 1
|
|
606
|
+
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
607
|
+
if (splitBracketToken(tokens, closeIdx)) {
|
|
608
|
+
continue
|
|
609
|
+
}
|
|
610
|
+
if (tokens[closeIdx].type === 'link_open') {
|
|
611
|
+
closeIdx = -1
|
|
612
|
+
break
|
|
613
|
+
}
|
|
614
|
+
closeIdx++
|
|
615
|
+
}
|
|
616
|
+
if (closeIdx === -1 || closeIdx >= tokens.length) {
|
|
617
|
+
i++
|
|
618
|
+
continue
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (closeIdx === i + 1) {
|
|
622
|
+
i++
|
|
623
|
+
continue
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const labelStart = i + 1
|
|
627
|
+
const labelEnd = closeIdx - 1
|
|
628
|
+
const labelLength = closeIdx - i - 1
|
|
629
|
+
const labelText = buildReferenceLabelRange(tokens, labelStart, labelEnd)
|
|
630
|
+
const cleanedLabel = cleanLabelText(labelText)
|
|
631
|
+
const whitespaceStart = closeIdx + 1
|
|
632
|
+
let refRemoveStart = whitespaceStart
|
|
633
|
+
while (refRemoveStart < tokens.length && isWhitespaceToken(tokens[refRemoveStart])) {
|
|
634
|
+
refRemoveStart++
|
|
635
|
+
}
|
|
636
|
+
if (splitBracketToken(tokens, refRemoveStart)) {
|
|
637
|
+
continue
|
|
638
|
+
}
|
|
639
|
+
const whitespaceCount = refRemoveStart - whitespaceStart
|
|
640
|
+
let refKey = null
|
|
641
|
+
let refRemoveCount = 0
|
|
642
|
+
let existingLinkOpen = null
|
|
643
|
+
let existingLinkClose = null
|
|
644
|
+
const nextToken = tokens[refRemoveStart]
|
|
645
|
+
if (isBracketToken(nextToken, '[]')) {
|
|
646
|
+
refKey = normalizeReferenceCandidate(state, cleanedLabel)
|
|
647
|
+
refRemoveCount = 1
|
|
648
|
+
} else if (isBracketToken(nextToken, '[')) {
|
|
649
|
+
let refCloseIdx = refRemoveStart + 1
|
|
650
|
+
while (refCloseIdx < tokens.length && !isBracketToken(tokens[refCloseIdx], ']')) {
|
|
651
|
+
refCloseIdx++
|
|
652
|
+
}
|
|
653
|
+
if (refCloseIdx >= tokens.length) {
|
|
654
|
+
i++
|
|
655
|
+
continue
|
|
656
|
+
}
|
|
657
|
+
const refStart = refRemoveStart + 1
|
|
658
|
+
const refEnd = refCloseIdx - 1
|
|
659
|
+
if (refStart > refEnd) {
|
|
660
|
+
refKey = normalizeReferenceCandidate(state, cleanedLabel)
|
|
661
|
+
} else {
|
|
662
|
+
const refLabelText = buildReferenceLabelRange(tokens, refStart, refEnd)
|
|
663
|
+
refKey = normalizeReferenceCandidate(state, refLabelText)
|
|
664
|
+
}
|
|
665
|
+
refRemoveCount = refCloseIdx - refRemoveStart + 1
|
|
666
|
+
} else if (nextToken && nextToken.type === 'link_open') {
|
|
667
|
+
const linkCloseIdx = findLinkCloseIndex(tokens, refRemoveStart)
|
|
668
|
+
if (linkCloseIdx === -1) {
|
|
669
|
+
i++
|
|
670
|
+
continue
|
|
671
|
+
}
|
|
672
|
+
existingLinkOpen = tokens[refRemoveStart]
|
|
673
|
+
existingLinkClose = tokens[linkCloseIdx]
|
|
674
|
+
refRemoveCount = linkCloseIdx - refRemoveStart + 1
|
|
675
|
+
} else {
|
|
676
|
+
i++
|
|
677
|
+
continue
|
|
678
|
+
}
|
|
679
|
+
let linkOpenToken = null
|
|
680
|
+
let linkCloseToken = null
|
|
681
|
+
if (existingLinkOpen && existingLinkClose) {
|
|
682
|
+
if (whitespaceCount > 0) {
|
|
683
|
+
tokens.splice(whitespaceStart, whitespaceCount)
|
|
684
|
+
refRemoveStart -= whitespaceCount
|
|
685
|
+
}
|
|
686
|
+
if (refRemoveCount > 0) {
|
|
687
|
+
tokens.splice(refRemoveStart, refRemoveCount)
|
|
688
|
+
}
|
|
689
|
+
linkOpenToken = existingLinkOpen
|
|
690
|
+
linkCloseToken = existingLinkClose
|
|
691
|
+
} else {
|
|
692
|
+
if (!refKey) {
|
|
693
|
+
i++
|
|
694
|
+
continue
|
|
695
|
+
}
|
|
696
|
+
const ref = references[refKey]
|
|
697
|
+
if (!ref) {
|
|
698
|
+
i++
|
|
699
|
+
continue
|
|
700
|
+
}
|
|
701
|
+
if (whitespaceCount > 0) {
|
|
702
|
+
tokens.splice(whitespaceStart, whitespaceCount)
|
|
703
|
+
refRemoveStart -= whitespaceCount
|
|
704
|
+
}
|
|
705
|
+
if (refRemoveCount > 0) {
|
|
706
|
+
tokens.splice(refRemoveStart, refRemoveCount)
|
|
707
|
+
}
|
|
708
|
+
linkOpenToken = new Token('link_open', 'a', 1)
|
|
709
|
+
linkOpenToken.attrs = [['href', ref.href]]
|
|
710
|
+
if (ref.title) linkOpenToken.attrPush(['title', ref.title])
|
|
711
|
+
linkOpenToken.markup = '[]'
|
|
712
|
+
linkOpenToken.info = 'auto'
|
|
713
|
+
linkCloseToken = new Token('link_close', 'a', -1)
|
|
714
|
+
linkCloseToken.markup = '[]'
|
|
715
|
+
linkCloseToken.info = 'auto'
|
|
716
|
+
}
|
|
717
|
+
tokens.splice(closeIdx, 1)
|
|
718
|
+
tokens.splice(i, 1)
|
|
719
|
+
|
|
720
|
+
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken)
|
|
721
|
+
i = nextIndex
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Link cleanup helpers
|
|
726
|
+
const mergeBrokenMarksAroundLinks = (tokens) => {
|
|
727
|
+
let i = 0
|
|
728
|
+
while (i < tokens.length) {
|
|
729
|
+
const closeToken = tokens[i]
|
|
730
|
+
if (!closeToken || !/_close$/.test(closeToken.type)) {
|
|
731
|
+
i++
|
|
732
|
+
continue
|
|
733
|
+
}
|
|
734
|
+
const openType = closeToken.type.replace('_close', '_open')
|
|
735
|
+
let j = i + 1
|
|
736
|
+
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
737
|
+
if (j >= tokens.length || tokens[j].type !== 'link_open') {
|
|
738
|
+
i++
|
|
739
|
+
continue
|
|
740
|
+
}
|
|
741
|
+
let linkDepth = 1
|
|
742
|
+
j++
|
|
743
|
+
while (j < tokens.length && linkDepth > 0) {
|
|
744
|
+
if (tokens[j].type === 'link_open') linkDepth++
|
|
745
|
+
if (tokens[j].type === 'link_close') linkDepth--
|
|
746
|
+
j++
|
|
747
|
+
}
|
|
748
|
+
if (linkDepth !== 0) {
|
|
749
|
+
i++
|
|
750
|
+
continue
|
|
751
|
+
}
|
|
752
|
+
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
753
|
+
if (j >= tokens.length) {
|
|
754
|
+
i++
|
|
755
|
+
continue
|
|
756
|
+
}
|
|
757
|
+
const reopenToken = tokens[j]
|
|
758
|
+
if (reopenToken.type !== openType || reopenToken.level !== closeToken.level) {
|
|
759
|
+
i++
|
|
760
|
+
continue
|
|
761
|
+
}
|
|
762
|
+
tokens.splice(j, 1)
|
|
763
|
+
tokens.splice(i, 1)
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export {
|
|
768
|
+
normalizeReferenceCandidate,
|
|
769
|
+
restoreLabelWhitespace,
|
|
770
|
+
convertInlineLinks,
|
|
771
|
+
convertCollapsedReferenceLinks,
|
|
772
|
+
mergeBrokenMarksAroundLinks,
|
|
773
|
+
getMapFromTokenRange
|
|
774
|
+
}
|