代码注释
您可以通过使用注释处理器来扩展 Pre 组件渲染代码的方式。
例如,这里我们定义了两个组件:一个为代码块添加红色边框,另一个添加深色背景。
import type { AnnotationHandler, RawCode } from "codehike/code"import { Pre, highlight } from "codehike/code"export async function MyCode({ codeblock: RawCode }) {const highlighted = await highlight(codeblock, "github-dark")return <Pre code={highlighted} handlers={[borderHandler, bgHandler]} />}const borderHandler: AnnotationHandler = {name: "border",Block: ({ annotation, children }) => (<div style={{ border: "1px solid red" }}>{children}</div>),}const bgHandler: AnnotationHandler = {name: "bg",Inline: ({ annotation, children }) => (<span style={{ background: "#2d26" }}>{children}</span>),}
我们可以在代码中使用这些处理器的 name 作为注释来使用组件:
```js// !border(1:2)const lorem = ipsum == null ? 0 : 1dolor = lorem - sit(dolor)// !bg[5:16]let amet = lorem ? consectetur(ipsum) : 3```
const lorem = ipsum == null ? 0 : 1dolor = lorem - sit(dolor)let amet = lorem ? consectetur(ipsum) : 3
注释语法
我们使用注释来标注代码块。注释语法取决于语言。例如,在 JavaScript 中我们使用 // !name(1:5),但在 Python 中我们使用 # !name(1:5)。对于 JSON(不支持注释),建议使用支持注释的 jsonc 或 json5。
在前面的示例中,我们可以看到两种类型的注释:
- 块注释应用于一块行。它们使用圆括号
()来定义行的范围。数字是相对于放置注释的行。 - 内联注释应用于行内的一组标记。它们使用方括号
[]来定义注释中包含的列范围。
注释查询
注释中的任何额外内容都会作为 query 传递给注释组件。
例如,我们可以修改前面示例中的组件,使用查询来定义边框和背景的颜色:
const borderHandler: AnnotationHandler = {name: "border",Block: ({ annotation, children }) => {const borderColor = annotation.query || "red"return <div style={{ border: "1px solid", borderColor }}>{children}</div>},}const bgHandler: AnnotationHandler = {name: "bg",Inline: ({ annotation, children }) => {const background = annotation.query || "#2d26"return <span style={{ background }}>{children}</span>},}
```js// !border(1:2) purpleconst lorem = ipsum == null ? 0 : 1dolor = lorem - sit(dolor)// !bg[5:16] darkbluelet amet = lorem ? consectetur(ipsum) : 3```
const lorem = ipsum == null ? 0 : 1dolor = lorem - sit(dolor)let amet = lorem ? consectetur(ipsum) : 3
自定义行和标记组件
有时您想要自定义每一行或标记的渲染,而不仅仅是被注释的那些。您可以通过定义 Line 和 Token 组件来实现:
import { InnerLine } from "codehike/code"const myHandler: AnnotationHandler = {name: "uglyLineNumbers",Line: (props) => {const { lineNumber, totalLines, indentation } = propsreturn (<div>{lineNumber} |<InnerLine merge={props} className="inline-block" /></div>)},}
什么是 InnerLine?由于同一行可能被多个注释处理器针对,我们需要使组件可组合。因此 InnerLine 会链接并合并来自不同处理器的所有属性。
例如,如果我们有这两个处理器:
const bgHandler: AnnotationHandler = {Line: (props) => (<InnerLinemerge={props}className="bg-red-200"data-line={props.lineNumber}/>),}const paddingHandler: AnnotationHandler = {Line: (props) => <InnerLine merge={props} className="px-2" />,}
最终渲染结果将是:
<pre><div className="bg-red-200 px-2" data-line="1">...</div><div className="bg-red-200 px-2" data-line="2">...</div><div className="bg-red-200 px-2" data-line="3">...</div></pre>
同样,您可以自定义每个标记的渲染,但这种情况要少见得多:
import { InnerToken } from "codehike/code"const myHandler: AnnotationHandler = {Token: (props) => {const { value, style, lineNumber } = propsreturn <InnerToken merge={props} style={{ display: "inline-block" }} />},}
自定义 Pre 组件
您还可以自定义 <pre> 元素本身的渲染:
import { InnerPre, getPreRef } from "codehike/code"const myHandler: AnnotationHandler = {Pre: (props) => (<InnerPre merge={props} className="rounded border border-blue-100" />),// If you need the ref to the pre element, use PreWithRef:PreWithRef: (props) => {const ref = getPreRef(props)doSomethingWithRef(ref)return <InnerPre merge={props} />},}
AnnotatedLine 和 AnnotatedToken
与 Line 和 Token 类似,您可以定义 AnnotatedLine 和 AnnotatedToken 来自定义作为注释一部分的单个行和标记的渲染。
有时将 Line 和 AnnotatedLine 组件结合使用以避免重复相同的代码是很有用的。
例如,这里我们为 mark 注释内的行添加 data-mark 属性,然后我们有一个添加边框的 Line 组件,但我们只为具有 data-mark 属性的行添加边框颜色:
const mark: AnnotationHandler = {name: "mark",AnnotatedLine: ({ annotation, ...props }) => (<InnerLine merge={props} data-mark={true} />),Line: (props) => (<InnerLinemerge={props}className="px-2 border-l-4 border-transparent data-[mark]:border-blue-400"/>),}
```jsconst lorem = ipsum == null ? 0 : 1// !mark(1:2)dolor = lorem - sit(dolor)let amet = lorem ? consectetur(ipsum) : 3```
const lorem = ipsum == null ? 0 : 1dolor = lorem - sit(dolor)let amet = lorem ? consectetur(ipsum) : 3
转换注释
您还可以在注释传递给组件之前对其进行转换。这对以下情况很有用:
const myHandler: AnnotationHandler = {transform: (annotation: InlineAnnotation) => {return {...annotation,data: { lorem: "ipsum" },}},...}
使用正则表达式而不是范围
您可以使用正则表达式来匹配注释的内容,而不是使用行和列范围。
您可以将其用于块注释,但更常见的是用于内联注释:
```js// !border[/ipsum/] yellowconst lorem = ipsum == null ? ipsum : 1// !border[/dolor/g] limedolor = lorem - sit(dolor)let amet = dolor ? consectetur(ipsum) : 3``````js// !border[/ipsum/gm] orangeconst lorem = ipsum == null ? ipsum : 1dolor = lorem - sit(dolor)let amet = dolor ? consectetur(ipsum) : 3```
const lorem = ipsum == null ? ipsum : 1dolor = lorem - sit(dolor)let amet = dolor ? consectetur(ipsum) : 3
const lorem = ipsum == null ? ipsum : 1dolor = lorem - sit(dolor)let amet = dolor ? consectetur(ipsum) : 3
正则表达式还支持标志。最常见的两个是 g(全局)和 m(多行)。
m:如果您想要在第一行之后继续搜索,请使用它g:如果您想要匹配多个出现,请使用它
```jsx// !border[/className="(.*?)"/gm] pinkfunction Foo() {return (<div className="bg-red-200 opacity-50"><span className="border">hey</span></div>)}```
function Foo() {return (<div className="bg-red-200 opacity-50"><span className="border">hey</span></div>)}