@peaceroad/markdown-it-strong-ja 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/index.js +31 -22
- package/package.json +8 -6
- package/src/token-compat.js +13 -18
- package/src/token-core.js +189 -76
- package/src/token-link-utils.js +386 -193
- package/src/token-postprocess/broken-ref.js +482 -0
- package/src/token-postprocess/guards.js +217 -198
- package/src/token-postprocess/orchestrator.js +295 -385
- package/src/token-utils.js +73 -27
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { isJapaneseChar
|
|
1
|
+
import { isJapaneseChar } from '../token-utils.js'
|
|
2
|
+
|
|
3
|
+
const CHAR_ASTERISK = 0x2A // *
|
|
2
4
|
|
|
3
5
|
const hasMarkerChars = (text) => {
|
|
4
6
|
return !!text && text.indexOf('*') !== -1
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
const contentHasMarkerCharsFrom = (content, from) => {
|
|
8
|
-
if (!content) return false
|
|
9
|
-
const start = from > 0 ? from : 0
|
|
10
|
-
if (start === 0) return hasMarkerChars(content)
|
|
11
|
-
if (start >= content.length) return false
|
|
12
|
-
return content.indexOf('*', start) !== -1
|
|
13
|
-
}
|
|
14
|
-
|
|
15
9
|
const isAsteriskEmphasisToken = (token) => {
|
|
16
10
|
if (!token || !token.type) return false
|
|
17
11
|
if (token.type !== 'strong_open' &&
|
|
@@ -78,20 +72,6 @@ const hasEmphasisSignalInRange = (tokens, startIdx, endIdx) => {
|
|
|
78
72
|
return false
|
|
79
73
|
}
|
|
80
74
|
|
|
81
|
-
const hasTextMarkerCharsInRange = (tokens, startIdx, endIdx, firstTextOffset = 0) => {
|
|
82
|
-
if (!tokens || startIdx < 0 || endIdx < startIdx) return false
|
|
83
|
-
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
84
|
-
const token = tokens[i]
|
|
85
|
-
if (!token || token.type !== 'text' || !token.content) continue
|
|
86
|
-
if (i === startIdx && firstTextOffset > 0) {
|
|
87
|
-
if (contentHasMarkerCharsFrom(token.content, firstTextOffset)) return true
|
|
88
|
-
continue
|
|
89
|
-
}
|
|
90
|
-
if (textTokenHasMarkerChars(token)) return true
|
|
91
|
-
}
|
|
92
|
-
return false
|
|
93
|
-
}
|
|
94
|
-
|
|
95
75
|
const isStrongRunSoftSpace = (code) => {
|
|
96
76
|
return code === 0x20 || code === 0x09 || code === 0x0A || code === 0x3000
|
|
97
77
|
}
|
|
@@ -107,21 +87,24 @@ const isStrongRunTextLike = (code) => {
|
|
|
107
87
|
return isStrongRunAsciiWord(code) || isJapaneseChar(code)
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
const countDelimiterLikeStrongRuns = (content,
|
|
90
|
+
const countDelimiterLikeStrongRuns = (content, from = 0, limit = 0) => {
|
|
111
91
|
let at = from > 0 ? from : 0
|
|
112
92
|
const len = content.length
|
|
113
|
-
const markerCode = marker.charCodeAt(0)
|
|
114
93
|
let count = 0
|
|
115
|
-
while (at < len) {
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
while (at + 1 < len) {
|
|
95
|
+
if (content.charCodeAt(at) !== CHAR_ASTERISK ||
|
|
96
|
+
content.charCodeAt(at + 1) !== CHAR_ASTERISK) {
|
|
97
|
+
at++
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
const pos = at
|
|
118
101
|
const prevCode = pos > 0 ? content.charCodeAt(pos - 1) : 0
|
|
119
|
-
const nextPos = pos +
|
|
102
|
+
const nextPos = pos + 2
|
|
120
103
|
const nextCode = nextPos < len ? content.charCodeAt(nextPos) : 0
|
|
121
|
-
const prevSameMarker = prevCode ===
|
|
122
|
-
const nextSameMarker = nextCode ===
|
|
104
|
+
const prevSameMarker = prevCode === CHAR_ASTERISK
|
|
105
|
+
const nextSameMarker = nextCode === CHAR_ASTERISK
|
|
123
106
|
if (prevSameMarker || nextSameMarker) {
|
|
124
|
-
at = pos +
|
|
107
|
+
at = pos + 2
|
|
125
108
|
continue
|
|
126
109
|
}
|
|
127
110
|
const prevSoft = prevCode !== 0 && isStrongRunSoftSpace(prevCode)
|
|
@@ -131,97 +114,55 @@ const countDelimiterLikeStrongRuns = (content, marker, from = 0, limit = 0) => {
|
|
|
131
114
|
const nextTextLike = isStrongRunTextLike(nextCode)
|
|
132
115
|
const hasTextNeighbor = prevTextLike || nextTextLike
|
|
133
116
|
if (!hasTextNeighbor) {
|
|
134
|
-
at = pos +
|
|
117
|
+
at = pos + 2
|
|
135
118
|
continue
|
|
136
119
|
}
|
|
137
120
|
const atBoundary = prevCode === 0 || nextCode === 0
|
|
138
121
|
if (!atBoundary && (!prevTextLike || !nextTextLike)) {
|
|
139
|
-
at = pos +
|
|
122
|
+
at = pos + 2
|
|
140
123
|
continue
|
|
141
124
|
}
|
|
142
125
|
if (hasPrevOrNext && !prevSoft && !nextSoft) {
|
|
143
126
|
count++
|
|
144
127
|
if (limit > 0 && count >= limit) return count
|
|
145
128
|
}
|
|
146
|
-
at = pos +
|
|
129
|
+
at = pos + 2
|
|
147
130
|
}
|
|
148
131
|
return count
|
|
149
132
|
}
|
|
150
133
|
|
|
151
|
-
const countStrongMarkerRunsInTextRange = (tokens, startIdx, endIdx, firstTextOffset = 0, limit = 0) => {
|
|
152
|
-
if (!tokens || startIdx < 0 || endIdx < startIdx) return 0
|
|
153
|
-
let total = 0
|
|
154
|
-
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
155
|
-
const token = tokens[i]
|
|
156
|
-
if (!token || token.type !== 'text' || !token.content) continue
|
|
157
|
-
const content = token.content
|
|
158
|
-
const scanFrom = i === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
159
|
-
if (scanFrom >= content.length) continue
|
|
160
|
-
const remain = limit > 0 ? (limit - total) : 0
|
|
161
|
-
total += countDelimiterLikeStrongRuns(content, '**', scanFrom, remain)
|
|
162
|
-
if (limit > 0 && total >= limit) {
|
|
163
|
-
return total
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return total
|
|
167
|
-
}
|
|
168
|
-
|
|
169
134
|
const buildAsteriskWrapperPrefixStats = (tokens) => {
|
|
170
135
|
const len = Array.isArray(tokens) ? tokens.length : 0
|
|
171
136
|
const strongDepthPrefix = new Array(len + 1)
|
|
172
137
|
const emDepthPrefix = new Array(len + 1)
|
|
173
|
-
const strongOpenPrefix = new Array(len + 1)
|
|
174
|
-
const strongClosePrefix = new Array(len + 1)
|
|
175
|
-
const emOpenPrefix = new Array(len + 1)
|
|
176
|
-
const emClosePrefix = new Array(len + 1)
|
|
177
138
|
let strongDepth = 0
|
|
178
139
|
let emDepthCount = 0
|
|
179
|
-
let strongOpenCount = 0
|
|
180
|
-
let strongCloseCount = 0
|
|
181
|
-
let emOpenCount = 0
|
|
182
|
-
let emCloseCount = 0
|
|
183
140
|
strongDepthPrefix[0] = 0
|
|
184
141
|
emDepthPrefix[0] = 0
|
|
185
|
-
strongOpenPrefix[0] = 0
|
|
186
|
-
strongClosePrefix[0] = 0
|
|
187
|
-
emOpenPrefix[0] = 0
|
|
188
|
-
emClosePrefix[0] = 0
|
|
189
142
|
for (let i = 0; i < len; i++) {
|
|
190
143
|
const token = tokens[i]
|
|
191
144
|
if (token && token.type && isAsteriskEmphasisToken(token)) {
|
|
192
145
|
if (token.type === 'strong_open') {
|
|
193
146
|
strongDepth++
|
|
194
|
-
strongOpenCount++
|
|
195
147
|
} else if (token.type === 'strong_close') {
|
|
196
148
|
if (strongDepth > 0) strongDepth--
|
|
197
|
-
strongCloseCount++
|
|
198
149
|
} else if (token.type === 'em_open') {
|
|
199
150
|
emDepthCount++
|
|
200
|
-
emOpenCount++
|
|
201
151
|
} else if (token.type === 'em_close') {
|
|
202
152
|
if (emDepthCount > 0) emDepthCount--
|
|
203
|
-
emCloseCount++
|
|
204
153
|
}
|
|
205
154
|
}
|
|
206
155
|
strongDepthPrefix[i + 1] = strongDepth
|
|
207
156
|
emDepthPrefix[i + 1] = emDepthCount
|
|
208
|
-
strongOpenPrefix[i + 1] = strongOpenCount
|
|
209
|
-
strongClosePrefix[i + 1] = strongCloseCount
|
|
210
|
-
emOpenPrefix[i + 1] = emOpenCount
|
|
211
|
-
emClosePrefix[i + 1] = emCloseCount
|
|
212
157
|
}
|
|
213
158
|
return {
|
|
214
159
|
strongDepth: strongDepthPrefix,
|
|
215
|
-
emDepth: emDepthPrefix
|
|
216
|
-
strongOpen: strongOpenPrefix,
|
|
217
|
-
strongClose: strongClosePrefix,
|
|
218
|
-
emOpen: emOpenPrefix,
|
|
219
|
-
emClose: emClosePrefix
|
|
160
|
+
emDepth: emDepthPrefix
|
|
220
161
|
}
|
|
221
162
|
}
|
|
222
163
|
|
|
223
|
-
const
|
|
224
|
-
|
|
164
|
+
const createBrokenRefWrapperRangeSignals = () => {
|
|
165
|
+
return {
|
|
225
166
|
hasLeadingUnmatchedClose: false,
|
|
226
167
|
hasImbalance: false,
|
|
227
168
|
hasAsteriskEmphasisToken: false,
|
|
@@ -229,123 +170,148 @@ const buildBrokenRefWrapperRangeSignals = (tokens, startIdx, endIdx, firstTextOf
|
|
|
229
170
|
hasUnderscoreText: false,
|
|
230
171
|
hasCodeInline: false,
|
|
231
172
|
hasUnderscoreEmphasisToken: false,
|
|
173
|
+
hasTextMarker: false,
|
|
174
|
+
strongRunCount: 0,
|
|
232
175
|
strongOpenInRange: 0,
|
|
233
176
|
strongCloseInRange: 0,
|
|
234
177
|
emOpenInRange: 0,
|
|
235
178
|
emCloseInRange: 0
|
|
236
179
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const updateBrokenRefTextRangeSignals = (signals, token, tokenIdx, startIdx, firstTextOffset) => {
|
|
183
|
+
if (!token || token.type !== 'text' || !token.content) return
|
|
184
|
+
const content = token.content
|
|
185
|
+
const scanFrom = tokenIdx === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
186
|
+
// Keep this at 0 (instead of firstTextOffset) so historical fail-safe
|
|
187
|
+
// behavior around noisy leading chains in the first text token stays unchanged.
|
|
188
|
+
if (!signals.hasLongStarNoise && content.indexOf('***') !== -1) {
|
|
189
|
+
signals.hasLongStarNoise = true
|
|
190
|
+
}
|
|
191
|
+
if (!signals.hasUnderscoreText) {
|
|
192
|
+
if (scanFrom < content.length && content.indexOf('_', scanFrom) !== -1) {
|
|
193
|
+
signals.hasUnderscoreText = true
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (!signals.hasTextMarker) {
|
|
197
|
+
signals.hasTextMarker = scanFrom === 0
|
|
198
|
+
? textTokenHasMarkerChars(token)
|
|
199
|
+
: content.indexOf('*', scanFrom) !== -1
|
|
200
|
+
}
|
|
201
|
+
if (signals.strongRunCount < 2 && scanFrom < content.length) {
|
|
202
|
+
signals.strongRunCount += countDelimiterLikeStrongRuns(content, scanFrom, 2 - signals.strongRunCount)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const updateBrokenRefWrapperTokenSignals = (signals, token, isAsteriskEmphasis) => {
|
|
207
|
+
if (!signals.hasCodeInline && token.type === 'code_inline') {
|
|
208
|
+
signals.hasCodeInline = true
|
|
209
|
+
}
|
|
210
|
+
if (isAsteriskEmphasis) {
|
|
211
|
+
signals.hasAsteriskEmphasisToken = true
|
|
212
|
+
}
|
|
213
|
+
if (!signals.hasUnderscoreEmphasisToken &&
|
|
214
|
+
(token.type === 'strong_open' ||
|
|
215
|
+
token.type === 'strong_close' ||
|
|
216
|
+
token.type === 'em_open' ||
|
|
217
|
+
token.type === 'em_close') &&
|
|
218
|
+
(token.markup === '_' || token.markup === '__')) {
|
|
219
|
+
signals.hasUnderscoreEmphasisToken = true
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const updateBrokenRefWrapperRangeDepthSignals = (signals, token, wrapperState, isAsteriskEmphasis) => {
|
|
224
|
+
if (!isAsteriskEmphasis) return
|
|
225
|
+
let depthKey = ''
|
|
226
|
+
if (token.type === 'strong_open' || token.type === 'strong_close') {
|
|
227
|
+
depthKey = 'strongDepth'
|
|
228
|
+
} else if (token.type === 'em_open' || token.type === 'em_close') {
|
|
229
|
+
depthKey = 'emDepth'
|
|
230
|
+
} else {
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
const isOpen = token.type.endsWith('_open')
|
|
234
|
+
if (!wrapperState.sawWrapper) {
|
|
235
|
+
wrapperState.sawWrapper = true
|
|
236
|
+
if (!isOpen) signals.hasLeadingUnmatchedClose = true
|
|
237
|
+
}
|
|
238
|
+
if (isOpen) {
|
|
239
|
+
wrapperState.sawOpen = true
|
|
240
|
+
signals.hasLeadingUnmatchedClose = false
|
|
241
|
+
wrapperState[depthKey]++
|
|
242
|
+
} else if (wrapperState[depthKey] <= 0) {
|
|
243
|
+
signals.hasImbalance = true
|
|
244
|
+
} else {
|
|
245
|
+
wrapperState[depthKey]--
|
|
246
|
+
}
|
|
247
|
+
if (token.type === 'strong_open') signals.strongOpenInRange++
|
|
248
|
+
else if (token.type === 'strong_close') signals.strongCloseInRange++
|
|
249
|
+
else if (token.type === 'em_open') signals.emOpenInRange++
|
|
250
|
+
else if (token.type === 'em_close') signals.emCloseInRange++
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const finalizeBrokenRefWrapperRangeSignals = (signals, wrapperState) => {
|
|
254
|
+
if (!wrapperState.sawWrapper || wrapperState.sawOpen) {
|
|
255
|
+
signals.hasLeadingUnmatchedClose = false
|
|
256
|
+
}
|
|
257
|
+
if (!signals.hasImbalance &&
|
|
258
|
+
(wrapperState.strongDepth !== 0 || wrapperState.emDepth !== 0)) {
|
|
259
|
+
signals.hasImbalance = true
|
|
260
|
+
}
|
|
261
|
+
return signals
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const buildBrokenRefWrapperRangeSignals = (tokens, startIdx, endIdx, firstTextOffset = 0) => {
|
|
265
|
+
const signals = createBrokenRefWrapperRangeSignals()
|
|
266
|
+
if (!tokens || startIdx < 0 || endIdx < startIdx) return signals
|
|
267
|
+
const wrapperState = { sawWrapper: false, sawOpen: false, strongDepth: 0, emDepth: 0 }
|
|
241
268
|
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
242
269
|
const token = tokens[i]
|
|
243
270
|
if (!token || !token.type) continue
|
|
244
|
-
if (!out.hasCodeInline && token.type === 'code_inline') {
|
|
245
|
-
out.hasCodeInline = true
|
|
246
|
-
}
|
|
247
271
|
const isAsteriskEmphasis = isAsteriskEmphasisToken(token)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
token.type === 'strong_close' ||
|
|
252
|
-
token.type === 'em_open' ||
|
|
253
|
-
token.type === 'em_close') &&
|
|
254
|
-
(token.markup === '_' || token.markup === '__')) {
|
|
255
|
-
out.hasUnderscoreEmphasisToken = true
|
|
256
|
-
}
|
|
257
|
-
if (token.type === 'text' && token.content) {
|
|
258
|
-
const content = token.content
|
|
259
|
-
// Keep this at 0 (instead of firstTextOffset) so historical fail-safe
|
|
260
|
-
// behavior around noisy leading chains in the first text token stays unchanged.
|
|
261
|
-
if (!out.hasLongStarNoise && content.indexOf('***') !== -1) {
|
|
262
|
-
out.hasLongStarNoise = true
|
|
263
|
-
}
|
|
264
|
-
if (!out.hasUnderscoreText) {
|
|
265
|
-
const scanFrom = i === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
266
|
-
if (scanFrom < content.length && content.indexOf('_', scanFrom) !== -1) {
|
|
267
|
-
out.hasUnderscoreText = true
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if ((token.type === 'strong_open' || token.type === 'strong_close' || token.type === 'em_open' || token.type === 'em_close') &&
|
|
272
|
-
!isAsteriskEmphasis) {
|
|
273
|
-
continue
|
|
274
|
-
}
|
|
275
|
-
const base = getInlineWrapperBase(token.type)
|
|
276
|
-
if (!base) continue
|
|
277
|
-
const isOpen = token.type.endsWith('_open')
|
|
278
|
-
if (!sawWrapper) {
|
|
279
|
-
sawWrapper = true
|
|
280
|
-
if (!isOpen) out.hasLeadingUnmatchedClose = true
|
|
281
|
-
}
|
|
282
|
-
if (isOpen) {
|
|
283
|
-
sawOpen = true
|
|
284
|
-
out.hasLeadingUnmatchedClose = false
|
|
285
|
-
depthMap.set(base, (depthMap.get(base) || 0) + 1)
|
|
286
|
-
} else {
|
|
287
|
-
const prev = depthMap.get(base) || 0
|
|
288
|
-
if (prev <= 0) {
|
|
289
|
-
out.hasImbalance = true
|
|
290
|
-
} else {
|
|
291
|
-
depthMap.set(base, prev - 1)
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (token.type === 'strong_open') out.strongOpenInRange++
|
|
295
|
-
else if (token.type === 'strong_close') out.strongCloseInRange++
|
|
296
|
-
else if (token.type === 'em_open') out.emOpenInRange++
|
|
297
|
-
else if (token.type === 'em_close') out.emCloseInRange++
|
|
298
|
-
}
|
|
299
|
-
if (!sawWrapper || sawOpen) out.hasLeadingUnmatchedClose = false
|
|
300
|
-
if (!out.hasImbalance) {
|
|
301
|
-
for (const depth of depthMap.values()) {
|
|
302
|
-
if (depth !== 0) {
|
|
303
|
-
out.hasImbalance = true
|
|
304
|
-
break
|
|
305
|
-
}
|
|
306
|
-
}
|
|
272
|
+
updateBrokenRefWrapperTokenSignals(signals, token, isAsteriskEmphasis)
|
|
273
|
+
updateBrokenRefTextRangeSignals(signals, token, i, startIdx, firstTextOffset)
|
|
274
|
+
updateBrokenRefWrapperRangeDepthSignals(signals, token, wrapperState, isAsteriskEmphasis)
|
|
307
275
|
}
|
|
308
|
-
return
|
|
276
|
+
return finalizeBrokenRefWrapperRangeSignals(signals, wrapperState)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const hasRangeCloseOnlyWrapperSignals = (signals) => {
|
|
280
|
+
if (!signals) return false
|
|
281
|
+
return (signals.strongCloseInRange > 0 && signals.strongOpenInRange === 0) ||
|
|
282
|
+
(signals.emCloseInRange > 0 && signals.emOpenInRange === 0)
|
|
309
283
|
}
|
|
310
284
|
|
|
311
285
|
const hasPreexistingWrapperCloseOnlyInRange = (tokens, startIdx, endIdx, prefixStats = null, wrapperSignals = null) => {
|
|
312
286
|
if (!tokens || startIdx <= 0 || endIdx < startIdx) return false
|
|
313
287
|
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, 0)
|
|
288
|
+
if (!hasRangeCloseOnlyWrapperSignals(signals)) return false
|
|
289
|
+
const needsStrongCloseOnly = signals.strongCloseInRange > 0 && signals.strongOpenInRange === 0
|
|
290
|
+
const needsEmCloseOnly = signals.emCloseInRange > 0 && signals.emOpenInRange === 0
|
|
314
291
|
|
|
315
292
|
let preStrongDepth = 0
|
|
316
293
|
let preEmDepth = 0
|
|
317
294
|
const hasPrefix =
|
|
318
295
|
!!prefixStats &&
|
|
319
296
|
Array.isArray(prefixStats.strongDepth) &&
|
|
320
|
-
Array.isArray(prefixStats.emDepth)
|
|
321
|
-
Array.isArray(prefixStats.strongOpen) &&
|
|
322
|
-
Array.isArray(prefixStats.strongClose) &&
|
|
323
|
-
Array.isArray(prefixStats.emOpen) &&
|
|
324
|
-
Array.isArray(prefixStats.emClose)
|
|
297
|
+
Array.isArray(prefixStats.emDepth)
|
|
325
298
|
if (hasPrefix &&
|
|
326
299
|
startIdx < prefixStats.strongDepth.length &&
|
|
327
|
-
startIdx < prefixStats.emDepth.length
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
(
|
|
331
|
-
(endIdx + 1) < prefixStats.emClose.length) {
|
|
332
|
-
preStrongDepth = prefixStats.strongDepth[startIdx] || 0
|
|
333
|
-
preEmDepth = prefixStats.emDepth[startIdx] || 0
|
|
334
|
-
if (preStrongDepth > 0) {
|
|
335
|
-
const strongOpensInRange = signals.strongOpenInRange
|
|
336
|
-
const strongClosesInRange = signals.strongCloseInRange
|
|
337
|
-
if (strongClosesInRange > 0 && strongOpensInRange === 0) return true
|
|
300
|
+
startIdx < prefixStats.emDepth.length) {
|
|
301
|
+
if (needsStrongCloseOnly) {
|
|
302
|
+
preStrongDepth = prefixStats.strongDepth[startIdx] || 0
|
|
303
|
+
if (preStrongDepth > 0) return true
|
|
338
304
|
}
|
|
339
|
-
if (
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (emClosesInRange > 0 && emOpensInRange === 0) return true
|
|
305
|
+
if (needsEmCloseOnly) {
|
|
306
|
+
preEmDepth = prefixStats.emDepth[startIdx] || 0
|
|
307
|
+
if (preEmDepth > 0) return true
|
|
343
308
|
}
|
|
344
309
|
return false
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
310
|
+
}
|
|
311
|
+
for (let i = 0; i < startIdx && i < tokens.length; i++) {
|
|
312
|
+
const token = tokens[i]
|
|
313
|
+
if (!token || !token.type || !isAsteriskEmphasisToken(token)) continue
|
|
314
|
+
if (needsStrongCloseOnly) {
|
|
349
315
|
if (token.type === 'strong_open') {
|
|
350
316
|
preStrongDepth++
|
|
351
317
|
continue
|
|
@@ -354,42 +320,100 @@ const hasPreexistingWrapperCloseOnlyInRange = (tokens, startIdx, endIdx, prefixS
|
|
|
354
320
|
if (preStrongDepth > 0) preStrongDepth--
|
|
355
321
|
continue
|
|
356
322
|
}
|
|
323
|
+
}
|
|
324
|
+
if (needsEmCloseOnly) {
|
|
357
325
|
if (token.type === 'em_open') {
|
|
358
326
|
preEmDepth++
|
|
359
327
|
continue
|
|
360
328
|
}
|
|
361
|
-
if (token.type === 'em_close') {
|
|
362
|
-
|
|
329
|
+
if (token.type === 'em_close' && preEmDepth > 0) {
|
|
330
|
+
preEmDepth--
|
|
363
331
|
}
|
|
364
332
|
}
|
|
365
333
|
}
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
334
|
+
if (needsStrongCloseOnly && preStrongDepth > 0) return true
|
|
335
|
+
if (needsEmCloseOnly && preEmDepth > 0) return true
|
|
368
336
|
return false
|
|
369
337
|
}
|
|
370
338
|
|
|
339
|
+
const hasBrokenRefLowConfidenceTextNoise = (signals) => {
|
|
340
|
+
return signals.hasLongStarNoise || signals.hasUnderscoreText
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const hasBrokenRefLowConfidenceInlineSyntax = (signals) => {
|
|
344
|
+
return signals.hasCodeInline || signals.hasUnderscoreEmphasisToken
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const hasBrokenRefLowConfidenceNoise = (signals) => {
|
|
348
|
+
return hasBrokenRefLowConfidenceTextNoise(signals) || hasBrokenRefLowConfidenceInlineSyntax(signals)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const hasBrokenRefCloseOnlyWrapperRisk = (
|
|
352
|
+
tokens,
|
|
353
|
+
startIdx,
|
|
354
|
+
endIdx,
|
|
355
|
+
wrapperPrefixStats = null,
|
|
356
|
+
wrapperSignals = null
|
|
357
|
+
) => {
|
|
358
|
+
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, 0)
|
|
359
|
+
return hasPreexistingWrapperCloseOnlyInRange(tokens, startIdx, endIdx, wrapperPrefixStats, signals)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const hasBrokenRefLowConfidenceWrapperRisk = (
|
|
363
|
+
tokens,
|
|
364
|
+
startIdx,
|
|
365
|
+
endIdx,
|
|
366
|
+
wrapperPrefixStats = null,
|
|
367
|
+
wrapperSignals = null
|
|
368
|
+
) => {
|
|
369
|
+
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, 0)
|
|
370
|
+
if (signals.hasLeadingUnmatchedClose) return true
|
|
371
|
+
return hasBrokenRefCloseOnlyWrapperRisk(tokens, startIdx, endIdx, wrapperPrefixStats, signals)
|
|
372
|
+
}
|
|
373
|
+
|
|
371
374
|
const isLowConfidenceBrokenRefRange = (tokens, startIdx, endIdx, firstTextOffset = 0, wrapperPrefixStats = null, wrapperSignals = null) => {
|
|
372
375
|
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, firstTextOffset)
|
|
373
|
-
if (signals
|
|
374
|
-
|
|
375
|
-
if (signals.hasLeadingUnmatchedClose) return true
|
|
376
|
-
if (hasPreexistingWrapperCloseOnlyInRange(tokens, startIdx, endIdx, wrapperPrefixStats, signals)) return true
|
|
377
|
-
return false
|
|
376
|
+
if (hasBrokenRefLowConfidenceNoise(signals)) return true
|
|
377
|
+
return hasBrokenRefLowConfidenceWrapperRisk(tokens, startIdx, endIdx, wrapperPrefixStats, signals)
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
if (isLowConfidenceBrokenRefRange(tokens, startIdx, endIdx, firstTextOffset, wrapperPrefixStats, wrapperSignals)) return false
|
|
383
|
-
if (wrapperSignals.hasImbalance) {
|
|
384
|
-
if (wrapperSignals.hasAsteriskEmphasisToken) return true
|
|
385
|
-
return countStrongMarkerRunsInTextRange(tokens, startIdx, endIdx, firstTextOffset, 2) >= 2
|
|
386
|
-
}
|
|
387
|
-
if (wrapperSignals.hasAsteriskEmphasisToken) return false
|
|
388
|
-
return countStrongMarkerRunsInTextRange(tokens, startIdx, endIdx, firstTextOffset, 2) >= 2
|
|
380
|
+
const hasBrokenRefStrongRunEvidence = (wrapperSignals) => {
|
|
381
|
+
return !!wrapperSignals && wrapperSignals.strongRunCount >= 2
|
|
389
382
|
}
|
|
390
383
|
|
|
391
|
-
const
|
|
392
|
-
|
|
384
|
+
const hasBrokenRefExplicitAsteriskSignal = (wrapperSignals) => {
|
|
385
|
+
return wrapperSignals.hasAsteriskEmphasisToken
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const hasBrokenRefImmediateRewriteSignal = (wrapperSignals) => {
|
|
389
|
+
return wrapperSignals.hasImbalance && hasBrokenRefExplicitAsteriskSignal(wrapperSignals)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const shouldRejectBalancedBrokenRefRewrite = (wrapperSignals) => {
|
|
393
|
+
return !wrapperSignals.hasImbalance && hasBrokenRefExplicitAsteriskSignal(wrapperSignals)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const shouldAttemptBrokenRefRewriteFromSignals = (wrapperSignals) => {
|
|
397
|
+
if (hasBrokenRefImmediateRewriteSignal(wrapperSignals)) return true
|
|
398
|
+
if (shouldRejectBalancedBrokenRefRewrite(wrapperSignals)) return false
|
|
399
|
+
return hasBrokenRefStrongRunEvidence(wrapperSignals)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const shouldAttemptBrokenRefRewrite = (
|
|
403
|
+
tokens,
|
|
404
|
+
startIdx,
|
|
405
|
+
endIdx,
|
|
406
|
+
firstTextOffset = 0,
|
|
407
|
+
wrapperPrefixStats = null,
|
|
408
|
+
wrapperSignals = null
|
|
409
|
+
) => {
|
|
410
|
+
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, firstTextOffset)
|
|
411
|
+
if (!signals.hasTextMarker) return false
|
|
412
|
+
if (isLowConfidenceBrokenRefRange(tokens, startIdx, endIdx, firstTextOffset, wrapperPrefixStats, signals)) return false
|
|
413
|
+
return shouldAttemptBrokenRefRewriteFromSignals(signals)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const scanInlinePostprocessSignals = (children) => {
|
|
393
417
|
let hasEmphasis = false
|
|
394
418
|
let hasLinkOpen = false
|
|
395
419
|
let hasLinkClose = false
|
|
@@ -409,14 +433,9 @@ const scanInlinePostprocessSignals = (children, hasBracketTextInContent = false)
|
|
|
409
433
|
if (!hasCodeInline && child.type === 'code_inline') {
|
|
410
434
|
hasCodeInline = true
|
|
411
435
|
}
|
|
412
|
-
if (
|
|
413
|
-
if (child.content.indexOf('[') !== -1 || child.content.indexOf(']') !== -1) {
|
|
414
|
-
hasBracketText = true
|
|
415
|
-
}
|
|
416
|
-
if (hasEmphasis && hasBracketText && hasLinkOpen && hasLinkClose) break
|
|
436
|
+
if (hasEmphasis && hasLinkOpen && hasLinkClose) break
|
|
417
437
|
}
|
|
418
438
|
return {
|
|
419
|
-
hasBracketText,
|
|
420
439
|
hasEmphasis,
|
|
421
440
|
hasLinkOpen,
|
|
422
441
|
hasLinkClose,
|
|
@@ -429,8 +448,8 @@ export {
|
|
|
429
448
|
isAsteriskEmphasisToken,
|
|
430
449
|
hasJapaneseContextInRange,
|
|
431
450
|
hasEmphasisSignalInRange,
|
|
432
|
-
hasTextMarkerCharsInRange,
|
|
433
451
|
buildAsteriskWrapperPrefixStats,
|
|
452
|
+
buildBrokenRefWrapperRangeSignals,
|
|
434
453
|
shouldAttemptBrokenRefRewrite,
|
|
435
454
|
scanInlinePostprocessSignals
|
|
436
455
|
}
|