@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
|
@@ -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
|
-
|
|
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
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
202
|
-
|
|
203
|
-
let
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
|
136
|
+
return facts.linkCloseMap
|
|
341
137
|
}
|
|
342
138
|
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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
|
-
|
|
558
|
-
|
|
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
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
const
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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
|
|