fix: edge cases highlight codeblock (#3984)

This commit is contained in:
Faisal Amir 2024-11-10 17:57:49 +07:00 committed by GitHub
parent 1643b9dd86
commit 244d3d10dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -69,6 +69,9 @@ const RichTextEditor = ({
}: RichTextEditorProps) => { }: RichTextEditorProps) => {
const [editor] = useState(() => withHistory(withReact(createEditor()))) const [editor] = useState(() => withHistory(withReact(createEditor())))
const currentLanguage = useRef<string>('plaintext') const currentLanguage = useRef<string>('plaintext')
const hasStartBackticks = useRef<boolean>(false)
const hasEndBackticks = useRef<boolean>(false)
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom) const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
const textareaRef = useRef<HTMLDivElement>(null) const textareaRef = useRef<HTMLDivElement>(null)
const activeThreadId = useAtomValue(getActiveThreadIdAtom) const activeThreadId = useAtomValue(getActiveThreadIdAtom)
@ -133,20 +136,31 @@ const RichTextEditor = ({
node.children.forEach((child: { text: any }, childIndex: number) => { node.children.forEach((child: { text: any }, childIndex: number) => {
const text = child.text const text = child.text
const codeBlockStartRegex = /```(\w*)/g
const matches = [...currentPrompt.matchAll(codeBlockStartRegex)]
if (matches.length % 2 !== 0) {
hasEndBackticks.current = false
}
// Match code block start and end // Match code block start and end
const startMatch = text.match(/^```(\w*)$/) const lang = text.match(/^```(\w*)$/)
const endMatch = text.match(/^```$/) const endMatch = text.match(/^```$/)
if (startMatch) { if (lang) {
// If it's the start of a code block, store the language // If it's the start of a code block, store the language
currentLanguage.current = startMatch[1] || 'plaintext' currentLanguage.current = lang[1] || 'plaintext'
} else if (endMatch) { } else if (endMatch) {
// Reset language when code block ends // Reset language when code block ends
currentLanguage.current = 'plaintext' currentLanguage.current = 'plaintext'
} else if (currentLanguage.current !== 'plaintext') { } else if (
hasStartBackticks.current &&
hasEndBackticks.current &&
currentLanguage.current !== 'plaintext'
) {
// Highlight entire code line if in a code block // Highlight entire code line if in a code block
const leadingSpaces = text.match(/^\s*/)?.[0] ?? '' // Capture leading spaces
const codeContent = text.trimStart() // Remove leading spaces for highlighting const codeContent = text.trim() // Remove leading spaces for highlighting
let highlighted = '' let highlighted = ''
highlighted = hljs.highlightAuto(codeContent).value highlighted = hljs.highlightAuto(codeContent).value
@ -168,21 +182,9 @@ const RichTextEditor = ({
let slateTextIndex = 0 let slateTextIndex = 0
// Adjust to include leading spaces in the ranges and preserve formatting
ranges.push({
anchor: { path: [...path, childIndex], offset: 0 },
focus: {
path: [...path, childIndex],
offset: slateTextIndex,
},
type: 'code',
code: true,
language: currentLanguage.current,
className: '', // No class for leading spaces
})
doc.body.childNodes.forEach((childNode) => { doc.body.childNodes.forEach((childNode) => {
const childText = childNode.textContent || '' const childText = childNode.textContent || ''
const length = childText.length const length = childText.length
const className = const className =
childNode.nodeType === Node.ELEMENT_NODE childNode.nodeType === Node.ELEMENT_NODE
@ -192,11 +194,11 @@ const RichTextEditor = ({
ranges.push({ ranges.push({
anchor: { anchor: {
path: [...path, childIndex], path: [...path, childIndex],
offset: slateTextIndex + leadingSpaces.length, offset: slateTextIndex,
}, },
focus: { focus: {
path: [...path, childIndex], path: [...path, childIndex],
offset: slateTextIndex + leadingSpaces.length + length, offset: slateTextIndex + length,
}, },
type: 'code', type: 'code',
code: true, code: true,
@ -220,7 +222,7 @@ const RichTextEditor = ({
return ranges return ranges
}, },
[editor] [currentPrompt, editor]
) )
// RenderLeaf applies the decoration styles // RenderLeaf applies the decoration styles
@ -340,9 +342,20 @@ const RichTextEditor = ({
currentLanguage.current = 'plaintext' currentLanguage.current = 'plaintext'
} }
const hasCodeBlockStart = combinedText.match(/^```(\w*)/m) const hasCodeBlockStart = combinedText.match(/^```(\w*)/m)
const hasCodeBlockEnd = combinedText.match(/^```$/m)
// Set language to plaintext if no code block with language identifier is found // Set language to plaintext if no code block with language identifier is found
if (!hasCodeBlockStart) { if (!hasCodeBlockStart) {
currentLanguage.current = 'plaintext' currentLanguage.current = 'plaintext'
hasStartBackticks.current = false
} else {
hasStartBackticks.current = true
}
if (!hasCodeBlockEnd) {
currentLanguage.current = 'plaintext'
hasEndBackticks.current = false
} else {
hasEndBackticks.current = true
} }
}} }}
> >