@peaceroad/markdown-it-strong-ja 0.9.0 → 0.9.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/README.md CHANGED
@@ -357,6 +357,83 @@ Supporting visuals:
357
357
  - `aggressive`:
358
358
  `<p>broken **tail <a href="https://x.test">aa<strong>aa</strong><em>Text</em><strong>and<em>More</em>bb</strong>bb</a> after</p>`
359
359
 
360
+ ## Compatibility Notes
361
+
362
+ ### `markdown-it-attrs` 5.x parity
363
+
364
+ When `markdown-it-attrs` is installed, strong-ja follows the token stream produced by that plugin and does not reinterpret where `{...}` attributes should be attached. This is intentional: strong-ja should not make attribute syntax mean something different from `markdown-it-attrs` alone.
365
+
366
+ One edge case to be aware of is a tight list item followed by an emphasized line:
367
+
368
+ ```markdown
369
+ - e {.li-style}
370
+ *{.ul-style}*
371
+ ```
372
+
373
+ With `markdown-it-attrs` 5.x, the first attribute block is consumed as a block-level attribute on the hidden `paragraph_open` inside the tight list. Because that paragraph token is hidden by markdown-it's tight-list rendering, the class is not visible in the final HTML. The second `{.ul-style}` is inside emphasis text, not a suffix after a closed inline token, so it remains literal text:
374
+
375
+ ```html
376
+ <ul>
377
+ <li>e
378
+ <em>{.ul-style}</em></li>
379
+ </ul>
380
+ ```
381
+
382
+ This output matches `markdown-it-attrs` alone. To attach attributes intentionally, use the syntax owned by `markdown-it-attrs`, for example:
383
+
384
+ ```markdown
385
+ - e
386
+ {.ul-style}
387
+ ```
388
+
389
+ ```html
390
+ <ul class="ul-style">
391
+ <li>e</li>
392
+ </ul>
393
+ ```
394
+
395
+ or attach inline attributes after the closing inline token:
396
+
397
+ ```markdown
398
+ - e
399
+ *x*{.ul-style}
400
+ ```
401
+
402
+ ```html
403
+ <ul>
404
+ <li>e
405
+ <em class="ul-style">x</em></li>
406
+ </ul>
407
+ ```
408
+
409
+ strong-ja keeps this as dependency parity rather than adding a local workaround.
410
+
411
+ ### `markdown-it` 14.2 astral delimiter policy
412
+
413
+ `markdown-it` 14.2 recognizes astral characters (surrogate pairs) as full Unicode code points when scanning emphasis delimiters. strong-ja keeps `compatible` mode aligned with that upstream behavior.
414
+
415
+ In Japanese modes, strong-ja still only adds its own delimiter relaxation when Japanese/CJK context is present. Astral Han characters, such as CJK Extension B, are treated as CJK context:
416
+
417
+ ```markdown
418
+ *𠀋?*abc*
419
+ ```
420
+
421
+ ```html
422
+ <p><em>𠀋?</em>abc*</p>
423
+ ```
424
+
425
+ Emoji or symbol-only English contexts remain aligned with `markdown-it` and are not promoted just because they are astral characters:
426
+
427
+ ```markdown
428
+ *😀?*abc*
429
+ ```
430
+
431
+ ```html
432
+ <p>*😀?<em>abc</em></p>
433
+ ```
434
+
435
+ Symbols inside Japanese prose may still be emphasized by the existing Japanese-context rule, for example `**😀**です` can render as `<p><strong>😀</strong>です</p>`. Use `mode: 'compatible'` when exact `markdown-it` 14.2 delimiter behavior is required.
436
+
360
437
  ## Options
361
438
 
362
439
  ### `mode`
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.9.0",
4
+ "version": "0.9.2",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "files": [
@@ -26,6 +26,7 @@
26
26
  "test:all": "node test/test-all.js",
27
27
  "bench:scan": "node test/material/perf-scan-delims.mjs",
28
28
  "bench:postprocess": "node test/material/perf-postprocess.mjs",
29
+ "bench:isolated": "node test/material/bench-isolated.mjs",
29
30
  "analyze:postprocess-calls": "node test/material/analyze-postprocess-calls.mjs",
30
31
  "analyze:fastpath": "node test/material/analyze-fastpath-hits.mjs"
31
32
  },
@@ -33,17 +34,16 @@
33
34
  "author": "peaceroad <peaceroad@gmail.com>",
34
35
  "license": "MIT",
35
36
  "dependencies": {
36
- "markdown-it": "^14.1.0"
37
+ "markdown-it": "^14.2.0"
37
38
  },
38
39
  "devDependencies": {
39
- "@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.10",
40
- "@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.11.0",
41
- "@peaceroad/markdown-it-renderer-image": "^0.12.0",
42
- "@peaceroad/markdown-it-renderer-inline-text": "^0.8.0",
43
- "markdown-it-attrs": "^4.3.1",
40
+ "@peaceroad/markdown-it-cjk-breaks-mod": "^0.1.11",
41
+ "@peaceroad/markdown-it-hr-sandwiched-semantic-container": "^0.12.0",
42
+ "@peaceroad/markdown-it-renderer-image": "^0.16.0",
43
+ "@peaceroad/markdown-it-renderer-inline-text": "^0.8.1",
44
+ "markdown-it-attrs": "^5.0.0",
44
45
  "markdown-it-sub": "^2.0.0",
45
46
  "markdown-it-sup": "^2.0.0",
46
- "p7d-markdown-it-p-captions": "^0.21.0"
47
+ "p7d-markdown-it-p-captions": "^0.23.0"
47
48
  }
48
49
  }
49
-
@@ -2,6 +2,9 @@ import Token from 'markdown-it/lib/token.mjs'
2
2
  import {
3
3
  REG_ATTRS,
4
4
  isJapaneseChar,
5
+ isAsciiWordCode,
6
+ codePointAtSafe,
7
+ codePointBeforeSafe,
5
8
  hasCjkBreaksRule,
6
9
  isCjkBreaksRuleName,
7
10
  getRuntimeOpt,
@@ -9,12 +12,6 @@ import {
9
12
  moveRuleAfter
10
13
  } from './token-utils.js'
11
14
 
12
- const isAsciiWordCode = (code) => {
13
- return (code >= 0x30 && code <= 0x39) ||
14
- (code >= 0x41 && code <= 0x5A) ||
15
- (code >= 0x61 && code <= 0x7A)
16
- }
17
-
18
15
  const trimTrailingSpaceTab = (text) => {
19
16
  if (!text) return text
20
17
  let end = text.length
@@ -124,8 +121,8 @@ const registerTokenCompat = (md, baseOpt) => {
124
121
  if (!prevToken || !nextToken) continue
125
122
  if (prevToken.type !== 'text' || !prevToken.content) continue
126
123
  if (nextToken.type !== 'text' || !nextToken.content) continue
127
- const prevCharCode = prevToken.content.charCodeAt(prevToken.content.length - 1)
128
- const nextCharCode = nextToken.content.charCodeAt(0)
124
+ const prevCharCode = codePointBeforeSafe(prevToken.content, prevToken.content.length, 0)
125
+ const nextCharCode = codePointAtSafe(nextToken.content, 0, 0)
129
126
  const isAsciiWord = isAsciiWordCode(nextCharCode)
130
127
  const shouldReplace = isAsciiWord &&
131
128
  isJapaneseChar(prevCharCode) && !isJapaneseChar(nextCharCode)
@@ -143,8 +140,8 @@ const registerTokenCompat = (md, baseOpt) => {
143
140
  for (let idx = 0; idx < child.content.length; idx++) {
144
141
  const ch = child.content[idx]
145
142
  if (ch === '\n') {
146
- const prevCharCode = idx > 0 ? child.content.charCodeAt(idx - 1) : 0
147
- const nextCharCode = idx + 1 < child.content.length ? child.content.charCodeAt(idx + 1) : 0
143
+ const prevCharCode = codePointBeforeSafe(child.content, idx, 0)
144
+ const nextCharCode = codePointAtSafe(child.content, idx + 1, 0)
148
145
  const isAsciiWord = isAsciiWordCode(nextCharCode)
149
146
  const shouldReplace = isAsciiWord &&
150
147
  isJapaneseChar(prevCharCode) && !isJapaneseChar(nextCharCode)
@@ -192,7 +189,7 @@ const registerTokenCompat = (md, baseOpt) => {
192
189
  if (!prevTextCharCode || !isJapaneseChar(prevTextCharCode)) continue
193
190
  const next = children[j + 1]
194
191
  if (!next || next.type !== 'text' || !next.content) continue
195
- const nextCharCode = next.content.charCodeAt(0)
192
+ const nextCharCode = codePointAtSafe(next.content, 0, 0)
196
193
  if (nextCharCode !== 0x7B) continue
197
194
  child.type = 'softbreak'
198
195
  child.tag = ''
@@ -201,7 +198,7 @@ const registerTokenCompat = (md, baseOpt) => {
201
198
  child.info = ''
202
199
  continue
203
200
  }
204
- prevTextCharCode = child.content.charCodeAt(child.content.length - 1)
201
+ prevTextCharCode = codePointBeforeSafe(child.content, child.content.length, 0)
205
202
  }
206
203
  }
207
204
  }
package/src/token-core.js CHANGED
@@ -2,16 +2,19 @@ import { isWhiteSpace } from 'markdown-it/lib/common/utils.mjs'
2
2
  import Token from 'markdown-it/lib/token.mjs'
3
3
  import {
4
4
  CHAR_ASTERISK,
5
- CHAR_SPACE,
6
- CHAR_TAB,
7
5
  CHAR_NEWLINE,
8
- CHAR_IDEOGRAPHIC_SPACE,
6
+ codePointAtSafe,
7
+ codePointBeforeSafe,
8
+ codePointStartBefore,
9
+ codePointSize,
9
10
  isJapaneseChar,
11
+ isAsciiWordCode,
12
+ isSoftSpaceCode,
10
13
  MODE_FLAG_COMPATIBLE,
11
14
  MODE_FLAG_AGGRESSIVE,
12
15
  MODE_FLAG_JAPANESE_PLUS,
13
- getRuntimeOpt,
14
- hasRuntimeOverride
16
+ hasRuntimeOverride,
17
+ getRuntimeOpt
15
18
  } from './token-utils.js'
16
19
 
17
20
  const SCAN_DELIMS_PATCHED = Symbol.for('strongJaTokenScanDelimsPatched')
@@ -20,10 +23,6 @@ const PREV_STAR_HAS_OPENER = 1
20
23
  const PREV_STAR_HAS_JP_BETWEEN = 2
21
24
  const SCAN_DELIMS_LOOKUP_KEY = Symbol.for('strongJaTokenScanDelimsLookup')
22
25
 
23
- const isSoftSpaceCode = (code) => {
24
- return code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_IDEOGRAPHIC_SPACE
25
- }
26
-
27
26
  const isPlusQuoteWrapperOpen = (code) => {
28
27
  return code === 0x2018 || // ‘
29
28
  code === 0x201C || // “
@@ -256,12 +255,6 @@ const isSingleStarClosingBoundary = (code) => {
256
255
  isClosingBracketLike(code)
257
256
  }
258
257
 
259
- const isAsciiAlphaNum = (code) => {
260
- return (code >= 0x30 && code <= 0x39) ||
261
- (code >= 0x41 && code <= 0x5A) ||
262
- (code >= 0x61 && code <= 0x7A)
263
- }
264
-
265
258
  const isAsciiGuardOpenWrapper = (code) => {
266
259
  return code === 0x22 || // "
267
260
  code === 0x27 || // '
@@ -291,24 +284,34 @@ const buildScanDelimsLookupCache = (src) => {
291
284
 
292
285
  let prev = -1
293
286
  for (let i = 0; i < len; i++) {
294
- const code = src.charCodeAt(i)
287
+ const code = codePointAtSafe(src, i)
295
288
  if (code === CHAR_NEWLINE) {
296
289
  prev = -1
297
290
  continue
298
291
  }
292
+ const size = codePointSize(code)
299
293
  if (!isSoftSpaceCode(code)) prev = i
300
294
  prevNonSpaceSameLine[i] = prev
295
+ if (size === 2 && i + 1 < len) {
296
+ prevNonSpaceSameLine[i + 1] = prev
297
+ i++
298
+ }
301
299
  }
302
300
 
303
301
  let next = -1
304
302
  for (let i = len - 1; i >= 0; i--) {
305
- const code = src.charCodeAt(i)
303
+ const cpStart = codePointStartBefore(src, i + 1)
304
+ const code = cpStart === -1 ? 0 : codePointAtSafe(src, cpStart)
306
305
  if (code === CHAR_NEWLINE) {
307
306
  next = -1
308
307
  continue
309
308
  }
310
- if (!isSoftSpaceCode(code)) next = i
309
+ if (!isSoftSpaceCode(code)) next = cpStart
311
310
  nextNonSpaceSameLine[i] = next
311
+ if (cpStart !== i) {
312
+ nextNonSpaceSameLine[cpStart] = next
313
+ i = cpStart
314
+ }
312
315
  }
313
316
 
314
317
  return {
@@ -334,11 +337,13 @@ const findPrevNonSpaceIndex = (src, start, lookupCache = null) => {
334
337
  start < lookupCache.prevNonSpaceSameLine.length) {
335
338
  return lookupCache.prevNonSpaceSameLine[start]
336
339
  }
337
- for (let i = start; i >= 0; i--) {
338
- const code = src.charCodeAt(i)
340
+ for (let i = start; i >= 0;) {
341
+ const cpStart = codePointStartBefore(src, i + 1)
342
+ if (cpStart === -1) return -1
343
+ const code = codePointAtSafe(src, cpStart)
339
344
  if (code === CHAR_NEWLINE) return -1
340
- if (isSoftSpaceCode(code)) continue
341
- return i
345
+ if (!isSoftSpaceCode(code)) return cpStart
346
+ i = cpStart - 1
342
347
  }
343
348
  return -1
344
349
  }
@@ -351,11 +356,11 @@ const findNextNonSpaceIndex = (src, start, max, lookupCache = null) => {
351
356
  const next = lookupCache.nextNonSpaceSameLine[start]
352
357
  return next !== -1 && next < max ? next : -1
353
358
  }
354
- for (let i = start; i < max; i++) {
355
- const code = src.charCodeAt(i)
359
+ for (let i = start; i < max;) {
360
+ const code = codePointAtSafe(src, i)
356
361
  if (code === CHAR_NEWLINE) return -1
357
- if (isSoftSpaceCode(code)) continue
358
- return i
362
+ if (!isSoftSpaceCode(code)) return i
363
+ i += codePointSize(code)
359
364
  }
360
365
  return -1
361
366
  }
@@ -364,26 +369,26 @@ const hasAsciiStartAfterOptionalOpenWrappers = (src, index, max, lookupCache = n
364
369
  let i = index
365
370
  // Two wrappers are enough for common shapes: * [ "word" ]*
366
371
  for (let wrappers = 0; wrappers < 2 && i >= 0 && i < max; wrappers++) {
367
- const code = src.charCodeAt(i)
372
+ const code = codePointAtSafe(src, i)
368
373
  if (!isAsciiGuardOpenWrapper(code)) break
369
374
  i = findNextNonSpaceIndex(src, i + 1, max, lookupCache)
370
375
  if (i === -1) return false
371
376
  }
372
377
  if (i < 0 || i >= max) return false
373
- return isAsciiAlphaNum(src.charCodeAt(i))
378
+ return isAsciiWordCode(codePointAtSafe(src, i))
374
379
  }
375
380
 
376
381
  const hasAsciiEndBeforeOptionalCloseWrappers = (src, index, lookupCache = null) => {
377
382
  let i = index
378
383
  // Two wrappers are enough for common shapes: *["word"] *
379
384
  for (let wrappers = 0; wrappers < 2 && i >= 0; wrappers++) {
380
- const code = src.charCodeAt(i)
385
+ const code = codePointAtSafe(src, i)
381
386
  if (!isAsciiGuardCloseWrapper(code)) break
382
387
  i = findPrevNonSpaceIndex(src, i - 1, lookupCache)
383
388
  if (i === -1) return false
384
389
  }
385
390
  if (i < 0) return false
386
- return isAsciiAlphaNum(src.charCodeAt(i))
391
+ return isAsciiWordCode(codePointAtSafe(src, i))
387
392
  }
388
393
 
389
394
  const isMarkdownStructuralOpenWrapper = (code) => {
@@ -420,18 +425,20 @@ const findPrevNonSpaceLimited = (src, start, maxLook, lookupCache = null) => {
420
425
  start < lookupCache.prevNonSpaceSameLine.length) {
421
426
  const prev = lookupCache.prevNonSpaceSameLine[start]
422
427
  if (prev !== -1 && (start - prev) < maxLook) {
423
- return src.charCodeAt(prev)
428
+ return codePointAtSafe(src, prev)
424
429
  }
425
430
  return 0
426
431
  }
427
432
  let looked = 0
428
- for (let i = start; i >= 0; i--) {
433
+ for (let i = start; i >= 0;) {
429
434
  if (looked >= maxLook) break
430
- const code = src.charCodeAt(i)
431
- looked++
435
+ const cpStart = codePointStartBefore(src, i + 1)
436
+ if (cpStart === -1) break
437
+ const code = codePointAtSafe(src, cpStart)
438
+ looked += i - cpStart + 1
432
439
  if (code === CHAR_NEWLINE) return 0
433
- if (isSoftSpaceCode(code)) continue
434
- return code
440
+ if (!isSoftSpaceCode(code)) return code
441
+ i = cpStart - 1
435
442
  }
436
443
  return 0
437
444
  }
@@ -443,18 +450,19 @@ const findNextNonSpaceLimited = (src, start, max, maxLook, lookupCache = null) =
443
450
  start < lookupCache.nextNonSpaceSameLine.length) {
444
451
  const next = lookupCache.nextNonSpaceSameLine[start]
445
452
  if (next !== -1 && next < max && (next - start) < maxLook) {
446
- return src.charCodeAt(next)
453
+ return codePointAtSafe(src, next)
447
454
  }
448
455
  return 0
449
456
  }
450
457
  let looked = 0
451
- for (let i = start; i < max; i++) {
458
+ for (let i = start; i < max;) {
452
459
  if (looked >= maxLook) break
453
- const code = src.charCodeAt(i)
454
- looked++
460
+ const code = codePointAtSafe(src, i)
461
+ const size = codePointSize(code)
462
+ looked += size
455
463
  if (code === CHAR_NEWLINE) return 0
456
- if (isSoftSpaceCode(code)) continue
457
- return code
464
+ if (!isSoftSpaceCode(code)) return code
465
+ i += size
458
466
  }
459
467
  return 0
460
468
  }
@@ -473,8 +481,8 @@ const hasJapaneseContextForBracketWrapper = (src, start, pos, max, lastChar, nex
473
481
 
474
482
  const scanPrevSingleStarContextFlags = (src, start) => {
475
483
  let hasJapaneseBetween = false
476
- for (let i = start - 1; i >= 0; i--) {
477
- const code = src.charCodeAt(i)
484
+ for (let i = codePointStartBefore(src, start); i >= 0; i = codePointStartBefore(src, i)) {
485
+ const code = codePointAtSafe(src, i)
478
486
  if (code === CHAR_NEWLINE) break
479
487
  if (isSentenceBoundaryStop(code) && i < start - 1) break
480
488
  if (code !== CHAR_ASTERISK) {
@@ -486,8 +494,8 @@ const scanPrevSingleStarContextFlags = (src, start) => {
486
494
  backslashCount++
487
495
  }
488
496
  if ((backslashCount % 2) === 1) continue
489
- const prevCode = i > 0 ? src.charCodeAt(i - 1) : 0
490
- const nextCode = i + 1 < src.length ? src.charCodeAt(i + 1) : 0
497
+ const prevCode = codePointBeforeSafe(src, i, 0)
498
+ const nextCode = codePointAtSafe(src, i + 1, 0)
491
499
  if (prevCode === CHAR_ASTERISK || nextCode === CHAR_ASTERISK) continue
492
500
  return hasJapaneseBetween ? PREV_STAR_HAS_OPENER | PREV_STAR_HAS_JP_BETWEEN : PREV_STAR_HAS_OPENER
493
501
  }
@@ -789,12 +797,12 @@ const patchScanDelims = (md) => {
789
797
  const aggressiveMode = (modeFlags & MODE_FLAG_AGGRESSIVE) !== 0
790
798
  const max = this.posMax
791
799
  let lookupCache = null
792
- const lastChar = start > 0 ? src.charCodeAt(start - 1) : 0x20
800
+ const lastChar = codePointBeforeSafe(src, start, 0x20)
793
801
 
794
802
  const count = base && base.length ? base.length : 1
795
803
  const pos = start + count
796
804
 
797
- const nextChar = pos < max ? src.charCodeAt(pos) : 0x20
805
+ const nextChar = codePointAtSafe(src, pos, 0x20)
798
806
  let prevStarFlags = -1
799
807
 
800
808
  const leftJapanese = isJapaneseChar(lastChar)
@@ -830,7 +838,7 @@ const patchScanDelims = (md) => {
830
838
  lookupCache || (lookupCache = getScanDelimsLookupCache(this))
831
839
  )
832
840
  if (prevNonSpaceIdx !== -1) {
833
- const prevNonSpaceLocal = src.charCodeAt(prevNonSpaceIdx)
841
+ const prevNonSpaceLocal = codePointAtSafe(src, prevNonSpaceIdx)
834
842
  const plusStrictAsciiBoundary = plusMode &&
835
843
  hasAsciiEndBeforeOptionalCloseWrappers(src, prevNonSpaceIdx, lookupCache)
836
844
  if (prevNonSpaceLocal !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
@@ -846,7 +854,7 @@ const patchScanDelims = (md) => {
846
854
  lookupCache || (lookupCache = getScanDelimsLookupCache(this))
847
855
  )
848
856
  if (nextNonSpaceIdx !== -1) {
849
- const nextNonSpace = src.charCodeAt(nextNonSpaceIdx)
857
+ const nextNonSpace = codePointAtSafe(src, nextNonSpaceIdx)
850
858
  const plusStrictAsciiBoundary = plusMode &&
851
859
  hasAsciiStartAfterOptionalOpenWrappers(src, nextNonSpaceIdx, max, lookupCache)
852
860
  if (nextNonSpace !== CHAR_ASTERISK && !plusStrictAsciiBoundary) {
@@ -870,8 +878,10 @@ const patchScanDelims = (md) => {
870
878
  if (!aggressiveMode && count === 1) {
871
879
  // Keep local directionality to avoid degrading markdown-it-valid runs,
872
880
  // e.g. `[。*a**](u)` where the first `*` should remain opener-only.
873
- const rightIsBoundary = isSingleStarClosingBoundary(nextChar) || isWrapperOpenLike(nextChar)
874
- const leftIsBoundary = isSingleStarBoundary(lastChar) || isWrapperCloseLike(lastChar)
881
+ const rightIsOpenWrapper = isWrapperOpenLike(nextChar)
882
+ const leftIsCloseWrapper = isWrapperCloseLike(lastChar)
883
+ const rightIsBoundary = isSingleStarClosingBoundary(nextChar) || rightIsOpenWrapper
884
+ const leftIsBoundary = isSingleStarBoundary(lastChar) || leftIsCloseWrapper
875
885
  if (leftJapanese && !rightJapanese && !rightIsBoundary) {
876
886
  prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
877
887
  if ((prevStarFlags & PREV_STAR_HAS_OPENER) === 0) {
@@ -880,28 +890,30 @@ const patchScanDelims = (md) => {
880
890
  } else if (!leftJapanese && rightJapanese && !leftIsBoundary) {
881
891
  relaxedOpen = false
882
892
  }
883
- const rightIsOpenWrapper = isWrapperOpenLike(nextChar)
884
- const leftIsCloseWrapper = isWrapperCloseLike(lastChar)
885
- prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
886
- const hasPrevJapaneseOpener = (prevStarFlags & PREV_STAR_HAS_OPENER) !== 0
887
- const hasJapaneseSincePrevStar = (prevStarFlags & PREV_STAR_HAS_JP_BETWEEN) !== 0
888
893
  const leftIsExtraClosePunct = isExtraSingleStarClosePunct(lastChar)
889
- const canForceCloseByPunct = leftIsExtraClosePunct && hasJapaneseSincePrevStar
890
- if (leftJapanese &&
891
- rightIsOpenWrapper &&
892
- !hasPrevJapaneseOpener &&
893
- !isMarkdownStructuralOpenWrapper(nextChar)) {
894
- forceOpen = true
895
- forceClose = false
896
- } else if (leftIsCloseWrapper && rightJapanese && hasPrevJapaneseOpener) {
897
- forceOpen = false
898
- forceClose = true
899
- } else if ((leftIsCloseWrapper || canForceCloseByPunct) &&
900
- !rightJapanese &&
901
- !rightIsBoundary &&
902
- hasPrevJapaneseOpener) {
903
- forceOpen = false
904
- forceClose = true
894
+ const canCheckForceOpen =
895
+ leftJapanese && rightIsOpenWrapper && !isMarkdownStructuralOpenWrapper(nextChar)
896
+ const canCheckForceClose =
897
+ (leftIsCloseWrapper && rightJapanese) ||
898
+ ((leftIsCloseWrapper || leftIsExtraClosePunct) && !rightJapanese && !rightIsBoundary)
899
+ if (canCheckForceOpen || canCheckForceClose) {
900
+ prevStarFlags = ensurePrevStarFlags(src, start, prevStarFlags)
901
+ const hasPrevJapaneseOpener = (prevStarFlags & PREV_STAR_HAS_OPENER) !== 0
902
+ const hasJapaneseSincePrevStar = (prevStarFlags & PREV_STAR_HAS_JP_BETWEEN) !== 0
903
+ const canForceCloseByPunct = leftIsExtraClosePunct && hasJapaneseSincePrevStar
904
+ if (canCheckForceOpen && !hasPrevJapaneseOpener) {
905
+ forceOpen = true
906
+ forceClose = false
907
+ } else if (leftIsCloseWrapper && rightJapanese && hasPrevJapaneseOpener) {
908
+ forceOpen = false
909
+ forceClose = true
910
+ } else if ((leftIsCloseWrapper || canForceCloseByPunct) &&
911
+ !rightJapanese &&
912
+ !rightIsBoundary &&
913
+ hasPrevJapaneseOpener) {
914
+ forceOpen = false
915
+ forceClose = true
916
+ }
905
917
  }
906
918
  }
907
919
  const finalOpen = forceOpen === null ? ((base && base.can_open) || relaxedOpen) : forceOpen
@@ -1,6 +1,6 @@
1
1
  import Token from 'markdown-it/lib/token.mjs'
2
2
  import { isWhiteSpace } from 'markdown-it/lib/common/utils.mjs'
3
- import { getReferenceCount } from './token-utils.js'
3
+ import { cloneMap, getReferenceCount } from './token-utils.js'
4
4
 
5
5
  const CHAR_OPEN_BRACKET = 0x5B // [
6
6
  const CHAR_CLOSE_BRACKET = 0x5D // ]
@@ -80,11 +80,6 @@ const getNormalizeRef = (state) => {
80
80
  }
81
81
 
82
82
 
83
- const cloneMap = (map) => {
84
- if (!map || !Array.isArray(map)) return null
85
- return [map[0], map[1]]
86
- }
87
-
88
83
  const getMapFromTokenRange = (tokens, startIdx, endIdx) => {
89
84
  if (!tokens || startIdx > endIdx) return null
90
85
  let startLine = null