use tiptap_rusty_parser::{Assoc, Mark, Node, PosContent, PosEdit, PosMap, PosRange};
fn p(text: &str) -> Node {
Node::element("paragraph").with_child(Node::text(text))
}
fn text(s: &str) -> PosContent {
PosContent::Text {
text: s.into(),
marks: None,
}
}
#[test]
fn empty_map_is_identity() {
let m = PosMap::new();
assert!(m.is_empty());
for pos in 0..10 {
assert_eq!(m.map(pos, Assoc::Left), pos);
assert_eq!(m.map(pos, Assoc::Right), pos);
}
}
#[test]
fn insertion_shifts_and_respects_assoc() {
let mut m = PosMap::new();
m.push(7, 0, 4);
assert_eq!(m.map(0, Assoc::Left), 0); assert_eq!(m.map(6, Assoc::Right), 6);
assert_eq!(m.map(7, Assoc::Left), 7); assert_eq!(m.map(7, Assoc::Right), 11); assert_eq!(m.map(8, Assoc::Left), 12); }
#[test]
fn deletion_collapses_interior() {
let mut m = PosMap::new();
m.push(5, 5, 0);
assert_eq!(m.map(4, Assoc::Left), 4);
assert_eq!(m.map(5, Assoc::Left), 5);
assert_eq!(m.map(7, Assoc::Left), 5); assert_eq!(m.map(7, Assoc::Right), 5);
assert_eq!(m.map(10, Assoc::Left), 5); assert_eq!(m.map(11, Assoc::Left), 6); }
#[test]
fn replacement_maps_edges() {
let mut m = PosMap::new();
m.push(5, 5, 2);
assert_eq!(m.map(5, Assoc::Left), 5);
assert_eq!(m.map(7, Assoc::Left), 5); assert_eq!(m.map(7, Assoc::Right), 7); assert_eq!(m.map(10, Assoc::Right), 7); assert_eq!(m.map(12, Assoc::Left), 9); }
#[test]
fn multiple_disjoint_steps_accumulate() {
let mut m = PosMap::new();
m.push(10, 0, 3);
m.push(2, 2, 0);
assert_eq!(m.map(1, Assoc::Left), 1);
assert_eq!(m.map(5, Assoc::Left), 3); assert_eq!(m.map(10, Assoc::Right), 11); assert_eq!(m.map(12, Assoc::Left), 13); }
#[test]
fn map_range_keeps_collapsed_collapsed() {
let mut m = PosMap::new();
m.push(3, 0, 5); let r = m.map_range(PosRange::new(3, 3), Assoc::Right);
assert_eq!((r.from, r.to), (8, 8)); }
#[test]
fn from_pos_edits_matches_manual() {
let edits = vec![
PosEdit::Delete { from: 2, to: 4 },
PosEdit::Insert {
pos: 10,
content: text("xyz"),
},
PosEdit::AddMark {
from: 6,
to: 7,
mark: Mark::new("bold"),
},
];
let m = PosMap::from_pos_edits(&edits);
let mut manual = PosMap::new();
manual.push(2, 2, 0);
manual.push(10, 0, 3);
assert_eq!(m, manual);
}
#[test]
fn from_pos_edits_counts_node_sizes() {
let edits = vec![PosEdit::Insert {
pos: 0,
content: PosContent::Nodes {
nodes: vec![p("ab")],
},
}];
let m = PosMap::from_pos_edits(&edits);
assert_eq!(m.map(0, Assoc::Right), 4);
}
#[test]
fn mapped_edit_anchors_position_in_new_doc() {
let mut doc = Node::element("doc").with_children([p("hello world"), p("second line")]);
let cursor = 21;
let (_patch, map) = doc
.apply_pos_edits_mapped(&[PosEdit::Insert {
pos: 7,
content: text("big "),
}])
.unwrap();
let moved = map.map(cursor, Assoc::Left);
assert_eq!(moved, 25);
let (block, inline) = doc.pos_to_inline(moved).unwrap();
assert_eq!(block, vec![1]);
assert_eq!(inline.offset, 7);
}
#[test]
fn mapped_delete_pulls_trailing_position_back() {
let mut doc = Node::element("doc").with_children([p("hello world"), p("second line")]);
let end = doc.content_size(); let (_patch, map) = doc
.apply_pos_edits_mapped(&[PosEdit::Delete { from: 6, to: 12 }]) .unwrap();
assert_eq!(map.map(end, Assoc::Left), end - 6);
assert_eq!(map.map(end, Assoc::Left), doc.content_size());
}