@peaceroad/markdown-it-strong-ja 0.7.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +326 -195
- package/index.js +27 -40
- package/package.json +26 -6
- package/src/token-compat.js +71 -22
- package/src/token-core.js +521 -132
- package/src/token-link-utils.js +434 -539
- package/src/token-postprocess/broken-ref.js +475 -0
- package/src/token-postprocess/fastpaths.js +349 -0
- package/src/token-postprocess/guards.js +499 -0
- package/src/token-postprocess/orchestrator.js +672 -0
- package/src/token-postprocess.js +1 -334
- package/src/token-utils.js +215 -142
package/src/token-core.js
CHANGED
|
@@ -4,13 +4,171 @@ import {
|
|
|
4
4
|
CHAR_ASTERISK,
|
|
5
5
|
CHAR_SPACE,
|
|
6
6
|
CHAR_TAB,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
CHAR_NEWLINE,
|
|
8
|
+
CHAR_IDEOGRAPHIC_SPACE,
|
|
9
|
+
isJapaneseChar,
|
|
10
|
+
MODE_FLAG_COMPATIBLE,
|
|
11
|
+
MODE_FLAG_AGGRESSIVE,
|
|
12
|
+
MODE_FLAG_JAPANESE_PLUS,
|
|
11
13
|
getRuntimeOpt
|
|
12
14
|
} from './token-utils.js'
|
|
13
15
|
|
|
16
|
+
const SCAN_DELIMS_PATCHED = Symbol.for('strongJaTokenScanDelimsPatched')
|
|
17
|
+
const SINGLE_STAR_LOOKAROUND_MAX = 16
|
|
18
|
+
const PREV_STAR_HAS_OPENER = 1
|
|
19
|
+
const PREV_STAR_HAS_JP_BETWEEN = 2
|
|
20
|
+
|
|
21
|
+
const isSoftSpaceCode = (code) => {
|
|
22
|
+
return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_IDEOGRAPHIC_SPACE
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const isPlusQuoteWrapperOpen = (code) => {
|
|
26
|
+
return code === 0x2018 || // ‘
|
|
27
|
+
code === 0x201C || // “
|
|
28
|
+
code === 0x301D || // 〝
|
|
29
|
+
code === 0x00AB // «
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isPlusQuoteWrapperClose = (code) => {
|
|
33
|
+
return code === 0x2019 || // ’
|
|
34
|
+
code === 0x201D || // ”
|
|
35
|
+
code === 0x301E || // 〞
|
|
36
|
+
code === 0x301F || // 〟
|
|
37
|
+
code === 0x00BB // »
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const isBacktick = (code) => code === 0x60 // `
|
|
41
|
+
|
|
42
|
+
const isOpeningBracketLike = (code) => {
|
|
43
|
+
switch (code) {
|
|
44
|
+
// ASCII
|
|
45
|
+
case 0x28: // (
|
|
46
|
+
case 0x5B: // [
|
|
47
|
+
case 0x7B: // {
|
|
48
|
+
// Fullwidth/halfwidth commonly used in JP text
|
|
49
|
+
case 0xFF08: // (
|
|
50
|
+
case 0xFF3B: // [
|
|
51
|
+
case 0xFF5B: // {
|
|
52
|
+
case 0xFF5F: // ⦅
|
|
53
|
+
case 0xFF62: // 「
|
|
54
|
+
case 0xFF1C: // <
|
|
55
|
+
// CJK punctuation brackets/quotes
|
|
56
|
+
case 0x3008: // 〈
|
|
57
|
+
case 0x300A: // 《
|
|
58
|
+
case 0x300C: // 「
|
|
59
|
+
case 0x300E: // 『
|
|
60
|
+
case 0x3010: // 【
|
|
61
|
+
case 0x3014: // 〔
|
|
62
|
+
case 0x3016: // 〖
|
|
63
|
+
case 0x3018: // 〘
|
|
64
|
+
case 0x301A: // 〚
|
|
65
|
+
// Mathematical/typographic angle brackets used in docs
|
|
66
|
+
case 0x27E6: // ⟦
|
|
67
|
+
case 0x27E8: // ⟨
|
|
68
|
+
case 0x27EA: // ⟪
|
|
69
|
+
case 0x27EC: // ⟬
|
|
70
|
+
case 0x27EE: // ⟮
|
|
71
|
+
case 0x2985: // ⦅
|
|
72
|
+
case 0x2987: // ⦇
|
|
73
|
+
case 0x2989: // ⦉
|
|
74
|
+
case 0x298B: // ⦋
|
|
75
|
+
case 0x298D: // ⦍
|
|
76
|
+
case 0x298F: // ⦏
|
|
77
|
+
case 0x2991: // ⦑
|
|
78
|
+
case 0x2993: // ⦓
|
|
79
|
+
case 0x2995: // ⦕
|
|
80
|
+
case 0x2997: // ⦗
|
|
81
|
+
case 0x29D8: // ⧘
|
|
82
|
+
case 0x29DA: // ⧚
|
|
83
|
+
case 0x29FC: // ⧼
|
|
84
|
+
// Vertical/small presentation forms found in JP publishing text
|
|
85
|
+
case 0xFE35: // ︵
|
|
86
|
+
case 0xFE37: // ︷
|
|
87
|
+
case 0xFE39: // ︹
|
|
88
|
+
case 0xFE3B: // ︻
|
|
89
|
+
case 0xFE3D: // ︽
|
|
90
|
+
case 0xFE3F: // ︿
|
|
91
|
+
case 0xFE41: // ﹁
|
|
92
|
+
case 0xFE43: // ﹃
|
|
93
|
+
case 0xFE47: // ﹇
|
|
94
|
+
case 0xFE59: // ﹙
|
|
95
|
+
case 0xFE5B: // ﹛
|
|
96
|
+
case 0xFE5D: // ﹝
|
|
97
|
+
return true
|
|
98
|
+
default:
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isClosingBracketLike = (code) => {
|
|
104
|
+
switch (code) {
|
|
105
|
+
// ASCII
|
|
106
|
+
case 0x29: // )
|
|
107
|
+
case 0x5D: // ]
|
|
108
|
+
case 0x7D: // }
|
|
109
|
+
// Fullwidth/halfwidth commonly used in JP text
|
|
110
|
+
case 0xFF09: // )
|
|
111
|
+
case 0xFF3D: // ]
|
|
112
|
+
case 0xFF5D: // }
|
|
113
|
+
case 0xFF60: // ⦆
|
|
114
|
+
case 0xFF63: // 」
|
|
115
|
+
case 0xFF1E: // >
|
|
116
|
+
// CJK punctuation brackets/quotes
|
|
117
|
+
case 0x3009: // 〉
|
|
118
|
+
case 0x300B: // 》
|
|
119
|
+
case 0x300D: // 」
|
|
120
|
+
case 0x300F: // 』
|
|
121
|
+
case 0x3011: // 】
|
|
122
|
+
case 0x3015: // 〕
|
|
123
|
+
case 0x3017: // 〗
|
|
124
|
+
case 0x3019: // 〙
|
|
125
|
+
case 0x301B: // 〛
|
|
126
|
+
// Mathematical/typographic angle brackets used in docs
|
|
127
|
+
case 0x27E7: // ⟧
|
|
128
|
+
case 0x27E9: // ⟩
|
|
129
|
+
case 0x27EB: // ⟫
|
|
130
|
+
case 0x27ED: // ⟭
|
|
131
|
+
case 0x27EF: // ⟯
|
|
132
|
+
case 0x2986: // ⦆
|
|
133
|
+
case 0x2988: // ⦈
|
|
134
|
+
case 0x298A: // ⦊
|
|
135
|
+
case 0x298C: // ⦌
|
|
136
|
+
case 0x298E: // ⦎
|
|
137
|
+
case 0x2990: // ⦐
|
|
138
|
+
case 0x2992: // ⦒
|
|
139
|
+
case 0x2994: // ⦔
|
|
140
|
+
case 0x2996: // ⦖
|
|
141
|
+
case 0x2998: // ⦘
|
|
142
|
+
case 0x29D9: // ⧙
|
|
143
|
+
case 0x29DB: // ⧛
|
|
144
|
+
case 0x29FD: // ⧽
|
|
145
|
+
// Vertical/small presentation forms found in JP publishing text
|
|
146
|
+
case 0xFE36: // ︶
|
|
147
|
+
case 0xFE38: // ︸
|
|
148
|
+
case 0xFE3A: // ︺
|
|
149
|
+
case 0xFE3C: // ︼
|
|
150
|
+
case 0xFE3E: // ︾
|
|
151
|
+
case 0xFE40: // ﹀
|
|
152
|
+
case 0xFE42: // ﹂
|
|
153
|
+
case 0xFE44: // ﹄
|
|
154
|
+
case 0xFE48: // ﹈
|
|
155
|
+
case 0xFE5A: // ﹚
|
|
156
|
+
case 0xFE5C: // ﹜
|
|
157
|
+
case 0xFE5E: // ﹞
|
|
158
|
+
return true
|
|
159
|
+
default:
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const isWrapperOpenLike = (code) => {
|
|
165
|
+
return isOpeningBracketLike(code) || isPlusQuoteWrapperOpen(code) || isBacktick(code)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const isWrapperCloseLike = (code) => {
|
|
169
|
+
return isClosingBracketLike(code) || isPlusQuoteWrapperClose(code) || isBacktick(code)
|
|
170
|
+
}
|
|
171
|
+
|
|
14
172
|
const findMatchingEmOpen = (tokens, closeIdx) => {
|
|
15
173
|
let depth = 0
|
|
16
174
|
for (let i = closeIdx; i >= 0; i--) {
|
|
@@ -36,18 +194,26 @@ const rebuildInlineLevels = (tokens) => {
|
|
|
36
194
|
}
|
|
37
195
|
}
|
|
38
196
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
197
|
+
const rebuildInlineLevelsFrom = (tokens, startIdx = 0) => {
|
|
198
|
+
if (!tokens || tokens.length === 0) return
|
|
199
|
+
let from = startIdx > 0 ? startIdx : 0
|
|
200
|
+
if (from >= tokens.length) return
|
|
201
|
+
let level = 0
|
|
202
|
+
if (from > 0) {
|
|
203
|
+
const prev = tokens[from - 1]
|
|
204
|
+
if (prev) {
|
|
205
|
+
level = prev.level
|
|
206
|
+
if (prev.nesting === 1) level++
|
|
207
|
+
else if (prev.nesting === -1) level--
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (let i = from; i < tokens.length; i++) {
|
|
42
211
|
const t = tokens[i]
|
|
43
212
|
if (!t) continue
|
|
44
|
-
|
|
45
|
-
if (t.
|
|
46
|
-
|
|
47
|
-
if (depth === 0) return i
|
|
48
|
-
}
|
|
213
|
+
t.level = level
|
|
214
|
+
if (t.nesting === 1) level++
|
|
215
|
+
else if (t.nesting === -1) level--
|
|
49
216
|
}
|
|
50
|
-
return -1
|
|
51
217
|
}
|
|
52
218
|
|
|
53
219
|
const findLinkOpen = (tokens, closeIdx) => {
|
|
@@ -74,7 +240,185 @@ const nextNonEmptyIndex = (tokens, startIdx) => {
|
|
|
74
240
|
return -1
|
|
75
241
|
}
|
|
76
242
|
|
|
77
|
-
const
|
|
243
|
+
const isSingleStarBoundary = (code) => {
|
|
244
|
+
return code === 0 ||
|
|
245
|
+
isSoftSpaceCode(code) ||
|
|
246
|
+
code === CHAR_NEWLINE ||
|
|
247
|
+
isOpeningBracketLike(code)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const isSingleStarClosingBoundary = (code) => {
|
|
251
|
+
return code === 0 ||
|
|
252
|
+
isSoftSpaceCode(code) ||
|
|
253
|
+
code === CHAR_NEWLINE ||
|
|
254
|
+
isClosingBracketLike(code)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const isAsciiAlphaNum = (code) => {
|
|
258
|
+
return (code >= 0x30 && code <= 0x39) ||
|
|
259
|
+
(code >= 0x41 && code <= 0x5A) ||
|
|
260
|
+
(code >= 0x61 && code <= 0x7A)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const isAsciiGuardOpenWrapper = (code) => {
|
|
264
|
+
return code === 0x22 || // "
|
|
265
|
+
code === 0x27 || // '
|
|
266
|
+
code === 0x28 || // (
|
|
267
|
+
code === 0x5B || // [
|
|
268
|
+
code === 0x7B || // {
|
|
269
|
+
code === 0x3C || // <
|
|
270
|
+
code === 0x60 // `
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const isAsciiGuardCloseWrapper = (code) => {
|
|
274
|
+
return code === 0x22 || // "
|
|
275
|
+
code === 0x27 || // '
|
|
276
|
+
code === 0x29 || // )
|
|
277
|
+
code === 0x5D || // ]
|
|
278
|
+
code === 0x7D || // }
|
|
279
|
+
code === 0x3E || // >
|
|
280
|
+
code === 0x60 // `
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const findPrevNonSpaceIndex = (src, start) => {
|
|
284
|
+
for (let i = start; i >= 0; i--) {
|
|
285
|
+
const code = src.charCodeAt(i)
|
|
286
|
+
if (code === CHAR_NEWLINE) return -1
|
|
287
|
+
if (isSoftSpaceCode(code)) continue
|
|
288
|
+
return i
|
|
289
|
+
}
|
|
290
|
+
return -1
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const findNextNonSpaceIndex = (src, start, max) => {
|
|
294
|
+
for (let i = start; i < max; i++) {
|
|
295
|
+
const code = src.charCodeAt(i)
|
|
296
|
+
if (code === CHAR_NEWLINE) return -1
|
|
297
|
+
if (isSoftSpaceCode(code)) continue
|
|
298
|
+
return i
|
|
299
|
+
}
|
|
300
|
+
return -1
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const hasAsciiStartAfterOptionalOpenWrappers = (src, index, max) => {
|
|
304
|
+
let i = index
|
|
305
|
+
// Two wrappers are enough for common shapes: * [ "word" ]*
|
|
306
|
+
for (let wrappers = 0; wrappers < 2 && i >= 0 && i < max; wrappers++) {
|
|
307
|
+
const code = src.charCodeAt(i)
|
|
308
|
+
if (!isAsciiGuardOpenWrapper(code)) break
|
|
309
|
+
i = findNextNonSpaceIndex(src, i + 1, max)
|
|
310
|
+
if (i === -1) return false
|
|
311
|
+
}
|
|
312
|
+
if (i < 0 || i >= max) return false
|
|
313
|
+
return isAsciiAlphaNum(src.charCodeAt(i))
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const hasAsciiEndBeforeOptionalCloseWrappers = (src, index) => {
|
|
317
|
+
let i = index
|
|
318
|
+
// Two wrappers are enough for common shapes: *["word"] *
|
|
319
|
+
for (let wrappers = 0; wrappers < 2 && i >= 0; wrappers++) {
|
|
320
|
+
const code = src.charCodeAt(i)
|
|
321
|
+
if (!isAsciiGuardCloseWrapper(code)) break
|
|
322
|
+
i = findPrevNonSpaceIndex(src, i - 1)
|
|
323
|
+
if (i === -1) return false
|
|
324
|
+
}
|
|
325
|
+
if (i < 0) return false
|
|
326
|
+
return isAsciiAlphaNum(src.charCodeAt(i))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const isMarkdownStructuralOpenWrapper = (code) => {
|
|
330
|
+
return code === 0x28 || // (
|
|
331
|
+
code === 0x5B || // [
|
|
332
|
+
code === 0x7B // {
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const isExtraSingleStarClosePunct = (code) => {
|
|
336
|
+
return code === 0x3F || // ?
|
|
337
|
+
code === 0x203C || // ‼
|
|
338
|
+
code === 0x2047 || // ⁇
|
|
339
|
+
code === 0x2048 || // ⁈
|
|
340
|
+
code === 0x2049 // ⁉
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const isSentenceBoundaryStop = (code) => {
|
|
344
|
+
return code === 0x3002 || // 。
|
|
345
|
+
code === 0xFF01 || // !
|
|
346
|
+
code === 0xFF1F || // ?
|
|
347
|
+
code === 0x2E || // .
|
|
348
|
+
code === 0x21 || // !
|
|
349
|
+
code === 0x3F || // ?
|
|
350
|
+
code === 0x203C || // ‼
|
|
351
|
+
code === 0x2047 || // ⁇
|
|
352
|
+
code === 0x2048 || // ⁈
|
|
353
|
+
code === 0x2049 // ⁉
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const findPrevNonSpaceLimited = (src, start, maxLook) => {
|
|
357
|
+
let looked = 0
|
|
358
|
+
for (let i = start; i >= 0; i--) {
|
|
359
|
+
if (looked >= maxLook) break
|
|
360
|
+
const code = src.charCodeAt(i)
|
|
361
|
+
looked++
|
|
362
|
+
if (code === CHAR_NEWLINE) return 0
|
|
363
|
+
if (isSoftSpaceCode(code)) continue
|
|
364
|
+
return code
|
|
365
|
+
}
|
|
366
|
+
return 0
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const findNextNonSpaceLimited = (src, start, max, maxLook) => {
|
|
370
|
+
let looked = 0
|
|
371
|
+
for (let i = start; i < max; i++) {
|
|
372
|
+
if (looked >= maxLook) break
|
|
373
|
+
const code = src.charCodeAt(i)
|
|
374
|
+
looked++
|
|
375
|
+
if (code === CHAR_NEWLINE) return 0
|
|
376
|
+
if (isSoftSpaceCode(code)) continue
|
|
377
|
+
return code
|
|
378
|
+
}
|
|
379
|
+
return 0
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const hasJapaneseContextForBracketWrapper = (src, start, pos, max, lastChar, nextChar) => {
|
|
383
|
+
if (isWrapperOpenLike(nextChar)) {
|
|
384
|
+
const right = findNextNonSpaceLimited(src, pos, max, SINGLE_STAR_LOOKAROUND_MAX)
|
|
385
|
+
if (isJapaneseChar(right)) return true
|
|
386
|
+
}
|
|
387
|
+
if (isWrapperCloseLike(lastChar)) {
|
|
388
|
+
const left = findPrevNonSpaceLimited(src, start - 2, SINGLE_STAR_LOOKAROUND_MAX)
|
|
389
|
+
if (isJapaneseChar(left)) return true
|
|
390
|
+
}
|
|
391
|
+
return false
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const scanPrevSingleStarContextFlags = (src, start) => {
|
|
395
|
+
let hasJapaneseBetween = false
|
|
396
|
+
for (let i = start - 1; i >= 0; i--) {
|
|
397
|
+
const code = src.charCodeAt(i)
|
|
398
|
+
if (code === CHAR_NEWLINE) break
|
|
399
|
+
if (isSentenceBoundaryStop(code) && i < start - 1) break
|
|
400
|
+
if (code !== CHAR_ASTERISK) {
|
|
401
|
+
if (!hasJapaneseBetween && isJapaneseChar(code)) hasJapaneseBetween = true
|
|
402
|
+
continue
|
|
403
|
+
}
|
|
404
|
+
let backslashCount = 0
|
|
405
|
+
for (let b = i - 1; b >= 0 && src.charCodeAt(b) === 0x5C; b--) {
|
|
406
|
+
backslashCount++
|
|
407
|
+
}
|
|
408
|
+
if ((backslashCount % 2) === 1) continue
|
|
409
|
+
const prevCode = i > 0 ? src.charCodeAt(i - 1) : 0
|
|
410
|
+
const nextCode = i + 1 < src.length ? src.charCodeAt(i + 1) : 0
|
|
411
|
+
if (prevCode === CHAR_ASTERISK || nextCode === CHAR_ASTERISK) continue
|
|
412
|
+
return hasJapaneseBetween ? PREV_STAR_HAS_OPENER | PREV_STAR_HAS_JP_BETWEEN : PREV_STAR_HAS_OPENER
|
|
413
|
+
}
|
|
414
|
+
return 0
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const ensurePrevStarFlags = (src, start, prevStarFlags) => {
|
|
418
|
+
return prevStarFlags >= 0 ? prevStarFlags : scanPrevSingleStarContextFlags(src, start)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const fixTrailingStrong = (tokens, onChangeStart = null) => {
|
|
78
422
|
let changed = false
|
|
79
423
|
for (let i = 1; i < tokens.length; i++) {
|
|
80
424
|
const token = tokens[i]
|
|
@@ -113,21 +457,23 @@ const fixTrailingStrong = (tokens) => {
|
|
|
113
457
|
tokens[innerOpenIdx + 2] && tokens[innerOpenIdx + 2].type === 'em_close' &&
|
|
114
458
|
tokens[innerOpenIdx + 3] && tokens[innerOpenIdx + 3].type === 'text' &&
|
|
115
459
|
closeIdx === innerOpenIdx + 4) {
|
|
116
|
-
tokens.splice(innerOpenIdx + 2, 1)
|
|
117
|
-
tokens.splice(innerOpenIdx, 1)
|
|
118
460
|
const movedOpen = new Token('em_open', 'em', 1)
|
|
119
461
|
movedOpen.markup = '*'
|
|
120
462
|
const movedClose = new Token('em_close', 'em', -1)
|
|
121
463
|
movedClose.markup = '*'
|
|
122
|
-
|
|
123
|
-
|
|
464
|
+
const innerReplacement = [
|
|
465
|
+
tokens[innerOpenIdx + 1],
|
|
466
|
+
movedOpen,
|
|
467
|
+
tokens[innerOpenIdx + 3],
|
|
468
|
+
movedClose
|
|
469
|
+
]
|
|
470
|
+
tokens.splice(innerOpenIdx, 4, ...innerReplacement)
|
|
124
471
|
}
|
|
125
472
|
|
|
126
473
|
const before = token.content.slice(0, starIdx)
|
|
127
474
|
const after = token.content.slice(starIdx + 2)
|
|
128
475
|
|
|
129
|
-
|
|
130
|
-
if (closeIdx < i) i--
|
|
476
|
+
if (onChangeStart) onChangeStart(openIdx)
|
|
131
477
|
|
|
132
478
|
const openToken = tokens[openIdx]
|
|
133
479
|
if (!openToken) continue
|
|
@@ -136,28 +482,27 @@ const fixTrailingStrong = (tokens) => {
|
|
|
136
482
|
openToken.markup = '**'
|
|
137
483
|
openToken.nesting = 1
|
|
138
484
|
|
|
485
|
+
const strongClose = new Token('strong_close', 'strong', -1)
|
|
486
|
+
strongClose.markup = '**'
|
|
487
|
+
const trailingReplacement = []
|
|
139
488
|
if (before) {
|
|
140
489
|
token.content = before
|
|
141
|
-
|
|
142
|
-
tokens.splice(i, 1)
|
|
143
|
-
i--
|
|
490
|
+
trailingReplacement.push(token)
|
|
144
491
|
}
|
|
145
|
-
|
|
146
|
-
const insertAt = i + 1
|
|
147
|
-
const strongClose = new Token('strong_close', 'strong', -1)
|
|
148
|
-
strongClose.markup = '**'
|
|
149
|
-
tokens.splice(insertAt, 0, strongClose)
|
|
492
|
+
trailingReplacement.push(strongClose)
|
|
150
493
|
if (after) {
|
|
151
494
|
const tail = new Token('text', '', 0)
|
|
152
495
|
tail.content = after
|
|
153
|
-
|
|
496
|
+
trailingReplacement.push(tail)
|
|
154
497
|
}
|
|
498
|
+
tokens.splice(closeIdx, 2, ...trailingReplacement)
|
|
499
|
+
i = closeIdx + trailingReplacement.length - 1
|
|
155
500
|
changed = true
|
|
156
501
|
}
|
|
157
502
|
return changed
|
|
158
503
|
}
|
|
159
504
|
|
|
160
|
-
function fixEmOuterStrongSequence(tokens) {
|
|
505
|
+
function fixEmOuterStrongSequence(tokens, onChangeStart = null) {
|
|
161
506
|
let changed = false
|
|
162
507
|
let i = 0
|
|
163
508
|
while (i < tokens.length) {
|
|
@@ -214,6 +559,7 @@ function fixEmOuterStrongSequence(tokens) {
|
|
|
214
559
|
continue
|
|
215
560
|
}
|
|
216
561
|
|
|
562
|
+
if (onChangeStart) onChangeStart(idx0)
|
|
217
563
|
t0.type = 'strong_open'
|
|
218
564
|
t0.tag = 'strong'
|
|
219
565
|
t0.markup = '**'
|
|
@@ -226,44 +572,23 @@ function fixEmOuterStrongSequence(tokens) {
|
|
|
226
572
|
const strongClose = new Token('strong_close', 'strong', -1)
|
|
227
573
|
strongClose.markup = '**'
|
|
228
574
|
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
changed = true
|
|
239
|
-
i = idx0 + 1
|
|
240
|
-
continue
|
|
241
|
-
}
|
|
242
|
-
tokens.splice(idxT4, 0, emOpen)
|
|
243
|
-
|
|
244
|
-
let idxT6 = tokens.indexOf(t6)
|
|
245
|
-
if (idxT6 === -1) {
|
|
246
|
-
changed = true
|
|
247
|
-
i = idx0 + 1
|
|
248
|
-
continue
|
|
249
|
-
}
|
|
250
|
-
tokens.splice(idxT6, 0, emClose)
|
|
251
|
-
|
|
252
|
-
idxT6 = tokens.indexOf(t6)
|
|
253
|
-
if (idxT6 === -1) {
|
|
254
|
-
changed = true
|
|
255
|
-
i = idx0 + 1
|
|
256
|
-
continue
|
|
257
|
-
}
|
|
258
|
-
tokens.splice(idxT6 + 1, 0, strongClose)
|
|
575
|
+
const replacement = [
|
|
576
|
+
...tokens.slice(idx1 + 1, idx3),
|
|
577
|
+
emOpen,
|
|
578
|
+
...tokens.slice(idx3 + 1, idx5),
|
|
579
|
+
emClose,
|
|
580
|
+
...tokens.slice(idx5 + 1, idx7),
|
|
581
|
+
strongClose
|
|
582
|
+
]
|
|
583
|
+
tokens.splice(idx1, idx7 - idx1 + 1, ...replacement)
|
|
259
584
|
|
|
260
585
|
changed = true
|
|
261
|
-
i =
|
|
586
|
+
i = idx1 + replacement.length
|
|
262
587
|
}
|
|
263
588
|
return changed
|
|
264
589
|
}
|
|
265
590
|
|
|
266
|
-
const shiftEmWithLeadingStar = (tokens, rangeStart, rangeEnd, closeIdx) => {
|
|
591
|
+
const shiftEmWithLeadingStar = (tokens, rangeStart, rangeEnd, closeIdx, onChangeStart = null) => {
|
|
267
592
|
let openIdx = findMatchingEmOpen(tokens, closeIdx)
|
|
268
593
|
if (openIdx === -1 || openIdx < rangeStart || openIdx >= rangeEnd) return false
|
|
269
594
|
|
|
@@ -279,10 +604,10 @@ const shiftEmWithLeadingStar = (tokens, rangeStart, rangeEnd, closeIdx) => {
|
|
|
279
604
|
if (!t || t.type !== 'text' || !t.content) continue
|
|
280
605
|
const pos = t.content.lastIndexOf('*')
|
|
281
606
|
if (pos <= 0) continue
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
if (
|
|
285
|
-
if (!
|
|
607
|
+
const prevCode = t.content.charCodeAt(pos - 1)
|
|
608
|
+
const nextCode = pos + 1 < t.content.length ? t.content.charCodeAt(pos + 1) : 0
|
|
609
|
+
if (!isWhiteSpace(prevCode)) continue
|
|
610
|
+
if (!nextCode || isWhiteSpace(nextCode) || nextCode === CHAR_ASTERISK) continue
|
|
286
611
|
starTokenIdx = i
|
|
287
612
|
starPos = pos
|
|
288
613
|
break
|
|
@@ -292,33 +617,26 @@ const shiftEmWithLeadingStar = (tokens, rangeStart, rangeEnd, closeIdx) => {
|
|
|
292
617
|
const starToken = tokens[starTokenIdx]
|
|
293
618
|
const before = starToken.content.slice(0, starPos)
|
|
294
619
|
const after = starToken.content.slice(starPos + 1)
|
|
295
|
-
|
|
296
|
-
if (before) {
|
|
297
|
-
starToken.content = before
|
|
298
|
-
insertAt = starTokenIdx + 1
|
|
299
|
-
} else {
|
|
300
|
-
tokens.splice(starTokenIdx, 1)
|
|
301
|
-
if (starTokenIdx < openIdx) {
|
|
302
|
-
openIdx--
|
|
303
|
-
closeIdx--
|
|
304
|
-
}
|
|
305
|
-
}
|
|
620
|
+
if (onChangeStart) onChangeStart(starTokenIdx)
|
|
306
621
|
|
|
307
622
|
const emOpen = new Token('em_open', 'em', 1)
|
|
308
623
|
emOpen.markup = '*'
|
|
309
|
-
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
624
|
+
const prefixReplacement = []
|
|
625
|
+
if (before) {
|
|
626
|
+
starToken.content = before
|
|
627
|
+
prefixReplacement.push(starToken)
|
|
313
628
|
}
|
|
629
|
+
prefixReplacement.push(emOpen)
|
|
314
630
|
if (after) {
|
|
315
631
|
const afterToken = new Token('text', '', 0)
|
|
316
632
|
afterToken.content = after
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
633
|
+
prefixReplacement.push(afterToken)
|
|
634
|
+
}
|
|
635
|
+
const prefixDelta = prefixReplacement.length - 1
|
|
636
|
+
tokens.splice(starTokenIdx, 1, ...prefixReplacement)
|
|
637
|
+
if (starTokenIdx < openIdx) {
|
|
638
|
+
openIdx += prefixDelta
|
|
639
|
+
closeIdx += prefixDelta
|
|
322
640
|
}
|
|
323
641
|
|
|
324
642
|
const openToken = tokens[openIdx]
|
|
@@ -328,19 +646,20 @@ const shiftEmWithLeadingStar = (tokens, rangeStart, rangeEnd, closeIdx) => {
|
|
|
328
646
|
openToken.markup = '*'
|
|
329
647
|
openToken.nesting = -1
|
|
330
648
|
|
|
331
|
-
|
|
649
|
+
const tailReplacement = []
|
|
332
650
|
const tailIdx = closeIdx - 1
|
|
333
651
|
if (tailIdx >= 0 && tokens[tailIdx] && tokens[tailIdx].type === 'text') {
|
|
334
652
|
tokens[tailIdx].content += '*'
|
|
335
653
|
} else {
|
|
336
654
|
const tail = new Token('text', '', 0)
|
|
337
655
|
tail.content = '*'
|
|
338
|
-
|
|
656
|
+
tailReplacement.push(tail)
|
|
339
657
|
}
|
|
658
|
+
tokens.splice(closeIdx, 1, ...tailReplacement)
|
|
340
659
|
return true
|
|
341
660
|
}
|
|
342
661
|
|
|
343
|
-
const fixLeadingAsteriskEm = (tokens) => {
|
|
662
|
+
const fixLeadingAsteriskEm = (tokens, onChangeStart = null) => {
|
|
344
663
|
let changed = false
|
|
345
664
|
for (let i = 0; i < tokens.length; i++) {
|
|
346
665
|
const t = tokens[i]
|
|
@@ -350,7 +669,7 @@ const fixLeadingAsteriskEm = (tokens) => {
|
|
|
350
669
|
const linkCloseIdx = nextIdx
|
|
351
670
|
const linkOpenIdx = findLinkOpen(tokens, linkCloseIdx)
|
|
352
671
|
if (linkOpenIdx === -1) continue
|
|
353
|
-
if (shiftEmWithLeadingStar(tokens, linkOpenIdx + 1, linkCloseIdx, i)) {
|
|
672
|
+
if (shiftEmWithLeadingStar(tokens, linkOpenIdx + 1, linkCloseIdx, i, onChangeStart)) {
|
|
354
673
|
changed = true
|
|
355
674
|
i = linkCloseIdx
|
|
356
675
|
}
|
|
@@ -359,77 +678,147 @@ const fixLeadingAsteriskEm = (tokens) => {
|
|
|
359
678
|
}
|
|
360
679
|
|
|
361
680
|
const patchScanDelims = (md) => {
|
|
362
|
-
if (md.
|
|
363
|
-
|
|
681
|
+
if (!md || !md.inline || !md.inline.State || !md.inline.State.prototype) return
|
|
682
|
+
const proto = md.inline.State.prototype
|
|
683
|
+
if (proto[SCAN_DELIMS_PATCHED] === true) {
|
|
684
|
+
return
|
|
685
|
+
}
|
|
686
|
+
const original = proto.scanDelims
|
|
687
|
+
if (typeof original !== 'function') return
|
|
364
688
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const marker =
|
|
689
|
+
proto.scanDelims = function strongJaTokenScanDelims(start, canSplitWord) {
|
|
690
|
+
const src = this.src
|
|
691
|
+
const marker = src.charCodeAt(start)
|
|
368
692
|
if (marker !== CHAR_ASTERISK) {
|
|
369
693
|
return original.call(this, start, canSplitWord)
|
|
370
694
|
}
|
|
695
|
+
const base = original.call(this, start, canSplitWord)
|
|
371
696
|
|
|
372
|
-
const baseOpt = this.md
|
|
373
|
-
const
|
|
697
|
+
const baseOpt = this.md ? this.md.__strongJaTokenOpt : null
|
|
698
|
+
const overrideOpt = this.env && this.env.__strongJaTokenOpt
|
|
699
|
+
const opt = overrideOpt ? getRuntimeOpt(this, baseOpt) : baseOpt
|
|
374
700
|
if (!opt) {
|
|
375
|
-
return
|
|
701
|
+
return base
|
|
376
702
|
}
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return original.call(this, start, canSplitWord)
|
|
703
|
+
const modeFlags = opt.__strongJaModeFlags
|
|
704
|
+
if (modeFlags & MODE_FLAG_COMPATIBLE) {
|
|
705
|
+
return base
|
|
381
706
|
}
|
|
382
|
-
|
|
707
|
+
const plusMode = (modeFlags & MODE_FLAG_JAPANESE_PLUS) !== 0
|
|
708
|
+
const aggressiveMode = (modeFlags & MODE_FLAG_AGGRESSIVE) !== 0
|
|
383
709
|
const max = this.posMax
|
|
384
|
-
const lastChar = start > 0 ?
|
|
710
|
+
const lastChar = start > 0 ? src.charCodeAt(start - 1) : 0x20
|
|
385
711
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const count = pos - start
|
|
712
|
+
const count = base && base.length ? base.length : 1
|
|
713
|
+
const pos = start + count
|
|
389
714
|
|
|
390
|
-
const nextChar = pos < max ?
|
|
715
|
+
const nextChar = pos < max ? src.charCodeAt(pos) : 0x20
|
|
716
|
+
let prevStarFlags = -1
|
|
391
717
|
|
|
392
|
-
const
|
|
393
|
-
const
|
|
718
|
+
const leftJapanese = isJapaneseChar(lastChar)
|
|
719
|
+
const rightJapanese = isJapaneseChar(nextChar)
|
|
720
|
+
let hasJapaneseContext = leftJapanese || rightJapanese
|
|
721
|
+
if (!hasJapaneseContext && count === 1) {
|
|
722
|
+
hasJapaneseContext = hasJapaneseContextForBracketWrapper(src, start, pos, max, lastChar, nextChar)
|
|
723
|
+
}
|
|
724
|
+
if (!hasJapaneseContext && count === 1 && isExtraSingleStarClosePunct(lastChar)) {
|
|
725
|
+
prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
|
|
726
|
+
hasJapaneseContext = (prevStarFlags & PREV_STAR_HAS_JP_BETWEEN) !== 0
|
|
727
|
+
}
|
|
728
|
+
const useRelaxed = aggressiveMode || hasJapaneseContext
|
|
729
|
+
if (!useRelaxed) {
|
|
730
|
+
return base
|
|
731
|
+
}
|
|
394
732
|
|
|
395
|
-
|
|
396
|
-
let
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
733
|
+
// 1) Normalize soft-space neighborhood around the current delimiter run.
|
|
734
|
+
let isLastWhiteSpace = isWhiteSpace(lastChar) || isSoftSpaceCode(lastChar)
|
|
735
|
+
let isNextWhiteSpace = isWhiteSpace(nextChar) || isSoftSpaceCode(nextChar)
|
|
736
|
+
if (isLastWhiteSpace && isSoftSpaceCode(lastChar)) {
|
|
737
|
+
const prevNonSpaceIdx = findPrevNonSpaceIndex(src, start - 2)
|
|
738
|
+
if (prevNonSpaceIdx !== -1) {
|
|
739
|
+
const prevNonSpaceLocal = src.charCodeAt(prevNonSpaceIdx)
|
|
740
|
+
const plusStrictAsciiBoundary = plusMode &&
|
|
741
|
+
hasAsciiEndBeforeOptionalCloseWrappers(src, prevNonSpaceIdx)
|
|
742
|
+
if (prevNonSpaceLocal !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
|
|
401
743
|
isLastWhiteSpace = false
|
|
402
744
|
}
|
|
403
745
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
746
|
+
}
|
|
747
|
+
if (isNextWhiteSpace && isSoftSpaceCode(nextChar)) {
|
|
748
|
+
const nextNonSpaceIdx = findNextNonSpaceIndex(src, pos, max)
|
|
749
|
+
if (nextNonSpaceIdx !== -1) {
|
|
750
|
+
const nextNonSpace = src.charCodeAt(nextNonSpaceIdx)
|
|
751
|
+
const plusStrictAsciiBoundary = plusMode &&
|
|
752
|
+
hasAsciiStartAfterOptionalOpenWrappers(src, nextNonSpaceIdx, max)
|
|
753
|
+
if (nextNonSpace !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
|
|
407
754
|
isNextWhiteSpace = false
|
|
408
755
|
}
|
|
409
756
|
}
|
|
410
757
|
}
|
|
411
758
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const right_flanking =
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
759
|
+
// 2) Compute markdown-it compatible flanking sides from normalized whitespace.
|
|
760
|
+
const left_flanking = !isNextWhiteSpace
|
|
761
|
+
const right_flanking = !isLastWhiteSpace
|
|
762
|
+
const can_open = left_flanking && (canSplitWord || !right_flanking)
|
|
763
|
+
const can_close = right_flanking && (canSplitWord || !left_flanking)
|
|
764
|
+
|
|
765
|
+
const forbidClose = isOpeningBracketLike(lastChar)
|
|
766
|
+
const forbidOpen = isClosingBracketLike(nextChar)
|
|
767
|
+
let relaxedOpen = forbidOpen ? false : can_open
|
|
768
|
+
let relaxedClose = forbidClose ? false : can_close
|
|
769
|
+
let forceOpen = null
|
|
770
|
+
let forceClose = null
|
|
771
|
+
if (!aggressiveMode && count === 1) {
|
|
772
|
+
// Keep local directionality to avoid degrading markdown-it-valid runs,
|
|
773
|
+
// e.g. `[。*a**](u)` where the first `*` should remain opener-only.
|
|
774
|
+
const rightIsBoundary = isSingleStarClosingBoundary(nextChar) || isWrapperOpenLike(nextChar)
|
|
775
|
+
const leftIsBoundary = isSingleStarBoundary(lastChar) || isWrapperCloseLike(lastChar)
|
|
776
|
+
if (leftJapanese && !rightJapanese && !rightIsBoundary) {
|
|
777
|
+
prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
|
|
778
|
+
if ((prevStarFlags & PREV_STAR_HAS_OPENER) === 0) {
|
|
779
|
+
relaxedClose = false
|
|
780
|
+
}
|
|
781
|
+
} else if (!leftJapanese && rightJapanese && !leftIsBoundary) {
|
|
782
|
+
relaxedOpen = false
|
|
783
|
+
}
|
|
784
|
+
const rightIsOpenWrapper = isWrapperOpenLike(nextChar)
|
|
785
|
+
const leftIsCloseWrapper = isWrapperCloseLike(lastChar)
|
|
786
|
+
prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
|
|
787
|
+
const hasPrevJapaneseOpener = (prevStarFlags & PREV_STAR_HAS_OPENER) !== 0
|
|
788
|
+
const hasJapaneseSincePrevStar = (prevStarFlags & PREV_STAR_HAS_JP_BETWEEN) !== 0
|
|
789
|
+
const leftIsExtraClosePunct = isExtraSingleStarClosePunct(lastChar)
|
|
790
|
+
const canForceCloseByPunct = leftIsExtraClosePunct && hasJapaneseSincePrevStar
|
|
791
|
+
if (leftJapanese &&
|
|
792
|
+
rightIsOpenWrapper &&
|
|
793
|
+
!hasPrevJapaneseOpener &&
|
|
794
|
+
!isMarkdownStructuralOpenWrapper(nextChar)) {
|
|
795
|
+
forceOpen = true
|
|
796
|
+
forceClose = false
|
|
797
|
+
} else if (leftIsCloseWrapper && rightJapanese && hasPrevJapaneseOpener) {
|
|
798
|
+
forceOpen = false
|
|
799
|
+
forceClose = true
|
|
800
|
+
} else if ((leftIsCloseWrapper || canForceCloseByPunct) &&
|
|
801
|
+
!rightJapanese &&
|
|
802
|
+
!rightIsBoundary &&
|
|
803
|
+
hasPrevJapaneseOpener) {
|
|
804
|
+
forceOpen = false
|
|
805
|
+
forceClose = true
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
const finalOpen = forceOpen === null ? ((base && base.can_open) || relaxedOpen) : forceOpen
|
|
809
|
+
const finalClose = forceClose === null ? ((base && base.can_close) || relaxedClose) : forceClose
|
|
422
810
|
return {
|
|
423
|
-
can_open:
|
|
424
|
-
can_close:
|
|
811
|
+
can_open: finalOpen,
|
|
812
|
+
can_close: finalClose,
|
|
425
813
|
length: count
|
|
426
814
|
}
|
|
427
815
|
}
|
|
816
|
+
proto[SCAN_DELIMS_PATCHED] = true
|
|
428
817
|
}
|
|
429
818
|
|
|
430
819
|
export {
|
|
431
820
|
rebuildInlineLevels,
|
|
432
|
-
|
|
821
|
+
rebuildInlineLevelsFrom,
|
|
433
822
|
findLinkOpen,
|
|
434
823
|
nextNonEmptyIndex,
|
|
435
824
|
fixTrailingStrong,
|