docs: fix code block highlighting for lines starting with spaces (#7356)
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user