docs: fix code block highlighting for lines starting with spaces (#7356)

This commit is contained in:
Shahed Nasser
2024-05-20 09:49:52 +03:00
committed by GitHub
parent 48aa09be23
commit 5d4bf83145
2 changed files with 144 additions and 96 deletions
@@ -15,8 +15,8 @@ The object that the subscriber function receives as a parameter has a `data` pro
For example: For example:
export const highlights = [ export const highlights = [
["8", "", "The event's data payload."], ["7", "", "The event's data payload."],
["9", "{ id: string }", "The type of expected data payloads."] ["8", "{ id: string }", "The type of expected data payloads."]
] ]
```ts title="src/subscribers/product-created.ts" highlights={highlights} ```ts title="src/subscribers/product-created.ts" highlights={highlights}
@@ -4,6 +4,18 @@ import { RenderProps, Token } from "prism-react-renderer"
import clsx from "clsx" import clsx from "clsx"
import { MarkdownContent, Tooltip } from "@/components" import { MarkdownContent, Tooltip } from "@/components"
type HighlightedTokens = {
start: number
end: number
highlight: Highlight
}
type TokensWithHighlights = {
tokens: Token[]
type: "default" | "highlighted"
highlight?: Highlight
}
type CodeBlockLineProps = { type CodeBlockLineProps = {
line: Token[] line: Token[]
highlights?: Highlight[] highlights?: Highlight[]
@@ -25,125 +37,161 @@ export const CodeBlockLine = ({
lineNumberColorClassName, lineNumberColorClassName,
}: CodeBlockLineProps) => { }: CodeBlockLineProps) => {
const lineProps = getLineProps({ line, key: lineNumber }) const lineProps = getLineProps({ line, key: lineNumber })
// collect highlighted tokens, if there are any // collect highlighted tokens, if there are any
const highlightedTokens: { const highlightedTokens: HighlightedTokens[] = useMemo(() => {
start: number const highlightedTokensArr: HighlightedTokens[] = []
end: number highlights.forEach((highlight) => {
highlight: Highlight if (!highlight.text) {
}[] = [] return
highlights.forEach((highlight) => {
if (!highlight.text) {
return
}
let startIndex: number | undefined = undefined
let currentPositionInHighlightedText = 0
let endIndex = 0
const found = line.some((token, tokenIndex) => {
if (token.empty || !token.content.length) {
startIndex = undefined
currentPositionInHighlightedText = 0
return false
} }
const comparisonLength = Math.min( let startIndex: number | undefined = undefined
token.content.length, let currentPositionInHighlightedText = 0
highlight.text!.substring(currentPositionInHighlightedText).length let endIndex = 0
) const found = line.some((token, tokenIndex) => {
const nextPositionInHighlightedText = if (token.empty || !token.content.length) {
currentPositionInHighlightedText + comparisonLength startIndex = undefined
currentPositionInHighlightedText = 0
const canHighlight = return false
!highlightedTokens.length || }
!highlightedTokens.some( const startNotSet = startIndex === undefined
(token) => tokenIndex >= token.start && tokenIndex <= token.end // trim the start of the script if the start
// of the highlight hasn't been found yet
const tokenContent = startNotSet
? token.content.trimStart()
: token.content
if (!tokenContent.length && startNotSet) {
return false
}
const comparisonLength = Math.min(
tokenContent.length,
highlight.text!.substring(currentPositionInHighlightedText).length
) )
const nextPositionInHighlightedText =
currentPositionInHighlightedText + comparisonLength
if ( const canHighlight =
token.content.substring(0, comparisonLength) === !highlightedTokensArr.length ||
!highlightedTokensArr.some(
(token) => tokenIndex >= token.start && tokenIndex <= token.end
)
const isMatching =
tokenContent.substring(0, comparisonLength) ===
highlight.text?.substring( highlight.text?.substring(
currentPositionInHighlightedText, currentPositionInHighlightedText,
nextPositionInHighlightedText nextPositionInHighlightedText
) && )
canHighlight
) {
if (startIndex === undefined) {
startIndex = tokenIndex
}
currentPositionInHighlightedText = nextPositionInHighlightedText
}
if (currentPositionInHighlightedText === highlight.text!.length) { if (isMatching && canHighlight) {
// matching text was found, break loop if (startNotSet) {
endIndex = tokenIndex startIndex = tokenIndex
return true }
currentPositionInHighlightedText = nextPositionInHighlightedText
}
if (currentPositionInHighlightedText === highlight.text!.length) {
// matching text was found, break loop
endIndex = tokenIndex
return true
}
})
if (found && startIndex !== undefined) {
highlightedTokensArr.push({
start: startIndex,
end: endIndex,
highlight,
})
} }
}) })
if (found && startIndex !== undefined) { // sort highlighted tokens by their start position
highlightedTokens.push({ highlightedTokensArr.sort((tokensA, tokensB) => {
start: startIndex, if (tokensA.start < tokensB.start) {
end: endIndex, return -1
highlight, }
})
}
})
// sort highlighted tokens by their start position return tokensA.start > tokensB.start ? 1 : 0
highlightedTokens.sort((tokensA, tokensB) => { })
if (tokensA.start < tokensB.start) {
return -1
}
return tokensA.start > tokensB.start ? 1 : 0 return highlightedTokensArr
}) }, [highlights, line])
// if there are highlighted tokens, split tokens in the // if there are highlighted tokens, split tokens in the
// line by segments of not highlighted and highlighted token // line by segments of not highlighted and highlighted token
// if there are no highlighted tokens, the line is used as-is. // if there are no highlighted tokens, the line is used as-is.
const transformedLine: { const transformedLine: TokensWithHighlights[] = useMemo(() => {
tokens: Token[] if (!highlightedTokens.length) {
type: "default" | "highlighted" return [
highlight?: Highlight
}[] = highlightedTokens.length
? []
: [
{ {
tokens: line, tokens: line,
type: "default", type: "default",
}, },
] ]
let lastIndex = 0
// go through highlighted tokens to add the segments before/after to the
// transformedLines array
highlightedTokens.forEach((highlightedTokensItem, index) => {
if (lastIndex < highlightedTokensItem.start) {
transformedLine.push({
tokens: line.slice(lastIndex, highlightedTokensItem.start),
type: "default",
})
} }
transformedLine.push({ const transformedLineArr: TokensWithHighlights[] = []
tokens: line.slice(
highlightedTokensItem.start, let lastIndex = 0
highlightedTokensItem.end + 1 // go through highlighted tokens to add the segments before/after to the
), // transformLineArr array
type: "highlighted", highlightedTokens.forEach((highlightedTokensItem, index) => {
highlight: highlightedTokensItem.highlight, if (lastIndex < highlightedTokensItem.start) {
transformedLineArr.push({
tokens: line.slice(lastIndex, highlightedTokensItem.start),
type: "default",
})
}
// check if the start text should be trimmed
const token = Object.assign({}, line[highlightedTokensItem.start])
if (
token.content.startsWith(" ") &&
!highlightedTokensItem.highlight.text?.startsWith(" ")
) {
const originalLength = token.content.length
token.content = token.content.trimStart()
// push the spaces as a separate token
// so that they won't be highlighted.
transformedLineArr.push({
tokens: [
{
content: " ".repeat(originalLength - token.content.length),
types: ["plain"],
},
],
type: "default",
})
}
transformedLineArr.push({
tokens: [
token,
...line.slice(
highlightedTokensItem.start + 1,
highlightedTokensItem.end + 1
),
],
type: "highlighted",
highlight: highlightedTokensItem.highlight,
})
lastIndex = highlightedTokensItem.end + 1
// if this is the last item in `highlightedTokens` and
// its end index is less than the line's length, that means
// there are tokens at the end of the line that aren't highlighted
// and should be pushed as-is to the `transformLineArr` array.
if (
index === highlightedTokens.length - 1 &&
lastIndex < line.length - 1
) {
transformedLineArr.push({
tokens: line.slice(lastIndex),
type: "default",
})
}
}) })
lastIndex = highlightedTokensItem.end + 1
// if this is the last item in `highlightedTokens` and return transformedLineArr
// its end index is less than the line's length, that means }, [highlightedTokens])
// there are tokens at the end of the line that aren't highlighted
// and should be pushed as-is to the `transformedLines` array.
if (index === highlightedTokens.length - 1 && lastIndex < line.length - 1) {
transformedLine.push({
tokens: line.slice(lastIndex),
type: "default",
})
}
})
const getTokensElm = ({ const getTokensElm = ({
tokens, tokens,