rust-analyzer / rowan

Apache License 2.0
689 stars 57 forks source link

Comparing GreenNodes #102

Closed DieracDelta closed 3 years ago

DieracDelta commented 3 years ago

How can I compare two SyntaxNode<T>s? I see that the Eq trait is implemented for GreenNode, but I don't see how to get a GreenNode from a SyntaxNode. .green() gives me a GreenNodeData which I can't turn into a GreenNode.

The motivation for this is I have a SyntaxNode child that I was to remove. So, I get the parent SyntaxNode, iterate through its children to find the index of the child SyntaxNode and then call remove_child. This gives me a type error, though. Here's the code I'm working with:

pub fn kill_node_attribute(
    node: &SyntaxNode<NixLanguage>,
) -> Result<SyntaxNode<NixLanguage>, String> {
    let parent = node.parent().unwrap();
    match parent.kind() {
        NODE_ATTR_SET => {
            let idx =
                parent
                    .green()
                    .children()
                    .enumerate()
                    .fold(0, |correct_idx, (idx, val)| -> usize {
                        if let Some(inner_node) = val.into_node() {
                            //if Borrow::<GreenNodeData>::borrow(inner_node) == node.green() {
                            if *inner_node == (*node.green()).into() {
                                idx
                            } else {
                                correct_idx
                            }
                        } else {
                            correct_idx
                        }
                    });
            let mut new_root =
                SyntaxNode::<NixLanguage>::new_root(parent.green().remove_child(idx));
            loop {
                println!("did one iteration of the inner loop");
                if let Some(parent) = new_root.parent() {
                    new_root = parent;
                } else {
                    break;
                }
            }
            Ok(Root::cast(new_root).unwrap().inner().unwrap())
        }
        NODE_STR => unimplemented!(),
    }
}

And the error:


0% ❯ bash run.sh
warning: unused import: `GreenNodeData`
 --> src/parser.rs:2:48
  |
2 | use rowan::{api::SyntaxNode, GreenNodeBuilder, GreenNodeData};
  |                                                ^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0277]: the trait bound `GreenNode: From<GreenNodeData>` is not satisfied
  --> src/parser.rs:42:63
   |
42 | ...                   if *inner_node == (*node.green()).into() {
   |                                                         ^^^^ the trait `From<GreenNodeData>` is not implemented for `GreenNode`
   |
   = note: required because of the requirements on the impl of `Into<GreenNode>` for `GreenNodeData`

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
error: could not compile `flake_generator`

To learn more, run the command again with --verbose.```
DieracDelta commented 3 years ago

It's worth noting that I realize that SyntaxNode implements equality, and exposes an API to iterate through children. However, I need the index of the greennode so I can remove_child (this is not implemented by SyntaxNode). To expand on this--I tried using pure SyntaxNodes and this just doesn't work because the indices of greennodes and syntax nodes are different. The code that demonstrates this goes something like:

pub fn kill_node_attribute(
    node: &SyntaxNode<NixLanguage>,
) -> Result<SyntaxNode<NixLanguage>, String> {
    let parent = node.parent().unwrap();
    match parent.kind() {
        NODE_ATTR_SET => {
            let idx = parent.children().enumerate().fold(
                50000,
                |correct_idx, (idx, inner_node)| -> usize {
                    if inner_node == *node {
                        println!("idx {}, Nuking: {:?}", idx, inner_node.to_string());
                        idx
                    } else {
                        correct_idx
                    }
                },
            );
            println!("Nuking idx: {:?}", idx);
            let new_parent = parent.green().remove_child(idx);
            println!("the new parent is: {:?}", new_parent.to_string());
            println!("the old parent is: {:?}", parent.to_string());
            let mut new_root = SyntaxNode::<NixLanguage>::new_root(parent.replace_with(new_parent));
            //if new_root.is_none() {
            //panic!("OH NO FAILURE")
            //}
            loop {
                println!("did one iteration of the inner loop!!!");
                if let Some(parent) = new_root.parent() {
                    println!("the kind of parent is: {:?}", parent.kind());
                    new_root = parent;
                } else {
                    break;
                }
            }
            let tmp = Root::cast(new_root).unwrap();
            Ok(tmp.inner().unwrap())
        }
        NODE_STR => unimplemented!(),
    }
}
iDawer commented 3 years ago

how to get a GreenNode from a SyntaxNode. .green()

You seems looking for this impl https://github.com/rust-analyzer/rowan/blob/91071e32fb9d02935e112de222c0fb22081e1753/src/green/node.rs#L48-L52

DieracDelta commented 3 years ago

@iDawer thank you that immediately fixed my problem. I'd like to add in either some documentation or example somewhere to explain how to do this (I didn't see anything super similar in the rust-analyzer source). I will follow up with something for examples on how to do this.