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,13 +37,10 @@ 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
const highlightedTokens: {
start: number
end: number
highlight: Highlight
}[] = []
// collect highlighted tokens, if there are any
const highlightedTokens: HighlightedTokens[] = useMemo(() => {
const highlightedTokensArr: HighlightedTokens[] = []
highlights.forEach((highlight) => { highlights.forEach((highlight) => {
if (!highlight.text) { if (!highlight.text) {
return return
@@ -45,28 +54,37 @@ export const CodeBlockLine = ({
currentPositionInHighlightedText = 0 currentPositionInHighlightedText = 0
return false return false
} }
const startNotSet = startIndex === undefined
// 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( const comparisonLength = Math.min(
token.content.length, tokenContent.length,
highlight.text!.substring(currentPositionInHighlightedText).length highlight.text!.substring(currentPositionInHighlightedText).length
) )
const nextPositionInHighlightedText = const nextPositionInHighlightedText =
currentPositionInHighlightedText + comparisonLength currentPositionInHighlightedText + comparisonLength
const canHighlight = const canHighlight =
!highlightedTokens.length || !highlightedTokensArr.length ||
!highlightedTokens.some( !highlightedTokensArr.some(
(token) => tokenIndex >= token.start && tokenIndex <= token.end (token) => tokenIndex >= token.start && tokenIndex <= token.end
) )
if ( const isMatching =
token.content.substring(0, comparisonLength) === tokenContent.substring(0, comparisonLength) ===
highlight.text?.substring( highlight.text?.substring(
currentPositionInHighlightedText, currentPositionInHighlightedText,
nextPositionInHighlightedText nextPositionInHighlightedText
) && )
canHighlight
) { if (isMatching && canHighlight) {
if (startIndex === undefined) { if (startNotSet) {
startIndex = tokenIndex startIndex = tokenIndex
} }
currentPositionInHighlightedText = nextPositionInHighlightedText currentPositionInHighlightedText = nextPositionInHighlightedText
@@ -80,7 +98,7 @@ export const CodeBlockLine = ({
}) })
if (found && startIndex !== undefined) { if (found && startIndex !== undefined) {
highlightedTokens.push({ highlightedTokensArr.push({
start: startIndex, start: startIndex,
end: endIndex, end: endIndex,
highlight, highlight,
@@ -89,7 +107,7 @@ export const CodeBlockLine = ({
}) })
// sort highlighted tokens by their start position // sort highlighted tokens by their start position
highlightedTokens.sort((tokensA, tokensB) => { highlightedTokensArr.sort((tokensA, tokensB) => {
if (tokensA.start < tokensB.start) { if (tokensA.start < tokensB.start) {
return -1 return -1
} }
@@ -97,37 +115,61 @@ export const CodeBlockLine = ({
return tokensA.start > tokensB.start ? 1 : 0 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",
}, },
] ]
}
const transformedLineArr: TokensWithHighlights[] = []
let lastIndex = 0 let lastIndex = 0
// go through highlighted tokens to add the segments before/after to the // go through highlighted tokens to add the segments before/after to the
// transformedLines array // transformLineArr array
highlightedTokens.forEach((highlightedTokensItem, index) => { highlightedTokens.forEach((highlightedTokensItem, index) => {
if (lastIndex < highlightedTokensItem.start) { if (lastIndex < highlightedTokensItem.start) {
transformedLine.push({ transformedLineArr.push({
tokens: line.slice(lastIndex, highlightedTokensItem.start), tokens: line.slice(lastIndex, highlightedTokensItem.start),
type: "default", type: "default",
}) })
} }
transformedLine.push({ // check if the start text should be trimmed
tokens: line.slice( const token = Object.assign({}, line[highlightedTokensItem.start])
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 highlightedTokensItem.end + 1
), ),
],
type: "highlighted", type: "highlighted",
highlight: highlightedTokensItem.highlight, highlight: highlightedTokensItem.highlight,
}) })
@@ -136,15 +178,21 @@ export const CodeBlockLine = ({
// if this is the last item in `highlightedTokens` and // if this is the last item in `highlightedTokens` and
// its end index is less than the line's length, that means // 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 // there are tokens at the end of the line that aren't highlighted
// and should be pushed as-is to the `transformedLines` array. // and should be pushed as-is to the `transformLineArr` array.
if (index === highlightedTokens.length - 1 && lastIndex < line.length - 1) { if (
transformedLine.push({ index === highlightedTokens.length - 1 &&
lastIndex < line.length - 1
) {
transformedLineArr.push({
tokens: line.slice(lastIndex), tokens: line.slice(lastIndex),
type: "default", type: "default",
}) })
} }
}) })
return transformedLineArr
}, [highlightedTokens])
const getTokensElm = ({ const getTokensElm = ({
tokens, tokens,
isHighlighted, isHighlighted,