use rowan::{TextRange, TextSize};
use crate::syntax::SyntaxNode;
pub fn drops_a_comment(outer: &SyntaxNode, keep: &[TextRange]) -> bool {
let span = outer.text_range();
let text = outer.text().to_string();
let offset = |at: TextSize| usize::from(at - span.start());
let mut cursor = span.start();
for kept in keep {
if kept.start() < cursor || kept.end() > span.end() {
return true;
}
if text[offset(cursor)..offset(kept.start())].contains('#') {
return true;
}
cursor = kept.end();
}
text[offset(cursor)..].contains('#')
}
pub fn inline_text(node: &SyntaxNode) -> Option<String> {
let text = node.text().to_string();
(!text.contains('\n')).then_some(text)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{AstNode, CallExpr, HasArgList};
use crate::parser::parse;
fn call_and_args(src: &str) -> (SyntaxNode, TextRange) {
let call = parse(src)
.cst
.descendants()
.find_map(CallExpr::cast)
.expect("a call");
let args = call.arg_list().expect("an argument list");
(call.syntax().clone(), args.syntax().text_range())
}
#[test]
fn drops_a_comment_sees_only_the_discarded_gaps() {
let (call, args) = call_and_args("length #= why =# (xs)\n");
assert!(drops_a_comment(&call, &[args]));
let (call, args) = call_and_args("length(#= why =# xs)\n");
assert!(!drops_a_comment(&call, &[args]));
let (call, _) = call_and_args("length(#= why =# xs)\n");
assert!(drops_a_comment(&call, &[]));
let (call, _) = call_and_args("length(xs)\n");
assert!(!drops_a_comment(&call, &[]));
}
#[test]
fn drops_a_comment_rejects_a_keep_list_it_cannot_trust() {
let (call, args) = call_and_args("length(xs)\n");
assert!(drops_a_comment(&call, &[args, args]));
assert!(drops_a_comment(
&call,
&[TextRange::new(0.into(), 100.into())]
));
}
#[test]
fn inline_text_declines_a_multi_line_node() {
let (call, _) = call_and_args("length(xs)\n");
assert_eq!(inline_text(&call).as_deref(), Some("length(xs)"));
let (call, _) = call_and_args("length([\n 1,\n])\n");
assert!(inline_text(&call).is_none());
}
}