Skip to content

Commit

Permalink
Clean up the replacement loop
Browse files Browse the repository at this point in the history
...but return the logic to the original. No longer ranging over the
children of a code snippet since HLJS spans can be nested.
  • Loading branch information
ptgott committed Nov 18, 2024
1 parent 12b2f3c commit 60bdef9
Showing 1 changed file with 67 additions and 92 deletions.
159 changes: 67 additions & 92 deletions server/rehype-hljs-var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const rehypeVarInHLJS = (
// We only visit text nodes inside code snippets
if (
node.type !== "text" ||
!parent ||
!parent.hasOwnProperty("tagName") ||
(parent as Element).tagName !== "code"
) {
Expand Down Expand Up @@ -87,109 +86,85 @@ export const rehypeVarInHLJS = (
// inserting these as HTML AST nodes.
visit(root, undefined, (node: Node, index: number, parent: Parent) => {
const el = node as Element;
// We only visit code fences
if (!el || el.tagName !== "code") {
// We expect the element to have a single text node
if (
el.type !== "element" ||
el.children.length !== 1 ||
el.children[0].type !== "text"
) {
return [CONTINUE];
}
const hljsSpanValue = (el.children[0] as Text).value;

for (let i = 0; i < el.children.length; i++) {
if (
el.children[i].type !== "text" &&
el.children[i].tagName !== "span"
) {
continue;
}

let textValue;
if ((el.children[i] as Element).tagName == "span") {
textValue = (el.children[i].children[0] as Text).value;
} else {
textValue = el.children[i].value;
}
if (!textValue) {
continue;
}
// 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];
}

// 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[textValue]) {
el.children[i] = placeholdersToVars[textValue];
continue;
}
const placeholders = Array.from(
hljsSpanValue.matchAll(new RegExp(placeholderPattern, "g"))
);

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

// No placeholders to recover, so there's nothing more to do.
if (placeholders.length == 0) {
// The element'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 newChildren: Array<MdxJsxFlowElement | Element> = [];

// Assemble a map of indexes to their corresponding placeholders so we
// can tell whether a given index falls within a placeholder.
const placeholderIndices = new Map();
placeholders.forEach((p) => {
placeholderIndices.set(p.index, p[0]);
});

let valueIdx = 0;
while (valueIdx < hljsSpanValue.length) {
// The current index is in a placeholder, so add the original Var
// component to newChildren.
if (placeholderIndices.has(valueIdx)) {
const placeholder = placeholderIndices.get(valueIdx);
valueIdx += placeholder.length;
newChildren.push(placeholdersToVars[placeholder]);
continue;
}
// The current index is outside a placeholder, so assemble a text or
// span node and push that to newChildren.
let textVal = "";
while (
!placeholderIndices.has(valueIdx) &&
valueIdx < hljsSpanValue.length
) {
textVal += hljsSpanValue[valueIdx];
valueIdx++;
}

// The element'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. Advance through the value we're splitting. If the current
// index lands inside a placeholder, add a Var tag to an array of new
// children. Otherwise add a text node or a span, depending on the
// value's parent.
let newChildren: Array<MdxJsxFlowElement | Element> = [];

// Assemble a map of indexes to their corresponding placeholders so we
// can tell whether a given index falls within a placeholder.
const placeholderIndices = new Map();
placeholders.forEach((p) => {
placeholderIndices.set(p.index, p[0]);
});

let valueIdx = 0;
while (valueIdx < textValue.length) {
// The current index is in a placeholder, so add the original Var
// component to newChildren.
if (placeholderIndices.has(valueIdx)) {
const placeholder = placeholderIndices.get(valueIdx);
console.log("placeholder:", placeholder);
valueIdx += placeholder.length;
newChildren.push(placeholdersToVars[placeholder]);
continue;
}
// The current index is outside a placeholder, so assemble a text or
// span node and push that to newChildren.
let textVal = "";
while (
!placeholderIndices.has(valueIdx) &&
valueIdx < textValue.length
) {
textVal += textValue[valueIdx];
valueIdx++;
}
if (el.children[i].type == "text") {
newChildren.push({
newChildren.push({
tagName: "span",
type: "element",
properties: el.properties,
children: [
{
type: "text",
value: textVal,
});
continue;
}
newChildren.push({
tagName: "span",
type: "element",
properties: el.children[i].properties,
children: [
{
type: "text",
value: textVal,
},
],
});
}

// Delete the current span and replace it with the new children.
(parent.children as Array<MdxJsxFlowElement | Element>).splice(
index,
1,
...newChildren
);
},
],
});
}
return [CONTINUE];

// 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];
});
};
};

0 comments on commit 60bdef9

Please sign in to comment.