Skip to content
This repository has been archived by the owner on Jan 8, 2025. It is now read-only.

Commit

Permalink
Use visitParents
Browse files Browse the repository at this point in the history
  • Loading branch information
ptgott committed Nov 15, 2024
1 parent e22a90b commit 7c310e9
Showing 1 changed file with 81 additions and 67 deletions.
148 changes: 81 additions & 67 deletions server/rehype-hljs-var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
import rehypeHighlight, {
Options as RehypeHighlightOptions,
} from "rehype-highlight";
import { visitParents } from "unist-util-visit-parents";
import { visit, CONTINUE, SKIP } from "unist-util-visit";
import { v4 as uuid } from "uuid";
import remarkParse from "remark-parse";
Expand Down Expand Up @@ -84,89 +85,102 @@ export const rehypeVarInHLJS = (
// series of span elements with different "hljs-*" classes. Find the
// placeholder UUIDs and replace them with their original Var elements,
// inserting these as HTML AST nodes.
visit(root, undefined, (node: Node, index: number, parent: Parent) => {
const el = node as Element;
visitParents(
root,
undefined,
(node: Node, index: number, parents: Array<Node>) => {
// The text node is the child of an element node, which is at least one
// level lower than the root node.
if (!parents || parents.length <= 1) {
return [CONTINUE];
}

console.log("el:", el);
if (el.type !== "text") {
return [CONTINUE];
}
const parent = parents[parents.length - 1];
const grandparent = parents[parents.length - 2];
const el = node as Element;
if (el.type !== "text") {
return [CONTINUE];
}

const hljsSpanValue = (el as Text).value;
const hljsSpanValue = (el as Text).value;

// This is an hljs span with only the placeholder as its child.
// We don't need the span, so replace it with the original Var.
if (placeholdersToVars[hljsSpanValue]) {
(parent as any).children[index] = placeholdersToVars[hljsSpanValue];
return [CONTINUE];
}

const placeholders = Array.from(
hljsSpanValue.matchAll(new RegExp(placeholderPattern, "g"))
);
// This is an hljs span with only the placeholder as its child.
// We don't need the span, so replace it with the original Var.
if (placeholdersToVars[hljsSpanValue]) {
(parent as any).children[index] = placeholdersToVars[hljsSpanValue];
return [CONTINUE];
}

// No placeholders to recover, so there's nothing more to do.
if (placeholders.length == 0) {
return [CONTINUE];
}
const placeholders = Array.from(
hljsSpanValue.matchAll(new RegExp(placeholderPattern, "g"))
);

// An hljs span's text includes one or more Vars among other content, so
// we need to replace the span with a series of spans separated by
// Vars.
let lastIndex = 0;
let newChildren: Array<MdxJsxFlowElement | Element> = [];
// If there is content before the first Var, separate it into a new hljs
// span.
if (placeholders[0].index > 0) {
newChildren.push({
tagName: "span",
type: "element",
properties: el.properties,
children: [
{
type: "text",
value: hljsSpanValue.substring(lastIndex, placeholders[0].index),
},
],
});
lastIndex = placeholders[0].index;
}
placeholders.forEach((ph, i) => {
const placeholderValue = ph[0];
newChildren.push(placeholdersToVars[placeholderValue]);
lastIndex += placeholderValue.length;

// Check if there is some non-Var text between either (a) this and the
// next Var or (b) between this Var and the end of the content. If
// so, add another span and advance the last index.
let nextIndex = 0;
if (i < placeholders.length - 1) {
nextIndex = placeholders[i + 1].index;
} else if (i == placeholders.length - 1) {
nextIndex = hljsSpanValue.length;
// No placeholders to recover, so there's nothing more to do.
if (placeholders.length == 0) {
return [CONTINUE];
}
if (lastIndex < nextIndex) {

// An hljs span's text includes one or more Vars among other content, so
// we need to replace the span with a series of spans separated by
// Vars.
let lastIndex = 0;
let newChildren: Array<MdxJsxFlowElement | Element> = [];
// If there is content before the first Var, separate it into a new hljs
// span.
if (placeholders[0].index > 0) {
newChildren.push({
tagName: "span",
type: "element",
properties: el.properties,
children: [
{
type: "text",
value: hljsSpanValue.substring(lastIndex, nextIndex),
value: hljsSpanValue.substring(
lastIndex,
placeholders[0].index
),
},
],
});
lastIndex = nextIndex;
lastIndex = placeholders[0].index;
}
});
// Delete the current span and replace it with the new children.
(parent.children as Array<MdxJsxFlowElement | Element>).splice(
index,
1,
...newChildren
);
return [SKIP, index + newChildren.length];
});
placeholders.forEach((ph, i) => {
const placeholderValue = ph[0];
newChildren.push(placeholdersToVars[placeholderValue]);
lastIndex += placeholderValue.length;

// Check if there is some non-Var text between either (a) this and the
// next Var or (b) between this Var and the end of the content. If
// so, add another span and advance the last index.
let nextIndex = 0;
if (i < placeholders.length - 1) {
nextIndex = placeholders[i + 1].index;
} else if (i == placeholders.length - 1) {
nextIndex = hljsSpanValue.length;
}
if (lastIndex < nextIndex) {
newChildren.push({
tagName: "span",
type: "element",
properties: el.properties,
children: [
{
type: "text",
value: hljsSpanValue.substring(lastIndex, nextIndex),
},
],
});
lastIndex = nextIndex;
}
});
// Delete the current span and replace it with the new children.
(grandparent.children as Array<MdxJsxFlowElement | Element>).splice(
index,
1,
...newChildren
);
return [SKIP, index + newChildren.length];
}
);
};
};

0 comments on commit 7c310e9

Please sign in to comment.