use code_blocks::{Block, BlockTree}; use tree_sitter::{Language, Parser, Point, Tree}; pub fn build_tree(text: &str, language: Language) -> Tree { let mut parser = Parser::new(); parser .set_language(language) .expect("failed to set language"); parser.parse(text, None).expect("failed to parse text") } #[allow(dead_code)] pub fn copy_item_above<'tree>( ident: &str, text: &str, trees: &Vec>, ) -> Option> { let pos = text .lines() .enumerate() .find_map(|(row, line)| line.find(ident).map(|col| Point::new(row - 1, col)))?; for tree in trees { if tree.block.tail().start_position() == pos { return Some(tree.block.clone()); } if let Some(node) = copy_item_above(ident, text, &tree.children) { return Some(node); } } None } #[macro_export] macro_rules! snapshot { ($name:tt, $lang:expr, $queries:expr, $input:literal) => { #[test] fn $name() { let text = $input; let queries = $queries; let force = false; fn check_fn(_a: &Block, _b: &Block) -> Result<()> { Ok(()) } let tree = build_tree(text, $lang); let items = code_blocks::get_query_subtrees(&queries, &tree, text); let src_block = copy_item_above("^src", text, &items).expect("failed to find src item"); let dst_item = copy_item_above("^dst", text, &items); let fail_item = copy_item_above("^fail", text, &items); let snapshot = if let Some(dst_item) = dst_item { let (new_text, mut new_src_start, mut new_dst_start) = code_blocks::move_block(src_block, dst_item, text, Some(check_fn), force) .expect("move block failed"); let mut new_lines = vec![]; let mut added_src = false; let mut added_dst = false; for line in new_text.lines() { new_lines.push(line.to_string()); if new_src_start > line.len() { new_src_start -= line.len() + 1; } else if !added_src { new_lines.push(" ".repeat(new_src_start) + "^ Source"); added_src = true; } if new_dst_start > line.len() { new_dst_start -= line.len() + 1; } else if !added_dst { new_lines.push(" ".repeat(new_dst_start) + "^ Dest"); added_dst = true; } } let new_text = new_lines.join("\n"); format!("input:\n{}\n---\noutput:\n{}", text, new_text) } else if let Some(fail_item) = fail_item { let err = code_blocks::move_block(src_block, fail_item, text, Some(check_fn), force) .err() .expect("move block succeeded, but should've failed"); format!("input:\n{}\n---\noutput:\n{:?}", text, err) } else { unreachable!("no dst/fail item in input"); }; insta::assert_display_snapshot!(snapshot); } }; ($name:tt, $lang:expr, $queries:expr, $check_fn:ident, $input:literal) => { #[test] fn $name() { let text = $input; let queries = $queries; let force = false; let tree = build_tree(text, $lang); let items = code_blocks::get_query_subtrees(&queries, &tree, text); let src_block = copy_item_above("^src", text, &items).expect("failed to find src item"); let dst_item = copy_item_above("^dst", text, &items); let fail_item = copy_item_above("^fail", text, &items); let snapshot = if let Some(dst_item) = dst_item { let (new_text, mut new_src_start, mut new_dst_start) = code_blocks::move_block(src_block, dst_item, text, Some($check_fn), force) .expect("move block failed"); let mut new_lines = vec![]; let mut added_src = false; let mut added_dst = false; for line in new_text.lines() { new_lines.push(line.to_string()); if new_src_start > line.len() { new_src_start -= line.len() + 1; } else if !added_src { new_lines.push(" ".repeat(new_src_start) + "^ Source"); added_src = true; } if new_dst_start > line.len() { new_dst_start -= line.len() + 1; } else if !added_dst { new_lines.push(" ".repeat(new_dst_start) + "^ Dest"); added_dst = true; } } let new_text = new_lines.join("\n"); format!("input:\n{}\n---\noutput:\n{}", text, new_text) } else if let Some(fail_item) = fail_item { let err = code_blocks::move_block(src_block, fail_item, text, Some($check_fn), force) .err() .expect("move block succeeded, but should've failed"); format!("input:\n{}\n---\noutput:\n{:?}", text, err) } else { unreachable!("no dst/fail item in input"); }; insta::assert_display_snapshot!(snapshot); } }; }