fix(web): render CJK-adjacent strong emphasis
This commit is contained in:
@@ -5,13 +5,15 @@ import rehypeKatex from 'rehype-katex'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
import { CodeBlock } from './CodeBlock.tsx'
|
||||
import { remarkCjkFriendlyStrong } from './remarkCjkFriendlyStrong.ts'
|
||||
import { remarkMathCompatibility } from './remarkMathCompatibility.ts'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import css from './MarkdownText.module.css'
|
||||
|
||||
const streamingRemarkPlugins = [remarkGfm]
|
||||
const streamingRemarkPlugins = [remarkGfm, remarkCjkFriendlyStrong]
|
||||
const settledRemarkPlugins = [
|
||||
remarkGfm,
|
||||
remarkCjkFriendlyStrong,
|
||||
remarkMathCompatibility,
|
||||
remarkMath,
|
||||
]
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
|
||||
|
||||
import { attention } from 'micromark-core-commonmark'
|
||||
import { unicodePunctuation } from 'micromark-util-character'
|
||||
import { classifyCharacter } from 'micromark-util-classify-character'
|
||||
import { codes, constants } from 'micromark-util-symbol'
|
||||
import type { Construct, Extension, State, Tokenizer } from 'micromark-util-types'
|
||||
|
||||
interface RemarkProcessor {
|
||||
data(): { micromarkExtensions?: Extension[] }
|
||||
}
|
||||
|
||||
const cjkCharacter = new RegExp([
|
||||
'\\p{Script_Extensions=Han}',
|
||||
'\\p{Script_Extensions=Hiragana}',
|
||||
'\\p{Script_Extensions=Katakana}',
|
||||
'\\p{Script_Extensions=Hangul}',
|
||||
'\\p{Script_Extensions=Bopomofo}',
|
||||
].join('|'), 'u')
|
||||
|
||||
function isCjkCharacter(code: number | null): boolean {
|
||||
return code !== null && code >= 0 && cjkCharacter.test(String.fromCodePoint(code))
|
||||
}
|
||||
|
||||
const tokenizeCjkFriendlyAttention: Tokenizer = function (effects, ok, nok) {
|
||||
const configuredAttentionMarkers = this.parser.constructs.attentionMarkers.null
|
||||
if (configuredAttentionMarkers === undefined) {
|
||||
throw new Error('micromark CommonMark attention markers are unavailable')
|
||||
}
|
||||
const attentionMarkers = configuredAttentionMarkers
|
||||
const previous = this.previous
|
||||
const before = classifyCharacter(previous)
|
||||
let marker: number | null = codes.eof
|
||||
|
||||
return start
|
||||
|
||||
function start(code: number | null): State | undefined {
|
||||
/* v8 ignore next -- this text construct is dispatched only for an asterisk. */
|
||||
if (code !== codes.asterisk) return nok(code)
|
||||
marker = code
|
||||
effects.enter('attentionSequence')
|
||||
return inside(code)
|
||||
}
|
||||
|
||||
function inside(code: number | null): State | undefined {
|
||||
if (code === marker) {
|
||||
effects.consume(code)
|
||||
return inside
|
||||
}
|
||||
|
||||
const token = effects.exit('attentionSequence')
|
||||
const after = classifyCharacter(code)
|
||||
const open = !after || (after === constants.characterGroupPunctuation && Boolean(before))
|
||||
|| attentionMarkers.includes(code)
|
||||
const commonMarkClose = !before
|
||||
|| (before === constants.characterGroupPunctuation && Boolean(after))
|
||||
|| attentionMarkers.includes(previous)
|
||||
const markerCount = token.end.offset - token.start.offset
|
||||
const cjkStrongClose = markerCount >= 2
|
||||
&& unicodePunctuation(previous)
|
||||
&& isCjkCharacter(code)
|
||||
const close = commonMarkClose || cjkStrongClose
|
||||
|
||||
token._open = open
|
||||
token._close = close
|
||||
return ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
const cjkFriendlyAttention: Construct = {
|
||||
name: 'cjkFriendlyAttention',
|
||||
resolveAll: attention.resolveAll,
|
||||
tokenize: tokenizeCjkFriendlyAttention,
|
||||
}
|
||||
|
||||
const cjkFriendlyStrong: Extension = {
|
||||
text: { [codes.asterisk]: cjkFriendlyAttention },
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend CommonMark asterisk strong emphasis for punctuation-delimited CJK prose.
|
||||
* @returns Nothing.
|
||||
*/
|
||||
export function remarkCjkFriendlyStrong(this: RemarkProcessor): undefined {
|
||||
const data = this.data()
|
||||
const extensions = data.micromarkExtensions ?? (data.micromarkExtensions = [])
|
||||
extensions.push(cjkFriendlyStrong)
|
||||
}
|
||||
Reference in New Issue
Block a user