@peaceroad/markdown-it-strong-ja 0.8.0 → 0.8.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 +14 -2
- package/index.js +36 -19
- package/package.json +8 -6
- package/src/token-compat.js +2 -8
- package/src/token-core.js +71 -57
- package/src/token-link-utils.js +381 -190
- package/src/token-postprocess/broken-ref.js +475 -0
- package/src/token-postprocess/guards.js +176 -113
- package/src/token-postprocess/orchestrator.js +311 -372
- package/src/token-utils.js +42 -13
package/src/token-link-utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Token from 'markdown-it/lib/token.mjs'
|
|
2
2
|
import { isWhiteSpace } from 'markdown-it/lib/common/utils.mjs'
|
|
3
|
+
import { getReferenceCount } from './token-utils.js'
|
|
3
4
|
|
|
4
5
|
const CHAR_OPEN_BRACKET = 0x5B // [
|
|
5
6
|
const CHAR_CLOSE_BRACKET = 0x5D // ]
|
|
@@ -28,6 +29,26 @@ const isWhitespaceToken = (token) => {
|
|
|
28
29
|
return isWhitespace
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
const hasReferenceLabelMarkerRange = (tokens, startIdx, endIdx) => {
|
|
33
|
+
if (startIdx > endIdx) return false
|
|
34
|
+
for (let idx = startIdx; idx <= endIdx; idx++) {
|
|
35
|
+
const token = tokens[idx]
|
|
36
|
+
if (!token || !token.type) continue
|
|
37
|
+
if (token.type === 'text' || token.type === 'code_inline') {
|
|
38
|
+
const content = token.content
|
|
39
|
+
if (content && (content.indexOf('*') !== -1 || content.indexOf('_') !== -1)) return true
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
if (token.type === 'softbreak' || token.type === 'hardbreak') continue
|
|
43
|
+
if (token.markup &&
|
|
44
|
+
(token.type.endsWith('_open') || token.type.endsWith('_close')) &&
|
|
45
|
+
(token.markup.indexOf('*') !== -1 || token.markup.indexOf('_') !== -1)) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
31
52
|
const buildReferenceLabelRange = (tokens, startIdx, endIdx) => {
|
|
32
53
|
if (startIdx > endIdx) return ''
|
|
33
54
|
let label = ''
|
|
@@ -58,11 +79,6 @@ const getNormalizeRef = (state) => {
|
|
|
58
79
|
return normalize
|
|
59
80
|
}
|
|
60
81
|
|
|
61
|
-
const adjustTokenLevels = (tokens, startIdx, endIdx, delta) => {
|
|
62
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
63
|
-
if (tokens[i]) tokens[i].level += delta
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
82
|
|
|
67
83
|
const cloneMap = (map) => {
|
|
68
84
|
if (!map || !Array.isArray(map)) return null
|
|
@@ -109,7 +125,7 @@ const applyBracketSegmentFlags = (token, seg) => {
|
|
|
109
125
|
token.__strongJaBracketAtomic = true
|
|
110
126
|
} else if (seg === '[]') {
|
|
111
127
|
token.__strongJaHasBracket = true
|
|
112
|
-
token.__strongJaBracketAtomic =
|
|
128
|
+
token.__strongJaBracketAtomic = true
|
|
113
129
|
} else {
|
|
114
130
|
token.__strongJaHasBracket = false
|
|
115
131
|
token.__strongJaBracketAtomic = false
|
|
@@ -204,13 +220,15 @@ const buildLinkCloseMap = (tokens, startIdx, endIdx) => {
|
|
|
204
220
|
return closeMap
|
|
205
221
|
}
|
|
206
222
|
|
|
207
|
-
const
|
|
223
|
+
const collectWrappedLabelPairs = (tokens, collapsedStartIdx, collapsedEndIdx) => {
|
|
208
224
|
const wrapperPairs = []
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
225
|
+
while (true) {
|
|
226
|
+
const wrapperOffset = wrapperPairs.length
|
|
227
|
+
const closeIdx = collapsedStartIdx - 1 - wrapperOffset
|
|
228
|
+
const openIdx = collapsedEndIdx + 1 + wrapperOffset
|
|
229
|
+
if (closeIdx < 0 || openIdx >= tokens.length) break
|
|
230
|
+
const prevToken = tokens[closeIdx]
|
|
231
|
+
const nextToken = tokens[openIdx]
|
|
214
232
|
if (!prevToken || !nextToken) break
|
|
215
233
|
if (!prevToken.type || !prevToken.type.endsWith('_close')) break
|
|
216
234
|
const expectedOpen = prevToken.type.replace('_close', '_open')
|
|
@@ -220,214 +238,363 @@ const wrapLabelTokensWithLink = (tokens, labelStartIdx, labelEndIdx, linkOpenTok
|
|
|
220
238
|
tag: prevToken.tag,
|
|
221
239
|
markup: prevToken.markup,
|
|
222
240
|
openMap: cloneMap(nextToken.map),
|
|
223
|
-
closeMap: cloneMap(prevToken.map)
|
|
241
|
+
closeMap: cloneMap(prevToken.map),
|
|
242
|
+
closeIdx,
|
|
243
|
+
openIdx
|
|
224
244
|
})
|
|
225
|
-
tokens.splice(endIdx + 1, 1)
|
|
226
|
-
tokens.splice(startIdx - 1, 1)
|
|
227
|
-
startIdx -= 1
|
|
228
|
-
endIdx -= 1
|
|
229
245
|
}
|
|
246
|
+
return wrapperPairs
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const resolveWrappedLabelReplaceRange = (wrapperPairs, collapsedStartIdx, collapsedEndIdx) => {
|
|
250
|
+
if (wrapperPairs.length === 0) {
|
|
251
|
+
return {
|
|
252
|
+
replaceStart: collapsedStartIdx,
|
|
253
|
+
replaceEnd: collapsedEndIdx
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const outerPair = wrapperPairs[wrapperPairs.length - 1]
|
|
257
|
+
return {
|
|
258
|
+
replaceStart: outerPair.closeIdx,
|
|
259
|
+
replaceEnd: outerPair.openIdx
|
|
260
|
+
}
|
|
261
|
+
}
|
|
230
262
|
|
|
231
|
-
|
|
263
|
+
const resolveInsertedWrapperMap = (pairMap, labelMap) => {
|
|
264
|
+
return pairMap || labelMap
|
|
265
|
+
}
|
|
232
266
|
|
|
233
|
-
|
|
234
|
-
const firstLabelToken =
|
|
267
|
+
const buildWrappedLabelReplacement = (labelTokens, linkOpenToken, linkCloseToken, wrapperPairs, labelMap) => {
|
|
268
|
+
const firstLabelToken = labelTokens[0]
|
|
235
269
|
const linkLevel = firstLabelToken ? Math.max(firstLabelToken.level - 1, 0) : 0
|
|
236
270
|
linkOpenToken.level = linkLevel
|
|
237
271
|
linkCloseToken.level = linkLevel
|
|
238
|
-
const labelMap = getMapFromTokenRange(tokens, startIdx, endIdx) || getNearbyMap(tokens, startIdx, endIdx)
|
|
239
272
|
if (labelMap) {
|
|
240
273
|
if (!linkOpenToken.map) linkOpenToken.map = cloneMap(labelMap)
|
|
241
274
|
if (!linkCloseToken.map) linkCloseToken.map = cloneMap(labelMap)
|
|
242
275
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
276
|
+
for (let idx = 0; idx < labelTokens.length; idx++) {
|
|
277
|
+
if (labelTokens[idx]) labelTokens[idx].level += 1
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const replacement = [linkOpenToken]
|
|
281
|
+
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
282
|
+
const pair = wrapperPairs[wp]
|
|
283
|
+
const innerOpen = new Token(pair.base + '_open', pair.tag, 1)
|
|
284
|
+
innerOpen.markup = pair.markup
|
|
285
|
+
innerOpen.level = linkLevel + 1 + wp
|
|
286
|
+
const openMap = resolveInsertedWrapperMap(pair.openMap, labelMap)
|
|
287
|
+
if (openMap && !innerOpen.map) innerOpen.map = cloneMap(openMap)
|
|
288
|
+
replacement.push(innerOpen)
|
|
289
|
+
}
|
|
290
|
+
replacement.push(...labelTokens)
|
|
291
|
+
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
292
|
+
const pair = wrapperPairs[wp]
|
|
293
|
+
const innerClose = new Token(pair.base + '_close', pair.tag, -1)
|
|
294
|
+
innerClose.markup = pair.markup
|
|
295
|
+
innerClose.level = linkLevel + 1 + wp
|
|
296
|
+
const closeMap = resolveInsertedWrapperMap(pair.closeMap, labelMap)
|
|
297
|
+
if (closeMap && !innerClose.map) innerClose.map = cloneMap(closeMap)
|
|
298
|
+
replacement.push(innerClose)
|
|
299
|
+
}
|
|
300
|
+
replacement.push(linkCloseToken)
|
|
301
|
+
return replacement
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const wrapLabelTokensWithLink = (
|
|
305
|
+
tokens,
|
|
306
|
+
collapsedStartIdx,
|
|
307
|
+
collapsedEndIdx,
|
|
308
|
+
labelStartIdx,
|
|
309
|
+
labelEndIdx,
|
|
310
|
+
linkOpenToken,
|
|
311
|
+
linkCloseToken
|
|
312
|
+
) => {
|
|
313
|
+
if (labelStartIdx > labelEndIdx) return collapsedStartIdx
|
|
314
|
+
const labelTokens = tokens.slice(labelStartIdx, labelEndIdx + 1)
|
|
315
|
+
const wrapperPairs = collectWrappedLabelPairs(tokens, collapsedStartIdx, collapsedEndIdx)
|
|
316
|
+
const { replaceStart, replaceEnd } = resolveWrappedLabelReplaceRange(
|
|
317
|
+
wrapperPairs,
|
|
318
|
+
collapsedStartIdx,
|
|
319
|
+
collapsedEndIdx
|
|
320
|
+
)
|
|
321
|
+
const labelMap = getMapFromTokenRange(tokens, labelStartIdx, labelEndIdx) || getNearbyMap(tokens, replaceStart, replaceEnd)
|
|
322
|
+
const replacement = buildWrappedLabelReplacement(
|
|
323
|
+
labelTokens,
|
|
324
|
+
linkOpenToken,
|
|
325
|
+
linkCloseToken,
|
|
326
|
+
wrapperPairs,
|
|
327
|
+
labelMap
|
|
328
|
+
)
|
|
329
|
+
tokens.splice(replaceStart, replaceEnd - replaceStart + 1, ...replacement)
|
|
330
|
+
return replaceStart + replacement.length
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const resolveCollapsedReferenceTarget = (
|
|
334
|
+
tokens,
|
|
335
|
+
state,
|
|
336
|
+
refRemoveStart,
|
|
337
|
+
getLabelText,
|
|
338
|
+
getLinkCloseMap
|
|
339
|
+
) => {
|
|
340
|
+
let refKey = null
|
|
341
|
+
let refRemoveCount = 0
|
|
342
|
+
let existingLinkOpen = null
|
|
343
|
+
let existingLinkClose = null
|
|
344
|
+
const nextToken = tokens[refRemoveStart]
|
|
345
|
+
if (isBracketToken(nextToken, '[]')) {
|
|
346
|
+
refKey = normalizeReferenceCandidate(state, getLabelText())
|
|
347
|
+
refRemoveCount = 1
|
|
348
|
+
} else if (isBracketToken(nextToken, '[')) {
|
|
349
|
+
let refCloseIdx = refRemoveStart + 1
|
|
350
|
+
while (refCloseIdx < tokens.length && !isBracketToken(tokens[refCloseIdx], ']')) {
|
|
351
|
+
refCloseIdx++
|
|
259
352
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
labelLength++
|
|
353
|
+
if (refCloseIdx >= tokens.length) return null
|
|
354
|
+
const refStart = refRemoveStart + 1
|
|
355
|
+
const refEnd = refCloseIdx - 1
|
|
356
|
+
if (refStart > refEnd) {
|
|
357
|
+
refKey = normalizeReferenceCandidate(state, getLabelText())
|
|
358
|
+
} else {
|
|
359
|
+
const refLabelText = buildReferenceLabelRange(tokens, refStart, refEnd)
|
|
360
|
+
refKey = normalizeReferenceCandidate(state, refLabelText)
|
|
269
361
|
}
|
|
362
|
+
refRemoveCount = refCloseIdx - refRemoveStart + 1
|
|
363
|
+
} else if (nextToken && nextToken.type === 'link_open') {
|
|
364
|
+
const linkCloseMap = getLinkCloseMap(refRemoveStart)
|
|
365
|
+
const linkCloseIdx = linkCloseMap.get(refRemoveStart) ?? -1
|
|
366
|
+
if (linkCloseIdx === -1) return null
|
|
367
|
+
existingLinkOpen = tokens[refRemoveStart]
|
|
368
|
+
existingLinkClose = tokens[linkCloseIdx]
|
|
369
|
+
refRemoveCount = linkCloseIdx - refRemoveStart + 1
|
|
370
|
+
} else {
|
|
371
|
+
return null
|
|
270
372
|
}
|
|
373
|
+
return {
|
|
374
|
+
refKey,
|
|
375
|
+
refRemoveCount,
|
|
376
|
+
existingLinkOpen,
|
|
377
|
+
existingLinkClose
|
|
378
|
+
}
|
|
379
|
+
}
|
|
271
380
|
|
|
272
|
-
|
|
381
|
+
const buildAutoCollapsedReferenceLinkPair = (ref) => {
|
|
382
|
+
if (!ref) return null
|
|
383
|
+
const linkOpenToken = new Token('link_open', 'a', 1)
|
|
384
|
+
linkOpenToken.attrs = [['href', ref.href]]
|
|
385
|
+
if (ref.title) linkOpenToken.attrPush(['title', ref.title])
|
|
386
|
+
linkOpenToken.markup = '[]'
|
|
387
|
+
linkOpenToken.info = 'auto'
|
|
388
|
+
|
|
389
|
+
const linkCloseToken = new Token('link_close', 'a', -1)
|
|
390
|
+
linkCloseToken.markup = '[]'
|
|
391
|
+
linkCloseToken.info = 'auto'
|
|
392
|
+
return { linkOpenToken, linkCloseToken }
|
|
273
393
|
}
|
|
274
394
|
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
if (referenceCount === 0) {
|
|
284
|
-
return
|
|
395
|
+
const resolveCollapsedReferenceLinkPair = (references, target) => {
|
|
396
|
+
if (!target) return null
|
|
397
|
+
if (target.existingLinkOpen && target.existingLinkClose) {
|
|
398
|
+
return {
|
|
399
|
+
linkOpenToken: target.existingLinkOpen,
|
|
400
|
+
linkCloseToken: target.existingLinkClose
|
|
401
|
+
}
|
|
285
402
|
}
|
|
403
|
+
if (!target.refKey) return null
|
|
404
|
+
return buildAutoCollapsedReferenceLinkPair(references[target.refKey])
|
|
405
|
+
}
|
|
286
406
|
|
|
287
|
-
|
|
407
|
+
const applyCollapsedReferenceRewrite = (
|
|
408
|
+
tokens,
|
|
409
|
+
startIdx,
|
|
410
|
+
labelStart,
|
|
411
|
+
labelEnd,
|
|
412
|
+
suffixRemoveCount,
|
|
413
|
+
linkOpenToken,
|
|
414
|
+
linkCloseToken
|
|
415
|
+
) => {
|
|
416
|
+
const labelLength = labelEnd - labelStart + 1
|
|
417
|
+
const collapsedReplaceCount = labelLength + 2 + suffixRemoveCount
|
|
418
|
+
const collapsedEnd = startIdx + collapsedReplaceCount - 1
|
|
419
|
+
linkOpenToken.__strongJaMergeMarksAroundLink = true
|
|
420
|
+
linkCloseToken.__strongJaMergeMarksAroundLink = true
|
|
421
|
+
return wrapLabelTokensWithLink(
|
|
422
|
+
tokens,
|
|
423
|
+
startIdx,
|
|
424
|
+
collapsedEnd,
|
|
425
|
+
labelStart,
|
|
426
|
+
labelEnd,
|
|
427
|
+
linkOpenToken,
|
|
428
|
+
linkCloseToken
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const COLLAPSED_REFERENCE_SCAN_RETRY = Symbol('collapsed-reference-scan-retry')
|
|
433
|
+
const COLLAPSED_REFERENCE_SCAN_SKIP = Symbol('collapsed-reference-scan-skip')
|
|
434
|
+
|
|
435
|
+
const createCollapsedReferenceLinkCloseMapAccessors = (tokens, cache = null) => {
|
|
288
436
|
let linkCloseMap = null
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
linkCloseMap
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
if (!isBracketToken(tokens[i], '[')) {
|
|
295
|
-
i++
|
|
296
|
-
continue
|
|
297
|
-
}
|
|
298
|
-
let closeIdx = i + 1
|
|
299
|
-
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
300
|
-
const closeToken = tokens[closeIdx]
|
|
301
|
-
if (closeToken && closeToken.type === 'text' && splitBracketToken(tokens, closeIdx)) {
|
|
302
|
-
linkCloseMap = null
|
|
303
|
-
continue
|
|
304
|
-
}
|
|
305
|
-
if (closeToken && closeToken.type === 'link_open') {
|
|
306
|
-
closeIdx = -1
|
|
307
|
-
break
|
|
437
|
+
const getLinkCloseMap = (startIdx = 0) => {
|
|
438
|
+
if (cache) {
|
|
439
|
+
if (cache.linkCloseMap === undefined) {
|
|
440
|
+
cache.linkCloseMap = buildLinkCloseMap(tokens, 0, tokens.length - 1)
|
|
308
441
|
}
|
|
309
|
-
|
|
442
|
+
return cache.linkCloseMap
|
|
310
443
|
}
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
continue
|
|
444
|
+
if (linkCloseMap === null) {
|
|
445
|
+
linkCloseMap = buildLinkCloseMap(tokens, startIdx, tokens.length - 1)
|
|
314
446
|
}
|
|
447
|
+
return linkCloseMap
|
|
448
|
+
}
|
|
449
|
+
const invalidateLinkCloseMap = () => {
|
|
450
|
+
linkCloseMap = null
|
|
451
|
+
if (cache) cache.linkCloseMap = undefined
|
|
452
|
+
}
|
|
453
|
+
return { getLinkCloseMap, invalidateLinkCloseMap }
|
|
454
|
+
}
|
|
315
455
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
456
|
+
const findCollapsedReferenceLabelClose = (tokens, startIdx, invalidateLinkCloseMap) => {
|
|
457
|
+
let closeIdx = startIdx + 1
|
|
458
|
+
while (closeIdx < tokens.length) {
|
|
459
|
+
if (isBracketToken(tokens[closeIdx], ']')) return closeIdx
|
|
460
|
+
const closeToken = tokens[closeIdx]
|
|
461
|
+
if (closeToken && closeToken.type === 'text' && splitBracketToken(tokens, closeIdx)) {
|
|
462
|
+
invalidateLinkCloseMap()
|
|
463
|
+
return COLLAPSED_REFERENCE_SCAN_RETRY
|
|
319
464
|
}
|
|
465
|
+
if (closeToken && closeToken.type === 'link_open') return -1
|
|
466
|
+
closeIdx++
|
|
467
|
+
}
|
|
468
|
+
return -1
|
|
469
|
+
}
|
|
320
470
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
471
|
+
const buildCollapsedReferenceCandidate = (
|
|
472
|
+
tokens,
|
|
473
|
+
state,
|
|
474
|
+
startIdx,
|
|
475
|
+
closeIdx,
|
|
476
|
+
getLinkCloseMap,
|
|
477
|
+
invalidateLinkCloseMap
|
|
478
|
+
) => {
|
|
479
|
+
if (closeIdx === startIdx + 1) return null
|
|
480
|
+
|
|
481
|
+
const labelStart = startIdx + 1
|
|
482
|
+
const labelEnd = closeIdx - 1
|
|
483
|
+
if (!hasReferenceLabelMarkerRange(tokens, labelStart, labelEnd)) return null
|
|
484
|
+
|
|
485
|
+
let labelText = null
|
|
486
|
+
const getLabelText = () => {
|
|
487
|
+
if (labelText === null) labelText = buildReferenceLabelRange(tokens, labelStart, labelEnd)
|
|
488
|
+
return labelText
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const whitespaceStart = closeIdx + 1
|
|
492
|
+
let refRemoveStart = whitespaceStart
|
|
493
|
+
while (refRemoveStart < tokens.length && isWhitespaceToken(tokens[refRemoveStart])) {
|
|
494
|
+
refRemoveStart++
|
|
495
|
+
}
|
|
496
|
+
const refStartToken = tokens[refRemoveStart]
|
|
497
|
+
if (refStartToken && refStartToken.type === 'text' && splitBracketToken(tokens, refRemoveStart)) {
|
|
498
|
+
invalidateLinkCloseMap()
|
|
499
|
+
return COLLAPSED_REFERENCE_SCAN_RETRY
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const target = resolveCollapsedReferenceTarget(
|
|
503
|
+
tokens,
|
|
504
|
+
state,
|
|
505
|
+
refRemoveStart,
|
|
506
|
+
getLabelText,
|
|
507
|
+
getLinkCloseMap
|
|
508
|
+
)
|
|
509
|
+
if (!target) return null
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
labelStart,
|
|
513
|
+
labelEnd,
|
|
514
|
+
suffixRemoveCount: (refRemoveStart - whitespaceStart) + target.refRemoveCount,
|
|
515
|
+
target
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const tryConvertCollapsedReferenceAt = (
|
|
520
|
+
tokens,
|
|
521
|
+
state,
|
|
522
|
+
references,
|
|
523
|
+
startIdx,
|
|
524
|
+
getLinkCloseMap,
|
|
525
|
+
invalidateLinkCloseMap,
|
|
526
|
+
onChangeStart = null
|
|
527
|
+
) => {
|
|
528
|
+
if (splitBracketToken(tokens, startIdx)) {
|
|
529
|
+
invalidateLinkCloseMap()
|
|
530
|
+
return COLLAPSED_REFERENCE_SCAN_RETRY
|
|
531
|
+
}
|
|
532
|
+
if (!isBracketToken(tokens[startIdx], '[')) return COLLAPSED_REFERENCE_SCAN_SKIP
|
|
533
|
+
|
|
534
|
+
const closeIdx = findCollapsedReferenceLabelClose(tokens, startIdx, invalidateLinkCloseMap)
|
|
535
|
+
if (closeIdx === COLLAPSED_REFERENCE_SCAN_RETRY) return COLLAPSED_REFERENCE_SCAN_RETRY
|
|
536
|
+
if (closeIdx === -1) return COLLAPSED_REFERENCE_SCAN_SKIP
|
|
537
|
+
|
|
538
|
+
const candidate = buildCollapsedReferenceCandidate(
|
|
539
|
+
tokens,
|
|
540
|
+
state,
|
|
541
|
+
startIdx,
|
|
542
|
+
closeIdx,
|
|
543
|
+
getLinkCloseMap,
|
|
544
|
+
invalidateLinkCloseMap
|
|
545
|
+
)
|
|
546
|
+
if (candidate === COLLAPSED_REFERENCE_SCAN_RETRY) return COLLAPSED_REFERENCE_SCAN_RETRY
|
|
547
|
+
if (!candidate) return COLLAPSED_REFERENCE_SCAN_SKIP
|
|
548
|
+
|
|
549
|
+
const linkPair = resolveCollapsedReferenceLinkPair(references, candidate.target)
|
|
550
|
+
if (!linkPair) return COLLAPSED_REFERENCE_SCAN_SKIP
|
|
551
|
+
|
|
552
|
+
if (onChangeStart) onChangeStart(startIdx)
|
|
553
|
+
invalidateLinkCloseMap()
|
|
554
|
+
return applyCollapsedReferenceRewrite(
|
|
555
|
+
tokens,
|
|
556
|
+
startIdx,
|
|
557
|
+
candidate.labelStart,
|
|
558
|
+
candidate.labelEnd,
|
|
559
|
+
candidate.suffixRemoveCount,
|
|
560
|
+
linkPair.linkOpenToken,
|
|
561
|
+
linkPair.linkCloseToken
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const convertCollapsedReferenceLinks = (tokens, state, cache = null, onChangeStart = null) => {
|
|
566
|
+
const references = state.env && state.env.references
|
|
567
|
+
if (!references) return false
|
|
568
|
+
if (getReferenceCount(state) === 0) {
|
|
569
|
+
return false
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
let changed = false
|
|
573
|
+
let i = 0
|
|
574
|
+
const { getLinkCloseMap, invalidateLinkCloseMap } = createCollapsedReferenceLinkCloseMapAccessors(tokens, cache)
|
|
575
|
+
while (i < tokens.length) {
|
|
576
|
+
const nextIndex = tryConvertCollapsedReferenceAt(
|
|
577
|
+
tokens,
|
|
578
|
+
state,
|
|
579
|
+
references,
|
|
580
|
+
i,
|
|
581
|
+
getLinkCloseMap,
|
|
582
|
+
invalidateLinkCloseMap,
|
|
583
|
+
onChangeStart
|
|
584
|
+
)
|
|
585
|
+
if (nextIndex === COLLAPSED_REFERENCE_SCAN_RETRY) continue
|
|
586
|
+
if (nextIndex === COLLAPSED_REFERENCE_SCAN_SKIP) {
|
|
379
587
|
i++
|
|
380
588
|
continue
|
|
381
589
|
}
|
|
382
|
-
|
|
383
|
-
let linkCloseToken = null
|
|
384
|
-
if (existingLinkOpen && existingLinkClose) {
|
|
385
|
-
linkCloseMap = null
|
|
386
|
-
if (whitespaceCount > 0) {
|
|
387
|
-
tokens.splice(whitespaceStart, whitespaceCount)
|
|
388
|
-
refRemoveStart -= whitespaceCount
|
|
389
|
-
}
|
|
390
|
-
if (refRemoveCount > 0) {
|
|
391
|
-
tokens.splice(refRemoveStart, refRemoveCount)
|
|
392
|
-
}
|
|
393
|
-
linkOpenToken = existingLinkOpen
|
|
394
|
-
linkCloseToken = existingLinkClose
|
|
395
|
-
} else {
|
|
396
|
-
if (!refKey) {
|
|
397
|
-
i++
|
|
398
|
-
continue
|
|
399
|
-
}
|
|
400
|
-
const ref = references[refKey]
|
|
401
|
-
if (!ref) {
|
|
402
|
-
i++
|
|
403
|
-
continue
|
|
404
|
-
}
|
|
405
|
-
linkCloseMap = null
|
|
406
|
-
if (whitespaceCount > 0) {
|
|
407
|
-
tokens.splice(whitespaceStart, whitespaceCount)
|
|
408
|
-
refRemoveStart -= whitespaceCount
|
|
409
|
-
}
|
|
410
|
-
if (refRemoveCount > 0) {
|
|
411
|
-
tokens.splice(refRemoveStart, refRemoveCount)
|
|
412
|
-
}
|
|
413
|
-
linkOpenToken = new Token('link_open', 'a', 1)
|
|
414
|
-
linkOpenToken.attrs = [['href', ref.href]]
|
|
415
|
-
if (ref.title) linkOpenToken.attrPush(['title', ref.title])
|
|
416
|
-
linkOpenToken.markup = '[]'
|
|
417
|
-
linkOpenToken.info = 'auto'
|
|
418
|
-
linkCloseToken = new Token('link_close', 'a', -1)
|
|
419
|
-
linkCloseToken.markup = '[]'
|
|
420
|
-
linkCloseToken.info = 'auto'
|
|
421
|
-
}
|
|
422
|
-
tokens.splice(closeIdx, 1)
|
|
423
|
-
tokens.splice(i, 1)
|
|
424
|
-
|
|
425
|
-
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken)
|
|
590
|
+
changed = true
|
|
426
591
|
i = nextIndex
|
|
427
592
|
}
|
|
593
|
+
return changed
|
|
428
594
|
}
|
|
429
595
|
|
|
430
|
-
const
|
|
596
|
+
const collectBrokenMarkLinkMergeRemovals = (tokens) => {
|
|
597
|
+
const removals = []
|
|
431
598
|
let i = 0
|
|
432
599
|
while (i < tokens.length) {
|
|
433
600
|
const closeToken = tokens[i]
|
|
@@ -439,7 +606,9 @@ const mergeBrokenMarksAroundLinks = (tokens) => {
|
|
|
439
606
|
const openType = closeToken.type.replace('_close', '_open')
|
|
440
607
|
let j = i + 1
|
|
441
608
|
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
442
|
-
if (j >= tokens.length ||
|
|
609
|
+
if (j >= tokens.length ||
|
|
610
|
+
tokens[j].type !== 'link_open' ||
|
|
611
|
+
tokens[j].__strongJaMergeMarksAroundLink !== true) {
|
|
443
612
|
i++
|
|
444
613
|
continue
|
|
445
614
|
}
|
|
@@ -464,9 +633,31 @@ const mergeBrokenMarksAroundLinks = (tokens) => {
|
|
|
464
633
|
i++
|
|
465
634
|
continue
|
|
466
635
|
}
|
|
467
|
-
|
|
468
|
-
|
|
636
|
+
removals.push({ closeIdx: i, reopenIdx: j })
|
|
637
|
+
i = j + 1
|
|
469
638
|
}
|
|
639
|
+
return removals
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const applyBrokenMarkLinkMergeRemovals = (tokens, removals, onChangeStart = null) => {
|
|
643
|
+
if (!removals || removals.length === 0) return false
|
|
644
|
+
const removeFlags = new Array(tokens.length).fill(false)
|
|
645
|
+
for (let idx = removals.length - 1; idx >= 0; idx--) {
|
|
646
|
+
const removal = removals[idx]
|
|
647
|
+
if (onChangeStart) onChangeStart(removal.closeIdx)
|
|
648
|
+
removeFlags[removal.closeIdx] = true
|
|
649
|
+
removeFlags[removal.reopenIdx] = true
|
|
650
|
+
}
|
|
651
|
+
const kept = []
|
|
652
|
+
for (let idx = 0; idx < tokens.length; idx++) {
|
|
653
|
+
if (!removeFlags[idx]) kept.push(tokens[idx])
|
|
654
|
+
}
|
|
655
|
+
tokens.splice(0, tokens.length, ...kept)
|
|
656
|
+
return true
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const mergeBrokenMarksAroundLinks = (tokens, onChangeStart = null) => {
|
|
660
|
+
return applyBrokenMarkLinkMergeRemovals(tokens, collectBrokenMarkLinkMergeRemovals(tokens), onChangeStart)
|
|
470
661
|
}
|
|
471
662
|
|
|
472
663
|
export {
|