mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-21 18:55:19 -04:00
Extract code highlighting from async call
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
c6857a46c8
commit
9b69914b60
3 changed files with 57 additions and 39 deletions
|
@ -9,9 +9,10 @@ import { CopyToClipboardButton } from '../../../common/copyable/copy-to-clipboar
|
||||||
import styles from './highlighted-code.module.scss'
|
import styles from './highlighted-code.module.scss'
|
||||||
import { cypressAttribute, cypressId } from '../../../../utils/cypress-attribute'
|
import { cypressAttribute, cypressId } from '../../../../utils/cypress-attribute'
|
||||||
import { AsyncLoadingBoundary } from '../../../common/async-loading-boundary'
|
import { AsyncLoadingBoundary } from '../../../common/async-loading-boundary'
|
||||||
import { useAsyncHighlightedCodeDom } from './hooks/use-async-highlighted-code-dom'
|
import { useAsyncHighlightJsImport } from './hooks/use-async-highlight-js-import'
|
||||||
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'
|
import { useAttachLineNumbers } from './hooks/use-attach-line-numbers'
|
||||||
import { testId } from '../../../../utils/test-id'
|
import { testId } from '../../../../utils/test-id'
|
||||||
|
import { useCodeDom } from './hooks/use-code-dom'
|
||||||
|
|
||||||
export interface HighlightedCodeProps {
|
export interface HighlightedCodeProps {
|
||||||
code: string
|
code: string
|
||||||
|
@ -30,8 +31,9 @@ export interface HighlightedCodeProps {
|
||||||
*/
|
*/
|
||||||
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, startLineNumber, wrapLines }) => {
|
export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language, startLineNumber, wrapLines }) => {
|
||||||
const showGutter = startLineNumber !== undefined
|
const showGutter = startLineNumber !== undefined
|
||||||
const { loading, error, value: highlightedLines } = useAsyncHighlightedCodeDom(code, language)
|
const { value: hljsApi, loading, error } = useAsyncHighlightJsImport()
|
||||||
const wrappedDomLines = useAttachLineNumbers(highlightedLines, startLineNumber)
|
const codeDom = useCodeDom(code, hljsApi, language)
|
||||||
|
const wrappedDomLines = useAttachLineNumbers(codeDom, startLineNumber)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncLoadingBoundary loading={loading} error={!!error} componentName={'highlight.js'}>
|
<AsyncLoadingBoundary loading={loading} error={!!error} componentName={'highlight.js'}>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useAsync } from 'react-use'
|
||||||
|
import { Logger } from '../../../../../utils/logger'
|
||||||
|
import type { AsyncState } from 'react-use/lib/useAsyncFn'
|
||||||
|
import type { HLJSApi } from 'highlight.js'
|
||||||
|
|
||||||
|
const log = new Logger('HighlightedCode')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy loads the highlight js library.
|
||||||
|
*
|
||||||
|
* @return the loaded js lib
|
||||||
|
*/
|
||||||
|
export const useAsyncHighlightJsImport = (): AsyncState<HLJSApi> => {
|
||||||
|
return useAsync(async () => {
|
||||||
|
try {
|
||||||
|
return (await import(/* webpackChunkName: "highlight.js" */ '../../../../common/hljs/hljs')).default
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Error while loading highlight.js', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
}
|
|
@ -5,12 +5,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import React, { Fragment } from 'react'
|
import React, { Fragment, useMemo } from 'react'
|
||||||
import { MarkdownExtensionCollection } from '../../markdown-extension-collection'
|
import { MarkdownExtensionCollection } from '../../markdown-extension-collection'
|
||||||
import convertHtmlToReact from '@hedgedoc/html-to-react'
|
import convertHtmlToReact from '@hedgedoc/html-to-react'
|
||||||
import { useAsync } from 'react-use'
|
import type { HLJSApi } from 'highlight.js'
|
||||||
import { Logger } from '../../../../../utils/logger'
|
|
||||||
import type { AsyncState } from 'react-use/lib/useAsyncFn'
|
/**
|
||||||
|
* Highlights the given code using highlight.js. If the language wasn't recognized then it won't be highlighted.
|
||||||
|
*
|
||||||
|
* @param code The code to highlight
|
||||||
|
* @param hljs The highlight.js API. Needs to be imported or lazy loaded.
|
||||||
|
* @param language The language of the code to use for highlighting
|
||||||
|
* @return The react elements that represent the highlighted code
|
||||||
|
*/
|
||||||
|
export const useCodeDom = (code: string, hljs: HLJSApi | undefined, language?: string): ReactElement[] | undefined => {
|
||||||
|
return useMemo(() => {
|
||||||
|
if (!hljs) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!!language && hljs.listLanguages().includes(language)) {
|
||||||
|
const highlightedHtml = hljs.highlight(code, { language }).value
|
||||||
|
return createHtmlLinesToReactDOM(omitNewLineAtEnd(highlightedHtml).split('\n'))
|
||||||
|
} else {
|
||||||
|
return createPlaintextToReactDOM(code)
|
||||||
|
}
|
||||||
|
}, [code, hljs, language])
|
||||||
|
}
|
||||||
|
|
||||||
const nodeProcessor = new MarkdownExtensionCollection([]).buildFlatNodeProcessor()
|
const nodeProcessor = new MarkdownExtensionCollection([]).buildFlatNodeProcessor()
|
||||||
|
|
||||||
|
@ -40,38 +60,6 @@ const createPlaintextToReactDOM = (text: string): ReactElement[] => {
|
||||||
return text.split('\n').map((line, lineIndex) => React.createElement('span', { key: lineIndex }, line))
|
return text.split('\n').map((line, lineIndex) => React.createElement('span', { key: lineIndex }, line))
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HighlightedCodeProps {
|
|
||||||
code: string
|
|
||||||
language?: string
|
|
||||||
startLineNumber?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = new Logger('HighlightedCode')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlights the given code using highlight.js. If the language wasn't recognized then it won't be highlighted.
|
|
||||||
*
|
|
||||||
* @param code The code to highlight
|
|
||||||
* @param language The language of the code to use for highlighting
|
|
||||||
* @return {@link AsyncState async state} that contains the converted React elements
|
|
||||||
*/
|
|
||||||
export const useAsyncHighlightedCodeDom = (code: string, language?: string): AsyncState<ReactElement[]> => {
|
|
||||||
return useAsync(async () => {
|
|
||||||
try {
|
|
||||||
const hljs = (await import(/* webpackChunkName: "highlight.js" */ '../../../../common/hljs/hljs')).default
|
|
||||||
if (!!language && hljs.listLanguages().includes(language)) {
|
|
||||||
const highlightedHtml = hljs.highlight(code, { language }).value
|
|
||||||
return createHtmlLinesToReactDOM(omitNewLineAtEnd(highlightedHtml).split('\n'))
|
|
||||||
} else {
|
|
||||||
return createPlaintextToReactDOM(code)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log.error('Error while loading highlight.js', error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}, [code, language])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the given code but without the last new line if the string ends with a new line.
|
* Returns the given code but without the last new line if the string ends with a new line.
|
||||||
*
|
*
|
Loading…
Add table
Add a link
Reference in a new issue