use text_document::TextDocument;
#[test]
fn set_markdown_populates_rope() {
let doc = TextDocument::new();
doc.set_markdown("first paragraph\n\nsecond paragraph")
.expect("set_markdown")
.wait()
.expect("wait");
let store = doc.rope_store_for_test();
let rope = store.rope.read();
let rope_text = rope.to_string();
assert_eq!(rope_text, "first paragraph\nsecond paragraph");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 2);
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 16); assert_eq!(offsets.total_bytes(), 32);
}
#[test]
fn set_html_populates_rope() {
let doc = TextDocument::new();
doc.set_html("<p>alpha</p><p>beta</p><p>gamma</p>")
.expect("set_html")
.wait()
.expect("wait");
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "alpha\nbeta\ngamma");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 3);
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 6); assert_eq!(offsets.entries[2].1, 11); assert_eq!(offsets.total_bytes(), 16); }
#[test]
fn insert_text_at_position_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("hello world").unwrap();
let cursor = doc.cursor_at(5);
cursor.insert_text(",").unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "hello, world");
}
#[test]
fn insert_text_at_end_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("hello").unwrap();
let cursor = doc.cursor_at(5);
cursor.insert_text(" world").unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "hello world");
}
#[test]
fn insert_text_into_block_other_than_first_shifts_offsets() {
let doc = TextDocument::new();
doc.set_plain_text("aaa\nbbb\nccc").unwrap();
let cursor = doc.cursor_at(5);
cursor.insert_text("XX").unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "aaa\nbXXbb\nccc");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 3);
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 4);
assert_eq!(offsets.entries[2].1, 10); assert_eq!(offsets.total_bytes(), 13);
}
#[test]
fn delete_within_block_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("hello, world").unwrap();
let cursor = doc.cursor_at(5);
cursor.set_position(7, text_document::MoveMode::KeepAnchor);
cursor.remove_selected_text().unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "helloworld");
}
#[test]
fn delete_within_middle_block_shifts_subsequent_offsets() {
let doc = TextDocument::new();
doc.set_plain_text("aaa\nbbbbb\nccc").unwrap();
let cursor = doc.cursor_at(5);
cursor.set_position(7, text_document::MoveMode::KeepAnchor);
cursor.remove_selected_text().unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "aaa\nbbb\nccc");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 4);
assert_eq!(offsets.entries[2].1, 8); assert_eq!(offsets.total_bytes(), 11);
}
#[test]
fn insert_image_inserts_object_replacement_sentinel() {
let doc = TextDocument::new();
doc.set_plain_text("ab").unwrap();
let cursor = doc.cursor_at(1);
cursor.insert_image("img1", 100, 100).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "a\u{FFFC}b");
assert_eq!(rope.len_bytes(), 5);
let offsets = store.block_offsets.read();
assert_eq!(offsets.total_bytes(), 5);
}
#[test]
fn insert_formatted_text_at_position_mirrors_to_rope() {
use text_document::TextFormat;
let doc = TextDocument::new();
doc.set_plain_text("hello world").unwrap();
let cursor = doc.cursor_at(5);
let fmt = TextFormat {
font_bold: Some(true),
..Default::default()
};
cursor.insert_formatted_text(",", &fmt).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "hello, world");
}
#[test]
fn insert_block_splits_existing_block_in_rope() {
let doc = TextDocument::new();
doc.set_plain_text("hello world").unwrap();
let cursor = doc.cursor_at(5);
cursor.insert_block().unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "hello\n world");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 2);
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 6); assert_eq!(offsets.total_bytes(), 12);
}
#[test]
fn cross_block_delete_merges_in_rope() {
let doc = TextDocument::new();
doc.set_plain_text("aaa\nbbb\nccc").unwrap();
let cursor = doc.cursor_at(2);
cursor.set_position(9, text_document::MoveMode::KeepAnchor);
cursor.remove_selected_text().unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "aacc");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 1);
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.total_bytes(), 4);
}
#[test]
fn insert_table_inserts_sentinel_and_cells_inline_in_rope() {
let doc = TextDocument::new();
doc.set_plain_text("alpha\nbeta\ngamma").unwrap();
let cursor = doc.cursor_at(6);
cursor.insert_table(2, 2).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "alpha\n\u{FFFC}\n\n\n\n\nbeta\ngamma");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 8);
assert!(offsets.entries[0].0.is_block(), "alpha");
assert!(
offsets.entries[1].0.as_table_anchor().is_some(),
"table anchor"
);
assert!(offsets.entries[2].0.is_block(), "cell (0,0)");
assert!(offsets.entries[3].0.is_block(), "cell (0,1)");
assert!(offsets.entries[4].0.is_block(), "cell (1,0)");
assert!(offsets.entries[5].0.is_block(), "cell (1,1)");
assert!(offsets.entries[6].0.is_block(), "beta");
assert!(offsets.entries[7].0.is_block(), "gamma");
assert_eq!(offsets.entries[0].1, 0);
assert_eq!(offsets.entries[1].1, 6);
assert_eq!(offsets.entries[2].1, 10);
assert_eq!(offsets.entries[3].1, 11);
assert_eq!(offsets.entries[4].1, 12);
assert_eq!(offsets.entries[5].1, 13);
assert_eq!(offsets.entries[6].1, 14);
assert_eq!(offsets.entries[7].1, 19);
assert_eq!(offsets.total_bytes(), 24);
}
#[test]
fn cell_text_edits_mirror_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("main").unwrap();
let cursor = doc.cursor_at(4);
let _table = cursor.insert_table(1, 2).unwrap();
let store = doc.rope_store_for_test();
let registered_block_ids: Vec<u64> = {
let offsets = store.block_offsets.read();
offsets
.entries
.iter()
.filter_map(|(m, _)| m.as_block())
.collect()
};
drop(store);
use text_document::FlowElement;
let mut first_cell_block_id: Option<u64> = None;
for elem in doc.flow() {
if let FlowElement::Table(t) = elem {
let snap = t.snapshot();
if let Some(cell) = snap.cells.first()
&& let Some(blk) = cell.blocks.first()
{
first_cell_block_id = Some(blk.block_id as u64);
}
break;
}
}
let cell_block_id = first_cell_block_id.expect("cell block not found in flow");
assert!(registered_block_ids.contains(&cell_block_id));
let cell_block = doc.block_by_id(cell_block_id as usize).expect("block");
let cell_pos = cell_block.position();
let cell_cursor = doc.cursor_at(cell_pos);
cell_cursor.insert_text("hi").unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert!(
rope.to_string().contains("hi"),
"rope should contain edited cell text, got {:?}",
rope.to_string()
);
}
#[test]
fn remove_table_strips_sentinel_from_rope() {
let doc = TextDocument::new();
doc.set_plain_text("alpha\nbeta").unwrap();
let cursor = doc.cursor_at(6);
let table = cursor.insert_table(1, 1).unwrap();
let table_id = table.id();
{
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert!(rope.to_string().contains('\u{FFFC}'));
}
let c2 = doc.cursor_at(0);
c2.remove_table(table_id).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "alpha\nbeta");
let offsets = store.block_offsets.read();
assert_eq!(offsets.entries.len(), 2);
assert!(offsets.entries.iter().all(|(m, _)| m.is_block()));
}
#[test]
fn set_html_table_cells_in_main_rope() {
let doc = TextDocument::new();
doc.set_html("<p>before</p><table><tr><td>cell</td></tr></table><p>after</p>")
.expect("set_html")
.wait()
.expect("wait");
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "before\n\u{FFFC}\ncell\nafter");
}
#[test]
fn paste_inline_fragment_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("hello world").unwrap();
let select = doc.cursor_at(6);
select.move_position(
text_document::MoveOperation::Right,
text_document::MoveMode::KeepAnchor,
5,
);
let frag = select.selection();
let paste = doc.cursor_at(0);
paste.insert_fragment(&frag).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
assert_eq!(rope.to_string(), "worldhello world");
}
#[test]
fn paste_multi_block_fragment_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("A1\nB2\nC3").unwrap();
let select = doc.cursor_at(3); select.move_position(
text_document::MoveOperation::Right,
text_document::MoveMode::KeepAnchor,
2,
);
let frag = select.selection();
let paste = doc.cursor_at(0);
paste.insert_fragment(&frag).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
let rope_text = rope.to_string();
assert_eq!(rope_text, "B2A1\nB2\nC3");
}
#[test]
fn paste_table_fragment_mirrors_to_rope() {
let src = TextDocument::new();
src.set_html("<p>before</p><table><tr><td>A</td><td>B</td></tr><tr><td>C</td><td>D</td></tr></table><p>after</p>")
.expect("set_html").wait().expect("wait");
use text_document::FlowElement;
let mut table_id_opt: Option<usize> = None;
for elem in src.flow() {
if let FlowElement::Table(t) = elem {
table_id_opt = Some(t.id());
break;
}
}
let table_id = table_id_opt.expect("table");
let cur = src.cursor_at(0);
cur.select_cell_range(table_id, 0, 0, 1, 1);
let frag = cur.selection();
let dst = TextDocument::new();
dst.set_plain_text("dst").unwrap();
let paste = dst.cursor_at(3);
paste.insert_fragment(&frag).unwrap();
let store = dst.rope_store_for_test();
let rope = store.rope.read();
let rope_text = rope.to_string();
assert!(
rope_text.contains('\u{FFFC}'),
"rope should contain table-anchor sentinel, got {:?}",
rope_text
);
for cell_text in &["A", "B", "C", "D"] {
assert!(
rope_text.contains(cell_text),
"rope should contain cell text {:?}, got {:?}",
cell_text,
rope_text
);
}
}
#[test]
fn paste_cross_block_fragment_mirrors_to_rope() {
let doc = TextDocument::new();
doc.set_plain_text("A1\nB2\nC3").unwrap();
let select = doc.cursor_at(1);
select.move_position(
text_document::MoveOperation::Right,
text_document::MoveMode::KeepAnchor,
6,
);
let frag = select.selection();
let paste = doc.cursor_at(8);
paste.insert_fragment(&frag).unwrap();
let store = doc.rope_store_for_test();
let rope = store.rope.read();
let rope_text = rope.to_string();
assert_eq!(rope_text, "A1\nB2\nC31\nB2\nC");
}