@peaceroad/markdown-it-strong-ja 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/index.js +9 -17
- package/package.json +4 -4
- package/src/token-compat.js +12 -11
- package/src/token-core.js +118 -19
- package/src/token-link-utils.js +12 -10
- package/src/token-postprocess/broken-ref.js +10 -3
- package/src/token-postprocess/guards.js +54 -98
- package/src/token-postprocess/orchestrator.js +23 -52
- package/src/token-utils.js +35 -18
package/README.md
CHANGED
|
@@ -396,10 +396,10 @@ Supporting visuals:
|
|
|
396
396
|
|
|
397
397
|
## Notes
|
|
398
398
|
|
|
399
|
-
- Use `state.env.__strongJaTokenOpt` to override options per render.
|
|
400
|
-
-
|
|
401
|
-
-
|
|
399
|
+
- Use `state.env.__strongJaTokenOpt` to override runtime-effective options per render.
|
|
400
|
+
- Repeated `.use(...)` on the same `MarkdownIt` instance is treated as first-install-wins no-op. Create a new `MarkdownIt` instance for a different plugin option set.
|
|
401
|
+
- Runtime-effective override keys are merged with plugin options, but setup-time behavior (such as rule registration/order) cannot be switched at render time and cannot be retrofitted after the first `.use(...)` on the same `MarkdownIt` instance.
|
|
402
|
+
- `mode` and `postprocess` are runtime-effective via initial install or per-render override. `mditAttrs`, `patchCorePush`, and `coreRulesBeforePostprocess` are setup-time effective after the first `.use(...)` on a `MarkdownIt` instance.
|
|
402
403
|
- This is an ESM plugin (`type: module`) and is tested against `markdown-it` 14.x in Node.js, browser bundlers, and VS Code extension pipelines that use `markdown-it` ESM.
|
|
403
404
|
- The implementation relies on `markdown-it` internal ESM modules / core rule internals (`lib/token.mjs`, `lib/common/utils.mjs`, `ruler.__rules__`) plus a `scanDelims` prototype patch, so internal `markdown-it` changes may require plugin updates.
|
|
404
405
|
- `scanDelims` patch is applied once per `MarkdownIt` prototype in the same process.
|
|
405
|
-
|
package/index.js
CHANGED
|
@@ -19,31 +19,23 @@ const buildNormalizedOption = (md, option) => {
|
|
|
19
19
|
return opt
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const writeSharedOption = (target, source) => {
|
|
23
|
-
for (const key of Object.keys(target)) {
|
|
24
|
-
delete target[key]
|
|
25
|
-
}
|
|
26
|
-
Object.assign(target, source)
|
|
27
|
-
return target
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
const mditStrongJa = (md, option) => {
|
|
31
23
|
if (option && typeof option.engine === 'string' && option.engine !== 'token') {
|
|
32
24
|
throw new Error('mditStrongJa: legacy engine was removed; use token (default)')
|
|
33
25
|
}
|
|
26
|
+
if (md.__strongJaTokenInstalled) {
|
|
27
|
+
return md
|
|
28
|
+
}
|
|
34
29
|
const nextOpt = buildNormalizedOption(md, option)
|
|
35
|
-
|
|
36
|
-
? writeSharedOption(md.__strongJaTokenOpt, nextOpt)
|
|
37
|
-
: nextOpt
|
|
38
|
-
|
|
39
|
-
md.__strongJaTokenOpt = opt
|
|
30
|
+
md.__strongJaTokenOpt = nextOpt
|
|
40
31
|
patchScanDelims(md)
|
|
41
|
-
registerTokenCompat(md,
|
|
32
|
+
registerTokenCompat(md, nextOpt)
|
|
42
33
|
|
|
43
|
-
registerTokenPostprocess(md,
|
|
44
|
-
ensureCoreRuleOrder(md,
|
|
34
|
+
registerTokenPostprocess(md, nextOpt)
|
|
35
|
+
ensureCoreRuleOrder(md, nextOpt.__strongJaNormalizedCoreRulesBeforePostprocess, 'strong_ja_token_postprocess')
|
|
36
|
+
md.__strongJaTokenInstalled = true
|
|
45
37
|
|
|
46
38
|
return md
|
|
47
39
|
}
|
|
48
40
|
|
|
49
|
-
export default mditStrongJa
|
|
41
|
+
export default mditStrongJa
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-strong-ja",
|
|
3
3
|
"description": "Extends asterisk emphasis handling for Japanese text while keeping markdown-it behavior as close as practical.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"markdown-it": "^14.1.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.
|
|
40
|
-
"@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.
|
|
39
|
+
"@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.10",
|
|
40
|
+
"@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.11.0",
|
|
41
41
|
"@peaceroad/markdown-it-renderer-image": "^0.12.0",
|
|
42
|
-
"@peaceroad/markdown-it-renderer-inline-text": "^0.
|
|
42
|
+
"@peaceroad/markdown-it-renderer-inline-text": "^0.8.0",
|
|
43
43
|
"markdown-it-attrs": "^4.3.1",
|
|
44
44
|
"markdown-it-sub": "^2.0.0",
|
|
45
45
|
"markdown-it-sup": "^2.0.0",
|
package/src/token-compat.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
hasCjkBreaksRule,
|
|
6
6
|
isCjkBreaksRuleName,
|
|
7
7
|
getRuntimeOpt,
|
|
8
|
+
hasRuntimeOverride,
|
|
8
9
|
moveRuleAfter
|
|
9
10
|
} from './token-utils.js'
|
|
10
11
|
|
|
@@ -25,10 +26,14 @@ const trimTrailingSpaceTab = (text) => {
|
|
|
25
26
|
return end === text.length ? text : text.slice(0, end)
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
const getStateSource = (state) => {
|
|
30
|
+
return state && typeof state.src === 'string' ? state.src : ''
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
const registerTokenCompat = (md, baseOpt) => {
|
|
29
34
|
const isCompatibleMode = (state) => {
|
|
30
35
|
const override = state && state.env && state.env.__strongJaTokenOpt
|
|
31
|
-
if (!override) return baseOpt.__strongJaIsCompatibleMode === true
|
|
36
|
+
if (!hasRuntimeOverride(override)) return baseOpt.__strongJaIsCompatibleMode === true
|
|
32
37
|
const opt = getRuntimeOpt(state, baseOpt)
|
|
33
38
|
return opt.__strongJaIsCompatibleMode === true
|
|
34
39
|
}
|
|
@@ -82,6 +87,8 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
82
87
|
const normalizeSoftbreakSpacing = (state) => {
|
|
83
88
|
if (isCompatibleMode(state)) return
|
|
84
89
|
if (!state) return
|
|
90
|
+
const src = getStateSource(state)
|
|
91
|
+
if (!src || src.indexOf('\n') === -1) return
|
|
85
92
|
if (baseOpt.hasCjkBreaks !== true && state.md) {
|
|
86
93
|
baseOpt.hasCjkBreaks = hasCjkBreaksRule(state.md)
|
|
87
94
|
}
|
|
@@ -164,11 +171,8 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
164
171
|
const restoreSoftbreaksAfterCjk = (state) => {
|
|
165
172
|
if (isCompatibleMode(state)) return
|
|
166
173
|
if (!state) return
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
169
|
-
const opt = getRuntimeOpt(state, baseOpt)
|
|
170
|
-
if (opt.mditAttrs !== false) return
|
|
171
|
-
}
|
|
174
|
+
const src = getStateSource(state)
|
|
175
|
+
if (!src || src.indexOf('\n') === -1 || src.indexOf('{') === -1) return
|
|
172
176
|
if (!state.md || state.md.__strongJaRestoreSoftbreaksForAttrs !== true) return
|
|
173
177
|
if (baseOpt.hasCjkBreaks !== true && state.md) {
|
|
174
178
|
baseOpt.hasCjkBreaks = hasCjkBreaksRule(state.md)
|
|
@@ -239,11 +243,8 @@ const registerTokenCompat = (md, baseOpt) => {
|
|
|
239
243
|
md.core.ruler.before('linkify', 'strong_ja_token_pre_attrs', (state) => {
|
|
240
244
|
if (isCompatibleMode(state)) return
|
|
241
245
|
if (!state || !state.tokens) return
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
244
|
-
const opt = getRuntimeOpt(state, baseOpt)
|
|
245
|
-
if (opt.mditAttrs === false) return
|
|
246
|
-
}
|
|
246
|
+
const src = getStateSource(state)
|
|
247
|
+
if (!src || src.indexOf('{') === -1 || src.indexOf('}') === -1) return
|
|
247
248
|
for (let i = 0; i < state.tokens.length; i++) {
|
|
248
249
|
const token = state.tokens[i]
|
|
249
250
|
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|
package/src/token-core.js
CHANGED
|
@@ -10,13 +10,15 @@ import {
|
|
|
10
10
|
MODE_FLAG_COMPATIBLE,
|
|
11
11
|
MODE_FLAG_AGGRESSIVE,
|
|
12
12
|
MODE_FLAG_JAPANESE_PLUS,
|
|
13
|
-
getRuntimeOpt
|
|
13
|
+
getRuntimeOpt,
|
|
14
|
+
hasRuntimeOverride
|
|
14
15
|
} from './token-utils.js'
|
|
15
16
|
|
|
16
17
|
const SCAN_DELIMS_PATCHED = Symbol.for('strongJaTokenScanDelimsPatched')
|
|
17
18
|
const SINGLE_STAR_LOOKAROUND_MAX = 16
|
|
18
19
|
const PREV_STAR_HAS_OPENER = 1
|
|
19
20
|
const PREV_STAR_HAS_JP_BETWEEN = 2
|
|
21
|
+
const SCAN_DELIMS_LOOKUP_KEY = Symbol.for('strongJaTokenScanDelimsLookup')
|
|
20
22
|
|
|
21
23
|
const isSoftSpaceCode = (code) => {
|
|
22
24
|
return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_IDEOGRAPHIC_SPACE
|
|
@@ -280,7 +282,58 @@ const isAsciiGuardCloseWrapper = (code) => {
|
|
|
280
282
|
code === 0x60 // `
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
const
|
|
285
|
+
const buildScanDelimsLookupCache = (src) => {
|
|
286
|
+
const len = typeof src === 'string' ? src.length : 0
|
|
287
|
+
const prevNonSpaceSameLine = new Int32Array(len)
|
|
288
|
+
const nextNonSpaceSameLine = new Int32Array(len)
|
|
289
|
+
prevNonSpaceSameLine.fill(-1)
|
|
290
|
+
nextNonSpaceSameLine.fill(-1)
|
|
291
|
+
|
|
292
|
+
let prev = -1
|
|
293
|
+
for (let i = 0; i < len; i++) {
|
|
294
|
+
const code = src.charCodeAt(i)
|
|
295
|
+
if (code === CHAR_NEWLINE) {
|
|
296
|
+
prev = -1
|
|
297
|
+
continue
|
|
298
|
+
}
|
|
299
|
+
if (!isSoftSpaceCode(code)) prev = i
|
|
300
|
+
prevNonSpaceSameLine[i] = prev
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let next = -1
|
|
304
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
305
|
+
const code = src.charCodeAt(i)
|
|
306
|
+
if (code === CHAR_NEWLINE) {
|
|
307
|
+
next = -1
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
if (!isSoftSpaceCode(code)) next = i
|
|
311
|
+
nextNonSpaceSameLine[i] = next
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
src,
|
|
316
|
+
prevNonSpaceSameLine,
|
|
317
|
+
nextNonSpaceSameLine
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const getScanDelimsLookupCache = (state) => {
|
|
322
|
+
if (!state || typeof state.src !== 'string') return null
|
|
323
|
+
const cached = state[SCAN_DELIMS_LOOKUP_KEY]
|
|
324
|
+
if (cached && cached.src === state.src) return cached
|
|
325
|
+
const next = buildScanDelimsLookupCache(state.src)
|
|
326
|
+
state[SCAN_DELIMS_LOOKUP_KEY] = next
|
|
327
|
+
return next
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const findPrevNonSpaceIndex = (src, start, lookupCache = null) => {
|
|
331
|
+
if (start < 0) return -1
|
|
332
|
+
if (lookupCache &&
|
|
333
|
+
lookupCache.src === src &&
|
|
334
|
+
start < lookupCache.prevNonSpaceSameLine.length) {
|
|
335
|
+
return lookupCache.prevNonSpaceSameLine[start]
|
|
336
|
+
}
|
|
284
337
|
for (let i = start; i >= 0; i--) {
|
|
285
338
|
const code = src.charCodeAt(i)
|
|
286
339
|
if (code === CHAR_NEWLINE) return -1
|
|
@@ -290,7 +343,14 @@ const findPrevNonSpaceIndex = (src, start) => {
|
|
|
290
343
|
return -1
|
|
291
344
|
}
|
|
292
345
|
|
|
293
|
-
const findNextNonSpaceIndex = (src, start, max) => {
|
|
346
|
+
const findNextNonSpaceIndex = (src, start, max, lookupCache = null) => {
|
|
347
|
+
if (lookupCache &&
|
|
348
|
+
lookupCache.src === src &&
|
|
349
|
+
start >= 0 &&
|
|
350
|
+
start < lookupCache.nextNonSpaceSameLine.length) {
|
|
351
|
+
const next = lookupCache.nextNonSpaceSameLine[start]
|
|
352
|
+
return next !== -1 && next < max ? next : -1
|
|
353
|
+
}
|
|
294
354
|
for (let i = start; i < max; i++) {
|
|
295
355
|
const code = src.charCodeAt(i)
|
|
296
356
|
if (code === CHAR_NEWLINE) return -1
|
|
@@ -300,26 +360,26 @@ const findNextNonSpaceIndex = (src, start, max) => {
|
|
|
300
360
|
return -1
|
|
301
361
|
}
|
|
302
362
|
|
|
303
|
-
const hasAsciiStartAfterOptionalOpenWrappers = (src, index, max) => {
|
|
363
|
+
const hasAsciiStartAfterOptionalOpenWrappers = (src, index, max, lookupCache = null) => {
|
|
304
364
|
let i = index
|
|
305
365
|
// Two wrappers are enough for common shapes: * [ "word" ]*
|
|
306
366
|
for (let wrappers = 0; wrappers < 2 && i >= 0 && i < max; wrappers++) {
|
|
307
367
|
const code = src.charCodeAt(i)
|
|
308
368
|
if (!isAsciiGuardOpenWrapper(code)) break
|
|
309
|
-
i = findNextNonSpaceIndex(src, i + 1, max)
|
|
369
|
+
i = findNextNonSpaceIndex(src, i + 1, max, lookupCache)
|
|
310
370
|
if (i === -1) return false
|
|
311
371
|
}
|
|
312
372
|
if (i < 0 || i >= max) return false
|
|
313
373
|
return isAsciiAlphaNum(src.charCodeAt(i))
|
|
314
374
|
}
|
|
315
375
|
|
|
316
|
-
const hasAsciiEndBeforeOptionalCloseWrappers = (src, index) => {
|
|
376
|
+
const hasAsciiEndBeforeOptionalCloseWrappers = (src, index, lookupCache = null) => {
|
|
317
377
|
let i = index
|
|
318
378
|
// Two wrappers are enough for common shapes: *["word"] *
|
|
319
379
|
for (let wrappers = 0; wrappers < 2 && i >= 0; wrappers++) {
|
|
320
380
|
const code = src.charCodeAt(i)
|
|
321
381
|
if (!isAsciiGuardCloseWrapper(code)) break
|
|
322
|
-
i = findPrevNonSpaceIndex(src, i - 1)
|
|
382
|
+
i = findPrevNonSpaceIndex(src, i - 1, lookupCache)
|
|
323
383
|
if (i === -1) return false
|
|
324
384
|
}
|
|
325
385
|
if (i < 0) return false
|
|
@@ -353,7 +413,17 @@ const isSentenceBoundaryStop = (code) => {
|
|
|
353
413
|
code === 0x2049 // ⁉
|
|
354
414
|
}
|
|
355
415
|
|
|
356
|
-
const findPrevNonSpaceLimited = (src, start, maxLook) => {
|
|
416
|
+
const findPrevNonSpaceLimited = (src, start, maxLook, lookupCache = null) => {
|
|
417
|
+
if (lookupCache &&
|
|
418
|
+
lookupCache.src === src &&
|
|
419
|
+
start >= 0 &&
|
|
420
|
+
start < lookupCache.prevNonSpaceSameLine.length) {
|
|
421
|
+
const prev = lookupCache.prevNonSpaceSameLine[start]
|
|
422
|
+
if (prev !== -1 && (start - prev) < maxLook) {
|
|
423
|
+
return src.charCodeAt(prev)
|
|
424
|
+
}
|
|
425
|
+
return 0
|
|
426
|
+
}
|
|
357
427
|
let looked = 0
|
|
358
428
|
for (let i = start; i >= 0; i--) {
|
|
359
429
|
if (looked >= maxLook) break
|
|
@@ -366,7 +436,17 @@ const findPrevNonSpaceLimited = (src, start, maxLook) => {
|
|
|
366
436
|
return 0
|
|
367
437
|
}
|
|
368
438
|
|
|
369
|
-
const findNextNonSpaceLimited = (src, start, max, maxLook) => {
|
|
439
|
+
const findNextNonSpaceLimited = (src, start, max, maxLook, lookupCache = null) => {
|
|
440
|
+
if (lookupCache &&
|
|
441
|
+
lookupCache.src === src &&
|
|
442
|
+
start >= 0 &&
|
|
443
|
+
start < lookupCache.nextNonSpaceSameLine.length) {
|
|
444
|
+
const next = lookupCache.nextNonSpaceSameLine[start]
|
|
445
|
+
if (next !== -1 && next < max && (next - start) < maxLook) {
|
|
446
|
+
return src.charCodeAt(next)
|
|
447
|
+
}
|
|
448
|
+
return 0
|
|
449
|
+
}
|
|
370
450
|
let looked = 0
|
|
371
451
|
for (let i = start; i < max; i++) {
|
|
372
452
|
if (looked >= maxLook) break
|
|
@@ -379,13 +459,13 @@ const findNextNonSpaceLimited = (src, start, max, maxLook) => {
|
|
|
379
459
|
return 0
|
|
380
460
|
}
|
|
381
461
|
|
|
382
|
-
const hasJapaneseContextForBracketWrapper = (src, start, pos, max, lastChar, nextChar) => {
|
|
462
|
+
const hasJapaneseContextForBracketWrapper = (src, start, pos, max, lastChar, nextChar, lookupCache = null) => {
|
|
383
463
|
if (isWrapperOpenLike(nextChar)) {
|
|
384
|
-
const right = findNextNonSpaceLimited(src, pos, max, SINGLE_STAR_LOOKAROUND_MAX)
|
|
464
|
+
const right = findNextNonSpaceLimited(src, pos, max, SINGLE_STAR_LOOKAROUND_MAX, lookupCache)
|
|
385
465
|
if (isJapaneseChar(right)) return true
|
|
386
466
|
}
|
|
387
467
|
if (isWrapperCloseLike(lastChar)) {
|
|
388
|
-
const left = findPrevNonSpaceLimited(src, start - 2, SINGLE_STAR_LOOKAROUND_MAX)
|
|
468
|
+
const left = findPrevNonSpaceLimited(src, start - 2, SINGLE_STAR_LOOKAROUND_MAX, lookupCache)
|
|
389
469
|
if (isJapaneseChar(left)) return true
|
|
390
470
|
}
|
|
391
471
|
return false
|
|
@@ -415,7 +495,8 @@ const scanPrevSingleStarContextFlags = (src, start) => {
|
|
|
415
495
|
}
|
|
416
496
|
|
|
417
497
|
const ensurePrevStarFlags = (src, start, prevStarFlags) => {
|
|
418
|
-
|
|
498
|
+
if (prevStarFlags >= 0) return prevStarFlags
|
|
499
|
+
return scanPrevSingleStarContextFlags(src, start)
|
|
419
500
|
}
|
|
420
501
|
|
|
421
502
|
const fixTrailingStrong = (tokens, onChangeStart = null) => {
|
|
@@ -696,7 +777,7 @@ const patchScanDelims = (md) => {
|
|
|
696
777
|
|
|
697
778
|
const baseOpt = this.md ? this.md.__strongJaTokenOpt : null
|
|
698
779
|
const overrideOpt = this.env && this.env.__strongJaTokenOpt
|
|
699
|
-
const opt = overrideOpt ? getRuntimeOpt(this, baseOpt) : baseOpt
|
|
780
|
+
const opt = hasRuntimeOverride(overrideOpt) ? getRuntimeOpt(this, baseOpt) : baseOpt
|
|
700
781
|
if (!opt) {
|
|
701
782
|
return base
|
|
702
783
|
}
|
|
@@ -707,6 +788,7 @@ const patchScanDelims = (md) => {
|
|
|
707
788
|
const plusMode = (modeFlags & MODE_FLAG_JAPANESE_PLUS) !== 0
|
|
708
789
|
const aggressiveMode = (modeFlags & MODE_FLAG_AGGRESSIVE) !== 0
|
|
709
790
|
const max = this.posMax
|
|
791
|
+
let lookupCache = null
|
|
710
792
|
const lastChar = start > 0 ? src.charCodeAt(start - 1) : 0x20
|
|
711
793
|
|
|
712
794
|
const count = base && base.length ? base.length : 1
|
|
@@ -719,7 +801,15 @@ const patchScanDelims = (md) => {
|
|
|
719
801
|
const rightJapanese = isJapaneseChar(nextChar)
|
|
720
802
|
let hasJapaneseContext = leftJapanese || rightJapanese
|
|
721
803
|
if (!hasJapaneseContext && count === 1) {
|
|
722
|
-
hasJapaneseContext = hasJapaneseContextForBracketWrapper(
|
|
804
|
+
hasJapaneseContext = hasJapaneseContextForBracketWrapper(
|
|
805
|
+
src,
|
|
806
|
+
start,
|
|
807
|
+
pos,
|
|
808
|
+
max,
|
|
809
|
+
lastChar,
|
|
810
|
+
nextChar,
|
|
811
|
+
lookupCache || (lookupCache = getScanDelimsLookupCache(this))
|
|
812
|
+
)
|
|
723
813
|
}
|
|
724
814
|
if (!hasJapaneseContext && count === 1 && isExtraSingleStarClosePunct(lastChar)) {
|
|
725
815
|
prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
|
|
@@ -734,22 +824,31 @@ const patchScanDelims = (md) => {
|
|
|
734
824
|
let isLastWhiteSpace = isWhiteSpace(lastChar) || isSoftSpaceCode(lastChar)
|
|
735
825
|
let isNextWhiteSpace = isWhiteSpace(nextChar) || isSoftSpaceCode(nextChar)
|
|
736
826
|
if (isLastWhiteSpace && isSoftSpaceCode(lastChar)) {
|
|
737
|
-
const prevNonSpaceIdx = findPrevNonSpaceIndex(
|
|
827
|
+
const prevNonSpaceIdx = findPrevNonSpaceIndex(
|
|
828
|
+
src,
|
|
829
|
+
start - 2,
|
|
830
|
+
lookupCache || (lookupCache = getScanDelimsLookupCache(this))
|
|
831
|
+
)
|
|
738
832
|
if (prevNonSpaceIdx !== -1) {
|
|
739
833
|
const prevNonSpaceLocal = src.charCodeAt(prevNonSpaceIdx)
|
|
740
834
|
const plusStrictAsciiBoundary = plusMode &&
|
|
741
|
-
hasAsciiEndBeforeOptionalCloseWrappers(src, prevNonSpaceIdx)
|
|
835
|
+
hasAsciiEndBeforeOptionalCloseWrappers(src, prevNonSpaceIdx, lookupCache)
|
|
742
836
|
if (prevNonSpaceLocal !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
|
|
743
837
|
isLastWhiteSpace = false
|
|
744
838
|
}
|
|
745
839
|
}
|
|
746
840
|
}
|
|
747
841
|
if (isNextWhiteSpace && isSoftSpaceCode(nextChar)) {
|
|
748
|
-
const nextNonSpaceIdx = findNextNonSpaceIndex(
|
|
842
|
+
const nextNonSpaceIdx = findNextNonSpaceIndex(
|
|
843
|
+
src,
|
|
844
|
+
pos,
|
|
845
|
+
max,
|
|
846
|
+
lookupCache || (lookupCache = getScanDelimsLookupCache(this))
|
|
847
|
+
)
|
|
749
848
|
if (nextNonSpaceIdx !== -1) {
|
|
750
849
|
const nextNonSpace = src.charCodeAt(nextNonSpaceIdx)
|
|
751
850
|
const plusStrictAsciiBoundary = plusMode &&
|
|
752
|
-
hasAsciiStartAfterOptionalOpenWrappers(src, nextNonSpaceIdx, max)
|
|
851
|
+
hasAsciiStartAfterOptionalOpenWrappers(src, nextNonSpaceIdx, max, lookupCache)
|
|
753
852
|
if (nextNonSpace !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
|
|
754
853
|
isNextWhiteSpace = false
|
|
755
854
|
}
|
package/src/token-link-utils.js
CHANGED
|
@@ -120,10 +120,7 @@ const cloneTextToken = (source, content) => {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const applyBracketSegmentFlags = (token, seg) => {
|
|
123
|
-
if (seg === '[' || seg === ']') {
|
|
124
|
-
token.__strongJaHasBracket = true
|
|
125
|
-
token.__strongJaBracketAtomic = true
|
|
126
|
-
} else if (seg === '[]') {
|
|
123
|
+
if (seg === '[' || seg === ']' || seg === '[]') {
|
|
127
124
|
token.__strongJaHasBracket = true
|
|
128
125
|
token.__strongJaBracketAtomic = true
|
|
129
126
|
} else {
|
|
@@ -641,16 +638,21 @@ const collectBrokenMarkLinkMergeRemovals = (tokens) => {
|
|
|
641
638
|
|
|
642
639
|
const applyBrokenMarkLinkMergeRemovals = (tokens, removals, onChangeStart = null) => {
|
|
643
640
|
if (!removals || removals.length === 0) return false
|
|
644
|
-
const removeFlags = new Array(tokens.length).fill(false)
|
|
645
641
|
for (let idx = removals.length - 1; idx >= 0; idx--) {
|
|
646
|
-
|
|
647
|
-
if (onChangeStart) onChangeStart(removal.closeIdx)
|
|
648
|
-
removeFlags[removal.closeIdx] = true
|
|
649
|
-
removeFlags[removal.reopenIdx] = true
|
|
642
|
+
if (onChangeStart) onChangeStart(removals[idx].closeIdx)
|
|
650
643
|
}
|
|
651
644
|
const kept = []
|
|
645
|
+
let removalIdx = 0
|
|
646
|
+
let nextRemoval = removals[removalIdx]
|
|
652
647
|
for (let idx = 0; idx < tokens.length; idx++) {
|
|
653
|
-
if (
|
|
648
|
+
if (nextRemoval && (idx === nextRemoval.closeIdx || idx === nextRemoval.reopenIdx)) {
|
|
649
|
+
if (idx === nextRemoval.reopenIdx) {
|
|
650
|
+
removalIdx++
|
|
651
|
+
nextRemoval = removals[removalIdx]
|
|
652
|
+
}
|
|
653
|
+
continue
|
|
654
|
+
}
|
|
655
|
+
kept.push(tokens[idx])
|
|
654
656
|
}
|
|
655
657
|
tokens.splice(0, tokens.length, ...kept)
|
|
656
658
|
return true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildLinkCloseMap } from '../token-link-utils.js'
|
|
2
2
|
import {
|
|
3
3
|
isAsteriskEmphasisToken,
|
|
4
|
-
|
|
4
|
+
buildBrokenRefWrapperRangeSignals,
|
|
5
5
|
buildAsteriskWrapperPrefixStats,
|
|
6
6
|
shouldAttemptBrokenRefRewrite
|
|
7
7
|
} from './guards.js'
|
|
@@ -194,7 +194,13 @@ const resolveBrokenRefCandidateGuardFlow = (
|
|
|
194
194
|
hooks = null,
|
|
195
195
|
fallbackCache = null
|
|
196
196
|
) => {
|
|
197
|
-
|
|
197
|
+
const wrapperSignals = buildBrokenRefWrapperRangeSignals(
|
|
198
|
+
children,
|
|
199
|
+
brokenRefCandidate.start,
|
|
200
|
+
segmentEnd,
|
|
201
|
+
brokenRefCandidate.startTextOffset
|
|
202
|
+
)
|
|
203
|
+
if (!wrapperSignals.hasTextMarker) {
|
|
198
204
|
return BROKEN_REF_FLOW_SKIP_NO_TEXT_MARKER
|
|
199
205
|
}
|
|
200
206
|
const wrapperPrefixStats = ensureBrokenRefWrapperPrefixStats(children, facts, hooks, fallbackCache)
|
|
@@ -203,7 +209,8 @@ const resolveBrokenRefCandidateGuardFlow = (
|
|
|
203
209
|
brokenRefCandidate.start,
|
|
204
210
|
segmentEnd,
|
|
205
211
|
brokenRefCandidate.startTextOffset,
|
|
206
|
-
wrapperPrefixStats
|
|
212
|
+
wrapperPrefixStats,
|
|
213
|
+
wrapperSignals
|
|
207
214
|
)) {
|
|
208
215
|
return BROKEN_REF_FLOW_SKIP_GUARD
|
|
209
216
|
}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import { isJapaneseChar } from '../token-utils.js'
|
|
2
2
|
|
|
3
|
+
const CHAR_ASTERISK = 0x2A // *
|
|
4
|
+
|
|
3
5
|
const hasMarkerChars = (text) => {
|
|
4
6
|
return !!text && text.indexOf('*') !== -1
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
const contentHasMarkerCharsFrom = (content, from) => {
|
|
8
|
-
if (!content) return false
|
|
9
|
-
const start = from > 0 ? from : 0
|
|
10
|
-
if (start === 0) return hasMarkerChars(content)
|
|
11
|
-
if (start >= content.length) return false
|
|
12
|
-
return content.indexOf('*', start) !== -1
|
|
13
|
-
}
|
|
14
|
-
|
|
15
9
|
const isAsteriskEmphasisToken = (token) => {
|
|
16
10
|
if (!token || !token.type) return false
|
|
17
11
|
if (token.type !== 'strong_open' &&
|
|
@@ -78,20 +72,6 @@ const hasEmphasisSignalInRange = (tokens, startIdx, endIdx) => {
|
|
|
78
72
|
return false
|
|
79
73
|
}
|
|
80
74
|
|
|
81
|
-
const hasTextMarkerCharsInRange = (tokens, startIdx, endIdx, firstTextOffset = 0) => {
|
|
82
|
-
if (!tokens || startIdx < 0 || endIdx < startIdx) return false
|
|
83
|
-
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
84
|
-
const token = tokens[i]
|
|
85
|
-
if (!token || token.type !== 'text' || !token.content) continue
|
|
86
|
-
if (i === startIdx && firstTextOffset > 0) {
|
|
87
|
-
if (contentHasMarkerCharsFrom(token.content, firstTextOffset)) return true
|
|
88
|
-
continue
|
|
89
|
-
}
|
|
90
|
-
if (textTokenHasMarkerChars(token)) return true
|
|
91
|
-
}
|
|
92
|
-
return false
|
|
93
|
-
}
|
|
94
|
-
|
|
95
75
|
const isStrongRunSoftSpace = (code) => {
|
|
96
76
|
return code === 0x20 || code === 0x09 || code === 0x0A || code === 0x3000
|
|
97
77
|
}
|
|
@@ -107,21 +87,24 @@ const isStrongRunTextLike = (code) => {
|
|
|
107
87
|
return isStrongRunAsciiWord(code) || isJapaneseChar(code)
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
const countDelimiterLikeStrongRuns = (content,
|
|
90
|
+
const countDelimiterLikeStrongRuns = (content, from = 0, limit = 0) => {
|
|
111
91
|
let at = from > 0 ? from : 0
|
|
112
92
|
const len = content.length
|
|
113
|
-
const markerCode = marker.charCodeAt(0)
|
|
114
93
|
let count = 0
|
|
115
|
-
while (at < len) {
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
while (at + 1 < len) {
|
|
95
|
+
if (content.charCodeAt(at) !== CHAR_ASTERISK ||
|
|
96
|
+
content.charCodeAt(at + 1) !== CHAR_ASTERISK) {
|
|
97
|
+
at++
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
const pos = at
|
|
118
101
|
const prevCode = pos > 0 ? content.charCodeAt(pos - 1) : 0
|
|
119
|
-
const nextPos = pos +
|
|
102
|
+
const nextPos = pos + 2
|
|
120
103
|
const nextCode = nextPos < len ? content.charCodeAt(nextPos) : 0
|
|
121
|
-
const prevSameMarker = prevCode ===
|
|
122
|
-
const nextSameMarker = nextCode ===
|
|
104
|
+
const prevSameMarker = prevCode === CHAR_ASTERISK
|
|
105
|
+
const nextSameMarker = nextCode === CHAR_ASTERISK
|
|
123
106
|
if (prevSameMarker || nextSameMarker) {
|
|
124
|
-
at = pos +
|
|
107
|
+
at = pos + 2
|
|
125
108
|
continue
|
|
126
109
|
}
|
|
127
110
|
const prevSoft = prevCode !== 0 && isStrongRunSoftSpace(prevCode)
|
|
@@ -131,92 +114,50 @@ const countDelimiterLikeStrongRuns = (content, marker, from = 0, limit = 0) => {
|
|
|
131
114
|
const nextTextLike = isStrongRunTextLike(nextCode)
|
|
132
115
|
const hasTextNeighbor = prevTextLike || nextTextLike
|
|
133
116
|
if (!hasTextNeighbor) {
|
|
134
|
-
at = pos +
|
|
117
|
+
at = pos + 2
|
|
135
118
|
continue
|
|
136
119
|
}
|
|
137
120
|
const atBoundary = prevCode === 0 || nextCode === 0
|
|
138
121
|
if (!atBoundary && (!prevTextLike || !nextTextLike)) {
|
|
139
|
-
at = pos +
|
|
122
|
+
at = pos + 2
|
|
140
123
|
continue
|
|
141
124
|
}
|
|
142
125
|
if (hasPrevOrNext && !prevSoft && !nextSoft) {
|
|
143
126
|
count++
|
|
144
127
|
if (limit > 0 && count >= limit) return count
|
|
145
128
|
}
|
|
146
|
-
at = pos +
|
|
129
|
+
at = pos + 2
|
|
147
130
|
}
|
|
148
131
|
return count
|
|
149
132
|
}
|
|
150
133
|
|
|
151
|
-
const countStrongMarkerRunsInTextRange = (tokens, startIdx, endIdx, firstTextOffset = 0, limit = 0) => {
|
|
152
|
-
if (!tokens || startIdx < 0 || endIdx < startIdx) return 0
|
|
153
|
-
let total = 0
|
|
154
|
-
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
155
|
-
const token = tokens[i]
|
|
156
|
-
if (!token || token.type !== 'text' || !token.content) continue
|
|
157
|
-
const content = token.content
|
|
158
|
-
const scanFrom = i === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
159
|
-
if (scanFrom >= content.length) continue
|
|
160
|
-
const remain = limit > 0 ? (limit - total) : 0
|
|
161
|
-
total += countDelimiterLikeStrongRuns(content, '**', scanFrom, remain)
|
|
162
|
-
if (limit > 0 && total >= limit) {
|
|
163
|
-
return total
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return total
|
|
167
|
-
}
|
|
168
|
-
|
|
169
134
|
const buildAsteriskWrapperPrefixStats = (tokens) => {
|
|
170
135
|
const len = Array.isArray(tokens) ? tokens.length : 0
|
|
171
136
|
const strongDepthPrefix = new Array(len + 1)
|
|
172
137
|
const emDepthPrefix = new Array(len + 1)
|
|
173
|
-
const strongOpenPrefix = new Array(len + 1)
|
|
174
|
-
const strongClosePrefix = new Array(len + 1)
|
|
175
|
-
const emOpenPrefix = new Array(len + 1)
|
|
176
|
-
const emClosePrefix = new Array(len + 1)
|
|
177
138
|
let strongDepth = 0
|
|
178
139
|
let emDepthCount = 0
|
|
179
|
-
let strongOpenCount = 0
|
|
180
|
-
let strongCloseCount = 0
|
|
181
|
-
let emOpenCount = 0
|
|
182
|
-
let emCloseCount = 0
|
|
183
140
|
strongDepthPrefix[0] = 0
|
|
184
141
|
emDepthPrefix[0] = 0
|
|
185
|
-
strongOpenPrefix[0] = 0
|
|
186
|
-
strongClosePrefix[0] = 0
|
|
187
|
-
emOpenPrefix[0] = 0
|
|
188
|
-
emClosePrefix[0] = 0
|
|
189
142
|
for (let i = 0; i < len; i++) {
|
|
190
143
|
const token = tokens[i]
|
|
191
144
|
if (token && token.type && isAsteriskEmphasisToken(token)) {
|
|
192
145
|
if (token.type === 'strong_open') {
|
|
193
146
|
strongDepth++
|
|
194
|
-
strongOpenCount++
|
|
195
147
|
} else if (token.type === 'strong_close') {
|
|
196
148
|
if (strongDepth > 0) strongDepth--
|
|
197
|
-
strongCloseCount++
|
|
198
149
|
} else if (token.type === 'em_open') {
|
|
199
150
|
emDepthCount++
|
|
200
|
-
emOpenCount++
|
|
201
151
|
} else if (token.type === 'em_close') {
|
|
202
152
|
if (emDepthCount > 0) emDepthCount--
|
|
203
|
-
emCloseCount++
|
|
204
153
|
}
|
|
205
154
|
}
|
|
206
155
|
strongDepthPrefix[i + 1] = strongDepth
|
|
207
156
|
emDepthPrefix[i + 1] = emDepthCount
|
|
208
|
-
strongOpenPrefix[i + 1] = strongOpenCount
|
|
209
|
-
strongClosePrefix[i + 1] = strongCloseCount
|
|
210
|
-
emOpenPrefix[i + 1] = emOpenCount
|
|
211
|
-
emClosePrefix[i + 1] = emCloseCount
|
|
212
157
|
}
|
|
213
158
|
return {
|
|
214
159
|
strongDepth: strongDepthPrefix,
|
|
215
|
-
emDepth: emDepthPrefix
|
|
216
|
-
strongOpen: strongOpenPrefix,
|
|
217
|
-
strongClose: strongClosePrefix,
|
|
218
|
-
emOpen: emOpenPrefix,
|
|
219
|
-
emClose: emClosePrefix
|
|
160
|
+
emDepth: emDepthPrefix
|
|
220
161
|
}
|
|
221
162
|
}
|
|
222
163
|
|
|
@@ -229,6 +170,8 @@ const createBrokenRefWrapperRangeSignals = () => {
|
|
|
229
170
|
hasUnderscoreText: false,
|
|
230
171
|
hasCodeInline: false,
|
|
231
172
|
hasUnderscoreEmphasisToken: false,
|
|
173
|
+
hasTextMarker: false,
|
|
174
|
+
strongRunCount: 0,
|
|
232
175
|
strongOpenInRange: 0,
|
|
233
176
|
strongCloseInRange: 0,
|
|
234
177
|
emOpenInRange: 0,
|
|
@@ -239,17 +182,25 @@ const createBrokenRefWrapperRangeSignals = () => {
|
|
|
239
182
|
const updateBrokenRefTextRangeSignals = (signals, token, tokenIdx, startIdx, firstTextOffset) => {
|
|
240
183
|
if (!token || token.type !== 'text' || !token.content) return
|
|
241
184
|
const content = token.content
|
|
185
|
+
const scanFrom = tokenIdx === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
242
186
|
// Keep this at 0 (instead of firstTextOffset) so historical fail-safe
|
|
243
187
|
// behavior around noisy leading chains in the first text token stays unchanged.
|
|
244
188
|
if (!signals.hasLongStarNoise && content.indexOf('***') !== -1) {
|
|
245
189
|
signals.hasLongStarNoise = true
|
|
246
190
|
}
|
|
247
191
|
if (!signals.hasUnderscoreText) {
|
|
248
|
-
const scanFrom = tokenIdx === startIdx && firstTextOffset > 0 ? firstTextOffset : 0
|
|
249
192
|
if (scanFrom < content.length && content.indexOf('_', scanFrom) !== -1) {
|
|
250
193
|
signals.hasUnderscoreText = true
|
|
251
194
|
}
|
|
252
195
|
}
|
|
196
|
+
if (!signals.hasTextMarker) {
|
|
197
|
+
signals.hasTextMarker = scanFrom === 0
|
|
198
|
+
? textTokenHasMarkerChars(token)
|
|
199
|
+
: content.indexOf('*', scanFrom) !== -1
|
|
200
|
+
}
|
|
201
|
+
if (signals.strongRunCount < 2 && scanFrom < content.length) {
|
|
202
|
+
signals.strongRunCount += countDelimiterLikeStrongRuns(content, scanFrom, 2 - signals.strongRunCount)
|
|
203
|
+
}
|
|
253
204
|
}
|
|
254
205
|
|
|
255
206
|
const updateBrokenRefWrapperTokenSignals = (signals, token, isAsteriskEmphasis) => {
|
|
@@ -343,18 +294,10 @@ const hasPreexistingWrapperCloseOnlyInRange = (tokens, startIdx, endIdx, prefixS
|
|
|
343
294
|
const hasPrefix =
|
|
344
295
|
!!prefixStats &&
|
|
345
296
|
Array.isArray(prefixStats.strongDepth) &&
|
|
346
|
-
Array.isArray(prefixStats.emDepth)
|
|
347
|
-
Array.isArray(prefixStats.strongOpen) &&
|
|
348
|
-
Array.isArray(prefixStats.strongClose) &&
|
|
349
|
-
Array.isArray(prefixStats.emOpen) &&
|
|
350
|
-
Array.isArray(prefixStats.emClose)
|
|
297
|
+
Array.isArray(prefixStats.emDepth)
|
|
351
298
|
if (hasPrefix &&
|
|
352
299
|
startIdx < prefixStats.strongDepth.length &&
|
|
353
|
-
startIdx < prefixStats.emDepth.length
|
|
354
|
-
(endIdx + 1) < prefixStats.strongOpen.length &&
|
|
355
|
-
(endIdx + 1) < prefixStats.strongClose.length &&
|
|
356
|
-
(endIdx + 1) < prefixStats.emOpen.length &&
|
|
357
|
-
(endIdx + 1) < prefixStats.emClose.length) {
|
|
300
|
+
startIdx < prefixStats.emDepth.length) {
|
|
358
301
|
if (needsStrongCloseOnly) {
|
|
359
302
|
preStrongDepth = prefixStats.strongDepth[startIdx] || 0
|
|
360
303
|
if (preStrongDepth > 0) return true
|
|
@@ -434,8 +377,8 @@ const isLowConfidenceBrokenRefRange = (tokens, startIdx, endIdx, firstTextOffset
|
|
|
434
377
|
return hasBrokenRefLowConfidenceWrapperRisk(tokens, startIdx, endIdx, wrapperPrefixStats, signals)
|
|
435
378
|
}
|
|
436
379
|
|
|
437
|
-
const hasBrokenRefStrongRunEvidence = (
|
|
438
|
-
return
|
|
380
|
+
const hasBrokenRefStrongRunEvidence = (wrapperSignals) => {
|
|
381
|
+
return !!wrapperSignals && wrapperSignals.strongRunCount >= 2
|
|
439
382
|
}
|
|
440
383
|
|
|
441
384
|
const hasBrokenRefExplicitAsteriskSignal = (wrapperSignals) => {
|
|
@@ -450,22 +393,31 @@ const shouldRejectBalancedBrokenRefRewrite = (wrapperSignals) => {
|
|
|
450
393
|
return !wrapperSignals.hasImbalance && hasBrokenRefExplicitAsteriskSignal(wrapperSignals)
|
|
451
394
|
}
|
|
452
395
|
|
|
453
|
-
const shouldAttemptBrokenRefRewriteFromSignals = (
|
|
396
|
+
const shouldAttemptBrokenRefRewriteFromSignals = (wrapperSignals) => {
|
|
454
397
|
if (hasBrokenRefImmediateRewriteSignal(wrapperSignals)) return true
|
|
455
398
|
if (shouldRejectBalancedBrokenRefRewrite(wrapperSignals)) return false
|
|
456
|
-
return hasBrokenRefStrongRunEvidence(
|
|
399
|
+
return hasBrokenRefStrongRunEvidence(wrapperSignals)
|
|
457
400
|
}
|
|
458
401
|
|
|
459
|
-
const shouldAttemptBrokenRefRewrite = (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
402
|
+
const shouldAttemptBrokenRefRewrite = (
|
|
403
|
+
tokens,
|
|
404
|
+
startIdx,
|
|
405
|
+
endIdx,
|
|
406
|
+
firstTextOffset = 0,
|
|
407
|
+
wrapperPrefixStats = null,
|
|
408
|
+
wrapperSignals = null
|
|
409
|
+
) => {
|
|
410
|
+
const signals = wrapperSignals || buildBrokenRefWrapperRangeSignals(tokens, startIdx, endIdx, firstTextOffset)
|
|
411
|
+
if (!signals.hasTextMarker) return false
|
|
412
|
+
if (isLowConfidenceBrokenRefRange(tokens, startIdx, endIdx, firstTextOffset, wrapperPrefixStats, signals)) return false
|
|
413
|
+
return shouldAttemptBrokenRefRewriteFromSignals(signals)
|
|
463
414
|
}
|
|
464
415
|
|
|
465
416
|
const scanInlinePostprocessSignals = (children) => {
|
|
466
417
|
let hasEmphasis = false
|
|
467
418
|
let hasLinkOpen = false
|
|
468
419
|
let hasLinkClose = false
|
|
420
|
+
let hasCodeInline = false
|
|
469
421
|
for (let j = 0; j < children.length; j++) {
|
|
470
422
|
const child = children[j]
|
|
471
423
|
if (!child) continue
|
|
@@ -478,12 +430,16 @@ const scanInlinePostprocessSignals = (children) => {
|
|
|
478
430
|
if (!hasLinkClose && child.type === 'link_close') {
|
|
479
431
|
hasLinkClose = true
|
|
480
432
|
}
|
|
433
|
+
if (!hasCodeInline && child.type === 'code_inline') {
|
|
434
|
+
hasCodeInline = true
|
|
435
|
+
}
|
|
481
436
|
if (hasEmphasis && hasLinkOpen && hasLinkClose) break
|
|
482
437
|
}
|
|
483
438
|
return {
|
|
484
439
|
hasEmphasis,
|
|
485
440
|
hasLinkOpen,
|
|
486
|
-
hasLinkClose
|
|
441
|
+
hasLinkClose,
|
|
442
|
+
hasCodeInline
|
|
487
443
|
}
|
|
488
444
|
}
|
|
489
445
|
|
|
@@ -492,8 +448,8 @@ export {
|
|
|
492
448
|
isAsteriskEmphasisToken,
|
|
493
449
|
hasJapaneseContextInRange,
|
|
494
450
|
hasEmphasisSignalInRange,
|
|
495
|
-
hasTextMarkerCharsInRange,
|
|
496
451
|
buildAsteriskWrapperPrefixStats,
|
|
452
|
+
buildBrokenRefWrapperRangeSignals,
|
|
497
453
|
shouldAttemptBrokenRefRewrite,
|
|
498
454
|
scanInlinePostprocessSignals
|
|
499
455
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../token-core.js'
|
|
11
11
|
import {
|
|
12
12
|
getRuntimeOpt,
|
|
13
|
+
hasRuntimeOverride,
|
|
13
14
|
getReferenceCount
|
|
14
15
|
} from '../token-utils.js'
|
|
15
16
|
import {
|
|
@@ -87,46 +88,13 @@ const buildInlinePostprocessFacts = (children, inlineContent) => {
|
|
|
87
88
|
hasEmphasis: preScan.hasEmphasis,
|
|
88
89
|
hasLinkOpen: preScan.hasLinkOpen,
|
|
89
90
|
hasLinkClose: preScan.hasLinkClose,
|
|
90
|
-
hasCodeInline:
|
|
91
|
-
referenceCount: undefined,
|
|
92
|
-
metrics: undefined,
|
|
91
|
+
hasCodeInline: preScan.hasCodeInline,
|
|
93
92
|
linkCloseMap: undefined,
|
|
94
93
|
wrapperPrefixStats: undefined,
|
|
95
94
|
rebuildLevelStart: undefined
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
const ensureInlineHasCodeInline = (facts, tokens) => {
|
|
100
|
-
if (facts.hasCodeInline !== undefined) return facts.hasCodeInline
|
|
101
|
-
let hasCodeInline = false
|
|
102
|
-
if (tokens && tokens.length > 0) {
|
|
103
|
-
for (let idx = 0; idx < tokens.length; idx++) {
|
|
104
|
-
if (tokens[idx] && tokens[idx].type === 'code_inline') {
|
|
105
|
-
hasCodeInline = true
|
|
106
|
-
break
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
facts.hasCodeInline = hasCodeInline
|
|
111
|
-
return hasCodeInline
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const ensureInlineReferenceCount = (facts, state) => {
|
|
115
|
-
if (!facts || !facts.hasBracketText) return 0
|
|
116
|
-
if (facts.referenceCount === undefined) {
|
|
117
|
-
facts.referenceCount = getReferenceCount(state)
|
|
118
|
-
}
|
|
119
|
-
return facts.referenceCount
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const ensureInlineMetrics = (facts, state) => {
|
|
123
|
-
if (!facts) return null
|
|
124
|
-
if (facts.metrics === undefined) {
|
|
125
|
-
facts.metrics = getPostprocessMetrics(state)
|
|
126
|
-
}
|
|
127
|
-
return facts.metrics
|
|
128
|
-
}
|
|
129
|
-
|
|
130
98
|
const ensureInlineLinkCloseMap = (facts, tokens) => {
|
|
131
99
|
if (!tokens || tokens.length === 0) return new Map()
|
|
132
100
|
if (!facts) return buildLinkCloseMap(tokens, 0, tokens.length - 1)
|
|
@@ -281,6 +249,9 @@ const isSoftSpaceCode = (code) => {
|
|
|
281
249
|
return code === 0x20 || code === 0x09 || code === 0x3000
|
|
282
250
|
}
|
|
283
251
|
|
|
252
|
+
const CHAR_ASTERISK = 0x2A // *
|
|
253
|
+
const CHAR_BACKSLASH = 0x5C // \
|
|
254
|
+
|
|
284
255
|
const isAsciiWordCode = (code) => {
|
|
285
256
|
return (code >= 0x30 && code <= 0x39) ||
|
|
286
257
|
(code >= 0x41 && code <= 0x5A) ||
|
|
@@ -299,7 +270,7 @@ const textStartsAsciiWord = (text) => {
|
|
|
299
270
|
|
|
300
271
|
const isEscapedMarkerAt = (content, index) => {
|
|
301
272
|
let slashCount = 0
|
|
302
|
-
for (let i = index - 1; i >= 0 && content.charCodeAt(i) ===
|
|
273
|
+
for (let i = index - 1; i >= 0 && content.charCodeAt(i) === CHAR_BACKSLASH; i--) {
|
|
303
274
|
slashCount++
|
|
304
275
|
}
|
|
305
276
|
return (slashCount % 2) === 1
|
|
@@ -307,24 +278,24 @@ const isEscapedMarkerAt = (content, index) => {
|
|
|
307
278
|
|
|
308
279
|
const findLastStandaloneStrongMarker = (content) => {
|
|
309
280
|
if (!content || content.length < 2) return -1
|
|
310
|
-
let pos = content.
|
|
311
|
-
|
|
281
|
+
for (let pos = content.length - 2; pos >= 0; pos--) {
|
|
282
|
+
if (content.charCodeAt(pos) !== CHAR_ASTERISK ||
|
|
283
|
+
content.charCodeAt(pos + 1) !== CHAR_ASTERISK) {
|
|
284
|
+
continue
|
|
285
|
+
}
|
|
312
286
|
const prev = pos > 0 ? content.charCodeAt(pos - 1) : 0
|
|
313
287
|
const next = pos + 2 < content.length ? content.charCodeAt(pos + 2) : 0
|
|
314
|
-
if (prev
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return pos
|
|
318
|
-
}
|
|
319
|
-
pos = content.lastIndexOf('**', pos - 1)
|
|
288
|
+
if (prev === CHAR_ASTERISK || next === CHAR_ASTERISK) continue
|
|
289
|
+
if (prev === CHAR_BACKSLASH && isEscapedMarkerAt(content, pos)) continue
|
|
290
|
+
return pos
|
|
320
291
|
}
|
|
321
292
|
return -1
|
|
322
293
|
}
|
|
323
294
|
|
|
324
295
|
const hasLeadingStandaloneStrongMarker = (content) => {
|
|
325
296
|
if (!content || content.length < 2) return false
|
|
326
|
-
if (content.charCodeAt(0) !==
|
|
327
|
-
if (content.length > 2 && content.charCodeAt(2) ===
|
|
297
|
+
if (content.charCodeAt(0) !== CHAR_ASTERISK || content.charCodeAt(1) !== CHAR_ASTERISK) return false
|
|
298
|
+
if (content.length > 2 && content.charCodeAt(2) === CHAR_ASTERISK) return false
|
|
328
299
|
return true
|
|
329
300
|
}
|
|
330
301
|
|
|
@@ -474,7 +445,7 @@ const tryActivateInlineEmphasis = (
|
|
|
474
445
|
return true
|
|
475
446
|
}
|
|
476
447
|
if (facts.hasBracketText || facts.hasLinkOpen || facts.hasLinkClose) return false
|
|
477
|
-
if (!
|
|
448
|
+
if (!facts.hasCodeInline) return false
|
|
478
449
|
if (tryPromoteStrongAroundInlineCode(children, strictAsciiCodeGuard, strictAsciiStrongGuard, facts)) {
|
|
479
450
|
facts.hasEmphasis = true
|
|
480
451
|
return true
|
|
@@ -485,7 +456,7 @@ const tryActivateInlineEmphasis = (
|
|
|
485
456
|
const shouldRunInlineBrokenRefRepair = (facts, inlineContent, state) => {
|
|
486
457
|
if (!facts || !facts.hasLinkOpen || !facts.hasLinkClose || !facts.hasBracketText) return false
|
|
487
458
|
if (inlineContent.indexOf('***') !== -1) return false
|
|
488
|
-
return
|
|
459
|
+
return getReferenceCount(state) > 0
|
|
489
460
|
}
|
|
490
461
|
|
|
491
462
|
const applyBrokenRefRepairFacts = (facts, repairs) => {
|
|
@@ -508,7 +479,7 @@ const runInlineBrokenRefRepairStage = (children, facts, inlineContent, state) =>
|
|
|
508
479
|
children,
|
|
509
480
|
maxRepairPass,
|
|
510
481
|
scanState,
|
|
511
|
-
|
|
482
|
+
getPostprocessMetrics(state),
|
|
512
483
|
facts,
|
|
513
484
|
BROKEN_REF_REPAIR_HOOKS
|
|
514
485
|
)
|
|
@@ -522,7 +493,7 @@ const runInlineEmphasisRepairStage = (children, facts, state, isJapaneseMode) =>
|
|
|
522
493
|
const markChangedFrom = createInlineChangeMarker(facts)
|
|
523
494
|
if (fixEmOuterStrongSequence(children, markChangedFrom)) changed = true
|
|
524
495
|
if (facts.hasLinkClose) {
|
|
525
|
-
const metrics =
|
|
496
|
+
const metrics = getPostprocessMetrics(state)
|
|
526
497
|
if (fixTailAfterLinkStrongClose(children, isJapaneseMode, metrics, markChangedFrom)) changed = true
|
|
527
498
|
if (fixLeadingAsteriskEm(children, markChangedFrom)) changed = true
|
|
528
499
|
}
|
|
@@ -533,7 +504,7 @@ const runInlineEmphasisRepairStage = (children, facts, state, isJapaneseMode) =>
|
|
|
533
504
|
|
|
534
505
|
const shouldRunInlineCollapsedRefRepair = (facts, state) => {
|
|
535
506
|
if (!facts || !facts.hasBracketText) return false
|
|
536
|
-
return
|
|
507
|
+
return getReferenceCount(state) > 0
|
|
537
508
|
}
|
|
538
509
|
|
|
539
510
|
const applyCollapsedRefRepairFacts = (facts) => {
|
|
@@ -567,7 +538,7 @@ const shouldSkipInlinePostprocessToken = (children, facts, isJapaneseMode) => {
|
|
|
567
538
|
!facts.hasBracketText &&
|
|
568
539
|
!facts.hasLinkOpen &&
|
|
569
540
|
!facts.hasLinkClose &&
|
|
570
|
-
!
|
|
541
|
+
!facts.hasCodeInline) {
|
|
571
542
|
return true
|
|
572
543
|
}
|
|
573
544
|
if (isJapaneseMode &&
|
|
@@ -655,7 +626,7 @@ const registerTokenPostprocess = (md, baseOpt) => {
|
|
|
655
626
|
md.core.ruler.after('inline', 'strong_ja_token_postprocess', (state) => {
|
|
656
627
|
if (!state || !state.tokens) return
|
|
657
628
|
const overrideOpt = state.env && state.env.__strongJaTokenOpt
|
|
658
|
-
const opt = overrideOpt ? getRuntimeOpt(state, baseOpt) : baseOpt
|
|
629
|
+
const opt = hasRuntimeOverride(overrideOpt) ? getRuntimeOpt(state, baseOpt) : baseOpt
|
|
659
630
|
if (!opt.__strongJaPostprocessActive) return
|
|
660
631
|
const isJapaneseMode = opt.__strongJaIsJapaneseMode
|
|
661
632
|
const strictAsciiCodeGuard = opt.__strongJaStrictAsciiCodeGuard
|
package/src/token-utils.js
CHANGED
|
@@ -8,6 +8,7 @@ const MODE_FLAG_AGGRESSIVE = 1 << 1
|
|
|
8
8
|
const MODE_FLAG_JAPANESE_BASE = 1 << 2
|
|
9
9
|
const MODE_FLAG_JAPANESE_PLUS = 1 << 3
|
|
10
10
|
const MODE_FLAG_JAPANESE_ANY = MODE_FLAG_JAPANESE_BASE | MODE_FLAG_JAPANESE_PLUS
|
|
11
|
+
const HAS_OWN = Object.prototype.hasOwnProperty
|
|
11
12
|
const REG_CJK_BREAKS_RULE_NAME = /(^|[_-])cjk_breaks([_-]|$)/
|
|
12
13
|
const VALID_CANONICAL_MODES = new Set([
|
|
13
14
|
'compatible',
|
|
@@ -126,27 +127,42 @@ const deriveOptionInfo = (opt) => {
|
|
|
126
127
|
return opt
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
state.__strongJaTokenRuntimeOpt
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
130
|
+
const hasRuntimeOverride = (override) => {
|
|
131
|
+
if (!override || typeof override !== 'object') return false
|
|
132
|
+
return (HAS_OWN.call(override, 'mode') && override.mode !== undefined) ||
|
|
133
|
+
(HAS_OWN.call(override, 'postprocess') && override.postprocess !== undefined)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const getRuntimeOpt = (state, baseOpt) => {
|
|
137
|
+
const override = state && state.env ? state.env.__strongJaTokenOpt : null
|
|
138
|
+
if (!hasRuntimeOverride(override)) return deriveOptionInfo(baseOpt)
|
|
139
|
+
if (state.__strongJaTokenRuntimeOpt &&
|
|
140
|
+
state.__strongJaTokenRuntimeBase === baseOpt &&
|
|
141
|
+
state.__strongJaTokenRuntimeOverride === override) {
|
|
142
|
+
return state.__strongJaTokenRuntimeOpt
|
|
143
|
+
}
|
|
144
|
+
const merged = baseOpt && typeof baseOpt === 'object' ? { ...baseOpt } : {}
|
|
145
|
+
if (HAS_OWN.call(override, 'mode') && override.mode !== undefined) merged.mode = override.mode
|
|
146
|
+
if (HAS_OWN.call(override, 'postprocess') && override.postprocess !== undefined) merged.postprocess = override.postprocess
|
|
147
|
+
state.__strongJaTokenRuntimeOpt = deriveOptionInfo(merged)
|
|
148
|
+
state.__strongJaTokenRuntimeBase = baseOpt
|
|
149
|
+
state.__strongJaTokenRuntimeOverride = override
|
|
150
|
+
return state.__strongJaTokenRuntimeOpt
|
|
151
|
+
}
|
|
143
152
|
|
|
144
153
|
const getReferenceCount = (state) => {
|
|
145
154
|
if (!state) return 0
|
|
146
155
|
let referenceCount = state.__strongJaReferenceCount
|
|
147
156
|
if (referenceCount !== undefined) return referenceCount
|
|
148
157
|
const references = state.env && state.env.references
|
|
149
|
-
|
|
158
|
+
if (!references) {
|
|
159
|
+
state.__strongJaReferenceCount = 0
|
|
160
|
+
return 0
|
|
161
|
+
}
|
|
162
|
+
referenceCount = 0
|
|
163
|
+
for (const key in references) {
|
|
164
|
+
if (HAS_OWN.call(references, key)) referenceCount++
|
|
165
|
+
}
|
|
150
166
|
state.__strongJaReferenceCount = referenceCount
|
|
151
167
|
return referenceCount
|
|
152
168
|
}
|
|
@@ -223,9 +239,10 @@ export {
|
|
|
223
239
|
isCjkBreaksRuleName,
|
|
224
240
|
resolveMode,
|
|
225
241
|
getModeFlags,
|
|
226
|
-
deriveModeInfo,
|
|
227
|
-
deriveOptionInfo,
|
|
228
|
-
|
|
242
|
+
deriveModeInfo,
|
|
243
|
+
deriveOptionInfo,
|
|
244
|
+
hasRuntimeOverride,
|
|
245
|
+
MODE_FLAG_COMPATIBLE,
|
|
229
246
|
MODE_FLAG_AGGRESSIVE,
|
|
230
247
|
MODE_FLAG_JAPANESE_BASE,
|
|
231
248
|
MODE_FLAG_JAPANESE_PLUS,
|