Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions docs/content/docs/features/blocks/code-blocks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,28 @@ type CodeBlockOptions = {

**Syntax Highlighting**

Syntax highlighting is handled by a separate editor extension, configured at the editor level via the `syntaxHighlighting` option (not on the code block itself), so it can highlight any block's content:
Syntax highlighting is handled by a separate editor extension that you add to the editor's `extensions` (not configured on the code block itself), so it can highlight any block that declares a language — the code block, and blocks like the math block. When the extension isn't added, those blocks render as plain text.

The extension is configured with a Shiki highlighter:

```ts
type SyntaxHighlightingOptions = {
createHighlighter?: () => Promise<HighlighterGeneric<any, any>>;
highlightBlock?: (block: {
type: string;
props: Record<string, any>;
}) => string | undefined;
createHighlighter: () => Promise<HighlighterGeneric<any, any>>;
};
```

`createHighlighter:` The [Shiki highlighter](https://shiki.style/guide/load-theme) to use for syntax highlighting.

`highlightBlock:` Picks the language to highlight a block's content as (return the language key, or `undefined` to leave it un-highlighted). This is how you enable highlighting for specific blocks. Defaults to the block's `language` prop (`(block) => block.props.language`), which covers the code block. For a block with a fixed language, return it directly — e.g. for a math block: `(block) => (block.type === "math" ? "latex" : block.props.language)`.
Which blocks get highlighted (and as which language) is decided by each block's spec via its `meta.highlight` callback — the code block highlights as its `language` prop, the math block always as `latex` — so you don't configure this on the extension.

BlockNote provides a generic, ready-to-use set of these in the `@blocknote/code-block` package, which supports a wide range of languages. The code block options and the highlighter are exported separately:
BlockNote provides a generic, ready-to-use highlighter in the `@blocknote/code-block` package, which supports a wide range of languages. It's exported as a pre-configured `syntaxHighlighter` extension, alongside the code block options:

```ts
import { createCodeBlockSpec } from "@blocknote/core";
import { codeBlockOptions, createHighlighter } from "@blocknote/code-block";
import { codeBlockOptions, syntaxHighlighter } from "@blocknote/code-block";

const editor = useCreateBlockNote({
syntaxHighlighting: { createHighlighter },
extensions: [syntaxHighlighter],
schema: BlockNoteSchema.create().extend({
blockSpecs: {
codeBlock: createCodeBlockSpec(codeBlockOptions),
Expand Down Expand Up @@ -110,19 +108,22 @@ This will generate a `shiki.bundle.ts` file that you can use to create a syntax
Like this:

```ts
import { SyntaxHighlightingExtension } from "@blocknote/core";
import { createHighlighter } from "./shiki.bundle.js";

// Build a syntax highlighter extension from your custom Shiki bundle, then add
// it to the editor's `extensions`.
const syntaxHighlighter = SyntaxHighlightingExtension({
createHighlighter: () =>
createHighlighter({
themes: ["light-plus", "dark-plus"],
langs: [],
}),
});

export default function App() {
const editor = useCreateBlockNote({
// The highlighter is configured at the editor level, separately from the
// code block's own options.
syntaxHighlighting: {
createHighlighter: () =>
createHighlighter({
themes: ["light-plus", "dark-plus"],
langs: [],
}),
},
extensions: [syntaxHighlighter],
schema: BlockNoteSchema.create().extend({
blockSpecs: {
codeBlock: createCodeBlockSpec({
Expand Down
11 changes: 6 additions & 5 deletions examples/04-theming/06-code-block/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
// This packages some of the most used languages in on-demand bundle
import { codeBlockOptions, createHighlighter } from "@blocknote/code-block";
// This packages some of the most used languages in on-demand bundle, and a
// ready-to-use syntax highlighter extension configured with them.
import { codeBlockOptions, syntaxHighlighter } from "@blocknote/code-block";

export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
// The Shiki highlighter is configured at the editor level, separately from
// the code block's own options (default language & language menu).
syntaxHighlighting: { createHighlighter },
// Adding the syntax highlighter extension enables syntax highlighting for
// the code block. Without it, code renders as plain text.
extensions: [syntaxHighlighter],
schema: BlockNoteSchema.create().extend({
blockSpecs: {
codeBlock: createCodeBlockSpec(codeBlockOptions),
Expand Down
29 changes: 18 additions & 11 deletions examples/04-theming/07-custom-code-block/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { BlockNoteSchema, createCodeBlockSpec } from "@blocknote/core";
import {
BlockNoteSchema,
createCodeBlockSpec,
SyntaxHighlightingExtension,
} from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
// Bundle created from `npx shiki-codegen --langs typescript,javascript,react --themes light-plus,dark-plus --engine javascript --precompiled ./shiki.bundle.ts`
import { createHighlighter } from "./shiki.bundle";

// Syntax highlighting is a separate extension, configured with a highlighter.
// Here we build one from our own custom Shiki bundle (with `dark-plus` /
// `light-plus` themes) and pass it to the editor's `extensions` below.
const syntaxHighlighter = SyntaxHighlightingExtension({
// This creates a highlighter, it can be asynchronous to load it afterwards
createHighlighter: () =>
createHighlighter({
themes: ["dark-plus", "light-plus"],
langs: [],
}),
});

export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
// The Shiki highlighter is configured at the editor level, separately from
// the code block's own options (default language & language menu).
syntaxHighlighting: {
// This creates a highlighter, it can be asynchronous to load it afterwards
createHighlighter: () =>
createHighlighter({
themes: ["dark-plus", "light-plus"],
langs: [],
}),
},
extensions: [syntaxHighlighter],
schema: BlockNoteSchema.create().extend({
blockSpecs: {
codeBlock: createCodeBlockSpec({
Expand Down
13 changes: 5 additions & 8 deletions examples/06-custom-schema/09-math-block/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
insertOrUpdateBlockForSlashMenu,
} from "@blocknote/core/extensions";
import { TextSelection } from "prosemirror-state";
import { createHighlighter } from "@blocknote/code-block";
import { syntaxHighlighter } from "@blocknote/code-block";
import {
createReactInlineMathSpec,
createReactMathBlockSpec,
Expand Down Expand Up @@ -85,13 +85,10 @@ const insertInlineMath = (editor: typeof schema.BlockNoteEditor) => ({

export default function App() {
const editor = useCreateBlockNote({
// Configures the syntax highlighting extension to always use LaTeX syntax highlighting in the
// Math block.
syntaxHighlighting: {
createHighlighter,
highlightBlock: (block) =>
block.type === "math" ? "latex" : block.props.language,
},
// The syntax highlighter extension highlights the LaTeX source of math
// blocks (they declare `highlight: () => "latex"`). Without it, they render
// as plain text.
extensions: [syntaxHighlighter],
schema,
initialContent: [
{
Expand Down
12 changes: 4 additions & 8 deletions examples/06-custom-schema/10-diagram-block/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createHighlighter } from "@blocknote/code-block";
import { syntaxHighlighter } from "@blocknote/code-block";
import { BlockNoteSchema } from "@blocknote/core";
import {
filterSuggestionItems,
Expand Down Expand Up @@ -42,13 +42,9 @@ const insertDiagram = (editor: typeof schema.BlockNoteEditor) => ({

export default function App() {
const editor = useCreateBlockNote({
// Configures the syntax highlighting extension to use Mermaid syntax
// highlighting in the Diagram block's source popup.
syntaxHighlighting: {
createHighlighter,
highlightBlock: (block) =>
block.type === "diagram" ? "mermaid" : block.props.language,
},
// The syntax highlighter extension highlights the Diagram block's Mermaid
// source in its popup (the block declares `highlight: () => "mermaid"`).
extensions: [syntaxHighlighter],
schema,
initialContent: [
{
Expand Down
9 changes: 6 additions & 3 deletions packages/code-block/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vite-plus/test";
import { codeBlockOptions, createHighlighter } from "./index.js";
import { codeBlockOptions, syntaxHighlighter } from "./index.js";

describe("codeBlock", () => {
it("should exist", () => {
Expand All @@ -11,7 +11,10 @@ describe("codeBlock", () => {
it("should have supportedLanguages", () => {
expect(codeBlockOptions.supportedLanguages).toBeDefined();
});
it("exports a separate createHighlighter", () => {
expect(createHighlighter).toBeDefined();
it("should not configure a highlighter (that's now the syntaxHighlighter extension)", () => {
expect("createHighlighter" in codeBlockOptions).toBe(false);
});
it("should export a pre-configured syntaxHighlighter extension", () => {
expect(syntaxHighlighter).toBeDefined();
});
});
28 changes: 21 additions & 7 deletions packages/code-block/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import type { CodeBlockOptions } from "@blocknote/core";
import { createHighlighter as createShikiHighlighter } from "./shiki.bundle.js";
import { SyntaxHighlightingExtension } from "@blocknote/core";
import { createHighlighter } from "./shiki.bundle.js";

export const createHighlighter = () =>
createShikiHighlighter({
themes: ["github-dark", "github-light"],
langs: [],
});
/**
* A ready-to-use syntax highlighting extension, pre-configured with this
* package's bundled Shiki highlighter (the languages in `codeBlockOptions` and
* the `github-dark` / `github-light` themes). Add it to the editor's
* `extensions` to enable syntax highlighting for code blocks (and any other
* block that declares a language, such as the math block):
*
* @example
* ```ts
* useCreateBlockNote({ extensions: [syntaxHighlighter] });
* ```
*/
export const syntaxHighlighter = SyntaxHighlightingExtension({
createHighlighter: () =>
createHighlighter({
themes: ["github-dark", "github-light"],
langs: [],
}),
});

// TODO: Should this be here or in the core code block?
export const codeBlockOptions = {
defaultLanguage: "javascript",
supportedLanguages: {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"emoji-mart": "^5.6.0",
"fast-deep-equal": "^3.1.3",
"lib0": "^0.2.99",
"prosemirror-highlight": "^0.15.1",
"prosemirror-highlight": "^0.15.3",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured out why math inline nodes weren't being highlighted & made a PR upstream to fix it & this is the result of that: ocavue/prosemirror-highlight#137

"prosemirror-model": "^1.25.4",
"prosemirror-state": "^1.4.4",
"prosemirror-tables": "^1.8.3",
Expand Down
15 changes: 7 additions & 8 deletions packages/core/src/blocks/Code/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const createCodeBlockSpec = createBlockSpec(
code: true,
defining: true,
isolating: false,
highlight: (block) => block.props.language,
},
parse: (el) => parsePreCode(el),
parseContent: (opts) => parsePreCodeContent(opts, "codeBlock"),
Expand All @@ -46,12 +47,10 @@ export const createCodeBlockSpec = createBlockSpec(
),
toExternalHTML: (block) => createPreCode(block),
}),
(options) => {
return [
CodeKeyboardShortcutsExtension(options)(
CODE_BLOCK_KEYBOARD_SHORTCUTS_KEY,
"codeBlock",
),
];
},
(options) => [
CodeKeyboardShortcutsExtension(options)(
CODE_BLOCK_KEYBOARD_SHORTCUTS_KEY,
"codeBlock",
),
],
);
8 changes: 0 additions & 8 deletions packages/core/src/editor/BlockNoteEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
DefaultStyleSchema,
PartialBlock,
} from "../blocks/index.js";
import type { SyntaxHighlightingOptions } from "../extensions/SyntaxHighlighting/SyntaxHighlighting.js";
import {
BlockChangeExtension,
DropCursorOptions,
Expand Down Expand Up @@ -254,13 +253,6 @@ export interface BlockNoteEditorOptions<
*/
setIdAttribute?: boolean;

/**
* Options for syntax highlighting block content: the Shiki highlighter to use,
* and a `highlightBlock` function picking which blocks to highlight and as
* which language.
*/
syntaxHighlighting?: SyntaxHighlightingOptions;

/**
* Determines behavior when pressing Tab (or Shift-Tab) while multiple blocks are selected and a toolbar is open.
* - `"prefer-navigate-ui"`: Changes focus to the toolbar. User must press Escape to close toolbar before indenting blocks. Better for keyboard accessibility.
Expand Down
Loading
Loading