@peaceroad/markdown-it-strong-ja 0.9.0 → 0.9.2

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,6 +1,6 @@
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
+ import { runBrokenRefRepairs } from './broken-ref.js'
4
4
  import {
5
5
  rebuildInlineLevels,
6
6
  rebuildInlineLevelsFrom,
@@ -11,68 +11,35 @@ import {
11
11
  import {
12
12
  getRuntimeOpt,
13
13
  hasRuntimeOverride,
14
- getReferenceCount
14
+ getReferenceCount,
15
+ isAsciiWordCode,
16
+ isSoftSpaceCode,
17
+ cloneMap
15
18
  } from '../token-utils.js'
16
19
  import {
17
20
  hasMarkerChars,
18
21
  hasJapaneseContextInRange,
19
22
  hasEmphasisSignalInRange,
20
23
  buildAsteriskWrapperPrefixStats,
21
- scanInlinePostprocessSignals
24
+ scanInlinePostprocessSignals,
25
+ INLINE_REPAIR_EM_OUTER_STRONG_SEQUENCE,
26
+ INLINE_REPAIR_TAIL_AFTER_LINK,
27
+ INLINE_REPAIR_LEADING_ASTERISK_EM,
28
+ INLINE_REPAIR_TRAILING_STRONG,
29
+ INLINE_REPAIR_BALANCE_SANITIZE
22
30
  } from './guards.js'
23
31
  import {
24
32
  tryFixTailPatternTokenOnly,
25
33
  tryFixTailDanglingStrongCloseTokenOnly
26
34
  } from './fastpaths.js'
35
+ import { sanitizeEmStrongBalance } from './emphasis-balance.js'
27
36
 
28
- const fallbackMarkupByType = (type) => {
29
- if (type === 'strong_open' || type === 'strong_close') return '**'
30
- if (type === 'em_open' || type === 'em_close') return '*'
31
- return ''
32
- }
33
-
34
- const makeTokenLiteralText = (token) => {
35
- if (!token) return
36
- const literal = token.markup || fallbackMarkupByType(token.type)
37
- token.type = 'text'
38
- token.tag = ''
39
- token.nesting = 0
40
- token.content = literal
41
- token.markup = ''
42
- token.info = ''
43
- }
44
-
45
- const sanitizeEmStrongBalance = (tokens, onChangeStart = null) => {
46
- if (!tokens || tokens.length === 0) return false
47
- const stack = []
48
- let changed = false
49
- for (let i = 0; i < tokens.length; i++) {
50
- const token = tokens[i]
51
- if (!token || !token.type) continue
52
- if (token.type === 'strong_open' || token.type === 'em_open') {
53
- stack.push({ type: token.type, idx: i })
54
- continue
55
- }
56
- if (token.type !== 'strong_close' && token.type !== 'em_close') continue
57
- const expected = token.type === 'strong_close' ? 'strong_open' : 'em_open'
58
- if (stack.length > 0 && stack[stack.length - 1].type === expected) {
59
- stack.pop()
60
- continue
61
- }
62
- if (onChangeStart) onChangeStart(i)
63
- makeTokenLiteralText(token)
64
- changed = true
65
- }
66
- for (let i = stack.length - 1; i >= 0; i--) {
67
- const entry = stack[i]
68
- const token = tokens[entry.idx]
69
- if (!token) continue
70
- if (onChangeStart) onChangeStart(entry.idx)
71
- makeTokenLiteralText(token)
72
- changed = true
73
- }
74
- return changed
75
- }
37
+ const INLINE_REPAIR_ALL_EMPHASIS_FIXERS =
38
+ INLINE_REPAIR_EM_OUTER_STRONG_SEQUENCE |
39
+ INLINE_REPAIR_TAIL_AFTER_LINK |
40
+ INLINE_REPAIR_LEADING_ASTERISK_EM |
41
+ INLINE_REPAIR_TRAILING_STRONG |
42
+ INLINE_REPAIR_BALANCE_SANITIZE
76
43
 
77
44
  const getPostprocessMetrics = (state) => {
78
45
  if (!state || !state.env) return null
@@ -81,14 +48,17 @@ const getPostprocessMetrics = (state) => {
81
48
  return metrics
82
49
  }
83
50
 
84
- const buildInlinePostprocessFacts = (children, inlineContent) => {
85
- const preScan = scanInlinePostprocessSignals(children)
51
+ const buildInlinePostprocessFacts = (children, inlineContent, collectJapaneseContext) => {
52
+ const preScan = scanInlinePostprocessSignals(children, collectJapaneseContext)
86
53
  return {
87
54
  hasBracketText: inlineContent.indexOf('[') !== -1 || inlineContent.indexOf(']') !== -1,
88
55
  hasEmphasis: preScan.hasEmphasis,
56
+ hasAsteriskWrapperImbalance: preScan.hasAsteriskWrapperImbalance,
89
57
  hasLinkOpen: preScan.hasLinkOpen,
90
58
  hasLinkClose: preScan.hasLinkClose,
91
59
  hasCodeInline: preScan.hasCodeInline,
60
+ hasJapaneseContext: preScan.hasJapaneseContext,
61
+ repairMask: preScan.repairMask,
92
62
  linkCloseMap: undefined,
93
63
  wrapperPrefixStats: undefined,
94
64
  rebuildLevelStart: undefined
@@ -159,14 +129,14 @@ const BROKEN_REF_REPAIR_HOOKS = {
159
129
  markLevelRebuildFrom: markInlineLevelRebuildFrom
160
130
  }
161
131
 
162
- const bumpPostprocessMetric = (metrics, bucket, key) => {
163
- if (!metrics || !bucket || !key) return
132
+ const bumpPostprocessMetric = (metrics, bucket, key, delta = 1) => {
133
+ if (!metrics || !bucket || !key || delta <= 0) return
164
134
  let table = metrics[bucket]
165
135
  if (!table || typeof table !== 'object') {
166
136
  table = Object.create(null)
167
137
  metrics[bucket] = table
168
138
  }
169
- table[key] = (table[key] || 0) + 1
139
+ table[key] = (table[key] || 0) + delta
170
140
  }
171
141
 
172
142
  const scanTailRepairCandidateAfterLinkClose = (tokens, linkCloseIdx) => {
@@ -232,11 +202,6 @@ const fixTailAfterLinkStrongClose = (tokens, isJapaneseMode, metrics = null, onC
232
202
  return false
233
203
  }
234
204
 
235
- const cloneMap = (map) => {
236
- if (!map || !Array.isArray(map)) return null
237
- return [map[0], map[1]]
238
- }
239
-
240
205
  const cloneTextToken = (source, content) => {
241
206
  const token = new Token('text', '', 0)
242
207
  Object.assign(token, source)
@@ -245,19 +210,9 @@ const cloneTextToken = (source, content) => {
245
210
  return token
246
211
  }
247
212
 
248
- const isSoftSpaceCode = (code) => {
249
- return code === 0x20 || code === 0x09 || code === 0x3000
250
- }
251
-
252
213
  const CHAR_ASTERISK = 0x2A // *
253
214
  const CHAR_BACKSLASH = 0x5C // \
254
215
 
255
- const isAsciiWordCode = (code) => {
256
- return (code >= 0x30 && code <= 0x39) ||
257
- (code >= 0x41 && code <= 0x5A) ||
258
- (code >= 0x61 && code <= 0x7A)
259
- }
260
-
261
216
  const textEndsAsciiWord = (text) => {
262
217
  if (!text || text.length === 0) return false
263
218
  return isAsciiWordCode(text.charCodeAt(text.length - 1))
@@ -459,46 +414,78 @@ const shouldRunInlineBrokenRefRepair = (facts, inlineContent, state) => {
459
414
  return getReferenceCount(state) > 0
460
415
  }
461
416
 
462
- const applyBrokenRefRepairFacts = (facts, repairs) => {
463
- if (!facts || !repairs) return
464
- facts.hasBracketText = repairs.hasBracketText
465
- facts.hasEmphasis = repairs.hasEmphasis
466
- facts.hasLinkClose = repairs.hasLinkClose
467
- }
468
-
469
- const createBrokenRefScanState = () => {
470
- return { depth: 0, brokenEnd: false, tailOpen: -1 }
471
- }
472
-
473
417
  const runInlineBrokenRefRepairStage = (children, facts, inlineContent, state) => {
474
418
  if (!shouldRunInlineBrokenRefRepair(facts, inlineContent, state)) return false
475
- const scanState = createBrokenRefScanState()
476
- const maxRepairPass = computeMaxBrokenRefRepairPass(children, scanState)
477
- if (maxRepairPass <= 0) return false
419
+ const scanState = { depth: 0, brokenEnd: false, tailOpen: -1 }
478
420
  const repairs = runBrokenRefRepairs(
479
421
  children,
480
- maxRepairPass,
481
422
  scanState,
482
423
  getPostprocessMetrics(state),
483
424
  facts,
484
425
  BROKEN_REF_REPAIR_HOOKS
485
426
  )
486
- applyBrokenRefRepairFacts(facts, repairs)
427
+ facts.hasBracketText = repairs.hasBracketText
428
+ facts.hasEmphasis = repairs.hasEmphasis
429
+ facts.hasLinkClose = repairs.hasLinkClose
487
430
  return repairs.changed
488
431
  }
489
432
 
490
- const runInlineEmphasisRepairStage = (children, facts, state, isJapaneseMode) => {
433
+ const runInlineEmphasisRepairStage = (
434
+ children,
435
+ facts,
436
+ state,
437
+ isJapaneseMode,
438
+ forceBalanceSanitize = false
439
+ ) => {
491
440
  if (!facts.hasEmphasis) return false
492
441
  let changed = false
493
442
  const markChangedFrom = createInlineChangeMarker(facts)
494
- if (fixEmOuterStrongSequence(children, markChangedFrom)) changed = true
443
+ const metrics = getPostprocessMetrics(state)
444
+ const repairMask = forceBalanceSanitize
445
+ ? INLINE_REPAIR_ALL_EMPHASIS_FIXERS
446
+ : (facts.repairMask || 0)
447
+ if ((repairMask & INLINE_REPAIR_EM_OUTER_STRONG_SEQUENCE) &&
448
+ fixEmOuterStrongSequence(children, markChangedFrom)) {
449
+ changed = true
450
+ bumpPostprocessMetric(metrics, 'emphasisFixers', 'em-outer-strong-sequence')
451
+ }
495
452
  if (facts.hasLinkClose) {
496
- const metrics = getPostprocessMetrics(state)
497
- if (fixTailAfterLinkStrongClose(children, isJapaneseMode, metrics, markChangedFrom)) changed = true
498
- if (fixLeadingAsteriskEm(children, markChangedFrom)) changed = true
453
+ if ((repairMask & INLINE_REPAIR_TAIL_AFTER_LINK) &&
454
+ fixTailAfterLinkStrongClose(children, isJapaneseMode, metrics, markChangedFrom)) {
455
+ changed = true
456
+ }
457
+ if ((repairMask & INLINE_REPAIR_LEADING_ASTERISK_EM) &&
458
+ fixLeadingAsteriskEm(children, markChangedFrom)) {
459
+ changed = true
460
+ bumpPostprocessMetric(metrics, 'emphasisFixers', 'leading-asterisk-em')
461
+ }
462
+ }
463
+ if ((repairMask & INLINE_REPAIR_TRAILING_STRONG) &&
464
+ fixTrailingStrong(children, markChangedFrom)) {
465
+ changed = true
466
+ bumpPostprocessMetric(metrics, 'emphasisFixers', 'trailing-strong')
467
+ }
468
+ const shouldAttemptSanitize = forceBalanceSanitize ||
469
+ changed ||
470
+ facts.hasAsteriskWrapperImbalance ||
471
+ (repairMask & INLINE_REPAIR_BALANCE_SANITIZE)
472
+ if (!shouldAttemptSanitize) {
473
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'skipped-balanced')
474
+ return changed
475
+ }
476
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'attempted')
477
+ if (forceBalanceSanitize || changed) {
478
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'attempted-after-change')
479
+ } else {
480
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'attempted-pre-scan-risk')
481
+ }
482
+ if (sanitizeEmStrongBalance(children, markChangedFrom)) {
483
+ changed = true
484
+ bumpPostprocessMetric(metrics, 'emphasisFixers', 'sanitize-em-strong-balance')
485
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'repaired')
486
+ } else {
487
+ bumpPostprocessMetric(metrics, 'emphasisSanitize', 'no-change')
499
488
  }
500
- if (fixTrailingStrong(children, markChangedFrom)) changed = true
501
- if (sanitizeEmStrongBalance(children, markChangedFrom)) changed = true
502
489
  return changed
503
490
  }
504
491
 
@@ -507,33 +494,17 @@ const shouldRunInlineCollapsedRefRepair = (facts, state) => {
507
494
  return getReferenceCount(state) > 0
508
495
  }
509
496
 
510
- const applyCollapsedRefRepairFacts = (facts) => {
511
- if (!facts) return
512
- facts.hasLinkOpen = true
513
- facts.hasLinkClose = true
514
- }
515
-
516
- const rewriteInlineCollapsedReferences = (children, facts, state, markChangedFrom) => {
517
- const changed = convertCollapsedReferenceLinks(
518
- children,
519
- state,
520
- facts,
521
- markChangedFrom
522
- )
523
- if (!changed) return false
524
- applyCollapsedRefRepairFacts(facts)
525
- return true
526
- }
527
-
528
497
  const runInlineCollapsedRefStage = (children, facts, state) => {
529
498
  if (!shouldRunInlineCollapsedRefRepair(facts, state)) return false
530
499
  const markChangedFrom = createInlineChangeMarker(facts)
531
- if (!rewriteInlineCollapsedReferences(children, facts, state, markChangedFrom)) return false
500
+ if (!convertCollapsedReferenceLinks(children, state, facts, markChangedFrom)) return false
501
+ facts.hasLinkOpen = true
502
+ facts.hasLinkClose = true
532
503
  finalizeInlineLinkRepairStage(children, facts, markChangedFrom)
533
504
  return true
534
505
  }
535
506
 
536
- const shouldSkipInlinePostprocessToken = (children, facts, isJapaneseMode) => {
507
+ const shouldSkipInlinePostprocessToken = (facts, isJapaneseMode) => {
537
508
  if (!facts.hasEmphasis &&
538
509
  !facts.hasBracketText &&
539
510
  !facts.hasLinkOpen &&
@@ -541,8 +512,7 @@ const shouldSkipInlinePostprocessToken = (children, facts, isJapaneseMode) => {
541
512
  !facts.hasCodeInline) {
542
513
  return true
543
514
  }
544
- if (isJapaneseMode &&
545
- !hasJapaneseContextInRange(children, 0, children.length - 1)) {
515
+ if (isJapaneseMode && !facts.hasJapaneseContext) {
546
516
  return true
547
517
  }
548
518
  return false
@@ -569,7 +539,7 @@ const runInlineCoreRepairStages = (
569
539
  return false
570
540
  }
571
541
  if (runInlineBrokenRefRepairStage(children, facts, inlineContent, state)) changed = true
572
- if (runInlineEmphasisRepairStage(children, facts, state, isJapaneseMode)) changed = true
542
+ if (runInlineEmphasisRepairStage(children, facts, state, isJapaneseMode, changed)) changed = true
573
543
  return changed
574
544
  }
575
545
 
@@ -583,8 +553,8 @@ const processInlinePostprocessToken = (
583
553
  ) => {
584
554
  if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) return
585
555
  const children = token.children
586
- const facts = buildInlinePostprocessFacts(children, inlineContent)
587
- if (shouldSkipInlinePostprocessToken(children, facts, isJapaneseMode)) return
556
+ const facts = buildInlinePostprocessFacts(children, inlineContent, isJapaneseMode)
557
+ if (shouldSkipInlinePostprocessToken(facts, isJapaneseMode)) return
588
558
  const changed = runInlineCoreRepairStages(
589
559
  children,
590
560
  facts,