Another batch of tests

This commit is contained in:
Piotr Osiewicz 2023-12-01 18:43:44 +01:00
parent 4c4ec221af
commit 53f3f960d2
3 changed files with 1064 additions and 1072 deletions

View file

@ -1026,337 +1026,337 @@ fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
} }
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use crate::{ // use crate::{
display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap}, // display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
MultiBuffer, // MultiBuffer,
}; // };
use gpui::test::observe; // use gpui::test::observe;
use rand::prelude::*; // use rand::prelude::*;
use settings::SettingsStore; // use settings::SettingsStore;
use smol::stream::StreamExt; // use smol::stream::StreamExt;
use std::{cmp, env, num::NonZeroU32}; // use std::{cmp, env, num::NonZeroU32};
use text::Rope; // use text::Rope;
#[gpui::test(iterations = 100)] // #[gpui::test(iterations = 100)]
async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) { // async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
init_test(cx); // init_test(cx);
cx.background_executor.set_block_on_ticks(0..=50); // cx.background_executor.set_block_on_ticks(0..=50);
let operations = env::var("OPERATIONS") // let operations = env::var("OPERATIONS")
.map(|i| i.parse().expect("invalid `OPERATIONS` variable")) // .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10); // .unwrap_or(10);
let font_cache = cx.read(|cx| cx.font_cache().clone()); // let font_cache = cx.read(|cx| cx.font_cache().clone());
let font_system = cx.platform().fonts(); // let font_system = cx.platform().fonts();
let mut wrap_width = if rng.gen_bool(0.1) { // let mut wrap_width = if rng.gen_bool(0.1) {
None // None
} else { // } else {
Some(rng.gen_range(0.0..=1000.0)) // Some(rng.gen_range(0.0..=1000.0))
}; // };
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); // let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
let family_id = font_cache // let family_id = font_cache
.load_family(&["Helvetica"], &Default::default()) // .load_family(&["Helvetica"], &Default::default())
.unwrap(); // .unwrap();
let font_id = font_cache // let font_id = font_cache
.select_font(family_id, &Default::default()) // .select_font(family_id, &Default::default())
.unwrap(); // .unwrap();
let font_size = 14.0; // let font_size = 14.0;
log::info!("Tab size: {}", tab_size); // log::info!("Tab size: {}", tab_size);
log::info!("Wrap width: {:?}", wrap_width); // log::info!("Wrap width: {:?}", wrap_width);
let buffer = cx.update(|cx| { // let buffer = cx.update(|cx| {
if rng.gen() { // if rng.gen() {
MultiBuffer::build_random(&mut rng, cx) // MultiBuffer::build_random(&mut rng, cx)
} else { // } else {
let len = rng.gen_range(0..10); // let len = rng.gen_range(0..10);
let text = util::RandomCharIter::new(&mut rng) // let text = util::RandomCharIter::new(&mut rng)
.take(len) // .take(len)
.collect::<String>(); // .collect::<String>();
MultiBuffer::build_simple(&text, cx) // MultiBuffer::build_simple(&text, cx)
} // }
}); // });
let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); // let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
log::info!("Buffer text: {:?}", buffer_snapshot.text()); // log::info!("Buffer text: {:?}", buffer_snapshot.text());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); // let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
log::info!("InlayMap text: {:?}", inlay_snapshot.text()); // log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone()); // let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
log::info!("FoldMap text: {:?}", fold_snapshot.text()); // log::info!("FoldMap text: {:?}", fold_snapshot.text());
let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size); // let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
let tabs_snapshot = tab_map.set_max_expansion_column(32); // let tabs_snapshot = tab_map.set_max_expansion_column(32);
log::info!("TabMap text: {:?}", tabs_snapshot.text()); // log::info!("TabMap text: {:?}", tabs_snapshot.text());
let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system); // let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
let unwrapped_text = tabs_snapshot.text(); // let unwrapped_text = tabs_snapshot.text();
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); // let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
let (wrap_map, _) = // let (wrap_map, _) =
cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx)); // cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx));
let mut notifications = observe(&wrap_map, cx); // let mut notifications = observe(&wrap_map, cx);
if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) { // if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
notifications.next().await.unwrap(); // notifications.next().await.unwrap();
} // }
let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| { // let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| {
assert!(!map.is_rewrapping()); // assert!(!map.is_rewrapping());
map.sync(tabs_snapshot.clone(), Vec::new(), cx) // map.sync(tabs_snapshot.clone(), Vec::new(), cx)
}); // });
let actual_text = initial_snapshot.text(); // let actual_text = initial_snapshot.text();
assert_eq!( // assert_eq!(
actual_text, expected_text, // actual_text, expected_text,
"unwrapped text is: {:?}", // "unwrapped text is: {:?}",
unwrapped_text // unwrapped_text
); // );
log::info!("Wrapped text: {:?}", actual_text); // log::info!("Wrapped text: {:?}", actual_text);
let mut next_inlay_id = 0; // let mut next_inlay_id = 0;
let mut edits = Vec::new(); // let mut edits = Vec::new();
for _i in 0..operations { // for _i in 0..operations {
log::info!("{} ==============================================", _i); // log::info!("{} ==============================================", _i);
let mut buffer_edits = Vec::new(); // let mut buffer_edits = Vec::new();
match rng.gen_range(0..=100) { // match rng.gen_range(0..=100) {
0..=19 => { // 0..=19 => {
wrap_width = if rng.gen_bool(0.2) { // wrap_width = if rng.gen_bool(0.2) {
None // None
} else { // } else {
Some(rng.gen_range(0.0..=1000.0)) // Some(rng.gen_range(0.0..=1000.0))
}; // };
log::info!("Setting wrap width to {:?}", wrap_width); // log::info!("Setting wrap width to {:?}", wrap_width);
wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx)); // wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
} // }
20..=39 => { // 20..=39 => {
for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) { // for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
let (tabs_snapshot, tab_edits) = // let (tabs_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, tab_size); // tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (mut snapshot, wrap_edits) = // let (mut snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx)); // wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
snapshot.check_invariants(); // snapshot.check_invariants();
snapshot.verify_chunks(&mut rng); // snapshot.verify_chunks(&mut rng);
edits.push((snapshot, wrap_edits)); // edits.push((snapshot, wrap_edits));
} // }
} // }
40..=59 => { // 40..=59 => {
let (inlay_snapshot, inlay_edits) = // let (inlay_snapshot, inlay_edits) =
inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng); // inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); // let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tabs_snapshot, tab_edits) = // let (tabs_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, tab_size); // tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (mut snapshot, wrap_edits) = // let (mut snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx)); // wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
snapshot.check_invariants(); // snapshot.check_invariants();
snapshot.verify_chunks(&mut rng); // snapshot.verify_chunks(&mut rng);
edits.push((snapshot, wrap_edits)); // edits.push((snapshot, wrap_edits));
} // }
_ => { // _ => {
buffer.update(cx, |buffer, cx| { // buffer.update(cx, |buffer, cx| {
let subscription = buffer.subscribe(); // let subscription = buffer.subscribe();
let edit_count = rng.gen_range(1..=5); // let edit_count = rng.gen_range(1..=5);
buffer.randomly_mutate(&mut rng, edit_count, cx); // buffer.randomly_mutate(&mut rng, edit_count, cx);
buffer_snapshot = buffer.snapshot(cx); // buffer_snapshot = buffer.snapshot(cx);
buffer_edits.extend(subscription.consume()); // buffer_edits.extend(subscription.consume());
}); // });
} // }
} // }
log::info!("Buffer text: {:?}", buffer_snapshot.text()); // log::info!("Buffer text: {:?}", buffer_snapshot.text());
let (inlay_snapshot, inlay_edits) = // let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits); // inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
log::info!("InlayMap text: {:?}", inlay_snapshot.text()); // log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits); // let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
log::info!("FoldMap text: {:?}", fold_snapshot.text()); // log::info!("FoldMap text: {:?}", fold_snapshot.text());
let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size); // let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
log::info!("TabMap text: {:?}", tabs_snapshot.text()); // log::info!("TabMap text: {:?}", tabs_snapshot.text());
let unwrapped_text = tabs_snapshot.text(); // let unwrapped_text = tabs_snapshot.text();
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper); // let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
let (mut snapshot, wrap_edits) = // let (mut snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx)); // wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx));
snapshot.check_invariants(); // snapshot.check_invariants();
snapshot.verify_chunks(&mut rng); // snapshot.verify_chunks(&mut rng);
edits.push((snapshot, wrap_edits)); // edits.push((snapshot, wrap_edits));
if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) { // if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
log::info!("Waiting for wrapping to finish"); // log::info!("Waiting for wrapping to finish");
while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) { // while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
notifications.next().await.unwrap(); // notifications.next().await.unwrap();
} // }
wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty())); // wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
} // }
if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) { // if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
let (mut wrapped_snapshot, wrap_edits) = // let (mut wrapped_snapshot, wrap_edits) =
wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx)); // wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
let actual_text = wrapped_snapshot.text(); // let actual_text = wrapped_snapshot.text();
let actual_longest_row = wrapped_snapshot.longest_row(); // let actual_longest_row = wrapped_snapshot.longest_row();
log::info!("Wrapping finished: {:?}", actual_text); // log::info!("Wrapping finished: {:?}", actual_text);
wrapped_snapshot.check_invariants(); // wrapped_snapshot.check_invariants();
wrapped_snapshot.verify_chunks(&mut rng); // wrapped_snapshot.verify_chunks(&mut rng);
edits.push((wrapped_snapshot.clone(), wrap_edits)); // edits.push((wrapped_snapshot.clone(), wrap_edits));
assert_eq!( // assert_eq!(
actual_text, expected_text, // actual_text, expected_text,
"unwrapped text is: {:?}", // "unwrapped text is: {:?}",
unwrapped_text // unwrapped_text
); // );
let mut summary = TextSummary::default(); // let mut summary = TextSummary::default();
for (ix, item) in wrapped_snapshot // for (ix, item) in wrapped_snapshot
.transforms // .transforms
.items(&()) // .items(&())
.into_iter() // .into_iter()
.enumerate() // .enumerate()
{ // {
summary += &item.summary.output; // summary += &item.summary.output;
log::info!("{} summary: {:?}", ix, item.summary.output,); // log::info!("{} summary: {:?}", ix, item.summary.output,);
} // }
if tab_size.get() == 1 // if tab_size.get() == 1
|| !wrapped_snapshot // || !wrapped_snapshot
.tab_snapshot // .tab_snapshot
.fold_snapshot // .fold_snapshot
.text() // .text()
.contains('\t') // .contains('\t')
{ // {
let mut expected_longest_rows = Vec::new(); // let mut expected_longest_rows = Vec::new();
let mut longest_line_len = -1; // let mut longest_line_len = -1;
for (row, line) in expected_text.split('\n').enumerate() { // for (row, line) in expected_text.split('\n').enumerate() {
let line_char_count = line.chars().count() as isize; // let line_char_count = line.chars().count() as isize;
if line_char_count > longest_line_len { // if line_char_count > longest_line_len {
expected_longest_rows.clear(); // expected_longest_rows.clear();
longest_line_len = line_char_count; // longest_line_len = line_char_count;
} // }
if line_char_count >= longest_line_len { // if line_char_count >= longest_line_len {
expected_longest_rows.push(row as u32); // expected_longest_rows.push(row as u32);
} // }
} // }
assert!( // assert!(
expected_longest_rows.contains(&actual_longest_row), // expected_longest_rows.contains(&actual_longest_row),
"incorrect longest row {}. expected {:?} with length {}", // "incorrect longest row {}. expected {:?} with length {}",
actual_longest_row, // actual_longest_row,
expected_longest_rows, // expected_longest_rows,
longest_line_len, // longest_line_len,
) // )
} // }
} // }
} // }
let mut initial_text = Rope::from(initial_snapshot.text().as_str()); // let mut initial_text = Rope::from(initial_snapshot.text().as_str());
for (snapshot, patch) in edits { // for (snapshot, patch) in edits {
let snapshot_text = Rope::from(snapshot.text().as_str()); // let snapshot_text = Rope::from(snapshot.text().as_str());
for edit in &patch { // for edit in &patch {
let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0)); // let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
let old_end = initial_text.point_to_offset(cmp::min( // let old_end = initial_text.point_to_offset(cmp::min(
Point::new(edit.new.start + edit.old.len() as u32, 0), // Point::new(edit.new.start + edit.old.len() as u32, 0),
initial_text.max_point(), // initial_text.max_point(),
)); // ));
let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0)); // let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0));
let new_end = snapshot_text.point_to_offset(cmp::min( // let new_end = snapshot_text.point_to_offset(cmp::min(
Point::new(edit.new.end, 0), // Point::new(edit.new.end, 0),
snapshot_text.max_point(), // snapshot_text.max_point(),
)); // ));
let new_text = snapshot_text // let new_text = snapshot_text
.chunks_in_range(new_start..new_end) // .chunks_in_range(new_start..new_end)
.collect::<String>(); // .collect::<String>();
initial_text.replace(old_start..old_end, &new_text); // initial_text.replace(old_start..old_end, &new_text);
} // }
assert_eq!(initial_text.to_string(), snapshot_text.to_string()); // assert_eq!(initial_text.to_string(), snapshot_text.to_string());
} // }
if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) { // if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
log::info!("Waiting for wrapping to finish"); // log::info!("Waiting for wrapping to finish");
while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) { // while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
notifications.next().await.unwrap(); // notifications.next().await.unwrap();
} // }
} // }
wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty())); // wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
} // }
fn init_test(cx: &mut gpui::TestAppContext) { // fn init_test(cx: &mut gpui::TestAppContext) {
cx.foreground_executor().forbid_parking(); // cx.foreground_executor().forbid_parking();
cx.update(|cx| { // cx.update(|cx| {
cx.set_global(SettingsStore::test(cx)); // cx.set_global(SettingsStore::test(cx));
theme::init((), cx); // theme::init((), cx);
}); // });
} // }
fn wrap_text( // fn wrap_text(
unwrapped_text: &str, // unwrapped_text: &str,
wrap_width: Option<f32>, // wrap_width: Option<f32>,
line_wrapper: &mut LineWrapper, // line_wrapper: &mut LineWrapper,
) -> String { // ) -> String {
if let Some(wrap_width) = wrap_width { // if let Some(wrap_width) = wrap_width {
let mut wrapped_text = String::new(); // let mut wrapped_text = String::new();
for (row, line) in unwrapped_text.split('\n').enumerate() { // for (row, line) in unwrapped_text.split('\n').enumerate() {
if row > 0 { // if row > 0 {
wrapped_text.push('\n') // wrapped_text.push('\n')
} // }
let mut prev_ix = 0; // let mut prev_ix = 0;
for boundary in line_wrapper.wrap_line(line, wrap_width) { // for boundary in line_wrapper.wrap_line(line, wrap_width) {
wrapped_text.push_str(&line[prev_ix..boundary.ix]); // wrapped_text.push_str(&line[prev_ix..boundary.ix]);
wrapped_text.push('\n'); // wrapped_text.push('\n');
wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize)); // wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
prev_ix = boundary.ix; // prev_ix = boundary.ix;
} // }
wrapped_text.push_str(&line[prev_ix..]); // wrapped_text.push_str(&line[prev_ix..]);
} // }
wrapped_text // wrapped_text
} else { // } else {
unwrapped_text.to_string() // unwrapped_text.to_string()
} // }
} // }
impl WrapSnapshot { // impl WrapSnapshot {
pub fn text(&self) -> String { // pub fn text(&self) -> String {
self.text_chunks(0).collect() // self.text_chunks(0).collect()
} // }
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> { // pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
self.chunks( // self.chunks(
wrap_row..self.max_point().row() + 1, // wrap_row..self.max_point().row() + 1,
false, // false,
Highlights::default(), // Highlights::default(),
) // )
.map(|h| h.text) // .map(|h| h.text)
} // }
fn verify_chunks(&mut self, rng: &mut impl Rng) { // fn verify_chunks(&mut self, rng: &mut impl Rng) {
for _ in 0..5 { // for _ in 0..5 {
let mut end_row = rng.gen_range(0..=self.max_point().row()); // let mut end_row = rng.gen_range(0..=self.max_point().row());
let start_row = rng.gen_range(0..=end_row); // let start_row = rng.gen_range(0..=end_row);
end_row += 1; // end_row += 1;
let mut expected_text = self.text_chunks(start_row).collect::<String>(); // let mut expected_text = self.text_chunks(start_row).collect::<String>();
if expected_text.ends_with('\n') { // if expected_text.ends_with('\n') {
expected_text.push('\n'); // expected_text.push('\n');
} // }
let mut expected_text = expected_text // let mut expected_text = expected_text
.lines() // .lines()
.take((end_row - start_row) as usize) // .take((end_row - start_row) as usize)
.collect::<Vec<_>>() // .collect::<Vec<_>>()
.join("\n"); // .join("\n");
if end_row <= self.max_point().row() { // if end_row <= self.max_point().row() {
expected_text.push('\n'); // expected_text.push('\n');
} // }
let actual_text = self // let actual_text = self
.chunks(start_row..end_row, true, Highlights::default()) // .chunks(start_row..end_row, true, Highlights::default())
.map(|c| c.text) // .map(|c| c.text)
.collect::<String>(); // .collect::<String>();
assert_eq!( // assert_eq!(
expected_text, // expected_text,
actual_text, // actual_text,
"chunks != highlighted_chunks for rows {:?}", // "chunks != highlighted_chunks for rows {:?}",
start_row..end_row // start_row..end_row
); // );
} // }
} // }
} // }
} // }

View file

@ -2401,346 +2401,346 @@ pub mod tests {
}); });
} }
#[gpui::test(iterations = 10)] // #[gpui::test(iterations = 10)]
async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) { // async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) {
init_test(cx, |settings| { // init_test(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings { // settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true, // enabled: true,
show_type_hints: true, // show_type_hints: true,
show_parameter_hints: true, // show_parameter_hints: true,
show_other_hints: true, // show_other_hints: true,
}) // })
}); // });
let mut language = Language::new( // let mut language = Language::new(
LanguageConfig { // LanguageConfig {
name: "Rust".into(), // name: "Rust".into(),
path_suffixes: vec!["rs".to_string()], // path_suffixes: vec!["rs".to_string()],
..Default::default() // ..Default::default()
}, // },
Some(tree_sitter_rust::language()), // Some(tree_sitter_rust::language()),
); // );
let mut fake_servers = language // let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter { // .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
capabilities: lsp::ServerCapabilities { // capabilities: lsp::ServerCapabilities {
inlay_hint_provider: Some(lsp::OneOf::Left(true)), // inlay_hint_provider: Some(lsp::OneOf::Left(true)),
..Default::default() // ..Default::default()
}, // },
..Default::default() // ..Default::default()
})) // }))
.await; // .await;
let language = Arc::new(language); // let language = Arc::new(language);
let fs = FakeFs::new(cx.background_executor.clone()); // let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree( // fs.insert_tree(
"/a", // "/a",
json!({ // json!({
"main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")), // "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
"other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")), // "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
}), // }),
) // )
.await; // .await;
let project = Project::test(fs, ["/a".as_ref()], cx).await; // let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| { // project.update(cx, |project, _| {
project.languages().add(Arc::clone(&language)) // project.languages().add(Arc::clone(&language))
}); // });
let worktree_id = project.update(cx, |project, cx| { // let worktree_id = project.update(cx, |project, cx| {
project.worktrees().next().unwrap().read(cx).id() // project.worktrees().next().unwrap().read(cx).id()
}); // });
let buffer_1 = project // let buffer_1 = project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.open_buffer((worktree_id, "main.rs"), cx) // project.open_buffer((worktree_id, "main.rs"), cx)
}) // })
.await // .await
.unwrap(); // .unwrap();
let buffer_2 = project // let buffer_2 = project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.open_buffer((worktree_id, "other.rs"), cx) // project.open_buffer((worktree_id, "other.rs"), cx)
}) // })
.await // .await
.unwrap(); // .unwrap();
let multibuffer = cx.build_model(|cx| { // let multibuffer = cx.build_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); // let mut multibuffer = MultiBuffer::new(0);
multibuffer.push_excerpts( // multibuffer.push_excerpts(
buffer_1.clone(), // buffer_1.clone(),
[ // [
ExcerptRange { // ExcerptRange {
context: Point::new(0, 0)..Point::new(2, 0), // context: Point::new(0, 0)..Point::new(2, 0),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(4, 0)..Point::new(11, 0), // context: Point::new(4, 0)..Point::new(11, 0),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(22, 0)..Point::new(33, 0), // context: Point::new(22, 0)..Point::new(33, 0),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(44, 0)..Point::new(55, 0), // context: Point::new(44, 0)..Point::new(55, 0),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(56, 0)..Point::new(66, 0), // context: Point::new(56, 0)..Point::new(66, 0),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(67, 0)..Point::new(77, 0), // context: Point::new(67, 0)..Point::new(77, 0),
primary: None, // primary: None,
}, // },
], // ],
cx, // cx,
); // );
multibuffer.push_excerpts( // multibuffer.push_excerpts(
buffer_2.clone(), // buffer_2.clone(),
[ // [
ExcerptRange { // ExcerptRange {
context: Point::new(0, 1)..Point::new(2, 1), // context: Point::new(0, 1)..Point::new(2, 1),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(4, 1)..Point::new(11, 1), // context: Point::new(4, 1)..Point::new(11, 1),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(22, 1)..Point::new(33, 1), // context: Point::new(22, 1)..Point::new(33, 1),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(44, 1)..Point::new(55, 1), // context: Point::new(44, 1)..Point::new(55, 1),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(56, 1)..Point::new(66, 1), // context: Point::new(56, 1)..Point::new(66, 1),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(67, 1)..Point::new(77, 1), // context: Point::new(67, 1)..Point::new(77, 1),
primary: None, // primary: None,
}, // },
], // ],
cx, // cx,
); // );
multibuffer // multibuffer
}); // });
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
let editor = // let editor =
cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx)); // cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
let editor_edited = Arc::new(AtomicBool::new(false)); // let editor_edited = Arc::new(AtomicBool::new(false));
let fake_server = fake_servers.next().await.unwrap(); // let fake_server = fake_servers.next().await.unwrap();
let closure_editor_edited = Arc::clone(&editor_edited); // let closure_editor_edited = Arc::clone(&editor_edited);
fake_server // fake_server
.handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| { // .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
let task_editor_edited = Arc::clone(&closure_editor_edited); // let task_editor_edited = Arc::clone(&closure_editor_edited);
async move { // async move {
let hint_text = if params.text_document.uri // let hint_text = if params.text_document.uri
== lsp::Url::from_file_path("/a/main.rs").unwrap() // == lsp::Url::from_file_path("/a/main.rs").unwrap()
{ // {
"main hint" // "main hint"
} else if params.text_document.uri // } else if params.text_document.uri
== lsp::Url::from_file_path("/a/other.rs").unwrap() // == lsp::Url::from_file_path("/a/other.rs").unwrap()
{ // {
"other hint" // "other hint"
} else { // } else {
panic!("unexpected uri: {:?}", params.text_document.uri); // panic!("unexpected uri: {:?}", params.text_document.uri);
}; // };
// one hint per excerpt // // one hint per excerpt
let positions = [ // let positions = [
lsp::Position::new(0, 2), // lsp::Position::new(0, 2),
lsp::Position::new(4, 2), // lsp::Position::new(4, 2),
lsp::Position::new(22, 2), // lsp::Position::new(22, 2),
lsp::Position::new(44, 2), // lsp::Position::new(44, 2),
lsp::Position::new(56, 2), // lsp::Position::new(56, 2),
lsp::Position::new(67, 2), // lsp::Position::new(67, 2),
]; // ];
let out_of_range_hint = lsp::InlayHint { // let out_of_range_hint = lsp::InlayHint {
position: lsp::Position::new( // position: lsp::Position::new(
params.range.start.line + 99, // params.range.start.line + 99,
params.range.start.character + 99, // params.range.start.character + 99,
), // ),
label: lsp::InlayHintLabel::String( // label: lsp::InlayHintLabel::String(
"out of excerpt range, should be ignored".to_string(), // "out of excerpt range, should be ignored".to_string(),
), // ),
kind: None, // kind: None,
text_edits: None, // text_edits: None,
tooltip: None, // tooltip: None,
padding_left: None, // padding_left: None,
padding_right: None, // padding_right: None,
data: None, // data: None,
}; // };
let edited = task_editor_edited.load(Ordering::Acquire); // let edited = task_editor_edited.load(Ordering::Acquire);
Ok(Some( // Ok(Some(
std::iter::once(out_of_range_hint) // std::iter::once(out_of_range_hint)
.chain(positions.into_iter().enumerate().map(|(i, position)| { // .chain(positions.into_iter().enumerate().map(|(i, position)| {
lsp::InlayHint { // lsp::InlayHint {
position, // position,
label: lsp::InlayHintLabel::String(format!( // label: lsp::InlayHintLabel::String(format!(
"{hint_text}{} #{i}", // "{hint_text}{} #{i}",
if edited { "(edited)" } else { "" }, // if edited { "(edited)" } else { "" },
)), // )),
kind: None, // kind: None,
text_edits: None, // text_edits: None,
tooltip: None, // tooltip: None,
padding_left: None, // padding_left: None,
padding_right: None, // padding_right: None,
data: None, // data: None,
} // }
})) // }))
.collect(), // .collect(),
)) // ))
} // }
}) // })
.next() // .next()
.await; // .await;
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
let expected_hints = vec![ // let expected_hints = vec![
"main hint #0".to_string(), // "main hint #0".to_string(),
"main hint #1".to_string(), // "main hint #1".to_string(),
"main hint #2".to_string(), // "main hint #2".to_string(),
"main hint #3".to_string(), // "main hint #3".to_string(),
// todo!() there used to be no these hints, but new gpui2 presumably scrolls a bit farther // // todo!() there used to be no these hints, but new gpui2 presumably scrolls a bit farther
// (or renders less?) note that tests below pass // // (or renders less?) note that tests below pass
"main hint #4".to_string(), // "main hint #4".to_string(),
"main hint #5".to_string(), // "main hint #5".to_string(),
]; // ];
assert_eq!( // assert_eq!(
expected_hints, // expected_hints,
cached_hint_labels(editor), // cached_hint_labels(editor),
"When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints" // "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
); // );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison"); // assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
}); // });
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::Next), cx, |s| { // editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges([Point::new(4, 0)..Point::new(4, 0)]) // s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
}); // });
editor.change_selections(Some(Autoscroll::Next), cx, |s| { // editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges([Point::new(22, 0)..Point::new(22, 0)]) // s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
}); // });
editor.change_selections(Some(Autoscroll::Next), cx, |s| { // editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges([Point::new(50, 0)..Point::new(50, 0)]) // s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
}); // });
}); // });
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
let expected_hints = vec![ // let expected_hints = vec![
"main hint #0".to_string(), // "main hint #0".to_string(),
"main hint #1".to_string(), // "main hint #1".to_string(),
"main hint #2".to_string(), // "main hint #2".to_string(),
"main hint #3".to_string(), // "main hint #3".to_string(),
"main hint #4".to_string(), // "main hint #4".to_string(),
"main hint #5".to_string(), // "main hint #5".to_string(),
"other hint #0".to_string(), // "other hint #0".to_string(),
"other hint #1".to_string(), // "other hint #1".to_string(),
"other hint #2".to_string(), // "other hint #2".to_string(),
]; // ];
assert_eq!(expected_hints, cached_hint_labels(editor), // assert_eq!(expected_hints, cached_hint_labels(editor),
"With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits"); // "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), // assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
"Due to every excerpt having one hint, we update cache per new excerpt scrolled"); // "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
}); // });
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::Next), cx, |s| { // editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges([Point::new(100, 0)..Point::new(100, 0)]) // s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
}); // });
}); // });
cx.executor().advance_clock(Duration::from_millis( // cx.executor().advance_clock(Duration::from_millis(
INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100, // INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
)); // ));
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
let last_scroll_update_version = editor.update(cx, |editor, cx| { // let last_scroll_update_version = editor.update(cx, |editor, cx| {
let expected_hints = vec![ // let expected_hints = vec![
"main hint #0".to_string(), // "main hint #0".to_string(),
"main hint #1".to_string(), // "main hint #1".to_string(),
"main hint #2".to_string(), // "main hint #2".to_string(),
"main hint #3".to_string(), // "main hint #3".to_string(),
"main hint #4".to_string(), // "main hint #4".to_string(),
"main hint #5".to_string(), // "main hint #5".to_string(),
"other hint #0".to_string(), // "other hint #0".to_string(),
"other hint #1".to_string(), // "other hint #1".to_string(),
"other hint #2".to_string(), // "other hint #2".to_string(),
"other hint #3".to_string(), // "other hint #3".to_string(),
"other hint #4".to_string(), // "other hint #4".to_string(),
"other hint #5".to_string(), // "other hint #5".to_string(),
]; // ];
assert_eq!(expected_hints, cached_hint_labels(editor), // assert_eq!(expected_hints, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, all hints for all excerpts should be fetched"); // "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len()); // assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
expected_hints.len() // expected_hints.len()
}).unwrap(); // }).unwrap();
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::Next), cx, |s| { // editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges([Point::new(4, 0)..Point::new(4, 0)]) // s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
}); // });
}); // });
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
let expected_hints = vec![ // let expected_hints = vec![
"main hint #0".to_string(), // "main hint #0".to_string(),
"main hint #1".to_string(), // "main hint #1".to_string(),
"main hint #2".to_string(), // "main hint #2".to_string(),
"main hint #3".to_string(), // "main hint #3".to_string(),
"main hint #4".to_string(), // "main hint #4".to_string(),
"main hint #5".to_string(), // "main hint #5".to_string(),
"other hint #0".to_string(), // "other hint #0".to_string(),
"other hint #1".to_string(), // "other hint #1".to_string(),
"other hint #2".to_string(), // "other hint #2".to_string(),
"other hint #3".to_string(), // "other hint #3".to_string(),
"other hint #4".to_string(), // "other hint #4".to_string(),
"other hint #5".to_string(), // "other hint #5".to_string(),
]; // ];
assert_eq!(expected_hints, cached_hint_labels(editor), // assert_eq!(expected_hints, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, further scrolls up should not bring more hints"); // "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer"); // assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
}); // });
editor_edited.store(true, Ordering::Release); // editor_edited.store(true, Ordering::Release);
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| { // editor.change_selections(None, cx, |s| {
s.select_ranges([Point::new(56, 0)..Point::new(56, 0)]) // s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
}); // });
editor.handle_input("++++more text++++", cx); // editor.handle_input("++++more text++++", cx);
}); // });
cx.executor().run_until_parked(); // cx.executor().run_until_parked();
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
let expected_hints = vec![ // let expected_hints = vec![
"main hint(edited) #0".to_string(), // "main hint(edited) #0".to_string(),
"main hint(edited) #1".to_string(), // "main hint(edited) #1".to_string(),
"main hint(edited) #2".to_string(), // "main hint(edited) #2".to_string(),
"main hint(edited) #3".to_string(), // "main hint(edited) #3".to_string(),
"main hint(edited) #4".to_string(), // "main hint(edited) #4".to_string(),
"main hint(edited) #5".to_string(), // "main hint(edited) #5".to_string(),
"other hint(edited) #0".to_string(), // "other hint(edited) #0".to_string(),
"other hint(edited) #1".to_string(), // "other hint(edited) #1".to_string(),
]; // ];
assert_eq!( // assert_eq!(
expected_hints, // expected_hints,
cached_hint_labels(editor), // cached_hint_labels(editor),
"After multibuffer edit, editor gets scolled back to the last selection; \ // "After multibuffer edit, editor gets scolled back to the last selection; \
all hints should be invalidated and requeried for all of its visible excerpts" // all hints should be invalidated and requeried for all of its visible excerpts"
); // );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
let current_cache_version = editor.inlay_hint_cache().version; // let current_cache_version = editor.inlay_hint_cache().version;
let minimum_expected_version = last_scroll_update_version + expected_hints.len(); // let minimum_expected_version = last_scroll_update_version + expected_hints.len();
assert!( // assert!(
current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1, // current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
"Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update" // "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
); // );
}); // });
} // }
#[gpui::test] #[gpui::test]
async fn test_excerpts_removed(cx: &mut gpui::TestAppContext) { async fn test_excerpts_removed(cx: &mut gpui::TestAppContext) {

View file

@ -452,483 +452,475 @@ pub fn split_display_range_by_lines(
result result
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use super::*; use super::*;
// use crate::{ use crate::{
// display_map::Inlay, display_map::Inlay,
// test::{}, test::{editor_test_context::EditorTestContext, marked_display_snapshot},
// Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer, Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
// }; };
// use project::Project; use gpui::{font, Context as _};
// use settings::SettingsStore; use project::Project;
// use util::post_inc; use settings::SettingsStore;
use util::post_inc;
// #[gpui::test] #[gpui::test]
// fn test_previous_word_start(cx: &mut gpui::AppContext) { fn test_previous_word_start(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) { fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// previous_word_start(&snapshot, display_points[1]), previous_word_start(&snapshot, display_points[1]),
// display_points[0] display_points[0]
// ); );
// } }
// assert("\nˇ ˇlorem", cx); assert("\nˇ ˇlorem", cx);
// assert("ˇ\nˇ lorem", cx); assert("ˇ\nˇ lorem", cx);
// assert(" ˇloremˇ", cx); assert(" ˇloremˇ", cx);
// assert("ˇ ˇlorem", cx); assert("ˇ ˇlorem", cx);
// assert(" ˇlorˇem", cx); assert(" ˇlorˇem", cx);
// assert("\nlorem\nˇ ˇipsum", cx); assert("\nlorem\nˇ ˇipsum", cx);
// assert("\n\nˇ\nˇ", cx); assert("\n\nˇ\nˇ", cx);
// assert(" ˇlorem ˇipsum", cx); assert(" ˇlorem ˇipsum", cx);
// assert("loremˇ-ˇipsum", cx); assert("loremˇ-ˇipsum", cx);
// assert("loremˇ-#$@ˇipsum", cx); assert("loremˇ-#$@ˇipsum", cx);
// assert("ˇlorem_ˇipsum", cx); assert("ˇlorem_ˇipsum", cx);
// assert(" ˇdefγˇ", cx); assert(" ˇdefγˇ", cx);
// assert(" ˇbcΔˇ", cx); assert(" ˇbcΔˇ", cx);
// assert(" abˇ——ˇcd", cx); assert(" abˇ——ˇcd", cx);
// } }
// #[gpui::test] #[gpui::test]
// fn test_previous_subword_start(cx: &mut gpui::AppContext) { fn test_previous_subword_start(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) { fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// previous_subword_start(&snapshot, display_points[1]), previous_subword_start(&snapshot, display_points[1]),
// display_points[0] display_points[0]
// ); );
// } }
// // Subword boundaries are respected // Subword boundaries are respected
// assert("lorem_ˇipˇsum", cx); assert("lorem_ˇipˇsum", cx);
// assert("lorem_ˇipsumˇ", cx); assert("lorem_ˇipsumˇ", cx);
// assert("ˇlorem_ˇipsum", cx); assert("ˇlorem_ˇipsum", cx);
// assert("lorem_ˇipsum_ˇdolor", cx); assert("lorem_ˇipsum_ˇdolor", cx);
// assert("loremˇIpˇsum", cx); assert("loremˇIpˇsum", cx);
// assert("loremˇIpsumˇ", cx); assert("loremˇIpsumˇ", cx);
// // Word boundaries are still respected // Word boundaries are still respected
// assert("\nˇ ˇlorem", cx); assert("\nˇ ˇlorem", cx);
// assert(" ˇloremˇ", cx); assert(" ˇloremˇ", cx);
// assert(" ˇlorˇem", cx); assert(" ˇlorˇem", cx);
// assert("\nlorem\nˇ ˇipsum", cx); assert("\nlorem\nˇ ˇipsum", cx);
// assert("\n\nˇ\nˇ", cx); assert("\n\nˇ\nˇ", cx);
// assert(" ˇlorem ˇipsum", cx); assert(" ˇlorem ˇipsum", cx);
// assert("loremˇ-ˇipsum", cx); assert("loremˇ-ˇipsum", cx);
// assert("loremˇ-#$@ˇipsum", cx); assert("loremˇ-#$@ˇipsum", cx);
// assert(" ˇdefγˇ", cx); assert(" ˇdefγˇ", cx);
// assert(" bcˇΔˇ", cx); assert(" bcˇΔˇ", cx);
// assert(" ˇbcδˇ", cx); assert(" ˇbcδˇ", cx);
// assert(" abˇ——ˇcd", cx); assert(" abˇ——ˇcd", cx);
// } }
// #[gpui::test] #[gpui::test]
// fn test_find_preceding_boundary(cx: &mut gpui::AppContext) { fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert( fn assert(
// marked_text: &str, marked_text: &str,
// cx: &mut gpui::AppContext, cx: &mut gpui::AppContext,
// is_boundary: impl FnMut(char, char) -> bool, is_boundary: impl FnMut(char, char) -> bool,
// ) { ) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// find_preceding_boundary( find_preceding_boundary(
// &snapshot, &snapshot,
// display_points[1], display_points[1],
// FindRange::MultiLine, FindRange::MultiLine,
// is_boundary is_boundary
// ), ),
// display_points[0] display_points[0]
// ); );
// } }
// assert("abcˇdef\ngh\nijˇk", cx, |left, right| { assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
// left == 'c' && right == 'd' left == 'c' && right == 'd'
// }); });
// assert("abcdef\nˇgh\nijˇk", cx, |left, right| { assert("abcdef\nˇgh\nijˇk", cx, |left, right| {
// left == '\n' && right == 'g' left == '\n' && right == 'g'
// }); });
// let mut line_count = 0; let mut line_count = 0;
// assert("abcdef\nˇgh\nijˇk", cx, |left, _| { assert("abcdef\nˇgh\nijˇk", cx, |left, _| {
// if left == '\n' { if left == '\n' {
// line_count += 1; line_count += 1;
// line_count == 2 line_count == 2
// } else { } else {
// false false
// } }
// }); });
// } }
// #[gpui::test] #[gpui::test]
// fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) { fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// let input_text = "abcdefghijklmnopqrstuvwxys"; let input_text = "abcdefghijklmnopqrstuvwxys";
// let family_id = cx let font = font("Helvetica");
// .font_cache() let font_size = px(14.0);
// .load_family(&["Helvetica"], &Default::default()) let buffer = MultiBuffer::build_simple(input_text, cx);
// .unwrap(); let buffer_snapshot = buffer.read(cx).snapshot(cx);
// let font_id = cx let display_map =
// .font_cache() cx.build_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
// .select_font(family_id, &Default::default())
// .unwrap();
// let font_size = 14.0;
// let buffer = MultiBuffer::build_simple(input_text, cx);
// let buffer_snapshot = buffer.read(cx).snapshot(cx);
// let display_map =
// cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
// // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
// let mut id = 0; let mut id = 0;
// let inlays = (0..buffer_snapshot.len()) let inlays = (0..buffer_snapshot.len())
// .map(|offset| { .map(|offset| {
// [ [
// Inlay { Inlay {
// id: InlayId::Suggestion(post_inc(&mut id)), id: InlayId::Suggestion(post_inc(&mut id)),
// position: buffer_snapshot.anchor_at(offset, Bias::Left), position: buffer_snapshot.anchor_at(offset, Bias::Left),
// text: format!("test").into(), text: format!("test").into(),
// }, },
// Inlay { Inlay {
// id: InlayId::Suggestion(post_inc(&mut id)), id: InlayId::Suggestion(post_inc(&mut id)),
// position: buffer_snapshot.anchor_at(offset, Bias::Right), position: buffer_snapshot.anchor_at(offset, Bias::Right),
// text: format!("test").into(), text: format!("test").into(),
// }, },
// Inlay { Inlay {
// id: InlayId::Hint(post_inc(&mut id)), id: InlayId::Hint(post_inc(&mut id)),
// position: buffer_snapshot.anchor_at(offset, Bias::Left), position: buffer_snapshot.anchor_at(offset, Bias::Left),
// text: format!("test").into(), text: format!("test").into(),
// }, },
// Inlay { Inlay {
// id: InlayId::Hint(post_inc(&mut id)), id: InlayId::Hint(post_inc(&mut id)),
// position: buffer_snapshot.anchor_at(offset, Bias::Right), position: buffer_snapshot.anchor_at(offset, Bias::Right),
// text: format!("test").into(), text: format!("test").into(),
// }, },
// ] ]
// }) })
// .flatten() .flatten()
// .collect(); .collect();
// let snapshot = display_map.update(cx, |map, cx| { let snapshot = display_map.update(cx, |map, cx| {
// map.splice_inlays(Vec::new(), inlays, cx); map.splice_inlays(Vec::new(), inlays, cx);
// map.snapshot(cx) map.snapshot(cx)
// }); });
// assert_eq!( assert_eq!(
// find_preceding_boundary( find_preceding_boundary(
// &snapshot, &snapshot,
// buffer_snapshot.len().to_display_point(&snapshot), buffer_snapshot.len().to_display_point(&snapshot),
// FindRange::MultiLine, FindRange::MultiLine,
// |left, _| left == 'e', |left, _| left == 'e',
// ), ),
// snapshot snapshot
// .buffer_snapshot .buffer_snapshot
// .offset_to_point(5) .offset_to_point(5)
// .to_display_point(&snapshot), .to_display_point(&snapshot),
// "Should not stop at inlays when looking for boundaries" "Should not stop at inlays when looking for boundaries"
// ); );
// } }
// #[gpui::test] #[gpui::test]
// fn test_next_word_end(cx: &mut gpui::AppContext) { fn test_next_word_end(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) { fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// next_word_end(&snapshot, display_points[0]), next_word_end(&snapshot, display_points[0]),
// display_points[1] display_points[1]
// ); );
// } }
// assert("\nˇ loremˇ", cx); assert("\nˇ loremˇ", cx);
// assert(" ˇloremˇ", cx); assert(" ˇloremˇ", cx);
// assert(" lorˇemˇ", cx); assert(" lorˇemˇ", cx);
// assert(" loremˇ ˇ\nipsum\n", cx); assert(" loremˇ ˇ\nipsum\n", cx);
// assert("\nˇ\nˇ\n\n", cx); assert("\nˇ\nˇ\n\n", cx);
// assert("loremˇ ipsumˇ ", cx); assert("loremˇ ipsumˇ ", cx);
// assert("loremˇ-ˇipsum", cx); assert("loremˇ-ˇipsum", cx);
// assert("loremˇ#$@-ˇipsum", cx); assert("loremˇ#$@-ˇipsum", cx);
// assert("loremˇ_ipsumˇ", cx); assert("loremˇ_ipsumˇ", cx);
// assert(" ˇbcΔˇ", cx); assert(" ˇbcΔˇ", cx);
// assert(" abˇ——ˇcd", cx); assert(" abˇ——ˇcd", cx);
// } }
// #[gpui::test] #[gpui::test]
// fn test_next_subword_end(cx: &mut gpui::AppContext) { fn test_next_subword_end(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) { fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// next_subword_end(&snapshot, display_points[0]), next_subword_end(&snapshot, display_points[0]),
// display_points[1] display_points[1]
// ); );
// } }
// // Subword boundaries are respected // Subword boundaries are respected
// assert("loˇremˇ_ipsum", cx); assert("loˇremˇ_ipsum", cx);
// assert("ˇloremˇ_ipsum", cx); assert("ˇloremˇ_ipsum", cx);
// assert("loremˇ_ipsumˇ", cx); assert("loremˇ_ipsumˇ", cx);
// assert("loremˇ_ipsumˇ_dolor", cx); assert("loremˇ_ipsumˇ_dolor", cx);
// assert("loˇremˇIpsum", cx); assert("loˇremˇIpsum", cx);
// assert("loremˇIpsumˇDolor", cx); assert("loremˇIpsumˇDolor", cx);
// // Word boundaries are still respected // Word boundaries are still respected
// assert("\nˇ loremˇ", cx); assert("\nˇ loremˇ", cx);
// assert(" ˇloremˇ", cx); assert(" ˇloremˇ", cx);
// assert(" lorˇemˇ", cx); assert(" lorˇemˇ", cx);
// assert(" loremˇ ˇ\nipsum\n", cx); assert(" loremˇ ˇ\nipsum\n", cx);
// assert("\nˇ\nˇ\n\n", cx); assert("\nˇ\nˇ\n\n", cx);
// assert("loremˇ ipsumˇ ", cx); assert("loremˇ ipsumˇ ", cx);
// assert("loremˇ-ˇipsum", cx); assert("loremˇ-ˇipsum", cx);
// assert("loremˇ#$@-ˇipsum", cx); assert("loremˇ#$@-ˇipsum", cx);
// assert("loremˇ_ipsumˇ", cx); assert("loremˇ_ipsumˇ", cx);
// assert(" ˇbcˇΔ", cx); assert(" ˇbcˇΔ", cx);
// assert(" abˇ——ˇcd", cx); assert(" abˇ——ˇcd", cx);
// } }
// #[gpui::test] #[gpui::test]
// fn test_find_boundary(cx: &mut gpui::AppContext) { fn test_find_boundary(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert( fn assert(
// marked_text: &str, marked_text: &str,
// cx: &mut gpui::AppContext, cx: &mut gpui::AppContext,
// is_boundary: impl FnMut(char, char) -> bool, is_boundary: impl FnMut(char, char) -> bool,
// ) { ) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// find_boundary( find_boundary(
// &snapshot, &snapshot,
// display_points[0], display_points[0],
// FindRange::MultiLine, FindRange::MultiLine,
// is_boundary is_boundary
// ), ),
// display_points[1] display_points[1]
// ); );
// } }
// assert("abcˇdef\ngh\nijˇk", cx, |left, right| { assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
// left == 'j' && right == 'k' left == 'j' && right == 'k'
// }); });
// assert("abˇcdef\ngh\nˇijk", cx, |left, right| { assert("abˇcdef\ngh\nˇijk", cx, |left, right| {
// left == '\n' && right == 'i' left == '\n' && right == 'i'
// }); });
// let mut line_count = 0; let mut line_count = 0;
// assert("abcˇdef\ngh\nˇijk", cx, |left, _| { assert("abcˇdef\ngh\nˇijk", cx, |left, _| {
// if left == '\n' { if left == '\n' {
// line_count += 1; line_count += 1;
// line_count == 2 line_count == 2
// } else { } else {
// false false
// } }
// }); });
// } }
// #[gpui::test] #[gpui::test]
// fn test_surrounding_word(cx: &mut gpui::AppContext) { fn test_surrounding_word(cx: &mut gpui::AppContext) {
// init_test(cx); init_test(cx);
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) { fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
// assert_eq!( assert_eq!(
// surrounding_word(&snapshot, display_points[1]), surrounding_word(&snapshot, display_points[1]),
// display_points[0]..display_points[2], display_points[0]..display_points[2],
// "{}", "{}",
// marked_text.to_string() marked_text.to_string()
// ); );
// } }
// assert("ˇˇloremˇ ipsum", cx); assert("ˇˇloremˇ ipsum", cx);
// assert("ˇloˇremˇ ipsum", cx); assert("ˇloˇremˇ ipsum", cx);
// assert("ˇloremˇˇ ipsum", cx); assert("ˇloremˇˇ ipsum", cx);
// assert("loremˇ ˇ ˇipsum", cx); assert("loremˇ ˇ ˇipsum", cx);
// assert("lorem\nˇˇˇ\nipsum", cx); assert("lorem\nˇˇˇ\nipsum", cx);
// assert("lorem\nˇˇipsumˇ", cx); assert("lorem\nˇˇipsumˇ", cx);
// assert("loremˇ,ˇˇ ipsum", cx); assert("loremˇ,ˇˇ ipsum", cx);
// assert("ˇloremˇˇ, ipsum", cx); assert("ˇloremˇˇ, ipsum", cx);
// } }
// #[gpui::test] #[gpui::test]
// async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) { async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) {
// cx.update(|cx| { cx.update(|cx| {
// init_test(cx); init_test(cx);
// }); });
// let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx).await;
// let editor = cx.editor.clone(); let editor = cx.editor.clone();
// let window = cx.window.clone(); let window = cx.window.clone();
// cx.update_window(window, |cx| { cx.update_window(window, |_, cx| {
// let text_layout_details = let text_layout_details =
// editor.read_with(cx, |editor, cx| editor.text_layout_details(cx)); editor.update(cx, |editor, cx| editor.text_layout_details(cx));
// let family_id = cx let font = font("Helvetica");
// .font_cache()
// .load_family(&["Helvetica"], &Default::default())
// .unwrap();
// let font_id = cx
// .font_cache()
// .select_font(family_id, &Default::default())
// .unwrap();
// let buffer = let buffer = cx
// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn")); .build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn"));
// let multibuffer = cx.add_model(|cx| { let multibuffer = cx.build_model(|cx| {
// let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0);
// multibuffer.push_excerpts( multibuffer.push_excerpts(
// buffer.clone(), buffer.clone(),
// [ [
// ExcerptRange { ExcerptRange {
// context: Point::new(0, 0)..Point::new(1, 4), context: Point::new(0, 0)..Point::new(1, 4),
// primary: None, primary: None,
// }, },
// ExcerptRange { ExcerptRange {
// context: Point::new(2, 0)..Point::new(3, 2), context: Point::new(2, 0)..Point::new(3, 2),
// primary: None, primary: None,
// }, },
// ], ],
// cx, cx,
// ); );
// multibuffer multibuffer
// }); });
// let display_map = let display_map =
// cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx)); cx.build_model(|cx| DisplayMap::new(multibuffer, font, px(14.0), None, 2, 2, cx));
// let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
// assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn"); assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
// let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details); let col_2_x =
snapshot.x_for_display_point(DisplayPoint::new(2, 2), &text_layout_details);
// // Can't move up into the first excerpt's header // Can't move up into the first excerpt's header
// assert_eq!( assert_eq!(
// up( up(
// &snapshot, &snapshot,
// DisplayPoint::new(2, 2), DisplayPoint::new(2, 2),
// SelectionGoal::HorizontalPosition(col_2_x), SelectionGoal::HorizontalPosition(col_2_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(2, 0), DisplayPoint::new(2, 0),
// SelectionGoal::HorizontalPosition(0.0) SelectionGoal::HorizontalPosition(0.0)
// ), ),
// ); );
// assert_eq!( assert_eq!(
// up( up(
// &snapshot, &snapshot,
// DisplayPoint::new(2, 0), DisplayPoint::new(2, 0),
// SelectionGoal::None, SelectionGoal::None,
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(2, 0), DisplayPoint::new(2, 0),
// SelectionGoal::HorizontalPosition(0.0) SelectionGoal::HorizontalPosition(0.0)
// ), ),
// ); );
// let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details); let col_4_x =
snapshot.x_for_display_point(DisplayPoint::new(3, 4), &text_layout_details);
// // Move up and down within first excerpt // Move up and down within first excerpt
// assert_eq!( assert_eq!(
// up( up(
// &snapshot, &snapshot,
// DisplayPoint::new(3, 4), DisplayPoint::new(3, 4),
// SelectionGoal::HorizontalPosition(col_4_x), SelectionGoal::HorizontalPosition(col_4_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(2, 3), DisplayPoint::new(2, 3),
// SelectionGoal::HorizontalPosition(col_4_x) SelectionGoal::HorizontalPosition(col_4_x.0)
// ), ),
// ); );
// assert_eq!( assert_eq!(
// down( down(
// &snapshot, &snapshot,
// DisplayPoint::new(2, 3), DisplayPoint::new(2, 3),
// SelectionGoal::HorizontalPosition(col_4_x), SelectionGoal::HorizontalPosition(col_4_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(3, 4), DisplayPoint::new(3, 4),
// SelectionGoal::HorizontalPosition(col_4_x) SelectionGoal::HorizontalPosition(col_4_x.0)
// ), ),
// ); );
// let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details); let col_5_x =
snapshot.x_for_display_point(DisplayPoint::new(6, 5), &text_layout_details);
// // Move up and down across second excerpt's header // Move up and down across second excerpt's header
// assert_eq!( assert_eq!(
// up( up(
// &snapshot, &snapshot,
// DisplayPoint::new(6, 5), DisplayPoint::new(6, 5),
// SelectionGoal::HorizontalPosition(col_5_x), SelectionGoal::HorizontalPosition(col_5_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(3, 4), DisplayPoint::new(3, 4),
// SelectionGoal::HorizontalPosition(col_5_x) SelectionGoal::HorizontalPosition(col_5_x.0)
// ), ),
// ); );
// assert_eq!( assert_eq!(
// down( down(
// &snapshot, &snapshot,
// DisplayPoint::new(3, 4), DisplayPoint::new(3, 4),
// SelectionGoal::HorizontalPosition(col_5_x), SelectionGoal::HorizontalPosition(col_5_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(6, 5), DisplayPoint::new(6, 5),
// SelectionGoal::HorizontalPosition(col_5_x) SelectionGoal::HorizontalPosition(col_5_x.0)
// ), ),
// ); );
// let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details); let max_point_x =
snapshot.x_for_display_point(DisplayPoint::new(7, 2), &text_layout_details);
// // Can't move down off the end // Can't move down off the end
// assert_eq!( assert_eq!(
// down( down(
// &snapshot, &snapshot,
// DisplayPoint::new(7, 0), DisplayPoint::new(7, 0),
// SelectionGoal::HorizontalPosition(0.0), SelectionGoal::HorizontalPosition(0.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(7, 2), DisplayPoint::new(7, 2),
// SelectionGoal::HorizontalPosition(max_point_x) SelectionGoal::HorizontalPosition(max_point_x.0)
// ), ),
// ); );
// assert_eq!( assert_eq!(
// down( down(
// &snapshot, &snapshot,
// DisplayPoint::new(7, 2), DisplayPoint::new(7, 2),
// SelectionGoal::HorizontalPosition(max_point_x), SelectionGoal::HorizontalPosition(max_point_x.0),
// false, false,
// &text_layout_details &text_layout_details
// ), ),
// ( (
// DisplayPoint::new(7, 2), DisplayPoint::new(7, 2),
// SelectionGoal::HorizontalPosition(max_point_x) SelectionGoal::HorizontalPosition(max_point_x.0)
// ), ),
// ); );
// }); });
// } }
// fn init_test(cx: &mut gpui::AppContext) { fn init_test(cx: &mut gpui::AppContext) {
// cx.set_global(SettingsStore::test(cx)); let settings_store = SettingsStore::test(cx);
// theme::init(cx); cx.set_global(settings_store);
// language::init(cx); theme::init(theme::LoadThemes::JustBase, cx);
// crate::init(cx); language::init(cx);
// Project::init_settings(cx); crate::init(cx);
// } Project::init_settings(cx);
// } }
}