Skip to content

Commit

Permalink
editor: fix code block highlighting not updating in some cases (#2267)
Browse files Browse the repository at this point in the history
Signed-off-by: Abdulrehman-Jafer <abdulrehmanjaferwork01233@gmail.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
  • Loading branch information
Abdulrehman-Jafer and thecodrr committed Apr 15, 2023
1 parent 26657c7 commit 0f191eb
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 13 deletions.
25 changes: 18 additions & 7 deletions packages/editor/src/extensions/code-block/code-block.ts
Expand Up @@ -127,7 +127,7 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
id: {
default: undefined,
rendered: false,
parseHTML: () => `codeblock-${nanoid(12)}`
parseHTML: () => createCodeblockId()
},
caretPosition: {
default: undefined,
Expand Down Expand Up @@ -227,13 +227,18 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
setCodeBlock:
(attributes) =>
({ commands }) => {
return commands.setNode(this.name, attributes);
return commands.setNode(this.name, {
...attributes,
id: createCodeblockId()
});
},
toggleCodeBlock:
(attributes) =>
({ commands }) => {
console.log("TOGGLING!");
return commands.toggleNode(this.name, "paragraph", attributes);
return commands.toggleNode(this.name, "paragraph", {
...attributes,
id: createCodeblockId()
});
},
changeCodeBlockIndentation:
(options) =>
Expand Down Expand Up @@ -477,14 +482,16 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
find: backtickInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
language: match[1],
id: createCodeblockId()
})
}),
textblockTypeInputRule({
find: tildeInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
language: match[1],
id: createCodeblockId()
})
})
];
Expand Down Expand Up @@ -520,7 +527,7 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
if (isCode && !isInsideCodeBlock) {
tr.replaceSelectionWith(
this.type.create({
id: `codeblock-${nanoid(12)}`,
id: createCodeblockId(),
language,
indentType: indent.type,
indentLength: indent.amount
Expand Down Expand Up @@ -805,3 +812,7 @@ export function inferLanguage(node: Element) {
);
return language?.filename;
}

function createCodeblockId() {
return `codeblock-${nanoid(12)}`;
}
26 changes: 20 additions & 6 deletions packages/editor/src/extensions/code-block/highlighter.ts
Expand Up @@ -125,9 +125,14 @@ export function HighlighterPlugin({

const changedBlocks: Set<string> = new Set();
for (const blockKey in pluginState.languages) {
if (HIGHLIGHTED_BLOCKS.has(blockKey)) continue;

const language = pluginState.languages[blockKey];
if (
HIGHLIGHTED_BLOCKS.has(blockKey) &&
refractor.registered(language)
) {
continue;
}

const languageDefinition = Languages.find(
(l) =>
l.filename === language || l.alias?.some((a) => a === language)
Expand Down Expand Up @@ -193,12 +198,21 @@ export function HighlighterPlugin({
});
if (changedBlocks.length > 0) {
const updated: Set<number> = new Set();
let hasChanges = false;

changedBlocks.forEach((block) => {
if (updated.has(block.pos)) return;
updated.add(block.pos);

const { id, language } = block.node.attrs;
if (languages[id]) {

if (
!languages[id] ||
(language && !refractor.registered(language))
) {
languages[id] = language;
hasChanges = true;
} else {
const newDecorations = getDecorations({
block,
defaultLanguage
Expand All @@ -221,12 +235,12 @@ export function HighlighterPlugin({
decorations = decorations.remove(toRemove);
if (toAdd.length > 0)
decorations = decorations.add(tr.doc, toAdd);
} else {
languages[id] = language;

hasChanges = true;
}
});

if (decorations !== pluginState.decorations) {
if (hasChanges) {
return { decorations, languages };
}
}
Expand Down
@@ -1,5 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Adding a new codeblock & changing the language should apply the new highlighting 1`] = `"<div><div contenteditable=\\"true\\" translate=\\"no\\" tabindex=\\"0\\" class=\\"ProseMirror\\"><div class=\\"codeblock-view-content-wrap\\"><div class=\\"node-content-wrapper\\" style=\\"white-space: pre; min-width: 20px;\\"><span class=\\"token keyword\\">function</span> <span class=\\"token function\\">hello</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span> <span class=\\"token punctuation\\">}</span></div></div></div></div>"`;
exports[`codeblocks should get highlighted after pasting 1`] = `"<div><div contenteditable=\\"true\\" translate=\\"no\\" tabindex=\\"0\\" class=\\"ProseMirror\\"><div class=\\"codeblock-view-content-wrap\\"><div class=\\"node-content-wrapper\\" style=\\"white-space: pre; min-width: 20px;\\"><span class=\\"token keyword\\">function</span> <span class=\\"token function\\">hello</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span> <span class=\\"token punctuation\\">}</span></div></div><div class=\\"codeblock-view-content-wrap\\"><div class=\\"node-content-wrapper\\" style=\\"white-space: pre; min-width: 20px;\\"><span class=\\"token keyword\\">function</span> <span class=\\"token function\\">hello</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span> <span class=\\"token punctuation\\">}</span></div></div></div></div>"`;
exports[`codeblocks should get highlighted on init 1`] = `"<div><div contenteditable=\\"true\\" translate=\\"no\\" tabindex=\\"0\\" class=\\"ProseMirror\\"><div class=\\"codeblock-view-content-wrap\\"><div class=\\"node-content-wrapper\\" style=\\"white-space: pre; min-width: 20px;\\"><span class=\\"token keyword\\">function</span> <span class=\\"token function\\">hello</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span> <span class=\\"token punctuation\\">}</span></div></div><div class=\\"codeblock-view-content-wrap\\"><div class=\\"node-content-wrapper\\" style=\\"white-space: pre; min-width: 20px;\\"><span class=\\"token keyword\\">function</span> <span class=\\"token function\\">hello</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span> <span class=\\"token punctuation\\">}</span></div></div></div></div>"`;
Expand Down
35 changes: 35 additions & 0 deletions packages/editor/src/extensions/code-block/tests/code-block.test.ts
Expand Up @@ -22,6 +22,7 @@ import { expect, test, vi } from "vitest";
import { CodeBlock, inferLanguage } from "../code-block";
import { HighlighterPlugin } from "../highlighter";
import { getChangedNodes } from "../../../utils/prosemirror";
import { refractor } from "refractor/lib/core";

const CODEBLOCKS_HTML = h("div", [
h("pre", [h("code", ["function hello() { }"])], {
Expand Down Expand Up @@ -191,3 +192,37 @@ test("editing code in a highlighted code block should not be too slow", async ()
expect(timings.reduce((a, b) => a + b) / timings.length).toBeLessThan(16);
expect(editorElement.outerHTML).toMatchSnapshot();
});

test("Adding a new codeblock & changing the language should apply the new highlighting", async () => {
const editorElement = h("div");
const { editor } = createEditor({
element: editorElement,
extensions: {
codeblock: CodeBlock
}
});

editor.commands.setCodeBlock();
editor.commands.insertContent("function hello() { }");

editor.commands.updateAttributes(CodeBlock.name, { language: "javascript" });

await new Promise((resolve) => setTimeout(resolve, 100));

expect(editorElement.outerHTML).toMatchSnapshot();
expect(refractor.registered("javascript")).toBe(true);
});

test("Switching codeblock language should register the new language", async () => {
const editorElement = h("div");
const { editor } = createEditor({
element: editorElement,
initialContent: CODEBLOCKS_HTML,
extensions: {
codeblock: CodeBlock
}
});
editor.commands.updateAttributes(CodeBlock.name, { language: "java" });
await new Promise((resolve) => setTimeout(resolve, 100));
expect(refractor.registered("java")).toBe(true);
});

0 comments on commit 0f191eb

Please sign in to comment.