@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.
@@ -1,139 +1,29 @@
1
1
  import Token from 'markdown-it/lib/token.mjs'
2
2
  import { buildLinkCloseMap, convertCollapsedReferenceLinks, mergeBrokenMarksAroundLinks } from '../token-link-utils.js'
3
+ import { computeMaxBrokenRefRepairPass, runBrokenRefRepairs } from './broken-ref.js'
3
4
  import {
4
5
  rebuildInlineLevels,
6
+ rebuildInlineLevelsFrom,
5
7
  fixEmOuterStrongSequence,
6
8
  fixLeadingAsteriskEm,
7
9
  fixTrailingStrong
8
10
  } from '../token-core.js'
9
11
  import {
10
- getInlineWrapperBase,
11
12
  getRuntimeOpt,
12
- MODE_FLAG_COMPATIBLE,
13
- MODE_FLAG_AGGRESSIVE,
14
- MODE_FLAG_JAPANESE_PLUS,
15
- MODE_FLAG_JAPANESE_ANY
13
+ getReferenceCount
16
14
  } from '../token-utils.js'
17
15
  import {
18
16
  hasMarkerChars,
19
- isAsteriskEmphasisToken,
20
17
  hasJapaneseContextInRange,
21
18
  hasEmphasisSignalInRange,
22
- hasTextMarkerCharsInRange,
23
19
  buildAsteriskWrapperPrefixStats,
24
- shouldAttemptBrokenRefRewrite,
25
20
  scanInlinePostprocessSignals
26
21
  } from './guards.js'
27
22
  import {
28
- BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE,
29
- BROKEN_REF_FAST_PATH_RESULT_NO_MATCH,
30
- applyBrokenRefTokenOnlyFastPath,
31
23
  tryFixTailPatternTokenOnly,
32
24
  tryFixTailDanglingStrongCloseTokenOnly
33
25
  } from './fastpaths.js'
34
26
 
35
- const scanBrokenRefState = (text, out) => {
36
- if (!text || text.indexOf('[') === -1) {
37
- out.depth = 0
38
- out.brokenEnd = false
39
- out.tailOpen = -1
40
- return out
41
- }
42
- let depth = 0
43
- let lastOpen = -1
44
- let lastClose = -1
45
- for (let i = 0; i < text.length; i++) {
46
- const ch = text.charCodeAt(i)
47
- if (ch === 0x5B) {
48
- depth++
49
- lastOpen = i
50
- } else if (ch === 0x5D) {
51
- if (depth > 0) depth--
52
- lastClose = i
53
- }
54
- }
55
- out.depth = depth
56
- out.brokenEnd = depth > 0 && lastOpen > lastClose
57
- out.tailOpen = out.brokenEnd ? lastOpen : -1
58
- return out
59
- }
60
-
61
- const resetBrokenRefScanState = (scanState) => {
62
- if (!scanState) return scanState
63
- scanState.depth = 0
64
- scanState.brokenEnd = false
65
- scanState.tailOpen = -1
66
- return scanState
67
- }
68
-
69
- const updateBracketDepth = (text, depth) => {
70
- if (!text || depth <= 0) return depth
71
- for (let i = 0; i < text.length; i++) {
72
- const ch = text.charCodeAt(i)
73
- if (ch === 0x5B) {
74
- depth++
75
- } else if (ch === 0x5D) {
76
- if (depth > 0) {
77
- depth--
78
- if (depth === 0) return 0
79
- }
80
- }
81
- }
82
- return depth
83
- }
84
-
85
- const expandSegmentEndForWrapperBalance = (tokens, startIdx, endIdx) => {
86
- if (!tokens || startIdx < 0 || endIdx < startIdx) return endIdx
87
- const depthMap = new Map()
88
- let openDepthTotal = 0
89
- let expandedEnd = endIdx
90
-
91
- for (let i = startIdx; i <= expandedEnd; i++) {
92
- const token = tokens[i]
93
- if (!token || !token.type) continue
94
- if ((token.type === 'strong_open' || token.type === 'strong_close' || token.type === 'em_open' || token.type === 'em_close') &&
95
- !isAsteriskEmphasisToken(token)) {
96
- continue
97
- }
98
- const base = getInlineWrapperBase(token.type)
99
- if (!base) continue
100
- if (token.type.endsWith('_open')) {
101
- depthMap.set(base, (depthMap.get(base) || 0) + 1)
102
- openDepthTotal++
103
- continue
104
- }
105
- const prev = depthMap.get(base) || 0
106
- if (prev > 0) {
107
- depthMap.set(base, prev - 1)
108
- openDepthTotal--
109
- }
110
- }
111
-
112
- while (openDepthTotal > 0 && expandedEnd + 1 < tokens.length) {
113
- expandedEnd++
114
- const token = tokens[expandedEnd]
115
- if (!token || !token.type) continue
116
- if ((token.type === 'strong_open' || token.type === 'strong_close' || token.type === 'em_open' || token.type === 'em_close') &&
117
- !isAsteriskEmphasisToken(token)) {
118
- continue
119
- }
120
- const base = getInlineWrapperBase(token.type)
121
- if (!base) continue
122
- if (token.type.endsWith('_open')) {
123
- depthMap.set(base, (depthMap.get(base) || 0) + 1)
124
- openDepthTotal++
125
- continue
126
- }
127
- const prev = depthMap.get(base) || 0
128
- if (prev > 0) {
129
- depthMap.set(base, prev - 1)
130
- openDepthTotal--
131
- }
132
- }
133
-
134
- return openDepthTotal > 0 ? -1 : expandedEnd
135
- }
136
-
137
27
  const fallbackMarkupByType = (type) => {
138
28
  if (type === 'strong_open' || type === 'strong_close') return '**'
139
29
  if (type === 'em_open' || type === 'em_close') return '*'
@@ -151,7 +41,7 @@ const makeTokenLiteralText = (token) => {
151
41
  token.info = ''
152
42
  }
153
43
 
154
- const sanitizeEmStrongBalance = (tokens) => {
44
+ const sanitizeEmStrongBalance = (tokens, onChangeStart = null) => {
155
45
  if (!tokens || tokens.length === 0) return false
156
46
  const stack = []
157
47
  let changed = false
@@ -168,6 +58,7 @@ const sanitizeEmStrongBalance = (tokens) => {
168
58
  stack.pop()
169
59
  continue
170
60
  }
61
+ if (onChangeStart) onChangeStart(i)
171
62
  makeTokenLiteralText(token)
172
63
  changed = true
173
64
  }
@@ -175,6 +66,7 @@ const sanitizeEmStrongBalance = (tokens) => {
175
66
  const entry = stack[i]
176
67
  const token = tokens[entry.idx]
177
68
  if (!token) continue
69
+ if (onChangeStart) onChangeStart(entry.idx)
178
70
  makeTokenLiteralText(token)
179
71
  changed = true
180
72
  }
@@ -188,179 +80,125 @@ const getPostprocessMetrics = (state) => {
188
80
  return metrics
189
81
  }
190
82
 
191
- const bumpPostprocessMetric = (metrics, bucket, key) => {
192
- if (!metrics || !bucket || !key) return
193
- let table = metrics[bucket]
194
- if (!table || typeof table !== 'object') {
195
- table = Object.create(null)
196
- metrics[bucket] = table
83
+ const buildInlinePostprocessFacts = (children, inlineContent) => {
84
+ const preScan = scanInlinePostprocessSignals(children)
85
+ return {
86
+ hasBracketText: inlineContent.indexOf('[') !== -1 || inlineContent.indexOf(']') !== -1,
87
+ hasEmphasis: preScan.hasEmphasis,
88
+ hasLinkOpen: preScan.hasLinkOpen,
89
+ hasLinkClose: preScan.hasLinkClose,
90
+ hasCodeInline: undefined,
91
+ referenceCount: undefined,
92
+ metrics: undefined,
93
+ linkCloseMap: undefined,
94
+ wrapperPrefixStats: undefined,
95
+ rebuildLevelStart: undefined
197
96
  }
198
- table[key] = (table[key] || 0) + 1
199
97
  }
200
98
 
201
- const runBrokenRefRepairPass = (children, scanState, metrics = null) => {
202
- resetBrokenRefScanState(scanState)
203
- let wrapperPrefixStats = null
204
- let brokenRefStart = -1
205
- let brokenRefDepth = 0
206
- let brokenRefStartTextOffset = 0
207
- let linkCloseMap = null
208
- let hasBracketText = false
209
- let hasEmphasis = false
210
- let hasLinkClose = false
211
-
212
- for (let j = 0; j < children.length; j++) {
213
- const child = children[j]
214
- if (!child) continue
215
-
216
- if (child.type === 'text' && child.content) {
217
- const text = child.content
218
- const hasOpenBracket = text.indexOf('[') !== -1
219
- const hasCloseBracket = text.indexOf(']') !== -1
220
- if (!hasBracketText && (hasOpenBracket || hasCloseBracket)) {
221
- hasBracketText = true
222
- }
223
- if (brokenRefStart === -1) {
224
- if (hasOpenBracket) {
225
- const scan = scanBrokenRefState(text, scanState)
226
- if (scan.brokenEnd) {
227
- brokenRefStart = j
228
- brokenRefDepth = scan.depth
229
- brokenRefStartTextOffset = scan.tailOpen > 0 ? scan.tailOpen : 0
230
- continue
231
- }
232
- }
233
- } else if (hasOpenBracket || hasCloseBracket) {
234
- brokenRefDepth = updateBracketDepth(text, brokenRefDepth)
235
- if (brokenRefDepth <= 0) {
236
- brokenRefStart = -1
237
- brokenRefDepth = 0
238
- brokenRefStartTextOffset = 0
239
- }
99
+ const ensureInlineHasCodeInline = (facts, tokens) => {
100
+ if (facts.hasCodeInline !== undefined) return facts.hasCodeInline
101
+ let hasCodeInline = false
102
+ if (tokens && tokens.length > 0) {
103
+ for (let idx = 0; idx < tokens.length; idx++) {
104
+ if (tokens[idx] && tokens[idx].type === 'code_inline') {
105
+ hasCodeInline = true
106
+ break
240
107
  }
241
108
  }
109
+ }
110
+ facts.hasCodeInline = hasCodeInline
111
+ return hasCodeInline
112
+ }
242
113
 
243
- if (!hasEmphasis && isAsteriskEmphasisToken(child)) {
244
- hasEmphasis = true
245
- }
246
- if (!hasLinkClose && child.type === 'link_close') {
247
- hasLinkClose = true
248
- }
249
- if (brokenRefStart === -1 || child.type !== 'link_open') continue
250
- if (brokenRefDepth <= 0) {
251
- brokenRefStart = -1
252
- brokenRefDepth = 0
253
- brokenRefStartTextOffset = 0
254
- continue
255
- }
256
- if (linkCloseMap === null) {
257
- linkCloseMap = buildLinkCloseMap(children, 0, children.length - 1)
258
- }
259
- const closeIdx = linkCloseMap.get(j) ?? -1
260
- if (closeIdx === -1) continue
261
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'candidate')
262
- let segmentEnd = expandSegmentEndForWrapperBalance(children, brokenRefStart, closeIdx)
263
- if (segmentEnd === -1) {
264
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'wrapper-expand-fallback')
265
- segmentEnd = closeIdx
266
- }
267
- if (!hasTextMarkerCharsInRange(children, brokenRefStart, segmentEnd, brokenRefStartTextOffset)) {
268
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'skip-no-text-marker')
269
- brokenRefStart = -1
270
- brokenRefDepth = 0
271
- brokenRefStartTextOffset = 0
272
- continue
273
- }
274
- if (wrapperPrefixStats === null) {
275
- wrapperPrefixStats = buildAsteriskWrapperPrefixStats(children)
276
- }
277
- if (!shouldAttemptBrokenRefRewrite(
278
- children,
279
- brokenRefStart,
280
- segmentEnd,
281
- brokenRefStartTextOffset,
282
- wrapperPrefixStats
283
- )) {
284
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'skip-guard')
285
- brokenRefStart = -1
286
- brokenRefDepth = 0
287
- brokenRefStartTextOffset = 0
288
- continue
289
- }
290
- const fastPathResult = applyBrokenRefTokenOnlyFastPath(
291
- children,
292
- brokenRefStart,
293
- segmentEnd,
294
- linkCloseMap,
295
- metrics,
296
- bumpPostprocessMetric
297
- )
298
- if (fastPathResult === BROKEN_REF_FAST_PATH_RESULT_NO_ACTIVE_SIGNATURE) {
299
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'skip-no-active-signature')
300
- brokenRefStart = -1
301
- brokenRefDepth = 0
302
- brokenRefStartTextOffset = 0
303
- continue
304
- }
305
- if (fastPathResult === BROKEN_REF_FAST_PATH_RESULT_NO_MATCH) {
306
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'skip-no-fastpath-match')
307
- brokenRefStart = -1
308
- brokenRefDepth = 0
309
- brokenRefStartTextOffset = 0
310
- continue
311
- }
312
- bumpPostprocessMetric(metrics, 'brokenRefFlow', 'repaired')
313
- return {
314
- didRepair: true,
315
- hasBracketText,
316
- hasEmphasis,
317
- hasLinkClose
318
- }
114
+ const ensureInlineReferenceCount = (facts, state) => {
115
+ if (!facts || !facts.hasBracketText) return 0
116
+ if (facts.referenceCount === undefined) {
117
+ facts.referenceCount = getReferenceCount(state)
319
118
  }
119
+ return facts.referenceCount
120
+ }
320
121
 
321
- return {
322
- didRepair: false,
323
- hasBracketText,
324
- hasEmphasis,
325
- hasLinkClose
122
+ const ensureInlineMetrics = (facts, state) => {
123
+ if (!facts) return null
124
+ if (facts.metrics === undefined) {
125
+ facts.metrics = getPostprocessMetrics(state)
326
126
  }
127
+ return facts.metrics
327
128
  }
328
129
 
329
- const computeMaxBrokenRefRepairPass = (children, scanState) => {
330
- resetBrokenRefScanState(scanState)
331
- let maxRepairPass = 0
332
- for (let j = 0; j < children.length; j++) {
333
- const child = children[j]
334
- if (!child || child.type !== 'text' || !child.content) continue
335
- if (child.content.indexOf('[') === -1) continue
336
- if (scanBrokenRefState(child.content, scanState).brokenEnd) {
337
- maxRepairPass++
338
- }
130
+ const ensureInlineLinkCloseMap = (facts, tokens) => {
131
+ if (!tokens || tokens.length === 0) return new Map()
132
+ if (!facts) return buildLinkCloseMap(tokens, 0, tokens.length - 1)
133
+ if (facts.linkCloseMap === undefined) {
134
+ facts.linkCloseMap = buildLinkCloseMap(tokens, 0, tokens.length - 1)
339
135
  }
340
- return maxRepairPass
136
+ return facts.linkCloseMap
341
137
  }
342
138
 
343
- const runBrokenRefRepairs = (children, maxRepairPass, scanState, metrics = null) => {
344
- let repairPassCount = 0
345
- let changed = false
346
- let hasBracketText = false
347
- let hasEmphasis = false
348
- let hasLinkClose = false
349
- while (repairPassCount < maxRepairPass) {
350
- const pass = runBrokenRefRepairPass(children, scanState, metrics)
351
- hasBracketText = pass.hasBracketText
352
- hasEmphasis = pass.hasEmphasis
353
- hasLinkClose = pass.hasLinkClose
354
- if (!pass.didRepair) break
355
- changed = true
356
- repairPassCount++
139
+ const ensureInlineWrapperPrefixStats = (facts, tokens) => {
140
+ if (!tokens || tokens.length === 0) return null
141
+ if (!facts) return buildAsteriskWrapperPrefixStats(tokens)
142
+ if (facts.wrapperPrefixStats === undefined) {
143
+ facts.wrapperPrefixStats = buildAsteriskWrapperPrefixStats(tokens)
357
144
  }
358
- return {
359
- changed,
360
- hasBracketText,
361
- hasEmphasis,
362
- hasLinkClose
145
+ return facts.wrapperPrefixStats
146
+ }
147
+
148
+ const invalidateInlineDerivedCaches = (facts) => {
149
+ if (!facts) return
150
+ facts.linkCloseMap = undefined
151
+ facts.wrapperPrefixStats = undefined
152
+ }
153
+
154
+ const markInlineLevelRebuildFrom = (facts, startIdx) => {
155
+ if (!facts) return
156
+ const from = startIdx > 0 ? startIdx : 0
157
+ if (facts.rebuildLevelStart === undefined || from < facts.rebuildLevelStart) {
158
+ facts.rebuildLevelStart = from
159
+ }
160
+ }
161
+
162
+ const rebuildInlineLevelsForFacts = (tokens, facts) => {
163
+ if (!facts || facts.rebuildLevelStart === undefined) {
164
+ rebuildInlineLevels(tokens)
165
+ } else {
166
+ rebuildInlineLevelsFrom(tokens, facts.rebuildLevelStart)
167
+ }
168
+ if (facts) {
169
+ facts.rebuildLevelStart = undefined
170
+ }
171
+ }
172
+
173
+ const createInlineChangeMarker = (facts) => {
174
+ return (startIdx) => {
175
+ markInlineLevelRebuildFrom(facts, startIdx)
176
+ }
177
+ }
178
+
179
+ const finalizeInlineLinkRepairStage = (children, facts, markChangedFrom) => {
180
+ invalidateInlineDerivedCaches(facts)
181
+ if (!mergeBrokenMarksAroundLinks(children, markChangedFrom)) return false
182
+ invalidateInlineDerivedCaches(facts)
183
+ rebuildInlineLevelsForFacts(children, facts)
184
+ return true
185
+ }
186
+
187
+ const BROKEN_REF_REPAIR_HOOKS = {
188
+ ensureLinkCloseMap: ensureInlineLinkCloseMap,
189
+ ensureWrapperPrefixStats: ensureInlineWrapperPrefixStats,
190
+ invalidateDerivedCaches: invalidateInlineDerivedCaches,
191
+ markLevelRebuildFrom: markInlineLevelRebuildFrom
192
+ }
193
+
194
+ const bumpPostprocessMetric = (metrics, bucket, key) => {
195
+ if (!metrics || !bucket || !key) return
196
+ let table = metrics[bucket]
197
+ if (!table || typeof table !== 'object') {
198
+ table = Object.create(null)
199
+ metrics[bucket] = table
363
200
  }
201
+ table[key] = (table[key] || 0) + 1
364
202
  }
365
203
 
366
204
  const scanTailRepairCandidateAfterLinkClose = (tokens, linkCloseIdx) => {
@@ -388,7 +226,7 @@ const scanTailRepairCandidateAfterLinkClose = (tokens, linkCloseIdx) => {
388
226
  return { startIdx, strongCloseIdx: foundStrongClose }
389
227
  }
390
228
 
391
- const tryRepairTailCandidate = (tokens, candidate, isJapaneseMode, metrics = null) => {
229
+ const tryRepairTailCandidate = (tokens, candidate, isJapaneseMode, metrics = null, onChangeStart = null) => {
392
230
  if (!tokens || !candidate) return false
393
231
  const startIdx = candidate.startIdx
394
232
  const strongCloseIdx = candidate.strongCloseIdx
@@ -396,17 +234,19 @@ const tryRepairTailCandidate = (tokens, candidate, isJapaneseMode, metrics = nul
396
234
  if (isJapaneseMode && !hasJapaneseContextInRange(tokens, startIdx, endIdx)) return false
397
235
  if (!hasEmphasisSignalInRange(tokens, startIdx, endIdx)) return false
398
236
  if (tryFixTailPatternTokenOnly(tokens, startIdx, endIdx)) {
237
+ if (onChangeStart) onChangeStart(startIdx)
399
238
  bumpPostprocessMetric(metrics, 'tailFastPaths', 'tail-pattern')
400
239
  return true
401
240
  }
402
241
  if (tryFixTailDanglingStrongCloseTokenOnly(tokens, startIdx, strongCloseIdx)) {
242
+ if (onChangeStart) onChangeStart(startIdx)
403
243
  bumpPostprocessMetric(metrics, 'tailFastPaths', 'tail-dangling-strong-close')
404
244
  return true
405
245
  }
406
246
  return false
407
247
  }
408
248
 
409
- const fixTailAfterLinkStrongClose = (tokens, isJapaneseMode, metrics = null) => {
249
+ const fixTailAfterLinkStrongClose = (tokens, isJapaneseMode, metrics = null, onChangeStart = null) => {
410
250
  let strongDepth = 0
411
251
  for (let i = 0; i < tokens.length; i++) {
412
252
  const t = tokens[i]
@@ -419,7 +259,7 @@ const fixTailAfterLinkStrongClose = (tokens, isJapaneseMode, metrics = null) =>
419
259
  if (strongDepth !== 0) continue
420
260
  const candidate = scanTailRepairCandidateAfterLinkClose(tokens, i)
421
261
  if (!candidate) continue
422
- if (tryRepairTailCandidate(tokens, candidate, isJapaneseMode, metrics)) return true
262
+ if (tryRepairTailCandidate(tokens, candidate, isJapaneseMode, metrics, onChangeStart)) return true
423
263
  }
424
264
  return false
425
265
  }
@@ -488,16 +328,13 @@ const hasLeadingStandaloneStrongMarker = (content) => {
488
328
  return true
489
329
  }
490
330
 
491
- const tryPromoteStrongAroundInlineLink = (tokens, strictAsciiStrongGuard = false) => {
331
+ const tryPromoteStrongAroundInlineLink = (tokens, strictAsciiStrongGuard = false, facts = null) => {
492
332
  if (!tokens || tokens.length < 3) return false
493
333
  let changed = false
494
- let linkCloseMap = null
495
334
  for (let i = 1; i < tokens.length - 1; i++) {
496
335
  const linkOpen = tokens[i]
497
336
  if (!linkOpen || linkOpen.type !== 'link_open') continue
498
- if (linkCloseMap === null) {
499
- linkCloseMap = buildLinkCloseMap(tokens, 0, tokens.length - 1)
500
- }
337
+ const linkCloseMap = ensureInlineLinkCloseMap(facts, tokens)
501
338
  const closeIdx = linkCloseMap.get(i) ?? -1
502
339
  if (closeIdx === -1 || closeIdx + 1 >= tokens.length) continue
503
340
 
@@ -554,8 +391,8 @@ const tryPromoteStrongAroundInlineLink = (tokens, strictAsciiStrongGuard = false
554
391
 
555
392
  tokens.splice(i - 1, closeIdx - i + 3, ...replacement)
556
393
  changed = true
557
- // Token indices changed; invalidate map for the next candidate.
558
- linkCloseMap = null
394
+ invalidateInlineDerivedCaches(facts)
395
+ markInlineLevelRebuildFrom(facts, i - 1)
559
396
  i = i - 1 + replacement.length - 1
560
397
  }
561
398
  return changed
@@ -564,7 +401,8 @@ const tryPromoteStrongAroundInlineLink = (tokens, strictAsciiStrongGuard = false
564
401
  const tryPromoteStrongAroundInlineCode = (
565
402
  tokens,
566
403
  strictAsciiCodeGuard = false,
567
- strictAsciiStrongGuard = false
404
+ strictAsciiStrongGuard = false,
405
+ facts = null
568
406
  ) => {
569
407
  if (!tokens || tokens.length < 3) return false
570
408
  let changed = false
@@ -615,82 +453,200 @@ const tryPromoteStrongAroundInlineCode = (
615
453
 
616
454
  tokens.splice(i, 3, ...replacement)
617
455
  changed = true
456
+ invalidateInlineDerivedCaches(facts)
457
+ markInlineLevelRebuildFrom(facts, i)
618
458
  i += replacement.length - 1
619
459
  }
620
460
  return changed
621
461
  }
622
462
 
463
+ const tryActivateInlineEmphasis = (
464
+ children,
465
+ facts,
466
+ strictAsciiCodeGuard,
467
+ strictAsciiStrongGuard
468
+ ) => {
469
+ if (!facts || facts.hasEmphasis) return false
470
+ if (facts.hasLinkOpen &&
471
+ facts.hasLinkClose &&
472
+ tryPromoteStrongAroundInlineLink(children, strictAsciiStrongGuard, facts)) {
473
+ facts.hasEmphasis = true
474
+ return true
475
+ }
476
+ if (facts.hasBracketText || facts.hasLinkOpen || facts.hasLinkClose) return false
477
+ if (!ensureInlineHasCodeInline(facts, children)) return false
478
+ if (tryPromoteStrongAroundInlineCode(children, strictAsciiCodeGuard, strictAsciiStrongGuard, facts)) {
479
+ facts.hasEmphasis = true
480
+ return true
481
+ }
482
+ return false
483
+ }
484
+
485
+ const shouldRunInlineBrokenRefRepair = (facts, inlineContent, state) => {
486
+ if (!facts || !facts.hasLinkOpen || !facts.hasLinkClose || !facts.hasBracketText) return false
487
+ if (inlineContent.indexOf('***') !== -1) return false
488
+ return ensureInlineReferenceCount(facts, state) > 0
489
+ }
490
+
491
+ const applyBrokenRefRepairFacts = (facts, repairs) => {
492
+ if (!facts || !repairs) return
493
+ facts.hasBracketText = repairs.hasBracketText
494
+ facts.hasEmphasis = repairs.hasEmphasis
495
+ facts.hasLinkClose = repairs.hasLinkClose
496
+ }
497
+
498
+ const createBrokenRefScanState = () => {
499
+ return { depth: 0, brokenEnd: false, tailOpen: -1 }
500
+ }
501
+
502
+ const runInlineBrokenRefRepairStage = (children, facts, inlineContent, state) => {
503
+ if (!shouldRunInlineBrokenRefRepair(facts, inlineContent, state)) return false
504
+ const scanState = createBrokenRefScanState()
505
+ const maxRepairPass = computeMaxBrokenRefRepairPass(children, scanState)
506
+ if (maxRepairPass <= 0) return false
507
+ const repairs = runBrokenRefRepairs(
508
+ children,
509
+ maxRepairPass,
510
+ scanState,
511
+ ensureInlineMetrics(facts, state),
512
+ facts,
513
+ BROKEN_REF_REPAIR_HOOKS
514
+ )
515
+ applyBrokenRefRepairFacts(facts, repairs)
516
+ return repairs.changed
517
+ }
518
+
519
+ const runInlineEmphasisRepairStage = (children, facts, state, isJapaneseMode) => {
520
+ if (!facts.hasEmphasis) return false
521
+ let changed = false
522
+ const markChangedFrom = createInlineChangeMarker(facts)
523
+ if (fixEmOuterStrongSequence(children, markChangedFrom)) changed = true
524
+ if (facts.hasLinkClose) {
525
+ const metrics = ensureInlineMetrics(facts, state)
526
+ if (fixTailAfterLinkStrongClose(children, isJapaneseMode, metrics, markChangedFrom)) changed = true
527
+ if (fixLeadingAsteriskEm(children, markChangedFrom)) changed = true
528
+ }
529
+ if (fixTrailingStrong(children, markChangedFrom)) changed = true
530
+ if (sanitizeEmStrongBalance(children, markChangedFrom)) changed = true
531
+ return changed
532
+ }
533
+
534
+ const shouldRunInlineCollapsedRefRepair = (facts, state) => {
535
+ if (!facts || !facts.hasBracketText) return false
536
+ return ensureInlineReferenceCount(facts, state) > 0
537
+ }
538
+
539
+ const applyCollapsedRefRepairFacts = (facts) => {
540
+ if (!facts) return
541
+ facts.hasLinkOpen = true
542
+ facts.hasLinkClose = true
543
+ }
544
+
545
+ const rewriteInlineCollapsedReferences = (children, facts, state, markChangedFrom) => {
546
+ const changed = convertCollapsedReferenceLinks(
547
+ children,
548
+ state,
549
+ facts,
550
+ markChangedFrom
551
+ )
552
+ if (!changed) return false
553
+ applyCollapsedRefRepairFacts(facts)
554
+ return true
555
+ }
556
+
557
+ const runInlineCollapsedRefStage = (children, facts, state) => {
558
+ if (!shouldRunInlineCollapsedRefRepair(facts, state)) return false
559
+ const markChangedFrom = createInlineChangeMarker(facts)
560
+ if (!rewriteInlineCollapsedReferences(children, facts, state, markChangedFrom)) return false
561
+ finalizeInlineLinkRepairStage(children, facts, markChangedFrom)
562
+ return true
563
+ }
564
+
565
+ const shouldSkipInlinePostprocessToken = (children, facts, isJapaneseMode) => {
566
+ if (!facts.hasEmphasis &&
567
+ !facts.hasBracketText &&
568
+ !facts.hasLinkOpen &&
569
+ !facts.hasLinkClose &&
570
+ !ensureInlineHasCodeInline(facts, children)) {
571
+ return true
572
+ }
573
+ if (isJapaneseMode &&
574
+ !hasJapaneseContextInRange(children, 0, children.length - 1)) {
575
+ return true
576
+ }
577
+ return false
578
+ }
579
+
580
+ const runInlineCoreRepairStages = (
581
+ children,
582
+ facts,
583
+ inlineContent,
584
+ state,
585
+ isJapaneseMode,
586
+ strictAsciiCodeGuard,
587
+ strictAsciiStrongGuard
588
+ ) => {
589
+ let changed = false
590
+ if (!facts.hasEmphasis && tryActivateInlineEmphasis(
591
+ children,
592
+ facts,
593
+ strictAsciiCodeGuard,
594
+ strictAsciiStrongGuard
595
+ )) {
596
+ changed = true
597
+ } else if (!facts.hasEmphasis && !facts.hasBracketText) {
598
+ return false
599
+ }
600
+ if (runInlineBrokenRefRepairStage(children, facts, inlineContent, state)) changed = true
601
+ if (runInlineEmphasisRepairStage(children, facts, state, isJapaneseMode)) changed = true
602
+ return changed
603
+ }
604
+
623
605
  const processInlinePostprocessToken = (
624
606
  token,
625
607
  inlineContent,
626
608
  state,
627
609
  isJapaneseMode,
628
610
  strictAsciiCodeGuard,
629
- strictAsciiStrongGuard,
630
- referenceCount,
631
- metrics = null
611
+ strictAsciiStrongGuard
632
612
  ) => {
633
613
  if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) return
634
614
  const children = token.children
635
- const hasBracketTextInContent = inlineContent.indexOf('[') !== -1 || inlineContent.indexOf(']') !== -1
636
- const preScan = scanInlinePostprocessSignals(children, hasBracketTextInContent)
637
- let hasBracketText = preScan.hasBracketText
638
- let hasEmphasis = preScan.hasEmphasis
639
- const hasLinkOpen = preScan.hasLinkOpen
640
- let hasLinkClose = preScan.hasLinkClose
641
- const hasCodeInline = preScan.hasCodeInline
642
- if (!hasEmphasis && !hasBracketText && !hasLinkOpen && !hasLinkClose && !hasCodeInline) {
643
- return
644
- }
645
- if (isJapaneseMode &&
646
- !hasJapaneseContextInRange(children, 0, children.length - 1)) {
647
- return
648
- }
649
- let changed = false
650
- if (!hasEmphasis) {
651
- if (hasLinkOpen &&
652
- hasLinkClose &&
653
- tryPromoteStrongAroundInlineLink(children, strictAsciiStrongGuard)) {
654
- hasEmphasis = true
655
- changed = true
656
- } else if (!hasBracketText) {
657
- if (!hasLinkOpen &&
658
- !hasLinkClose &&
659
- tryPromoteStrongAroundInlineCode(children, strictAsciiCodeGuard, strictAsciiStrongGuard)) {
660
- hasEmphasis = true
661
- changed = true
662
- } else {
663
- return
664
- }
665
- }
666
- }
667
- let shouldTryBrokenRefRepair = hasLinkOpen && hasLinkClose && hasBracketText && referenceCount > 0
668
- if (shouldTryBrokenRefRepair && inlineContent.indexOf('***') !== -1) {
669
- shouldTryBrokenRefRepair = false
670
- }
671
- if (shouldTryBrokenRefRepair) {
672
- const scanState = { depth: 0, brokenEnd: false, tailOpen: -1 }
673
- const maxRepairPass = computeMaxBrokenRefRepairPass(children, scanState)
674
- if (maxRepairPass > 0) {
675
- const repairs = runBrokenRefRepairs(children, maxRepairPass, scanState, metrics)
676
- hasBracketText = repairs.hasBracketText
677
- hasEmphasis = repairs.hasEmphasis
678
- hasLinkClose = repairs.hasLinkClose
679
- if (repairs.changed) changed = true
680
- }
681
- }
682
- if (hasEmphasis) {
683
- if (fixEmOuterStrongSequence(children)) changed = true
684
- if (hasLinkClose && fixTailAfterLinkStrongClose(children, isJapaneseMode, metrics)) changed = true
685
- if (hasLinkClose && fixLeadingAsteriskEm(children)) changed = true
686
- if (fixTrailingStrong(children)) changed = true
687
- if (sanitizeEmStrongBalance(children)) changed = true
615
+ const facts = buildInlinePostprocessFacts(children, inlineContent)
616
+ if (shouldSkipInlinePostprocessToken(children, facts, isJapaneseMode)) return
617
+ const changed = runInlineCoreRepairStages(
618
+ children,
619
+ facts,
620
+ inlineContent,
621
+ state,
622
+ isJapaneseMode,
623
+ strictAsciiCodeGuard,
624
+ strictAsciiStrongGuard
625
+ )
626
+ if (changed) rebuildInlineLevelsForFacts(children, facts)
627
+ runInlineCollapsedRefStage(children, facts, state)
628
+ }
629
+
630
+ const processInlinePostprocessStateTokens = (
631
+ state,
632
+ isJapaneseMode,
633
+ strictAsciiCodeGuard,
634
+ strictAsciiStrongGuard
635
+ ) => {
636
+ for (let i = 0; i < state.tokens.length; i++) {
637
+ const token = state.tokens[i]
638
+ if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
639
+ const inlineContent = typeof token.content === 'string' ? token.content : ''
640
+ if (!hasMarkerChars(inlineContent)) continue
641
+ processInlinePostprocessToken(
642
+ token,
643
+ inlineContent,
644
+ state,
645
+ isJapaneseMode,
646
+ strictAsciiCodeGuard,
647
+ strictAsciiStrongGuard
648
+ )
688
649
  }
689
- if (changed) rebuildInlineLevels(children)
690
- if (!hasBracketText) return
691
- if (referenceCount > 0) convertCollapsedReferenceLinks(children, state)
692
- if (referenceCount === 0 && !hasLinkClose) return
693
- mergeBrokenMarksAroundLinks(children)
694
650
  }
695
651
 
696
652
  const registerTokenPostprocess = (md, baseOpt) => {
@@ -698,35 +654,18 @@ const registerTokenPostprocess = (md, baseOpt) => {
698
654
  md.__strongJaTokenPostprocessRegistered = true
699
655
  md.core.ruler.after('inline', 'strong_ja_token_postprocess', (state) => {
700
656
  if (!state || !state.tokens) return
701
- const opt = getRuntimeOpt(state, baseOpt)
702
- const modeFlags = opt.__strongJaModeFlags
703
- const isJapaneseMode = (modeFlags & MODE_FLAG_JAPANESE_ANY) !== 0
704
- const strictAsciiCodeGuard = (modeFlags & MODE_FLAG_JAPANESE_PLUS) !== 0
705
- const strictAsciiStrongGuard = (modeFlags & MODE_FLAG_AGGRESSIVE) === 0
706
- if (modeFlags & MODE_FLAG_COMPATIBLE) return
707
- if (opt.postprocess === false) return
708
- const references = state.env && state.env.references ? state.env.references : null
709
- if (state.__strongJaReferenceCount === undefined) {
710
- state.__strongJaReferenceCount = references ? Object.keys(references).length : 0
711
- }
712
- const referenceCount = state.__strongJaReferenceCount
713
- const metrics = getPostprocessMetrics(state)
714
- for (let i = 0; i < state.tokens.length; i++) {
715
- const token = state.tokens[i]
716
- if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
717
- const inlineContent = typeof token.content === 'string' ? token.content : ''
718
- if (!hasMarkerChars(inlineContent)) continue
719
- processInlinePostprocessToken(
720
- token,
721
- inlineContent,
722
- state,
723
- isJapaneseMode,
724
- strictAsciiCodeGuard,
725
- strictAsciiStrongGuard,
726
- referenceCount,
727
- metrics
728
- )
729
- }
657
+ const overrideOpt = state.env && state.env.__strongJaTokenOpt
658
+ const opt = overrideOpt ? getRuntimeOpt(state, baseOpt) : baseOpt
659
+ if (!opt.__strongJaPostprocessActive) return
660
+ const isJapaneseMode = opt.__strongJaIsJapaneseMode
661
+ const strictAsciiCodeGuard = opt.__strongJaStrictAsciiCodeGuard
662
+ const strictAsciiStrongGuard = opt.__strongJaStrictAsciiStrongGuard
663
+ processInlinePostprocessStateTokens(
664
+ state,
665
+ isJapaneseMode,
666
+ strictAsciiCodeGuard,
667
+ strictAsciiStrongGuard
668
+ )
730
669
  })
731
670
  }
732
671