hi guys, my goal is to write a prosemirror plugin that searches the doc ONCE when loading the editor and shows/hides children of nodes that have some attribute, whose value is a range (e.g. 2-4), based on this range attribute. (a range of 2-4 means show node at index 2, index 3, index 4) it should do it for nodes and then for their child nodes and then for their child nodes …so on… which is the difficulty with the whole thing, as the positions may become invalid under certain circumstances
thats how my current implementation look like:
export const showNChildNodesPlugin = new Plugin({
view(view)
{
if (view.editable)
{
return {};
}
const { tr } = view.state;
const seen = {};
for (let goon = false; ;)
{
tr.doc.descendants((node, pos) =>
{
if (goon)
{
return false;
}
const range = node.attrs[SHOW_N_CHILD_NODES_ATTR_NAME]?.split?.('-');
if (!(range?.length === 2))
{
return true;
}// range is [x,y]
if (seen[pos])
{
return true;
}
goon = true;
showNChildNodes(tr, node, pos, range);
seen[pos] = true;
return false;
});
if (goon)
{
goon = false;
continue;
}
break;
}
view.dispatch(tr);
return {};
},
});
/**
* @param {import('prosemirror-state').Transaction} tr
* @param {import('prosemirror-model').Node} node
* @param {number} pos
* @param {[number,number]} range
* Only the child nodes with index i for which n<=i<=m are shown.
*/
export function showNChildNodes(tr, node, pos, [n, m])
{
const /** @type{import('prosemirror-model').Node[]} */keep = [];
node.forEach((child, _, i) =>
{
if (n <= i && i <= m)
{
keep.push(child);
}
});
const newNode = node.copy(Fragment.from(keep));
tr.replaceWith(pos, pos + node.nodeSize, newNode);
}
its working quite fine, but this loop and the seen map thing bothers me
any ideas how to do it better?