use text_document::{DocumentEvent, TextDocument};
fn doc_with_table(rows: usize, columns: usize) -> (TextDocument, usize) {
let doc = TextDocument::new();
doc.set_plain_text("before").unwrap();
let cursor = doc.cursor_at(doc.character_count());
let table = cursor.insert_table(rows, columns).unwrap();
let table_id = table.id();
let cursor = doc.cursor_at(doc.character_count());
cursor.insert_text("after").unwrap();
doc.poll_events();
(doc, table_id)
}
fn one_contents_changed(events: &[DocumentEvent], what: &str) -> (usize, usize, usize, usize) {
let found: Vec<_> = events
.iter()
.filter_map(|e| match e {
DocumentEvent::ContentsChanged {
position,
chars_removed,
chars_added,
blocks_affected,
} => Some((*position, *chars_removed, *chars_added, *blocks_affected)),
_ => None,
})
.collect();
assert_eq!(
found.len(),
1,
"{what} should report exactly one content change; events were {events:?}"
);
found[0]
}
fn reports_a_change(what: &str, edit: impl FnOnce(&TextDocument, usize)) {
let (doc, table_id) = doc_with_table(3, 3);
let before_revision = doc.content_revision();
edit(&doc, table_id);
let events = doc.poll_events();
let (_, _, _, blocks_affected) = one_contents_changed(&events, what);
assert_ne!(
doc.content_revision(),
before_revision,
"{what}: the revision must move, or a cache keyed on it serves stale layout"
);
assert!(
blocks_affected > 0,
"{what}: reported no affected blocks, which cannot be true of a structural edit"
);
}
#[test]
fn every_structural_table_edit_reports_a_content_change() {
reports_a_change("inserting a row", |doc, id| {
doc.cursor_at(0).insert_table_row(id, 1).unwrap()
});
reports_a_change("inserting a column", |doc, id| {
doc.cursor_at(0).insert_table_column(id, 1).unwrap()
});
reports_a_change("removing a row", |doc, id| {
doc.cursor_at(0).remove_table_row(id, 1).unwrap()
});
reports_a_change("removing a column", |doc, id| {
doc.cursor_at(0).remove_table_column(id, 1).unwrap()
});
reports_a_change("merging cells", |doc, id| {
doc.cursor_at(0).merge_table_cells(id, 0, 0, 0, 1).unwrap()
});
reports_a_change("removing the table", |doc, id| {
doc.cursor_at(0).remove_table(id).unwrap()
});
}
#[test]
fn inserting_and_removing_report_opposite_deltas() {
let (doc, table_id) = doc_with_table(3, 3);
doc.cursor_at(0).insert_table_row(table_id, 1).unwrap();
let (_, removed_on_insert, added_on_insert, _) =
one_contents_changed(&doc.poll_events(), "inserting a row");
assert!(
added_on_insert > 0 && removed_on_insert == 0,
"an insert only adds"
);
doc.cursor_at(0).remove_table_row(table_id, 1).unwrap();
let (_, removed_on_remove, added_on_remove, _) =
one_contents_changed(&doc.poll_events(), "removing a row");
assert!(
removed_on_remove > 0 && added_on_remove == 0,
"a removal only removes"
);
}
#[test]
fn a_table_edit_and_its_undo_report_through_the_same_mechanism() {
let (doc, table_id) = doc_with_table(3, 3);
doc.cursor_at(0).insert_table_row(table_id, 1).unwrap();
let (insert_pos, _, insert_added, insert_blocks) =
one_contents_changed(&doc.poll_events(), "inserting a row");
doc.undo().unwrap();
let (undo_pos, undo_removed, _, undo_blocks) =
one_contents_changed(&doc.poll_events(), "undoing the insert");
assert_eq!(undo_pos, insert_pos, "the same place");
assert_eq!(undo_removed, insert_added, "the same amount, the other way");
assert_eq!(undo_blocks, insert_blocks, "the same blocks");
}
#[test]
fn the_revision_moves_exactly_once_per_edit() {
let (doc, table_id) = doc_with_table(3, 3);
let before = doc.content_revision();
doc.cursor_at(0).insert_table_row(table_id, 1).unwrap();
assert_eq!(
doc.content_revision(),
before.wrapping_add(1),
"one edit, one revision"
);
}
#[test]
fn setting_a_table_format_reports_no_content_change() {
let (doc, table_id) = doc_with_table(2, 2);
let format = text_document::TableFormat::default();
doc.cursor_at(0)
.set_table_format(table_id, &format)
.unwrap();
assert!(
!doc.poll_events()
.iter()
.any(|e| matches!(e, DocumentEvent::ContentsChanged { .. })),
"setting a format is not a content change"
);
}