@peaceroad/markdown-it-strong-ja 0.7.2 → 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 +326 -195
- package/index.js +27 -40
- package/package.json +26 -6
- package/src/token-compat.js +71 -22
- package/src/token-core.js +521 -132
- package/src/token-link-utils.js +434 -539
- package/src/token-postprocess/broken-ref.js +475 -0
- package/src/token-postprocess/fastpaths.js +349 -0
- package/src/token-postprocess/guards.js +499 -0
- package/src/token-postprocess/orchestrator.js +672 -0
- package/src/token-postprocess.js +1 -334
- package/src/token-utils.js +215 -142
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import { buildLinkCloseMap } from '../token-link-utils.js'
|
|
2
|
+
import {
|
|
3
|
+
isAsteriskEmphasisToken,
|
|
4
|
+
hasTextMarkerCharsInRange,
|
|
5
|
+
buildAsteriskWrapperPrefixStats,
|
|
6
|
+
shouldAttemptBrokenRefRewrite
|
|
7
|
+
} from './guards.js'
|
|
8
|
+
import {
|
|
9
|
+
BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE,
|
|
10
|
+
BROKEN_REF_FAST_PATH_RESULT_NO_MATCH,
|
|
11
|
+
applyBrokenRefTokenOnlyFastPath
|
|
12
|
+
} from './fastpaths.js'
|
|
13
|
+
|
|
14
|
+
const scanBrokenRefState = (text, out) => {
|
|
15
|
+
if (!text || text.indexOf('[') === -1) {
|
|
16
|
+
out.depth = 0
|
|
17
|
+
out.brokenEnd = false
|
|
18
|
+
out.tailOpen = -1
|
|
19
|
+
return out
|
|
20
|
+
}
|
|
21
|
+
let depth = 0
|
|
22
|
+
let lastOpen = -1
|
|
23
|
+
let lastClose = -1
|
|
24
|
+
for (let i = 0; i < text.length; i++) {
|
|
25
|
+
const ch = text.charCodeAt(i)
|
|
26
|
+
if (ch === 0x5B) {
|
|
27
|
+
depth++
|
|
28
|
+
lastOpen = i
|
|
29
|
+
} else if (ch === 0x5D) {
|
|
30
|
+
if (depth > 0) depth--
|
|
31
|
+
lastClose = i
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
out.depth = depth
|
|
35
|
+
out.brokenEnd = depth > 0 && lastOpen > lastClose
|
|
36
|
+
out.tailOpen = out.brokenEnd ? lastOpen : -1
|
|
37
|
+
return out
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const resetBrokenRefScanState = (scanState) => {
|
|
41
|
+
if (!scanState) return scanState
|
|
42
|
+
scanState.depth = 0
|
|
43
|
+
scanState.brokenEnd = false
|
|
44
|
+
scanState.tailOpen = -1
|
|
45
|
+
return scanState
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const updateBracketDepth = (text, depth) => {
|
|
49
|
+
if (!text || depth <= 0) return depth
|
|
50
|
+
for (let i = 0; i < text.length; i++) {
|
|
51
|
+
const ch = text.charCodeAt(i)
|
|
52
|
+
if (ch === 0x5B) {
|
|
53
|
+
depth++
|
|
54
|
+
} else if (ch === 0x5D) {
|
|
55
|
+
if (depth > 0) {
|
|
56
|
+
depth--
|
|
57
|
+
if (depth === 0) return 0
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return depth
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const createBrokenRefWrapperBalance = () => {
|
|
65
|
+
return {
|
|
66
|
+
strong: 0,
|
|
67
|
+
em: 0,
|
|
68
|
+
total: 0
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const updateBrokenRefWrapperBalance = (token, balance) => {
|
|
73
|
+
if (!token || !token.type) return
|
|
74
|
+
if ((token.type === 'strong_open' || token.type === 'strong_close' || token.type === 'em_open' || token.type === 'em_close') &&
|
|
75
|
+
!isAsteriskEmphasisToken(token)) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
if (token.type === 'strong_open') {
|
|
79
|
+
balance.strong++
|
|
80
|
+
balance.total++
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
if (token.type === 'em_open') {
|
|
84
|
+
balance.em++
|
|
85
|
+
balance.total++
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
if (token.type === 'strong_close') {
|
|
89
|
+
if (balance.strong > 0) {
|
|
90
|
+
balance.strong--
|
|
91
|
+
balance.total--
|
|
92
|
+
}
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
if (token.type === 'em_close' && balance.em > 0) {
|
|
96
|
+
balance.em--
|
|
97
|
+
balance.total--
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const expandSegmentEndForWrapperBalance = (tokens, startIdx, endIdx) => {
|
|
102
|
+
if (!tokens || startIdx < 0 || endIdx < startIdx) return endIdx
|
|
103
|
+
const balance = createBrokenRefWrapperBalance()
|
|
104
|
+
let expandedEnd = endIdx
|
|
105
|
+
|
|
106
|
+
for (let i = startIdx; i <= expandedEnd; i++) {
|
|
107
|
+
updateBrokenRefWrapperBalance(tokens[i], balance)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
while (balance.total > 0 && expandedEnd + 1 < tokens.length) {
|
|
111
|
+
expandedEnd++
|
|
112
|
+
updateBrokenRefWrapperBalance(tokens[expandedEnd], balance)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return balance.total > 0 ? -1 : expandedEnd
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const bumpBrokenRefMetric = (metrics, bucket, key) => {
|
|
119
|
+
if (!metrics || !bucket || !key) return
|
|
120
|
+
let table = metrics[bucket]
|
|
121
|
+
if (!table || typeof table !== 'object') {
|
|
122
|
+
table = Object.create(null)
|
|
123
|
+
metrics[bucket] = table
|
|
124
|
+
}
|
|
125
|
+
table[key] = (table[key] || 0) + 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const ensureBrokenRefLinkCloseMap = (tokens, facts = null, hooks = null, fallbackCache = null) => {
|
|
129
|
+
if (hooks && typeof hooks.ensureLinkCloseMap === 'function') {
|
|
130
|
+
return hooks.ensureLinkCloseMap(facts, tokens)
|
|
131
|
+
}
|
|
132
|
+
if (fallbackCache && fallbackCache.linkCloseMap !== undefined) {
|
|
133
|
+
return fallbackCache.linkCloseMap
|
|
134
|
+
}
|
|
135
|
+
const linkCloseMap = (!tokens || tokens.length === 0)
|
|
136
|
+
? new Map()
|
|
137
|
+
: buildLinkCloseMap(tokens, 0, tokens.length - 1)
|
|
138
|
+
if (fallbackCache) {
|
|
139
|
+
fallbackCache.linkCloseMap = linkCloseMap
|
|
140
|
+
}
|
|
141
|
+
return linkCloseMap
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const ensureBrokenRefWrapperPrefixStats = (tokens, facts = null, hooks = null, fallbackCache = null) => {
|
|
145
|
+
if (hooks && typeof hooks.ensureWrapperPrefixStats === 'function') {
|
|
146
|
+
return hooks.ensureWrapperPrefixStats(facts, tokens)
|
|
147
|
+
}
|
|
148
|
+
if (fallbackCache && fallbackCache.wrapperPrefixStats !== undefined) {
|
|
149
|
+
return fallbackCache.wrapperPrefixStats
|
|
150
|
+
}
|
|
151
|
+
const wrapperPrefixStats = (!tokens || tokens.length === 0)
|
|
152
|
+
? null
|
|
153
|
+
: buildAsteriskWrapperPrefixStats(tokens)
|
|
154
|
+
if (fallbackCache) {
|
|
155
|
+
fallbackCache.wrapperPrefixStats = wrapperPrefixStats
|
|
156
|
+
}
|
|
157
|
+
return wrapperPrefixStats
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const invalidateBrokenRefDerivedCaches = (facts = null, hooks = null, fallbackCache = null) => {
|
|
161
|
+
if (fallbackCache) {
|
|
162
|
+
fallbackCache.linkCloseMap = undefined
|
|
163
|
+
fallbackCache.wrapperPrefixStats = undefined
|
|
164
|
+
}
|
|
165
|
+
if (hooks && typeof hooks.invalidateDerivedCaches === 'function') {
|
|
166
|
+
hooks.invalidateDerivedCaches(facts)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const markBrokenRefLevelRebuildFrom = (facts = null, startIdx = 0, hooks = null) => {
|
|
171
|
+
if (hooks && typeof hooks.markLevelRebuildFrom === 'function') {
|
|
172
|
+
hooks.markLevelRebuildFrom(facts, startIdx)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const BROKEN_REF_FLOW_SKIP_NO_TEXT_MARKER = 'skip-no-text-marker'
|
|
177
|
+
const BROKEN_REF_FLOW_SKIP_GUARD = 'skip-guard'
|
|
178
|
+
const BROKEN_REF_FLOW_SKIP_NO_ACTIVE_SIGNATURE = 'skip-no-active-signature'
|
|
179
|
+
const BROKEN_REF_FLOW_SKIP_NO_FASTPATH_MATCH = 'skip-no-fastpath-match'
|
|
180
|
+
const BROKEN_REF_FLOW_REPAIRED = 'repaired'
|
|
181
|
+
|
|
182
|
+
const resolveBrokenRefSegmentEnd = (children, brokenRefCandidate, closeIdx, metrics = null) => {
|
|
183
|
+
let segmentEnd = expandSegmentEndForWrapperBalance(children, brokenRefCandidate.start, closeIdx)
|
|
184
|
+
if (segmentEnd !== -1) return segmentEnd
|
|
185
|
+
bumpBrokenRefMetric(metrics, 'brokenRefFlow', 'wrapper-expand-fallback')
|
|
186
|
+
return closeIdx
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const resolveBrokenRefCandidateGuardFlow = (
|
|
190
|
+
children,
|
|
191
|
+
brokenRefCandidate,
|
|
192
|
+
segmentEnd,
|
|
193
|
+
facts = null,
|
|
194
|
+
hooks = null,
|
|
195
|
+
fallbackCache = null
|
|
196
|
+
) => {
|
|
197
|
+
if (!hasTextMarkerCharsInRange(children, brokenRefCandidate.start, segmentEnd, brokenRefCandidate.startTextOffset)) {
|
|
198
|
+
return BROKEN_REF_FLOW_SKIP_NO_TEXT_MARKER
|
|
199
|
+
}
|
|
200
|
+
const wrapperPrefixStats = ensureBrokenRefWrapperPrefixStats(children, facts, hooks, fallbackCache)
|
|
201
|
+
if (!shouldAttemptBrokenRefRewrite(
|
|
202
|
+
children,
|
|
203
|
+
brokenRefCandidate.start,
|
|
204
|
+
segmentEnd,
|
|
205
|
+
brokenRefCandidate.startTextOffset,
|
|
206
|
+
wrapperPrefixStats
|
|
207
|
+
)) {
|
|
208
|
+
return BROKEN_REF_FLOW_SKIP_GUARD
|
|
209
|
+
}
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const resolveBrokenRefFastPathFlow = (
|
|
214
|
+
children,
|
|
215
|
+
brokenRefCandidate,
|
|
216
|
+
segmentEnd,
|
|
217
|
+
linkCloseMap,
|
|
218
|
+
metrics = null
|
|
219
|
+
) => {
|
|
220
|
+
const fastPathResult = applyBrokenRefTokenOnlyFastPath(
|
|
221
|
+
children,
|
|
222
|
+
brokenRefCandidate.start,
|
|
223
|
+
segmentEnd,
|
|
224
|
+
linkCloseMap,
|
|
225
|
+
metrics,
|
|
226
|
+
bumpBrokenRefMetric
|
|
227
|
+
)
|
|
228
|
+
if (fastPathResult === BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE) {
|
|
229
|
+
return BROKEN_REF_FLOW_SKIP_NO_ACTIVE_SIGNATURE
|
|
230
|
+
}
|
|
231
|
+
if (fastPathResult === BROKEN_REF_FAST_PATH_RESULT_NO_MATCH) {
|
|
232
|
+
return BROKEN_REF_FLOW_SKIP_NO_FASTPATH_MATCH
|
|
233
|
+
}
|
|
234
|
+
return BROKEN_REF_FLOW_REPAIRED
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const runBrokenRefCandidateRewrite = (
|
|
238
|
+
children,
|
|
239
|
+
brokenRefCandidate,
|
|
240
|
+
closeIdx,
|
|
241
|
+
linkCloseMap,
|
|
242
|
+
metrics = null,
|
|
243
|
+
facts = null,
|
|
244
|
+
hooks = null,
|
|
245
|
+
fallbackCache = null
|
|
246
|
+
) => {
|
|
247
|
+
const segmentEnd = resolveBrokenRefSegmentEnd(children, brokenRefCandidate, closeIdx, metrics)
|
|
248
|
+
const guardFlow = resolveBrokenRefCandidateGuardFlow(
|
|
249
|
+
children,
|
|
250
|
+
brokenRefCandidate,
|
|
251
|
+
segmentEnd,
|
|
252
|
+
facts,
|
|
253
|
+
hooks,
|
|
254
|
+
fallbackCache
|
|
255
|
+
)
|
|
256
|
+
if (guardFlow) return guardFlow
|
|
257
|
+
const fastPathFlow = resolveBrokenRefFastPathFlow(
|
|
258
|
+
children,
|
|
259
|
+
brokenRefCandidate,
|
|
260
|
+
segmentEnd,
|
|
261
|
+
linkCloseMap,
|
|
262
|
+
metrics
|
|
263
|
+
)
|
|
264
|
+
if (fastPathFlow !== BROKEN_REF_FLOW_REPAIRED) return fastPathFlow
|
|
265
|
+
invalidateBrokenRefDerivedCaches(facts, hooks, fallbackCache)
|
|
266
|
+
markBrokenRefLevelRebuildFrom(facts, brokenRefCandidate.start, hooks)
|
|
267
|
+
return BROKEN_REF_FLOW_REPAIRED
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const resetBrokenRefCandidateState = (candidateState) => {
|
|
271
|
+
candidateState.start = -1
|
|
272
|
+
candidateState.depth = 0
|
|
273
|
+
candidateState.startTextOffset = 0
|
|
274
|
+
return candidateState
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const startBrokenRefCandidateState = (candidateState, tokenIdx, scan) => {
|
|
278
|
+
candidateState.start = tokenIdx
|
|
279
|
+
candidateState.depth = scan.depth
|
|
280
|
+
candidateState.startTextOffset = scan.tailOpen > 0 ? scan.tailOpen : 0
|
|
281
|
+
return candidateState
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const createBrokenRefSignalSeed = (facts = null) => {
|
|
285
|
+
return {
|
|
286
|
+
hasBracketText: !!(facts && facts.hasBracketText),
|
|
287
|
+
hasEmphasis: !!(facts && facts.hasEmphasis),
|
|
288
|
+
hasLinkClose: !!(facts && facts.hasLinkClose)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const createBrokenRefPassSignals = (seedSignals = null) => {
|
|
293
|
+
const seed = seedSignals || {}
|
|
294
|
+
return {
|
|
295
|
+
hasBracketText: !!seed.hasBracketText,
|
|
296
|
+
hasEmphasis: !!seed.hasEmphasis,
|
|
297
|
+
hasLinkClose: !!seed.hasLinkClose
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const observeBrokenRefTextToken = (passSignals, candidateState, text, tokenIdx, scanState) => {
|
|
302
|
+
const hasOpenBracket = text.indexOf('[') !== -1
|
|
303
|
+
const hasCloseBracket = text.indexOf(']') !== -1
|
|
304
|
+
if (!passSignals.hasBracketText && (hasOpenBracket || hasCloseBracket)) {
|
|
305
|
+
passSignals.hasBracketText = true
|
|
306
|
+
}
|
|
307
|
+
if (candidateState.start === -1) {
|
|
308
|
+
if (!hasOpenBracket) return
|
|
309
|
+
const scan = scanBrokenRefState(text, scanState)
|
|
310
|
+
if (scan.brokenEnd) {
|
|
311
|
+
startBrokenRefCandidateState(candidateState, tokenIdx, scan)
|
|
312
|
+
}
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
if (!hasOpenBracket && !hasCloseBracket) return
|
|
316
|
+
candidateState.depth = updateBracketDepth(text, candidateState.depth)
|
|
317
|
+
if (candidateState.depth <= 0) {
|
|
318
|
+
resetBrokenRefCandidateState(candidateState)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const observeBrokenRefPassTokenFlags = (passSignals, child) => {
|
|
323
|
+
if (!passSignals.hasEmphasis && isAsteriskEmphasisToken(child)) {
|
|
324
|
+
passSignals.hasEmphasis = true
|
|
325
|
+
}
|
|
326
|
+
if (!passSignals.hasLinkClose && child.type === 'link_close') {
|
|
327
|
+
passSignals.hasLinkClose = true
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const buildBrokenRefRepairPassResult = (didRepair, passSignals) => {
|
|
332
|
+
return {
|
|
333
|
+
didRepair,
|
|
334
|
+
hasBracketText: passSignals.hasBracketText,
|
|
335
|
+
hasEmphasis: passSignals.hasEmphasis,
|
|
336
|
+
hasLinkClose: passSignals.hasLinkClose
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const collectBrokenRefPassSignals = (children, seedSignals = null) => {
|
|
341
|
+
const passSignals = createBrokenRefPassSignals(seedSignals)
|
|
342
|
+
if (!children || children.length === 0) return passSignals
|
|
343
|
+
for (let i = 0; i < children.length; i++) {
|
|
344
|
+
const child = children[i]
|
|
345
|
+
if (!child) continue
|
|
346
|
+
if (!passSignals.hasBracketText && child.type === 'text' && child.content) {
|
|
347
|
+
if (child.content.indexOf('[') !== -1 || child.content.indexOf(']') !== -1) {
|
|
348
|
+
passSignals.hasBracketText = true
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
observeBrokenRefPassTokenFlags(passSignals, child)
|
|
352
|
+
if (passSignals.hasBracketText && passSignals.hasEmphasis && passSignals.hasLinkClose) {
|
|
353
|
+
break
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return passSignals
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const tryRepairBrokenRefCandidateAtLinkOpen = (
|
|
360
|
+
children,
|
|
361
|
+
child,
|
|
362
|
+
childIdx,
|
|
363
|
+
brokenRefCandidate,
|
|
364
|
+
passSignals,
|
|
365
|
+
metrics = null,
|
|
366
|
+
facts = null,
|
|
367
|
+
hooks = null,
|
|
368
|
+
fallbackCache = null
|
|
369
|
+
) => {
|
|
370
|
+
if (!child || child.type !== 'link_open' || brokenRefCandidate.start === -1) return null
|
|
371
|
+
if (brokenRefCandidate.depth <= 0) {
|
|
372
|
+
resetBrokenRefCandidateState(brokenRefCandidate)
|
|
373
|
+
return null
|
|
374
|
+
}
|
|
375
|
+
const linkCloseMap = ensureBrokenRefLinkCloseMap(children, facts, hooks, fallbackCache)
|
|
376
|
+
const closeIdx = linkCloseMap.get(childIdx) ?? -1
|
|
377
|
+
if (closeIdx === -1) return null
|
|
378
|
+
bumpBrokenRefMetric(metrics, 'brokenRefFlow', 'candidate')
|
|
379
|
+
const flowResult = runBrokenRefCandidateRewrite(
|
|
380
|
+
children,
|
|
381
|
+
brokenRefCandidate,
|
|
382
|
+
closeIdx,
|
|
383
|
+
linkCloseMap,
|
|
384
|
+
metrics,
|
|
385
|
+
facts,
|
|
386
|
+
hooks,
|
|
387
|
+
fallbackCache
|
|
388
|
+
)
|
|
389
|
+
if (flowResult !== BROKEN_REF_FLOW_REPAIRED) {
|
|
390
|
+
bumpBrokenRefMetric(metrics, 'brokenRefFlow', flowResult)
|
|
391
|
+
resetBrokenRefCandidateState(brokenRefCandidate)
|
|
392
|
+
return null
|
|
393
|
+
}
|
|
394
|
+
bumpBrokenRefMetric(metrics, 'brokenRefFlow', BROKEN_REF_FLOW_REPAIRED)
|
|
395
|
+
return buildBrokenRefRepairPassResult(true, passSignals)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const runBrokenRefRepairPass = (children, scanState, metrics = null, facts = null, hooks = null) => {
|
|
399
|
+
resetBrokenRefScanState(scanState)
|
|
400
|
+
const brokenRefCandidate = resetBrokenRefCandidateState({ start: -1, depth: 0, startTextOffset: 0 })
|
|
401
|
+
const passSignals = createBrokenRefPassSignals(createBrokenRefSignalSeed(facts))
|
|
402
|
+
const fallbackCache = {
|
|
403
|
+
linkCloseMap: undefined,
|
|
404
|
+
wrapperPrefixStats: undefined
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
for (let j = 0; j < children.length; j++) {
|
|
408
|
+
const child = children[j]
|
|
409
|
+
if (!child) continue
|
|
410
|
+
|
|
411
|
+
if (child.type === 'text' && child.content) {
|
|
412
|
+
observeBrokenRefTextToken(passSignals, brokenRefCandidate, child.content, j, scanState)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
observeBrokenRefPassTokenFlags(passSignals, child)
|
|
416
|
+
const repaired = tryRepairBrokenRefCandidateAtLinkOpen(
|
|
417
|
+
children,
|
|
418
|
+
child,
|
|
419
|
+
j,
|
|
420
|
+
brokenRefCandidate,
|
|
421
|
+
passSignals,
|
|
422
|
+
metrics,
|
|
423
|
+
facts,
|
|
424
|
+
hooks,
|
|
425
|
+
fallbackCache
|
|
426
|
+
)
|
|
427
|
+
if (repaired) return repaired
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return buildBrokenRefRepairPassResult(false, passSignals)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const computeMaxBrokenRefRepairPass = (children, scanState) => {
|
|
434
|
+
resetBrokenRefScanState(scanState)
|
|
435
|
+
let maxRepairPass = 0
|
|
436
|
+
for (let j = 0; j < children.length; j++) {
|
|
437
|
+
const child = children[j]
|
|
438
|
+
if (!child || child.type !== 'text' || !child.content) continue
|
|
439
|
+
if (child.content.indexOf('[') === -1) continue
|
|
440
|
+
if (scanBrokenRefState(child.content, scanState).brokenEnd) {
|
|
441
|
+
maxRepairPass++
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return maxRepairPass
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const runBrokenRefRepairs = (children, maxRepairPass, scanState, metrics = null, facts = null, hooks = null) => {
|
|
448
|
+
let repairPassCount = 0
|
|
449
|
+
let changed = false
|
|
450
|
+
while (repairPassCount < maxRepairPass) {
|
|
451
|
+
const pass = runBrokenRefRepairPass(children, scanState, metrics, facts, hooks)
|
|
452
|
+
if (!pass.didRepair) {
|
|
453
|
+
return {
|
|
454
|
+
changed,
|
|
455
|
+
hasBracketText: pass.hasBracketText,
|
|
456
|
+
hasEmphasis: pass.hasEmphasis,
|
|
457
|
+
hasLinkClose: pass.hasLinkClose
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
changed = true
|
|
461
|
+
repairPassCount++
|
|
462
|
+
}
|
|
463
|
+
const finalSignals = collectBrokenRefPassSignals(children, createBrokenRefSignalSeed(facts))
|
|
464
|
+
return {
|
|
465
|
+
changed,
|
|
466
|
+
hasBracketText: finalSignals.hasBracketText,
|
|
467
|
+
hasEmphasis: finalSignals.hasEmphasis,
|
|
468
|
+
hasLinkClose: finalSignals.hasLinkClose
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export {
|
|
473
|
+
computeMaxBrokenRefRepairPass,
|
|
474
|
+
runBrokenRefRepairs
|
|
475
|
+
}
|