use rnix::{match_ast, ast}; use rowan::ast::AstNode; use crate::queries::*; type NixExprs = Box>; type Pipe = (Vec, NixExprs); #[derive(Debug)] pub struct Change { pub node: rnix::SyntaxNode, pub kind: Operator } impl Filter { fn apply(&self, (mut changes, acc): Pipe) -> Pipe { use Selector::*; // several of these closures take ownership of the operator, hence clone let mut acc: NixExprs = match self.selector.clone() { Down => Box::new(acc.map(|s| s.children()).flatten()), DownRecursive => Box::new(acc.map(|s| s.descendants()).flatten()), Up => Box::new(acc.filter_map(|s| s.parent())), UpRecursive => Box::new(acc.map(|s| s.ancestors()).flatten()), // TODO: how to select roles relative to previous node? NixSyntaxNode(kind) => Box::new(acc.filter(move |s| s.kind() == kind)), NixSyntaxRole(role) => {use crate::queries::NixSyntaxRole::*; match role { Argument => Box::new(acc.filter_map(move |s| match_ast! { match s { ast::Apply(value) => value.argument().map(|s| s.syntax().to_owned()), _ => None }})), _ => todo!() }} Named(name) => Box::new(acc .filter(move |node| match_ast! { match node { ast::AttrpathValue(value) => { name == value.attrpath().unwrap().to_string() }, ast::Apply(value) => { // TODO: special case lambda = NODE_SELECT here? name == value.lambda().unwrap().to_string() }, // TODO: this is difficult — I want to use free-form names // to select things below, too, but that might not always be // possible. perhaps it is possible to skip over descendants? ast::Ident(value) => { name == value.to_string() }, _ => false }}) ), }; if let Some(operator) = &self.args { let (mut nchanges, nacc): (Vec<_>, Vec<_>) = acc .map(|node| (Change { node: node.clone(), kind: operator.clone() }, node)) .unzip(); acc = Box::new(nacc.into_iter()); changes.append(&mut nchanges); } (changes, acc) } } impl Query { pub fn apply(&self, _content: &str, nexp: rnix::SyntaxNode) -> anyhow::Result<(Vec, Vec)> { let mut pipe: Pipe = (Vec::new(), Box::new(std::iter::once(nexp))); for filter in &self.filters { pipe = filter.apply(pipe); } // let results = // acc.map(|node| content[node.text_range().start().into()..node.text_range().end().into()].to_owned()) // .collect(); Ok((pipe.0, pipe.1.collect())) } }