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