use gpui::{App, TestAppContext};
use markdown::SourceText;
const BEFORE: &str =
"# Title\n\nSome **bold** text and `code`.\n\n- one\n- two\n\n```rust\nfn main() {}\n```\n";
fn afters() -> Vec<String> {
let mut afters = vec![
BEFORE.replacen("bold", "bolder", 1),
BEFORE.replacen("text and", "text\nand", 1),
BEFORE.replacen("- one\n- two", "- one- two", 1),
format!("> {BEFORE}"),
format!("{BEFORE}tail"),
BEFORE.trim_end().to_owned(),
BEFORE.replacen("**bold** text and `code`.\n\n- one", "x", 1),
String::new(),
"a\nb".to_owned(),
BEFORE.replacen("# Title", "```\n# Title", 1),
BEFORE.replacen("Some", "Sömé", 1),
];
afters.push(BEFORE.to_owned());
afters
}
fn assert_matches(edited: &SourceText, after: &str, cx: &App) {
let fresh = SourceText::new(after, cx);
assert_eq!(edited.text(), after);
assert_eq!(edited.lines(), fresh.lines(), "lines of {after:?}");
for offset in 0..=after.len() + 1 {
assert_eq!(
edited.line_of(offset),
fresh.line_of(offset),
"line of {offset} in {after:?}"
);
}
}
#[gpui::test]
fn an_edit_answers_what_a_fresh_build_does(cx: &mut TestAppContext) {
cx.update(|cx| {
for after in afters() {
let mut edited = SourceText::new(BEFORE, cx);
let job = edited.edit(&after, cx);
assert_eq!(job.is_some(), after != BEFORE, "a job only for a change");
assert_matches(&edited, &after, cx);
if let Some(job) = job {
assert!(edited.set_spans(job.run()));
}
assert_eq!(
edited.spans(),
SourceText::new(after.as_str(), cx).spans(),
"spans of {after:?}"
);
}
});
}
#[gpui::test]
fn edits_in_a_row_stay_in_step(cx: &mut TestAppContext) {
cx.update(|cx| {
let mut edited = SourceText::new(BEFORE, cx);
for after in afters() {
edited.edit(&after, cx);
assert_matches(&edited, &after, cx);
}
});
}
#[gpui::test]
fn spans_before_the_job_skip_the_edit(cx: &mut TestAppContext) {
cx.update(|cx| {
let after = BEFORE.replacen("bold", "bolder", 1);
let mut edited = SourceText::new(BEFORE, cx);
let _job = edited.edit(&after, cx);
let spans = edited.spans().expect("markdown is coloured");
let written = BEFORE.find("bold").unwrap() + "bold".len();
for pair in spans.windows(2) {
assert!(pair[0].0.end <= pair[1].0.start, "{pair:?}");
}
for (range, _) in spans {
assert!(
range.start < range.end && range.end <= after.len(),
"{range:?}"
);
assert!(!(range.start..range.end).contains(&written), "{range:?}");
}
});
}
#[gpui::test]
fn a_job_for_an_older_text_is_refused(cx: &mut TestAppContext) {
cx.update(|cx| {
let mut edited = SourceText::new(BEFORE, cx);
let stale = edited.edit("first", cx).unwrap();
let current = edited.edit("second", cx).unwrap();
assert!(!edited.set_spans(stale.run()));
assert!(edited.set_spans(current.run()));
});
}