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