Another batch of tests

Piotr Osiewicz created

Change summary

crates/editor2/src/display_map/block_map.rs | 1338 +++++++++++-----------
crates/editor2/src/editor_tests.rs          |  652 +++++-----
2 files changed, 987 insertions(+), 1,003 deletions(-)

Detailed changes

crates/editor2/src/display_map/block_map.rs 🔗

@@ -988,680 +988,664 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
     (row, offset)
 }
 
-// #[cfg(test)]
-// mod tests {
-//     use super::*;
-//     use crate::display_map::inlay_map::InlayMap;
-//     use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
-//     use gpui::Element;
-//     use multi_buffer::MultiBuffer;
-//     use rand::prelude::*;
-//     use settings::SettingsStore;
-//     use std::env;
-//     use util::RandomCharIter;
-
-//     #[gpui::test]
-//     fn test_offset_for_row() {
-//         assert_eq!(offset_for_row("", 0), (0, 0));
-//         assert_eq!(offset_for_row("", 1), (0, 0));
-//         assert_eq!(offset_for_row("abcd", 0), (0, 0));
-//         assert_eq!(offset_for_row("abcd", 1), (0, 4));
-//         assert_eq!(offset_for_row("\n", 0), (0, 0));
-//         assert_eq!(offset_for_row("\n", 1), (1, 1));
-//         assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
-//         assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
-//         assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
-//         assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
-//     }
-
-//     #[gpui::test]
-//     fn test_basic_blocks(cx: &mut gpui::AppContext) {
-//         init_test(cx);
-
-//         let family_id = cx
-//             .font_cache()
-//             .load_family(&["Helvetica"], &Default::default())
-//             .unwrap();
-//         let font_id = cx
-//             .font_cache()
-//             .select_font(family_id, &Default::default())
-//             .unwrap();
-
-//         let text = "aaa\nbbb\nccc\nddd";
-
-//         let buffer = MultiBuffer::build_simple(text, cx);
-//         let buffer_snapshot = buffer.read(cx).snapshot(cx);
-//         let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-//         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-//         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-//         let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
-//         let (wrap_map, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, None, cx);
-//         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
-
-//         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
-//         let block_ids = writer.insert(vec![
-//             BlockProperties {
-//                 style: BlockStyle::Fixed,
-//                 position: buffer_snapshot.anchor_after(Point::new(1, 0)),
-//                 height: 1,
-//                 disposition: BlockDisposition::Above,
-//                 render: Arc::new(|_| Empty::new().into_any_named("block 1")),
-//             },
-//             BlockProperties {
-//                 style: BlockStyle::Fixed,
-//                 position: buffer_snapshot.anchor_after(Point::new(1, 2)),
-//                 height: 2,
-//                 disposition: BlockDisposition::Above,
-//                 render: Arc::new(|_| Empty::new().into_any_named("block 2")),
-//             },
-//             BlockProperties {
-//                 style: BlockStyle::Fixed,
-//                 position: buffer_snapshot.anchor_after(Point::new(3, 3)),
-//                 height: 3,
-//                 disposition: BlockDisposition::Below,
-//                 render: Arc::new(|_| Empty::new().into_any_named("block 3")),
-//             },
-//         ]);
-
-//         let snapshot = block_map.read(wraps_snapshot, Default::default());
-//         assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
-
-//         let blocks = snapshot
-//             .blocks_in_range(0..8)
-//             .map(|(start_row, block)| {
-//                 let block = block.as_custom().unwrap();
-//                 (start_row..start_row + block.height as u32, block.id)
-//             })
-//             .collect::<Vec<_>>();
-
-//         // When multiple blocks are on the same line, the newer blocks appear first.
-//         assert_eq!(
-//             blocks,
-//             &[
-//                 (1..2, block_ids[0]),
-//                 (2..4, block_ids[1]),
-//                 (7..10, block_ids[2]),
-//             ]
-//         );
-
-//         assert_eq!(
-//             snapshot.to_block_point(WrapPoint::new(0, 3)),
-//             BlockPoint::new(0, 3)
-//         );
-//         assert_eq!(
-//             snapshot.to_block_point(WrapPoint::new(1, 0)),
-//             BlockPoint::new(4, 0)
-//         );
-//         assert_eq!(
-//             snapshot.to_block_point(WrapPoint::new(3, 3)),
-//             BlockPoint::new(6, 3)
-//         );
-
-//         assert_eq!(
-//             snapshot.to_wrap_point(BlockPoint::new(0, 3)),
-//             WrapPoint::new(0, 3)
-//         );
-//         assert_eq!(
-//             snapshot.to_wrap_point(BlockPoint::new(1, 0)),
-//             WrapPoint::new(1, 0)
-//         );
-//         assert_eq!(
-//             snapshot.to_wrap_point(BlockPoint::new(3, 0)),
-//             WrapPoint::new(1, 0)
-//         );
-//         assert_eq!(
-//             snapshot.to_wrap_point(BlockPoint::new(7, 0)),
-//             WrapPoint::new(3, 3)
-//         );
-
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
-//             BlockPoint::new(0, 3)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
-//             BlockPoint::new(4, 0)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
-//             BlockPoint::new(0, 3)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
-//             BlockPoint::new(4, 0)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
-//             BlockPoint::new(4, 0)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
-//             BlockPoint::new(4, 0)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
-//             BlockPoint::new(6, 3)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
-//             BlockPoint::new(6, 3)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
-//             BlockPoint::new(6, 3)
-//         );
-//         assert_eq!(
-//             snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
-//             BlockPoint::new(6, 3)
-//         );
-
-//         assert_eq!(
-//             snapshot.buffer_rows(0).collect::<Vec<_>>(),
-//             &[
-//                 Some(0),
-//                 None,
-//                 None,
-//                 None,
-//                 Some(1),
-//                 Some(2),
-//                 Some(3),
-//                 None,
-//                 None,
-//                 None
-//             ]
-//         );
-
-//         // Insert a line break, separating two block decorations into separate lines.
-//         let buffer_snapshot = buffer.update(cx, |buffer, cx| {
-//             buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
-//             buffer.snapshot(cx)
-//         });
-
-//         let (inlay_snapshot, inlay_edits) =
-//             inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
-//         let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-//         let (tab_snapshot, tab_edits) =
-//             tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
-//         let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-//             wrap_map.sync(tab_snapshot, tab_edits, cx)
-//         });
-//         let snapshot = block_map.read(wraps_snapshot, wrap_edits);
-//         assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
-//     }
-
-//     #[gpui::test]
-//     fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) {
-//         init_test(cx);
-
-//         let family_id = cx
-//             .font_cache()
-//             .load_family(&["Helvetica"], &Default::default())
-//             .unwrap();
-//         let font_id = cx
-//             .font_cache()
-//             .select_font(family_id, &Default::default())
-//             .unwrap();
-
-//         let text = "one two three\nfour five six\nseven eight";
-
-//         let buffer = MultiBuffer::build_simple(text, cx);
-//         let buffer_snapshot = buffer.read(cx).snapshot(cx);
-//         let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-//         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
-//         let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-//         let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, Some(60.), cx);
-//         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
-
-//         let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
-//         writer.insert(vec![
-//             BlockProperties {
-//                 style: BlockStyle::Fixed,
-//                 position: buffer_snapshot.anchor_after(Point::new(1, 12)),
-//                 disposition: BlockDisposition::Above,
-//                 render: Arc::new(|_| Empty::new().into_any_named("block 1")),
-//                 height: 1,
-//             },
-//             BlockProperties {
-//                 style: BlockStyle::Fixed,
-//                 position: buffer_snapshot.anchor_after(Point::new(1, 1)),
-//                 disposition: BlockDisposition::Below,
-//                 render: Arc::new(|_| Empty::new().into_any_named("block 2")),
-//                 height: 1,
-//             },
-//         ]);
-
-//         // Blocks with an 'above' disposition go above their corresponding buffer line.
-//         // Blocks with a 'below' disposition go below their corresponding buffer line.
-//         let snapshot = block_map.read(wraps_snapshot, Default::default());
-//         assert_eq!(
-//             snapshot.text(),
-//             "one two \nthree\n\nfour five \nsix\n\nseven \neight"
-//         );
-//     }
-
-//     #[gpui::test(iterations = 100)]
-//     fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) {
-//         init_test(cx);
-
-//         let operations = env::var("OPERATIONS")
-//             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-//             .unwrap_or(10);
-
-//         let wrap_width = if rng.gen_bool(0.2) {
-//             None
-//         } else {
-//             Some(rng.gen_range(0.0..=100.0))
-//         };
-//         let tab_size = 1.try_into().unwrap();
-//         let family_id = cx
-//             .font_cache()
-//             .load_family(&["Helvetica"], &Default::default())
-//             .unwrap();
-//         let font_id = cx
-//             .font_cache()
-//             .select_font(family_id, &Default::default())
-//             .unwrap();
-//         let font_size = 14.0;
-//         let buffer_start_header_height = rng.gen_range(1..=5);
-//         let excerpt_header_height = rng.gen_range(1..=5);
-
-//         log::info!("Wrap width: {:?}", wrap_width);
-//         log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
-
-//         let buffer = if rng.gen() {
-//             let len = rng.gen_range(0..10);
-//             let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-//             log::info!("initial buffer text: {:?}", text);
-//             MultiBuffer::build_simple(&text, cx)
-//         } else {
-//             MultiBuffer::build_random(&mut rng, cx)
-//         };
-
-//         let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
-//         let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
-//         let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
-//         let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
-//         let (wrap_map, wraps_snapshot) =
-//             WrapMap::new(tab_snapshot, font_id, font_size, wrap_width, cx);
-//         let mut block_map = BlockMap::new(
-//             wraps_snapshot,
-//             buffer_start_header_height,
-//             excerpt_header_height,
-//         );
-//         let mut custom_blocks = Vec::new();
-
-//         for _ in 0..operations {
-//             let mut buffer_edits = Vec::new();
-//             match rng.gen_range(0..=100) {
-//                 0..=19 => {
-//                     let wrap_width = if rng.gen_bool(0.2) {
-//                         None
-//                     } else {
-//                         Some(rng.gen_range(0.0..=100.0))
-//                     };
-//                     log::info!("Setting wrap width to {:?}", wrap_width);
-//                     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
-//                 }
-//                 20..=39 => {
-//                     let block_count = rng.gen_range(1..=5);
-//                     let block_properties = (0..block_count)
-//                         .map(|_| {
-//                             let buffer = buffer.read(cx).read(cx);
-//                             let position = buffer.anchor_after(
-//                                 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
-//                             );
-
-//                             let disposition = if rng.gen() {
-//                                 BlockDisposition::Above
-//                             } else {
-//                                 BlockDisposition::Below
-//                             };
-//                             let height = rng.gen_range(1..5);
-//                             log::info!(
-//                                 "inserting block {:?} {:?} with height {}",
-//                                 disposition,
-//                                 position.to_point(&buffer),
-//                                 height
-//                             );
-//                             BlockProperties {
-//                                 style: BlockStyle::Fixed,
-//                                 position,
-//                                 height,
-//                                 disposition,
-//                                 render: Arc::new(|_| Empty::new().into_any()),
-//                             }
-//                         })
-//                         .collect::<Vec<_>>();
-
-//                     let (inlay_snapshot, inlay_edits) =
-//                         inlay_map.sync(buffer_snapshot.clone(), vec![]);
-//                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-//                     let (tab_snapshot, tab_edits) =
-//                         tab_map.sync(fold_snapshot, fold_edits, tab_size);
-//                     let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-//                         wrap_map.sync(tab_snapshot, tab_edits, cx)
-//                     });
-//                     let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
-//                     let block_ids = block_map.insert(block_properties.clone());
-//                     for (block_id, props) in block_ids.into_iter().zip(block_properties) {
-//                         custom_blocks.push((block_id, props));
-//                     }
-//                 }
-//                 40..=59 if !custom_blocks.is_empty() => {
-//                     let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
-//                     let block_ids_to_remove = (0..block_count)
-//                         .map(|_| {
-//                             custom_blocks
-//                                 .remove(rng.gen_range(0..custom_blocks.len()))
-//                                 .0
-//                         })
-//                         .collect();
-
-//                     let (inlay_snapshot, inlay_edits) =
-//                         inlay_map.sync(buffer_snapshot.clone(), vec![]);
-//                     let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-//                     let (tab_snapshot, tab_edits) =
-//                         tab_map.sync(fold_snapshot, fold_edits, tab_size);
-//                     let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-//                         wrap_map.sync(tab_snapshot, tab_edits, cx)
-//                     });
-//                     let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
-//                     block_map.remove(block_ids_to_remove);
-//                 }
-//                 _ => {
-//                     buffer.update(cx, |buffer, cx| {
-//                         let mutation_count = rng.gen_range(1..=5);
-//                         let subscription = buffer.subscribe();
-//                         buffer.randomly_mutate(&mut rng, mutation_count, cx);
-//                         buffer_snapshot = buffer.snapshot(cx);
-//                         buffer_edits.extend(subscription.consume());
-//                         log::info!("buffer text: {:?}", buffer_snapshot.text());
-//                     });
-//                 }
-//             }
-
-//             let (inlay_snapshot, inlay_edits) =
-//                 inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
-//             let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
-//             let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
-//             let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
-//                 wrap_map.sync(tab_snapshot, tab_edits, cx)
-//             });
-//             let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
-//             assert_eq!(
-//                 blocks_snapshot.transforms.summary().input_rows,
-//                 wraps_snapshot.max_point().row() + 1
-//             );
-//             log::info!("blocks text: {:?}", blocks_snapshot.text());
-
-//             let mut expected_blocks = Vec::new();
-//             expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
-//                 let mut position = block.position.to_point(&buffer_snapshot);
-//                 match block.disposition {
-//                     BlockDisposition::Above => {
-//                         position.column = 0;
-//                     }
-//                     BlockDisposition::Below => {
-//                         position.column = buffer_snapshot.line_len(position.row);
-//                     }
-//                 };
-//                 let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
-//                 (
-//                     row,
-//                     ExpectedBlock::Custom {
-//                         disposition: block.disposition,
-//                         id: *id,
-//                         height: block.height,
-//                     },
-//                 )
-//             }));
-//             expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
-//                 |boundary| {
-//                     let position =
-//                         wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
-//                     (
-//                         position.row(),
-//                         ExpectedBlock::ExcerptHeader {
-//                             height: if boundary.starts_new_buffer {
-//                                 buffer_start_header_height
-//                             } else {
-//                                 excerpt_header_height
-//                             },
-//                             starts_new_buffer: boundary.starts_new_buffer,
-//                         },
-//                     )
-//                 },
-//             ));
-//             expected_blocks.sort_unstable();
-//             let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
-
-//             let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
-//             let mut expected_buffer_rows = Vec::new();
-//             let mut expected_text = String::new();
-//             let mut expected_block_positions = Vec::new();
-//             let input_text = wraps_snapshot.text();
-//             for (row, input_line) in input_text.split('\n').enumerate() {
-//                 let row = row as u32;
-//                 if row > 0 {
-//                     expected_text.push('\n');
-//                 }
-
-//                 let buffer_row = input_buffer_rows[wraps_snapshot
-//                     .to_point(WrapPoint::new(row, 0), Bias::Left)
-//                     .row as usize];
-
-//                 while let Some((block_row, block)) = sorted_blocks_iter.peek() {
-//                     if *block_row == row && block.disposition() == BlockDisposition::Above {
-//                         let (_, block) = sorted_blocks_iter.next().unwrap();
-//                         let height = block.height() as usize;
-//                         expected_block_positions
-//                             .push((expected_text.matches('\n').count() as u32, block));
-//                         let text = "\n".repeat(height);
-//                         expected_text.push_str(&text);
-//                         for _ in 0..height {
-//                             expected_buffer_rows.push(None);
-//                         }
-//                     } else {
-//                         break;
-//                     }
-//                 }
-
-//                 let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
-//                 expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
-//                 expected_text.push_str(input_line);
-
-//                 while let Some((block_row, block)) = sorted_blocks_iter.peek() {
-//                     if *block_row == row && block.disposition() == BlockDisposition::Below {
-//                         let (_, block) = sorted_blocks_iter.next().unwrap();
-//                         let height = block.height() as usize;
-//                         expected_block_positions
-//                             .push((expected_text.matches('\n').count() as u32 + 1, block));
-//                         let text = "\n".repeat(height);
-//                         expected_text.push_str(&text);
-//                         for _ in 0..height {
-//                             expected_buffer_rows.push(None);
-//                         }
-//                     } else {
-//                         break;
-//                     }
-//                 }
-//             }
-
-//             let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
-//             let expected_row_count = expected_lines.len();
-//             for start_row in 0..expected_row_count {
-//                 let expected_text = expected_lines[start_row..].join("\n");
-//                 let actual_text = blocks_snapshot
-//                     .chunks(
-//                         start_row as u32..blocks_snapshot.max_point().row + 1,
-//                         false,
-//                         Highlights::default(),
-//                     )
-//                     .map(|chunk| chunk.text)
-//                     .collect::<String>();
-//                 assert_eq!(
-//                     actual_text, expected_text,
-//                     "incorrect text starting from row {}",
-//                     start_row
-//                 );
-//                 assert_eq!(
-//                     blocks_snapshot
-//                         .buffer_rows(start_row as u32)
-//                         .collect::<Vec<_>>(),
-//                     &expected_buffer_rows[start_row..]
-//                 );
-//             }
-
-//             assert_eq!(
-//                 blocks_snapshot
-//                     .blocks_in_range(0..(expected_row_count as u32))
-//                     .map(|(row, block)| (row, block.clone().into()))
-//                     .collect::<Vec<_>>(),
-//                 expected_block_positions
-//             );
-
-//             let mut expected_longest_rows = Vec::new();
-//             let mut longest_line_len = -1_isize;
-//             for (row, line) in expected_lines.iter().enumerate() {
-//                 let row = row as u32;
-
-//                 assert_eq!(
-//                     blocks_snapshot.line_len(row),
-//                     line.len() as u32,
-//                     "invalid line len for row {}",
-//                     row
-//                 );
-
-//                 let line_char_count = line.chars().count() as isize;
-//                 match line_char_count.cmp(&longest_line_len) {
-//                     Ordering::Less => {}
-//                     Ordering::Equal => expected_longest_rows.push(row),
-//                     Ordering::Greater => {
-//                         longest_line_len = line_char_count;
-//                         expected_longest_rows.clear();
-//                         expected_longest_rows.push(row);
-//                     }
-//                 }
-//             }
-
-//             let longest_row = blocks_snapshot.longest_row();
-//             assert!(
-//                 expected_longest_rows.contains(&longest_row),
-//                 "incorrect longest row {}. expected {:?} with length {}",
-//                 longest_row,
-//                 expected_longest_rows,
-//                 longest_line_len,
-//             );
-
-//             for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
-//                 let wrap_point = WrapPoint::new(row, 0);
-//                 let block_point = blocks_snapshot.to_block_point(wrap_point);
-//                 assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
-//             }
-
-//             let mut block_point = BlockPoint::new(0, 0);
-//             for c in expected_text.chars() {
-//                 let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
-//                 let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
-//                 assert_eq!(
-//                     blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
-//                     left_point
-//                 );
-//                 assert_eq!(
-//                     left_buffer_point,
-//                     buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
-//                     "{:?} is not valid in buffer coordinates",
-//                     left_point
-//                 );
-
-//                 let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
-//                 let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
-//                 assert_eq!(
-//                     blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
-//                     right_point
-//                 );
-//                 assert_eq!(
-//                     right_buffer_point,
-//                     buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
-//                     "{:?} is not valid in buffer coordinates",
-//                     right_point
-//                 );
-
-//                 if c == '\n' {
-//                     block_point.0 += Point::new(1, 0);
-//                 } else {
-//                     block_point.column += c.len_utf8() as u32;
-//                 }
-//             }
-//         }
-
-//         #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
-//         enum ExpectedBlock {
-//             ExcerptHeader {
-//                 height: u8,
-//                 starts_new_buffer: bool,
-//             },
-//             Custom {
-//                 disposition: BlockDisposition,
-//                 id: BlockId,
-//                 height: u8,
-//             },
-//         }
-
-//         impl ExpectedBlock {
-//             fn height(&self) -> u8 {
-//                 match self {
-//                     ExpectedBlock::ExcerptHeader { height, .. } => *height,
-//                     ExpectedBlock::Custom { height, .. } => *height,
-//                 }
-//             }
-
-//             fn disposition(&self) -> BlockDisposition {
-//                 match self {
-//                     ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
-//                     ExpectedBlock::Custom { disposition, .. } => *disposition,
-//                 }
-//             }
-//         }
-
-//         impl From<TransformBlock> for ExpectedBlock {
-//             fn from(block: TransformBlock) -> Self {
-//                 match block {
-//                     TransformBlock::Custom(block) => ExpectedBlock::Custom {
-//                         id: block.id,
-//                         disposition: block.disposition,
-//                         height: block.height,
-//                     },
-//                     TransformBlock::ExcerptHeader {
-//                         height,
-//                         starts_new_buffer,
-//                         ..
-//                     } => ExpectedBlock::ExcerptHeader {
-//                         height,
-//                         starts_new_buffer,
-//                     },
-//                 }
-//             }
-//         }
-//     }
-
-//     fn init_test(cx: &mut gpui::AppContext) {
-//         cx.set_global(SettingsStore::test(cx));
-//         theme::init(cx);
-//     }
-
-//     impl TransformBlock {
-//         fn as_custom(&self) -> Option<&Block> {
-//             match self {
-//                 TransformBlock::Custom(block) => Some(block),
-//                 TransformBlock::ExcerptHeader { .. } => None,
-//             }
-//         }
-//     }
-
-//     impl BlockSnapshot {
-//         fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
-//             self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
-//         }
-//     }
-// }
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::display_map::inlay_map::InlayMap;
+    use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
+    use gpui::{div, font, px, Element, Platform as _};
+    use multi_buffer::MultiBuffer;
+    use rand::prelude::*;
+    use settings::SettingsStore;
+    use std::env;
+    use util::RandomCharIter;
+
+    #[gpui::test]
+    fn test_offset_for_row() {
+        assert_eq!(offset_for_row("", 0), (0, 0));
+        assert_eq!(offset_for_row("", 1), (0, 0));
+        assert_eq!(offset_for_row("abcd", 0), (0, 0));
+        assert_eq!(offset_for_row("abcd", 1), (0, 4));
+        assert_eq!(offset_for_row("\n", 0), (0, 0));
+        assert_eq!(offset_for_row("\n", 1), (1, 1));
+        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
+        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
+        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
+        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
+    }
+
+    #[gpui::test]
+    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
+        cx.update(|cx| init_test(cx));
+
+        let text = "aaa\nbbb\nccc\nddd";
+
+        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
+        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
+        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
+        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
+        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
+        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
+        let (wrap_map, wraps_snapshot) =
+            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
+        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
+
+        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
+        let block_ids = writer.insert(vec![
+            BlockProperties {
+                style: BlockStyle::Fixed,
+                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
+                height: 1,
+                disposition: BlockDisposition::Above,
+                render: Arc::new(|_| div().into_any()),
+            },
+            BlockProperties {
+                style: BlockStyle::Fixed,
+                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
+                height: 2,
+                disposition: BlockDisposition::Above,
+                render: Arc::new(|_| div().into_any()),
+            },
+            BlockProperties {
+                style: BlockStyle::Fixed,
+                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
+                height: 3,
+                disposition: BlockDisposition::Below,
+                render: Arc::new(|_| div().into_any()),
+            },
+        ]);
+
+        let snapshot = block_map.read(wraps_snapshot, Default::default());
+        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
+
+        let blocks = snapshot
+            .blocks_in_range(0..8)
+            .map(|(start_row, block)| {
+                let block = block.as_custom().unwrap();
+                (start_row..start_row + block.height as u32, block.id)
+            })
+            .collect::<Vec<_>>();
+
+        // When multiple blocks are on the same line, the newer blocks appear first.
+        assert_eq!(
+            blocks,
+            &[
+                (1..2, block_ids[0]),
+                (2..4, block_ids[1]),
+                (7..10, block_ids[2]),
+            ]
+        );
+
+        assert_eq!(
+            snapshot.to_block_point(WrapPoint::new(0, 3)),
+            BlockPoint::new(0, 3)
+        );
+        assert_eq!(
+            snapshot.to_block_point(WrapPoint::new(1, 0)),
+            BlockPoint::new(4, 0)
+        );
+        assert_eq!(
+            snapshot.to_block_point(WrapPoint::new(3, 3)),
+            BlockPoint::new(6, 3)
+        );
+
+        assert_eq!(
+            snapshot.to_wrap_point(BlockPoint::new(0, 3)),
+            WrapPoint::new(0, 3)
+        );
+        assert_eq!(
+            snapshot.to_wrap_point(BlockPoint::new(1, 0)),
+            WrapPoint::new(1, 0)
+        );
+        assert_eq!(
+            snapshot.to_wrap_point(BlockPoint::new(3, 0)),
+            WrapPoint::new(1, 0)
+        );
+        assert_eq!(
+            snapshot.to_wrap_point(BlockPoint::new(7, 0)),
+            WrapPoint::new(3, 3)
+        );
+
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
+            BlockPoint::new(0, 3)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
+            BlockPoint::new(4, 0)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
+            BlockPoint::new(0, 3)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
+            BlockPoint::new(4, 0)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
+            BlockPoint::new(4, 0)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
+            BlockPoint::new(4, 0)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
+            BlockPoint::new(6, 3)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
+            BlockPoint::new(6, 3)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
+            BlockPoint::new(6, 3)
+        );
+        assert_eq!(
+            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
+            BlockPoint::new(6, 3)
+        );
+
+        assert_eq!(
+            snapshot.buffer_rows(0).collect::<Vec<_>>(),
+            &[
+                Some(0),
+                None,
+                None,
+                None,
+                Some(1),
+                Some(2),
+                Some(3),
+                None,
+                None,
+                None
+            ]
+        );
+
+        // Insert a line break, separating two block decorations into separate lines.
+        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
+            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
+            buffer.snapshot(cx)
+        });
+
+        let (inlay_snapshot, inlay_edits) =
+            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
+        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
+        let (tab_snapshot, tab_edits) =
+            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
+        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
+            wrap_map.sync(tab_snapshot, tab_edits, cx)
+        });
+        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
+        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
+    }
+
+    #[gpui::test]
+    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
+        cx.update(|cx| init_test(cx));
+
+        let font_id = cx
+            .test_platform
+            .text_system()
+            .font_id(&font("Helvetica"))
+            .unwrap();
+
+        let text = "one two three\nfour five six\nseven eight";
+
+        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
+        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
+        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
+        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
+        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (_, wraps_snapshot) = cx.update(|cx| {
+            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
+        });
+        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
+
+        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
+        writer.insert(vec![
+            BlockProperties {
+                style: BlockStyle::Fixed,
+                position: buffer_snapshot.anchor_after(Point::new(1, 12)),
+                disposition: BlockDisposition::Above,
+                render: Arc::new(|_| div().into_any()),
+                height: 1,
+            },
+            BlockProperties {
+                style: BlockStyle::Fixed,
+                position: buffer_snapshot.anchor_after(Point::new(1, 1)),
+                disposition: BlockDisposition::Below,
+                render: Arc::new(|_| div().into_any()),
+                height: 1,
+            },
+        ]);
+
+        // Blocks with an 'above' disposition go above their corresponding buffer line.
+        // Blocks with a 'below' disposition go below their corresponding buffer line.
+        let snapshot = block_map.read(wraps_snapshot, Default::default());
+        assert_eq!(
+            snapshot.text(),
+            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
+        );
+    }
+
+    #[gpui::test(iterations = 100)]
+    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
+        cx.update(|cx| init_test(cx));
+
+        let operations = env::var("OPERATIONS")
+            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+            .unwrap_or(10);
+
+        let wrap_width = if rng.gen_bool(0.2) {
+            None
+        } else {
+            Some(px(rng.gen_range(0.0..=100.0)))
+        };
+        let tab_size = 1.try_into().unwrap();
+        let font_size = px(14.0);
+        let buffer_start_header_height = rng.gen_range(1..=5);
+        let excerpt_header_height = rng.gen_range(1..=5);
+
+        log::info!("Wrap width: {:?}", wrap_width);
+        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
+
+        let buffer = if rng.gen() {
+            let len = rng.gen_range(0..10);
+            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
+            log::info!("initial buffer text: {:?}", text);
+            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
+        } else {
+            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
+        };
+
+        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
+        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
+        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
+        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
+        let (wrap_map, wraps_snapshot) = cx
+            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
+        let mut block_map = BlockMap::new(
+            wraps_snapshot,
+            buffer_start_header_height,
+            excerpt_header_height,
+        );
+        let mut custom_blocks = Vec::new();
+
+        for _ in 0..operations {
+            let mut buffer_edits = Vec::new();
+            match rng.gen_range(0..=100) {
+                0..=19 => {
+                    let wrap_width = if rng.gen_bool(0.2) {
+                        None
+                    } else {
+                        Some(px(rng.gen_range(0.0..=100.0)))
+                    };
+                    log::info!("Setting wrap width to {:?}", wrap_width);
+                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
+                }
+                20..=39 => {
+                    let block_count = rng.gen_range(1..=5);
+                    let block_properties = (0..block_count)
+                        .map(|_| {
+                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
+                            let position = buffer.anchor_after(
+                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
+                            );
+
+                            let disposition = if rng.gen() {
+                                BlockDisposition::Above
+                            } else {
+                                BlockDisposition::Below
+                            };
+                            let height = rng.gen_range(1..5);
+                            log::info!(
+                                "inserting block {:?} {:?} with height {}",
+                                disposition,
+                                position.to_point(&buffer),
+                                height
+                            );
+                            BlockProperties {
+                                style: BlockStyle::Fixed,
+                                position,
+                                height,
+                                disposition,
+                                render: Arc::new(|_| div().into_any()),
+                            }
+                        })
+                        .collect::<Vec<_>>();
+
+                    let (inlay_snapshot, inlay_edits) =
+                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
+                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
+                    let (tab_snapshot, tab_edits) =
+                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
+                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
+                        wrap_map.sync(tab_snapshot, tab_edits, cx)
+                    });
+                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
+                    let block_ids = block_map.insert(block_properties.clone());
+                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
+                        custom_blocks.push((block_id, props));
+                    }
+                }
+                40..=59 if !custom_blocks.is_empty() => {
+                    let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
+                    let block_ids_to_remove = (0..block_count)
+                        .map(|_| {
+                            custom_blocks
+                                .remove(rng.gen_range(0..custom_blocks.len()))
+                                .0
+                        })
+                        .collect();
+
+                    let (inlay_snapshot, inlay_edits) =
+                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
+                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
+                    let (tab_snapshot, tab_edits) =
+                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
+                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
+                        wrap_map.sync(tab_snapshot, tab_edits, cx)
+                    });
+                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
+                    block_map.remove(block_ids_to_remove);
+                }
+                _ => {
+                    buffer.update(cx, |buffer, cx| {
+                        let mutation_count = rng.gen_range(1..=5);
+                        let subscription = buffer.subscribe();
+                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
+                        buffer_snapshot = buffer.snapshot(cx);
+                        buffer_edits.extend(subscription.consume());
+                        log::info!("buffer text: {:?}", buffer_snapshot.text());
+                    });
+                }
+            }
+
+            let (inlay_snapshot, inlay_edits) =
+                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
+            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
+            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
+            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
+                wrap_map.sync(tab_snapshot, tab_edits, cx)
+            });
+            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
+            assert_eq!(
+                blocks_snapshot.transforms.summary().input_rows,
+                wraps_snapshot.max_point().row() + 1
+            );
+            log::info!("blocks text: {:?}", blocks_snapshot.text());
+
+            let mut expected_blocks = Vec::new();
+            expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
+                let mut position = block.position.to_point(&buffer_snapshot);
+                match block.disposition {
+                    BlockDisposition::Above => {
+                        position.column = 0;
+                    }
+                    BlockDisposition::Below => {
+                        position.column = buffer_snapshot.line_len(position.row);
+                    }
+                };
+                let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
+                (
+                    row,
+                    ExpectedBlock::Custom {
+                        disposition: block.disposition,
+                        id: *id,
+                        height: block.height,
+                    },
+                )
+            }));
+            expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
+                |boundary| {
+                    let position =
+                        wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
+                    (
+                        position.row(),
+                        ExpectedBlock::ExcerptHeader {
+                            height: if boundary.starts_new_buffer {
+                                buffer_start_header_height
+                            } else {
+                                excerpt_header_height
+                            },
+                            starts_new_buffer: boundary.starts_new_buffer,
+                        },
+                    )
+                },
+            ));
+            expected_blocks.sort_unstable();
+            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
+
+            let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
+            let mut expected_buffer_rows = Vec::new();
+            let mut expected_text = String::new();
+            let mut expected_block_positions = Vec::new();
+            let input_text = wraps_snapshot.text();
+            for (row, input_line) in input_text.split('\n').enumerate() {
+                let row = row as u32;
+                if row > 0 {
+                    expected_text.push('\n');
+                }
+
+                let buffer_row = input_buffer_rows[wraps_snapshot
+                    .to_point(WrapPoint::new(row, 0), Bias::Left)
+                    .row as usize];
+
+                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
+                    if *block_row == row && block.disposition() == BlockDisposition::Above {
+                        let (_, block) = sorted_blocks_iter.next().unwrap();
+                        let height = block.height() as usize;
+                        expected_block_positions
+                            .push((expected_text.matches('\n').count() as u32, block));
+                        let text = "\n".repeat(height);
+                        expected_text.push_str(&text);
+                        for _ in 0..height {
+                            expected_buffer_rows.push(None);
+                        }
+                    } else {
+                        break;
+                    }
+                }
+
+                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
+                expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
+                expected_text.push_str(input_line);
+
+                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
+                    if *block_row == row && block.disposition() == BlockDisposition::Below {
+                        let (_, block) = sorted_blocks_iter.next().unwrap();
+                        let height = block.height() as usize;
+                        expected_block_positions
+                            .push((expected_text.matches('\n').count() as u32 + 1, block));
+                        let text = "\n".repeat(height);
+                        expected_text.push_str(&text);
+                        for _ in 0..height {
+                            expected_buffer_rows.push(None);
+                        }
+                    } else {
+                        break;
+                    }
+                }
+            }
+
+            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
+            let expected_row_count = expected_lines.len();
+            for start_row in 0..expected_row_count {
+                let expected_text = expected_lines[start_row..].join("\n");
+                let actual_text = blocks_snapshot
+                    .chunks(
+                        start_row as u32..blocks_snapshot.max_point().row + 1,
+                        false,
+                        Highlights::default(),
+                    )
+                    .map(|chunk| chunk.text)
+                    .collect::<String>();
+                assert_eq!(
+                    actual_text, expected_text,
+                    "incorrect text starting from row {}",
+                    start_row
+                );
+                assert_eq!(
+                    blocks_snapshot
+                        .buffer_rows(start_row as u32)
+                        .collect::<Vec<_>>(),
+                    &expected_buffer_rows[start_row..]
+                );
+            }
+
+            assert_eq!(
+                blocks_snapshot
+                    .blocks_in_range(0..(expected_row_count as u32))
+                    .map(|(row, block)| (row, block.clone().into()))
+                    .collect::<Vec<_>>(),
+                expected_block_positions
+            );
+
+            let mut expected_longest_rows = Vec::new();
+            let mut longest_line_len = -1_isize;
+            for (row, line) in expected_lines.iter().enumerate() {
+                let row = row as u32;
+
+                assert_eq!(
+                    blocks_snapshot.line_len(row),
+                    line.len() as u32,
+                    "invalid line len for row {}",
+                    row
+                );
+
+                let line_char_count = line.chars().count() as isize;
+                match line_char_count.cmp(&longest_line_len) {
+                    Ordering::Less => {}
+                    Ordering::Equal => expected_longest_rows.push(row),
+                    Ordering::Greater => {
+                        longest_line_len = line_char_count;
+                        expected_longest_rows.clear();
+                        expected_longest_rows.push(row);
+                    }
+                }
+            }
+
+            let longest_row = blocks_snapshot.longest_row();
+            assert!(
+                expected_longest_rows.contains(&longest_row),
+                "incorrect longest row {}. expected {:?} with length {}",
+                longest_row,
+                expected_longest_rows,
+                longest_line_len,
+            );
+
+            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
+                let wrap_point = WrapPoint::new(row, 0);
+                let block_point = blocks_snapshot.to_block_point(wrap_point);
+                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
+            }
+
+            let mut block_point = BlockPoint::new(0, 0);
+            for c in expected_text.chars() {
+                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
+                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
+                assert_eq!(
+                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
+                    left_point
+                );
+                assert_eq!(
+                    left_buffer_point,
+                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
+                    "{:?} is not valid in buffer coordinates",
+                    left_point
+                );
+
+                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
+                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
+                assert_eq!(
+                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
+                    right_point
+                );
+                assert_eq!(
+                    right_buffer_point,
+                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
+                    "{:?} is not valid in buffer coordinates",
+                    right_point
+                );
+
+                if c == '\n' {
+                    block_point.0 += Point::new(1, 0);
+                } else {
+                    block_point.column += c.len_utf8() as u32;
+                }
+            }
+        }
+
+        #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+        enum ExpectedBlock {
+            ExcerptHeader {
+                height: u8,
+                starts_new_buffer: bool,
+            },
+            Custom {
+                disposition: BlockDisposition,
+                id: BlockId,
+                height: u8,
+            },
+        }
+
+        impl ExpectedBlock {
+            fn height(&self) -> u8 {
+                match self {
+                    ExpectedBlock::ExcerptHeader { height, .. } => *height,
+                    ExpectedBlock::Custom { height, .. } => *height,
+                }
+            }
+
+            fn disposition(&self) -> BlockDisposition {
+                match self {
+                    ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
+                    ExpectedBlock::Custom { disposition, .. } => *disposition,
+                }
+            }
+        }
+
+        impl From<TransformBlock> for ExpectedBlock {
+            fn from(block: TransformBlock) -> Self {
+                match block {
+                    TransformBlock::Custom(block) => ExpectedBlock::Custom {
+                        id: block.id,
+                        disposition: block.disposition,
+                        height: block.height,
+                    },
+                    TransformBlock::ExcerptHeader {
+                        height,
+                        starts_new_buffer,
+                        ..
+                    } => ExpectedBlock::ExcerptHeader {
+                        height,
+                        starts_new_buffer,
+                    },
+                }
+            }
+        }
+    }
+
+    fn init_test(cx: &mut gpui::AppContext) {
+        let settings = SettingsStore::test(cx);
+        cx.set_global(settings);
+        theme::init(theme::LoadThemes::JustBase, cx);
+    }
+
+    impl TransformBlock {
+        fn as_custom(&self) -> Option<&Block> {
+            match self {
+                TransformBlock::Custom(block) => Some(block),
+                TransformBlock::ExcerptHeader { .. } => None,
+            }
+        }
+    }
+
+    impl BlockSnapshot {
+        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
+            self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
+        }
+    }
+}

crates/editor2/src/editor_tests.rs 🔗

@@ -4809,114 +4809,113 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
 }
 
 // todo!(select_anchor_ranges)
-// #[gpui::test]
-// async fn test_snippets(cx: &mut gpui::TestAppContext) {
-//     init_test(cx, |_| {});
+#[gpui::test]
+async fn test_snippets(cx: &mut gpui::TestAppContext) {
+    init_test(cx, |_| {});
 
-//     let (text, insertion_ranges) = marked_text_ranges(
-//         indoc! {"
-//             a.ˇ b
-//             a.ˇ b
-//             a.ˇ b
-//         "},
-//         false,
-//     );
+    let (text, insertion_ranges) = marked_text_ranges(
+        indoc! {"
+            a.ˇ b
+            a.ˇ b
+            a.ˇ b
+        "},
+        false,
+    );
 
-//     let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
-//     let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-//     let cx = &mut cx;
+    let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
+    let (editor, mut cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
 
-//     editor.update(cx, |editor, cx| {
-//         let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
+    editor.update(cx, |editor, cx| {
+        let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
 
-//         editor
-//             .insert_snippet(&insertion_ranges, snippet, cx)
-//             .unwrap();
+        editor
+            .insert_snippet(&insertion_ranges, snippet, cx)
+            .unwrap();
 
-//         fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
-//             let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
-//             assert_eq!(editor.text(cx), expected_text);
-//             assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
-//         }
+        fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text: &str) {
+            let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false);
+            assert_eq!(editor.text(cx), expected_text);
+            assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
+        }
 
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//             "},
-//         );
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+            "},
+        );
 
-//         // Can't move earlier than the first tab stop
-//         assert!(!editor.move_to_prev_snippet_tabstop(cx));
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//             "},
-//         );
+        // Can't move earlier than the first tab stop
+        assert!(!editor.move_to_prev_snippet_tabstop(cx));
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+            "},
+        );
 
-//         assert!(editor.move_to_next_snippet_tabstop(cx));
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(one, «two», three) b
-//                 a.f(one, «two», three) b
-//                 a.f(one, «two», three) b
-//             "},
-//         );
+        assert!(editor.move_to_next_snippet_tabstop(cx));
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(one, «two», three) b
+                a.f(one, «two», three) b
+                a.f(one, «two», three) b
+            "},
+        );
 
-//         editor.move_to_prev_snippet_tabstop(cx);
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//                 a.f(«one», two, «three») b
-//             "},
-//         );
+        editor.move_to_prev_snippet_tabstop(cx);
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+                a.f(«one», two, «three») b
+            "},
+        );
 
-//         assert!(editor.move_to_next_snippet_tabstop(cx));
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(one, «two», three) b
-//                 a.f(one, «two», three) b
-//                 a.f(one, «two», three) b
-//             "},
-//         );
-//         assert!(editor.move_to_next_snippet_tabstop(cx));
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(one, two, three)ˇ b
-//                 a.f(one, two, three)ˇ b
-//                 a.f(one, two, three)ˇ b
-//             "},
-//         );
+        assert!(editor.move_to_next_snippet_tabstop(cx));
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(one, «two», three) b
+                a.f(one, «two», three) b
+                a.f(one, «two», three) b
+            "},
+        );
+        assert!(editor.move_to_next_snippet_tabstop(cx));
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(one, two, three)ˇ b
+                a.f(one, two, three)ˇ b
+                a.f(one, two, three)ˇ b
+            "},
+        );
 
-//         // As soon as the last tab stop is reached, snippet state is gone
-//         editor.move_to_prev_snippet_tabstop(cx);
-//         assert(
-//             editor,
-//             cx,
-//             indoc! {"
-//                 a.f(one, two, three)ˇ b
-//                 a.f(one, two, three)ˇ b
-//                 a.f(one, two, three)ˇ b
-//             "},
-//         );
-//     });
-// }
+        // As soon as the last tab stop is reached, snippet state is gone
+        editor.move_to_prev_snippet_tabstop(cx);
+        assert(
+            editor,
+            cx,
+            indoc! {"
+                a.f(one, two, three)ˇ b
+                a.f(one, two, three)ˇ b
+                a.f(one, two, three)ˇ b
+            "},
+        );
+    });
+}
 
 #[gpui::test]
 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
@@ -7046,255 +7045,256 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
 }
 
 // todo!(completions)
-// #[gpui::test(iterations = 10)]
-// async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
-//     init_test(cx, |_| {});
+#[gpui::test(iterations = 10)]
+async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
+    // flaky
+    init_test(cx, |_| {});
 
-//     let (copilot, copilot_lsp) = Copilot::fake(cx);
-//     cx.update(|cx| cx.set_global(copilot));
-//     let mut cx = EditorLspTestContext::new_rust(
-//         lsp::ServerCapabilities {
-//             completion_provider: Some(lsp::CompletionOptions {
-//                 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
-//                 ..Default::default()
-//             }),
-//             ..Default::default()
-//         },
-//         cx,
-//     )
-//     .await;
+    let (copilot, copilot_lsp) = Copilot::fake(cx);
+    cx.update(|cx| cx.set_global(copilot));
+    let mut cx = EditorLspTestContext::new_rust(
+        lsp::ServerCapabilities {
+            completion_provider: Some(lsp::CompletionOptions {
+                trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
+                ..Default::default()
+            }),
+            ..Default::default()
+        },
+        cx,
+    )
+    .await;
 
-//     // When inserting, ensure autocompletion is favored over Copilot suggestions.
-//     cx.set_state(indoc! {"
-//         oneˇ
-//         two
-//         three
-//     "});
-//     cx.simulate_keystroke(".");
-//     let _ = handle_completion_request(
-//         &mut cx,
-//         indoc! {"
-//             one.|<>
-//             two
-//             three
-//         "},
-//         vec!["completion_a", "completion_b"],
-//     );
-//     handle_copilot_completion_request(
-//         &copilot_lsp,
-//         vec![copilot::request::Completion {
-//             text: "one.copilot1".into(),
-//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-//             ..Default::default()
-//         }],
-//         vec![],
-//     );
-//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-//     cx.update_editor(|editor, cx| {
-//         assert!(editor.context_menu_visible());
-//         assert!(!editor.has_active_copilot_suggestion(cx));
+    // When inserting, ensure autocompletion is favored over Copilot suggestions.
+    cx.set_state(indoc! {"
+        oneˇ
+        two
+        three
+    "});
+    cx.simulate_keystroke(".");
+    let _ = handle_completion_request(
+        &mut cx,
+        indoc! {"
+            one.|<>
+            two
+            three
+        "},
+        vec!["completion_a", "completion_b"],
+    );
+    handle_copilot_completion_request(
+        &copilot_lsp,
+        vec![copilot::request::Completion {
+            text: "one.copilot1".into(),
+            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+            ..Default::default()
+        }],
+        vec![],
+    );
+    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+    cx.update_editor(|editor, cx| {
+        assert!(editor.context_menu_visible());
+        assert!(!editor.has_active_copilot_suggestion(cx));
 
-//         // Confirming a completion inserts it and hides the context menu, without showing
-//         // the copilot suggestion afterwards.
-//         editor
-//             .confirm_completion(&Default::default(), cx)
-//             .unwrap()
-//             .detach();
-//         assert!(!editor.context_menu_visible());
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
-//         assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
-//     });
+        // Confirming a completion inserts it and hides the context menu, without showing
+        // the copilot suggestion afterwards.
+        editor
+            .confirm_completion(&Default::default(), cx)
+            .unwrap()
+            .detach();
+        assert!(!editor.context_menu_visible());
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.text(cx), "one.completion_a\ntwo\nthree\n");
+        assert_eq!(editor.display_text(cx), "one.completion_a\ntwo\nthree\n");
+    });
 
-//     // Ensure Copilot suggestions are shown right away if no autocompletion is available.
-//     cx.set_state(indoc! {"
-//         oneˇ
-//         two
-//         three
-//     "});
-//     cx.simulate_keystroke(".");
-//     let _ = handle_completion_request(
-//         &mut cx,
-//         indoc! {"
-//             one.|<>
-//             two
-//             three
-//         "},
-//         vec![],
-//     );
-//     handle_copilot_completion_request(
-//         &copilot_lsp,
-//         vec![copilot::request::Completion {
-//             text: "one.copilot1".into(),
-//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-//             ..Default::default()
-//         }],
-//         vec![],
-//     );
-//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-//     cx.update_editor(|editor, cx| {
-//         assert!(!editor.context_menu_visible());
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
-//     });
+    // Ensure Copilot suggestions are shown right away if no autocompletion is available.
+    cx.set_state(indoc! {"
+        oneˇ
+        two
+        three
+    "});
+    cx.simulate_keystroke(".");
+    let _ = handle_completion_request(
+        &mut cx,
+        indoc! {"
+            one.|<>
+            two
+            three
+        "},
+        vec![],
+    );
+    handle_copilot_completion_request(
+        &copilot_lsp,
+        vec![copilot::request::Completion {
+            text: "one.copilot1".into(),
+            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+            ..Default::default()
+        }],
+        vec![],
+    );
+    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+    cx.update_editor(|editor, cx| {
+        assert!(!editor.context_menu_visible());
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
+    });
 
-//     // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
-//     cx.set_state(indoc! {"
-//         oneˇ
-//         two
-//         three
-//     "});
-//     cx.simulate_keystroke(".");
-//     let _ = handle_completion_request(
-//         &mut cx,
-//         indoc! {"
-//             one.|<>
-//             two
-//             three
-//         "},
-//         vec!["completion_a", "completion_b"],
-//     );
-//     handle_copilot_completion_request(
-//         &copilot_lsp,
-//         vec![copilot::request::Completion {
-//             text: "one.copilot1".into(),
-//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
-//             ..Default::default()
-//         }],
-//         vec![],
-//     );
-//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-//     cx.update_editor(|editor, cx| {
-//         assert!(editor.context_menu_visible());
-//         assert!(!editor.has_active_copilot_suggestion(cx));
+    // Reset editor, and ensure autocompletion is still favored over Copilot suggestions.
+    cx.set_state(indoc! {"
+        oneˇ
+        two
+        three
+    "});
+    cx.simulate_keystroke(".");
+    let _ = handle_completion_request(
+        &mut cx,
+        indoc! {"
+            one.|<>
+            two
+            three
+        "},
+        vec!["completion_a", "completion_b"],
+    );
+    handle_copilot_completion_request(
+        &copilot_lsp,
+        vec![copilot::request::Completion {
+            text: "one.copilot1".into(),
+            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 4)),
+            ..Default::default()
+        }],
+        vec![],
+    );
+    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+    cx.update_editor(|editor, cx| {
+        assert!(editor.context_menu_visible());
+        assert!(!editor.has_active_copilot_suggestion(cx));
 
-//         // When hiding the context menu, the Copilot suggestion becomes visible.
-//         editor.hide_context_menu(cx);
-//         assert!(!editor.context_menu_visible());
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
-//     });
+        // When hiding the context menu, the Copilot suggestion becomes visible.
+        editor.hide_context_menu(cx);
+        assert!(!editor.context_menu_visible());
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.\ntwo\nthree\n");
+    });
 
-//     // Ensure existing completion is interpolated when inserting again.
-//     cx.simulate_keystroke("c");
-//     executor.run_until_parked();
-//     cx.update_editor(|editor, cx| {
-//         assert!(!editor.context_menu_visible());
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-//     });
+    // Ensure existing completion is interpolated when inserting again.
+    cx.simulate_keystroke("c");
+    executor.run_until_parked();
+    cx.update_editor(|editor, cx| {
+        assert!(!editor.context_menu_visible());
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+    });
 
-//     // After debouncing, new Copilot completions should be requested.
-//     handle_copilot_completion_request(
-//         &copilot_lsp,
-//         vec![copilot::request::Completion {
-//             text: "one.copilot2".into(),
-//             range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
-//             ..Default::default()
-//         }],
-//         vec![],
-//     );
-//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-//     cx.update_editor(|editor, cx| {
-//         assert!(!editor.context_menu_visible());
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+    // After debouncing, new Copilot completions should be requested.
+    handle_copilot_completion_request(
+        &copilot_lsp,
+        vec![copilot::request::Completion {
+            text: "one.copilot2".into(),
+            range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 5)),
+            ..Default::default()
+        }],
+        vec![],
+    );
+    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+    cx.update_editor(|editor, cx| {
+        assert!(!editor.context_menu_visible());
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
 
-//         // Canceling should remove the active Copilot suggestion.
-//         editor.cancel(&Default::default(), cx);
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+        // Canceling should remove the active Copilot suggestion.
+        editor.cancel(&Default::default(), cx);
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
 
-//         // After canceling, tabbing shouldn't insert the previously shown suggestion.
-//         editor.tab(&Default::default(), cx);
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
+        // After canceling, tabbing shouldn't insert the previously shown suggestion.
+        editor.tab(&Default::default(), cx);
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.c   \ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.c   \ntwo\nthree\n");
 
-//         // When undoing the previously active suggestion is shown again.
-//         editor.undo(&Default::default(), cx);
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
-//     });
+        // When undoing the previously active suggestion is shown again.
+        editor.undo(&Default::default(), cx);
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
+    });
 
-//     // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
-//     cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
-//     cx.update_editor(|editor, cx| {
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+    // If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
+    cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
+    cx.update_editor(|editor, cx| {
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
 
-//         // Tabbing when there is an active suggestion inserts it.
-//         editor.tab(&Default::default(), cx);
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
+        // Tabbing when there is an active suggestion inserts it.
+        editor.tab(&Default::default(), cx);
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
 
-//         // When undoing the previously active suggestion is shown again.
-//         editor.undo(&Default::default(), cx);
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+        // When undoing the previously active suggestion is shown again.
+        editor.undo(&Default::default(), cx);
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
 
-//         // Hide suggestion.
-//         editor.cancel(&Default::default(), cx);
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
-//     });
+        // Hide suggestion.
+        editor.cancel(&Default::default(), cx);
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
+    });
 
-//     // If an edit occurs outside of this editor but no suggestion is being shown,
-//     // we won't make it visible.
-//     cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
-//     cx.update_editor(|editor, cx| {
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
-//         assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
-//     });
+    // If an edit occurs outside of this editor but no suggestion is being shown,
+    // we won't make it visible.
+    cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
+    cx.update_editor(|editor, cx| {
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
+        assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
+    });
 
-//     // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
-//     cx.update_editor(|editor, cx| {
-//         editor.set_text("fn foo() {\n  \n}", cx);
-//         editor.change_selections(None, cx, |s| {
-//             s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
-//         });
-//     });
-//     handle_copilot_completion_request(
-//         &copilot_lsp,
-//         vec![copilot::request::Completion {
-//             text: "    let x = 4;".into(),
-//             range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
-//             ..Default::default()
-//         }],
-//         vec![],
-//     );
+    // Reset the editor to verify how suggestions behave when tabbing on leading indentation.
+    cx.update_editor(|editor, cx| {
+        editor.set_text("fn foo() {\n  \n}", cx);
+        editor.change_selections(None, cx, |s| {
+            s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
+        });
+    });
+    handle_copilot_completion_request(
+        &copilot_lsp,
+        vec![copilot::request::Completion {
+            text: "    let x = 4;".into(),
+            range: lsp::Range::new(lsp::Position::new(1, 0), lsp::Position::new(1, 2)),
+            ..Default::default()
+        }],
+        vec![],
+    );
 
-//     cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
-//     executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
-//     cx.update_editor(|editor, cx| {
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
-//         assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
+    cx.update_editor(|editor, cx| editor.next_copilot_suggestion(&Default::default(), cx));
+    executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
+    cx.update_editor(|editor, cx| {
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+        assert_eq!(editor.text(cx), "fn foo() {\n  \n}");
 
-//         // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
-//         editor.tab(&Default::default(), cx);
-//         assert!(editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
-//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+        // Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
+        editor.tab(&Default::default(), cx);
+        assert!(editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.text(cx), "fn foo() {\n    \n}");
+        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
 
-//         // Tabbing again accepts the suggestion.
-//         editor.tab(&Default::default(), cx);
-//         assert!(!editor.has_active_copilot_suggestion(cx));
-//         assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
-//         assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
-//     });
-// }
+        // Tabbing again accepts the suggestion.
+        editor.tab(&Default::default(), cx);
+        assert!(!editor.has_active_copilot_suggestion(cx));
+        assert_eq!(editor.text(cx), "fn foo() {\n    let x = 4;\n}");
+        assert_eq!(editor.display_text(cx), "fn foo() {\n    let x = 4;\n}");
+    });
+}
 
 #[gpui::test]
 async fn test_copilot_completion_invalidation(