-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iterable $dfs #6664
Iterable $dfs #6664
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
size-limit report 📦
|
|
||
if (node !== null && node.is(end)) { | ||
nodes.push({depth, node}); | ||
export function $getNextSiblingOrParentSibling( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentionally exported, this 0 added cost function is a handy utility to skip a subtree or (unrelated) help find the next sibling that is not directly connected to the same parent.
packages/lexical-utils/src/index.ts
Outdated
/** | ||
* $dfs iterator. Tree traversal is done on the fly as new values are requested with O(1) memory. | ||
* @param startNode - The node to start the search, if ommitted, it will start at the root node. | ||
* @param endNode - The node to end the search, if ommitted, it will find all descendants of the startingNode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @param endNode - The node to end the search, if ommitted, it will find all descendants of the startingNode. | |
* @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode. |
packages/lexical-utils/src/index.ts
Outdated
endNode?: LexicalNode, | ||
): DFSIterator { | ||
const start = (startNode || $getRoot()).getLatest(); | ||
const end = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this could be left optional and check depth < startDepth if undefined? Would save some traversing
|
||
if (node !== null && node.is(end)) { | ||
nodes.push({depth, node}); | ||
export function $getNextSiblingOrParentSibling( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like exporting this, seems very useful for when you are sure you don't want to traverse all subtrees. It could probably use its own tests since it's exported though
* @param endNode - The node to end the search, if ommitted, it will find all descendants of the startingNode. | ||
* @returns An iterator, each yielded value is a DFSNode. It will always return at least 1 node (the start node). | ||
*/ | ||
export function $dfsIterator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder what kind of trouble users will get themselves into when they mutate the document and continue iterating
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@etrepum good observation, how much do we want to do to prevent misuse? I'm inclined to make it flexible and not just read-only. I believe we can control the flow by listening to editor updates when the iteration is started but I do foresee this traversal can be useful even in write mode to tweak some nodes as you go as long as you understand what you're doing...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should try very hard, we don't really have a way to do it even if we wanted to, but there ought to be some documentation about what you should or shouldn't do and what the semantics are. I'm writing this without another close look at the code but we should also be intentional about the next state in the iteration, e.g. we should probably know enough to find what the next key is going to be before we return the node from the iterator so that if the user replaces the given node the iterator doesn't halt, or if they insert something after that the new node doesn't end up in the iterator.
$dfs
is handy but not very flexible. The fact that it computes the entire array every time means that it's either all or nothing.A common use case for a DFS is data format conversion, and data conversion formats that are modular will delegate the processing of a part of the tree to the responsible modules. An iterable version of
$dfs
makes it possible to stop and resume the iteration without paying the redundant cost.Iterable is not just limited to this one use case, I'm confident that this small addition to the utils library is not a one-off.
This is how the application of this looks like in the wild (not a one-liner but I also don't think we want to overoptimize in one use case).