@peaceroad/markdown-it-strong-ja 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/index.js +1651 -1057
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,61 +1,152 @@
|
|
|
1
|
-
import Token from 'markdown-it/lib/token.mjs'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
while (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
1
|
+
import Token from 'markdown-it/lib/token.mjs'
|
|
2
|
+
import { parseLinkDestination, parseLinkTitle } from 'markdown-it/lib/helpers/index.mjs'
|
|
3
|
+
import { isSpace } from 'markdown-it/lib/common/utils.mjs'
|
|
4
|
+
|
|
5
|
+
const CHAR_ASTERISK = 0x2A // *
|
|
6
|
+
//const CHAR_UNDERSCORE = 0x5F // _
|
|
7
|
+
const CHAR_BACKSLASH = 0x5C // \
|
|
8
|
+
const CHAR_BACKTICK = 0x60 // `
|
|
9
|
+
const CHAR_DOLLAR = 0x24 // $
|
|
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_ASTERISKS = /^\*+$/
|
|
24
|
+
const REG_ATTRS = /{[^{}\n!@#%^&*()]+?}$/
|
|
25
|
+
const REG_PUNCTUATION = /[!-/:-@[-`{-~ ]/
|
|
26
|
+
const REG_JAPANESE = /\p{Script=Hiragana}|\p{Script=Katakana}|\p{Script=Han}|\p{General_Category=Punctuation}|\p{General_Category=Symbol}|\p{General_Category=Format}|\p{Emoji}/u // ひらがな|カタカナ|漢字|句読点|記号|フォーマット文字|絵文字
|
|
27
|
+
|
|
28
|
+
const REG_MARKDOWN_HTML = /^\[[^\[\]]+\]\([^)]+\)$|^<([a-zA-Z][a-zA-Z0-9]*)[^>]*>([^<]+<\/\1>)$|^`[^`]+`$|^\$[^$]+\$$/ // for mixed-language context detection
|
|
29
|
+
|
|
30
|
+
const hasBackslash = (state, start) => {
|
|
31
|
+
if (start <= 0) return false
|
|
32
|
+
const cache = state.__strongJaBackslashCache
|
|
33
|
+
if (cache && cache.has(start)) {
|
|
34
|
+
return cache.get(start)
|
|
35
|
+
}
|
|
36
|
+
let slashNum = 0
|
|
37
|
+
let i = start - 1
|
|
38
|
+
const src = state.src
|
|
39
|
+
if (i < 0 || src.charCodeAt(i) !== CHAR_BACKSLASH) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
while (i >= 0 && src.charCodeAt(i) === CHAR_BACKSLASH) {
|
|
43
|
+
slashNum++
|
|
44
|
+
i--
|
|
45
|
+
}
|
|
46
|
+
const isEscaped = slashNum % 2 === 1
|
|
47
|
+
if (cache) {
|
|
48
|
+
cache.set(start, isEscaped)
|
|
49
|
+
} else {
|
|
50
|
+
state.__strongJaBackslashCache = new Map([[start, isEscaped]])
|
|
51
|
+
}
|
|
52
|
+
return isEscaped
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const findMatchingBracket = (state, start, max, openChar, closeChar) => {
|
|
56
|
+
let depth = 1
|
|
57
|
+
let pos = start + 1
|
|
58
|
+
const src = state.src
|
|
59
|
+
while (pos < max) {
|
|
60
|
+
const ch = src.charCodeAt(pos)
|
|
61
|
+
if (ch === openChar && !hasBackslash(state, pos)) {
|
|
62
|
+
depth++
|
|
63
|
+
} else if (ch === closeChar && !hasBackslash(state, pos)) {
|
|
64
|
+
depth--
|
|
65
|
+
if (depth === 0) return pos
|
|
66
|
+
}
|
|
67
|
+
pos++
|
|
68
|
+
}
|
|
69
|
+
return -1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const getInlineLabelRanges = (inlineLinkRanges) => {
|
|
73
|
+
if (!inlineLinkRanges || inlineLinkRanges.length === 0) return null
|
|
74
|
+
return inlineLinkRanges.__labelRanges
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const hasInlineLinkLabelCrossing = (inlineLinkRanges, from, to) => {
|
|
78
|
+
if (from >= to) return false
|
|
79
|
+
const labelRanges = getInlineLabelRanges(inlineLinkRanges)
|
|
80
|
+
if (!labelRanges || labelRanges.length === 0) return false
|
|
81
|
+
if (labelRanges.length <= 8) {
|
|
82
|
+
for (let idx = 0; idx < labelRanges.length; idx++) {
|
|
83
|
+
const range = labelRanges[idx]
|
|
84
|
+
if (range.start >= to) break
|
|
85
|
+
if (range.start >= from && range.end >= to) return true
|
|
86
|
+
}
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
let left = 0
|
|
90
|
+
let right = labelRanges.length - 1
|
|
91
|
+
let firstIdx = labelRanges.length
|
|
92
|
+
while (left <= right) {
|
|
93
|
+
const mid = left + Math.floor((right - left) / 2)
|
|
94
|
+
if (labelRanges[mid].start < from) {
|
|
95
|
+
left = mid + 1
|
|
96
|
+
} else {
|
|
97
|
+
firstIdx = mid
|
|
98
|
+
right = mid - 1
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (let idx = firstIdx; idx < labelRanges.length; idx++) {
|
|
102
|
+
const range = labelRanges[idx]
|
|
103
|
+
if (range.start >= to) break
|
|
104
|
+
if (range.end >= to) return true
|
|
105
|
+
}
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
55
109
|
const findRefRangeIndex = (pos, refRanges) => {
|
|
56
110
|
if (!refRanges || refRanges.length === 0) return -1
|
|
57
|
-
|
|
58
|
-
|
|
111
|
+
|
|
112
|
+
const tryIndex = (idx) => {
|
|
113
|
+
if (idx < 0 || idx >= refRanges.length) return -1
|
|
114
|
+
const range = refRanges[idx]
|
|
115
|
+
if (pos >= range.start && pos <= range.end) {
|
|
116
|
+
return range.hasReference ? idx : -1
|
|
117
|
+
}
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const tracker = refRanges.__lastIndexState || (refRanges.__lastIndexState = { idx: 0 })
|
|
122
|
+
let idx = tracker.idx
|
|
123
|
+
if (idx >= refRanges.length) idx = refRanges.length - 1
|
|
124
|
+
let result = tryIndex(idx)
|
|
125
|
+
if (result !== null) {
|
|
126
|
+
tracker.idx = idx
|
|
127
|
+
return result
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (pos < refRanges[idx].start) {
|
|
131
|
+
while (idx > 0 && pos < refRanges[idx].start) {
|
|
132
|
+
idx--
|
|
133
|
+
result = tryIndex(idx)
|
|
134
|
+
if (result !== null) {
|
|
135
|
+
tracker.idx = idx
|
|
136
|
+
return result
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
while (idx < refRanges.length - 1 && pos > refRanges[idx].end) {
|
|
141
|
+
idx++
|
|
142
|
+
result = tryIndex(idx)
|
|
143
|
+
if (result !== null) {
|
|
144
|
+
tracker.idx = idx
|
|
145
|
+
return result
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
59
150
|
let left = 0
|
|
60
151
|
let right = refRanges.length - 1
|
|
61
152
|
while (left <= right) {
|
|
@@ -66,43 +157,43 @@ const findRefRangeIndex = (pos, refRanges) => {
|
|
|
66
157
|
} else if (pos > range.end) {
|
|
67
158
|
left = mid + 1
|
|
68
159
|
} else {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return result
|
|
160
|
+
tracker.idx = mid
|
|
161
|
+
return range.hasReference ? mid : -1
|
|
72
162
|
}
|
|
73
163
|
}
|
|
74
|
-
if (cache) cache.set(pos, -1)
|
|
75
164
|
return -1
|
|
76
165
|
}
|
|
77
|
-
|
|
78
|
-
// Detect reference-link label ranges within the current inline slice
|
|
166
|
+
|
|
167
|
+
// Detect reference-link label ranges within the current inline slice
|
|
79
168
|
const computeReferenceRanges = (state, start, max) => {
|
|
80
169
|
const src = state.src
|
|
81
170
|
const references = state.env && state.env.references
|
|
82
|
-
const
|
|
171
|
+
const referenceCount = state.__strongJaReferenceCount
|
|
172
|
+
const hasReferences = references && (referenceCount !== undefined
|
|
173
|
+
? referenceCount > 0
|
|
174
|
+
: Object.keys(references).length > 0)
|
|
175
|
+
if (!hasReferences) return []
|
|
83
176
|
const firstBracket = src.indexOf('[', start)
|
|
84
177
|
if (firstBracket === -1 || firstBracket >= max) return []
|
|
85
178
|
const ranges = []
|
|
86
179
|
let pos = start
|
|
87
180
|
while (pos < max) {
|
|
88
|
-
if (src.charCodeAt(pos) === CHAR_OPEN_BRACKET && !hasBackslash(state, pos)) {
|
|
89
|
-
const labelClose = findMatchingBracket(state, pos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
181
|
+
if (src.charCodeAt(pos) === CHAR_OPEN_BRACKET && !hasBackslash(state, pos)) {
|
|
182
|
+
const labelClose = findMatchingBracket(state, pos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
90
183
|
if (labelClose !== -1) {
|
|
91
184
|
const nextPos = labelClose + 1
|
|
92
185
|
if (nextPos < max && src.charCodeAt(nextPos) === CHAR_OPEN_BRACKET && !hasBackslash(state, nextPos)) {
|
|
93
186
|
const refClose = findMatchingBracket(state, nextPos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
94
187
|
if (refClose !== -1) {
|
|
95
188
|
let hasReference = false
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
hasReference = !!references[normalizedRef]
|
|
105
|
-
}
|
|
189
|
+
if (refClose === nextPos + 1) {
|
|
190
|
+
const labelRaw = src.slice(pos + 1, labelClose)
|
|
191
|
+
const normalizedLabel = normalizeReferenceCandidate(state, labelRaw, { useClean: true })
|
|
192
|
+
hasReference = !!references[normalizedLabel]
|
|
193
|
+
} else {
|
|
194
|
+
const refRaw = src.slice(nextPos + 1, refClose)
|
|
195
|
+
const normalizedRef = normalizeReferenceCandidate(state, refRaw)
|
|
196
|
+
hasReference = !!references[normalizedRef]
|
|
106
197
|
}
|
|
107
198
|
if (hasReference) {
|
|
108
199
|
ranges.push({ start: pos, end: labelClose, hasReference: true })
|
|
@@ -121,7 +212,82 @@ const computeReferenceRanges = (state, start, max) => {
|
|
|
121
212
|
}
|
|
122
213
|
return ranges
|
|
123
214
|
}
|
|
124
|
-
|
|
215
|
+
|
|
216
|
+
const computeInlineLinkRanges = (state, start, max) => {
|
|
217
|
+
const src = state.src
|
|
218
|
+
const ranges = []
|
|
219
|
+
const labelRanges = []
|
|
220
|
+
let pos = start
|
|
221
|
+
let rangeId = 0
|
|
222
|
+
while (pos < max) {
|
|
223
|
+
if (src.charCodeAt(pos) === CHAR_OPEN_BRACKET && !hasBackslash(state, pos)) {
|
|
224
|
+
const labelClose = findMatchingBracket(state, pos, max, CHAR_OPEN_BRACKET, CHAR_CLOSE_BRACKET)
|
|
225
|
+
if (labelClose === -1) break
|
|
226
|
+
let destStart = labelClose + 1
|
|
227
|
+
while (destStart < max) {
|
|
228
|
+
const ch = src.charCodeAt(destStart)
|
|
229
|
+
if (ch !== CHAR_SPACE && ch !== 0x0A && ch !== 0x09) break
|
|
230
|
+
destStart++
|
|
231
|
+
}
|
|
232
|
+
if (destStart < max && src.charCodeAt(destStart) === CHAR_OPEN_PAREN && !hasBackslash(state, destStart)) {
|
|
233
|
+
const destClose = findMatchingBracket(state, destStart, max, CHAR_OPEN_PAREN, CHAR_CLOSE_PAREN)
|
|
234
|
+
if (destClose !== -1) {
|
|
235
|
+
const labelRange = { start: pos, end: labelClose, kind: 'label', id: rangeId }
|
|
236
|
+
ranges.push(labelRange)
|
|
237
|
+
labelRanges.push(labelRange)
|
|
238
|
+
ranges.push({ start: destStart, end: destClose, kind: 'dest', id: rangeId })
|
|
239
|
+
rangeId++
|
|
240
|
+
pos = destClose + 1
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
pos = labelClose + 1
|
|
245
|
+
continue
|
|
246
|
+
}
|
|
247
|
+
pos++
|
|
248
|
+
}
|
|
249
|
+
if (ranges.length && labelRanges.length) {
|
|
250
|
+
ranges.__labelRanges = labelRanges
|
|
251
|
+
}
|
|
252
|
+
return ranges
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const getInlineRangeCacheMap = (ranges, kind, create) => {
|
|
256
|
+
const prop = kind ? `__cache_${kind}` : '__cache_any'
|
|
257
|
+
let cache = ranges[prop]
|
|
258
|
+
if (!cache && create) {
|
|
259
|
+
cache = new Map()
|
|
260
|
+
ranges[prop] = cache
|
|
261
|
+
}
|
|
262
|
+
return cache
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const findInlineLinkRange = (pos, ranges, kind) => {
|
|
266
|
+
if (!ranges || ranges.length === 0) return null
|
|
267
|
+
const cache = getInlineRangeCacheMap(ranges, kind, false)
|
|
268
|
+
if (cache && cache.has(pos)) return cache.get(pos)
|
|
269
|
+
let left = 0
|
|
270
|
+
let right = ranges.length - 1
|
|
271
|
+
let found = null
|
|
272
|
+
while (left <= right) {
|
|
273
|
+
const mid = left + Math.floor((right - left) / 2)
|
|
274
|
+
const range = ranges[mid]
|
|
275
|
+
if (pos < range.start) {
|
|
276
|
+
right = mid - 1
|
|
277
|
+
} else if (pos > range.end) {
|
|
278
|
+
left = mid + 1
|
|
279
|
+
} else {
|
|
280
|
+
if (!kind || range.kind === kind) {
|
|
281
|
+
found = range
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const storeCache = getInlineRangeCacheMap(ranges, kind, true)
|
|
287
|
+
storeCache.set(pos, found)
|
|
288
|
+
return found
|
|
289
|
+
}
|
|
290
|
+
|
|
125
291
|
const copyInlineTokenFields = (dest, src) => {
|
|
126
292
|
if (src.attrs) dest.attrs = src.attrs
|
|
127
293
|
if (src.map) dest.map = src.map
|
|
@@ -135,21 +301,14 @@ const copyInlineTokenFields = (dest, src) => {
|
|
|
135
301
|
dest.hidden = src.hidden
|
|
136
302
|
}
|
|
137
303
|
|
|
138
|
-
const
|
|
139
|
-
if (state.__strongJaHasCollapsedRefs === undefined) {
|
|
140
|
-
state.__strongJaHasCollapsedRefs = /\[[^\]]*\]\s*\[[^\]]*\]/.test(state.src)
|
|
141
|
-
}
|
|
142
|
-
return state.__strongJaHasCollapsedRefs
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const registerCollapsedRefTarget = (state) => {
|
|
304
|
+
const registerPostProcessTarget = (state) => {
|
|
146
305
|
const env = state.env
|
|
147
|
-
if (!env.
|
|
148
|
-
env.
|
|
149
|
-
env.
|
|
306
|
+
if (!env.__strongJaPostProcessTargets) {
|
|
307
|
+
env.__strongJaPostProcessTargets = []
|
|
308
|
+
env.__strongJaPostProcessTargetSet = typeof WeakSet !== 'undefined' ? new WeakSet() : null
|
|
150
309
|
}
|
|
151
|
-
const targets = env.
|
|
152
|
-
const targetSet = env.
|
|
310
|
+
const targets = env.__strongJaPostProcessTargets
|
|
311
|
+
const targetSet = env.__strongJaPostProcessTargetSet
|
|
153
312
|
if (targetSet) {
|
|
154
313
|
if (targetSet.has(state.tokens)) return
|
|
155
314
|
targetSet.add(state.tokens)
|
|
@@ -160,112 +319,112 @@ const registerCollapsedRefTarget = (state) => {
|
|
|
160
319
|
}
|
|
161
320
|
|
|
162
321
|
const setToken = (state, inlines, opt) => {
|
|
163
|
-
const src = state.src
|
|
164
|
-
let i = 0
|
|
165
|
-
let attrsIsText = {
|
|
166
|
-
val: false,
|
|
167
|
-
tag: '',
|
|
168
|
-
}
|
|
169
|
-
while (i < inlines.length) {
|
|
170
|
-
let type = inlines[i].type
|
|
171
|
-
const tag = type.replace(/(?:_open|_close)$/, '')
|
|
172
|
-
|
|
173
|
-
if (/_open$/.test(type)) {
|
|
174
|
-
const startToken = state.push(type, tag, 1)
|
|
175
|
-
startToken.markup = tag === 'strong' ? '**' : '*'
|
|
176
|
-
attrsIsText = {
|
|
177
|
-
val: true,
|
|
178
|
-
tag: tag,
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (type === 'html_inline') {
|
|
183
|
-
type = 'text'
|
|
184
|
-
}
|
|
185
|
-
if (type === 'text') {
|
|
186
|
-
let content = src.slice(inlines[i].s, inlines[i].e + 1)
|
|
187
|
-
if (REG_ASTERISKS.test(content)) {
|
|
188
|
-
const asteriskToken = state.push(type, '', 0)
|
|
189
|
-
asteriskToken.content = content
|
|
190
|
-
i++
|
|
191
|
-
continue
|
|
192
|
-
}
|
|
193
|
-
if (opt.mditAttrs && attrsIsText.val && i + 1 < inlines.length) {
|
|
194
|
-
const hasImmediatelyAfterAsteriskClose = inlines[i+1].type === attrsIsText.tag + '_close'
|
|
195
|
-
if (hasImmediatelyAfterAsteriskClose && REG_ATTRS.test(content)) {
|
|
196
|
-
const attrsToken = state.push(type, '', 0)
|
|
197
|
-
|
|
198
|
-
const hasBackslashBeforeCurlyAttribute = content.match(/(\\+){/)
|
|
199
|
-
if (hasBackslashBeforeCurlyAttribute) {
|
|
200
|
-
if (hasBackslashBeforeCurlyAttribute[1].length === 1) {
|
|
201
|
-
attrsToken.content = content.replace(/\\{/, '{')
|
|
202
|
-
} else {
|
|
203
|
-
let backSlashNum = Math.floor(hasBackslashBeforeCurlyAttribute[1].length / 2)
|
|
204
|
-
let k = 0
|
|
205
|
-
let backSlash = ''
|
|
206
|
-
while (k < backSlashNum) {
|
|
207
|
-
backSlash += '\\'
|
|
208
|
-
k++
|
|
209
|
-
}
|
|
210
|
-
attrsToken.content = content.replace(/\\+{/, backSlash + '{')
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
attrsToken.content = content
|
|
214
|
-
}
|
|
215
|
-
attrsIsText.val = false
|
|
216
|
-
i++
|
|
217
|
-
continue
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const childTokens = state.md.parseInline(content, state.env)
|
|
222
|
-
if (childTokens[0] && childTokens[0].children) {
|
|
223
|
-
let j = 0
|
|
224
|
-
while (j < childTokens[0].children.length) {
|
|
225
|
-
const t = childTokens[0].children[j]
|
|
226
|
-
if (t.type === 'softbreak' && !opt.mdBreaks) {
|
|
227
|
-
t.type = 'text'
|
|
228
|
-
t.tag = ''
|
|
229
|
-
t.content = '\n'
|
|
230
|
-
}
|
|
231
|
-
if (!opt.mditAttrs && t.tag === 'br') {
|
|
232
|
-
t.tag = ''
|
|
233
|
-
t.content = '\n'
|
|
234
|
-
}
|
|
322
|
+
const src = state.src
|
|
323
|
+
let i = 0
|
|
324
|
+
let attrsIsText = {
|
|
325
|
+
val: false,
|
|
326
|
+
tag: '',
|
|
327
|
+
}
|
|
328
|
+
while (i < inlines.length) {
|
|
329
|
+
let type = inlines[i].type
|
|
330
|
+
const tag = type.replace(/(?:_open|_close)$/, '')
|
|
331
|
+
|
|
332
|
+
if (/_open$/.test(type)) {
|
|
333
|
+
const startToken = state.push(type, tag, 1)
|
|
334
|
+
startToken.markup = tag === 'strong' ? '**' : '*'
|
|
335
|
+
attrsIsText = {
|
|
336
|
+
val: true,
|
|
337
|
+
tag: tag,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (type === 'html_inline') {
|
|
342
|
+
type = 'text'
|
|
343
|
+
}
|
|
344
|
+
if (type === 'text') {
|
|
345
|
+
let content = src.slice(inlines[i].s, inlines[i].e + 1)
|
|
346
|
+
if (REG_ASTERISKS.test(content)) {
|
|
347
|
+
const asteriskToken = state.push(type, '', 0)
|
|
348
|
+
asteriskToken.content = content
|
|
349
|
+
i++
|
|
350
|
+
continue
|
|
351
|
+
}
|
|
352
|
+
if (opt.mditAttrs && attrsIsText.val && i + 1 < inlines.length) {
|
|
353
|
+
const hasImmediatelyAfterAsteriskClose = inlines[i+1].type === attrsIsText.tag + '_close'
|
|
354
|
+
if (hasImmediatelyAfterAsteriskClose && REG_ATTRS.test(content)) {
|
|
355
|
+
const attrsToken = state.push(type, '', 0)
|
|
356
|
+
|
|
357
|
+
const hasBackslashBeforeCurlyAttribute = content.match(/(\\+){/)
|
|
358
|
+
if (hasBackslashBeforeCurlyAttribute) {
|
|
359
|
+
if (hasBackslashBeforeCurlyAttribute[1].length === 1) {
|
|
360
|
+
attrsToken.content = content.replace(/\\{/, '{')
|
|
361
|
+
} else {
|
|
362
|
+
let backSlashNum = Math.floor(hasBackslashBeforeCurlyAttribute[1].length / 2)
|
|
363
|
+
let k = 0
|
|
364
|
+
let backSlash = ''
|
|
365
|
+
while (k < backSlashNum) {
|
|
366
|
+
backSlash += '\\'
|
|
367
|
+
k++
|
|
368
|
+
}
|
|
369
|
+
attrsToken.content = content.replace(/\\+{/, backSlash + '{')
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
attrsToken.content = content
|
|
373
|
+
}
|
|
374
|
+
attrsIsText.val = false
|
|
375
|
+
i++
|
|
376
|
+
continue
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const childTokens = state.md.parseInline(content, state.env)
|
|
381
|
+
if (childTokens[0] && childTokens[0].children) {
|
|
382
|
+
let j = 0
|
|
383
|
+
while (j < childTokens[0].children.length) {
|
|
384
|
+
const t = childTokens[0].children[j]
|
|
385
|
+
if (t.type === 'softbreak' && !opt.mdBreaks) {
|
|
386
|
+
t.type = 'text'
|
|
387
|
+
t.tag = ''
|
|
388
|
+
t.content = '\n'
|
|
389
|
+
}
|
|
390
|
+
if (!opt.mditAttrs && t.tag === 'br') {
|
|
391
|
+
t.tag = ''
|
|
392
|
+
t.content = '\n'
|
|
393
|
+
}
|
|
235
394
|
const token = state.push(t.type, t.tag, t.nesting)
|
|
236
395
|
copyInlineTokenFields(token, t)
|
|
237
|
-
j++
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (/_close$/.test(type)) {
|
|
243
|
-
const closeToken = state.push(type, tag, -1)
|
|
244
|
-
closeToken.markup = tag === 'strong' ? '**' : '*'
|
|
245
|
-
attrsIsText = {
|
|
246
|
-
val: false,
|
|
247
|
-
tag: '',
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
i++
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const pushInlines = (inlines, s, e, len, type, tag, tagType) => {
|
|
256
|
-
const inline = {
|
|
257
|
-
s: s,
|
|
258
|
-
sp: s,
|
|
259
|
-
e: e,
|
|
260
|
-
ep: e,
|
|
261
|
-
len: len,
|
|
262
|
-
type: type,
|
|
263
|
-
check: type === 'text',
|
|
264
|
-
}
|
|
265
|
-
if (tag) inline.tag = [tag, tagType]
|
|
266
|
-
inlines.push(inline)
|
|
267
|
-
}
|
|
268
|
-
|
|
396
|
+
j++
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (/_close$/.test(type)) {
|
|
402
|
+
const closeToken = state.push(type, tag, -1)
|
|
403
|
+
closeToken.markup = tag === 'strong' ? '**' : '*'
|
|
404
|
+
attrsIsText = {
|
|
405
|
+
val: false,
|
|
406
|
+
tag: '',
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
i++
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const pushInlines = (inlines, s, e, len, type, tag, tagType) => {
|
|
415
|
+
const inline = {
|
|
416
|
+
s: s,
|
|
417
|
+
sp: s,
|
|
418
|
+
e: e,
|
|
419
|
+
ep: e,
|
|
420
|
+
len: len,
|
|
421
|
+
type: type,
|
|
422
|
+
check: type === 'text',
|
|
423
|
+
}
|
|
424
|
+
if (tag) inline.tag = [tag, tagType]
|
|
425
|
+
inlines.push(inline)
|
|
426
|
+
}
|
|
427
|
+
|
|
269
428
|
const findNextSymbolPos = (state, n, max, symbol) => {
|
|
270
429
|
const src = state.src
|
|
271
430
|
if (src.charCodeAt(n) !== symbol || hasBackslash(state, n)) return -1
|
|
@@ -274,699 +433,804 @@ const findNextSymbolPos = (state, n, max, symbol) => {
|
|
|
274
433
|
return i
|
|
275
434
|
}
|
|
276
435
|
}
|
|
277
|
-
return -1
|
|
278
|
-
}
|
|
436
|
+
return -1
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const processSymbolPair = (state, n, srcLen, symbol, noMark, textStart, pushInlines) => {
|
|
440
|
+
const nextSymbolPos = findNextSymbolPos(state, n, srcLen, symbol)
|
|
441
|
+
if (nextSymbolPos === -1) {
|
|
442
|
+
return { shouldBreak: false, shouldContinue: false, newN: n, newNoMark: noMark }
|
|
443
|
+
}
|
|
444
|
+
const src = state.src
|
|
445
|
+
const innerText = src.slice(n + 1, nextSymbolPos)
|
|
446
|
+
const markup = src.slice(n, nextSymbolPos + 1)
|
|
447
|
+
const newNoMark = noMark + innerText + markup
|
|
448
|
+
if (nextSymbolPos === srcLen - 1) {
|
|
449
|
+
pushInlines(textStart, nextSymbolPos, nextSymbolPos - textStart + 1, 'text')
|
|
450
|
+
return { shouldBreak: true, newN: nextSymbolPos + 1, newNoMark }
|
|
451
|
+
}
|
|
452
|
+
return { shouldBreak: false, shouldContinue: true, newN: nextSymbolPos + 1, newNoMark }
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const processTextSegment = (inlines, textStart, n, noMark) => {
|
|
456
|
+
if (n !== 0 && noMark.length !== 0) {
|
|
457
|
+
pushInlines(inlines, textStart, n - 1, n - textStart, 'text')
|
|
458
|
+
return ''
|
|
459
|
+
}
|
|
460
|
+
return noMark
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const createInlines = (state, start, max, opt) => {
|
|
464
|
+
const src = state.src
|
|
465
|
+
const srcLen = max
|
|
466
|
+
const htmlEnabled = state.md.options.html
|
|
467
|
+
let n = start
|
|
468
|
+
let inlines = []
|
|
469
|
+
let noMark = ''
|
|
470
|
+
let textStart = n
|
|
471
|
+
|
|
472
|
+
// Infinite loop prevention
|
|
473
|
+
const maxIterations = srcLen * 2 // Safe upper bound
|
|
474
|
+
let iterations = 0
|
|
475
|
+
|
|
476
|
+
while (n < srcLen) {
|
|
477
|
+
// Prevent infinite loops
|
|
478
|
+
iterations++
|
|
479
|
+
if (iterations > maxIterations) {
|
|
480
|
+
// Add remaining text as-is and exit safely
|
|
481
|
+
if (textStart < srcLen) {
|
|
482
|
+
pushInlines(inlines, textStart, srcLen - 1, srcLen - textStart, 'text')
|
|
483
|
+
}
|
|
484
|
+
break
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const currentChar = src.charCodeAt(n)
|
|
488
|
+
|
|
489
|
+
// Unified escape check
|
|
490
|
+
let isEscaped = false
|
|
491
|
+
if (currentChar === CHAR_ASTERISK || currentChar === CHAR_BACKTICK ||
|
|
492
|
+
(opt.dollarMath && currentChar === CHAR_DOLLAR) ||
|
|
493
|
+
(htmlEnabled && currentChar === CHAR_LT)) {
|
|
494
|
+
isEscaped = hasBackslash(state, n)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Asterisk handling
|
|
498
|
+
if (currentChar === CHAR_ASTERISK) {
|
|
499
|
+
if (!isEscaped) {
|
|
500
|
+
noMark = processTextSegment(inlines, textStart, n, noMark)
|
|
501
|
+
if (n === srcLen - 1) {
|
|
502
|
+
pushInlines(inlines, n, n, 1, '')
|
|
503
|
+
break
|
|
504
|
+
}
|
|
505
|
+
let i = n + 1
|
|
506
|
+
while (i < srcLen && src.charCodeAt(i) === CHAR_ASTERISK) {
|
|
507
|
+
i++
|
|
508
|
+
}
|
|
509
|
+
if (i === srcLen) {
|
|
510
|
+
pushInlines(inlines, n, i - 1, i - n, '')
|
|
511
|
+
} else {
|
|
512
|
+
pushInlines(inlines, n, i - 1, i - n, '')
|
|
513
|
+
textStart = i
|
|
514
|
+
}
|
|
515
|
+
n = i
|
|
516
|
+
continue
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Inline code (backticks)
|
|
521
|
+
if (currentChar === CHAR_BACKTICK) {
|
|
522
|
+
if (!isEscaped) {
|
|
523
|
+
const result = processSymbolPair(state, n, srcLen, CHAR_BACKTICK, noMark, textStart,
|
|
524
|
+
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
525
|
+
if (result.shouldBreak) break
|
|
526
|
+
if (result.shouldContinue) {
|
|
527
|
+
n = result.newN
|
|
528
|
+
noMark = result.newNoMark
|
|
529
|
+
continue
|
|
530
|
+
}
|
|
531
|
+
noMark = result.newNoMark
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Inline math ($...$)
|
|
536
|
+
if (opt.dollarMath && currentChar === CHAR_DOLLAR) {
|
|
537
|
+
if (!isEscaped) {
|
|
538
|
+
const result = processSymbolPair(state, n, srcLen, CHAR_DOLLAR, noMark, textStart,
|
|
539
|
+
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
540
|
+
if (result.shouldBreak) break
|
|
541
|
+
if (result.shouldContinue) {
|
|
542
|
+
n = result.newN
|
|
543
|
+
noMark = result.newNoMark
|
|
544
|
+
continue
|
|
545
|
+
}
|
|
546
|
+
noMark = result.newNoMark
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// HTML tags
|
|
551
|
+
if (htmlEnabled && currentChar === CHAR_LT) {
|
|
552
|
+
if (!isEscaped) {
|
|
553
|
+
let foundClosingTag = false
|
|
554
|
+
for (let i = n + 1; i < srcLen; i++) {
|
|
555
|
+
if (src.charCodeAt(i) === CHAR_GT && !hasBackslash(state, i)) {
|
|
556
|
+
noMark = processTextSegment(inlines, textStart, n, noMark)
|
|
557
|
+
let tag = src.slice(n + 1, i)
|
|
558
|
+
let tagType
|
|
559
|
+
if (tag.charCodeAt(0) === CHAR_SLASH) {
|
|
560
|
+
tag = tag.slice(1)
|
|
561
|
+
tagType = 'close'
|
|
562
|
+
} else {
|
|
563
|
+
tagType = 'open'
|
|
564
|
+
}
|
|
565
|
+
pushInlines(inlines, n, i, i - n + 1, 'html_inline', tag, tagType)
|
|
566
|
+
textStart = i + 1
|
|
567
|
+
n = i + 1
|
|
568
|
+
foundClosingTag = true
|
|
569
|
+
break
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (foundClosingTag) {
|
|
573
|
+
continue
|
|
574
|
+
}
|
|
575
|
+
// If no closing tag found, treat as regular character to prevent infinite loops
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Regular character
|
|
580
|
+
noMark += src[n]
|
|
581
|
+
if (n === srcLen - 1) {
|
|
582
|
+
pushInlines(inlines, textStart, n, n - textStart + 1, 'text')
|
|
583
|
+
break
|
|
584
|
+
}
|
|
585
|
+
n++
|
|
586
|
+
}
|
|
587
|
+
return inlines
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const pushMark = (marks, opts) => {
|
|
591
|
+
// Maintain sorted order during insertion
|
|
592
|
+
const newMark = {
|
|
593
|
+
nest: opts.nest,
|
|
594
|
+
s: opts.s,
|
|
595
|
+
e: opts.e,
|
|
596
|
+
len: opts.len,
|
|
597
|
+
oLen: opts.oLen,
|
|
598
|
+
type: opts.type
|
|
599
|
+
}
|
|
600
|
+
// Binary search for insertion point to maintain sorted order
|
|
601
|
+
let left = 0
|
|
602
|
+
let right = marks.length
|
|
603
|
+
while (left < right) {
|
|
604
|
+
const mid = Math.floor((left + right) / 2)
|
|
605
|
+
if (marks[mid].s <= newMark.s) {
|
|
606
|
+
left = mid + 1
|
|
607
|
+
} else {
|
|
608
|
+
right = mid
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
marks.splice(left, 0, newMark)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const setStrong = (state, inlines, marks, n, memo, opt, nestTracker, refRanges, inlineLinkRanges) => {
|
|
616
|
+
if (opt.disallowMixed === true) {
|
|
617
|
+
let i = n + 1
|
|
618
|
+
const inlinesLength = inlines.length
|
|
619
|
+
while (i < inlinesLength) {
|
|
620
|
+
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
621
|
+
if (inlines[i].type !== '') { i++; continue }
|
|
622
|
+
|
|
623
|
+
if (inlines[i].len > 1) {
|
|
624
|
+
const mixedCheck = checkMixedLanguagePattern(state, inlines, n, i, opt)
|
|
625
|
+
if (mixedCheck.shouldBlock) {
|
|
626
|
+
return [n, 0]
|
|
627
|
+
}
|
|
628
|
+
break
|
|
629
|
+
}
|
|
630
|
+
i++
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const strongOpenRange = findRefRangeIndex(inlines[n].s, refRanges)
|
|
635
|
+
const openLinkRange = findInlineLinkRange(inlines[n].s, inlineLinkRanges)
|
|
636
|
+
let i = n + 1
|
|
637
|
+
let j = 0
|
|
638
|
+
let nest = 0
|
|
639
|
+
let insideTagsIsClose = 1
|
|
640
|
+
const inlinesLength = inlines.length
|
|
641
|
+
while (i < inlinesLength) {
|
|
642
|
+
if (inlines[i].type !== '') { i++; continue }
|
|
643
|
+
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
644
|
+
if (inlines[i].type === 'html_inline') {
|
|
645
|
+
inlines[i].check = true
|
|
646
|
+
insideTagsIsClose = checkInsideTags(inlines, i, memo)
|
|
647
|
+
if (insideTagsIsClose === -1) return [n, nest]
|
|
648
|
+
if (insideTagsIsClose === 0) { i++; continue }
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (inlineLinkRanges && inlineLinkRanges.length > 0 &&
|
|
652
|
+
hasInlineLinkLabelCrossing(inlineLinkRanges, inlines[n].ep + 1, inlines[i].sp)) {
|
|
653
|
+
i++
|
|
654
|
+
continue
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const closeRange = findRefRangeIndex(inlines[i].s, refRanges)
|
|
658
|
+
if (strongOpenRange !== closeRange) { i++; continue }
|
|
659
|
+
|
|
660
|
+
const closeLinkRange = findInlineLinkRange(inlines[i].s, inlineLinkRanges)
|
|
661
|
+
if (openLinkRange || closeLinkRange) {
|
|
662
|
+
if (!openLinkRange || !closeLinkRange || openLinkRange.id !== closeLinkRange.id || openLinkRange.kind !== closeLinkRange.kind) {
|
|
663
|
+
i++
|
|
664
|
+
continue
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
669
|
+
if (nest === -1) return [n, nest]
|
|
670
|
+
|
|
671
|
+
if (inlines[i].len === 1 && inlines[n].len > 2) {
|
|
672
|
+
pushMark(marks, {
|
|
673
|
+
nest: nest,
|
|
674
|
+
s: inlines[n].ep,
|
|
675
|
+
e: inlines[n].ep,
|
|
676
|
+
len: 1,
|
|
677
|
+
oLen: inlines[n].len - 1,
|
|
678
|
+
type: 'em_open'
|
|
679
|
+
})
|
|
680
|
+
pushMark(marks, {
|
|
681
|
+
nest: nest,
|
|
682
|
+
s: inlines[i].sp,
|
|
683
|
+
e: inlines[i].ep,
|
|
684
|
+
len: 1,
|
|
685
|
+
oLen: inlines[i].len - 1,
|
|
686
|
+
type: 'em_close'
|
|
687
|
+
})
|
|
688
|
+
inlines[n].len -= 1
|
|
689
|
+
inlines[n].ep -= 1
|
|
690
|
+
inlines[i].len -= 1
|
|
691
|
+
if (inlines[i].len > 0) inlines[i].sp += 1
|
|
692
|
+
if (insideTagsIsClose === 1) {
|
|
693
|
+
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges, inlineLinkRanges)
|
|
694
|
+
n = newN
|
|
695
|
+
nest = newNest
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
let strongNum = Math.trunc(Math.min(inlines[n].len, inlines[i].len) / 2)
|
|
699
|
+
|
|
700
|
+
if (inlines[i].len > 1) {
|
|
701
|
+
if (hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges)) {
|
|
702
|
+
if (memo.inlineMarkEnd) {
|
|
703
|
+
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
704
|
+
if (inlines[i].len === 0) { i++; continue }
|
|
705
|
+
} else {
|
|
706
|
+
return [n, nest]
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
j = 0
|
|
711
|
+
while (j < strongNum) {
|
|
712
|
+
pushMark(marks, {
|
|
713
|
+
nest: nest + strongNum - 1 - j,
|
|
714
|
+
s: inlines[n].ep - 1,
|
|
715
|
+
e: inlines[n].ep,
|
|
716
|
+
len: 2,
|
|
717
|
+
oLen: inlines[n].len - 2,
|
|
718
|
+
type: 'strong_open'
|
|
719
|
+
})
|
|
720
|
+
inlines[n].ep -= 2
|
|
721
|
+
inlines[n].len -= 2
|
|
722
|
+
pushMark(marks, {
|
|
723
|
+
nest: nest + strongNum - 1 - j,
|
|
724
|
+
s: inlines[i].sp,
|
|
725
|
+
e: inlines[i].sp + 1,
|
|
726
|
+
len: 2,
|
|
727
|
+
oLen: inlines[i].len - 2,
|
|
728
|
+
type: 'strong_close'
|
|
729
|
+
})
|
|
730
|
+
inlines[i].sp += 2
|
|
731
|
+
inlines[i].len -= 2
|
|
732
|
+
j++
|
|
733
|
+
}
|
|
734
|
+
if (inlines[n].len === 0) return [n, nest]
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (inlines[n].len === 1 && inlines[i].len > 0) {
|
|
738
|
+
nest++
|
|
739
|
+
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, nest, nestTracker, refRanges, inlineLinkRanges)
|
|
740
|
+
n = newN
|
|
741
|
+
nest = newNest
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
i++
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (n == 0 && memo.inlineMarkEnd) {
|
|
748
|
+
marks.push(...createMarks(state, inlines, n + 1, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
749
|
+
}
|
|
750
|
+
return [n, nest]
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const checkInsideTags = (inlines, i, memo) => {
|
|
754
|
+
if (inlines[i].tag === undefined) return 0
|
|
755
|
+
const tagName = inlines[i].tag[0].toLowerCase()
|
|
756
|
+
if (memo.htmlTags[tagName] === undefined) {
|
|
757
|
+
memo.htmlTags[tagName] = 0
|
|
758
|
+
}
|
|
759
|
+
if (inlines[i].tag[1] === 'open') {
|
|
760
|
+
memo.htmlTags[tagName] += 1
|
|
761
|
+
}
|
|
762
|
+
if (inlines[i].tag[1] === 'close') {
|
|
763
|
+
memo.htmlTags[tagName] -= 1
|
|
764
|
+
}
|
|
765
|
+
if (memo.htmlTags[tagName] < 0) {
|
|
766
|
+
return -1
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Direct check instead of Object.values().every()
|
|
770
|
+
for (const count of Object.values(memo.htmlTags)) {
|
|
771
|
+
if (count !== 0) return 0
|
|
772
|
+
}
|
|
773
|
+
return 1
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Check if character is ASCII punctuation or space
|
|
777
|
+
// Covers: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and space
|
|
778
|
+
const isPunctuation = (ch) => {
|
|
779
|
+
if (!ch) return false
|
|
780
|
+
const code = ch.charCodeAt(0)
|
|
781
|
+
// ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
|
782
|
+
return (code >= 33 && code <= 47) || (code >= 58 && code <= 64) ||
|
|
783
|
+
(code >= 91 && code <= 96) || (code >= 123 && code <= 126) || code === 32
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Check if character is Japanese (hiragana, katakana, kanji, punctuation, symbols, format chars, emoji)
|
|
787
|
+
// Uses fast Unicode range checks for common cases, falls back to REG_JAPANESE for complex Unicode
|
|
788
|
+
const isJapanese = (ch) => {
|
|
789
|
+
if (!ch) return false
|
|
790
|
+
const code = ch.charCodeAt(0)
|
|
791
|
+
// Fast ASCII check first
|
|
792
|
+
if (code < 128) return false
|
|
793
|
+
// Hiragana: U+3040-U+309F, Katakana: U+30A0-U+30FF, Kanji: U+4E00-U+9FAF
|
|
794
|
+
return (code >= 0x3040 && code <= 0x309F) ||
|
|
795
|
+
(code >= 0x30A0 && code <= 0x30FF) ||
|
|
796
|
+
(code >= 0x4E00 && code <= 0x9FAF) ||
|
|
797
|
+
// Fallback to regex for complex Unicode cases
|
|
798
|
+
REG_JAPANESE.test(ch)
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Check if character is English (letters, numbers) or other non-Japanese characters
|
|
802
|
+
// Uses REG_JAPANESE and REG_PUNCTUATION to exclude Japanese and punctuation characters
|
|
803
|
+
const isEnglish = (ch) => {
|
|
804
|
+
if (!ch) return false
|
|
805
|
+
const code = ch.charCodeAt(0)
|
|
806
|
+
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
|
|
807
|
+
return true
|
|
808
|
+
}
|
|
809
|
+
if (code < 128) {
|
|
810
|
+
return code === CHAR_SPACE || (code > 126)
|
|
811
|
+
}
|
|
812
|
+
return !REG_JAPANESE.test(ch) && !REG_PUNCTUATION.test(ch)
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const checkMixedLanguagePattern = (state, inlines, n, i, opt) => {
|
|
816
|
+
const src = state.src
|
|
817
|
+
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
818
|
+
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
819
|
+
|
|
820
|
+
const isEnglishPrefix = isEnglish(openPrevChar)
|
|
821
|
+
const isEnglishSuffix = isEnglish(closeNextChar)
|
|
822
|
+
if (!isEnglishPrefix && !isEnglishSuffix) {
|
|
823
|
+
return { hasEnglishContext: false, hasMarkdownOrHtml: false, shouldBlock: false }
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const contentBetween = src.slice(inlines[n].e + 1, inlines[i].s)
|
|
827
|
+
const hasMarkdownOrHtml = REG_MARKDOWN_HTML.test(contentBetween)
|
|
828
|
+
|
|
829
|
+
return {
|
|
830
|
+
hasEnglishContext: true,
|
|
831
|
+
hasMarkdownOrHtml,
|
|
832
|
+
shouldBlock: hasMarkdownOrHtml
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const hasPunctuationOrNonJapanese = (state, inlines, n, i, opt, refRanges) => {
|
|
837
|
+
const src = state.src
|
|
838
|
+
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
839
|
+
const openNextChar = src[inlines[n].e + 1] || ''
|
|
840
|
+
let checkOpenNextChar = isPunctuation(openNextChar)
|
|
841
|
+
if (checkOpenNextChar && (openNextChar === '[' || openNextChar === ']')) {
|
|
842
|
+
const openNextRange = findRefRangeIndex(inlines[n].e + 1, refRanges)
|
|
843
|
+
if (openNextRange !== -1) {
|
|
844
|
+
checkOpenNextChar = false
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const closePrevChar = src[inlines[i].s - 1] || ''
|
|
848
|
+
let checkClosePrevChar = isPunctuation(closePrevChar)
|
|
849
|
+
if (checkClosePrevChar && (closePrevChar === '[' || closePrevChar === ']')) {
|
|
850
|
+
const closePrevRange = findRefRangeIndex(inlines[i].s - 1, refRanges)
|
|
851
|
+
if (closePrevRange !== -1) {
|
|
852
|
+
checkClosePrevChar = false
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
856
|
+
const checkCloseNextChar = (isPunctuation(closeNextChar) || i === inlines.length - 1)
|
|
857
|
+
|
|
858
|
+
if (opt.disallowMixed === false) {
|
|
859
|
+
if (isEnglish(openPrevChar) || isEnglish(closeNextChar)) {
|
|
860
|
+
const contentBetween = src.slice(inlines[n].e + 1, inlines[i].s)
|
|
861
|
+
if (REG_MARKDOWN_HTML.test(contentBetween)) {
|
|
862
|
+
return false
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const result = (checkOpenNextChar || checkClosePrevChar) && !checkCloseNextChar && !(isJapanese(openPrevChar) || isJapanese(closeNextChar))
|
|
868
|
+
return result
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const setEm = (state, inlines, marks, n, memo, opt, sNest, nestTracker, refRanges, inlineLinkRanges) => {
|
|
872
|
+
const emOpenRange = findRefRangeIndex(inlines[n].s, refRanges)
|
|
873
|
+
const openLinkRange = findInlineLinkRange(inlines[n].s, inlineLinkRanges)
|
|
874
|
+
if (opt.disallowMixed === true && !sNest) {
|
|
875
|
+
let i = n + 1
|
|
876
|
+
const inlinesLength = inlines.length
|
|
877
|
+
while (i < inlinesLength) {
|
|
878
|
+
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
879
|
+
if (inlines[i].type !== '') { i++; continue }
|
|
880
|
+
|
|
881
|
+
if (inlines[i].len > 0) {
|
|
882
|
+
const mixedCheck = checkMixedLanguagePattern(state, inlines, n, i, opt)
|
|
883
|
+
if (mixedCheck.shouldBlock) {
|
|
884
|
+
return [n, 0]
|
|
885
|
+
}
|
|
886
|
+
break
|
|
887
|
+
}
|
|
888
|
+
i++
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
let i = n + 1
|
|
893
|
+
let nest = 0
|
|
894
|
+
let strongPNum = 0
|
|
895
|
+
let insideTagsIsClose = 1
|
|
896
|
+
const inlinesLength = inlines.length
|
|
897
|
+
while (i < inlinesLength) {
|
|
898
|
+
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
899
|
+
if (!sNest && inlines[i].type === 'html_inline') {
|
|
900
|
+
inlines[i].check = true
|
|
901
|
+
insideTagsIsClose = checkInsideTags(inlines, i, memo)
|
|
902
|
+
if (insideTagsIsClose === -1) return [n, nest]
|
|
903
|
+
if (insideTagsIsClose === 0) { i++; continue }
|
|
904
|
+
}
|
|
905
|
+
if (inlines[i].type !== '') { i++; continue }
|
|
906
|
+
|
|
907
|
+
if (inlineLinkRanges && inlineLinkRanges.length > 0 &&
|
|
908
|
+
hasInlineLinkLabelCrossing(inlineLinkRanges, inlines[n].ep + 1, inlines[i].sp)) {
|
|
909
|
+
i++
|
|
910
|
+
continue
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const closeRange = findRefRangeIndex(inlines[i].s, refRanges)
|
|
914
|
+
if (emOpenRange !== closeRange) {
|
|
915
|
+
i++
|
|
916
|
+
continue
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const closeLinkRange = findInlineLinkRange(inlines[i].s, inlineLinkRanges)
|
|
920
|
+
if (openLinkRange || closeLinkRange) {
|
|
921
|
+
if (!openLinkRange || !closeLinkRange || openLinkRange.id !== closeLinkRange.id || openLinkRange.kind !== closeLinkRange.kind) {
|
|
922
|
+
i++
|
|
923
|
+
continue
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const emNum = Math.min(inlines[n].len, inlines[i].len)
|
|
928
|
+
|
|
929
|
+
if (!sNest && emNum !== 1) return [n, sNest, memo]
|
|
930
|
+
|
|
931
|
+
const hasMarkersAtStartAndEnd = (i) => {
|
|
932
|
+
let flag = memo.inlineMarkStart
|
|
933
|
+
if (!flag) return false
|
|
934
|
+
inlinesLength - 1 === i ? flag = true : flag = false
|
|
935
|
+
if (!flag) return false
|
|
936
|
+
inlines[i].len > 1 ? flag = true : flag = false
|
|
937
|
+
return flag
|
|
938
|
+
}
|
|
939
|
+
if (!sNest && inlines[i].len === 2 && !hasMarkersAtStartAndEnd(i)) {
|
|
940
|
+
strongPNum++
|
|
941
|
+
i++
|
|
942
|
+
continue
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (sNest) {
|
|
946
|
+
nest = sNest - 1
|
|
947
|
+
} else {
|
|
948
|
+
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
949
|
+
}
|
|
950
|
+
if (nest === -1) return [n, nest]
|
|
951
|
+
|
|
952
|
+
if (emNum === 1) {
|
|
953
|
+
if (hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges)) {
|
|
954
|
+
if (memo.inlineMarkEnd) {
|
|
955
|
+
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges, inlineLinkRanges))
|
|
956
|
+
|
|
957
|
+
if (inlines[i].len === 0) { i++; continue }
|
|
958
|
+
} else {
|
|
959
|
+
return [n, nest]
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (inlines[i].len < 1) {
|
|
963
|
+
i++; continue;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
pushMark(marks, {
|
|
967
|
+
nest: nest,
|
|
968
|
+
s: inlines[n].ep,
|
|
969
|
+
e: inlines[n].ep,
|
|
970
|
+
len: 1,
|
|
971
|
+
oLen: inlines[n].len - 1,
|
|
972
|
+
type: 'em_open'
|
|
973
|
+
})
|
|
974
|
+
inlines[n].ep -= 1
|
|
975
|
+
inlines[n].len -= 1
|
|
976
|
+
|
|
977
|
+
if (strongPNum % 2 === 0 || inlines[i].len < 2) {
|
|
978
|
+
pushMark(marks, {
|
|
979
|
+
nest: nest,
|
|
980
|
+
s: inlines[i].sp,
|
|
981
|
+
e: inlines[i].sp,
|
|
982
|
+
len: 1,
|
|
983
|
+
oLen: inlines[i].len - 1,
|
|
984
|
+
type: 'em_close'
|
|
985
|
+
})
|
|
986
|
+
inlines[i].sp += 1
|
|
987
|
+
} else {
|
|
988
|
+
pushMark(marks, {
|
|
989
|
+
nest: nest,
|
|
990
|
+
s: inlines[i].ep,
|
|
991
|
+
e: inlines[i].ep,
|
|
992
|
+
len: 1,
|
|
993
|
+
oLen: inlines[i].len - 1,
|
|
994
|
+
type: 'em_close'
|
|
995
|
+
})
|
|
996
|
+
inlines[i].sp = inlines[i].ep - 1
|
|
997
|
+
inlines[i].ep -= 1
|
|
998
|
+
}
|
|
999
|
+
inlines[i].len -= 1
|
|
1000
|
+
if (inlines[n].len === 0) return [n, nest]
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
i++
|
|
1004
|
+
}
|
|
1005
|
+
return [n, nest]
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const setText = (inlines, marks, n, nest) => {
|
|
1009
|
+
pushMark(marks, {
|
|
1010
|
+
nest: nest,
|
|
1011
|
+
s: inlines[n].sp,
|
|
1012
|
+
e: inlines[n].ep,
|
|
1013
|
+
len: inlines[n].len,
|
|
1014
|
+
oLen: -1,
|
|
1015
|
+
type: 'text'
|
|
1016
|
+
})
|
|
1017
|
+
inlines[n].len = 0
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Nest state management
|
|
1021
|
+
const createNestTracker = () => {
|
|
1022
|
+
return {
|
|
1023
|
+
strongNest: 0,
|
|
1024
|
+
emNest: 0,
|
|
1025
|
+
markIndex: 0
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const updateNestTracker = (tracker, marks, targetPos) => {
|
|
1030
|
+
while (tracker.markIndex < marks.length && marks[tracker.markIndex].s <= targetPos) {
|
|
1031
|
+
const mark = marks[tracker.markIndex]
|
|
1032
|
+
if (mark.type === 'strong_open') tracker.strongNest++
|
|
1033
|
+
else if (mark.type === 'strong_close') tracker.strongNest--
|
|
1034
|
+
else if (mark.type === 'em_open') tracker.emNest++
|
|
1035
|
+
else if (mark.type === 'em_close') tracker.emNest--
|
|
1036
|
+
tracker.markIndex++
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
const checkNest = (inlines, marks, n, i, nestTracker) => {
|
|
1041
|
+
if (marks.length === 0) return 1
|
|
1042
|
+
// Update nest state up to current position
|
|
1043
|
+
updateNestTracker(nestTracker, marks, inlines[n].s)
|
|
1044
|
+
|
|
1045
|
+
const parentNest = nestTracker.strongNest + nestTracker.emNest
|
|
1046
|
+
// Check if there's a conflicting close mark before the end position
|
|
1047
|
+
let parentCloseN = nestTracker.markIndex
|
|
1048
|
+
while (parentCloseN < marks.length) {
|
|
1049
|
+
if (marks[parentCloseN].nest === parentNest) break
|
|
1050
|
+
parentCloseN++
|
|
1051
|
+
}
|
|
1052
|
+
if (parentCloseN < marks.length && marks[parentCloseN].s < inlines[i].s) {
|
|
1053
|
+
return -1
|
|
1054
|
+
}
|
|
1055
|
+
return parentNest + 1
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const createMarks = (state, inlines, start, end, memo, opt, refRanges, inlineLinkRanges) => {
|
|
1059
|
+
let marks = []
|
|
1060
|
+
let n = start
|
|
1061
|
+
const nestTracker = createNestTracker()
|
|
1062
|
+
|
|
1063
|
+
while (n < end) {
|
|
1064
|
+
if (inlines[n].type !== '') { n++; continue }
|
|
1065
|
+
let nest = 0
|
|
1066
|
+
|
|
1067
|
+
if (inlines[n].len > 1) {
|
|
1068
|
+
const [newN, newNest] = setStrong(state, inlines, marks, n, memo, opt, nestTracker, refRanges, inlineLinkRanges)
|
|
1069
|
+
n = newN
|
|
1070
|
+
nest = newNest
|
|
1071
|
+
}
|
|
1072
|
+
if (inlines[n].len !== 0) {
|
|
1073
|
+
const [newN2, newNest2] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges, inlineLinkRanges)
|
|
1074
|
+
n = newN2
|
|
1075
|
+
nest = newNest2
|
|
1076
|
+
}
|
|
1077
|
+
if (inlines[n].len !== 0) {
|
|
1078
|
+
setText(inlines, marks, n, nest)
|
|
1079
|
+
}
|
|
1080
|
+
n++
|
|
1081
|
+
}
|
|
1082
|
+
return marks
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const mergeInlinesAndMarks = (inlines, marks) => {
|
|
1086
|
+
// marks array is already sorted, skip sorting
|
|
1087
|
+
const merged = []
|
|
1088
|
+
let markIndex = 0
|
|
1089
|
+
for (const token of inlines) {
|
|
1090
|
+
if (token.type === '') {
|
|
1091
|
+
while (markIndex < marks.length && marks[markIndex].s >= token.s && marks[markIndex].e <= token.e) {
|
|
1092
|
+
merged.push(marks[markIndex])
|
|
1093
|
+
markIndex++
|
|
1094
|
+
}
|
|
1095
|
+
} else {
|
|
1096
|
+
merged.push(token)
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
while (markIndex < marks.length) {
|
|
1100
|
+
merged.push(marks[markIndex++])
|
|
1101
|
+
}
|
|
1102
|
+
return merged
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const isWhitespaceToken = (token) => token && token.type === 'text' && token.content.trim() === ''
|
|
1106
|
+
|
|
1107
|
+
const strongJa = (state, silent, opt) => {
|
|
1108
|
+
if (silent) return false
|
|
1109
|
+
const start = state.pos
|
|
1110
|
+
let max = state.posMax
|
|
1111
|
+
const originalMax = max
|
|
1112
|
+
const src = state.src
|
|
1113
|
+
let attributesSrc
|
|
1114
|
+
if (start > max) return false
|
|
1115
|
+
if (src.charCodeAt(start) !== CHAR_ASTERISK) return false
|
|
1116
|
+
if (hasBackslash(state, start)) return false
|
|
1117
|
+
|
|
1118
|
+
if (start === 0) {
|
|
1119
|
+
state.__strongJaRefRangeCache = null
|
|
1120
|
+
state.__strongJaInlineLinkRangeCache = null
|
|
1121
|
+
state.__strongJaBackslashCache = undefined
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (opt.mditAttrs) {
|
|
1125
|
+
let attrCandidate = false
|
|
1126
|
+
let probe = originalMax - 1
|
|
1127
|
+
while (probe >= start) {
|
|
1128
|
+
const code = src.charCodeAt(probe)
|
|
1129
|
+
if (code === CHAR_CLOSE_CURLY) {
|
|
1130
|
+
attrCandidate = true
|
|
1131
|
+
break
|
|
1132
|
+
}
|
|
1133
|
+
if (code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_NEWLINE) {
|
|
1134
|
+
probe--
|
|
1135
|
+
continue
|
|
1136
|
+
}
|
|
1137
|
+
break
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (attrCandidate) {
|
|
1141
|
+
const attrScanTarget = originalMax === src.length ? src : src.slice(0, originalMax)
|
|
1142
|
+
attributesSrc = attrScanTarget.match(/((\n)? *){([^{}\n!@#%^&*()]+?)} *$/)
|
|
1143
|
+
if (attributesSrc && attributesSrc[3] !== '.') {
|
|
1144
|
+
max = attrScanTarget.slice(0, attributesSrc.index).length
|
|
1145
|
+
if (attributesSrc[2] === '\n') {
|
|
1146
|
+
max = attrScanTarget.slice(0, attributesSrc.index - 1).length
|
|
1147
|
+
}
|
|
1148
|
+
if (hasBackslash(state, attributesSrc.index) && attributesSrc[2] === '' && attributesSrc[1].length === 0) {
|
|
1149
|
+
max = state.posMax
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
const endCurlyKet = attrScanTarget.match(/(\n *){([^{}\n!@#%^&*()]*?)}.*(} *?)$/)
|
|
1153
|
+
if (endCurlyKet) {
|
|
1154
|
+
max -= endCurlyKet[3].length
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
279
1159
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (nextSymbolPos === -1) {
|
|
283
|
-
return { shouldBreak: false, shouldContinue: false, newN: n, newNoMark: noMark }
|
|
284
|
-
}
|
|
285
|
-
const src = state.src
|
|
286
|
-
const innerText = src.slice(n + 1, nextSymbolPos)
|
|
287
|
-
const markup = src.slice(n, nextSymbolPos + 1)
|
|
288
|
-
const newNoMark = noMark + innerText + markup
|
|
289
|
-
if (nextSymbolPos === srcLen - 1) {
|
|
290
|
-
pushInlines(textStart, nextSymbolPos, nextSymbolPos - textStart + 1, 'text')
|
|
291
|
-
return { shouldBreak: true, newN: nextSymbolPos + 1, newNoMark }
|
|
1160
|
+
if (state.__strongJaHasCollapsedRefs === undefined) {
|
|
1161
|
+
state.__strongJaHasCollapsedRefs = /\[[^\]]*\]\s*\[[^\]]*\]/.test(state.src)
|
|
292
1162
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (n !== 0 && noMark.length !== 0) {
|
|
298
|
-
pushInlines(inlines, textStart, n - 1, n - textStart, 'text')
|
|
299
|
-
return ''
|
|
300
|
-
}
|
|
301
|
-
return noMark
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const createInlines = (state, start, max, opt) => {
|
|
305
|
-
const src = state.src
|
|
306
|
-
const srcLen = max
|
|
307
|
-
const htmlEnabled = state.md.options.html
|
|
308
|
-
let n = start
|
|
309
|
-
let inlines = []
|
|
310
|
-
let noMark = ''
|
|
311
|
-
let textStart = n
|
|
312
|
-
|
|
313
|
-
// Infinite loop prevention
|
|
314
|
-
const maxIterations = srcLen * 2 // Safe upper bound
|
|
315
|
-
let iterations = 0
|
|
316
|
-
|
|
317
|
-
while (n < srcLen) {
|
|
318
|
-
// Prevent infinite loops
|
|
319
|
-
iterations++
|
|
320
|
-
if (iterations > maxIterations) {
|
|
321
|
-
// Add remaining text as-is and exit safely
|
|
322
|
-
if (textStart < srcLen) {
|
|
323
|
-
pushInlines(inlines, textStart, srcLen - 1, srcLen - textStart, 'text')
|
|
324
|
-
}
|
|
325
|
-
break
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const currentChar = src.charCodeAt(n)
|
|
329
|
-
|
|
330
|
-
// Unified escape check
|
|
331
|
-
let isEscaped = false
|
|
332
|
-
if (currentChar === CHAR_ASTERISK || currentChar === CHAR_BACKTICK ||
|
|
333
|
-
(opt.dollarMath && currentChar === CHAR_DOLLAR) ||
|
|
334
|
-
(htmlEnabled && currentChar === CHAR_LT)) {
|
|
335
|
-
isEscaped = hasBackslash(state, n)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Asterisk handling
|
|
339
|
-
if (currentChar === CHAR_ASTERISK) {
|
|
340
|
-
if (!isEscaped) {
|
|
341
|
-
noMark = processTextSegment(inlines, textStart, n, noMark)
|
|
342
|
-
if (n === srcLen - 1) {
|
|
343
|
-
pushInlines(inlines, n, n, 1, '')
|
|
344
|
-
break
|
|
345
|
-
}
|
|
346
|
-
let i = n + 1
|
|
347
|
-
while (i < srcLen && src.charCodeAt(i) === CHAR_ASTERISK) {
|
|
348
|
-
i++
|
|
349
|
-
}
|
|
350
|
-
if (i === srcLen) {
|
|
351
|
-
pushInlines(inlines, n, i - 1, i - n, '')
|
|
352
|
-
} else {
|
|
353
|
-
pushInlines(inlines, n, i - 1, i - n, '')
|
|
354
|
-
textStart = i
|
|
355
|
-
}
|
|
356
|
-
n = i
|
|
357
|
-
continue
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Inline code (backticks)
|
|
362
|
-
if (currentChar === CHAR_BACKTICK) {
|
|
363
|
-
if (!isEscaped) {
|
|
364
|
-
const result = processSymbolPair(state, n, srcLen, CHAR_BACKTICK, noMark, textStart,
|
|
365
|
-
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
366
|
-
if (result.shouldBreak) break
|
|
367
|
-
if (result.shouldContinue) {
|
|
368
|
-
n = result.newN
|
|
369
|
-
noMark = result.newNoMark
|
|
370
|
-
continue
|
|
371
|
-
}
|
|
372
|
-
noMark = result.newNoMark
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Inline math ($...$)
|
|
377
|
-
if (opt.dollarMath && currentChar === CHAR_DOLLAR) {
|
|
378
|
-
if (!isEscaped) {
|
|
379
|
-
const result = processSymbolPair(state, n, srcLen, CHAR_DOLLAR, noMark, textStart,
|
|
380
|
-
(start, end, len, type) => pushInlines(inlines, start, end, len, type))
|
|
381
|
-
if (result.shouldBreak) break
|
|
382
|
-
if (result.shouldContinue) {
|
|
383
|
-
n = result.newN
|
|
384
|
-
noMark = result.newNoMark
|
|
385
|
-
continue
|
|
386
|
-
}
|
|
387
|
-
noMark = result.newNoMark
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// HTML tags
|
|
392
|
-
if (htmlEnabled && currentChar === CHAR_LT) {
|
|
393
|
-
if (!isEscaped) {
|
|
394
|
-
let foundClosingTag = false
|
|
395
|
-
for (let i = n + 1; i < srcLen; i++) {
|
|
396
|
-
if (src.charCodeAt(i) === CHAR_GT && !hasBackslash(state, i)) {
|
|
397
|
-
noMark = processTextSegment(inlines, textStart, n, noMark)
|
|
398
|
-
let tag = src.slice(n + 1, i)
|
|
399
|
-
let tagType
|
|
400
|
-
if (tag.charCodeAt(0) === CHAR_SLASH) {
|
|
401
|
-
tag = tag.slice(1)
|
|
402
|
-
tagType = 'close'
|
|
403
|
-
} else {
|
|
404
|
-
tagType = 'open'
|
|
405
|
-
}
|
|
406
|
-
pushInlines(inlines, n, i, i - n + 1, 'html_inline', tag, tagType)
|
|
407
|
-
textStart = i + 1
|
|
408
|
-
n = i + 1
|
|
409
|
-
foundClosingTag = true
|
|
410
|
-
break
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if (foundClosingTag) {
|
|
414
|
-
continue
|
|
415
|
-
}
|
|
416
|
-
// If no closing tag found, treat as regular character to prevent infinite loops
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Regular character
|
|
421
|
-
noMark += src[n]
|
|
422
|
-
if (n === srcLen - 1) {
|
|
423
|
-
pushInlines(inlines, textStart, n, n - textStart + 1, 'text')
|
|
424
|
-
break
|
|
425
|
-
}
|
|
426
|
-
n++
|
|
427
|
-
}
|
|
428
|
-
return inlines
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const pushMark = (marks, opts) => {
|
|
432
|
-
// Maintain sorted order during insertion
|
|
433
|
-
const newMark = {
|
|
434
|
-
nest: opts.nest,
|
|
435
|
-
s: opts.s,
|
|
436
|
-
e: opts.e,
|
|
437
|
-
len: opts.len,
|
|
438
|
-
oLen: opts.oLen,
|
|
439
|
-
type: opts.type
|
|
440
|
-
}
|
|
441
|
-
// Binary search for insertion point to maintain sorted order
|
|
442
|
-
let left = 0
|
|
443
|
-
let right = marks.length
|
|
444
|
-
while (left < right) {
|
|
445
|
-
const mid = Math.floor((left + right) / 2)
|
|
446
|
-
if (marks[mid].s <= newMark.s) {
|
|
447
|
-
left = mid + 1
|
|
448
|
-
} else {
|
|
449
|
-
right = mid
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
marks.splice(left, 0, newMark)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const setStrong = (state, inlines, marks, n, memo, opt, nestTracker, refRanges) => {
|
|
457
|
-
if (opt.disallowMixed === true) {
|
|
458
|
-
let i = n + 1
|
|
459
|
-
const inlinesLength = inlines.length
|
|
460
|
-
while (i < inlinesLength) {
|
|
461
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
462
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
463
|
-
|
|
464
|
-
if (inlines[i].len > 1) {
|
|
465
|
-
const mixedCheck = checkMixedLanguagePattern(state, inlines, n, i, opt)
|
|
466
|
-
if (mixedCheck.shouldBlock) {
|
|
467
|
-
return [n, 0]
|
|
468
|
-
}
|
|
469
|
-
break
|
|
470
|
-
}
|
|
471
|
-
i++
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const strongOpenRange = findRefRangeIndex(inlines[n].s, refRanges)
|
|
476
|
-
let i = n + 1
|
|
477
|
-
let j = 0
|
|
478
|
-
let nest = 0
|
|
479
|
-
let insideTagsIsClose = 1
|
|
480
|
-
const inlinesLength = inlines.length
|
|
481
|
-
while (i < inlinesLength) {
|
|
482
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
483
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
484
|
-
if (inlines[i].type === 'html_inline') {
|
|
485
|
-
inlines[i].check = true
|
|
486
|
-
insideTagsIsClose = checkInsideTags(inlines, i, memo)
|
|
487
|
-
if (insideTagsIsClose === -1) return [n, nest]
|
|
488
|
-
if (insideTagsIsClose === 0) { i++; continue }
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const closeRange = findRefRangeIndex(inlines[i].s, refRanges)
|
|
492
|
-
if (strongOpenRange !== closeRange) { i++; continue }
|
|
493
|
-
|
|
494
|
-
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
495
|
-
if (nest === -1) return [n, nest]
|
|
496
|
-
|
|
497
|
-
if (inlines[i].len === 1 && inlines[n].len > 2) {
|
|
498
|
-
pushMark(marks, {
|
|
499
|
-
nest: nest,
|
|
500
|
-
s: inlines[n].ep,
|
|
501
|
-
e: inlines[n].ep,
|
|
502
|
-
len: 1,
|
|
503
|
-
oLen: inlines[n].len - 1,
|
|
504
|
-
type: 'em_open'
|
|
505
|
-
})
|
|
506
|
-
pushMark(marks, {
|
|
507
|
-
nest: nest,
|
|
508
|
-
s: inlines[i].sp,
|
|
509
|
-
e: inlines[i].ep,
|
|
510
|
-
len: 1,
|
|
511
|
-
oLen: inlines[i].len - 1,
|
|
512
|
-
type: 'em_close'
|
|
513
|
-
})
|
|
514
|
-
inlines[n].len -= 1
|
|
515
|
-
inlines[n].ep -= 1
|
|
516
|
-
inlines[i].len -= 1
|
|
517
|
-
if (inlines[i].len > 0) inlines[i].sp += 1
|
|
518
|
-
if (insideTagsIsClose === 1) {
|
|
519
|
-
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges)
|
|
520
|
-
n = newN
|
|
521
|
-
nest = newNest
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
let strongNum = Math.trunc(Math.min(inlines[n].len, inlines[i].len) / 2)
|
|
525
|
-
|
|
526
|
-
if (inlines[i].len > 1) {
|
|
527
|
-
if (hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges)) {
|
|
528
|
-
if (memo.inlineMarkEnd) {
|
|
529
|
-
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges))
|
|
530
|
-
if (inlines[i].len === 0) { i++; continue }
|
|
531
|
-
} else {
|
|
532
|
-
return [n, nest]
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
j = 0
|
|
537
|
-
while (j < strongNum) {
|
|
538
|
-
pushMark(marks, {
|
|
539
|
-
nest: nest + strongNum - 1 - j,
|
|
540
|
-
s: inlines[n].ep - 1,
|
|
541
|
-
e: inlines[n].ep,
|
|
542
|
-
len: 2,
|
|
543
|
-
oLen: inlines[n].len - 2,
|
|
544
|
-
type: 'strong_open'
|
|
545
|
-
})
|
|
546
|
-
inlines[n].ep -= 2
|
|
547
|
-
inlines[n].len -= 2
|
|
548
|
-
pushMark(marks, {
|
|
549
|
-
nest: nest + strongNum - 1 - j,
|
|
550
|
-
s: inlines[i].sp,
|
|
551
|
-
e: inlines[i].sp + 1,
|
|
552
|
-
len: 2,
|
|
553
|
-
oLen: inlines[i].len - 2,
|
|
554
|
-
type: 'strong_close'
|
|
555
|
-
})
|
|
556
|
-
inlines[i].sp += 2
|
|
557
|
-
inlines[i].len -= 2
|
|
558
|
-
j++
|
|
559
|
-
}
|
|
560
|
-
if (inlines[n].len === 0) return [n, nest]
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (inlines[n].len === 1 && inlines[i].len > 0) {
|
|
564
|
-
nest++
|
|
565
|
-
const [newN, newNest] = setEm(state, inlines, marks, n, memo, opt, nest, nestTracker, refRanges)
|
|
566
|
-
n = newN
|
|
567
|
-
nest = newNest
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
i++
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (n == 0 && memo.inlineMarkEnd) {
|
|
574
|
-
marks.push(...createMarks(state, inlines, n + 1, inlinesLength - 1, memo, opt, refRanges))
|
|
575
|
-
}
|
|
576
|
-
return [n, nest]
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const checkInsideTags = (inlines, i, memo) => {
|
|
580
|
-
if (inlines[i].tag === undefined) return 0
|
|
581
|
-
const tagName = inlines[i].tag[0].toLowerCase()
|
|
582
|
-
if (memo.htmlTags[tagName] === undefined) {
|
|
583
|
-
memo.htmlTags[tagName] = 0
|
|
584
|
-
}
|
|
585
|
-
if (inlines[i].tag[1] === 'open') {
|
|
586
|
-
memo.htmlTags[tagName] += 1
|
|
587
|
-
}
|
|
588
|
-
if (inlines[i].tag[1] === 'close') {
|
|
589
|
-
memo.htmlTags[tagName] -= 1
|
|
590
|
-
}
|
|
591
|
-
if (memo.htmlTags[tagName] < 0) {
|
|
592
|
-
return -1
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// Direct check instead of Object.values().every()
|
|
596
|
-
for (const count of Object.values(memo.htmlTags)) {
|
|
597
|
-
if (count !== 0) return 0
|
|
598
|
-
}
|
|
599
|
-
return 1
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Check if character is ASCII punctuation or space
|
|
603
|
-
// Covers: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and space
|
|
604
|
-
const isPunctuation = (ch) => {
|
|
605
|
-
if (!ch) return false
|
|
606
|
-
const code = ch.charCodeAt(0)
|
|
607
|
-
// ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
|
608
|
-
return (code >= 33 && code <= 47) || (code >= 58 && code <= 64) ||
|
|
609
|
-
(code >= 91 && code <= 96) || (code >= 123 && code <= 126) || code === 32
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Check if character is Japanese (hiragana, katakana, kanji, punctuation, symbols, format chars, emoji)
|
|
613
|
-
// Uses fast Unicode range checks for common cases, falls back to REG_JAPANESE for complex Unicode
|
|
614
|
-
const isJapanese = (ch) => {
|
|
615
|
-
if (!ch) return false
|
|
616
|
-
const code = ch.charCodeAt(0)
|
|
617
|
-
// Fast ASCII check first
|
|
618
|
-
if (code < 128) return false
|
|
619
|
-
// Hiragana: U+3040-U+309F, Katakana: U+30A0-U+30FF, Kanji: U+4E00-U+9FAF
|
|
620
|
-
return (code >= 0x3040 && code <= 0x309F) ||
|
|
621
|
-
(code >= 0x30A0 && code <= 0x30FF) ||
|
|
622
|
-
(code >= 0x4E00 && code <= 0x9FAF) ||
|
|
623
|
-
// Fallback to regex for complex Unicode cases
|
|
624
|
-
REG_JAPANESE.test(ch)
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Check if character is English (letters, numbers) or other non-Japanese characters
|
|
628
|
-
// Uses REG_JAPANESE and REG_PUNCTUATION to exclude Japanese and punctuation characters
|
|
629
|
-
const isEnglish = (ch) => {
|
|
630
|
-
if (!ch) return false
|
|
631
|
-
const code = ch.charCodeAt(0)
|
|
632
|
-
if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
|
|
633
|
-
return true
|
|
634
|
-
}
|
|
635
|
-
if (code < 128) {
|
|
636
|
-
return code === CHAR_SPACE || (code > 126)
|
|
637
|
-
}
|
|
638
|
-
return !REG_JAPANESE.test(ch) && !REG_PUNCTUATION.test(ch)
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const checkMixedLanguagePattern = (state, inlines, n, i, opt) => {
|
|
642
|
-
const src = state.src
|
|
643
|
-
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
644
|
-
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
645
|
-
|
|
646
|
-
const isEnglishPrefix = isEnglish(openPrevChar)
|
|
647
|
-
const isEnglishSuffix = isEnglish(closeNextChar)
|
|
648
|
-
if (!isEnglishPrefix && !isEnglishSuffix) {
|
|
649
|
-
return { hasEnglishContext: false, hasMarkdownOrHtml: false, shouldBlock: false }
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const contentBetween = src.slice(inlines[n].e + 1, inlines[i].s)
|
|
653
|
-
const hasMarkdownOrHtml = REG_MARKDOWN_HTML.test(contentBetween)
|
|
654
|
-
|
|
655
|
-
return {
|
|
656
|
-
hasEnglishContext: true,
|
|
657
|
-
hasMarkdownOrHtml,
|
|
658
|
-
shouldBlock: hasMarkdownOrHtml
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const hasPunctuationOrNonJapanese = (state, inlines, n, i, opt, refRanges) => {
|
|
663
|
-
const src = state.src
|
|
664
|
-
const openPrevChar = src[inlines[n].s - 1] || ''
|
|
665
|
-
const openNextChar = src[inlines[n].e + 1] || ''
|
|
666
|
-
let checkOpenNextChar = isPunctuation(openNextChar)
|
|
667
|
-
if (checkOpenNextChar && (openNextChar === '[' || openNextChar === ']')) {
|
|
668
|
-
const openNextRange = findRefRangeIndex(inlines[n].e + 1, refRanges)
|
|
669
|
-
if (openNextRange !== -1) {
|
|
670
|
-
checkOpenNextChar = false
|
|
671
|
-
}
|
|
1163
|
+
|
|
1164
|
+
if (state.__strongJaReferenceCount === undefined) {
|
|
1165
|
+
const references = state.env && state.env.references
|
|
1166
|
+
state.__strongJaReferenceCount = references ? Object.keys(references).length : 0
|
|
672
1167
|
}
|
|
673
|
-
|
|
674
|
-
let
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1168
|
+
|
|
1169
|
+
let refRanges = []
|
|
1170
|
+
const hasReferenceDefinitions = state.__strongJaReferenceCount > 0
|
|
1171
|
+
if (hasReferenceDefinitions) {
|
|
1172
|
+
const refCache = state.__strongJaRefRangeCache
|
|
1173
|
+
if (refCache && refCache.max === max && refCache.start <= start) {
|
|
1174
|
+
refRanges = refCache.ranges
|
|
1175
|
+
} else {
|
|
1176
|
+
refRanges = computeReferenceRanges(state, start, max)
|
|
1177
|
+
state.__strongJaRefRangeCache = { start, max, ranges: refRanges }
|
|
679
1178
|
}
|
|
680
1179
|
}
|
|
681
|
-
const closeNextChar = src[inlines[i].e + 1] || ''
|
|
682
|
-
const checkCloseNextChar = (isPunctuation(closeNextChar) || i === inlines.length - 1)
|
|
683
|
-
|
|
684
|
-
if (opt.disallowMixed === false) {
|
|
685
|
-
if (isEnglish(openPrevChar) || isEnglish(closeNextChar)) {
|
|
686
|
-
const contentBetween = src.slice(inlines[n].e + 1, inlines[i].s)
|
|
687
|
-
if (REG_MARKDOWN_HTML.test(contentBetween)) {
|
|
688
|
-
return false
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
const result = (checkOpenNextChar || checkClosePrevChar) && !checkCloseNextChar && !(isJapanese(openPrevChar) || isJapanese(closeNextChar))
|
|
694
|
-
return result
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const setEm = (state, inlines, marks, n, memo, opt, sNest, nestTracker, refRanges) => {
|
|
698
|
-
const emOpenRange = findRefRangeIndex(inlines[n].s, refRanges)
|
|
699
|
-
if (opt.disallowMixed === true && !sNest) {
|
|
700
|
-
let i = n + 1
|
|
701
|
-
const inlinesLength = inlines.length
|
|
702
|
-
while (i < inlinesLength) {
|
|
703
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
704
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
705
|
-
|
|
706
|
-
if (inlines[i].len > 0) {
|
|
707
|
-
const mixedCheck = checkMixedLanguagePattern(state, inlines, n, i, opt)
|
|
708
|
-
if (mixedCheck.shouldBlock) {
|
|
709
|
-
return [n, 0]
|
|
710
|
-
}
|
|
711
|
-
break
|
|
712
|
-
}
|
|
713
|
-
i++
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
let i = n + 1
|
|
718
|
-
let nest = 0
|
|
719
|
-
let strongPNum = 0
|
|
720
|
-
let insideTagsIsClose = 1
|
|
721
|
-
const inlinesLength = inlines.length
|
|
722
|
-
while (i < inlinesLength) {
|
|
723
|
-
if (inlines[i].len === 0 || inlines[i].check) { i++; continue }
|
|
724
|
-
if (!sNest && inlines[i].type === 'html_inline') {
|
|
725
|
-
inlines.check = true
|
|
726
|
-
insideTagsIsClose = checkInsideTags(inlines, i, memo)
|
|
727
|
-
if (insideTagsIsClose === -1) return [n, nest]
|
|
728
|
-
if (insideTagsIsClose === 0) { i++; continue }
|
|
729
|
-
}
|
|
730
|
-
if (inlines[i].type !== '') { i++; continue }
|
|
731
|
-
|
|
732
|
-
const closeRange = findRefRangeIndex(inlines[i].s, refRanges)
|
|
733
|
-
if (emOpenRange !== closeRange) {
|
|
734
|
-
i++
|
|
735
|
-
continue
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
const emNum = Math.min(inlines[n].len, inlines[i].len)
|
|
739
|
-
|
|
740
|
-
if (!sNest && emNum !== 1) return [n, sNest, memo]
|
|
741
|
-
|
|
742
|
-
const hasMarkersAtStartAndEnd = (i) => {
|
|
743
|
-
let flag = memo.inlineMarkStart
|
|
744
|
-
if (!flag) return false
|
|
745
|
-
inlinesLength - 1 === i ? flag = true : flag = false
|
|
746
|
-
if (!flag) return false
|
|
747
|
-
inlines[i].len > 1 ? flag = true : flag = false
|
|
748
|
-
return flag
|
|
749
|
-
}
|
|
750
|
-
if (!sNest && inlines[i].len === 2 && !hasMarkersAtStartAndEnd(i)) {
|
|
751
|
-
strongPNum++
|
|
752
|
-
i++
|
|
753
|
-
continue
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (sNest) {
|
|
757
|
-
nest = sNest - 1
|
|
758
|
-
} else {
|
|
759
|
-
nest = checkNest(inlines, marks, n, i, nestTracker)
|
|
760
|
-
}
|
|
761
|
-
if (nest === -1) return [n, nest]
|
|
762
|
-
|
|
763
|
-
if (emNum === 1) {
|
|
764
|
-
if (hasPunctuationOrNonJapanese(state, inlines, n, i, opt, refRanges)) {
|
|
765
|
-
if (memo.inlineMarkEnd) {
|
|
766
|
-
marks.push(...createMarks(state, inlines, i, inlinesLength - 1, memo, opt, refRanges))
|
|
767
|
-
|
|
768
|
-
if (inlines[i].len === 0) { i++; continue }
|
|
769
|
-
} else {
|
|
770
|
-
return [n, nest]
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
if (inlines[i].len < 1) {
|
|
774
|
-
i++; continue;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
pushMark(marks, {
|
|
778
|
-
nest: nest,
|
|
779
|
-
s: inlines[n].ep,
|
|
780
|
-
e: inlines[n].ep,
|
|
781
|
-
len: 1,
|
|
782
|
-
oLen: inlines[n].len - 1,
|
|
783
|
-
type: 'em_open'
|
|
784
|
-
})
|
|
785
|
-
inlines[n].ep -= 1
|
|
786
|
-
inlines[n].len -= 1
|
|
787
|
-
|
|
788
|
-
if (strongPNum % 2 === 0 || inlines[i].len < 2) {
|
|
789
|
-
pushMark(marks, {
|
|
790
|
-
nest: nest,
|
|
791
|
-
s: inlines[i].sp,
|
|
792
|
-
e: inlines[i].sp,
|
|
793
|
-
len: 1,
|
|
794
|
-
oLen: inlines[i].len - 1,
|
|
795
|
-
type: 'em_close'
|
|
796
|
-
})
|
|
797
|
-
inlines[i].sp += 1
|
|
798
|
-
} else {
|
|
799
|
-
pushMark(marks, {
|
|
800
|
-
nest: nest,
|
|
801
|
-
s: inlines[i].ep,
|
|
802
|
-
e: inlines[i].ep,
|
|
803
|
-
len: 1,
|
|
804
|
-
oLen: inlines[i].len - 1,
|
|
805
|
-
type: 'em_close'
|
|
806
|
-
})
|
|
807
|
-
inlines[i].sp = inlines[i].ep - 1
|
|
808
|
-
inlines[i].ep -= 1
|
|
809
|
-
}
|
|
810
|
-
inlines[i].len -= 1
|
|
811
|
-
if (inlines[n].len === 0) return [n, nest]
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
i++
|
|
815
|
-
}
|
|
816
|
-
return [n, nest]
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
const setText = (inlines, marks, n, nest) => {
|
|
820
|
-
pushMark(marks, {
|
|
821
|
-
nest: nest,
|
|
822
|
-
s: inlines[n].sp,
|
|
823
|
-
e: inlines[n].ep,
|
|
824
|
-
len: inlines[n].len,
|
|
825
|
-
oLen: -1,
|
|
826
|
-
type: 'text'
|
|
827
|
-
})
|
|
828
|
-
inlines[n].len = 0
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// Nest state management
|
|
832
|
-
const createNestTracker = () => {
|
|
833
|
-
return {
|
|
834
|
-
strongNest: 0,
|
|
835
|
-
emNest: 0,
|
|
836
|
-
markIndex: 0
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
const updateNestTracker = (tracker, marks, targetPos) => {
|
|
841
|
-
while (tracker.markIndex < marks.length && marks[tracker.markIndex].s <= targetPos) {
|
|
842
|
-
const mark = marks[tracker.markIndex]
|
|
843
|
-
if (mark.type === 'strong_open') tracker.strongNest++
|
|
844
|
-
else if (mark.type === 'strong_close') tracker.strongNest--
|
|
845
|
-
else if (mark.type === 'em_open') tracker.emNest++
|
|
846
|
-
else if (mark.type === 'em_close') tracker.emNest--
|
|
847
|
-
tracker.markIndex++
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const checkNest = (inlines, marks, n, i, nestTracker) => {
|
|
852
|
-
if (marks.length === 0) return 1
|
|
853
|
-
// Update nest state up to current position
|
|
854
|
-
updateNestTracker(nestTracker, marks, inlines[n].s)
|
|
855
|
-
|
|
856
|
-
const parentNest = nestTracker.strongNest + nestTracker.emNest
|
|
857
|
-
// Check if there's a conflicting close mark before the end position
|
|
858
|
-
let parentCloseN = nestTracker.markIndex
|
|
859
|
-
while (parentCloseN < marks.length) {
|
|
860
|
-
if (marks[parentCloseN].nest === parentNest) break
|
|
861
|
-
parentCloseN++
|
|
862
|
-
}
|
|
863
|
-
if (parentCloseN < marks.length && marks[parentCloseN].s < inlines[i].s) {
|
|
864
|
-
return -1
|
|
865
|
-
}
|
|
866
|
-
return parentNest + 1
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
const createMarks = (state, inlines, start, end, memo, opt, refRanges) => {
|
|
870
|
-
let marks = []
|
|
871
|
-
let n = start
|
|
872
|
-
const nestTracker = createNestTracker()
|
|
873
|
-
|
|
874
|
-
while (n < end) {
|
|
875
|
-
if (inlines[n].type !== '') { n++; continue }
|
|
876
|
-
let nest = 0
|
|
877
|
-
|
|
878
|
-
if (inlines[n].len > 1) {
|
|
879
|
-
const [newN, newNest] = setStrong(state, inlines, marks, n, memo, opt, nestTracker, refRanges)
|
|
880
|
-
n = newN
|
|
881
|
-
nest = newNest
|
|
882
|
-
}
|
|
883
|
-
if (inlines[n].len !== 0) {
|
|
884
|
-
const [newN2, newNest2] = setEm(state, inlines, marks, n, memo, opt, null, nestTracker, refRanges)
|
|
885
|
-
n = newN2
|
|
886
|
-
nest = newNest2
|
|
887
|
-
}
|
|
888
|
-
if (inlines[n].len !== 0) {
|
|
889
|
-
setText(inlines, marks, n, nest)
|
|
890
|
-
}
|
|
891
|
-
n++
|
|
892
|
-
}
|
|
893
|
-
return marks
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
const mergeInlinesAndMarks = (inlines, marks) => {
|
|
897
|
-
// marks array is already sorted, skip sorting
|
|
898
|
-
const merged = []
|
|
899
|
-
let markIndex = 0
|
|
900
|
-
for (const token of inlines) {
|
|
901
|
-
if (token.type === '') {
|
|
902
|
-
while (markIndex < marks.length && marks[markIndex].s >= token.s && marks[markIndex].e <= token.e) {
|
|
903
|
-
merged.push(marks[markIndex])
|
|
904
|
-
markIndex++
|
|
905
|
-
}
|
|
906
|
-
} else {
|
|
907
|
-
merged.push(token)
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
while (markIndex < marks.length) {
|
|
911
|
-
merged.push(marks[markIndex++])
|
|
912
|
-
}
|
|
913
|
-
return merged
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
const isWhitespaceToken = (token) => token && token.type === 'text' && token.content.trim() === ''
|
|
917
|
-
|
|
918
|
-
const strongJa = (state, silent, opt) => {
|
|
919
|
-
if (silent) return false
|
|
920
|
-
const start = state.pos
|
|
921
|
-
let max = state.posMax
|
|
922
|
-
const src = state.src
|
|
923
|
-
let attributesSrc
|
|
924
|
-
if (start > max) return false
|
|
925
|
-
if (src.charCodeAt(start) !== CHAR_ASTERISK) return false
|
|
926
|
-
if (hasBackslash(state, start)) return false
|
|
927
|
-
|
|
928
|
-
if (opt.mditAttrs) {
|
|
929
|
-
attributesSrc = src.match(/((\n)? *){([^{}\n!@#%^&*()]+?)} *$/)
|
|
930
|
-
if (attributesSrc && attributesSrc[3] !== '.') {
|
|
931
|
-
max = src.slice(0, attributesSrc.index).length
|
|
932
|
-
if (attributesSrc[2] === '\n') {
|
|
933
|
-
max = src.slice(0, attributesSrc.index - 1).length
|
|
934
|
-
}
|
|
935
|
-
if(hasBackslash(state, attributesSrc.index) && attributesSrc[2] === '' && attributesSrc[1].length === 0) {
|
|
936
|
-
max = state.posMax
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
let endCurlyKet = src.match(/(\n *){([^{}\n!@#%^&*()]*?)}.*(} *?)$/)
|
|
940
|
-
if (endCurlyKet) {
|
|
941
|
-
max -= endCurlyKet[3].length
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
const refRanges = computeReferenceRanges(state, start, max)
|
|
947
1180
|
if (refRanges.length > 0) {
|
|
948
1181
|
state.__strongJaHasCollapsedRefs = true
|
|
949
1182
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1183
|
+
|
|
1184
|
+
let inlineLinkRanges = null
|
|
1185
|
+
const inlineLinkCandidatePos = state.src.indexOf('](', start)
|
|
1186
|
+
const hasInlineLinkCandidate = inlineLinkCandidatePos !== -1 && inlineLinkCandidatePos < max
|
|
1187
|
+
if (hasInlineLinkCandidate) {
|
|
1188
|
+
const inlineCache = state.__strongJaInlineLinkRangeCache
|
|
1189
|
+
if (inlineCache && inlineCache.max === max && inlineCache.start <= start) {
|
|
1190
|
+
inlineLinkRanges = inlineCache.ranges
|
|
1191
|
+
} else {
|
|
1192
|
+
inlineLinkRanges = computeInlineLinkRanges(state, start, max)
|
|
1193
|
+
state.__strongJaInlineLinkRangeCache = { start, max, ranges: inlineLinkRanges }
|
|
1194
|
+
}
|
|
1195
|
+
if (inlineLinkRanges.length > 0) {
|
|
1196
|
+
state.__strongJaHasInlineLinks = true
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
let inlines = createInlines(state, start, max, opt)
|
|
1200
|
+
|
|
1201
|
+
const memo = {
|
|
1202
|
+
html: state.md.options.html,
|
|
1203
|
+
htmlTags: {},
|
|
1204
|
+
inlineMarkStart: src.charCodeAt(0) === CHAR_ASTERISK,
|
|
1205
|
+
inlineMarkEnd: src.charCodeAt(max - 1) === CHAR_ASTERISK,
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
let marks = createMarks(state, inlines, 0, inlines.length, memo, opt, refRanges, inlineLinkRanges)
|
|
1209
|
+
|
|
1210
|
+
inlines = mergeInlinesAndMarks(inlines, marks)
|
|
1211
|
+
|
|
963
1212
|
setToken(state, inlines, opt)
|
|
964
1213
|
|
|
965
|
-
if (
|
|
966
|
-
|
|
967
|
-
|
|
1214
|
+
if (inlineLinkRanges && inlineLinkRanges.length > 0) {
|
|
1215
|
+
const labelSources = []
|
|
1216
|
+
for (let idx = 0; idx < inlineLinkRanges.length; idx++) {
|
|
1217
|
+
const range = inlineLinkRanges[idx]
|
|
1218
|
+
if (range.kind !== 'label') continue
|
|
1219
|
+
labelSources.push(src.slice(range.start + 1, range.end))
|
|
1220
|
+
}
|
|
1221
|
+
if (labelSources.length > 0) {
|
|
1222
|
+
state.tokens.__strongJaInlineLabelSources = labelSources
|
|
1223
|
+
state.tokens.__strongJaInlineLabelIndex = 0
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const needsInlineLinkFix = state.__strongJaHasInlineLinks === true
|
|
1228
|
+
const needsCollapsedRefFix = state.__strongJaHasCollapsedRefs === true
|
|
1229
|
+
if ((needsCollapsedRefFix || needsInlineLinkFix) && !state.__strongJaPostProcessRegistered) {
|
|
1230
|
+
registerPostProcessTarget(state)
|
|
1231
|
+
state.__strongJaPostProcessRegistered = true
|
|
968
1232
|
}
|
|
969
|
-
|
|
1233
|
+
|
|
970
1234
|
if (opt.mditAttrs && max !== state.posMax) {
|
|
971
1235
|
if (!attributesSrc) {
|
|
972
1236
|
state.pos = max
|
|
@@ -976,26 +1240,26 @@ const strongJa = (state, silent, opt) => {
|
|
|
976
1240
|
return true
|
|
977
1241
|
}
|
|
978
1242
|
state.pos = max
|
|
979
|
-
return true
|
|
980
|
-
}
|
|
981
|
-
|
|
1243
|
+
return true
|
|
1244
|
+
}
|
|
1245
|
+
|
|
982
1246
|
// Collapsed reference helpers
|
|
983
1247
|
const buildReferenceLabel = (tokens) => {
|
|
984
|
-
let label = ''
|
|
985
|
-
for (const token of tokens) {
|
|
986
|
-
if (token.type === 'text' || token.type === 'code_inline') {
|
|
987
|
-
label += token.content
|
|
988
|
-
} else if (token.type === 'softbreak' || token.type === 'hardbreak') {
|
|
989
|
-
label += ' '
|
|
990
|
-
} else if (token.type && token.type.endsWith('_open') && token.markup) {
|
|
991
|
-
label += token.markup
|
|
992
|
-
} else if (token.type && token.type.endsWith('_close') && token.markup) {
|
|
993
|
-
label += token.markup
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return label
|
|
997
|
-
}
|
|
998
|
-
|
|
1248
|
+
let label = ''
|
|
1249
|
+
for (const token of tokens) {
|
|
1250
|
+
if (token.type === 'text' || token.type === 'code_inline') {
|
|
1251
|
+
label += token.content
|
|
1252
|
+
} else if (token.type === 'softbreak' || token.type === 'hardbreak') {
|
|
1253
|
+
label += ' '
|
|
1254
|
+
} else if (token.type && token.type.endsWith('_open') && token.markup) {
|
|
1255
|
+
label += token.markup
|
|
1256
|
+
} else if (token.type && token.type.endsWith('_close') && token.markup) {
|
|
1257
|
+
label += token.markup
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
return label
|
|
1261
|
+
}
|
|
1262
|
+
|
|
999
1263
|
const cleanLabelText = (label) => {
|
|
1000
1264
|
return label.replace(/^[*_]+/, '').replace(/[*_]+$/, '')
|
|
1001
1265
|
}
|
|
@@ -1005,44 +1269,63 @@ const normalizeReferenceCandidate = (state, text, { useClean = false } = {}) =>
|
|
|
1005
1269
|
return normalizeRefKey(state, source)
|
|
1006
1270
|
}
|
|
1007
1271
|
|
|
1008
|
-
const
|
|
1272
|
+
const getNormalizeRef = (state) => {
|
|
1273
|
+
if (state.__strongJaNormalizeRef) return state.__strongJaNormalizeRef
|
|
1009
1274
|
const normalize = state.md && state.md.utils && state.md.utils.normalizeReference
|
|
1010
1275
|
? state.md.utils.normalizeReference
|
|
1011
1276
|
: (str) => str.trim().replace(/\s+/g, ' ').toUpperCase()
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
newToken
|
|
1028
|
-
newToken.
|
|
1029
|
-
newToken.
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1277
|
+
state.__strongJaNormalizeRef = normalize
|
|
1278
|
+
return normalize
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const normalizeRefKey = (state, label) => {
|
|
1282
|
+
return getNormalizeRef(state)(label)
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
const adjustTokenLevels = (tokens, startIdx, endIdx, delta) => {
|
|
1286
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
1287
|
+
if (tokens[i]) tokens[i].level += delta
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const cloneTextToken = (source, content) => {
|
|
1292
|
+
const newToken = new Token('text', '', 0)
|
|
1293
|
+
newToken.content = content
|
|
1294
|
+
newToken.level = source.level
|
|
1295
|
+
newToken.markup = source.markup
|
|
1296
|
+
newToken.info = source.info
|
|
1297
|
+
newToken.meta = source.meta ? {...source.meta} : null
|
|
1298
|
+
newToken.block = source.block
|
|
1299
|
+
newToken.hidden = source.hidden
|
|
1300
|
+
return newToken
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Split only text tokens that actually contain bracket characters
|
|
1304
|
+
const splitBracketToken = (tokens, index, options) => {
|
|
1035
1305
|
const token = tokens[index]
|
|
1036
1306
|
if (!token || token.type !== 'text') return false
|
|
1307
|
+
if (token.__strongJaBracketAtomic) return false
|
|
1308
|
+
if (token.__strongJaHasBracket === false) return false
|
|
1037
1309
|
const content = token.content
|
|
1038
|
-
if (!content
|
|
1310
|
+
if (!content) {
|
|
1311
|
+
token.__strongJaHasBracket = false
|
|
1312
|
+
token.__strongJaBracketAtomic = false
|
|
1039
1313
|
return false
|
|
1040
1314
|
}
|
|
1315
|
+
if (token.__strongJaHasBracket !== true) {
|
|
1316
|
+
if (content.indexOf('[') === -1 && content.indexOf(']') === -1) {
|
|
1317
|
+
token.__strongJaHasBracket = false
|
|
1318
|
+
token.__strongJaBracketAtomic = false
|
|
1319
|
+
return false
|
|
1320
|
+
}
|
|
1321
|
+
token.__strongJaHasBracket = true
|
|
1322
|
+
}
|
|
1323
|
+
const splitEmptyPair = options && options.splitEmptyPair
|
|
1041
1324
|
const segments = []
|
|
1042
1325
|
let buffer = ''
|
|
1043
1326
|
let pos = 0
|
|
1044
1327
|
while (pos < content.length) {
|
|
1045
|
-
if (content.startsWith('[]', pos)) {
|
|
1328
|
+
if (!splitEmptyPair && content.startsWith('[]', pos)) {
|
|
1046
1329
|
if (buffer) {
|
|
1047
1330
|
segments.push(buffer)
|
|
1048
1331
|
buffer = ''
|
|
@@ -1065,17 +1348,55 @@ const splitBracketToken = (tokens, index) => {
|
|
|
1065
1348
|
pos++
|
|
1066
1349
|
}
|
|
1067
1350
|
if (buffer) segments.push(buffer)
|
|
1068
|
-
if (segments.length <= 1)
|
|
1351
|
+
if (segments.length <= 1) {
|
|
1352
|
+
if (segments.length === 0) {
|
|
1353
|
+
token.__strongJaHasBracket = false
|
|
1354
|
+
token.__strongJaBracketAtomic = false
|
|
1355
|
+
} else {
|
|
1356
|
+
const seg = segments[0]
|
|
1357
|
+
if (seg === '[' || seg === ']') {
|
|
1358
|
+
token.__strongJaHasBracket = true
|
|
1359
|
+
token.__strongJaBracketAtomic = true
|
|
1360
|
+
} else if (seg === '[]') {
|
|
1361
|
+
token.__strongJaHasBracket = true
|
|
1362
|
+
token.__strongJaBracketAtomic = false
|
|
1363
|
+
} else {
|
|
1364
|
+
token.__strongJaHasBracket = false
|
|
1365
|
+
token.__strongJaBracketAtomic = false
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return false
|
|
1369
|
+
}
|
|
1069
1370
|
token.content = segments[0]
|
|
1371
|
+
if (token.content === '[' || token.content === ']') {
|
|
1372
|
+
token.__strongJaHasBracket = true
|
|
1373
|
+
token.__strongJaBracketAtomic = true
|
|
1374
|
+
} else if (token.content === '[]') {
|
|
1375
|
+
token.__strongJaHasBracket = true
|
|
1376
|
+
token.__strongJaBracketAtomic = false
|
|
1377
|
+
} else {
|
|
1378
|
+
token.__strongJaHasBracket = false
|
|
1379
|
+
token.__strongJaBracketAtomic = false
|
|
1380
|
+
}
|
|
1070
1381
|
let insertIdx = index + 1
|
|
1071
1382
|
for (let s = 1; s < segments.length; s++) {
|
|
1072
1383
|
const newToken = cloneTextToken(token, segments[s])
|
|
1384
|
+
if (segments[s] === '[' || segments[s] === ']') {
|
|
1385
|
+
newToken.__strongJaHasBracket = true
|
|
1386
|
+
newToken.__strongJaBracketAtomic = true
|
|
1387
|
+
} else if (segments[s] === '[]') {
|
|
1388
|
+
newToken.__strongJaHasBracket = true
|
|
1389
|
+
newToken.__strongJaBracketAtomic = false
|
|
1390
|
+
} else {
|
|
1391
|
+
newToken.__strongJaHasBracket = false
|
|
1392
|
+
newToken.__strongJaBracketAtomic = false
|
|
1393
|
+
}
|
|
1073
1394
|
tokens.splice(insertIdx, 0, newToken)
|
|
1074
1395
|
insertIdx++
|
|
1075
1396
|
}
|
|
1076
1397
|
return true
|
|
1077
1398
|
}
|
|
1078
|
-
|
|
1399
|
+
|
|
1079
1400
|
const isBracketToken = (token, bracket) => {
|
|
1080
1401
|
return token && token.type === 'text' && token.content === bracket
|
|
1081
1402
|
}
|
|
@@ -1093,9 +1414,285 @@ const findLinkCloseIndex = (tokens, startIdx) => {
|
|
|
1093
1414
|
return -1
|
|
1094
1415
|
}
|
|
1095
1416
|
|
|
1417
|
+
const consumeCharactersFromTokens = (tokens, startIdx, count) => {
|
|
1418
|
+
let remaining = count
|
|
1419
|
+
let idx = startIdx
|
|
1420
|
+
while (idx < tokens.length && remaining > 0) {
|
|
1421
|
+
const token = tokens[idx]
|
|
1422
|
+
if (!token || token.type !== 'text') {
|
|
1423
|
+
return false
|
|
1424
|
+
}
|
|
1425
|
+
const len = token.content.length
|
|
1426
|
+
if (remaining >= len) {
|
|
1427
|
+
remaining -= len
|
|
1428
|
+
tokens.splice(idx, 1)
|
|
1429
|
+
continue
|
|
1430
|
+
}
|
|
1431
|
+
token.content = token.content.slice(remaining)
|
|
1432
|
+
remaining = 0
|
|
1433
|
+
}
|
|
1434
|
+
return remaining === 0
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
const wrapLabelTokensWithLink = (tokens, labelStartIdx, labelEndIdx, linkOpenToken, linkCloseToken, labelSource) => {
|
|
1438
|
+
const wrapperPairs = []
|
|
1439
|
+
let startIdx = labelStartIdx
|
|
1440
|
+
let endIdx = labelEndIdx
|
|
1441
|
+
while (startIdx > 0) {
|
|
1442
|
+
const prevToken = tokens[startIdx - 1]
|
|
1443
|
+
const nextToken = tokens[endIdx + 1]
|
|
1444
|
+
if (!prevToken || !nextToken) break
|
|
1445
|
+
if (!/_close$/.test(prevToken.type)) break
|
|
1446
|
+
const expectedOpen = prevToken.type.replace('_close', '_open')
|
|
1447
|
+
if (nextToken.type !== expectedOpen) break
|
|
1448
|
+
wrapperPairs.push({
|
|
1449
|
+
base: prevToken.type.replace('_close', ''),
|
|
1450
|
+
tag: prevToken.tag,
|
|
1451
|
+
markup: prevToken.markup
|
|
1452
|
+
})
|
|
1453
|
+
tokens.splice(endIdx + 1, 1)
|
|
1454
|
+
tokens.splice(startIdx - 1, 1)
|
|
1455
|
+
startIdx -= 1
|
|
1456
|
+
endIdx -= 1
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (startIdx > endIdx) {
|
|
1460
|
+
if (labelSource !== undefined && labelSource !== null) {
|
|
1461
|
+
const placeholder = new Token('text', '', 0)
|
|
1462
|
+
placeholder.content = labelSource
|
|
1463
|
+
placeholder.level = linkOpenToken.level + 1
|
|
1464
|
+
tokens.splice(startIdx, 0, placeholder)
|
|
1465
|
+
endIdx = startIdx
|
|
1466
|
+
} else {
|
|
1467
|
+
return startIdx
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
let labelLength = endIdx - startIdx + 1
|
|
1472
|
+
const firstLabelToken = tokens[startIdx]
|
|
1473
|
+
const linkLevel = firstLabelToken ? Math.max(firstLabelToken.level - 1, 0) : 0
|
|
1474
|
+
linkOpenToken.level = linkLevel
|
|
1475
|
+
linkCloseToken.level = linkLevel
|
|
1476
|
+
tokens.splice(startIdx, 0, linkOpenToken)
|
|
1477
|
+
tokens.splice(startIdx + labelLength + 1, 0, linkCloseToken)
|
|
1478
|
+
|
|
1479
|
+
adjustTokenLevels(tokens, startIdx + 1, startIdx + labelLength + 1, 1)
|
|
1480
|
+
|
|
1481
|
+
if (wrapperPairs.length > 0) {
|
|
1482
|
+
let insertIdx = startIdx + 1
|
|
1483
|
+
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
1484
|
+
const pair = wrapperPairs[wp]
|
|
1485
|
+
const innerOpen = new Token(pair.base + '_open', pair.tag, 1)
|
|
1486
|
+
innerOpen.markup = pair.markup
|
|
1487
|
+
innerOpen.level = linkLevel + 1 + wp
|
|
1488
|
+
tokens.splice(insertIdx, 0, innerOpen)
|
|
1489
|
+
insertIdx++
|
|
1490
|
+
labelLength++
|
|
1491
|
+
}
|
|
1492
|
+
let linkClosePos = startIdx + labelLength + 1
|
|
1493
|
+
for (let wp = wrapperPairs.length - 1; wp >= 0; wp--) {
|
|
1494
|
+
const pair = wrapperPairs[wp]
|
|
1495
|
+
const innerClose = new Token(pair.base + '_close', pair.tag, -1)
|
|
1496
|
+
innerClose.markup = pair.markup
|
|
1497
|
+
innerClose.level = linkLevel + 1 + wp
|
|
1498
|
+
tokens.splice(linkClosePos, 0, innerClose)
|
|
1499
|
+
labelLength++
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
return startIdx + labelLength + 2
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
const parseInlineLinkTail = (content, md) => {
|
|
1507
|
+
if (!content || content.charCodeAt(0) !== CHAR_OPEN_PAREN) return null
|
|
1508
|
+
const max = content.length
|
|
1509
|
+
let pos = 1
|
|
1510
|
+
while (pos < max) {
|
|
1511
|
+
const code = content.charCodeAt(pos)
|
|
1512
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
1513
|
+
pos++
|
|
1514
|
+
}
|
|
1515
|
+
if (pos >= max) return null
|
|
1516
|
+
|
|
1517
|
+
let href = ''
|
|
1518
|
+
let destPos = pos
|
|
1519
|
+
if (pos < max && content.charCodeAt(pos) === CHAR_CLOSE_PAREN) {
|
|
1520
|
+
href = ''
|
|
1521
|
+
} else {
|
|
1522
|
+
const dest = parseLinkDestination(content, pos, max)
|
|
1523
|
+
if (!dest.ok) return null
|
|
1524
|
+
href = md.normalizeLink(dest.str)
|
|
1525
|
+
if (!md.validateLink(href)) {
|
|
1526
|
+
return null
|
|
1527
|
+
}
|
|
1528
|
+
pos = dest.pos
|
|
1529
|
+
destPos = dest.pos
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
while (pos < max) {
|
|
1533
|
+
const code = content.charCodeAt(pos)
|
|
1534
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
1535
|
+
pos++
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
let title = ''
|
|
1539
|
+
const titleRes = parseLinkTitle(content, pos, max)
|
|
1540
|
+
if (pos < max && pos !== destPos && titleRes.ok) {
|
|
1541
|
+
title = titleRes.str
|
|
1542
|
+
pos = titleRes.pos
|
|
1543
|
+
while (pos < max) {
|
|
1544
|
+
const code = content.charCodeAt(pos)
|
|
1545
|
+
if (!isSpace(code) && code !== 0x0A) break
|
|
1546
|
+
pos++
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if (pos >= max || content.charCodeAt(pos) !== CHAR_CLOSE_PAREN) {
|
|
1551
|
+
return null
|
|
1552
|
+
}
|
|
1553
|
+
pos++
|
|
1554
|
+
return { href, title, consumed: pos }
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
const INLINE_LINK_BRACKET_SPLIT_OPTIONS = { splitEmptyPair: true }
|
|
1558
|
+
|
|
1559
|
+
const removeGhostLabelText = (tokens, linkCloseToken, labelText) => {
|
|
1560
|
+
if (!labelText) return
|
|
1561
|
+
const closeIdx = tokens.indexOf(linkCloseToken)
|
|
1562
|
+
if (closeIdx === -1) return
|
|
1563
|
+
let idx = closeIdx + 1
|
|
1564
|
+
while (idx < tokens.length) {
|
|
1565
|
+
const token = tokens[idx]
|
|
1566
|
+
if (!token) {
|
|
1567
|
+
idx++
|
|
1568
|
+
continue
|
|
1569
|
+
}
|
|
1570
|
+
if (token.type === 'text') {
|
|
1571
|
+
if (token.content.startsWith(labelText)) {
|
|
1572
|
+
if (token.content.length === labelText.length) {
|
|
1573
|
+
tokens.splice(idx, 1)
|
|
1574
|
+
} else {
|
|
1575
|
+
token.content = token.content.slice(labelText.length)
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
break
|
|
1579
|
+
}
|
|
1580
|
+
if (!/_close$/.test(token.type)) {
|
|
1581
|
+
break
|
|
1582
|
+
}
|
|
1583
|
+
idx++
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
const convertInlineLinks = (tokens, state) => {
|
|
1588
|
+
if (!tokens || tokens.length === 0) return
|
|
1589
|
+
const labelSources = tokens.__strongJaInlineLabelSources
|
|
1590
|
+
let labelSourceIndex = tokens.__strongJaInlineLabelIndex || 0
|
|
1591
|
+
let i = 0
|
|
1592
|
+
while (i < tokens.length) {
|
|
1593
|
+
if (splitBracketToken(tokens, i, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1594
|
+
continue
|
|
1595
|
+
}
|
|
1596
|
+
if (!isBracketToken(tokens[i], '[')) {
|
|
1597
|
+
i++
|
|
1598
|
+
continue
|
|
1599
|
+
}
|
|
1600
|
+
let closeIdx = i + 1
|
|
1601
|
+
let invalid = false
|
|
1602
|
+
while (closeIdx < tokens.length && !isBracketToken(tokens[closeIdx], ']')) {
|
|
1603
|
+
if (splitBracketToken(tokens, closeIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1604
|
+
continue
|
|
1605
|
+
}
|
|
1606
|
+
if (tokens[closeIdx].type === 'link_open') {
|
|
1607
|
+
invalid = true
|
|
1608
|
+
break
|
|
1609
|
+
}
|
|
1610
|
+
closeIdx++
|
|
1611
|
+
}
|
|
1612
|
+
if (invalid || closeIdx >= tokens.length) {
|
|
1613
|
+
i++
|
|
1614
|
+
continue
|
|
1615
|
+
}
|
|
1616
|
+
const currentLabelSource = labelSources && labelSourceIndex < labelSources.length
|
|
1617
|
+
? labelSources[labelSourceIndex]
|
|
1618
|
+
: undefined
|
|
1619
|
+
|
|
1620
|
+
const labelLength = closeIdx - i - 1
|
|
1621
|
+
const needsPlaceholder = labelLength <= 0
|
|
1622
|
+
if (needsPlaceholder && !currentLabelSource) {
|
|
1623
|
+
i++
|
|
1624
|
+
continue
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
let tailIdx = closeIdx + 1
|
|
1628
|
+
let tailContent = ''
|
|
1629
|
+
let parsedTail = null
|
|
1630
|
+
while (tailIdx < tokens.length) {
|
|
1631
|
+
if (splitBracketToken(tokens, tailIdx, INLINE_LINK_BRACKET_SPLIT_OPTIONS)) {
|
|
1632
|
+
continue
|
|
1633
|
+
}
|
|
1634
|
+
const tailToken = tokens[tailIdx]
|
|
1635
|
+
if (tailToken.type !== 'text' || !tailToken.content) {
|
|
1636
|
+
break
|
|
1637
|
+
}
|
|
1638
|
+
tailContent += tailToken.content
|
|
1639
|
+
parsedTail = parseInlineLinkTail(tailContent, state.md)
|
|
1640
|
+
if (parsedTail) break
|
|
1641
|
+
tailIdx++
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
if (!parsedTail) {
|
|
1645
|
+
i++
|
|
1646
|
+
continue
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
if (!consumeCharactersFromTokens(tokens, closeIdx + 1, parsedTail.consumed)) {
|
|
1650
|
+
i++
|
|
1651
|
+
continue
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
tokens.splice(closeIdx, 1)
|
|
1655
|
+
tokens.splice(i, 1)
|
|
1656
|
+
|
|
1657
|
+
const linkOpenToken = new Token('link_open', 'a', 1)
|
|
1658
|
+
linkOpenToken.attrs = [['href', parsedTail.href]]
|
|
1659
|
+
if (parsedTail.title) linkOpenToken.attrPush(['title', parsedTail.title])
|
|
1660
|
+
linkOpenToken.markup = '[]()'
|
|
1661
|
+
linkOpenToken.info = 'auto'
|
|
1662
|
+
const linkCloseToken = new Token('link_close', 'a', -1)
|
|
1663
|
+
linkCloseToken.markup = '[]()'
|
|
1664
|
+
linkCloseToken.info = 'auto'
|
|
1665
|
+
|
|
1666
|
+
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelLength - 1, linkOpenToken, linkCloseToken, currentLabelSource)
|
|
1667
|
+
if (nextIndex === i) {
|
|
1668
|
+
i++
|
|
1669
|
+
continue
|
|
1670
|
+
}
|
|
1671
|
+
if (needsPlaceholder && currentLabelSource) {
|
|
1672
|
+
removeGhostLabelText(tokens, linkCloseToken, currentLabelSource)
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
if (labelSources && labelSources.length > 0) {
|
|
1676
|
+
if (labelSourceIndex < labelSources.length) {
|
|
1677
|
+
labelSourceIndex++
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
i = nextIndex
|
|
1681
|
+
}
|
|
1682
|
+
if (labelSources) {
|
|
1683
|
+
tokens.__strongJaInlineLabelIndex = labelSourceIndex
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1096
1687
|
const convertCollapsedReferenceLinks = (tokens, state) => {
|
|
1097
1688
|
const references = state.env && state.env.references
|
|
1098
|
-
if (!references
|
|
1689
|
+
if (!references) return
|
|
1690
|
+
const referenceCount = state.__strongJaReferenceCount
|
|
1691
|
+
if (referenceCount !== undefined) {
|
|
1692
|
+
if (referenceCount === 0) return
|
|
1693
|
+
} else if (Object.keys(references).length === 0) {
|
|
1694
|
+
return
|
|
1695
|
+
}
|
|
1099
1696
|
|
|
1100
1697
|
let i = 0
|
|
1101
1698
|
while (i < tokens.length) {
|
|
@@ -1116,12 +1713,12 @@ const convertCollapsedReferenceLinks = (tokens, state) => {
|
|
|
1116
1713
|
break
|
|
1117
1714
|
}
|
|
1118
1715
|
closeIdx++
|
|
1119
|
-
}
|
|
1120
|
-
if (closeIdx === -1 || closeIdx >= tokens.length) {
|
|
1121
|
-
i++
|
|
1122
|
-
continue
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1716
|
+
}
|
|
1717
|
+
if (closeIdx === -1 || closeIdx >= tokens.length) {
|
|
1718
|
+
i++
|
|
1719
|
+
continue
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1125
1722
|
if (closeIdx === i + 1) {
|
|
1126
1723
|
i++
|
|
1127
1724
|
continue
|
|
@@ -1217,141 +1814,138 @@ const convertCollapsedReferenceLinks = (tokens, state) => {
|
|
|
1217
1814
|
}
|
|
1218
1815
|
tokens.splice(closeIdx, 1)
|
|
1219
1816
|
tokens.splice(i, 1)
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
const wrapperPairs = []
|
|
1229
|
-
while (labelStartIdx > 0) {
|
|
1230
|
-
const prevToken = tokens[labelStartIdx - 1]
|
|
1231
|
-
const nextToken = tokens[labelEndIdx + 1]
|
|
1232
|
-
if (!prevToken || !nextToken) break
|
|
1233
|
-
if (!/_close$/.test(prevToken.type)) break
|
|
1234
|
-
const expectedOpen = prevToken.type.replace('_close', '_open')
|
|
1235
|
-
if (nextToken.type !== expectedOpen) break
|
|
1236
|
-
wrapperPairs.push({
|
|
1237
|
-
base: prevToken.type.replace('_close', ''),
|
|
1238
|
-
tag: prevToken.tag,
|
|
1239
|
-
markup: prevToken.markup
|
|
1240
|
-
})
|
|
1241
|
-
tokens.splice(labelEndIdx + 1, 1)
|
|
1242
|
-
tokens.splice(labelStartIdx - 1, 1)
|
|
1243
|
-
labelStartIdx -= 1
|
|
1244
|
-
labelEndIdx -= 1
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
if (labelStartIdx > labelEndIdx) {
|
|
1248
|
-
i++
|
|
1249
|
-
continue
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
let labelLength = labelEndIdx - labelStartIdx + 1
|
|
1253
|
-
const firstLabelToken = tokens[labelStartIdx]
|
|
1254
|
-
const linkLevel = firstLabelToken ? Math.max(firstLabelToken.level - 1, 0) : 0
|
|
1255
|
-
linkOpenToken.level = linkLevel
|
|
1256
|
-
linkCloseToken.level = linkLevel
|
|
1257
|
-
tokens.splice(labelStartIdx, 0, linkOpenToken)
|
|
1258
|
-
tokens.splice(labelStartIdx + labelLength + 1, 0, linkCloseToken)
|
|
1259
|
-
|
|
1260
|
-
adjustTokenLevels(tokens, labelStartIdx + 1, labelStartIdx + labelLength + 1, 1)
|
|
1261
|
-
|
|
1262
|
-
if (wrapperPairs.length > 0) {
|
|
1263
|
-
let insertIdx = labelStartIdx + 1
|
|
1264
|
-
for (let wp = 0; wp < wrapperPairs.length; wp++) {
|
|
1265
|
-
const pair = wrapperPairs[wp]
|
|
1266
|
-
const innerOpen = new Token(pair.base + '_open', pair.tag, 1)
|
|
1267
|
-
innerOpen.markup = pair.markup
|
|
1268
|
-
innerOpen.level = linkLevel + 1 + wp
|
|
1269
|
-
tokens.splice(insertIdx, 0, innerOpen)
|
|
1270
|
-
insertIdx++
|
|
1271
|
-
labelLength++
|
|
1272
|
-
}
|
|
1273
|
-
let linkClosePos = labelStartIdx + labelLength + 1
|
|
1274
|
-
for (let wp = wrapperPairs.length - 1; wp >= 0; wp--) {
|
|
1275
|
-
const pair = wrapperPairs[wp]
|
|
1276
|
-
const innerClose = new Token(pair.base + '_close', pair.tag, -1)
|
|
1277
|
-
innerClose.markup = pair.markup
|
|
1278
|
-
innerClose.level = linkLevel + 1 + wp
|
|
1279
|
-
tokens.splice(linkClosePos, 0, innerClose)
|
|
1280
|
-
labelLength++
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
i = labelStartIdx + labelLength + 2
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1817
|
+
|
|
1818
|
+
const nextIndex = wrapLabelTokensWithLink(tokens, i, i + labelTokens.length - 1, linkOpenToken, linkCloseToken)
|
|
1819
|
+
i = nextIndex
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1288
1823
|
// Link cleanup helpers
|
|
1289
1824
|
const mergeBrokenMarksAroundLinks = (tokens) => {
|
|
1290
|
-
let i = 0
|
|
1291
|
-
while (i < tokens.length) {
|
|
1292
|
-
const closeToken = tokens[i]
|
|
1293
|
-
if (!closeToken || !/_close$/.test(closeToken.type)) {
|
|
1294
|
-
i++
|
|
1295
|
-
continue
|
|
1296
|
-
}
|
|
1297
|
-
const openType = closeToken.type.replace('_close', '_open')
|
|
1298
|
-
let j = i + 1
|
|
1299
|
-
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
1300
|
-
if (j >= tokens.length || tokens[j].type !== 'link_open') {
|
|
1301
|
-
i++
|
|
1302
|
-
continue
|
|
1303
|
-
}
|
|
1304
|
-
let linkDepth = 1
|
|
1305
|
-
j++
|
|
1306
|
-
while (j < tokens.length && linkDepth > 0) {
|
|
1307
|
-
if (tokens[j].type === 'link_open') linkDepth++
|
|
1308
|
-
if (tokens[j].type === 'link_close') linkDepth--
|
|
1309
|
-
j++
|
|
1310
|
-
}
|
|
1311
|
-
if (linkDepth !== 0) {
|
|
1312
|
-
i++
|
|
1313
|
-
continue
|
|
1314
|
-
}
|
|
1315
|
-
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
1316
|
-
if (j >= tokens.length) {
|
|
1317
|
-
i++
|
|
1318
|
-
continue
|
|
1319
|
-
}
|
|
1320
|
-
const reopenToken = tokens[j]
|
|
1321
|
-
if (reopenToken.type !== openType || reopenToken.level !== closeToken.level) {
|
|
1322
|
-
i++
|
|
1323
|
-
continue
|
|
1324
|
-
}
|
|
1325
|
-
tokens.splice(j, 1)
|
|
1326
|
-
tokens.splice(i, 1)
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
const mditStrongJa = (md, option) => {
|
|
1332
|
-
const opt = {
|
|
1333
|
-
dollarMath: true, //inline math $...$
|
|
1334
|
-
mditAttrs: true, //markdown-it-attrs
|
|
1335
|
-
mdBreaks: md.options.breaks,
|
|
1336
|
-
disallowMixed: false, //Non-Japanese text handling
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1825
|
+
let i = 0
|
|
1826
|
+
while (i < tokens.length) {
|
|
1827
|
+
const closeToken = tokens[i]
|
|
1828
|
+
if (!closeToken || !/_close$/.test(closeToken.type)) {
|
|
1829
|
+
i++
|
|
1830
|
+
continue
|
|
1831
|
+
}
|
|
1832
|
+
const openType = closeToken.type.replace('_close', '_open')
|
|
1833
|
+
let j = i + 1
|
|
1834
|
+
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
1835
|
+
if (j >= tokens.length || tokens[j].type !== 'link_open') {
|
|
1836
|
+
i++
|
|
1837
|
+
continue
|
|
1838
|
+
}
|
|
1839
|
+
let linkDepth = 1
|
|
1840
|
+
j++
|
|
1841
|
+
while (j < tokens.length && linkDepth > 0) {
|
|
1842
|
+
if (tokens[j].type === 'link_open') linkDepth++
|
|
1843
|
+
if (tokens[j].type === 'link_close') linkDepth--
|
|
1844
|
+
j++
|
|
1845
|
+
}
|
|
1846
|
+
if (linkDepth !== 0) {
|
|
1847
|
+
i++
|
|
1848
|
+
continue
|
|
1849
|
+
}
|
|
1850
|
+
while (j < tokens.length && isWhitespaceToken(tokens[j])) j++
|
|
1851
|
+
if (j >= tokens.length) {
|
|
1852
|
+
i++
|
|
1853
|
+
continue
|
|
1854
|
+
}
|
|
1855
|
+
const reopenToken = tokens[j]
|
|
1856
|
+
if (reopenToken.type !== openType || reopenToken.level !== closeToken.level) {
|
|
1857
|
+
i++
|
|
1858
|
+
continue
|
|
1859
|
+
}
|
|
1860
|
+
tokens.splice(j, 1)
|
|
1861
|
+
tokens.splice(i, 1)
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
const mditStrongJa = (md, option) => {
|
|
1867
|
+
const opt = {
|
|
1868
|
+
dollarMath: true, //inline math $...$
|
|
1869
|
+
mditAttrs: true, //markdown-it-attrs
|
|
1870
|
+
mdBreaks: md.options.breaks,
|
|
1871
|
+
disallowMixed: false, //Non-Japanese text handling
|
|
1872
|
+
coreRulesBeforePostprocess: [] // e.g. ['cjk_breaks'] when CJK line-break plugins are active
|
|
1873
|
+
}
|
|
1874
|
+
if (option) Object.assign(opt, option)
|
|
1875
|
+
const rawCoreRules = opt.coreRulesBeforePostprocess
|
|
1876
|
+
const hasCoreRuleConfig = Array.isArray(rawCoreRules)
|
|
1877
|
+
? rawCoreRules.length > 0
|
|
1878
|
+
: !!rawCoreRules
|
|
1879
|
+
const coreRulesBeforePostprocess = hasCoreRuleConfig
|
|
1880
|
+
? normalizeCoreRulesBeforePostprocess(rawCoreRules)
|
|
1881
|
+
: []
|
|
1882
|
+
|
|
1883
|
+
md.inline.ruler.before('emphasis', 'strong_ja', (state, silent) => {
|
|
1884
|
+
return strongJa(state, silent, opt)
|
|
1885
|
+
})
|
|
1886
|
+
|
|
1887
|
+
md.core.ruler.after('inline', 'strong_ja_postprocess', (state) => {
|
|
1888
|
+
const targets = state.env.__strongJaPostProcessTargets
|
|
1346
1889
|
if (!targets || targets.length === 0) return
|
|
1347
1890
|
for (const tokens of targets) {
|
|
1348
1891
|
if (!tokens || !tokens.length) continue
|
|
1892
|
+
convertInlineLinks(tokens, state)
|
|
1349
1893
|
convertCollapsedReferenceLinks(tokens, state)
|
|
1350
1894
|
mergeBrokenMarksAroundLinks(tokens)
|
|
1895
|
+
delete tokens.__strongJaInlineLabelSources
|
|
1896
|
+
delete tokens.__strongJaInlineLabelIndex
|
|
1351
1897
|
}
|
|
1352
|
-
delete state.env.
|
|
1353
|
-
delete state.env.
|
|
1898
|
+
delete state.env.__strongJaPostProcessTargets
|
|
1899
|
+
delete state.env.__strongJaPostProcessTargetSet
|
|
1354
1900
|
})
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1901
|
+
|
|
1902
|
+
if (coreRulesBeforePostprocess.length > 0) {
|
|
1903
|
+
ensureCoreRuleOrder(md, coreRulesBeforePostprocess)
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
export default mditStrongJa
|
|
1908
|
+
|
|
1909
|
+
|
|
1910
|
+
function normalizeCoreRulesBeforePostprocess(value) {
|
|
1911
|
+
if (!value) return []
|
|
1912
|
+
const list = Array.isArray(value) ? value : [value]
|
|
1913
|
+
const normalized = []
|
|
1914
|
+
const seen = new Set()
|
|
1915
|
+
for (let idx = 0; idx < list.length; idx++) {
|
|
1916
|
+
const raw = list[idx]
|
|
1917
|
+
if (typeof raw !== 'string') continue
|
|
1918
|
+
const trimmed = raw.trim()
|
|
1919
|
+
if (!trimmed || seen.has(trimmed)) continue
|
|
1920
|
+
seen.add(trimmed)
|
|
1921
|
+
normalized.push(trimmed)
|
|
1922
|
+
}
|
|
1923
|
+
return normalized
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
function ensureCoreRuleOrder(md, ruleNames) {
|
|
1928
|
+
if (!md || !md.core || !md.core.ruler) return
|
|
1929
|
+
if (!ruleNames || ruleNames.length === 0) return
|
|
1930
|
+
for (let idx = 0; idx < ruleNames.length; idx++) {
|
|
1931
|
+
moveRuleBefore(md.core.ruler, ruleNames[idx], 'strong_ja_postprocess')
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
|
|
1936
|
+
function moveRuleBefore(ruler, ruleName, beforeName) {
|
|
1937
|
+
if (!ruler || !ruler.__rules__) return
|
|
1938
|
+
const rules = ruler.__rules__
|
|
1939
|
+
let fromIdx = -1
|
|
1940
|
+
let beforeIdx = -1
|
|
1941
|
+
for (let idx = 0; idx < rules.length; idx++) {
|
|
1942
|
+
if (rules[idx].name === ruleName) fromIdx = idx
|
|
1943
|
+
if (rules[idx].name === beforeName) beforeIdx = idx
|
|
1944
|
+
if (fromIdx !== -1 && beforeIdx !== -1) break
|
|
1945
|
+
}
|
|
1946
|
+
if (fromIdx === -1 || beforeIdx === -1 || fromIdx < beforeIdx) return
|
|
1947
|
+
|
|
1948
|
+
const rule = rules.splice(fromIdx, 1)[0]
|
|
1949
|
+
rules.splice(beforeIdx, 0, rule)
|
|
1950
|
+
ruler.__cache__ = null
|
|
1951
|
+
}
|