use ui::input::*;
#[test]
fn boundaries_step_over_a_flag_emoji() {
let text = "a๐ฏ๐ตb";
assert_eq!(next_boundary(text, 0), 1, "past 'a'");
assert_eq!(next_boundary(text, 1), 9, "over the whole flag");
assert_eq!(previous_boundary(text, 9), 1, "back to the flag's start");
assert_eq!(previous_boundary(text, 1), 0);
}
#[test]
fn boundaries_step_over_a_combining_mark() {
let text = "e\u{301}x";
assert_eq!(next_boundary(text, 0), 3, "over e+accent together");
assert_eq!(previous_boundary(text, 3), 0);
}
#[test]
fn boundaries_clamp_at_both_ends() {
let text = "hi";
assert_eq!(previous_boundary(text, 0), 0, "no underflow");
assert_eq!(next_boundary(text, text.len()), text.len(), "no overflow");
assert_eq!(next_boundary("", 0), 0, "empty is inert");
assert_eq!(previous_boundary("", 0), 0);
}
#[test]
fn utf16_offsets_round_trip() {
for text in ["ascii", "ๆฅๆฌ่ช", "a๐b", "๐ฏ๐ตx", "e\u{301}"] {
let mut byte = 0;
for ch in text.chars() {
let utf16 = offset_to_utf16(text, byte);
assert_eq!(
offset_from_utf16(text, utf16),
byte,
"{text:?} byte {byte} โ utf16 {utf16} โ back"
);
byte += ch.len_utf8();
}
assert_eq!(offset_to_utf16(text, byte), text.encode_utf16().count());
}
}
#[test]
fn typing_a_run_stays_one_undo_group() {
let mut last = None;
for offset in 1..6 {
assert!(
joins_group(last, EditKind::Insert, offset - 1) || last.is_none(),
"insert at {offset} should continue the run"
);
last = Some((EditKind::Insert, offset));
}
}
#[test]
fn the_first_edit_opens_a_group() {
assert!(!joins_group(None, EditKind::Insert, 0));
assert!(!joins_group(None, EditKind::Delete, 7));
}
#[test]
fn an_edit_elsewhere_starts_a_new_group() {
let last = Some((EditKind::Insert, 5));
assert!(
joins_group(last, EditKind::Insert, 5),
"same spot continues"
);
assert!(!joins_group(last, EditKind::Insert, 9), "moved away");
assert!(!joins_group(last, EditKind::Insert, 4), "even by one");
}
#[test]
fn changing_edit_kind_starts_a_new_group() {
assert!(!joins_group(
Some((EditKind::Insert, 5)),
EditKind::Delete,
5
));
assert!(!joins_group(
Some((EditKind::Delete, 5)),
EditKind::Insert,
5
));
}
#[test]
fn normalize_folds_crlf_whatever_the_shape() {
for shape in [Shape::Line, Shape::Rows(3), Shape::Grow { min: 1, max: 4 }] {
let out = normalize("a\r\nb\rc", shape);
assert!(!out.contains('\r'), "{shape:?} left a carriage return");
assert_eq!(out.len(), 5, "{shape:?} changed the character count");
}
}
#[test]
fn normalize_keeps_a_single_line_single() {
assert_eq!(normalize("a\nb", Shape::Line), "a b");
assert_eq!(normalize("a\r\nb", Shape::Line), "a b");
assert_eq!(normalize("one\ntwo\nthree", Shape::Line), "one two three");
}
#[test]
fn normalize_keeps_breaks_when_the_shape_has_room() {
assert_eq!(normalize("a\nb", Shape::Rows(2)), "a\nb");
assert_eq!(normalize("a\r\nb", Shape::Grow { min: 1, max: 3 }), "a\nb");
}
#[test]
fn line_bounds_on_one_line_are_the_whole_content() {
let text = "the quick brown";
for offset in [0, 4, text.len()] {
assert_eq!(line_start(text, offset), 0);
assert_eq!(line_end(text, offset), text.len());
}
assert_eq!(line_start("", 0), 0);
assert_eq!(line_end("", 0), 0);
}
#[test]
fn line_bounds_are_bounded_by_newlines() {
let text = "one\ntwo\nup";
assert_eq!(line_start(text, 0), 0, "first line starts at 0");
assert_eq!(line_end(text, 0), 3, "and ends before the newline");
assert_eq!(line_start(text, 5), 4, "past the newline, not on it");
assert_eq!(line_end(text, 5), 7);
assert_eq!(line_end(text, 8), text.len(), "last line runs to the end");
}
#[test]
fn line_bounds_at_a_newline_stay_put() {
let text = "one\ntwo";
assert_eq!(line_end(text, 3), 3, "already at the end, so no move");
assert_eq!(line_start(text, 3), 0);
assert_eq!(line_start(text, 4), 4, "start of the next line is itself");
}
#[test]
fn line_bounds_handle_empty_lines() {
let text = "a\n\nb";
assert_eq!(line_start(text, 2), 2, "the empty line between them");
assert_eq!(line_end(text, 2), 2);
}
#[test]
fn word_motion_walks_word_starts_and_ends() {
let text = "the quick brown";
assert_eq!(next_word_boundary(text, 0), 3, "end of 'the'");
assert_eq!(
next_word_boundary(text, 3),
9,
"skips the space, ends 'quick'"
);
assert_eq!(next_word_boundary(text, 6), 9, "from mid-word to its end");
assert_eq!(previous_word_boundary(text, 15), 10, "start of 'brown'");
assert_eq!(previous_word_boundary(text, 10), 4, "start of 'quick'");
assert_eq!(
previous_word_boundary(text, 6),
4,
"from mid-word to its start"
);
}
#[test]
fn word_motion_keeps_identifiers_whole_but_splits_paths() {
for identifier in ["foo.bar", "foo_bar"] {
assert_eq!(
next_word_boundary(identifier, 0),
identifier.len(),
"{identifier:?} is one word"
);
assert_eq!(previous_word_boundary(identifier, identifier.len()), 0);
}
let text = "path/to/file";
assert_eq!(next_word_boundary(text, 0), 4, "stops at the slash");
assert_eq!(next_word_boundary(text, 4), 7, "then 'to'");
assert_eq!(
previous_word_boundary(text, text.len()),
8,
"back to 'file'"
);
assert_eq!(next_word_boundary("a-b", 0), 1, "hyphen breaks");
}
#[test]
fn word_motion_clamps_and_survives_runs_of_separators() {
let text = " a b ";
assert_eq!(previous_word_boundary(text, 0), 0, "no underflow");
assert_eq!(
next_word_boundary(text, text.len()),
text.len(),
"no overflow"
);
assert_eq!(next_word_boundary(text, 0), 3, "over leading spaces to 'a'");
assert_eq!(previous_word_boundary(text, text.len()), 6, "back to 'b'");
assert_eq!(next_word_boundary(" ", 0), 3);
assert_eq!(previous_word_boundary(" ", 3), 0);
assert_eq!(next_word_boundary("", 0), 0);
}
#[test]
fn word_motion_lands_on_char_boundaries() {
for text in ["ๆฅๆฌ่ช ใฎ ใในใ", "a๐b c", "๐ฏ๐ต x"] {
for offset in 0..=text.len() {
if !text.is_char_boundary(offset) {
continue;
}
let prev = previous_word_boundary(text, offset);
let next = next_word_boundary(text, offset);
assert!(text.is_char_boundary(prev), "{text:?} prev {prev}");
assert!(text.is_char_boundary(next), "{text:?} next {next}");
assert!(prev <= offset, "prev never moves forward");
assert!(next >= offset, "next never moves back");
}
}
}
#[test]
fn utf16_offsets_count_surrogate_pairs_as_two() {
assert_eq!(offset_to_utf16("๐", 4), 2);
assert_eq!(offset_from_utf16("๐", 2), 4);
assert_eq!(offset_to_utf16("ๆฅ", 3), 1);
assert_eq!(offset_from_utf16("ๆฅ", 1), 3);
}