@peaceroad/markdown-it-strong-ja 0.6.2 → 0.7.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 +42 -13
- package/index.js +44 -2488
- package/package.json +4 -3
- package/src/token-compat.js +226 -0
- package/src/token-core.js +439 -0
- package/src/token-link-utils.js +774 -0
- package/src/token-postprocess.js +340 -0
- package/src/token-utils.js +166 -0
package/index.js
CHANGED
|
@@ -1,2506 +1,62 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { hasCjkBreaksRule, normalizeCoreRulesBeforePostprocess, ensureCoreRuleOrder, resolveMode } from './src/token-utils.js'
|
|
2
|
+
import { patchScanDelims } from './src/token-core.js'
|
|
3
|
+
import { registerTokenCompat } from './src/token-compat.js'
|
|
4
|
+
import { registerTokenPostprocess } from './src/token-postprocess.js'
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const CHAR_LT = 0x3C // <
|
|
11
|
-
const CHAR_GT = 0x3E // >
|
|
12
|
-
const CHAR_SLASH = 0x2F // /
|
|
13
|
-
const CHAR_SPACE = 0x20 // ' ' (space)
|
|
14
|
-
const CHAR_OPEN_BRACKET = 0x5B // [
|
|
15
|
-
const CHAR_CLOSE_BRACKET = 0x5D // ]
|
|
16
|
-
const CHAR_OPEN_PAREN = 0x28 // (
|
|
17
|
-
const CHAR_CLOSE_PAREN = 0x29 // )
|
|
18
|
-
const CHAR_NEWLINE = 0x0A // \n
|
|
19
|
-
const CHAR_TAB = 0x09 // tab
|
|
20
|
-
//const CHAR_OPEN_CURLY = 0x7B // {
|
|
21
|
-
const CHAR_CLOSE_CURLY = 0x7D // }
|
|
22
|
-
|
|
23
|
-
const REG_ATTRS = /{[^{}\n!@#%^&*()]+?}$/
|
|
24
|
-
const REG_ASCII_PUNCT = /[!-/:-@[-`{-~]/g
|
|
25
|
-
const REG_JAPANESE = /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}\u3000-\u303F\uFF00-\uFFEF]/u // ひらがな|カタカナ|漢字|CJK句読点・全角形状(絵文字は除外)
|
|
26
|
-
|
|
27
|
-
const REG_MARKDOWN_HTML = /^\[[^\[\]]+\]\([^)]+\)$|^<([a-zA-Z][a-zA-Z0-9]*)[^>]*>([^<]+<\/\1>)$|^`[^`]+`$|^\$[^$]+\$$/ // for mixed-language context detection
|
|
28
|
-
|
|
29
|
-
const hasCjkBreaksRule = (md) => {
|
|
30
|
-
if (!md || !md.core || !md.core.ruler || !Array.isArray(md.core.ruler.__rules__)) return false
|
|
31
|
-
if (md.__strongJaHasCjkBreaks === true) return true
|
|
32
|
-
const found = md.core.ruler.__rules__.some((rule) => rule && typeof rule.name === 'string' && rule.name.indexOf('cjk_breaks') !== -1)
|
|
33
|
-
if (found) md.__strongJaHasCjkBreaks = true
|
|
34
|
-
return found
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const hasBackslash = (state, start) => {
|
|
38
|
-
if (start <= 0) return false
|
|
39
|
-
if (state.__strongJaHasBackslash === false) return false
|
|
40
|
-
if (state.__strongJaHasBackslash === undefined) {
|
|
41
|
-
state.__strongJaHasBackslash = state.src.indexOf('\\') !== -1
|
|
42
|
-
if (!state.__strongJaHasBackslash) return false
|
|
43
|
-
}
|
|
44
|
-
const cache = state.__strongJaBackslashCache
|
|
45
|
-
if (cache && cache.has(start)) {
|
|
46
|
-
return cache.get(start)
|
|
47
|
-
}
|
|
48
|
-
let slashNum = 0
|
|
49
|
-
let i = start - 1
|
|
50
|
-
const src = state.src
|
|
51
|
-
if (i < 0 || src.charCodeAt(i) !== CHAR_BACKSLASH) {
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
while (i >= 0 && src.charCodeAt(i) === CHAR_BACKSLASH) {
|
|
55
|
-
slashNum++
|
|
56
|
-
i--
|
|
57
|
-
}
|
|
58
|
-
const isEscaped = slashNum % 2 === 1
|
|
59
|
-
if (cache) {
|
|
60
|
-
cache.set(start, isEscaped)
|
|
61
|
-
} else {
|
|
62
|
-
state.__strongJaBackslashCache = new Map([[start, isEscaped]])
|
|
63
|
-
}
|
|
64
|
-
return isEscaped
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const findMatchingBracket = (state, start, max, openChar, closeChar) => {
|
|
68
|
-
let depth = 1
|
|
69
|
-
let pos = start + 1
|
|
70
|
-
const src = state.src
|
|
71
|
-
while (pos < max) {
|
|
72
|
-
const ch = src.charCodeAt(pos)
|
|
73
|
-
if (ch === openChar && !hasBackslash(state, pos)) {
|
|
74
|
-
depth++
|
|
75
|
-
} else if (ch === closeChar && !hasBackslash(state, pos)) {
|
|
76
|
-
depth--
|
|
77
|
-
if (depth === 0) return pos
|
|
78
|
-
}
|
|
79
|
-
pos++
|
|
80
|
-
}
|
|
81
|
-
return -1
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const getInlineLabelRanges = (inlineLinkRanges) => {
|
|
85
|
-
if (!inlineLinkRanges || inlineLinkRanges.length === 0) return null
|
|
86
|
-
return inlineLinkRanges.__labelRanges
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const hasInlineLinkLabelCrossing = (inlineLinkRanges, from, to) => {
|
|
90
|
-
if (from >= to) return false
|
|
91
|
-
const labelRanges = getInlineLabelRanges(inlineLinkRanges)
|
|
92
|
-
if (!labelRanges || labelRanges.length === 0) return false
|
|
93
|
-
if (labelRanges.length <= 8) {
|
|
94
|
-
for (let idx = 0; idx < labelRanges.length; idx++) {
|
|
95
|
-
const range = labelRanges[idx]
|
|
96
|
-
if (range.start >= to) break
|
|
97
|
-
if (range.start >= from && range.end >= to) return true
|
|
98
|
-
}
|
|
99
|
-
return false
|
|
100
|
-
}
|
|
101
|
-
let left = 0
|
|
102
|
-
let right = labelRanges.length - 1
|
|
103
|
-
let firstIdx = labelRanges.length
|
|
104
|
-
while (left <= right) {
|
|
105
|
-
const mid = left + Math.floor((right - left) / 2)
|
|
106
|
-
if (labelRanges[mid].start < from) {
|
|
107
|
-
left = mid + 1
|
|
108
|
-
} else {
|
|
109
|
-
firstIdx = mid
|
|
110
|
-
right = mid - 1
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
for (let idx = firstIdx; idx < labelRanges.length; idx++) {
|
|
114
|
-
const range = labelRanges[idx]
|
|
115
|
-
if (range.start >= to) break
|
|
116
|
-
if (range.end >= to) return true
|
|
117
|
-
}
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const findRefRangeIndex = (pos, refRanges) => {
|
|
122
|
-
if (!refRanges || refRanges.length === 0) return -1
|
|
123
|
-
|
|
124
|
-
const tryIndex = (idx) => {
|
|
125
|
-
if (idx < 0 || idx >= refRanges.length) return -1
|
|
126
|
-
const range = refRanges[idx]
|
|
127
|
-
if (pos >= range.start && pos <= range.end) {
|
|
128
|
-
return range.hasReference ? idx : -1
|
|
129
|
-
}
|
|
130
|
-
return null
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const tracker = refRanges.__lastIndexState || (refRanges.__lastIndexState = { idx: 0 })
|
|
134
|
-
let idx = tracker.idx
|
|
135
|
-
if (idx >= refRanges.length) idx = refRanges.length - 1
|
|
136
|
-
let result = tryIndex(idx)
|
|
137
|
-
if (result !== null) {
|
|
138
|
-
tracker.idx = idx
|
|
139
|
-
return result
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (pos < refRanges[idx].start) {
|
|
143
|
-
while (idx > 0 && pos < refRanges[idx].start) {
|
|
144
|
-
idx--
|
|
145
|
-
result = tryIndex(idx)
|
|
146
|
-
if (result !== null) {
|
|
147
|
-
tracker.idx = idx
|
|
148
|
-
return result
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
while (idx < refRanges.length - 1 && pos > refRanges[idx].end) {
|
|
153
|
-
idx++
|
|
154
|
-
result = tryIndex(idx)
|
|
155
|
-
if (result !== null) {
|
|
156
|
-
tracker.idx = idx
|
|
157
|
-
return result
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let left = 0
|
|
163
|
-
let right = refRanges.length - 1
|
|
164
|
-
while (left <= right) {
|
|
165
|
-
const mid = left + Math.floor((right - left) / 2)
|
|
166
|
-
const range = refRanges[mid]
|
|
167
|
-
if (pos < range.start) {
|
|
168
|
-
right = mid - 1
|
|
169
|
-
} else if (pos > range.end) {
|
|
170
|
-
left = mid + 1
|
|
171
|
-
} else {
|
|
172
|
-
tracker.idx = mid
|
|
173
|
-
return range.hasReference ? mid : -1
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return -1
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Detect reference-link label ranges within the current inline slice
|
|
180
|
-
const computeReferenceRanges = (state, start, max) => {
|
|
181
|
-
const src = state.src
|
|
182
|
-
const references = state.env && state.env.references
|
|
183
|
-
const referenceCount = state.__strongJaReferenceCount
|
|
184
|
-
const hasReferences = references && (referenceCount !== undefined
|
|
185
|
-
? referenceCount > 0
|
|
186
|
-
: Object.keys(references).length > 0)
|
|
187
|
-
if (!hasReferences) return []
|
|
188
|
-
let pos = src.indexOf('[', start)
|
|
189
|
-
if (pos === -1 || pos >= max) return []
|
|
190
|
-
const ranges = []
|
|
191
|
-
while (pos !== -1 && pos < max) {
|
|
192
|
-
if (!hasBackslash(state, pos)) {
|
|
193
|
-
const labelClose = findMatchingBracket(state, pos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
194
|
-
if (labelClose !== -1) {
|
|
195
|
-
const nextPos = labelClose + 1
|
|
196
|
-
if (nextPos < max && src.charCodeAt(nextPos) === CHAR_OPEN_BRACKET && !hasBackslash(state, nextPos)) {
|
|
197
|
-
const refClose = findMatchingBracket(state, nextPos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
198
|
-
if (refClose !== -1) {
|
|
199
|
-
let hasReference = false
|
|
200
|
-
if (refClose === nextPos + 1) {
|
|
201
|
-
const labelRaw = src.slice(pos + 1, labelClose)
|
|
202
|
-
const normalizedLabel = normalizeReferenceCandidate(state, labelRaw, { useClean: true })
|
|
203
|
-
hasReference = !!references[normalizedLabel]
|
|
204
|
-
} else {
|
|
205
|
-
const refRaw = src.slice(nextPos + 1, refClose)
|
|
206
|
-
const normalizedRef = normalizeReferenceCandidate(state, refRaw)
|
|
207
|
-
hasReference = !!references[normalizedRef]
|
|
208
|
-
}
|
|
209
|
-
if (hasReference) {
|
|
210
|
-
ranges.push({ start: pos, end: labelClose, hasReference: true })
|
|
211
|
-
ranges.push({ start: nextPos, end: refClose, hasReference: true })
|
|
212
|
-
}
|
|
213
|
-
pos = src.indexOf('[', refClose + 1)
|
|
214
|
-
continue
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
pos = src.indexOf('[', pos + 1)
|
|
220
|
-
}
|
|
221
|
-
return ranges
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const computeInlineLinkRanges = (state, start, max) => {
|
|
225
|
-
const src = state.src
|
|
226
|
-
const ranges = []
|
|
227
|
-
const labelRanges = []
|
|
228
|
-
let pos = src.indexOf('[', start)
|
|
229
|
-
if (pos === -1 || pos >= max) return []
|
|
230
|
-
let rangeId = 0
|
|
231
|
-
while (pos !== -1 && pos < max) {
|
|
232
|
-
if (!hasBackslash(state, pos)) {
|
|
233
|
-
const labelClose = findMatchingBracket(state, pos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
234
|
-
if (labelClose === -1) break
|
|
235
|
-
let destStart = labelClose + 1
|
|
236
|
-
while (destStart < max) {
|
|
237
|
-
const ch = src.charCodeAt(destStart)
|
|
238
|
-
if (ch !== CHAR_SPACE && ch !== 0x0A && ch !== 0x09) break
|
|
239
|
-
destStart++
|
|
240
|
-
}
|
|
241
|
-
if (destStart < max && src.charCodeAt(destStart) === CHAR_OPEN_PAREN && !hasBackslash(state, destStart)) {
|
|
242
|
-
const destClose = findMatchingBracket(state, destStart, max, CHAR_OPEN_PAREN, CHAR_CLOSE_PAREN)
|
|
243
|
-
if (destClose !== -1) {
|
|
244
|
-
const labelRange = { start: pos, end: labelClose, kind: 'label', id: rangeId }
|
|
245
|
-
ranges.push(labelRange)
|
|
246
|
-
labelRanges.push(labelRange)
|
|
247
|
-
ranges.push({ start: destStart, end: destClose, kind: 'dest', id: rangeId })
|
|
248
|
-
rangeId++
|
|
249
|
-
pos = src.indexOf('[', destClose + 1)
|
|
250
|
-
continue
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
pos = src.indexOf('[', labelClose + 1)
|
|
254
|
-
continue
|
|
255
|
-
}
|
|
256
|
-
pos = src.indexOf('[', pos + 1)
|
|
257
|
-
}
|
|
258
|
-
if (ranges.length && labelRanges.length) {
|
|
259
|
-
ranges.__labelRanges = labelRanges
|
|
260
|
-
}
|
|
261
|
-
return ranges
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const getInlineRangeCacheMap = (ranges, kind, create) => {
|
|
265
|
-
const prop = kind ? `__cache_${kind}` : '__cache_any'
|
|
266
|
-
let cache = ranges[prop]
|
|
267
|
-
if (!cache && create) {
|
|
268
|
-
cache = new Map()
|
|
269
|
-
ranges[prop] = cache
|
|
270
|
-
}
|
|
271
|
-
return cache
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const findInlineLinkRange = (pos, ranges, kind) => {
|
|
275
|
-
if (!ranges || ranges.length === 0) return null
|
|
276
|
-
const useCache = ranges.length > 32
|
|
277
|
-
const cache = useCache ? getInlineRangeCacheMap(ranges, kind, false) : null
|
|
278
|
-
if (cache && cache.has(pos)) return cache.get(pos)
|
|
279
|
-
const first = ranges[0]
|
|
280
|
-
const last = ranges[ranges.length - 1]
|
|
281
|
-
if (pos < first.start || pos > last.end) {
|
|
282
|
-
if (useCache) {
|
|
283
|
-
const storeCache = getInlineRangeCacheMap(ranges, kind, true)
|
|
284
|
-
storeCache.set(pos, null)
|
|
285
|
-
}
|
|
286
|
-
return null
|
|
287
|
-
}
|
|
288
|
-
let left = 0
|
|
289
|
-
let right = ranges.length - 1
|
|
290
|
-
let found = null
|
|
291
|
-
while (left <= right) {
|
|
292
|
-
const mid = left + Math.floor((right - left) / 2)
|
|
293
|
-
const range = ranges[mid]
|
|
294
|
-
if (pos < range.start) {
|
|
295
|
-
right = mid - 1
|
|
296
|
-
} else if (pos > range.end) {
|
|
297
|
-
left = mid + 1
|
|
298
|
-
} else {
|
|
299
|
-
if (!kind || range.kind === kind) {
|
|
300
|
-
found = range
|
|
301
|
-
}
|
|
302
|
-
break
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (useCache) {
|
|
306
|
-
const storeCache = getInlineRangeCacheMap(ranges, kind, true)
|
|
307
|
-
storeCache.set(pos, found)
|
|
308
|
-
}
|
|
309
|
-
return found
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const copyInlineTokenFields = (dest, src) => {
|
|
313
|
-
Object.assign(dest, src)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const registerPostProcessTarget = (state) => {
|
|
317
|
-
const env = state.env
|
|
318
|
-
if (!env.__strongJaPostProcessTargets) {
|
|
319
|
-
env.__strongJaPostProcessTargets = []
|
|
320
|
-
env.__strongJaPostProcessTargetSet = typeof WeakSet !== 'undefined' ? new WeakSet() : null
|
|
321
|
-
}
|
|
322
|
-
const targets = env.__strongJaPostProcessTargets
|
|
323
|
-
const targetSet = env.__strongJaPostProcessTargetSet
|
|
324
|
-
if (targetSet) {
|
|
325
|
-
if (targetSet.has(state.tokens)) return
|
|
326
|
-
targetSet.add(state.tokens)
|
|
327
|
-
} else if (targets.includes(state.tokens)) {
|
|
328
|
-
return
|
|
329
|
-
}
|
|
330
|
-
targets.push(state.tokens)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const hasMditAttrs = (state) => {
|
|
334
|
-
if (state.__strongJaHasAttrs !== undefined) return state.__strongJaHasAttrs
|
|
335
|
-
const rules = state.md && state.md.core && state.md.core.ruler && state.md.core.ruler.__rules__
|
|
336
|
-
if (!rules || !Array.isArray(rules)) {
|
|
337
|
-
state.__strongJaHasAttrs = false
|
|
338
|
-
return false
|
|
339
|
-
}
|
|
340
|
-
for (let i = 0; i < rules.length; i++) {
|
|
341
|
-
if (rules[i].name === 'curly_attributes') {
|
|
342
|
-
state.__strongJaHasAttrs = true
|
|
343
|
-
return true
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
state.__strongJaHasAttrs = false
|
|
347
|
-
return false
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const isAllAsterisks = (content) => {
|
|
351
|
-
for (let i = 0; i < content.length; i++) {
|
|
352
|
-
if (content.charCodeAt(i) !== CHAR_ASTERISK) return false
|
|
353
|
-
}
|
|
354
|
-
return true
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function isPlainTextContent(content) {
|
|
358
|
-
for (let idx = 0; idx < content.length; idx++) {
|
|
359
|
-
const code = content.charCodeAt(idx)
|
|
360
|
-
if (code === CHAR_BACKSLASH || code === CHAR_NEWLINE || code === CHAR_TAB) {
|
|
361
|
-
return false
|
|
362
|
-
}
|
|
363
|
-
if (code === CHAR_BACKTICK || code === CHAR_DOLLAR || code === CHAR_LT || code === CHAR_GT) {
|
|
364
|
-
return false
|
|
365
|
-
}
|
|
366
|
-
if (code === CHAR_OPEN_BRACKET || code === CHAR_CLOSE_BRACKET || code === CHAR_OPEN_PAREN || code === CHAR_CLOSE_PAREN) {
|
|
367
|
-
return false
|
|
368
|
-
}
|
|
369
|
-
if (code === 0x5E || code === 0x7E) {
|
|
370
|
-
return false
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return true
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Cache newline positions for lightweight map generation
|
|
377
|
-
const getLineOffsets = (state) => {
|
|
378
|
-
if (state.__strongJaLineOffsets) return state.__strongJaLineOffsets
|
|
379
|
-
const offsets = []
|
|
380
|
-
const src = state.src || ''
|
|
381
|
-
for (let i = 0; i < src.length; i++) {
|
|
382
|
-
if (src.charCodeAt(i) === CHAR_NEWLINE) offsets.push(i)
|
|
383
|
-
}
|
|
384
|
-
state.__strongJaLineOffsets = offsets
|
|
385
|
-
return offsets
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const createLineMapper = (state) => {
|
|
389
|
-
const offsets = getLineOffsets(state)
|
|
390
|
-
let idx = 0
|
|
391
|
-
const maxIdx = offsets.length
|
|
392
|
-
return (startPos, endPos) => {
|
|
393
|
-
const start = startPos === undefined || startPos === null ? 0 : startPos
|
|
394
|
-
const end = endPos === undefined || endPos === null ? start : endPos
|
|
395
|
-
while (idx < maxIdx && offsets[idx] < start) idx++
|
|
396
|
-
const startLine = idx
|
|
397
|
-
let endIdx = idx
|
|
398
|
-
while (endIdx < maxIdx && offsets[endIdx] < end) endIdx++
|
|
399
|
-
return [startLine, endIdx]
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const setToken = (state, inlines, opt, attrsEnabled) => {
|
|
404
|
-
const src = state.src
|
|
405
|
-
const mapFromPos = createLineMapper(state)
|
|
406
|
-
let i = 0
|
|
407
|
-
let lastTextToken = null
|
|
408
|
-
while (i < inlines.length) {
|
|
409
|
-
let type = inlines[i].type
|
|
410
|
-
let tag = ''
|
|
411
|
-
let isOpen = false
|
|
412
|
-
let isClose = false
|
|
413
|
-
if (type.length > 5 && type.endsWith('_open')) {
|
|
414
|
-
isOpen = true
|
|
415
|
-
tag = type.slice(0, -5)
|
|
416
|
-
} else if (type.length > 6 && type.endsWith('_close')) {
|
|
417
|
-
isClose = true
|
|
418
|
-
tag = type.slice(0, -6)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (isOpen) {
|
|
422
|
-
const startToken = state.push(type, tag, 1)
|
|
423
|
-
startToken.markup = tag === 'strong' ? '**' : '*'
|
|
424
|
-
startToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (type === 'html_inline') {
|
|
428
|
-
const content = src.slice(inlines[i].s, inlines[i].e + 1)
|
|
429
|
-
if (lastTextToken && inlines[i].s > 0) {
|
|
430
|
-
const prevChar = src.charAt(inlines[i].s - 1)
|
|
431
|
-
if (prevChar === ' ' || prevChar === '\t') {
|
|
432
|
-
if (!lastTextToken.content.endsWith(prevChar)) {
|
|
433
|
-
lastTextToken.content += prevChar
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
const htmlToken = state.push('html_inline', '', 0)
|
|
438
|
-
htmlToken.content = content
|
|
439
|
-
htmlToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
440
|
-
i++
|
|
441
|
-
continue
|
|
442
|
-
}
|
|
443
|
-
if (type === 'text') {
|
|
444
|
-
let content = src.slice(inlines[i].s, inlines[i].e + 1)
|
|
445
|
-
if (content.length > 0 && content.charCodeAt(0) === CHAR_ASTERISK) {
|
|
446
|
-
if (isAllAsterisks(content)) {
|
|
447
|
-
const asteriskToken = state.push(type, '', 0)
|
|
448
|
-
asteriskToken.content = content
|
|
449
|
-
asteriskToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
450
|
-
i++
|
|
451
|
-
continue
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
const attrMatch = attrsEnabled && content.length > 0 && content.charCodeAt(content.length - 1) === CHAR_CLOSE_CURLY && REG_ATTRS.test(content)
|
|
455
|
-
? content.match(/^(.*?)(\s+{[^{}\n!@#%^&*()]+?})$/)
|
|
456
|
-
: null
|
|
457
|
-
if (attrMatch) {
|
|
458
|
-
const textPart = attrMatch[1] ? attrMatch[1].replace(/[ \t]+$/, '') : ''
|
|
459
|
-
const attrPart = attrMatch[2]
|
|
460
|
-
if (textPart && textPart.length > 0) {
|
|
461
|
-
const textToken = state.push(type, '', 0)
|
|
462
|
-
textToken.content = textPart
|
|
463
|
-
textToken.map = mapFromPos(inlines[i].s, inlines[i].s + textPart.length)
|
|
464
|
-
lastTextToken = textToken
|
|
465
|
-
}
|
|
466
|
-
const attrsToken = state.push(type, '', 0)
|
|
467
|
-
let attrsContent = attrPart.replace(/^\s+/, '')
|
|
468
|
-
if (attrsContent.indexOf('\\') !== -1) {
|
|
469
|
-
const hasBackslashBeforeCurlyAttribute = attrsContent.match(/(\\+){/)
|
|
470
|
-
if (hasBackslashBeforeCurlyAttribute) {
|
|
471
|
-
if (hasBackslashBeforeCurlyAttribute[1].length === 1) {
|
|
472
|
-
attrsContent = attrsContent.replace(/\\{/, '{')
|
|
473
|
-
} else {
|
|
474
|
-
let backSlashNum = Math.floor(hasBackslashBeforeCurlyAttribute[1].length / 2)
|
|
475
|
-
let k = 0
|
|
476
|
-
let backSlash = ''
|
|
477
|
-
while (k < backSlashNum) {
|
|
478
|
-
backSlash += '\\'
|
|
479
|
-
k++
|
|
480
|
-
}
|
|
481
|
-
attrsContent = attrsContent.replace(/\\+{/, backSlash + '{')
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
attrsToken.content = attrsContent
|
|
486
|
-
attrsToken.map = mapFromPos(inlines[i].s + content.length - attrPart.length, inlines[i].e)
|
|
487
|
-
i++
|
|
488
|
-
continue
|
|
489
|
-
}
|
|
490
|
-
if (isPlainTextContent(content)) {
|
|
491
|
-
const textToken = state.push(type, '', 0)
|
|
492
|
-
textToken.content = content
|
|
493
|
-
textToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
494
|
-
lastTextToken = textToken
|
|
495
|
-
i++
|
|
496
|
-
continue
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const hasOnlySimpleNewline = attrsEnabled && (content.indexOf('{') !== -1 || content.indexOf('}') !== -1) &&
|
|
500
|
-
content.indexOf('\n') !== -1 &&
|
|
501
|
-
content.indexOf('`') === -1 &&
|
|
502
|
-
content.indexOf('$') === -1 &&
|
|
503
|
-
content.indexOf('<') === -1 &&
|
|
504
|
-
content.indexOf('>') === -1 &&
|
|
505
|
-
content.indexOf('[') === -1 &&
|
|
506
|
-
content.indexOf(']') === -1 &&
|
|
507
|
-
content.indexOf('(') === -1 &&
|
|
508
|
-
content.indexOf(')') === -1 &&
|
|
509
|
-
content.indexOf('^') === -1 &&
|
|
510
|
-
content.indexOf('~') === -1 &&
|
|
511
|
-
content.indexOf('\\') === -1
|
|
512
|
-
|
|
513
|
-
if (hasOnlySimpleNewline) {
|
|
514
|
-
const textToken = state.push(type, '', 0)
|
|
515
|
-
textToken.content = content
|
|
516
|
-
textToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
517
|
-
lastTextToken = textToken
|
|
518
|
-
i++
|
|
519
|
-
continue
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const childTokens = []
|
|
523
|
-
state.md.inline.parse(content, state.md, state.env, childTokens)
|
|
524
|
-
let j = 0
|
|
525
|
-
while (j < childTokens.length) {
|
|
526
|
-
const t = childTokens[j]
|
|
527
|
-
if (t.type === 'softbreak' && !opt.mdBreaks) {
|
|
528
|
-
const hasCjk = opt.hasCjkBreaks === true
|
|
529
|
-
if (hasCjk) {
|
|
530
|
-
const prevToken = childTokens[j - 1]
|
|
531
|
-
const nextToken = childTokens[j + 1]
|
|
532
|
-
const prevChar = prevToken && prevToken.content ? prevToken.content.slice(-1) : ''
|
|
533
|
-
const nextChar = nextToken && nextToken.content ? nextToken.content.charAt(0) : ''
|
|
534
|
-
const isAsciiWord = nextChar >= '0' && nextChar <= 'z' && /[A-Za-z0-9]/.test(nextChar)
|
|
535
|
-
if (isAsciiWord && isJapanese(prevChar) && !isJapanese(nextChar)) {
|
|
536
|
-
t.type = 'text'
|
|
537
|
-
t.tag = ''
|
|
538
|
-
t.content = ' '
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
if (!attrsEnabled && t.tag === 'br') {
|
|
543
|
-
t.tag = ''
|
|
544
|
-
t.content = '\n'
|
|
545
|
-
}
|
|
546
|
-
const token = state.push(t.type, t.tag, t.nesting)
|
|
547
|
-
copyInlineTokenFields(token, t)
|
|
548
|
-
if (t.type === 'text') {
|
|
549
|
-
lastTextToken = token
|
|
550
|
-
}
|
|
551
|
-
j++
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (isClose) {
|
|
556
|
-
const closeToken = state.push(type, tag, -1)
|
|
557
|
-
closeToken.markup = tag === 'strong' ? '**' : '*'
|
|
558
|
-
closeToken.map = mapFromPos(inlines[i].s, inlines[i].e)
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
i++
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const pushInlines = (inlines, s, e, len, type, tag, tagType) => {
|
|
566
|
-
const inline = {
|
|
567
|
-
s: s,
|
|
568
|
-
sp: s,
|
|
569
|
-
e: e,
|
|
570
|
-
ep: e,
|
|
571
|
-
len: len,
|
|
572
|
-
type: type,
|
|
573
|
-
check: false
|
|
574
|
-
}
|
|
575
|
-
if (tag) inline.tag = [tag, tagType]
|
|
576
|
-
inlines.push(inline)
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const findNextAsciiPunctuation = (src, start, max) => {
|
|
580
|
-
REG_ASCII_PUNCT.lastIndex = start
|
|
581
|
-
const match = REG_ASCII_PUNCT.exec(src)
|
|
582
|
-
if (!match || match.index >= max) return -1
|
|
583
|
-
return match.index
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const findNextSymbolPos = (state, n, max, symbol, symbolChar) => {
|
|
587
|
-
const src = state.src
|
|
588
|
-
if (src.charCodeAt(n) !== symbol || hasBackslash(state, n)) return -1
|
|
589
|
-
let i = src.indexOf(symbolChar, n + 1)
|
|
590
|
-
while (i !== -1 && i < max) {
|
|
591
|
-
if (!hasBackslash(state, i)) return i
|
|
592
|
-
i = src.indexOf(symbolChar, i + 1)
|
|
593
|
-
}
|
|
594
|
-
return -1
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const processSymbolPair = (state, n, srcLen, symbol, symbolChar, hasText, textStart, pushInlines) => {
|
|
598
|
-
const nextSymbolPos = findNextSymbolPos(state, n, srcLen, symbol, symbolChar)
|
|
599
|
-
if (nextSymbolPos === -1) {
|
|
600
|
-
return { shouldBreak: false, shouldContinue: false, newN: n, hasText: hasText }
|
|
601
|
-
}
|
|
602
|
-
if (nextSymbolPos === srcLen - 1) {
|
|
603
|
-
pushInlines(textStart, nextSymbolPos, nextSymbolPos - textStart + 1, 'text')
|
|
604
|
-
return { shouldBreak: true, newN: nextSymbolPos + 1, hasText: true }
|
|
605
|
-
}
|
|
606
|
-
return { shouldBreak: false, shouldContinue: true, newN: nextSymbolPos + 1, hasText: true }
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
const processTextSegment = (inlines, textStart, n, hasText) => {
|
|
610
|
-
if (n !== 0 && hasText) {
|
|
611
|
-
pushInlines(inlines, textStart, n - 1, n - textStart, 'text')
|
|
612
|
-
return false
|
|
613
|
-
}
|
|
614
|
-
return hasText
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
const createInlines = (state, start, max, opt) => {
|
|
618
|
-
const src = state.src
|
|
619
|
-
const srcLen = max
|
|
620
|
-
const htmlEnabled = state.md.options.html
|
|
621
|
-
const dollarMath = opt.dollarMath
|
|
622
|
-
let n = start
|
|
623
|
-
let inlines = []
|
|
624
|
-
let hasText = false
|
|
625
|
-
let textStart = n
|
|
626
|
-
|
|
627
|
-
while (n < srcLen) {
|
|
628
|
-
let currentChar = src.charCodeAt(n)
|
|
629
|
-
|
|
630
|
-
if (!isAsciiPunctuationCode(currentChar)) {
|
|
631
|
-
const nextPunc = findNextAsciiPunctuation(src, n, srcLen)
|
|
632
|
-
if (nextPunc === -1) {
|
|
633
|
-
if (textStart < srcLen) {
|
|
634
|
-
pushInlines(inlines, textStart, srcLen - 1, srcLen - textStart, 'text')
|
|
635
|
-
}
|
|
636
|
-
break
|
|
637
|
-
}
|
|
638
|
-
if (nextPunc > n) {
|
|
639
|
-
hasText = true
|
|
640
|
-
n = nextPunc
|
|
641
|
-
currentChar = src.charCodeAt(n)
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// Unified escape check
|
|
646
|
-
let isEscaped = false
|
|
647
|
-
if (currentChar === CHAR_ASTERISK || currentChar === CHAR_BACKTICK ||
|
|
648
|
-
(dollarMath && currentChar === CHAR_DOLLAR) ||
|
|
649
|
-
(htmlEnabled && currentChar === CHAR_LT)) {
|
|
650
|
-
isEscaped = hasBackslash(state, n)
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Asterisk handling
|
|
654
|
-
if (currentChar === CHAR_ASTERISK) {
|
|
655
|
-
if (!isEscaped) {
|
|
656
|
-
hasText = processTextSegment(inlines, textStart, n, hasText)
|
|
657
|
-
if (n === srcLen - 1) {
|
|
658
|
-
pushInlines(inlines, n, n, 1, '')
|
|
659
|
-
break
|
|
660
|
-
}
|
|
661
|
-
let i = n + 1
|
|
662
|
-
while (i < srcLen && src.charCodeAt(i) === CHAR_ASTERISK) {
|
|
663
|
-
i++
|
|
664
|
-
}
|
|
665
|
-
if (i === srcLen) {
|
|
666
|
-
pushInlines(inlines, n, i - 1, i - n, '')
|
|
667
|
-
} else {
|
|
668
|
-
pushInlines(inlines, n, i - 1, i - n, '')
|
|
669
|
-
textStart = i
|
|
670
|
-
hasText = false
|
|
671
|
-
}
|
|
672
|
-
n = i
|
|
673
|
-
continue
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// Inline code (backticks)
|
|
678
|
-
if (currentChar === CHAR_BACKTICK) {
|
|
679
|
-
if (!isEscaped) {
|
|
680
|
-
const result = processSymbolPair(state, n, srcLen, CHAR_BACKTICK, '`', hasText, textStart,
|
|
681
|
-
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
682
|
-
if (result.shouldBreak) break
|
|
683
|
-
if (result.shouldContinue) {
|
|
684
|
-
n = result.newN
|
|
685
|
-
hasText = result.hasText
|
|
686
|
-
continue
|
|
687
|
-
}
|
|
688
|
-
hasText = result.hasText
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// Inline math ($...$)
|
|
693
|
-
if (dollarMath && currentChar === CHAR_DOLLAR) {
|
|
694
|
-
if (!isEscaped) {
|
|
695
|
-
const result = processSymbolPair(state, n, srcLen, CHAR_DOLLAR, '$', hasText, textStart,
|
|
696
|
-
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
697
|
-
if (result.shouldBreak) break
|
|
698
|
-
if (result.shouldContinue) {
|
|
699
|
-
n = result.newN
|
|
700
|
-
hasText = result.hasText
|
|
701
|
-
continue
|
|
702
|
-
}
|
|
703
|
-
hasText = result.hasText
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// HTML tags
|
|
708
|
-
if (htmlEnabled && currentChar === CHAR_LT) {
|
|
709
|
-
if (!isEscaped) {
|
|
710
|
-
const guardHtml = srcLen - n > 8192
|
|
711
|
-
const maxScanEnd = guardHtml ? Math.min(srcLen, n + 8192) : srcLen
|
|
712
|
-
let foundClosingTag = false
|
|
713
|
-
let i = n + 1
|
|
714
|
-
while (i < srcLen) {
|
|
715
|
-
i = src.indexOf('>', i)
|
|
716
|
-
if (i === -1 || i >= maxScanEnd) break
|
|
717
|
-
if (!hasBackslash(state, i)) {
|
|
718
|
-
hasText = processTextSegment(inlines, textStart, n, hasText)
|
|
719
|
-
let tag = src.slice(n + 1, i)
|
|
720
|
-
let tagType
|
|
721
|
-
if (tag.charCodeAt(0) === CHAR_SLASH) {
|
|
722
|
-
tag = tag.slice(1)
|
|
723
|
-
tagType = 'close'
|
|
724
|
-
} else {
|
|
725
|
-
tagType = 'open'
|
|
726
|
-
}
|
|
727
|
-
pushInlines(inlines, n, i, i - n + 1, 'html_inline', tag, tagType)
|
|
728
|
-
textStart = i + 1
|
|
729
|
-
hasText = false
|
|
730
|
-
n = i + 1
|
|
731
|
-
foundClosingTag = true
|
|
732
|
-
break
|
|
733
|
-
}
|
|
734
|
-
i += 1
|
|
735
|
-
}
|
|
736
|
-
if (foundClosingTag) {
|
|
737
|
-
continue
|
|
738
|
-
}
|
|
739
|
-
// If no closing tag found, treat as regular character to prevent infinite loops
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Regular character
|
|
744
|
-
hasText = true
|
|
745
|
-
if (n === srcLen - 1) {
|
|
746
|
-
pushInlines(inlines, textStart, n, n - textStart + 1, 'text')
|
|
747
|
-
break
|
|
748
|
-
}
|
|
749
|
-
n++
|
|
750
|
-
}
|
|
751
|
-
return inlines
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
const pushMark = (marks, opts) => {
|
|
755
|
-
// Maintain sorted order during insertion
|
|
756
|
-
const newMark = {
|
|
757
|
-
nest: opts.nest,
|
|
758
|
-
s: opts.s,
|
|
759
|
-
e: opts.e,
|
|
760
|
-
len: opts.len,
|
|
761
|
-
oLen: opts.oLen,
|
|
762
|
-
type: opts.type
|
|
763
|
-
}
|
|
764
|
-
if (marks.length === 0 || marks[marks.length - 1].s <= newMark.s) {
|
|
765
|
-
marks.push(newMark)
|
|
766
|
-
return
|
|
767
|
-
}
|
|
768
|
-
// Binary search for insertion point to maintain sorted order
|
|
769
|
-
let left = 0
|
|
770
|
-
let right = marks.length
|
|
771
|
-
while (left < right) {
|
|
772
|
-
const mid = Math.floor((left + right) / 2)
|
|
773
|
-
if (marks[mid].s <= newMark.s) {
|
|
774
|
-
left = mid + 1
|
|
775
|
-
} else {
|
|
776
|
-
right = mid
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
marks.splice(left, 0, newMark)
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
const setStrong = (state, inlines, marks, n, memo, opt, nestTracker, refRanges, inlineLinkRanges) => {
|
|
784
|
-
const hasInlineLinkRanges = inlineLinkRanges && inlineLinkRanges.length > 0
|
|
785
|
-
const hasRefRanges = refRanges && refRanges.length > 0
|
|
786
|
-
const inlinesLength = inlines.length
|
|
787
|
-
const leadingCompat = opt.leadingAsterisk === false
|
|
788
|
-
const conservativePunctuation = opt.disallowMixed === true
|
|
789
|
-
if (opt.disallowMixed === true) {
|
|
790
|
-
let i = n + 1
|
|
791
|
-
while (i < inlinesLength) {
|
|
792
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
793
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
794
|
-
|
|
795
|
-
if (inlines[i].len > 1) {
|
|
796
|
-
if (shouldBlockMixedLanguage(state, inlines, n, i)) {
|
|
797
|
-
return [n, 0]
|
|
798
|
-
}
|
|
799
|
-
break
|
|
800
|
-
}
|
|
801
|
-
i++
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
const strongOpenRange = hasRefRanges ? findRefRangeIndex(inlines[n].s, refRanges) : -1
|
|
806
|
-
const openLinkRange = hasInlineLinkRanges ? findInlineLinkRange(inlines[n].s, inlineLinkRanges) : null
|
|
807
|
-
let i = n + 1
|
|
808
|
-
let j = 0
|
|
809
|
-
let nest = 0
|
|
810
|
-
while (i < inlinesLength) {
|
|
811
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
812
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
813
|
-
|
|
814
|
-
if (hasInlineLinkRanges &&
|
|
815
|
-
hasInlineLinkLabelCrossing(inlineLinkRanges, inlines[n].ep + 1, inlines[i].sp)) {
|
|
816
|
-
i++
|
|
817
|
-
continue
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
const closeRange = hasRefRanges ? findRefRangeIndex(inlines[i].s, refRanges) : -1
|
|
821
|
-
if (strongOpenRange !== closeRange) { i++; continue }
|
|
822
|
-
|
|
823
|
-
const closeLinkRange = hasInlineLinkRanges ? findInlineLinkRange(inlines[i].s, inlineLinkRanges) : null
|
|
824
|
-
if (openLinkRange || closeLinkRange) {
|
|
825
|
-
if (!openLinkRange || !closeLinkRange || openLinkRange.id !== closeLinkRange.id || openLinkRange.kind !== closeLinkRange.kind) {
|
|
826
|
-
i++
|
|
827
|
-
continue
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
if (state.md && state.md.options && state.md.options.html && hasCodeTagInside(state, inlines, n, i)) {
|
|
832
|
-
return [n, nest]
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
836
|
-
if (nest === -1) return [n, nest]
|
|
837
|
-
|
|
838
|
-
if (inlines[i].len === 1 && inlines[n].len > 2) {
|
|
839
|
-
pushMark(marks, {
|
|
840
|
-
nest: nest,
|
|
841
|
-
s: inlines[n].ep,
|
|
842
|
-
e: inlines[n].ep,
|
|
843
|
-
len: 1,
|
|
844
|
-
oLen: inlines[n].len - 1,
|
|
845
|
-
type: 'em_open'
|
|
846
|
-
})
|
|
847
|
-
pushMark(marks, {
|
|
848
|
-
nest: nest,
|
|
849
|
-
s: inlines[i].sp,
|
|
850
|
-
e: inlines[i].ep,
|
|
851
|
-
len: 1,
|
|
852
|
-
oLen: inlines[i].len - 1,
|
|
853
|
-
type: 'em_close'
|
|
854
|
-
})
|
|
855
|
-
inlines[n].len -= 1
|
|
856
|
-
inlines[n].ep -= 1
|
|
857
|
-
inlines[i].len -= 1
|
|
858
|
-
if (inlines[i].len > 0) inlines[i].sp += 1
|
|
859
|
-
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges, inlineLinkRanges)
|
|
860
|
-
n = newN
|
|
861
|
-
nest = newNest
|
|
862
|
-
}
|
|
863
|
-
let strongNum = Math.trunc(Math.min(inlines[n].len, inlines[i].len) / 2)
|
|
864
|
-
|
|
865
|
-
if (inlines[i].len > 1) {
|
|
866
|
-
const hasJapaneseContext = isJapanese(state.src[inlines[n].s - 1] || '') || isJapanese(state.src[inlines[i].e + 1] || '')
|
|
867
|
-
const needsPunctuationCheck = (conservativePunctuation && !hasJapaneseContext) || hasHtmlLikePunctuation(state, inlines, n, i) || hasAngleBracketInside(state, inlines, n, i)
|
|
868
|
-
if (needsPunctuationCheck && hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges, hasRefRanges)) {
|
|
869
|
-
if (leadingCompat) {
|
|
870
|
-
return [n, nest]
|
|
871
|
-
}
|
|
872
|
-
if (memo.inlineMarkEnd) {
|
|
873
|
-
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
874
|
-
if (inlines[i].len === 0) { i++; continue }
|
|
875
|
-
} else {
|
|
876
|
-
return [n, nest]
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
j = 0
|
|
881
|
-
while (j < strongNum) {
|
|
882
|
-
pushMark(marks, {
|
|
883
|
-
nest: nest + strongNum - 1 - j,
|
|
884
|
-
s: inlines[n].ep - 1,
|
|
885
|
-
e: inlines[n].ep,
|
|
886
|
-
len: 2,
|
|
887
|
-
oLen: inlines[n].len - 2,
|
|
888
|
-
type: 'strong_open'
|
|
889
|
-
})
|
|
890
|
-
inlines[n].ep -= 2
|
|
891
|
-
inlines[n].len -= 2
|
|
892
|
-
pushMark(marks, {
|
|
893
|
-
nest: nest + strongNum - 1 - j,
|
|
894
|
-
s: inlines[i].sp,
|
|
895
|
-
e: inlines[i].sp + 1,
|
|
896
|
-
len: 2,
|
|
897
|
-
oLen: inlines[i].len - 2,
|
|
898
|
-
type: 'strong_close'
|
|
899
|
-
})
|
|
900
|
-
inlines[i].sp += 2
|
|
901
|
-
inlines[i].len -= 2
|
|
902
|
-
j++
|
|
903
|
-
}
|
|
904
|
-
if (inlines[n].len === 0) return [n, nest]
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if (inlines[n].len === 1 && inlines[i].len > 0) {
|
|
908
|
-
nest++
|
|
909
|
-
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, nest, nestTracker, refRanges, inlineLinkRanges)
|
|
910
|
-
n = newN
|
|
911
|
-
nest = newNest
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
i++
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (n == 0 && memo.inlineMarkEnd) {
|
|
918
|
-
marks.push(...createMarks(state, inlines, n + 1, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
919
|
-
}
|
|
920
|
-
return [n, nest]
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const checkInsideTags = (inlines, i, memo) => {
|
|
924
|
-
if (inlines[i].tag === undefined) return 0
|
|
925
|
-
const tagName = inlines[i].tag[0].toLowerCase()
|
|
926
|
-
if (memo.htmlTags[tagName] === undefined) {
|
|
927
|
-
memo.htmlTags[tagName] = 0
|
|
928
|
-
}
|
|
929
|
-
const tagType = inlines[i].tag[1]
|
|
930
|
-
if (tagType === 'open') {
|
|
931
|
-
memo.htmlTags[tagName] += 1
|
|
932
|
-
memo.htmlTagDepth += 1
|
|
933
|
-
}
|
|
934
|
-
if (tagType === 'close') {
|
|
935
|
-
memo.htmlTags[tagName] -= 1
|
|
936
|
-
memo.htmlTagDepth -= 1
|
|
937
|
-
}
|
|
938
|
-
if (memo.htmlTags[tagName] < 0 || memo.htmlTagDepth < 0) {
|
|
939
|
-
return -1
|
|
940
|
-
}
|
|
941
|
-
return memo.htmlTagDepth === 0 ? 1 : 0
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// Check if character is ASCII punctuation or space
|
|
945
|
-
// Covers: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and space
|
|
946
|
-
const isPunctuation = (ch) => {
|
|
947
|
-
if (!ch) return false
|
|
948
|
-
const code = ch.charCodeAt(0)
|
|
949
|
-
// ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
|
950
|
-
return (code >= 33 && code <= 47) || (code >= 58 && code <= 64) ||
|
|
951
|
-
(code >= 91 && code <= 96) || (code >= 123 && code <= 126) || code === 32
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const isAsciiPunctuationCode = (code) => {
|
|
955
|
-
if (code < 33 || code > 126) return false
|
|
956
|
-
return (code <= 47) || (code >= 58 && code <= 64) || (code >= 91 && code <= 96) || (code >= 123)
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
const isUnicodePunctuation = (ch) => {
|
|
960
|
-
if (!ch) return false
|
|
961
|
-
return /\p{P}/u.test(ch)
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Check if character is Japanese (hiragana, katakana, kanji, CJK punctuation/fullwidth)
|
|
965
|
-
// Uses fast Unicode range checks for common cases, falls back to REG_JAPANESE for complex Unicode
|
|
966
|
-
const isJapanese = (ch) => {
|
|
967
|
-
if (!ch) return false
|
|
968
|
-
const code = ch.charCodeAt(0)
|
|
969
|
-
// Fast ASCII check first
|
|
970
|
-
if (code < 128) return false
|
|
971
|
-
// Hiragana: U+3040-U+309F, Katakana: U+30A0-U+30FF, Kanji: U+4E00-U+9FAF
|
|
972
|
-
return (code >= 0x3040 && code <= 0x309F) ||
|
|
973
|
-
(code >= 0x30A0 && code <= 0x30FF) ||
|
|
974
|
-
(code >= 0x4E00 && code <= 0x9FAF) ||
|
|
975
|
-
// Fallback to regex for complex Unicode cases
|
|
976
|
-
REG_JAPANESE.test(ch)
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
const hasJapaneseText = (str) => {
|
|
980
|
-
if (!str) return false
|
|
981
|
-
return REG_JAPANESE.test(str)
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
const resolveLeadingAsterisk = (state, opt, start, max) => {
|
|
985
|
-
const modeRaw = opt.mode || 'japanese-only'
|
|
986
|
-
const mode = typeof modeRaw === 'string' ? modeRaw.toLowerCase() : 'japanese-only'
|
|
987
|
-
if (mode === 'aggressive') return true
|
|
988
|
-
if (mode === 'compatible') return false
|
|
989
|
-
let hasJapanese = state.__strongJaHasJapanese
|
|
990
|
-
if (hasJapanese === undefined) {
|
|
991
|
-
hasJapanese = hasJapaneseText(state.src.slice(0, max))
|
|
992
|
-
state.__strongJaHasJapanese = hasJapanese
|
|
993
|
-
}
|
|
994
|
-
if (opt.disallowMixed === true) return hasJapanese
|
|
995
|
-
|
|
996
|
-
return hasJapanese
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
// Check if character is English (letters, numbers) or other non-Japanese characters
|
|
1000
|
-
// Uses REG_JAPANESE to exclude Japanese characters
|
|
1001
|
-
const isEnglish = (ch) => {
|
|
1002
|
-
if (!ch) return false
|
|
1003
|
-
const code = ch.charCodeAt(0)
|
|
1004
|
-
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
|
|
1005
|
-
return true
|
|
1006
|
-
}
|
|
1007
|
-
if (code < 128) {
|
|
1008
|
-
return code === CHAR_SPACE || (code > 126)
|
|
1009
|
-
}
|
|
1010
|
-
return !REG_JAPANESE.test(ch)
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
const shouldBlockMixedLanguage = (state, inlines, n, i) => {
|
|
1014
|
-
const src = state.src
|
|
1015
|
-
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
1016
|
-
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
1017
|
-
|
|
1018
|
-
const isEnglishPrefix = isEnglish(openPrevChar)
|
|
1019
|
-
const isEnglishSuffix = isEnglish(closeNextChar)
|
|
1020
|
-
if (!isEnglishPrefix && !isEnglishSuffix) {
|
|
1021
|
-
return false
|
|
1022
|
-
}
|
|
1023
|
-
return hasMarkdownHtmlPattern(src, inlines[n].e + 1, inlines[i].s)
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const hasPunctuationOrNonJapanese = (state, inlines, n, i, opt, refRanges, hasRefRanges) => {
|
|
1027
|
-
const src = state.src
|
|
1028
|
-
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
1029
|
-
const openNextChar = src[inlines[n].e + 1] || ''
|
|
1030
|
-
let checkOpenNextChar = isPunctuation(openNextChar)
|
|
1031
|
-
if (!checkOpenNextChar && opt.leadingAsterisk === false && isUnicodePunctuation(openNextChar)) {
|
|
1032
|
-
checkOpenNextChar = true
|
|
1033
|
-
}
|
|
1034
|
-
if (hasRefRanges && checkOpenNextChar && (openNextChar === '[' || openNextChar === ']')) {
|
|
1035
|
-
const openNextRange = findRefRangeIndex(inlines[n].e + 1, refRanges)
|
|
1036
|
-
if (openNextRange !== -1) {
|
|
1037
|
-
checkOpenNextChar = false
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
const closePrevChar = src[inlines[i].s - 1] || ''
|
|
1041
|
-
let checkClosePrevChar = isPunctuation(closePrevChar)
|
|
1042
|
-
if (!checkClosePrevChar && opt.leadingAsterisk === false && isUnicodePunctuation(closePrevChar)) {
|
|
1043
|
-
checkClosePrevChar = true
|
|
1044
|
-
}
|
|
1045
|
-
if (hasRefRanges && checkClosePrevChar && (closePrevChar === '[' || closePrevChar === ']')) {
|
|
1046
|
-
const closePrevRange = findRefRangeIndex(inlines[i].s - 1, refRanges)
|
|
1047
|
-
if (closePrevRange !== -1) {
|
|
1048
|
-
checkClosePrevChar = false
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
1052
|
-
const isLastInline = i === inlines.length - 1
|
|
1053
|
-
let checkCloseNextChar = isLastInline || isPunctuation(closeNextChar) || closeNextChar === '\n'
|
|
1054
|
-
if (!checkCloseNextChar && opt.leadingAsterisk === false && isUnicodePunctuation(closeNextChar)) {
|
|
1055
|
-
checkCloseNextChar = true
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
if (opt.disallowMixed === false) {
|
|
1059
|
-
if (isEnglish(openPrevChar) || isEnglish(closeNextChar)) {
|
|
1060
|
-
if (hasMarkdownHtmlPattern(src, inlines[n].e + 1, inlines[i].s)) {
|
|
1061
|
-
return false
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
const result = (checkOpenNextChar || checkClosePrevChar) && !checkCloseNextChar && !(isJapanese(openPrevChar) || isJapanese(closeNextChar))
|
|
1067
|
-
return result
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
const hasHtmlLikePunctuation = (state, inlines, n, i) => {
|
|
1071
|
-
const src = state.src
|
|
1072
|
-
const chars = [
|
|
1073
|
-
src[inlines[n].e + 1] || '',
|
|
1074
|
-
src[inlines[i].s - 1] || '',
|
|
1075
|
-
src[inlines[i].e + 1] || ''
|
|
1076
|
-
]
|
|
1077
|
-
for (let idx = 0; idx < chars.length; idx++) {
|
|
1078
|
-
const ch = chars[idx]
|
|
1079
|
-
if (ch === '<' || ch === '>') return true
|
|
1080
|
-
}
|
|
1081
|
-
return false
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
const hasAngleBracketInside = (state, inlines, n, i) => {
|
|
1085
|
-
const src = state.src
|
|
1086
|
-
const start = inlines[n].s
|
|
1087
|
-
const end = inlines[i].e
|
|
1088
|
-
const ltPos = src.indexOf('<', start)
|
|
1089
|
-
if (ltPos !== -1 && ltPos <= end) return true
|
|
1090
|
-
const gtPos = src.indexOf('>', start)
|
|
1091
|
-
return gtPos !== -1 && gtPos <= end
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
const hasCodeTagInside = (state, inlines, n, i) => {
|
|
1095
|
-
const src = state.src
|
|
1096
|
-
const start = inlines[n].s
|
|
1097
|
-
const end = inlines[i].e
|
|
1098
|
-
const codeOpen = src.indexOf('<code', start)
|
|
1099
|
-
if (codeOpen !== -1 && codeOpen <= end) return true
|
|
1100
|
-
const codeClose = src.indexOf('</code', start)
|
|
1101
|
-
if (codeClose !== -1 && codeClose <= end) return true
|
|
1102
|
-
const preOpen = src.indexOf('<pre', start)
|
|
1103
|
-
if (preOpen !== -1 && preOpen <= end) return true
|
|
1104
|
-
const preClose = src.indexOf('</pre', start)
|
|
1105
|
-
return preClose !== -1 && preClose <= end
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
const setEm = (state, inlines, marks, n, memo, opt, sNest, nestTracker, refRanges, inlineLinkRanges) => {
|
|
1109
|
-
const hasInlineLinkRanges = inlineLinkRanges && inlineLinkRanges.length > 0
|
|
1110
|
-
const hasRefRanges = refRanges && refRanges.length > 0
|
|
1111
|
-
const inlinesLength = inlines.length
|
|
1112
|
-
const emOpenRange = hasRefRanges ? findRefRangeIndex(inlines[n].s, refRanges) : -1
|
|
1113
|
-
const openLinkRange = hasInlineLinkRanges ? findInlineLinkRange(inlines[n].s, inlineLinkRanges) : null
|
|
1114
|
-
const leadingCompat = opt.leadingAsterisk === false
|
|
1115
|
-
const conservativePunctuation = leadingCompat || opt.disallowMixed === true
|
|
1116
|
-
if (opt.disallowMixed === true && !sNest) {
|
|
1117
|
-
let i = n + 1
|
|
1118
|
-
while (i < inlinesLength) {
|
|
1119
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
1120
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
1121
|
-
|
|
1122
|
-
if (inlines[i].len > 0) {
|
|
1123
|
-
if (shouldBlockMixedLanguage(state, inlines, n, i)) {
|
|
1124
|
-
return [n, 0]
|
|
1125
|
-
}
|
|
1126
|
-
break
|
|
1127
|
-
}
|
|
1128
|
-
i++
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
let i = n + 1
|
|
1133
|
-
let nest = 0
|
|
1134
|
-
let strongPNum = 0
|
|
1135
|
-
let insideTagsIsClose = 1
|
|
1136
|
-
while (i < inlinesLength) {
|
|
1137
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
1138
|
-
if (!sNest && inlines[i].type === 'html_inline') {
|
|
1139
|
-
inlines[i].check = true
|
|
1140
|
-
insideTagsIsClose = checkInsideTags(inlines, i, memo)
|
|
1141
|
-
if (insideTagsIsClose === -1) return [n, nest]
|
|
1142
|
-
if (insideTagsIsClose === 0) { i++; continue }
|
|
1143
|
-
}
|
|
1144
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
1145
|
-
|
|
1146
|
-
if (hasInlineLinkRanges &&
|
|
1147
|
-
hasInlineLinkLabelCrossing(inlineLinkRanges, inlines[n].ep + 1, inlines[i].sp)) {
|
|
1148
|
-
i++
|
|
1149
|
-
continue
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
const closeRange = hasRefRanges ? findRefRangeIndex(inlines[i].s, refRanges) : -1
|
|
1153
|
-
if (emOpenRange !== closeRange) {
|
|
1154
|
-
i++
|
|
1155
|
-
continue
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
const closeLinkRange = hasInlineLinkRanges ? findInlineLinkRange(inlines[i].s, inlineLinkRanges) : null
|
|
1159
|
-
if (openLinkRange || closeLinkRange) {
|
|
1160
|
-
if (!openLinkRange || !closeLinkRange || openLinkRange.id !== closeLinkRange.id || openLinkRange.kind !== closeLinkRange.kind) {
|
|
1161
|
-
i++
|
|
1162
|
-
continue
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
if (state.md && state.md.options && state.md.options.html && hasCodeTagInside(state, inlines, n, i)) {
|
|
1167
|
-
return [n, nest]
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
const emNum = Math.min(inlines[n].len, inlines[i].len)
|
|
1171
|
-
|
|
1172
|
-
if (!sNest && emNum !== 1) return [n, sNest, memo]
|
|
1173
|
-
|
|
1174
|
-
const isMarkerAtStartAndEnd = memo.inlineMarkStart &&
|
|
1175
|
-
i === inlinesLength - 1 &&
|
|
1176
|
-
inlines[i].len > 1
|
|
1177
|
-
if (!sNest && inlines[i].len === 2 && !isMarkerAtStartAndEnd) {
|
|
1178
|
-
strongPNum++
|
|
1179
|
-
i++
|
|
1180
|
-
continue
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
if (sNest) {
|
|
1184
|
-
nest = sNest - 1
|
|
1185
|
-
} else {
|
|
1186
|
-
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
1187
|
-
}
|
|
1188
|
-
if (nest === -1) return [n, nest]
|
|
1189
|
-
|
|
1190
|
-
if (emNum === 1) {
|
|
1191
|
-
const needsPunctuationCheckClose = conservativePunctuation || hasHtmlLikePunctuation(state, inlines, n, i) || hasAngleBracketInside(state, inlines, n, i)
|
|
1192
|
-
if (needsPunctuationCheckClose && hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges, hasRefRanges)) {
|
|
1193
|
-
if (leadingCompat) {
|
|
1194
|
-
return [n, nest]
|
|
1195
|
-
}
|
|
1196
|
-
if (memo.inlineMarkEnd) {
|
|
1197
|
-
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
1198
|
-
|
|
1199
|
-
if (inlines[i].len === 0) { i++; continue }
|
|
1200
|
-
} else {
|
|
1201
|
-
return [n, nest]
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
if (inlines[i].len < 1) {
|
|
1205
|
-
i++; continue;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
pushMark(marks, {
|
|
1209
|
-
nest: nest,
|
|
1210
|
-
s: inlines[n].ep,
|
|
1211
|
-
e: inlines[n].ep,
|
|
1212
|
-
len: 1,
|
|
1213
|
-
oLen: inlines[n].len - 1,
|
|
1214
|
-
type: 'em_open'
|
|
1215
|
-
})
|
|
1216
|
-
inlines[n].ep -= 1
|
|
1217
|
-
inlines[n].len -= 1
|
|
1218
|
-
|
|
1219
|
-
if (strongPNum % 2 === 0 || inlines[i].len < 2) {
|
|
1220
|
-
pushMark(marks, {
|
|
1221
|
-
nest: nest,
|
|
1222
|
-
s: inlines[i].sp,
|
|
1223
|
-
e: inlines[i].sp,
|
|
1224
|
-
len: 1,
|
|
1225
|
-
oLen: inlines[i].len - 1,
|
|
1226
|
-
type: 'em_close'
|
|
1227
|
-
})
|
|
1228
|
-
inlines[i].sp += 1
|
|
1229
|
-
} else {
|
|
1230
|
-
pushMark(marks, {
|
|
1231
|
-
nest: nest,
|
|
1232
|
-
s: inlines[i].ep,
|
|
1233
|
-
e: inlines[i].ep,
|
|
1234
|
-
len: 1,
|
|
1235
|
-
oLen: inlines[i].len - 1,
|
|
1236
|
-
type: 'em_close'
|
|
1237
|
-
})
|
|
1238
|
-
inlines[i].sp = inlines[i].ep - 1
|
|
1239
|
-
inlines[i].ep -= 1
|
|
1240
|
-
}
|
|
1241
|
-
inlines[i].len -= 1
|
|
1242
|
-
if (inlines[n].len === 0) return [n, nest]
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
i++
|
|
1246
|
-
}
|
|
1247
|
-
return [n, nest]
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
const setText = (inlines, marks, n, nest) => {
|
|
1251
|
-
pushMark(marks, {
|
|
1252
|
-
nest: nest,
|
|
1253
|
-
s: inlines[n].sp,
|
|
1254
|
-
e: inlines[n].ep,
|
|
1255
|
-
len: inlines[n].len,
|
|
1256
|
-
oLen: -1,
|
|
1257
|
-
type: 'text'
|
|
1258
|
-
})
|
|
1259
|
-
inlines[n].len = 0
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
// Nest state management
|
|
1263
|
-
const createNestTracker = () => {
|
|
1264
|
-
return {
|
|
1265
|
-
strongNest: 0,
|
|
1266
|
-
emNest: 0,
|
|
1267
|
-
markIndex: 0
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
const updateNestTracker = (tracker, marks, targetPos) => {
|
|
1272
|
-
while (tracker.markIndex < marks.length && marks[tracker.markIndex].s <= targetPos) {
|
|
1273
|
-
const mark = marks[tracker.markIndex]
|
|
1274
|
-
if (mark.type === 'strong_open') tracker.strongNest++
|
|
1275
|
-
else if (mark.type === 'strong_close') tracker.strongNest--
|
|
1276
|
-
else if (mark.type === 'em_open') tracker.emNest++
|
|
1277
|
-
else if (mark.type === 'em_close') tracker.emNest--
|
|
1278
|
-
tracker.markIndex++
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
const checkNest = (inlines, marks, n, i, nestTracker) => {
|
|
1283
|
-
if (marks.length === 0) return 1
|
|
1284
|
-
// Update nest state up to current position
|
|
1285
|
-
updateNestTracker(nestTracker, marks, inlines[n].s)
|
|
1286
|
-
|
|
1287
|
-
const parentNest = nestTracker.strongNest + nestTracker.emNest
|
|
1288
|
-
// Check if there's a conflicting close mark before the end position
|
|
1289
|
-
let parentCloseN = nestTracker.markIndex
|
|
1290
|
-
while (parentCloseN < marks.length) {
|
|
1291
|
-
if (marks[parentCloseN].nest === parentNest) break
|
|
1292
|
-
parentCloseN++
|
|
1293
|
-
}
|
|
1294
|
-
if (parentCloseN < marks.length && marks[parentCloseN].s < inlines[i].s) {
|
|
1295
|
-
return -1
|
|
1296
|
-
}
|
|
1297
|
-
return parentNest + 1
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
const createMarks = (state, inlines, start, end, memo, opt, refRanges, inlineLinkRanges) => {
|
|
1301
|
-
let marks = []
|
|
1302
|
-
let n = start
|
|
1303
|
-
const nestTracker = createNestTracker()
|
|
1304
|
-
|
|
1305
|
-
while (n < end) {
|
|
1306
|
-
if (inlines[n].type !== '') { n++; continue }
|
|
1307
|
-
let nest = 0
|
|
1308
|
-
|
|
1309
|
-
if (inlines[n].len > 1) {
|
|
1310
|
-
const [newN, newNest] = setStrong(state, inlines, marks, n, memo, opt, nestTracker, refRanges, inlineLinkRanges)
|
|
1311
|
-
n = newN
|
|
1312
|
-
nest = newNest
|
|
1313
|
-
}
|
|
1314
|
-
if (inlines[n].len !== 0) {
|
|
1315
|
-
const [newN2, newNest2] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges, inlineLinkRanges)
|
|
1316
|
-
n = newN2
|
|
1317
|
-
nest = newNest2
|
|
1318
|
-
}
|
|
1319
|
-
if (inlines[n].len !== 0) {
|
|
1320
|
-
setText(inlines, marks, n, nest)
|
|
1321
|
-
}
|
|
1322
|
-
n++
|
|
1323
|
-
}
|
|
1324
|
-
return marks
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
const mergeInlinesAndMarks = (inlines, marks) => {
|
|
1328
|
-
// marks array is already sorted, skip sorting
|
|
1329
|
-
const merged = []
|
|
1330
|
-
let markIndex = 0
|
|
1331
|
-
for (const token of inlines) {
|
|
1332
|
-
if (token.type === '') {
|
|
1333
|
-
while (markIndex < marks.length && marks[markIndex].s >= token.s && marks[markIndex].e <= token.e) {
|
|
1334
|
-
merged.push(marks[markIndex])
|
|
1335
|
-
markIndex++
|
|
1336
|
-
}
|
|
1337
|
-
} else {
|
|
1338
|
-
merged.push(token)
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
while (markIndex < marks.length) {
|
|
1342
|
-
merged.push(marks[markIndex++])
|
|
1343
|
-
}
|
|
1344
|
-
return merged
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
const isWhitespaceToken = (token) => {
|
|
1348
|
-
if (!token || token.type !== 'text') return false
|
|
1349
|
-
const content = token.content
|
|
1350
|
-
if (!content) return true
|
|
1351
|
-
for (let i = 0; i < content.length; i++) {
|
|
1352
|
-
if (!isWhiteSpace(content.charCodeAt(i))) return false
|
|
1353
|
-
}
|
|
1354
|
-
return true
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
const hasMarkdownHtmlPattern = (src, start, end) => {
|
|
1358
|
-
if (start >= end) return false
|
|
1359
|
-
const first = src.charCodeAt(start)
|
|
1360
|
-
const last = src.charCodeAt(end - 1)
|
|
1361
|
-
if (first === CHAR_OPEN_BRACKET) {
|
|
1362
|
-
if (last !== CHAR_CLOSE_PAREN) return false
|
|
1363
|
-
} else if (first === CHAR_LT) {
|
|
1364
|
-
if (last !== CHAR_GT) return false
|
|
1365
|
-
} else if (first === CHAR_BACKTICK) {
|
|
1366
|
-
if (last !== CHAR_BACKTICK) return false
|
|
1367
|
-
} else if (first === CHAR_DOLLAR) {
|
|
1368
|
-
if (last !== CHAR_DOLLAR) return false
|
|
1369
|
-
} else {
|
|
1370
|
-
return false
|
|
1371
|
-
}
|
|
1372
|
-
return REG_MARKDOWN_HTML.test(src.slice(start, end))
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
const strongJa = (state, silent, opt) => {
|
|
1376
|
-
if (silent) return false
|
|
1377
|
-
const start = state.pos
|
|
1378
|
-
let max = state.posMax
|
|
1379
|
-
const originalMax = max
|
|
1380
|
-
const src = state.src
|
|
1381
|
-
let attributesSrc
|
|
1382
|
-
if (start > max) return false
|
|
1383
|
-
if (src.charCodeAt(start) !== CHAR_ASTERISK) return false
|
|
1384
|
-
if (hasBackslash(state, start)) return false
|
|
1385
|
-
|
|
1386
|
-
const attrsEnabled = opt.mditAttrs && hasMditAttrs(state)
|
|
1387
|
-
|
|
1388
|
-
const leadingAsterisk = resolveLeadingAsterisk(state, opt, start, originalMax)
|
|
1389
|
-
|
|
1390
|
-
if (leadingAsterisk === false) {
|
|
1391
|
-
return false
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
const runtimeOpt = leadingAsterisk === opt.leadingAsterisk
|
|
1395
|
-
? opt
|
|
1396
|
-
: { ...opt, leadingAsterisk }
|
|
1397
|
-
|
|
1398
|
-
if (start === 0) {
|
|
1399
|
-
state.__strongJaRefRangeCache = null
|
|
1400
|
-
state.__strongJaInlineLinkRangeCache = null
|
|
1401
|
-
state.__strongJaBackslashCache = undefined
|
|
1402
|
-
state.__strongJaHasBackslash = undefined
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
if (attrsEnabled) {
|
|
1406
|
-
let attrCandidate = false
|
|
1407
|
-
let probe = originalMax - 1
|
|
1408
|
-
while (probe >= start) {
|
|
1409
|
-
const code = src.charCodeAt(probe)
|
|
1410
|
-
if (code === CHAR_CLOSE_CURLY) {
|
|
1411
|
-
attrCandidate = true
|
|
1412
|
-
break
|
|
1413
|
-
}
|
|
1414
|
-
if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_NEWLINE) {
|
|
1415
|
-
probe--
|
|
1416
|
-
continue
|
|
1417
|
-
}
|
|
1418
|
-
break
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
if (attrCandidate) {
|
|
1422
|
-
const attrScanTarget = originalMax === src.length ? src : src.slice(0, originalMax)
|
|
1423
|
-
attributesSrc = attrScanTarget.match(/((\n)? *){([^{}\n!@#%^&*()]+?)} *$/)
|
|
1424
|
-
if (attributesSrc && attributesSrc[3] !== '.') {
|
|
1425
|
-
max = attrScanTarget.slice(0, attributesSrc.index).length
|
|
1426
|
-
if (attributesSrc[2] === '\n') {
|
|
1427
|
-
max = attrScanTarget.slice(0, attributesSrc.index - 1).length
|
|
1428
|
-
}
|
|
1429
|
-
if (hasBackslash(state, attributesSrc.index) && attributesSrc[2] === '' && attributesSrc[1].length === 0) {
|
|
1430
|
-
max = state.posMax
|
|
1431
|
-
}
|
|
1432
|
-
} else {
|
|
1433
|
-
const endCurlyKet = attrScanTarget.match(/(\n *){([^{}\n!@#%^&*()]*?)}.*(} *?)$/)
|
|
1434
|
-
if (endCurlyKet) {
|
|
1435
|
-
max -= endCurlyKet[3].length
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
if (state.__strongJaHasCollapsedRefs === undefined) {
|
|
1442
|
-
state.__strongJaHasCollapsedRefs = src.indexOf('[') !== -1 &&
|
|
1443
|
-
/\[[^\]]*\]\s*\[[^\]]*\]/.test(src)
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
if (state.__strongJaReferenceCount === undefined) {
|
|
1447
|
-
const references = state.env && state.env.references
|
|
1448
|
-
state.__strongJaReferenceCount = references ? Object.keys(references).length : 0
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
let refRanges = []
|
|
1452
|
-
const hasReferenceDefinitions = state.__strongJaReferenceCount > 0
|
|
1453
|
-
const refScanStart = 0
|
|
1454
|
-
if (hasReferenceDefinitions) {
|
|
1455
|
-
const firstRefBracket = state.src.indexOf('[', refScanStart)
|
|
1456
|
-
if (firstRefBracket !== -1 && firstRefBracket < max) {
|
|
1457
|
-
const refCache = state.__strongJaRefRangeCache
|
|
1458
|
-
if (refCache && refCache.max === max && refCache.start === refScanStart) {
|
|
1459
|
-
refRanges = refCache.ranges
|
|
1460
|
-
} else {
|
|
1461
|
-
refRanges = computeReferenceRanges(state, refScanStart, max)
|
|
1462
|
-
state.__strongJaRefRangeCache = { start: refScanStart, max, ranges: refRanges }
|
|
1463
|
-
}
|
|
1464
|
-
if (refRanges.length > 0) {
|
|
1465
|
-
state.__strongJaHasCollapsedRefs = true
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
let inlineLinkRanges = null
|
|
1471
|
-
const inlineLinkScanStart = 0
|
|
1472
|
-
const inlineLinkCandidatePos = state.src.indexOf('](', inlineLinkScanStart)
|
|
1473
|
-
const hasInlineLinkCandidate = inlineLinkCandidatePos !== -1 && inlineLinkCandidatePos < max
|
|
1474
|
-
if (hasInlineLinkCandidate) {
|
|
1475
|
-
const inlineCache = state.__strongJaInlineLinkRangeCache
|
|
1476
|
-
if (inlineCache && inlineCache.max === max && inlineCache.start === inlineLinkScanStart) {
|
|
1477
|
-
inlineLinkRanges = inlineCache.ranges
|
|
1478
|
-
} else {
|
|
1479
|
-
inlineLinkRanges = computeInlineLinkRanges(state, inlineLinkScanStart, max)
|
|
1480
|
-
state.__strongJaInlineLinkRangeCache = { start: inlineLinkScanStart, max, ranges: inlineLinkRanges }
|
|
1481
|
-
}
|
|
1482
|
-
if (inlineLinkRanges.length > 0) {
|
|
1483
|
-
state.__strongJaHasInlineLinks = true
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
let inlines = createInlines(state, start, max, runtimeOpt)
|
|
1487
|
-
|
|
1488
|
-
const memo = {
|
|
1489
|
-
html: state.md.options.html,
|
|
1490
|
-
htmlTags: {},
|
|
1491
|
-
htmlTagDepth: 0,
|
|
1492
|
-
inlineMarkStart: src.charCodeAt(0) === CHAR_ASTERISK,
|
|
1493
|
-
inlineMarkEnd: src.charCodeAt(max - 1) === CHAR_ASTERISK,
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
let marks = createMarks(state, inlines, 0, inlines.length, memo, runtimeOpt, refRanges, inlineLinkRanges)
|
|
1497
|
-
|
|
1498
|
-
inlines = mergeInlinesAndMarks(inlines, marks)
|
|
1499
|
-
|
|
1500
|
-
setToken(state, inlines, runtimeOpt, attrsEnabled)
|
|
1501
|
-
|
|
1502
|
-
if (inlineLinkRanges && inlineLinkRanges.length > 0) {
|
|
1503
|
-
const labelSources = []
|
|
1504
|
-
for (let idx = 0; idx < inlineLinkRanges.length; idx++) {
|
|
1505
|
-
const range = inlineLinkRanges[idx]
|
|
1506
|
-
if (range.kind !== 'label') continue
|
|
1507
|
-
labelSources.push(src.slice(range.start + 1, range.end))
|
|
1508
|
-
}
|
|
1509
|
-
if (labelSources.length > 0) {
|
|
1510
|
-
restoreLabelWhitespace(state.tokens, labelSources)
|
|
1511
|
-
state.tokens.__strongJaInlineLabelSources = labelSources
|
|
1512
|
-
state.tokens.__strongJaInlineLabelIndex = 0
|
|
1513
|
-
if (state.env) {
|
|
1514
|
-
if (!state.env.__strongJaInlineLabelSourceList) {
|
|
1515
|
-
state.env.__strongJaInlineLabelSourceList = []
|
|
1516
|
-
}
|
|
1517
|
-
state.env.__strongJaInlineLabelSourceList.push(labelSources)
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
const needsInlineLinkFix = state.__strongJaHasInlineLinks === true
|
|
1523
|
-
const needsCollapsedRefFix = state.__strongJaHasCollapsedRefs === true
|
|
1524
|
-
if ((needsCollapsedRefFix || needsInlineLinkFix) && !state.__strongJaPostProcessRegistered) {
|
|
1525
|
-
registerPostProcessTarget(state)
|
|
1526
|
-
state.__strongJaPostProcessRegistered = true
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
if (attrsEnabled && max !== state.posMax) {
|
|
1530
|
-
if (!attributesSrc) {
|
|
1531
|
-
state.pos = max
|
|
1532
|
-
return true
|
|
1533
|
-
}
|
|
1534
|
-
state.pos = attributesSrc[1].length > 1 ? max + attributesSrc[1].length : max
|
|
1535
|
-
return true
|
|
1536
|
-
}
|
|
1537
|
-
state.pos = max
|
|
1538
|
-
return true
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
// Collapsed reference helpers
|
|
1542
|
-
const buildReferenceLabelRange = (tokens, startIdx, endIdx) => {
|
|
1543
|
-
if (startIdx > endIdx) return ''
|
|
1544
|
-
let label = ''
|
|
1545
|
-
for (let idx = startIdx; idx <= endIdx; idx++) {
|
|
1546
|
-
const token = tokens[idx]
|
|
1547
|
-
if (!token) continue
|
|
1548
|
-
if (token.type === 'text' || token.type === 'code_inline') {
|
|
1549
|
-
label += token.content
|
|
1550
|
-
} else if (token.type === 'softbreak' || token.type === 'hardbreak') {
|
|
1551
|
-
label += ' '
|
|
1552
|
-
} else if (token.type && token.type.endsWith('_open') && token.markup) {
|
|
1553
|
-
label += token.markup
|
|
1554
|
-
} else if (token.type && token.type.endsWith('_close') && token.markup) {
|
|
1555
|
-
label += token.markup
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
return label
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
const cleanLabelText = (label) => {
|
|
1562
|
-
if (label.indexOf('*') === -1 && label.indexOf('_') === -1) return label
|
|
1563
|
-
return label.replace(/^[*_]+/, '').replace(/[*_]+$/, '')
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
const normalizeReferenceCandidate = (state, text, { useClean = false } = {}) => {
|
|
1567
|
-
const source = useClean ? cleanLabelText(text) : text
|
|
1568
|
-
return normalizeRefKey(state, source)
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
const getNormalizeRef = (state) => {
|
|
1572
|
-
if (state.__strongJaNormalizeRef) return state.__strongJaNormalizeRef
|
|
1573
|
-
const normalize = state.md && state.md.utils && state.md.utils.normalizeReference
|
|
1574
|
-
? state.md.utils.normalizeReference
|
|
1575
|
-
: (str) => str.trim().replace(/\s+/g, ' ').toUpperCase()
|
|
1576
|
-
state.__strongJaNormalizeRef = normalize
|
|
1577
|
-
return normalize
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
const normalizeRefKey = (state, label) => {
|
|
1581
|
-
return getNormalizeRef(state)(label)
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
const adjustTokenLevels = (tokens, startIdx, endIdx, delta) => {
|
|
1585
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
1586
|
-
if (tokens[i]) tokens[i].level += delta
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
const cloneTextToken = (source, content) => {
|
|
1591
|
-
const newToken = new Token('text', '', 0)
|
|
1592
|
-
Object.assign(newToken, source)
|
|
1593
|
-
newToken.content = content
|
|
1594
|
-
if (source.meta) newToken.meta = { ...source.meta }
|
|
1595
|
-
if (source.map) newToken.map = source.map
|
|
1596
|
-
return newToken
|
|
6
|
+
const buildNoLinkCacheKey = (opt) => {
|
|
7
|
+
const mode = resolveMode(opt)
|
|
8
|
+
const mditAttrs = opt && opt.mditAttrs === false ? '0' : '1'
|
|
9
|
+
const mdBreaks = opt && opt.mdBreaks === true ? '1' : '0'
|
|
10
|
+
return `${mode}|${mditAttrs}|${mdBreaks}`
|
|
1597
11
|
}
|
|
1598
12
|
|
|
1599
|
-
|
|
1600
|
-
const
|
|
1601
|
-
const
|
|
1602
|
-
if (!
|
|
1603
|
-
|
|
1604
|
-
if (token.__strongJaHasBracket === false) return false
|
|
1605
|
-
const content = token.content
|
|
1606
|
-
if (!content) {
|
|
1607
|
-
token.__strongJaHasBracket = false
|
|
1608
|
-
token.__strongJaBracketAtomic = false
|
|
1609
|
-
return false
|
|
1610
|
-
}
|
|
1611
|
-
if (token.__strongJaHasBracket !== true) {
|
|
1612
|
-
if (content.indexOf('[') === -1 && content.indexOf(']') === -1) {
|
|
1613
|
-
token.__strongJaHasBracket = false
|
|
1614
|
-
token.__strongJaBracketAtomic = false
|
|
1615
|
-
return false
|
|
1616
|
-
}
|
|
1617
|
-
token.__strongJaHasBracket = true
|
|
1618
|
-
}
|
|
1619
|
-
const splitEmptyPair = options && options.splitEmptyPair
|
|
1620
|
-
const segments = []
|
|
1621
|
-
let buffer = ''
|
|
1622
|
-
let pos = 0
|
|
1623
|
-
while (pos < content.length) {
|
|
1624
|
-
if (!splitEmptyPair &&
|
|
1625
|
-
content.charCodeAt(pos) === CHAR_OPEN_BRACKET &&
|
|
1626
|
-
content.charCodeAt(pos + 1) === CHAR_CLOSE_BRACKET) {
|
|
1627
|
-
if (buffer) {
|
|
1628
|
-
segments.push(buffer)
|
|
1629
|
-
buffer = ''
|
|
1630
|
-
}
|
|
1631
|
-
segments.push('[]')
|
|
1632
|
-
pos += 2
|
|
1633
|
-
continue
|
|
1634
|
-
}
|
|
1635
|
-
const ch = content[pos]
|
|
1636
|
-
if (ch === '[' || ch === ']') {
|
|
1637
|
-
if (buffer) {
|
|
1638
|
-
segments.push(buffer)
|
|
1639
|
-
buffer = ''
|
|
1640
|
-
}
|
|
1641
|
-
segments.push(ch)
|
|
1642
|
-
pos++
|
|
1643
|
-
continue
|
|
1644
|
-
}
|
|
1645
|
-
buffer += ch
|
|
1646
|
-
pos++
|
|
13
|
+
const getNoLinkMdInstance = (md, opt) => {
|
|
14
|
+
const baseOpt = opt || md.__strongJaTokenOpt || { mode: 'japanese' }
|
|
15
|
+
const key = buildNoLinkCacheKey(baseOpt)
|
|
16
|
+
if (!md.__strongJaTokenNoLinkCache) {
|
|
17
|
+
md.__strongJaTokenNoLinkCache = new Map()
|
|
1647
18
|
}
|
|
1648
|
-
|
|
1649
|
-
if (
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
if (seg === '[' || seg === ']') {
|
|
1656
|
-
token.__strongJaHasBracket = true
|
|
1657
|
-
token.__strongJaBracketAtomic = true
|
|
1658
|
-
} else if (seg === '[]') {
|
|
1659
|
-
token.__strongJaHasBracket = true
|
|
1660
|
-
token.__strongJaBracketAtomic = false
|
|
1661
|
-
} else {
|
|
1662
|
-
token.__strongJaHasBracket = false
|
|
1663
|
-
token.__strongJaBracketAtomic = false
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
return false
|
|
1667
|
-
}
|
|
1668
|
-
token.content = segments[0]
|
|
1669
|
-
if (token.content === '[' || token.content === ']') {
|
|
1670
|
-
token.__strongJaHasBracket = true
|
|
1671
|
-
token.__strongJaBracketAtomic = true
|
|
1672
|
-
} else if (token.content === '[]') {
|
|
1673
|
-
token.__strongJaHasBracket = true
|
|
1674
|
-
token.__strongJaBracketAtomic = false
|
|
1675
|
-
} else {
|
|
1676
|
-
token.__strongJaHasBracket = false
|
|
1677
|
-
token.__strongJaBracketAtomic = false
|
|
1678
|
-
}
|
|
1679
|
-
let insertIdx = index + 1
|
|
1680
|
-
for (let s = 1; s < segments.length; s++) {
|
|
1681
|
-
const newToken = cloneTextToken(token, segments[s])
|
|
1682
|
-
if (segments[s] === '[' || segments[s] === ']') {
|
|
1683
|
-
newToken.__strongJaHasBracket = true
|
|
1684
|
-
newToken.__strongJaBracketAtomic = true
|
|
1685
|
-
} else if (segments[s] === '[]') {
|
|
1686
|
-
newToken.__strongJaHasBracket = true
|
|
1687
|
-
newToken.__strongJaBracketAtomic = false
|
|
1688
|
-
} else {
|
|
1689
|
-
newToken.__strongJaHasBracket = false
|
|
1690
|
-
newToken.__strongJaBracketAtomic = false
|
|
1691
|
-
}
|
|
1692
|
-
tokens.splice(insertIdx, 0, newToken)
|
|
1693
|
-
insertIdx++
|
|
1694
|
-
}
|
|
1695
|
-
return true
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
const isBracketToken = (token, bracket) => {
|
|
1699
|
-
return token && token.type === 'text' && token.content === bracket
|
|
19
|
+
const cache = md.__strongJaTokenNoLinkCache
|
|
20
|
+
if (cache.has(key)) return cache.get(key)
|
|
21
|
+
const noLink = new md.constructor(md.options)
|
|
22
|
+
mditStrongJa(noLink, { ...baseOpt, _skipPostprocess: true })
|
|
23
|
+
noLink.inline.ruler.disable(['link'])
|
|
24
|
+
cache.set(key, noLink)
|
|
25
|
+
return noLink
|
|
1700
26
|
}
|
|
1701
27
|
|
|
1702
|
-
const findLinkCloseIndex = (tokens, startIdx) => {
|
|
1703
|
-
let depth = 0
|
|
1704
|
-
for (let idx = startIdx; idx < tokens.length; idx++) {
|
|
1705
|
-
const token = tokens[idx]
|
|
1706
|
-
if (token.type === 'link_open') depth++
|
|
1707
|
-
if (token.type === 'link_close') {
|
|
1708
|
-
depth--
|
|
1709
|
-
if (depth === 0) return idx
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
return -1
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
const consumeCharactersFromTokens = (tokens, startIdx, count) => {
|
|
1716
|
-
let remaining = count
|
|
1717
|
-
let idx = startIdx
|
|
1718
|
-
while (idx < tokens.length && remaining > 0) {
|
|
1719
|
-
const token = tokens[idx]
|
|
1720
|
-
if (!token || token.type !== 'text') {
|
|
1721
|
-
return false
|
|
1722
|
-
}
|
|
1723
|
-
const len = token.content.length
|
|
1724
|
-
if (remaining >= len) {
|
|
1725
|
-
remaining -= len
|
|
1726
|
-
tokens.splice(idx, 1)
|
|
1727
|
-
continue
|
|
1728
|
-
}
|
|
1729
|
-
token.content = token.content.slice(remaining)
|
|
1730
|
-
remaining = 0
|
|
1731
|
-
}
|
|
1732
|
-
return remaining === 0
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
const wrapLabelTokensWithLink = (tokens, labelStartIdx, labelEndIdx, linkOpenToken, linkCloseToken, labelSource) => {
|
|
1736
|
-
const wrapperPairs = []
|
|
1737
|
-
let startIdx = labelStartIdx
|
|
1738
|
-
let endIdx = labelEndIdx
|
|
1739
|
-
while (startIdx > 0) {
|
|
1740
|
-
const prevToken = tokens[startIdx - 1]
|
|
1741
|
-
const nextToken = tokens[endIdx + 1]
|
|
1742
|
-
if (!prevToken || !nextToken) break
|
|
1743
|
-
if (!/_close$/.test(prevToken.type)) break
|
|
1744
|
-
const expectedOpen = prevToken.type.replace('_close', '_open')
|
|
1745
|
-
if (nextToken.type !== expectedOpen) break
|
|
1746
|
-
wrapperPairs.push({
|
|
1747
|
-
base: prevToken.type.replace('_close', ''),
|
|
1748
|
-
tag: prevToken.tag,
|
|
1749
|
-
markup: prevToken.markup
|
|
1750
|
-
})
|
|
1751
|
-
tokens.splice(endIdx + 1, 1)
|
|
1752
|
-
tokens.splice(startIdx - 1, 1)
|
|
1753
|
-
startIdx -= 1
|
|
1754
|
-
endIdx -= 1
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
if (startIdx > endIdx) {
|
|
1758
|
-
if (labelSource !== undefined && labelSource !== null) {
|
|
1759
|
-
const placeholder = new Token('text', '', 0)
|
|
1760
|
-
placeholder.content = labelSource
|
|
1761
|
-
placeholder.level = linkOpenToken.level + 1
|
|
1762
|
-
tokens.splice(startIdx, 0, placeholder)
|
|
1763
|
-
endIdx = startIdx
|
|
1764
|
-
} else {
|
|
1765
|
-
return startIdx
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
let labelLength = endIdx - startIdx + 1
|
|
1770
|
-
const firstLabelToken = tokens[startIdx]
|
|
1771
|
-
const linkLevel = firstLabelToken ? Math.max(firstLabelToken.level - 1, 0) : 0
|
|
1772
|
-
linkOpenToken.level = linkLevel
|
|
1773
|
-
linkCloseToken.level = linkLevel
|
|
1774
|
-
tokens.splice(startIdx, 0, linkOpenToken)
|
|
1775
|
-
tokens.splice(startIdx + labelLength + 1, 0, linkCloseToken)
|
|
1776
|
-
|
|
1777
|
-
adjustTokenLevels(tokens, startIdx + 1, startIdx + labelLength + 1, 1)
|
|
1778
|
-
|
|
1779
|
-
if (wrapperPairs.length > 0) {
|
|
1780
|
-
let insertIdx = startIdx + 1
|
|
1781
|
-
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
1782
|
-
const pair = wrapperPairs[wp]
|
|
1783
|
-
const innerOpen = new Token(pair.base + '_open', pair.tag, 1)
|
|
1784
|
-
innerOpen.markup = pair.markup
|
|
1785
|
-
innerOpen.level = linkLevel + 1 + wp
|
|
1786
|
-
tokens.splice(insertIdx, 0, innerOpen)
|
|
1787
|
-
insertIdx++
|
|
1788
|
-
labelLength++
|
|
1789
|
-
}
|
|
1790
|
-
let linkClosePos = startIdx + labelLength + 1
|
|
1791
|
-
for (let wp = wrapperPairs.length - 1; wp >= 0; wp--) {
|
|
1792
|
-
const pair = wrapperPairs[wp]
|
|
1793
|
-
const innerClose = new Token(pair.base + '_close', pair.tag, -1)
|
|
1794
|
-
innerClose.markup = pair.markup
|
|
1795
|
-
innerClose.level = linkLevel + 1 + wp
|
|
1796
|
-
tokens.splice(linkClosePos, 0, innerClose)
|
|
1797
|
-
labelLength++
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
return startIdx + labelLength + 2
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
const parseInlineLinkTail = (content, md) => {
|
|
1805
|
-
if (!content || content.charCodeAt(0) !== CHAR_OPEN_PAREN) return null
|
|
1806
|
-
const max = content.length
|
|
1807
|
-
let pos = 1
|
|
1808
|
-
while (pos < max) {
|
|
1809
|
-
const code = content.charCodeAt(pos)
|
|
1810
|
-
if (!isSpace(code) && code !== 0x0A) break
|
|
1811
|
-
pos++
|
|
1812
|
-
}
|
|
1813
|
-
if (pos >= max) return null
|
|
1814
|
-
|
|
1815
|
-
let href = ''
|
|
1816
|
-
let destPos = pos
|
|
1817
|
-
if (pos < max && content.charCodeAt(pos) === CHAR_CLOSE_PAREN) {
|
|
1818
|
-
href = ''
|
|
1819
|
-
} else {
|
|
1820
|
-
const dest = parseLinkDestination(content, pos, max)
|
|
1821
|
-
if (!dest.ok) return null
|
|
1822
|
-
href = md.normalizeLink(dest.str)
|
|
1823
|
-
if (!md.validateLink(href)) {
|
|
1824
|
-
return null
|
|
1825
|
-
}
|
|
1826
|
-
pos = dest.pos
|
|
1827
|
-
destPos = dest.pos
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
while (pos < max) {
|
|
1831
|
-
const code = content.charCodeAt(pos)
|
|
1832
|
-
if (!isSpace(code) && code !== 0x0A) break
|
|
1833
|
-
pos++
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
let title = ''
|
|
1837
|
-
const titleRes = parseLinkTitle(content, pos, max)
|
|
1838
|
-
if (pos < max && pos !== destPos && titleRes.ok) {
|
|
1839
|
-
title = titleRes.str
|
|
1840
|
-
pos = titleRes.pos
|
|
1841
|
-
while (pos < max) {
|
|
1842
|
-
const code = content.charCodeAt(pos)
|
|
1843
|
-
if (!isSpace(code) && code !== 0x0A) break
|
|
1844
|
-
pos++
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
if (pos >= max || content.charCodeAt(pos) !== CHAR_CLOSE_PAREN) {
|
|
1849
|
-
return null
|
|
1850
|
-
}
|
|
1851
|
-
pos++
|
|
1852
|
-
return { href, title, consumed: pos }
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
const INLINE_LINK_BRACKET_SPLIT_OPTIONS = { splitEmptyPair: true }
|
|
1856
|
-
|
|
1857
|
-
const removeGhostLabelText = (tokens, linkCloseIndex, labelText) => {
|
|
1858
|
-
if (!labelText) return
|
|
1859
|
-
if (linkCloseIndex === null || linkCloseIndex === undefined) return
|
|
1860
|
-
if (linkCloseIndex < 0 || linkCloseIndex >= tokens.length) return
|
|
1861
|
-
const closeToken = tokens[linkCloseIndex]
|
|
1862
|
-
if (!closeToken || closeToken.type !== 'link_close') return
|
|
1863
|
-
let idx = linkCloseIndex + 1
|
|
1864
|
-
while (idx < tokens.length) {
|
|
1865
|
-
const token = tokens[idx]
|
|
1866
|
-
if (!token) {
|
|
1867
|
-
idx++
|
|
1868
|
-
continue
|
|
1869
|
-
}
|
|
1870
|
-
if (token.type === 'text') {
|
|
1871
|
-
if (token.content.startsWith(labelText)) {
|
|
1872
|
-
if (token.content.length === labelText.length) {
|
|
1873
|
-
tokens.splice(idx, 1)
|
|
1874
|
-
} else {
|
|
1875
|
-
token.content = token.content.slice(labelText.length)
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
break
|
|
1879
|
-
}
|
|
1880
|
-
if (!/_close$/.test(token.type)) {
|
|
1881
|
-
break
|
|
1882
|
-
}
|
|
1883
|
-
idx++
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
const restoreLabelWhitespace = (tokens, labelSources) => {
|
|
1888
|
-
if (!tokens || !labelSources || labelSources.length === 0) return
|
|
1889
|
-
let labelIdx = 0
|
|
1890
|
-
for (let i = 0; i < tokens.length && labelIdx < labelSources.length; i++) {
|
|
1891
|
-
if (tokens[i].type !== 'link_open') continue
|
|
1892
|
-
const closeIdx = findLinkCloseIndex(tokens, i)
|
|
1893
|
-
if (closeIdx === -1) continue
|
|
1894
|
-
const labelSource = labelSources[labelIdx] || ''
|
|
1895
|
-
if (!labelSource) {
|
|
1896
|
-
labelIdx++
|
|
1897
|
-
continue
|
|
1898
|
-
}
|
|
1899
|
-
let cursor = 0
|
|
1900
|
-
for (let pos = i + 1; pos < closeIdx; pos++) {
|
|
1901
|
-
const t = tokens[pos]
|
|
1902
|
-
const markup = t.markup || ''
|
|
1903
|
-
const text = t.content || ''
|
|
1904
|
-
const startPos = cursor
|
|
1905
|
-
if (t.type === 'text') {
|
|
1906
|
-
cursor += text.length
|
|
1907
|
-
} else if (t.type === 'code_inline') {
|
|
1908
|
-
cursor += markup.length + text.length + markup.length
|
|
1909
|
-
} else if (markup) {
|
|
1910
|
-
cursor += markup.length
|
|
1911
|
-
}
|
|
1912
|
-
if ((t.type === 'strong_open' || t.type === 'em_open') && startPos > 0) {
|
|
1913
|
-
const prevToken = tokens[pos - 1]
|
|
1914
|
-
if (prevToken && prevToken.type === 'text' && prevToken.content && !prevToken.content.endsWith(' ')) {
|
|
1915
|
-
const hasSpaceBefore = startPos - 1 >= 0 && startPos - 1 < labelSource.length && labelSource[startPos - 1] === ' '
|
|
1916
|
-
const hasSpaceAt = startPos >= 0 && startPos < labelSource.length && labelSource[startPos] === ' '
|
|
1917
|
-
if (hasSpaceBefore || hasSpaceAt) {
|
|
1918
|
-
prevToken.content += ' '
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
labelIdx++
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
const convertInlineLinks = (tokens, state) => {
|
|
1928
|
-
if (!tokens || tokens.length === 0) return
|
|
1929
|
-
let labelSources = tokens.__strongJaInlineLabelSources
|
|
1930
|
-
if ((!labelSources || labelSources.length === 0) && state && state.env && Array.isArray(state.env.__strongJaInlineLabelSourceList) && state.env.__strongJaInlineLabelSourceList.length > 0) {
|
|
1931
|
-
labelSources = state.env.__strongJaInlineLabelSourceList.shift()
|
|
1932
|
-
}
|
|
1933
|
-
let labelSourceIndex = tokens.__strongJaInlineLabelIndex || 0
|
|
1934
|
-
let i = 0
|
|
1935
|
-
while (i < tokens.length) {
|
|
1936
|
-
if (splitBracketToken(tokens, i, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1937
|
-
continue
|
|
1938
|
-
}
|
|
1939
|
-
if (!isBracketToken(tokens[i], '[')) {
|
|
1940
|
-
i++
|
|
1941
|
-
continue
|
|
1942
|
-
}
|
|
1943
|
-
let closeIdx = i + 1
|
|
1944
|
-
let invalid = false
|
|
1945
|
-
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
1946
|
-
if (splitBracketToken(tokens, closeIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1947
|
-
continue
|
|
1948
|
-
}
|
|
1949
|
-
if (tokens[closeIdx].type === 'link_open') {
|
|
1950
|
-
invalid = true
|
|
1951
|
-
break
|
|
1952
|
-
}
|
|
1953
|
-
closeIdx++
|
|
1954
|
-
}
|
|
1955
|
-
if (invalid || closeIdx >= tokens.length) {
|
|
1956
|
-
i++
|
|
1957
|
-
continue
|
|
1958
|
-
}
|
|
1959
|
-
const currentLabelSource = labelSources && labelSourceIndex < labelSources.length
|
|
1960
|
-
? labelSources[labelSourceIndex]
|
|
1961
|
-
: undefined
|
|
1962
|
-
|
|
1963
|
-
const labelLength = closeIdx - i - 1
|
|
1964
|
-
const needsPlaceholder = labelLength <= 0
|
|
1965
|
-
if (needsPlaceholder && !currentLabelSource) {
|
|
1966
|
-
i++
|
|
1967
|
-
continue
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
let tailIdx = closeIdx + 1
|
|
1971
|
-
let tailContent = ''
|
|
1972
|
-
let parsedTail = null
|
|
1973
|
-
let tailHasCloseParen = false
|
|
1974
|
-
while (tailIdx < tokens.length) {
|
|
1975
|
-
if (splitBracketToken(tokens, tailIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1976
|
-
continue
|
|
1977
|
-
}
|
|
1978
|
-
const tailToken = tokens[tailIdx]
|
|
1979
|
-
if (tailToken.type !== 'text' || !tailToken.content) {
|
|
1980
|
-
break
|
|
1981
|
-
}
|
|
1982
|
-
tailContent += tailToken.content
|
|
1983
|
-
if (!tailHasCloseParen) {
|
|
1984
|
-
if (tailToken.content.indexOf(')') === -1) {
|
|
1985
|
-
tailIdx++
|
|
1986
|
-
continue
|
|
1987
|
-
}
|
|
1988
|
-
tailHasCloseParen = true
|
|
1989
|
-
}
|
|
1990
|
-
parsedTail = parseInlineLinkTail(tailContent, state.md)
|
|
1991
|
-
if (parsedTail) break
|
|
1992
|
-
tailIdx++
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
if (!parsedTail) {
|
|
1996
|
-
i++
|
|
1997
|
-
continue
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
if (!consumeCharactersFromTokens(tokens, closeIdx + 1, parsedTail.consumed)) {
|
|
2001
|
-
i++
|
|
2002
|
-
continue
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
tokens.splice(closeIdx, 1)
|
|
2006
|
-
tokens.splice(i, 1)
|
|
2007
|
-
|
|
2008
|
-
const linkOpenToken = new Token('link_open', 'a', 1)
|
|
2009
|
-
linkOpenToken.attrs = [['href', parsedTail.href]]
|
|
2010
|
-
if (parsedTail.title) linkOpenToken.attrPush(['title', parsedTail.title])
|
|
2011
|
-
linkOpenToken.markup = '[]()'
|
|
2012
|
-
linkOpenToken.info = 'auto'
|
|
2013
|
-
const linkCloseToken = new Token('link_close', 'a', -1)
|
|
2014
|
-
linkCloseToken.markup = '[]()'
|
|
2015
|
-
linkCloseToken.info = 'auto'
|
|
2016
|
-
|
|
2017
|
-
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken, currentLabelSource)
|
|
2018
|
-
if (nextIndex === i) {
|
|
2019
|
-
i++
|
|
2020
|
-
continue
|
|
2021
|
-
}
|
|
2022
|
-
if (currentLabelSource) {
|
|
2023
|
-
const linkCloseIdx = findLinkCloseIndex(tokens, i)
|
|
2024
|
-
if (linkCloseIdx !== -1) {
|
|
2025
|
-
let cursor = 0
|
|
2026
|
-
for (let pos = i + 1; pos < linkCloseIdx; pos++) {
|
|
2027
|
-
const t = tokens[pos]
|
|
2028
|
-
const markup = t.markup || ''
|
|
2029
|
-
const text = t.content || ''
|
|
2030
|
-
const startPos = cursor
|
|
2031
|
-
if (t.type === 'text') {
|
|
2032
|
-
cursor += text.length
|
|
2033
|
-
} else if (t.type === 'code_inline') {
|
|
2034
|
-
cursor += markup.length + text.length + markup.length
|
|
2035
|
-
} else if (markup) {
|
|
2036
|
-
cursor += markup.length
|
|
2037
|
-
}
|
|
2038
|
-
if ((t.type === 'strong_open' || t.type === 'em_open') && startPos > 0) {
|
|
2039
|
-
const prevToken = tokens[pos - 1]
|
|
2040
|
-
if (prevToken && prevToken.type === 'text' && prevToken.content && !prevToken.content.endsWith(' ')) {
|
|
2041
|
-
const labelHasSpaceBefore = startPos - 1 >= 0 && startPos - 1 < currentLabelSource.length && currentLabelSource[startPos - 1] === ' '
|
|
2042
|
-
const labelHasSpaceAt = startPos >= 0 && startPos < currentLabelSource.length && currentLabelSource[startPos] === ' '
|
|
2043
|
-
if (labelHasSpaceBefore || labelHasSpaceAt) {
|
|
2044
|
-
prevToken.content += ' '
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
if (needsPlaceholder && currentLabelSource) {
|
|
2052
|
-
removeGhostLabelText(tokens, nextIndex - 1, currentLabelSource)
|
|
2053
|
-
}
|
|
2054
|
-
|
|
2055
|
-
if (labelSources && labelSources.length > 0) {
|
|
2056
|
-
if (labelSourceIndex < labelSources.length) {
|
|
2057
|
-
labelSourceIndex++
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
i = nextIndex
|
|
2061
|
-
}
|
|
2062
|
-
if (labelSources) {
|
|
2063
|
-
tokens.__strongJaInlineLabelIndex = labelSourceIndex
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
const convertCollapsedReferenceLinks = (tokens, state) => {
|
|
2068
|
-
const references = state.env && state.env.references
|
|
2069
|
-
if (!references) return
|
|
2070
|
-
const referenceCount = state.__strongJaReferenceCount
|
|
2071
|
-
if (referenceCount !== undefined) {
|
|
2072
|
-
if (referenceCount === 0) return
|
|
2073
|
-
} else if (Object.keys(references).length === 0) {
|
|
2074
|
-
return
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
let i = 0
|
|
2078
|
-
while (i < tokens.length) {
|
|
2079
|
-
if (splitBracketToken(tokens, i)) {
|
|
2080
|
-
continue
|
|
2081
|
-
}
|
|
2082
|
-
if (!isBracketToken(tokens[i], '[')) {
|
|
2083
|
-
i++
|
|
2084
|
-
continue
|
|
2085
|
-
}
|
|
2086
|
-
let closeIdx = i + 1
|
|
2087
|
-
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
2088
|
-
if (splitBracketToken(tokens, closeIdx)) {
|
|
2089
|
-
continue
|
|
2090
|
-
}
|
|
2091
|
-
if (tokens[closeIdx].type === 'link_open') {
|
|
2092
|
-
closeIdx = -1
|
|
2093
|
-
break
|
|
2094
|
-
}
|
|
2095
|
-
closeIdx++
|
|
2096
|
-
}
|
|
2097
|
-
if (closeIdx === -1 || closeIdx >= tokens.length) {
|
|
2098
|
-
i++
|
|
2099
|
-
continue
|
|
2100
|
-
}
|
|
2101
|
-
|
|
2102
|
-
if (closeIdx === i + 1) {
|
|
2103
|
-
i++
|
|
2104
|
-
continue
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
const labelStart = i + 1
|
|
2108
|
-
const labelEnd = closeIdx - 1
|
|
2109
|
-
const labelLength = closeIdx - i - 1
|
|
2110
|
-
const labelText = buildReferenceLabelRange(tokens, labelStart, labelEnd)
|
|
2111
|
-
const cleanedLabel = cleanLabelText(labelText)
|
|
2112
|
-
const whitespaceStart = closeIdx + 1
|
|
2113
|
-
let refRemoveStart = whitespaceStart
|
|
2114
|
-
while (refRemoveStart < tokens.length && isWhitespaceToken(tokens[refRemoveStart])) {
|
|
2115
|
-
refRemoveStart++
|
|
2116
|
-
}
|
|
2117
|
-
if (splitBracketToken(tokens, refRemoveStart)) {
|
|
2118
|
-
continue
|
|
2119
|
-
}
|
|
2120
|
-
const whitespaceCount = refRemoveStart - whitespaceStart
|
|
2121
|
-
let refKey = null
|
|
2122
|
-
let refRemoveCount = 0
|
|
2123
|
-
let existingLinkOpen = null
|
|
2124
|
-
let existingLinkClose = null
|
|
2125
|
-
const nextToken = tokens[refRemoveStart]
|
|
2126
|
-
if (isBracketToken(nextToken, '[]')) {
|
|
2127
|
-
refKey = normalizeReferenceCandidate(state, cleanedLabel)
|
|
2128
|
-
refRemoveCount = 1
|
|
2129
|
-
} else if (isBracketToken(nextToken, '[')) {
|
|
2130
|
-
let refCloseIdx = refRemoveStart + 1
|
|
2131
|
-
while (refCloseIdx < tokens.length && !isBracketToken(tokens[refCloseIdx], ']')) {
|
|
2132
|
-
refCloseIdx++
|
|
2133
|
-
}
|
|
2134
|
-
if (refCloseIdx >= tokens.length) {
|
|
2135
|
-
i++
|
|
2136
|
-
continue
|
|
2137
|
-
}
|
|
2138
|
-
const refStart = refRemoveStart + 1
|
|
2139
|
-
const refEnd = refCloseIdx - 1
|
|
2140
|
-
if (refStart > refEnd) {
|
|
2141
|
-
refKey = normalizeReferenceCandidate(state, cleanedLabel)
|
|
2142
|
-
} else {
|
|
2143
|
-
const refLabelText = buildReferenceLabelRange(tokens, refStart, refEnd)
|
|
2144
|
-
refKey = normalizeReferenceCandidate(state, refLabelText)
|
|
2145
|
-
}
|
|
2146
|
-
refRemoveCount = refCloseIdx - refRemoveStart + 1
|
|
2147
|
-
} else if (nextToken && nextToken.type === 'link_open') {
|
|
2148
|
-
const linkCloseIdx = findLinkCloseIndex(tokens, refRemoveStart)
|
|
2149
|
-
if (linkCloseIdx === -1) {
|
|
2150
|
-
i++
|
|
2151
|
-
continue
|
|
2152
|
-
}
|
|
2153
|
-
existingLinkOpen = tokens[refRemoveStart]
|
|
2154
|
-
existingLinkClose = tokens[linkCloseIdx]
|
|
2155
|
-
refRemoveCount = linkCloseIdx - refRemoveStart + 1
|
|
2156
|
-
} else {
|
|
2157
|
-
i++
|
|
2158
|
-
continue
|
|
2159
|
-
}
|
|
2160
|
-
let linkOpenToken = null
|
|
2161
|
-
let linkCloseToken = null
|
|
2162
|
-
if (existingLinkOpen && existingLinkClose) {
|
|
2163
|
-
if (whitespaceCount > 0) {
|
|
2164
|
-
tokens.splice(whitespaceStart, whitespaceCount)
|
|
2165
|
-
refRemoveStart -= whitespaceCount
|
|
2166
|
-
}
|
|
2167
|
-
if (refRemoveCount > 0) {
|
|
2168
|
-
tokens.splice(refRemoveStart, refRemoveCount)
|
|
2169
|
-
}
|
|
2170
|
-
linkOpenToken = existingLinkOpen
|
|
2171
|
-
linkCloseToken = existingLinkClose
|
|
2172
|
-
} else {
|
|
2173
|
-
if (!refKey) {
|
|
2174
|
-
i++
|
|
2175
|
-
continue
|
|
2176
|
-
}
|
|
2177
|
-
const ref = references[refKey]
|
|
2178
|
-
if (!ref) {
|
|
2179
|
-
i++
|
|
2180
|
-
continue
|
|
2181
|
-
}
|
|
2182
|
-
if (whitespaceCount > 0) {
|
|
2183
|
-
tokens.splice(whitespaceStart, whitespaceCount)
|
|
2184
|
-
refRemoveStart -= whitespaceCount
|
|
2185
|
-
}
|
|
2186
|
-
if (refRemoveCount > 0) {
|
|
2187
|
-
tokens.splice(refRemoveStart, refRemoveCount)
|
|
2188
|
-
}
|
|
2189
|
-
linkOpenToken = new Token('link_open', 'a', 1)
|
|
2190
|
-
linkOpenToken.attrs = [['href', ref.href]]
|
|
2191
|
-
if (ref.title) linkOpenToken.attrPush(['title', ref.title])
|
|
2192
|
-
linkOpenToken.markup = '[]'
|
|
2193
|
-
linkOpenToken.info = 'auto'
|
|
2194
|
-
linkCloseToken = new Token('link_close', 'a', -1)
|
|
2195
|
-
linkCloseToken.markup = '[]'
|
|
2196
|
-
linkCloseToken.info = 'auto'
|
|
2197
|
-
}
|
|
2198
|
-
tokens.splice(closeIdx, 1)
|
|
2199
|
-
tokens.splice(i, 1)
|
|
2200
|
-
|
|
2201
|
-
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken)
|
|
2202
|
-
i = nextIndex
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
// Link cleanup helpers
|
|
2207
|
-
const mergeBrokenMarksAroundLinks = (tokens) => {
|
|
2208
|
-
let i = 0
|
|
2209
|
-
while (i < tokens.length) {
|
|
2210
|
-
const closeToken = tokens[i]
|
|
2211
|
-
if (!closeToken || !/_close$/.test(closeToken.type)) {
|
|
2212
|
-
i++
|
|
2213
|
-
continue
|
|
2214
|
-
}
|
|
2215
|
-
const openType = closeToken.type.replace('_close', '_open')
|
|
2216
|
-
let j = i + 1
|
|
2217
|
-
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
2218
|
-
if (j >= tokens.length || tokens[j].type !== 'link_open') {
|
|
2219
|
-
i++
|
|
2220
|
-
continue
|
|
2221
|
-
}
|
|
2222
|
-
let linkDepth = 1
|
|
2223
|
-
j++
|
|
2224
|
-
while (j < tokens.length && linkDepth > 0) {
|
|
2225
|
-
if (tokens[j].type === 'link_open') linkDepth++
|
|
2226
|
-
if (tokens[j].type === 'link_close') linkDepth--
|
|
2227
|
-
j++
|
|
2228
|
-
}
|
|
2229
|
-
if (linkDepth !== 0) {
|
|
2230
|
-
i++
|
|
2231
|
-
continue
|
|
2232
|
-
}
|
|
2233
|
-
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
2234
|
-
if (j >= tokens.length) {
|
|
2235
|
-
i++
|
|
2236
|
-
continue
|
|
2237
|
-
}
|
|
2238
|
-
const reopenToken = tokens[j]
|
|
2239
|
-
if (reopenToken.type !== openType || reopenToken.level !== closeToken.level) {
|
|
2240
|
-
i++
|
|
2241
|
-
continue
|
|
2242
|
-
}
|
|
2243
|
-
tokens.splice(j, 1)
|
|
2244
|
-
tokens.splice(i, 1)
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
28
|
const mditStrongJa = (md, option) => {
|
|
29
|
+
if (option && typeof option.engine === 'string' && option.engine !== 'token') {
|
|
30
|
+
throw new Error('mditStrongJa: legacy engine was removed; use token (default)')
|
|
31
|
+
}
|
|
2250
32
|
const opt = {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
33
|
+
mditAttrs: true, // assume markdown-it-attrs integration by default
|
|
34
|
+
mdBreaks: md.options.breaks, // inherit md.options.breaks for compat handling
|
|
35
|
+
mode: 'japanese', // 'japanese' | 'aggressive' | 'compatible' (pairing behavior)
|
|
36
|
+
coreRulesBeforePostprocess: [], // e.g. ['cjk_breaks'] to keep rules ahead of postprocess
|
|
37
|
+
postprocess: true, // enable link/ref reconstruction pass
|
|
38
|
+
patchCorePush: true // keep restore-softbreaks after late cjk_breaks
|
|
2257
39
|
}
|
|
2258
40
|
if (option) Object.assign(opt, option)
|
|
2259
41
|
opt.hasCjkBreaks = hasCjkBreaksRule(md)
|
|
2260
|
-
const rawCoreRules = opt.coreRulesBeforePostprocess
|
|
2261
|
-
const hasCoreRuleConfig = Array.isArray(rawCoreRules)
|
|
2262
|
-
? rawCoreRules.length > 0
|
|
2263
|
-
: !!rawCoreRules
|
|
2264
|
-
const coreRulesBeforePostprocess = hasCoreRuleConfig
|
|
2265
|
-
? normalizeCoreRulesBeforePostprocess(rawCoreRules)
|
|
2266
|
-
: []
|
|
2267
|
-
|
|
2268
|
-
md.inline.ruler.before('emphasis', 'strong_ja', (state, silent) => {
|
|
2269
|
-
return strongJa(state, silent, opt)
|
|
2270
|
-
})
|
|
2271
42
|
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
if (!state || !state.tokens) return
|
|
2276
|
-
for (let i = 0; i < state.tokens.length; i++) {
|
|
2277
|
-
const token = state.tokens[i]
|
|
2278
|
-
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|
|
2279
|
-
let idx = token.children.length - 1
|
|
2280
|
-
while (idx >= 0 && (!token.children[idx] || (token.children[idx].type === 'text' && token.children[idx].content === ''))) {
|
|
2281
|
-
idx--
|
|
2282
|
-
}
|
|
2283
|
-
if (idx < 0) continue
|
|
2284
|
-
const tail = token.children[idx]
|
|
2285
|
-
if (!tail || tail.type !== 'text' || !tail.content) continue
|
|
2286
|
-
const trimmed = tail.content.replace(/[ \t]+$/, '')
|
|
2287
|
-
if (trimmed !== tail.content) {
|
|
2288
|
-
tail.content = trimmed
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
const hasTextJoinRule = Array.isArray(md.core?.ruler?.__rules__)
|
|
2293
|
-
? md.core.ruler.__rules__.some((rule) => rule && rule.name === 'text_join')
|
|
2294
|
-
: false
|
|
2295
|
-
if (hasTextJoinRule) {
|
|
2296
|
-
md.core.ruler.after('text_join', 'strong_ja_trim_trailing_spaces', trimInlineTrailingSpaces)
|
|
2297
|
-
} else {
|
|
2298
|
-
md.core.ruler.after('inline', 'strong_ja_trim_trailing_spaces', trimInlineTrailingSpaces)
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
const normalizeSoftbreakSpacing = (state) => {
|
|
2302
|
-
if (!state || opt.hasCjkBreaks !== true) return
|
|
2303
|
-
if (!state.tokens || state.tokens.length === 0) return
|
|
2304
|
-
for (let i = 0; i < state.tokens.length; i++) {
|
|
2305
|
-
const token = state.tokens[i]
|
|
2306
|
-
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|
|
2307
|
-
for (let j = 0; j < token.children.length; j++) {
|
|
2308
|
-
const child = token.children[j]
|
|
2309
|
-
if (!child || child.type !== 'text' || !child.content) continue
|
|
2310
|
-
if (child.content.indexOf('\n') === -1) continue
|
|
2311
|
-
let normalized = ''
|
|
2312
|
-
for (let idx = 0; idx < child.content.length; idx++) {
|
|
2313
|
-
const ch = child.content[idx]
|
|
2314
|
-
if (ch === '\n') {
|
|
2315
|
-
const prevChar = idx > 0 ? child.content[idx - 1] : ''
|
|
2316
|
-
const nextChar = idx + 1 < child.content.length ? child.content[idx + 1] : ''
|
|
2317
|
-
const isAsciiWord = nextChar && nextChar >= '0' && nextChar <= 'z' && /[A-Za-z0-9]/.test(nextChar)
|
|
2318
|
-
const shouldReplace = isAsciiWord && nextChar !== '{' && nextChar !== '\\' && isJapanese(prevChar) && !isJapanese(nextChar)
|
|
2319
|
-
if (shouldReplace) {
|
|
2320
|
-
normalized += ' '
|
|
2321
|
-
continue
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
normalized += ch
|
|
2325
|
-
}
|
|
2326
|
-
if (normalized !== child.content) {
|
|
2327
|
-
child.content = normalized
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
if (hasTextJoinRule) {
|
|
2333
|
-
md.core.ruler.after('text_join', 'strong_ja_softbreak_spacing', normalizeSoftbreakSpacing)
|
|
2334
|
-
} else {
|
|
2335
|
-
md.core.ruler.after('inline', 'strong_ja_softbreak_spacing', normalizeSoftbreakSpacing)
|
|
2336
|
-
}
|
|
43
|
+
md.__strongJaTokenOpt = opt
|
|
44
|
+
patchScanDelims(md)
|
|
45
|
+
registerTokenCompat(md, opt)
|
|
2337
46
|
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
const child = children[j]
|
|
2349
|
-
if (!child || child.type !== 'text' || child.content !== '') continue
|
|
2350
|
-
// Find previous non-empty text content to inspect the trailing character.
|
|
2351
|
-
let prevChar = ''
|
|
2352
|
-
for (let k = j - 1; k >= 0; k--) {
|
|
2353
|
-
const prev = children[k]
|
|
2354
|
-
if (prev && prev.type === 'text' && prev.content) {
|
|
2355
|
-
prevChar = prev.content.charAt(prev.content.length - 1)
|
|
2356
|
-
break
|
|
2357
|
-
}
|
|
2358
|
-
}
|
|
2359
|
-
if (!prevChar || !isJapanese(prevChar)) continue
|
|
2360
|
-
const next = children[j + 1]
|
|
2361
|
-
if (!next || next.type !== 'text' || !next.content) continue
|
|
2362
|
-
const nextChar = next.content.charAt(0)
|
|
2363
|
-
if (nextChar !== '{') continue
|
|
2364
|
-
child.type = 'softbreak'
|
|
2365
|
-
child.tag = ''
|
|
2366
|
-
child.content = '\n'
|
|
2367
|
-
child.markup = ''
|
|
2368
|
-
child.info = ''
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
47
|
+
if (!opt._skipPostprocess) {
|
|
48
|
+
registerTokenPostprocess(md, opt, getNoLinkMdInstance)
|
|
49
|
+
const rawCoreRules = opt.coreRulesBeforePostprocess
|
|
50
|
+
const hasCoreRuleConfig = Array.isArray(rawCoreRules)
|
|
51
|
+
? rawCoreRules.length > 0
|
|
52
|
+
: !!rawCoreRules
|
|
53
|
+
const coreRulesBeforePostprocess = hasCoreRuleConfig
|
|
54
|
+
? normalizeCoreRulesBeforePostprocess(rawCoreRules)
|
|
55
|
+
: []
|
|
56
|
+
ensureCoreRuleOrder(md, coreRulesBeforePostprocess, 'strong_ja_token_postprocess')
|
|
2371
57
|
}
|
|
2372
58
|
|
|
2373
|
-
|
|
2374
|
-
if (md.__strongJaRestoreRegistered) return
|
|
2375
|
-
const anchorRule = hasTextJoinRule ? 'text_join' : 'inline'
|
|
2376
|
-
const added = md.core.ruler.after(anchorRule, 'strong_ja_restore_softbreaks', restoreSoftbreaksAfterCjk)
|
|
2377
|
-
if (added !== false) {
|
|
2378
|
-
md.__strongJaRestoreRegistered = true
|
|
2379
|
-
md.__strongJaRestoreSoftbreaksForAttrs = opt.mditAttrs === false
|
|
2380
|
-
if (opt.hasCjkBreaks) {
|
|
2381
|
-
moveRuleAfter(md.core.ruler, 'strong_ja_restore_softbreaks', 'cjk_breaks')
|
|
2382
|
-
md.__strongJaRestoreReordered = true
|
|
2383
|
-
}
|
|
2384
|
-
if (!md.__strongJaPatchCorePush) {
|
|
2385
|
-
md.__strongJaPatchCorePush = true
|
|
2386
|
-
const originalPush = md.core.ruler.push.bind(md.core.ruler)
|
|
2387
|
-
md.core.ruler.push = (name, fn, options) => {
|
|
2388
|
-
const res = originalPush(name, fn, options)
|
|
2389
|
-
if (name && name.indexOf && name.indexOf('cjk_breaks') !== -1) {
|
|
2390
|
-
opt.hasCjkBreaks = true
|
|
2391
|
-
moveRuleAfter(md.core.ruler, 'strong_ja_restore_softbreaks', name)
|
|
2392
|
-
md.__strongJaRestoreReordered = true
|
|
2393
|
-
}
|
|
2394
|
-
return res
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
if (opt.hasCjkBreaks) {
|
|
2398
|
-
moveRuleAfter(md.core.ruler, 'strong_ja_restore_softbreaks', 'cjk_breaks')
|
|
2399
|
-
md.__strongJaRestoreReordered = true
|
|
2400
|
-
}
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
registerRestoreSoftbreaks()
|
|
2404
|
-
|
|
2405
|
-
md.core.ruler.after('inline', 'strong_ja_postprocess', (state) => {
|
|
2406
|
-
const targets = state.env.__strongJaPostProcessTargets
|
|
2407
|
-
if (!targets || targets.length === 0) return
|
|
2408
|
-
for (const tokens of targets) {
|
|
2409
|
-
if (!tokens || !tokens.length) continue
|
|
2410
|
-
let hasBracketText = false
|
|
2411
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
2412
|
-
const token = tokens[i]
|
|
2413
|
-
if (!token || token.type !== 'text') continue
|
|
2414
|
-
const content = token.content
|
|
2415
|
-
if (!content) continue
|
|
2416
|
-
if (content.indexOf('[') !== -1 || content.indexOf(']') !== -1) {
|
|
2417
|
-
hasBracketText = true
|
|
2418
|
-
break
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
if (!hasBracketText) {
|
|
2422
|
-
delete tokens.__strongJaInlineLabelSources
|
|
2423
|
-
delete tokens.__strongJaInlineLabelIndex
|
|
2424
|
-
continue
|
|
2425
|
-
}
|
|
2426
|
-
convertInlineLinks(tokens, state)
|
|
2427
|
-
convertCollapsedReferenceLinks(tokens, state)
|
|
2428
|
-
mergeBrokenMarksAroundLinks(tokens)
|
|
2429
|
-
delete tokens.__strongJaInlineLabelSources
|
|
2430
|
-
delete tokens.__strongJaInlineLabelIndex
|
|
2431
|
-
}
|
|
2432
|
-
if (state.env && state.env.__strongJaInlineLabelSourceList) {
|
|
2433
|
-
delete state.env.__strongJaInlineLabelSourceList
|
|
2434
|
-
}
|
|
2435
|
-
delete state.env.__strongJaPostProcessTargets
|
|
2436
|
-
delete state.env.__strongJaPostProcessTargetSet
|
|
2437
|
-
})
|
|
2438
|
-
|
|
2439
|
-
if (coreRulesBeforePostprocess.length > 0) {
|
|
2440
|
-
ensureCoreRuleOrder(md, coreRulesBeforePostprocess)
|
|
2441
|
-
}
|
|
59
|
+
return md
|
|
2442
60
|
}
|
|
2443
61
|
|
|
2444
62
|
export default mditStrongJa
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
function normalizeCoreRulesBeforePostprocess(value) {
|
|
2448
|
-
if (!value) return []
|
|
2449
|
-
const list = Array.isArray(value) ? value : [value]
|
|
2450
|
-
const normalized = []
|
|
2451
|
-
const seen = new Set()
|
|
2452
|
-
for (let idx = 0; idx < list.length; idx++) {
|
|
2453
|
-
const raw = list[idx]
|
|
2454
|
-
if (typeof raw !== 'string') continue
|
|
2455
|
-
const trimmed = raw.trim()
|
|
2456
|
-
if (!trimmed || seen.has(trimmed)) continue
|
|
2457
|
-
seen.add(trimmed)
|
|
2458
|
-
normalized.push(trimmed)
|
|
2459
|
-
}
|
|
2460
|
-
return normalized
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
function ensureCoreRuleOrder(md, ruleNames) {
|
|
2465
|
-
if (!md || !md.core || !md.core.ruler) return
|
|
2466
|
-
if (!ruleNames || ruleNames.length === 0) return
|
|
2467
|
-
for (let idx = 0; idx < ruleNames.length; idx++) {
|
|
2468
|
-
moveRuleBefore(md.core.ruler, ruleNames[idx], 'strong_ja_postprocess')
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
function moveRuleBefore(ruler, ruleName, beforeName) {
|
|
2474
|
-
if (!ruler || !ruler.__rules__) return
|
|
2475
|
-
const rules = ruler.__rules__
|
|
2476
|
-
let fromIdx = -1
|
|
2477
|
-
let beforeIdx = -1
|
|
2478
|
-
for (let idx = 0; idx < rules.length; idx++) {
|
|
2479
|
-
if (rules[idx].name === ruleName) fromIdx = idx
|
|
2480
|
-
if (rules[idx].name === beforeName) beforeIdx = idx
|
|
2481
|
-
if (fromIdx !== -1 && beforeIdx !== -1) break
|
|
2482
|
-
}
|
|
2483
|
-
if (fromIdx === -1 || beforeIdx === -1 || fromIdx < beforeIdx) return
|
|
2484
|
-
|
|
2485
|
-
const rule = rules.splice(fromIdx, 1)[0]
|
|
2486
|
-
rules.splice(beforeIdx, 0, rule)
|
|
2487
|
-
ruler.__cache__ = null
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
function moveRuleAfter(ruler, ruleName, afterName) {
|
|
2491
|
-
if (!ruler || !ruler.__rules__) return
|
|
2492
|
-
const rules = ruler.__rules__
|
|
2493
|
-
let fromIdx = -1
|
|
2494
|
-
let afterIdx = -1
|
|
2495
|
-
for (let idx = 0; idx < rules.length; idx++) {
|
|
2496
|
-
if (rules[idx].name === ruleName) fromIdx = idx
|
|
2497
|
-
if (rules[idx].name === afterName) afterIdx = idx
|
|
2498
|
-
if (fromIdx !== -1 && afterIdx !== -1) break
|
|
2499
|
-
}
|
|
2500
|
-
if (fromIdx === -1 || afterIdx === -1 || fromIdx === afterIdx + 1) return
|
|
2501
|
-
|
|
2502
|
-
const rule = rules.splice(fromIdx, 1)[0]
|
|
2503
|
-
const targetIdx = fromIdx < afterIdx ? afterIdx - 1 : afterIdx
|
|
2504
|
-
rules.splice(targetIdx + 1, 0, rule)
|
|
2505
|
-
ruler.__cache__ = null
|
|
2506
|
-
}
|