This commit is contained in:
Chris Kruining 2025-03-12 16:50:49 +01:00
parent 97036272dd
commit b1e617e74a
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
11 changed files with 476 additions and 186 deletions

View file

@ -1,82 +1,78 @@
import type { Node, Text, Element, ElementContent, Parent, RootContent } from 'hast';
import type { Node, Text, Parent, RootContent } from 'hast';
import { find } from 'unist-util-find';
import { visit } from 'unist-util-visit';
import { deepCopy } from '~/utilities';
import { hash } from './temp';
/**
*
* Given
* root
* |- element
* | |- text [0, 6]
* | |- element
* | | |- text [7, 18]
* | |- text [19, 25]
* |- element
* |- text [26, 40]
* |- element
* | |- text [41, 53]
* |- text [54, 60]
*
* split at 10
*
* root
* |- element
* | |- text [0, 6]
* | |- element
* | | |- text [7, 9]
*
* root
* |- element
* | |- element
* | | |- text [10, 18]
* | |- text [19, 25]
* |- element
* |- text [26, 40]
* |- element
* | |- text [41, 53]
* |- text [54, 60]
*/
export const createElement = (tagName: string, children: any[], properties: object = {}) => ({ type: 'element', tagName, children, properties });
export const splitAt = (tree: Parent, node: Text, offset: number): [RootContent[], RootContent[]] => {
const index = tree.children.findIndex(c => find(c, { ...node }));
interface SplitPoint {
node: Text;
offset: number;
}
if (index === -1) {
throw new Error('The tree does not contain the given node');
export const splitBy = (tree: Parent, splitPoints: SplitPoint[]): RootContent[][] => {
const result: RootContent[][] = [];
let remaining: RootContent[] = Object.hasOwn(tree, 'children') ? (tree as Parent).children : [];
console.log('kaas');
// console.log(Object.groupBy(splitPoints, p => hash(p.node)));
for (const { node, offset } of splitPoints) {
const index = remaining.findIndex(c => find(c, n => equals(n, node)));
if (index === -1) {
throw new Error('The tree does not contain the given node');
}
const [targetLeft, targetRight] = splitNode(remaining[index], node, offset);
const left = remaining.slice(0, index);
const right = remaining.slice(index + 1);
if (targetLeft) {
left.push(targetLeft);
}
if (targetRight) {
right.unshift(targetRight);
}
remaining = right;
result.push(left);
}
const left = tree.children.slice(0, index);
const right = tree.children.slice(index + 1);
result.push(remaining);
if (offset === 0) {
right.unshift(tree.children[index]);
}
else if (offset === node.value.length) {
left.push(tree.children[index]);
}
else {
const targetLeft = deepCopy(tree.children[index]);
const targetRight = tree.children[index];
left.push(targetLeft);
right.unshift(targetRight);
visit(targetLeft, (n): n is Text => equals(n, node), n => {
n.value = n.value.slice(0, offset);
})
visit(targetRight, (n): n is Text => equals(n, node), n => {
n.value = n.value.slice(offset);
})
}
return [left, right];
return result;
};
const splitNode = (node: Node, offset: number) => {
const splitNode = (node: Node, text: Text, offset: number): [RootContent | undefined, RootContent | undefined] => {
if (offset === 0) {
return [undefined, node as RootContent];
}
if (offset === text.value.length) {
return [node as RootContent, undefined];
}
const left = structuredClone(node) as RootContent;
const right = node as RootContent;
visit(left, (n): n is Text => equals(n, text), n => {
n.value = n.value.slice(0, offset);
})
visit(right, (n): n is Text => equals(n, text), n => {
n.value = n.value.slice(offset);
})
return [left, right];
}
export const mergeNodes = (...nodes: Text[]): Text => {
return { type: 'text', value: nodes.map(n => n.value).join() };
};
const equals = (a: Node, b: Node): boolean => {
if (a === b) {
return true;
@ -86,8 +82,5 @@ const equals = (a: Node, b: Node): boolean => {
return false;
}
// This is the nasty version of deep object checking,
// but I hope this is safe to do in this case because
// we are working with a html-ast and not just any type of object.
return JSON.stringify(a) === JSON.stringify(b);
return hash(a) === hash(b);
};