Skip to content

Commit

Permalink
fix code blocks in tables in Markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
jcv8000 committed Oct 11, 2023
1 parent d03c0d2 commit bd5498e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import StarterKit from "@tiptap/starter-kit";
import Focus from "@tiptap/extension-focus";
import { createLowlight, all as lowlightAll } from "lowlight";
import Image from "@tiptap/extension-image";
import Table from "@tiptap/extension-table";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import TableRow from "@tiptap/extension-table-row";
Expand All @@ -26,6 +25,7 @@ import { Markdown } from "tiptap-markdown";
import { FontSize } from "./extensions/FontSize";
import { CustomLink } from "./extensions/CustomLink";
import { CustomCode } from "./extensions/CustomCode";
import { CustomTable } from "./extensions/CustomTable";

export function extensions(options: { useTypography: boolean }) {
const e = [
Expand Down Expand Up @@ -53,7 +53,7 @@ export function extensions(options: { useTypography: boolean }) {
Image.configure({
allowBase64: true
}),
Table.configure({
CustomTable.configure({
// resizable: true,
// lastColumnResizable: false,
// allowTableNodeSelection: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Table from "@tiptap/extension-table";
import { Fragment } from "@tiptap/pm/model";
import { getHTMLFromFragment } from "@tiptap/core";
import { Node } from "@tiptap/pm/model";

export const CustomTable = Table.extend({
addStorage() {
return {
markdown: {
// Turn code blocks into back-tick markdown expressions instead of HTTML inside table cells
// This lets code blocks display correctly inside tables on GitHub
serialize(state: any, node: Node, parent: Node) {
let html = serializeHTML(node, parent);
const regex = /<pre><code class="language-(.*)">([\S\s]*?)<\/code><\/pre>/gm;
let m: RegExpExecArray | null;

while ((m = regex.exec(html)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}

const lang = m[1];
const code = m[2];

html = html.replace(
/<pre><code class="language-(.*)">([\S\s]*?)<\/code><\/pre>/gm,
`\n\n\`\`\`${lang}\n${code}\n\`\`\`\n`
);
}

state.write(html);
if (node.isBlock) {
state.closeBlock(node);
}
},
parse: {
// handled by markdown-it
}
}
};
}
});

/**
* These functions were copied from
* https://github.com/aguingand/tiptap-markdown/blob/main/src/extensions/nodes/html.js
*/

function serializeHTML(node: Node, parent: Node) {
const schema = node.type.schema;
const html = getHTMLFromFragment(Fragment.from(node), schema);

if (node.isBlock && parent.type.name === schema.topNodeType.name) {
return formatBlock(html);
}

return html;
}

function formatBlock(html: string) {
const dom = elementFromString(html);
const element = dom.firstElementChild;

element!.innerHTML = element!.innerHTML.trim() ? `\n${element!.innerHTML}\n` : `\n`;

return element!.outerHTML;
}

function elementFromString(value: string) {
// add a wrapper to preserve leading and trailing whitespace
const wrappedValue = `<body>${value}</body>`;

return new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
}

0 comments on commit bd5498e

Please sign in to comment.