diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index d42c3460cf470565a5f5f66f30abadf35f43bea4..058fb038067bb7ecbeba1faf82a6c27a2558e985 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -2393,259 +2393,245 @@ mod tests { sync::atomic::{self, AtomicUsize}, }; - #[test] - fn test_edit() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "abc", ctx); - assert_eq!(buffer.text(), "abc"); - buffer.edit(vec![3..3], "def", None).unwrap(); - assert_eq!(buffer.text(), "abcdef"); - buffer.edit(vec![0..0], "ghi", None).unwrap(); - assert_eq!(buffer.text(), "ghiabcdef"); - buffer.edit(vec![5..5], "jkl", None).unwrap(); - assert_eq!(buffer.text(), "ghiabjklcdef"); - buffer.edit(vec![6..7], "", None).unwrap(); - assert_eq!(buffer.text(), "ghiabjlcdef"); - buffer.edit(vec![4..9], "mno", None).unwrap(); - assert_eq!(buffer.text(), "ghiamnoef"); - buffer - }); - }) + #[gpui::test] + fn test_edit(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "abc", ctx); + assert_eq!(buffer.text(), "abc"); + buffer.edit(vec![3..3], "def", None).unwrap(); + assert_eq!(buffer.text(), "abcdef"); + buffer.edit(vec![0..0], "ghi", None).unwrap(); + assert_eq!(buffer.text(), "ghiabcdef"); + buffer.edit(vec![5..5], "jkl", None).unwrap(); + assert_eq!(buffer.text(), "ghiabjklcdef"); + buffer.edit(vec![6..7], "", None).unwrap(); + assert_eq!(buffer.text(), "ghiabjlcdef"); + buffer.edit(vec![4..9], "mno", None).unwrap(); + assert_eq!(buffer.text(), "ghiamnoef"); + buffer + }); } - #[test] - fn test_edit_events() { - App::test((), |app| { - let mut now = Instant::now(); - let buffer_1_events = Rc::new(RefCell::new(Vec::new())); - let buffer_2_events = Rc::new(RefCell::new(Vec::new())); - - let buffer1 = app.add_model(|ctx| Buffer::new(0, "abcdef", ctx)); - let buffer2 = app.add_model(|ctx| Buffer::new(1, "abcdef", ctx)); - let mut buffer_ops = Vec::new(); - buffer1.update(app, |buffer, ctx| { - let buffer_1_events = buffer_1_events.clone(); - ctx.subscribe(&buffer1, move |_, event, _| { - buffer_1_events.borrow_mut().push(event.clone()) - }); - let buffer_2_events = buffer_2_events.clone(); - ctx.subscribe(&buffer2, move |_, event, _| { - buffer_2_events.borrow_mut().push(event.clone()) - }); + #[gpui::test] + fn test_edit_events(app: &mut gpui::MutableAppContext) { + let mut now = Instant::now(); + let buffer_1_events = Rc::new(RefCell::new(Vec::new())); + let buffer_2_events = Rc::new(RefCell::new(Vec::new())); - // An edit emits an edited event, followed by a dirtied event, - // since the buffer was previously in a clean state. - let ops = buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap(); - buffer_ops.extend_from_slice(&ops); - - // An empty transaction does not emit any events. - buffer.start_transaction(None).unwrap(); - buffer.end_transaction(None, Some(ctx)).unwrap(); - - // A transaction containing two edits emits one edited event. - now += Duration::from_secs(1); - buffer.start_transaction_at(None, now).unwrap(); - let ops = buffer.edit(Some(5..5), "u", Some(ctx)).unwrap(); - buffer_ops.extend_from_slice(&ops); - let ops = buffer.edit(Some(6..6), "w", Some(ctx)).unwrap(); - buffer_ops.extend_from_slice(&ops); - buffer.end_transaction_at(None, now, Some(ctx)).unwrap(); - - // Undoing a transaction emits one edited event. - let ops = buffer.undo(Some(ctx)); - buffer_ops.extend_from_slice(&ops); + let buffer1 = app.add_model(|ctx| Buffer::new(0, "abcdef", ctx)); + let buffer2 = app.add_model(|ctx| Buffer::new(1, "abcdef", ctx)); + let mut buffer_ops = Vec::new(); + buffer1.update(app, |buffer, ctx| { + let buffer_1_events = buffer_1_events.clone(); + ctx.subscribe(&buffer1, move |_, event, _| { + buffer_1_events.borrow_mut().push(event.clone()) }); - - // Incorporating a set of remote ops emits a single edited event, - // followed by a dirtied event. - buffer2.update(app, |buffer, ctx| { - buffer.apply_ops(buffer_ops, Some(ctx)).unwrap(); + let buffer_2_events = buffer_2_events.clone(); + ctx.subscribe(&buffer2, move |_, event, _| { + buffer_2_events.borrow_mut().push(event.clone()) }); - let buffer_1_events = buffer_1_events.borrow(); - assert_eq!( - *buffer_1_events, - vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited] - ); + // An edit emits an edited event, followed by a dirtied event, + // since the buffer was previously in a clean state. + let ops = buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + + // An empty transaction does not emit any events. + buffer.start_transaction(None).unwrap(); + buffer.end_transaction(None, Some(ctx)).unwrap(); + + // A transaction containing two edits emits one edited event. + now += Duration::from_secs(1); + buffer.start_transaction_at(None, now).unwrap(); + let ops = buffer.edit(Some(5..5), "u", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + let ops = buffer.edit(Some(6..6), "w", Some(ctx)).unwrap(); + buffer_ops.extend_from_slice(&ops); + buffer.end_transaction_at(None, now, Some(ctx)).unwrap(); + + // Undoing a transaction emits one edited event. + let ops = buffer.undo(Some(ctx)); + buffer_ops.extend_from_slice(&ops); + }); - let buffer_2_events = buffer_2_events.borrow(); - assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]); + // Incorporating a set of remote ops emits a single edited event, + // followed by a dirtied event. + buffer2.update(app, |buffer, ctx| { + buffer.apply_ops(buffer_ops, Some(ctx)).unwrap(); }); + + let buffer_1_events = buffer_1_events.borrow(); + assert_eq!( + *buffer_1_events, + vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited] + ); + + let buffer_2_events = buffer_2_events.borrow(); + assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]); } - #[test] - fn test_random_edits() { + #[gpui::test] + fn test_random_edits(ctx: &mut gpui::MutableAppContext) { for seed in 0..100 { - App::test((), |ctx| { - println!("{:?}", seed); - let mut rng = &mut StdRng::seed_from_u64(seed); - - let reference_string_len = rng.gen_range(0..3); - let mut reference_string = RandomCharIter::new(&mut rng) - .take(reference_string_len) - .collect::(); - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, reference_string.as_str(), ctx); - let mut buffer_versions = Vec::new(); - for _i in 0..10 { - let (old_ranges, new_text, _) = buffer.randomly_mutate(rng, None); - for old_range in old_ranges.iter().rev() { - reference_string = [ - &reference_string[0..old_range.start], - new_text.as_str(), - &reference_string[old_range.end..], - ] - .concat(); - } - assert_eq!(buffer.text(), reference_string); + println!("{:?}", seed); + let mut rng = &mut StdRng::seed_from_u64(seed); - if rng.gen_bool(0.25) { - buffer.randomly_undo_redo(rng); - reference_string = buffer.text(); - } + let reference_string_len = rng.gen_range(0..3); + let mut reference_string = RandomCharIter::new(&mut rng) + .take(reference_string_len) + .collect::(); + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, reference_string.as_str(), ctx); + let mut buffer_versions = Vec::new(); + for _i in 0..10 { + let (old_ranges, new_text, _) = buffer.randomly_mutate(rng, None); + for old_range in old_ranges.iter().rev() { + reference_string = [ + &reference_string[0..old_range.start], + new_text.as_str(), + &reference_string[old_range.end..], + ] + .concat(); + } + assert_eq!(buffer.text(), reference_string); - { - let line_lengths = line_lengths_in_range(&buffer, 0..buffer.len()); + if rng.gen_bool(0.25) { + buffer.randomly_undo_redo(rng); + reference_string = buffer.text(); + } - for (len, rows) in &line_lengths { - for row in rows { - assert_eq!(buffer.line_len(*row).unwrap(), *len); - } - } + { + let line_lengths = line_lengths_in_range(&buffer, 0..buffer.len()); - let (longest_column, longest_rows) = - line_lengths.iter().next_back().unwrap(); - let rightmost_point = buffer.rightmost_point(); - assert_eq!(rightmost_point.column, *longest_column); - assert!(longest_rows.contains(&rightmost_point.row)); + for (len, rows) in &line_lengths { + for row in rows { + assert_eq!(buffer.line_len(*row).unwrap(), *len); + } } - for _ in 0..5 { - let end = rng.gen_range(0..buffer.len() + 1); - let start = rng.gen_range(0..end + 1); - - let line_lengths = line_lengths_in_range(&buffer, start..end); - let (longest_column, longest_rows) = - line_lengths.iter().next_back().unwrap(); - let range_sum = buffer.text_summary_for_range(start..end); - assert_eq!(range_sum.rightmost_point.column, *longest_column); - assert!(longest_rows.contains(&range_sum.rightmost_point.row)); - let range_text = &buffer.text()[start..end]; - assert_eq!(range_sum.chars, range_text.chars().count()); - assert_eq!(range_sum.bytes, range_text.len()); - } + let (longest_column, longest_rows) = + line_lengths.iter().next_back().unwrap(); + let rightmost_point = buffer.rightmost_point(); + assert_eq!(rightmost_point.column, *longest_column); + assert!(longest_rows.contains(&rightmost_point.row)); + } - if rng.gen_bool(0.3) { - buffer_versions.push(buffer.clone()); - } + for _ in 0..5 { + let end = rng.gen_range(0..buffer.len() + 1); + let start = rng.gen_range(0..end + 1); + + let line_lengths = line_lengths_in_range(&buffer, start..end); + let (longest_column, longest_rows) = + line_lengths.iter().next_back().unwrap(); + let range_sum = buffer.text_summary_for_range(start..end); + assert_eq!(range_sum.rightmost_point.column, *longest_column); + assert!(longest_rows.contains(&range_sum.rightmost_point.row)); + let range_text = &buffer.text()[start..end]; + assert_eq!(range_sum.chars, range_text.chars().count()); + assert_eq!(range_sum.bytes, range_text.len()); } - for mut old_buffer in buffer_versions { - let mut delta = 0_isize; - for Edit { - old_range, - new_range, - } in buffer.edits_since(old_buffer.version.clone()) - { - let old_len = old_range.end - old_range.start; - let new_len = new_range.end - new_range.start; - let old_start = (old_range.start as isize + delta) as usize; - let new_text: String = - buffer.text_for_range(new_range).unwrap().collect(); - old_buffer - .edit(Some(old_start..old_start + old_len), new_text, None) - .unwrap(); - - delta += new_len as isize - old_len as isize; - } - assert_eq!(old_buffer.text(), buffer.text()); + if rng.gen_bool(0.3) { + buffer_versions.push(buffer.clone()); } + } - buffer - }) + for mut old_buffer in buffer_versions { + let mut delta = 0_isize; + for Edit { + old_range, + new_range, + } in buffer.edits_since(old_buffer.version.clone()) + { + let old_len = old_range.end - old_range.start; + let new_len = new_range.end - new_range.start; + let old_start = (old_range.start as isize + delta) as usize; + let new_text: String = buffer.text_for_range(new_range).unwrap().collect(); + old_buffer + .edit(Some(old_start..old_start + old_len), new_text, None) + .unwrap(); + + delta += new_len as isize - old_len as isize; + } + assert_eq!(old_buffer.text(), buffer.text()); + } + + buffer }); } } - #[test] - fn test_line_len() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "", ctx); - buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); - buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); - buffer.edit(vec![18..18], "\npqrs\n", None).unwrap(); - buffer.edit(vec![18..21], "\nPQ", None).unwrap(); - - assert_eq!(buffer.line_len(0).unwrap(), 4); - assert_eq!(buffer.line_len(1).unwrap(), 3); - assert_eq!(buffer.line_len(2).unwrap(), 5); - assert_eq!(buffer.line_len(3).unwrap(), 3); - assert_eq!(buffer.line_len(4).unwrap(), 4); - assert_eq!(buffer.line_len(5).unwrap(), 0); - assert!(buffer.line_len(6).is_err()); - buffer - }); + #[gpui::test] + fn test_line_len(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); + buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); + buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); + buffer.edit(vec![18..18], "\npqrs\n", None).unwrap(); + buffer.edit(vec![18..21], "\nPQ", None).unwrap(); + + assert_eq!(buffer.line_len(0).unwrap(), 4); + assert_eq!(buffer.line_len(1).unwrap(), 3); + assert_eq!(buffer.line_len(2).unwrap(), 5); + assert_eq!(buffer.line_len(3).unwrap(), 3); + assert_eq!(buffer.line_len(4).unwrap(), 4); + assert_eq!(buffer.line_len(5).unwrap(), 0); + assert!(buffer.line_len(6).is_err()); + buffer }); } - #[test] - fn test_rightmost_point() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "", ctx); - assert_eq!(buffer.rightmost_point().row, 0); - buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); - assert_eq!(buffer.rightmost_point().row, 0); - buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); - assert_eq!(buffer.rightmost_point().row, 2); - buffer.edit(vec![18..18], "\npqrs", None).unwrap(); - assert_eq!(buffer.rightmost_point().row, 2); - buffer.edit(vec![10..12], "", None).unwrap(); - assert_eq!(buffer.rightmost_point().row, 0); - buffer.edit(vec![24..24], "tuv", None).unwrap(); - assert_eq!(buffer.rightmost_point().row, 4); - buffer - }); + #[gpui::test] + fn test_rightmost_point(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); + assert_eq!(buffer.rightmost_point().row, 0); + buffer.edit(vec![0..0], "abcd\nefg\nhij", None).unwrap(); + assert_eq!(buffer.rightmost_point().row, 0); + buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); + assert_eq!(buffer.rightmost_point().row, 2); + buffer.edit(vec![18..18], "\npqrs", None).unwrap(); + assert_eq!(buffer.rightmost_point().row, 2); + buffer.edit(vec![10..12], "", None).unwrap(); + assert_eq!(buffer.rightmost_point().row, 0); + buffer.edit(vec![24..24], "tuv", None).unwrap(); + assert_eq!(buffer.rightmost_point().row, 4); + buffer }); } - #[test] - fn test_text_summary_for_range() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx); - let text = Text::from(buffer.text()); - assert_eq!( - buffer.text_summary_for_range(1..3), - text.slice(1..3).summary() - ); - assert_eq!( - buffer.text_summary_for_range(1..12), - text.slice(1..12).summary() - ); - assert_eq!( - buffer.text_summary_for_range(0..20), - text.slice(0..20).summary() - ); - assert_eq!( - buffer.text_summary_for_range(0..22), - text.slice(0..22).summary() - ); - assert_eq!( - buffer.text_summary_for_range(7..22), - text.slice(7..22).summary() - ); - buffer - }); + #[gpui::test] + fn test_text_summary_for_range(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx); + let text = Text::from(buffer.text()); + assert_eq!( + buffer.text_summary_for_range(1..3), + text.slice(1..3).summary() + ); + assert_eq!( + buffer.text_summary_for_range(1..12), + text.slice(1..12).summary() + ); + assert_eq!( + buffer.text_summary_for_range(0..20), + text.slice(0..20).summary() + ); + assert_eq!( + buffer.text_summary_for_range(0..22), + text.slice(0..22).summary() + ); + assert_eq!( + buffer.text_summary_for_range(7..22), + text.slice(7..22).summary() + ); + buffer }); } - #[test] - fn test_chars_at() { - App::test((), |ctx| { - ctx.add_model(|ctx| { + #[gpui::test] + fn test_chars_at(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { let mut buffer = Buffer::new(0, "", ctx); buffer.edit(vec![0..0], "abcd\nefgh\nij", None).unwrap(); buffer.edit(vec![12..12], "kl\nmno", None).unwrap(); @@ -2677,7 +2663,6 @@ mod tests { buffer }); - }); } // #[test] @@ -2794,196 +2779,192 @@ mod tests { } } - #[test] - fn test_anchors() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "", ctx); - buffer.edit(vec![0..0], "abc", None).unwrap(); - let left_anchor = buffer.anchor_before(2).unwrap(); - let right_anchor = buffer.anchor_after(2).unwrap(); - - buffer.edit(vec![1..1], "def\n", None).unwrap(); - assert_eq!(buffer.text(), "adef\nbc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); + #[gpui::test] + fn test_anchors(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); + buffer.edit(vec![0..0], "abc", None).unwrap(); + let left_anchor = buffer.anchor_before(2).unwrap(); + let right_anchor = buffer.anchor_after(2).unwrap(); - buffer.edit(vec![2..3], "", None).unwrap(); - assert_eq!(buffer.text(), "adf\nbc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); + buffer.edit(vec![1..1], "def\n", None).unwrap(); + assert_eq!(buffer.text(), "adef\nbc"); + assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 6); + assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 6); + assert_eq!( + left_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 } + ); + assert_eq!( + right_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 } + ); - buffer.edit(vec![5..5], "ghi\n", None).unwrap(); - assert_eq!(buffer.text(), "adf\nbghi\nc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 9); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 } - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 2, column: 0 } - ); + buffer.edit(vec![2..3], "", None).unwrap(); + assert_eq!(buffer.text(), "adf\nbc"); + assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); + assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 5); + assert_eq!( + left_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 } + ); + assert_eq!( + right_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 } + ); - buffer.edit(vec![7..9], "", None).unwrap(); - assert_eq!(buffer.text(), "adf\nbghc"); - assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); - assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 7); - assert_eq!( - left_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 1 }, - ); - assert_eq!( - right_anchor.to_point(&buffer).unwrap(), - Point { row: 1, column: 3 } - ); + buffer.edit(vec![5..5], "ghi\n", None).unwrap(); + assert_eq!(buffer.text(), "adf\nbghi\nc"); + assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); + assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 9); + assert_eq!( + left_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 } + ); + assert_eq!( + right_anchor.to_point(&buffer).unwrap(), + Point { row: 2, column: 0 } + ); - // Ensure anchoring to a point is equivalent to anchoring to an offset. - assert_eq!( - buffer.anchor_before(Point { row: 0, column: 0 }).unwrap(), - buffer.anchor_before(0).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 0, column: 1 }).unwrap(), - buffer.anchor_before(1).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 0, column: 2 }).unwrap(), - buffer.anchor_before(2).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 0, column: 3 }).unwrap(), - buffer.anchor_before(3).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 1, column: 0 }).unwrap(), - buffer.anchor_before(4).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 1, column: 1 }).unwrap(), - buffer.anchor_before(5).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 1, column: 2 }).unwrap(), - buffer.anchor_before(6).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 1, column: 3 }).unwrap(), - buffer.anchor_before(7).unwrap() - ); - assert_eq!( - buffer.anchor_before(Point { row: 1, column: 4 }).unwrap(), - buffer.anchor_before(8).unwrap() - ); + buffer.edit(vec![7..9], "", None).unwrap(); + assert_eq!(buffer.text(), "adf\nbghc"); + assert_eq!(left_anchor.to_offset(&buffer).unwrap(), 5); + assert_eq!(right_anchor.to_offset(&buffer).unwrap(), 7); + assert_eq!( + left_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 1 }, + ); + assert_eq!( + right_anchor.to_point(&buffer).unwrap(), + Point { row: 1, column: 3 } + ); - // Comparison between anchors. - let anchor_at_offset_0 = buffer.anchor_before(0).unwrap(); - let anchor_at_offset_1 = buffer.anchor_before(1).unwrap(); - let anchor_at_offset_2 = buffer.anchor_before(2).unwrap(); + // Ensure anchoring to a point is equivalent to anchoring to an offset. + assert_eq!( + buffer.anchor_before(Point { row: 0, column: 0 }).unwrap(), + buffer.anchor_before(0).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 0, column: 1 }).unwrap(), + buffer.anchor_before(1).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 0, column: 2 }).unwrap(), + buffer.anchor_before(2).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 0, column: 3 }).unwrap(), + buffer.anchor_before(3).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 1, column: 0 }).unwrap(), + buffer.anchor_before(4).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 1, column: 1 }).unwrap(), + buffer.anchor_before(5).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 1, column: 2 }).unwrap(), + buffer.anchor_before(6).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 1, column: 3 }).unwrap(), + buffer.anchor_before(7).unwrap() + ); + assert_eq!( + buffer.anchor_before(Point { row: 1, column: 4 }).unwrap(), + buffer.anchor_before(8).unwrap() + ); - assert_eq!( - anchor_at_offset_0 - .cmp(&anchor_at_offset_0, &buffer) - .unwrap(), - Ordering::Equal - ); - assert_eq!( - anchor_at_offset_1 - .cmp(&anchor_at_offset_1, &buffer) - .unwrap(), - Ordering::Equal - ); - assert_eq!( - anchor_at_offset_2 - .cmp(&anchor_at_offset_2, &buffer) - .unwrap(), - Ordering::Equal - ); + // Comparison between anchors. + let anchor_at_offset_0 = buffer.anchor_before(0).unwrap(); + let anchor_at_offset_1 = buffer.anchor_before(1).unwrap(); + let anchor_at_offset_2 = buffer.anchor_before(2).unwrap(); - assert_eq!( - anchor_at_offset_0 - .cmp(&anchor_at_offset_1, &buffer) - .unwrap(), - Ordering::Less - ); - assert_eq!( - anchor_at_offset_1 - .cmp(&anchor_at_offset_2, &buffer) - .unwrap(), - Ordering::Less - ); - assert_eq!( - anchor_at_offset_0 - .cmp(&anchor_at_offset_2, &buffer) - .unwrap(), - Ordering::Less - ); + assert_eq!( + anchor_at_offset_0 + .cmp(&anchor_at_offset_0, &buffer) + .unwrap(), + Ordering::Equal + ); + assert_eq!( + anchor_at_offset_1 + .cmp(&anchor_at_offset_1, &buffer) + .unwrap(), + Ordering::Equal + ); + assert_eq!( + anchor_at_offset_2 + .cmp(&anchor_at_offset_2, &buffer) + .unwrap(), + Ordering::Equal + ); - assert_eq!( - anchor_at_offset_1 - .cmp(&anchor_at_offset_0, &buffer) - .unwrap(), - Ordering::Greater - ); - assert_eq!( - anchor_at_offset_2 - .cmp(&anchor_at_offset_1, &buffer) - .unwrap(), - Ordering::Greater - ); - assert_eq!( - anchor_at_offset_2 - .cmp(&anchor_at_offset_0, &buffer) - .unwrap(), - Ordering::Greater - ); - buffer - }); + assert_eq!( + anchor_at_offset_0 + .cmp(&anchor_at_offset_1, &buffer) + .unwrap(), + Ordering::Less + ); + assert_eq!( + anchor_at_offset_1 + .cmp(&anchor_at_offset_2, &buffer) + .unwrap(), + Ordering::Less + ); + assert_eq!( + anchor_at_offset_0 + .cmp(&anchor_at_offset_2, &buffer) + .unwrap(), + Ordering::Less + ); + + assert_eq!( + anchor_at_offset_1 + .cmp(&anchor_at_offset_0, &buffer) + .unwrap(), + Ordering::Greater + ); + assert_eq!( + anchor_at_offset_2 + .cmp(&anchor_at_offset_1, &buffer) + .unwrap(), + Ordering::Greater + ); + assert_eq!( + anchor_at_offset_2 + .cmp(&anchor_at_offset_0, &buffer) + .unwrap(), + Ordering::Greater + ); + buffer }); } - #[test] - fn test_anchors_at_start_and_end() { - App::test((), |ctx| { - ctx.add_model(|ctx| { - let mut buffer = Buffer::new(0, "", ctx); - let before_start_anchor = buffer.anchor_before(0).unwrap(); - let after_end_anchor = buffer.anchor_after(0).unwrap(); - - buffer.edit(vec![0..0], "abc", None).unwrap(); - assert_eq!(buffer.text(), "abc"); - assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); - assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 3); - - let after_start_anchor = buffer.anchor_after(0).unwrap(); - let before_end_anchor = buffer.anchor_before(3).unwrap(); - - buffer.edit(vec![3..3], "def", None).unwrap(); - buffer.edit(vec![0..0], "ghi", None).unwrap(); - assert_eq!(buffer.text(), "ghiabcdef"); - assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); - assert_eq!(after_start_anchor.to_offset(&buffer).unwrap(), 3); - assert_eq!(before_end_anchor.to_offset(&buffer).unwrap(), 6); - assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 9); - buffer - }); + #[gpui::test] + fn test_anchors_at_start_and_end(ctx: &mut gpui::MutableAppContext) { + ctx.add_model(|ctx| { + let mut buffer = Buffer::new(0, "", ctx); + let before_start_anchor = buffer.anchor_before(0).unwrap(); + let after_end_anchor = buffer.anchor_after(0).unwrap(); + + buffer.edit(vec![0..0], "abc", None).unwrap(); + assert_eq!(buffer.text(), "abc"); + assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); + assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 3); + + let after_start_anchor = buffer.anchor_after(0).unwrap(); + let before_end_anchor = buffer.anchor_before(3).unwrap(); + + buffer.edit(vec![3..3], "def", None).unwrap(); + buffer.edit(vec![0..0], "ghi", None).unwrap(); + assert_eq!(buffer.text(), "ghiabcdef"); + assert_eq!(before_start_anchor.to_offset(&buffer).unwrap(), 0); + assert_eq!(after_start_anchor.to_offset(&buffer).unwrap(), 3); + assert_eq!(before_end_anchor.to_offset(&buffer).unwrap(), 6); + assert_eq!(after_end_anchor.to_offset(&buffer).unwrap(), 9); + buffer }); } @@ -3105,115 +3086,111 @@ mod tests { }); } - #[test] - fn test_undo_redo() { - App::test((), |app| { - app.add_model(|ctx| { - let mut buffer = Buffer::new(0, "1234", ctx); - - let edit1 = buffer.edit(vec![1..1], "abx", None).unwrap(); - let edit2 = buffer.edit(vec![3..4], "yzef", None).unwrap(); - let edit3 = buffer.edit(vec![3..5], "cd", None).unwrap(); - assert_eq!(buffer.text(), "1abcdef234"); - - buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1cdef234"); - buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abcdef234"); - - buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abcdx234"); - buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abx234"); - buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abyzef234"); - buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abcdef234"); - - buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1abyzef234"); - buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1yzef234"); - buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); - assert_eq!(buffer.text(), "1234"); - - buffer - }); + #[gpui::test] + fn test_undo_redo(app: &mut gpui::MutableAppContext) { + app.add_model(|ctx| { + let mut buffer = Buffer::new(0, "1234", ctx); + + let edit1 = buffer.edit(vec![1..1], "abx", None).unwrap(); + let edit2 = buffer.edit(vec![3..4], "yzef", None).unwrap(); + let edit3 = buffer.edit(vec![3..5], "cd", None).unwrap(); + assert_eq!(buffer.text(), "1abcdef234"); + + buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1cdef234"); + buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abcdef234"); + + buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abcdx234"); + buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abx234"); + buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abyzef234"); + buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abcdef234"); + + buffer.undo_or_redo(edit3[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1abyzef234"); + buffer.undo_or_redo(edit1[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1yzef234"); + buffer.undo_or_redo(edit2[0].edit_id().unwrap()).unwrap(); + assert_eq!(buffer.text(), "1234"); + + buffer }); } - #[test] - fn test_history() { - App::test((), |app| { - app.add_model(|ctx| { - let mut now = Instant::now(); - let mut buffer = Buffer::new(0, "123456", ctx); - - let (set_id, _) = buffer - .add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap(), None); - buffer.start_transaction_at(Some(set_id), now).unwrap(); - buffer.edit(vec![2..4], "cd", None).unwrap(); - buffer.end_transaction_at(Some(set_id), now, None).unwrap(); - assert_eq!(buffer.text(), "12cd56"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]); - - buffer.start_transaction_at(Some(set_id), now).unwrap(); - buffer - .update_selection_set( - set_id, - buffer.selections_from_ranges(vec![1..3]).unwrap(), - None, - ) - .unwrap(); - buffer.edit(vec![4..5], "e", None).unwrap(); - buffer.end_transaction_at(Some(set_id), now, None).unwrap(); - assert_eq!(buffer.text(), "12cde6"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); - - now += UNDO_GROUP_INTERVAL + Duration::from_millis(1); - buffer.start_transaction_at(Some(set_id), now).unwrap(); - buffer - .update_selection_set( - set_id, - buffer.selections_from_ranges(vec![2..2]).unwrap(), - None, - ) - .unwrap(); - buffer.edit(vec![0..1], "a", None).unwrap(); - buffer.edit(vec![1..1], "b", None).unwrap(); - buffer.end_transaction_at(Some(set_id), now, None).unwrap(); - assert_eq!(buffer.text(), "ab2cde6"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]); - - // Last transaction happened past the group interval, undo it on its - // own. - buffer.undo(None); - assert_eq!(buffer.text(), "12cde6"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); - - // First two transactions happened within the group interval, undo them - // together. - buffer.undo(None); - assert_eq!(buffer.text(), "123456"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]); - - // Redo the first two transactions together. - buffer.redo(None); - assert_eq!(buffer.text(), "12cde6"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); - - // Redo the last transaction on its own. - buffer.redo(None); - assert_eq!(buffer.text(), "ab2cde6"); - assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]); - - buffer - }); + #[gpui::test] + fn test_history(app: &mut gpui::MutableAppContext) { + app.add_model(|ctx| { + let mut now = Instant::now(); + let mut buffer = Buffer::new(0, "123456", ctx); + + let (set_id, _) = + buffer.add_selection_set(buffer.selections_from_ranges(vec![4..4]).unwrap(), None); + buffer.start_transaction_at(Some(set_id), now).unwrap(); + buffer.edit(vec![2..4], "cd", None).unwrap(); + buffer.end_transaction_at(Some(set_id), now, None).unwrap(); + assert_eq!(buffer.text(), "12cd56"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]); + + buffer.start_transaction_at(Some(set_id), now).unwrap(); + buffer + .update_selection_set( + set_id, + buffer.selections_from_ranges(vec![1..3]).unwrap(), + None, + ) + .unwrap(); + buffer.edit(vec![4..5], "e", None).unwrap(); + buffer.end_transaction_at(Some(set_id), now, None).unwrap(); + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); + + now += UNDO_GROUP_INTERVAL + Duration::from_millis(1); + buffer.start_transaction_at(Some(set_id), now).unwrap(); + buffer + .update_selection_set( + set_id, + buffer.selections_from_ranges(vec![2..2]).unwrap(), + None, + ) + .unwrap(); + buffer.edit(vec![0..1], "a", None).unwrap(); + buffer.edit(vec![1..1], "b", None).unwrap(); + buffer.end_transaction_at(Some(set_id), now, None).unwrap(); + assert_eq!(buffer.text(), "ab2cde6"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]); + + // Last transaction happened past the group interval, undo it on its + // own. + buffer.undo(None); + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); + + // First two transactions happened within the group interval, undo them + // together. + buffer.undo(None); + assert_eq!(buffer.text(), "123456"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![4..4]); + + // Redo the first two transactions together. + buffer.redo(None); + assert_eq!(buffer.text(), "12cde6"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![1..3]); + + // Redo the last transaction on its own. + buffer.redo(None); + assert_eq!(buffer.text(), "ab2cde6"); + assert_eq!(buffer.selection_ranges(set_id).unwrap(), vec![3..3]); + + buffer }); } - #[test] - fn test_random_concurrent_edits() { + #[gpui::test] + fn test_random_concurrent_edits(ctx: &mut gpui::MutableAppContext) { use crate::test::Network; const PEERS: usize = 5; @@ -3222,66 +3199,64 @@ mod tests { println!("{:?}", seed); let mut rng = &mut StdRng::seed_from_u64(seed); - App::test((), |ctx| { - let base_text_len = rng.gen_range(0..10); - let base_text = RandomCharIter::new(&mut rng) - .take(base_text_len) - .collect::(); - let mut replica_ids = Vec::new(); - let mut buffers = Vec::new(); - let mut network = Network::new(); - for i in 0..PEERS { - let buffer = - ctx.add_model(|ctx| Buffer::new(i as ReplicaId, base_text.as_str(), ctx)); - buffers.push(buffer); - replica_ids.push(i as u16); - network.add_peer(i as u16); - } - - let mut mutation_count = 10; - loop { - let replica_index = rng.gen_range(0..PEERS); - let replica_id = replica_ids[replica_index]; - buffers[replica_index].update(ctx, |buffer, _| match rng.gen_range(0..=100) { - 0..=50 if mutation_count != 0 => { - let (_, _, ops) = buffer.randomly_mutate(&mut rng, None); - network.broadcast(replica_id, ops, &mut rng); - mutation_count -= 1; - } - 51..=70 if mutation_count != 0 => { - let ops = buffer.randomly_undo_redo(&mut rng); - network.broadcast(replica_id, ops, &mut rng); - mutation_count -= 1; - } - 71..=100 if network.has_unreceived(replica_id) => { - buffer - .apply_ops(network.receive(replica_id, &mut rng), None) - .unwrap(); - } - _ => {} - }); + let base_text_len = rng.gen_range(0..10); + let base_text = RandomCharIter::new(&mut rng) + .take(base_text_len) + .collect::(); + let mut replica_ids = Vec::new(); + let mut buffers = Vec::new(); + let mut network = Network::new(); + for i in 0..PEERS { + let buffer = + ctx.add_model(|ctx| Buffer::new(i as ReplicaId, base_text.as_str(), ctx)); + buffers.push(buffer); + replica_ids.push(i as u16); + network.add_peer(i as u16); + } - if mutation_count == 0 && network.is_idle() { - break; + let mut mutation_count = 10; + loop { + let replica_index = rng.gen_range(0..PEERS); + let replica_id = replica_ids[replica_index]; + buffers[replica_index].update(ctx, |buffer, _| match rng.gen_range(0..=100) { + 0..=50 if mutation_count != 0 => { + let (_, _, ops) = buffer.randomly_mutate(&mut rng, None); + network.broadcast(replica_id, ops, &mut rng); + mutation_count -= 1; } - } + 51..=70 if mutation_count != 0 => { + let ops = buffer.randomly_undo_redo(&mut rng); + network.broadcast(replica_id, ops, &mut rng); + mutation_count -= 1; + } + 71..=100 if network.has_unreceived(replica_id) => { + buffer + .apply_ops(network.receive(replica_id, &mut rng), None) + .unwrap(); + } + _ => {} + }); - let first_buffer = buffers[0].read(ctx); - for buffer in &buffers[1..] { - let buffer = buffer.read(ctx); - assert_eq!(buffer.text(), first_buffer.text()); - assert_eq!( - buffer.all_selections().collect::>(), - first_buffer.all_selections().collect::>() - ); - assert_eq!( - buffer.all_selection_ranges().collect::>(), - first_buffer - .all_selection_ranges() - .collect::>() - ); + if mutation_count == 0 && network.is_idle() { + break; } - }); + } + + let first_buffer = buffers[0].read(ctx); + for buffer in &buffers[1..] { + let buffer = buffer.read(ctx); + assert_eq!(buffer.text(), first_buffer.text()); + assert_eq!( + buffer.all_selections().collect::>(), + first_buffer.all_selections().collect::>() + ); + assert_eq!( + buffer.all_selection_ranges().collect::>(), + first_buffer + .all_selection_ranges() + .collect::>() + ); + } } } diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index a76f558b96784d5686a5a3b1d0c75e15285a4b24..2e54336e681a5deaf59e21d7dea78990b5251994 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -2511,222 +2511,207 @@ impl workspace::ItemView for BufferView { mod tests { use super::*; use crate::{editor::Point, settings, test::sample_text}; - use gpui::App; use unindent::Unindent; - #[test] - fn test_selection_with_mouse() { - App::test((), |app| { - let buffer = - app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, buffer_view) = - app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - - buffer_view.update(app, |view, ctx| { - view.begin_selection(DisplayPoint::new(2, 2), false, ctx); - }); + #[gpui::test] + fn test_selection_with_mouse(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, buffer_view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] - ); + buffer_view.update(app, |view, ctx| { + view.begin_selection(DisplayPoint::new(2, 2), false, ctx); + }); - buffer_view.update(app, |view, ctx| { - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); - }); + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] + ); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); + buffer_view.update(app, |view, ctx| { + view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); + }); - buffer_view.update(app, |view, ctx| { - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); - }); + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); + buffer_view.update(app, |view, ctx| { + view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); + }); - buffer_view.update(app, |view, ctx| { - view.end_selection(ctx); - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); - }); + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); + buffer_view.update(app, |view, ctx| { + view.end_selection(ctx); + view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); + }); - buffer_view.update(app, |view, ctx| { - view.begin_selection(DisplayPoint::new(3, 3), true, ctx); - view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx); - }); + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [ - DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) - ] - ); + buffer_view.update(app, |view, ctx| { + view.begin_selection(DisplayPoint::new(3, 3), true, ctx); + view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx); + }); - buffer_view.update(app, |view, ctx| { - view.end_selection(ctx); - }); + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [ + DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) + ] + ); - let view = buffer_view.read(app); - let selections = view - .selections_in_range( - DisplayPoint::zero()..view.max_point(app.as_ref()), - app.as_ref(), - ) - .collect::>(); - assert_eq!( - selections, - [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] - ); + buffer_view.update(app, |view, ctx| { + view.end_selection(ctx); }); + + let view = buffer_view.read(app); + let selections = view + .selections_in_range( + DisplayPoint::zero()..view.max_point(app.as_ref()), + app.as_ref(), + ) + .collect::>(); + assert_eq!( + selections, + [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] + ); } - #[test] - fn test_canceling_pending_selection() { - App::test((), |app| { - let buffer = - app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + #[gpui::test] + fn test_canceling_pending_selection(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.begin_selection(DisplayPoint::new(2, 2), false, ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] - ); + view.update(app, |view, ctx| { + view.begin_selection(DisplayPoint::new(2, 2), false, ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] + ); - view.update(app, |view, ctx| { - view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); + view.update(app, |view, ctx| { + view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); - view.update(app, |view, ctx| { - view.cancel(&(), ctx); - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); + view.update(app, |view, ctx| { + view.cancel(&(), ctx); + view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); } - #[test] - fn test_cancel() { - App::test((), |app| { - let buffer = - app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + #[gpui::test] + fn test_cancel(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.begin_selection(DisplayPoint::new(3, 4), false, ctx); - view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); - view.end_selection(ctx); + view.update(app, |view, ctx| { + view.begin_selection(DisplayPoint::new(3, 4), false, ctx); + view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx); + view.end_selection(ctx); - view.begin_selection(DisplayPoint::new(0, 1), true, ctx); - view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), ctx); - view.end_selection(ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), - ] - ); + view.begin_selection(DisplayPoint::new(0, 1), true, ctx); + view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), ctx); + view.end_selection(ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), + ] + ); - view.update(app, |view, ctx| view.cancel(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] - ); + view.update(app, |view, ctx| view.cancel(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] + ); - view.update(app, |view, ctx| view.cancel(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] - ); - }); + view.update(app, |view, ctx| view.cancel(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] + ); } - #[test] - fn test_layout_line_numbers() { - App::test((), |app| { - let layout_cache = TextLayoutCache::new(app.platform().fonts()); - let font_cache = app.font_cache().clone(); + #[gpui::test] + fn test_layout_line_numbers(app: &mut gpui::MutableAppContext) { + let layout_cache = TextLayoutCache::new(app.platform().fonts()); + let font_cache = app.font_cache().clone(); - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); - let settings = settings::channel(&font_cache).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); + let settings = settings::channel(&font_cache).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - let layouts = view - .read(app) - .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref()) - .unwrap(); - assert_eq!(layouts.len(), 6); - }) + let layouts = view + .read(app) + .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref()) + .unwrap(); + assert_eq!(layouts.len(), 6); } - #[test] - fn test_fold() { - App::test((), |app| { - let buffer = app.add_model(|ctx| { - Buffer::new( - 0, - " + #[gpui::test] + fn test_fold(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| { + Buffer::new( + 0, + " impl Foo { // Hello! @@ -2743,24 +2728,20 @@ mod tests { } } " - .unindent(), - ctx, - ) - }); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], - ctx, - ) + .unindent(), + ctx, + ) + }); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); + + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx) .unwrap(); - view.fold(&(), ctx); - assert_eq!( - view.text(ctx.as_ref()), - " + view.fold(&(), ctx); + assert_eq!( + view.text(ctx.as_ref()), + " impl Foo { // Hello! @@ -2775,23 +2756,23 @@ mod tests { } } " - .unindent(), - ); + .unindent(), + ); - view.fold(&(), ctx); - assert_eq!( - view.text(ctx.as_ref()), - " + view.fold(&(), ctx); + assert_eq!( + view.text(ctx.as_ref()), + " impl Foo {… } " - .unindent(), - ); + .unindent(), + ); - view.unfold(&(), ctx); - assert_eq!( - view.text(ctx.as_ref()), - " + view.unfold(&(), ctx); + assert_eq!( + view.text(ctx.as_ref()), + " impl Foo { // Hello! @@ -2806,1109 +2787,1055 @@ mod tests { } } " - .unindent(), - ); + .unindent(), + ); - view.unfold(&(), ctx); - assert_eq!(view.text(ctx.as_ref()), buffer.read(ctx).text()); - }); + view.unfold(&(), ctx); + assert_eq!(view.text(ctx.as_ref()), buffer.read(ctx).text()); }); } - #[test] - fn test_move_cursor() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - - buffer.update(app, |buffer, ctx| { - buffer - .edit( - vec![ - Point::new(1, 0)..Point::new(1, 0), - Point::new(1, 1)..Point::new(1, 1), - ], - "\t", - Some(ctx), - ) - .unwrap(); - }); - - view.update(app, |view, ctx| { - view.move_down(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] - ); - - view.move_right(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] - ); - - view.move_left(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] - ); - - view.move_up(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] - ); - - view.move_to_end(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)] - ); - - view.move_to_beginning(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] - ); - - view.select_display_ranges( - &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], - ctx, - ) - .unwrap(); - view.select_to_beginning(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)] - ); - - view.select_to_end(&(), ctx); - assert_eq!( - view.selection_ranges(ctx.as_ref()), - &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)] - ); - }); - }); - } + #[gpui::test] + fn test_move_cursor(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 6), ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - #[test] - fn test_beginning_end_of_line() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), + buffer.update(app, |buffer, ctx| { + buffer + .edit( + vec![ + Point::new(1, 0)..Point::new(1, 0), + Point::new(1, 1)..Point::new(1, 1), ], - ctx, + "\t", + Some(ctx), ) .unwrap(); - }); - - view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), - ] - ); + }); - view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); + view.update(app, |view, ctx| { + view.move_down(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); - view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); + view.move_right(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)] ); - view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx)); + view.move_left(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)] ); - // Moving to the end of line again is a no-op. - view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx)); + view.move_up(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); - view.update(app, |view, ctx| { - view.move_left(&(), ctx); - view.select_to_beginning_of_line(&true, ctx); - }); + view.move_to_end(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)] ); - view.update(app, |view, ctx| { - view.select_to_beginning_of_line(&true, ctx) - }); + view.move_to_beginning(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] ); - view.update(app, |view, ctx| { - view.select_to_beginning_of_line(&true, ctx) - }); + view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], ctx) + .unwrap(); + view.select_to_beginning(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)] ); - view.update(app, |view, ctx| view.select_to_end_of_line(&(), ctx)); + view.select_to_end(&(), ctx); assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5), - ] + view.selection_ranges(ctx.as_ref()), + &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)] ); + }); + } - view.update(app, |view, ctx| view.delete_to_end_of_line(&(), ctx)); - assert_eq!(view.read(app).text(app.as_ref()), "ab\n de"); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), + #[gpui::test] + fn test_beginning_end_of_line(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\n def", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), - ] - ); - - view.update(app, |view, ctx| view.delete_to_beginning_of_line(&(), ctx)); - assert_eq!(view.read(app).text(app.as_ref()), "\n"); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ] - ); + ], + ctx, + ) + .unwrap(); }); - } - #[test] - fn test_prev_next_word_boundary() { - App::test((), |app| { - let buffer = app - .add_model(|ctx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), - DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4), - ], - ctx, - ) - .unwrap(); - }); + view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3), - ] - ); + view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7), - DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), - ] - ); + view.update(app, |view, ctx| view.move_to_beginning_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), - DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), - ] - ); + view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ] - ); + // Moving to the end of line again is a no-op. + view.update(app, |view, ctx| view.move_to_end_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24), - ] - ); + view.update(app, |view, ctx| { + view.move_left(&(), ctx); + view.select_to_beginning_of_line(&true, ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), + ] + ); - view.update(app, |view, ctx| { - view.move_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23), - ] - ); + view.update(app, |view, ctx| { + view.select_to_beginning_of_line(&true, ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0), + ] + ); - view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24), - ] - ); + view.update(app, |view, ctx| { + view.select_to_beginning_of_line(&true, ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2), + ] + ); - view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ] - ); + view.update(app, |view, ctx| view.select_to_end_of_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5), + ] + ); - view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7), - DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), - ] - ); + view.update(app, |view, ctx| view.delete_to_end_of_line(&(), ctx)); + assert_eq!(view.read(app).text(app.as_ref()), "ab\n de"); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4), + ] + ); - view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), - DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), - ] - ); + view.update(app, |view, ctx| view.delete_to_beginning_of_line(&(), ctx)); + assert_eq!(view.read(app).text(app.as_ref()), "\n"); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + ] + ); + } - view.update(app, |view, ctx| { - view.move_right(&(), ctx); - view.select_to_previous_word_boundary(&(), ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), + #[gpui::test] + fn test_prev_next_word_boundary(app: &mut gpui::MutableAppContext) { + let buffer = + app.add_model(|ctx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( &[ - DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2), - ] - ); + DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11), + DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4), + ], + ctx, + ) + .unwrap(); + }); - view.update(app, |view, ctx| { - view.select_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 0), - ] - ); + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3), + ] + ); - view.update(app, |view, ctx| view.select_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2), - ] - ); + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7), + DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), + ] + ); - view.update(app, |view, ctx| view.delete_to_next_word_boundary(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "use std::s::{foo, bar}\n\n {az.qux()}" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 10)..DisplayPoint::new(0, 10), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3), - ] - ); + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), + DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), + ] + ); - view.update(app, |view, ctx| { - view.delete_to_previous_word_boundary(&(), ctx) - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "use std::::{foo, bar}\n\n az.qux()}" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), - DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), - ] - ); + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) }); - } + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + ] + ); - #[test] - fn test_backspace() { - App::test((), |app| { - let buffer = app.add_model(|ctx| { - Buffer::new( - 0, - "one two three\nfour five six\nseven eight nine\nten\n", - ctx, - ) - }); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - // an empty selection - the preceding character is deleted - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - // one character selected - it is deleted - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - // a line suffix selected - it is deleted - DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), - ], - ctx, - ) - .unwrap(); - view.backspace(&(), ctx); - }); + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24), + ] + ); - assert_eq!( - buffer.read(app).text(), - "oe two three\nfou five six\nseven ten\n" - ); - }) + view.update(app, |view, ctx| { + view.move_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23), + ] + ); + + view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24), + ] + ); + + view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + ] + ); + + view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7), + DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), + ] + ); + + view.update(app, |view, ctx| view.move_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), + DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), + ] + ); + + view.update(app, |view, ctx| { + view.move_right(&(), ctx); + view.select_to_previous_word_boundary(&(), ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2), + ] + ); + + view.update(app, |view, ctx| { + view.select_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 0), + ] + ); + + view.update(app, |view, ctx| view.select_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2), + ] + ); + + view.update(app, |view, ctx| view.delete_to_next_word_boundary(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "use std::s::{foo, bar}\n\n {az.qux()}" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 10)..DisplayPoint::new(0, 10), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3), + ] + ); + + view.update(app, |view, ctx| { + view.delete_to_previous_word_boundary(&(), ctx) + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "use std::::{foo, bar}\n\n az.qux()}" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9), + DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2), + ] + ); } - #[test] - fn test_delete() { - App::test((), |app| { - let buffer = app.add_model(|ctx| { - Buffer::new( - 0, - "one two three\nfour five six\nseven eight nine\nten\n", - ctx, - ) - }); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = - app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - // an empty selection - the following character is deleted - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - // one character selected - it is deleted - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - // a line suffix selected - it is deleted - DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), - ], - ctx, - ) - .unwrap(); - view.delete(&(), ctx); - }); + #[gpui::test] + fn test_backspace(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| { + Buffer::new( + 0, + "one two three\nfour five six\nseven eight nine\nten\n", + ctx, + ) + }); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); - assert_eq!( - buffer.read(app).text(), - "on two three\nfou five six\nseven ten\n" - ); - }) - } - - #[test] - fn test_delete_line() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ], - ctx, - ) - .unwrap(); - view.delete_line(&(), ctx); - }); - assert_eq!(view.read(app).text(app.as_ref()), "ghi"); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1) - ] - ); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + // an empty selection - the preceding character is deleted + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + // one character selected - it is deleted + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + // a line suffix selected - it is deleted + DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), + ], + ctx, + ) + .unwrap(); + view.backspace(&(), ctx); + }); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], - ctx, - ) - .unwrap(); - view.delete_line(&(), ctx); - }); - assert_eq!(view.read(app).text(app.as_ref()), "ghi\n"); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)] - ); + assert_eq!( + buffer.read(app).text(), + "oe two three\nfou five six\nseven ten\n" + ); + } + + #[gpui::test] + fn test_delete(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| { + Buffer::new( + 0, + "one two three\nfour five six\nseven eight nine\nten\n", + ctx, + ) + }); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)); + + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + // an empty selection - the following character is deleted + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + // one character selected - it is deleted + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + // a line suffix selected - it is deleted + DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0), + ], + ctx, + ) + .unwrap(); + view.delete(&(), ctx); }); + + assert_eq!( + buffer.read(app).text(), + "on two three\nfou five six\nseven ten\n" + ); } - #[test] - fn test_duplicate_line() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ], - ctx, - ) - .unwrap(); - view.duplicate_line(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "abc\nabc\ndef\ndef\nghi\n\n" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ + #[gpui::test] + fn test_delete_line(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), - DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0), - ] - ); + ], + ctx, + ) + .unwrap(); + view.delete_line(&(), ctx); + }); + assert_eq!(view.read(app).text(app.as_ref()), "ghi"); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0), + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1) + ] + ); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1), - ], - ctx, - ) + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], ctx) .unwrap(); - view.duplicate_line(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "abc\ndef\nghi\nabc\ndef\nghi\n" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1), - DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1), - ] - ); + view.delete_line(&(), ctx); }); + assert_eq!(view.read(app).text(app.as_ref()), "ghi\n"); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)] + ); } - #[test] - fn test_move_line_up_down() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(10, 5), ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.fold_ranges( - vec![ - Point::new(0, 2)..Point::new(1, 2), - Point::new(2, 3)..Point::new(4, 1), - Point::new(7, 0)..Point::new(8, 4), - ], - ctx, - ); - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), - DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2), - ], - ctx, - ) - .unwrap(); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj" - ); + #[gpui::test] + fn test_duplicate_line(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), + ], + ctx, + ) + .unwrap(); + view.duplicate_line(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "abc\nabc\ndef\ndef\nghi\n\n" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), + DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2), + DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), + DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0), + ] + ); - view.update(app, |view, ctx| view.move_line_up(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), - DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), - DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) - ] - ); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndef\nghi\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1), + ], + ctx, + ) + .unwrap(); + view.duplicate_line(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "abc\ndef\nghi\nabc\ndef\nghi\n" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1), + DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1), + ] + ); + } - view.update(app, |view, ctx| view.move_line_down(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), + #[gpui::test] + fn test_move_line_up_down(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(10, 5), ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.fold_ranges( vec![ - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), - DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) - ] + Point::new(0, 2)..Point::new(1, 2), + Point::new(2, 3)..Point::new(4, 1), + Point::new(7, 0)..Point::new(8, 4), + ], + ctx, ); - - view.update(app, |view, ctx| view.move_line_down(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), - DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) - ] - ); - - view.update(app, |view, ctx| view.move_line_up(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), - DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), - DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) - ] - ); + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2), + ], + ctx, + ) + .unwrap(); }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj" + ); + + view.update(app, |view, ctx| view.move_line_up(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_down(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_down(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(4, 2), + DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2) + ] + ); + + view.update(app, |view, ctx| view.move_line_up(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(2, 2)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2) + ] + ); } - #[test] - fn test_clipboard() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, "one two three four five six ", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let view = app - .add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)) - .1; + #[gpui::test] + fn test_clipboard(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "one two three four five six ", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let view = app + .add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx)) + .1; - // Cut with three selections. Clipboard text is divided into three slices. - view.update(app, |view, ctx| { - view.select_ranges(vec![0..4, 8..14, 19..24], false, ctx); - view.cut(&(), ctx); - }); - assert_eq!(view.read(app).text(app.as_ref()), "two four six "); + // Cut with three selections. Clipboard text is divided into three slices. + view.update(app, |view, ctx| { + view.select_ranges(vec![0..4, 8..14, 19..24], false, ctx); + view.cut(&(), ctx); + }); + assert_eq!(view.read(app).text(app.as_ref()), "two four six "); - // Paste with three cursors. Each cursor pastes one slice of the clipboard text. - view.update(app, |view, ctx| { - view.select_ranges(vec![4..4, 9..9, 13..13], false, ctx); - view.paste(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "two one four three six five " - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(0, 8)..DisplayPoint::new(0, 8), - DisplayPoint::new(0, 19)..DisplayPoint::new(0, 19), - DisplayPoint::new(0, 28)..DisplayPoint::new(0, 28) - ] - ); + // Paste with three cursors. Each cursor pastes one slice of the clipboard text. + view.update(app, |view, ctx| { + view.select_ranges(vec![4..4, 9..9, 13..13], false, ctx); + view.paste(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "two one four three six five " + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 8)..DisplayPoint::new(0, 8), + DisplayPoint::new(0, 19)..DisplayPoint::new(0, 19), + DisplayPoint::new(0, 28)..DisplayPoint::new(0, 28) + ] + ); - // Paste again but with only two cursors. Since the number of cursors doesn't - // match the number of slices in the clipboard, the entire clipboard text - // is pasted at each cursor. - view.update(app, |view, ctx| { - view.select_ranges(vec![0..0, 28..28], false, ctx); - view.insert(&"( ".to_string(), ctx); - view.paste(&(), ctx); - view.insert(&") ".to_string(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "( one three five ) two one four three six five ( one three five ) " - ); + // Paste again but with only two cursors. Since the number of cursors doesn't + // match the number of slices in the clipboard, the entire clipboard text + // is pasted at each cursor. + view.update(app, |view, ctx| { + view.select_ranges(vec![0..0, 28..28], false, ctx); + view.insert(&"( ".to_string(), ctx); + view.paste(&(), ctx); + view.insert(&") ".to_string(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "( one three five ) two one four three six five ( one three five ) " + ); - view.update(app, |view, ctx| { - view.select_ranges(vec![0..0], false, ctx); - view.insert(&"123\n4567\n89\n".to_string(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "123\n4567\n89\n( one three five ) two one four three six five ( one three five ) " - ); + view.update(app, |view, ctx| { + view.select_ranges(vec![0..0], false, ctx); + view.insert(&"123\n4567\n89\n".to_string(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "123\n4567\n89\n( one three five ) two one four three six five ( one three five ) " + ); - // Cut with three selections, one of which is full-line. - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1), - ], - ctx, - ) - .unwrap(); - view.cut(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "13\n9\n( one three five ) two one four three six five ( one three five ) " - ); + // Cut with three selections, one of which is full-line. + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1), + ], + ctx, + ) + .unwrap(); + view.cut(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "13\n9\n( one three five ) two one four three six five ( one three five ) " + ); - // Paste with three selections, noticing how the copied selection that was full-line - // gets inserted before the second cursor. - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3), - ], - ctx, - ) - .unwrap(); - view.paste(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "123\n4567\n9\n( 8ne three five ) two one four three six five ( one three five ) " - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), + // Paste with three selections, noticing how the copied selection that was full-line + // gets inserted before the second cursor. + view.update(app, |view, ctx| { + view.select_display_ranges( &[ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), - DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3), - ] - ); + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3), + ], + ctx, + ) + .unwrap(); + view.paste(&(), ctx); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "123\n4567\n9\n( 8ne three five ) two one four three six five ( one three five ) " + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3), + ] + ); - // Copy with a single cursor only, which writes the whole line into the clipboard. - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], - ctx, - ) + // Copy with a single cursor only, which writes the whole line into the clipboard. + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], ctx) .unwrap(); - view.copy(&(), ctx); - }); + view.copy(&(), ctx); + }); - // Paste with three selections, noticing how the copied full-line selection is inserted - // before the empty selections but replaces the selection that is non-empty. - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2), - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), - ], - ctx, - ) - .unwrap(); - view.paste(&(), ctx); - }); - assert_eq!( + // Paste with three selections, noticing how the copied full-line selection is inserted + // before the empty selections but replaces the selection that is non-empty. + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2), + DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), + ], + ctx, + ) + .unwrap(); + view.paste(&(), ctx); + }); + assert_eq!( view.read(app).text(app.as_ref()), "123\n123\n123\n67\n123\n9\n( 8ne three five ) two one four three six five ( one three five ) " ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[ - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1), - ] - ); - }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[ + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), + DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1), + ] + ); } - #[test] - fn test_select_all() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\nde\nfgh", ctx)); - let settings = settings::channel(&app.font_cache()).unwrap().1; - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |b, ctx| b.select_all(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)] - ); - }); + #[gpui::test] + fn test_select_all(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\nde\nfgh", ctx)); + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |b, ctx| b.select_all(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)] + ); } - #[test] - fn test_select_line() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), - ], - ctx, - ) - .unwrap(); - view.select_line(&(), ctx); - }); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0), - DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0), - ] - ); + #[gpui::test] + fn test_select_line(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.select_display_ranges( + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), + ], + ctx, + ) + .unwrap(); + view.select_line(&(), ctx); + }); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0), + DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0), + ] + ); - view.update(app, |view, ctx| view.select_line(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0), - DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5), - ] - ); + view.update(app, |view, ctx| view.select_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0), + DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5), + ] + ); - view.update(app, |view, ctx| view.select_line(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)] - ); - }); + view.update(app, |view, ctx| view.select_line(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)] + ); } - #[test] - fn test_split_selection_into_lines() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.fold_ranges( - vec![ - Point::new(0, 2)..Point::new(1, 2), - Point::new(2, 3)..Point::new(4, 1), - Point::new(7, 0)..Point::new(8, 4), - ], - ctx, - ); - view.select_display_ranges( - &[ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), - ], - ctx, - ) - .unwrap(); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" - ); - - view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx)); - assert_eq!( - view.read(app).text(app.as_ref()), - "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" + #[gpui::test] + fn test_split_selection_into_lines(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + view.update(app, |view, ctx| { + view.fold_ranges( + vec![ + Point::new(0, 2)..Point::new(1, 2), + Point::new(2, 3)..Point::new(4, 1), + Point::new(7, 0)..Point::new(8, 4), + ], + ctx, ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + view.select_display_ranges( + &[ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2) - ] - ); + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2), + ], + ctx, + ) + .unwrap(); + }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" + ); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)], - ctx, - ) + view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx)); + assert_eq!( + view.read(app).text(app.as_ref()), + "aa…bbb\nccc…eeee\nfffff\nggggg\n…i" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2) + ] + ); + + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)], ctx) .unwrap(); - view.split_selection_into_lines(&(), ctx); - }); - assert_eq!( - view.read(app).text(app.as_ref()), - "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i" - ); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - [ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), - DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), - DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5), - DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5), - DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5), - DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5), - DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0) - ] - ); + view.split_selection_into_lines(&(), ctx); }); + assert_eq!( + view.read(app).text(app.as_ref()), + "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i" + ); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), + DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), + DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5), + DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5), + DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5), + DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5), + DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0) + ] + ); } - #[test] - fn test_add_selection_above_below() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", ctx)); - let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); + #[gpui::test] + fn test_add_selection_above_below(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; + let buffer = app.add_model(|ctx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", ctx)); + let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx)); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], - ctx, - ) + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], ctx) .unwrap(); - }); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) - ] - ); + }); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) + ] + ); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) - ] - ); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) + ] + ); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] - ); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] + ); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) - ] - ); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) + ] + ); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) - ] - ); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) + ] + ); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], - ctx, - ) + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], ctx) .unwrap(); - }); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) - ] - ); + }); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) + ] + ); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) - ] - ); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) + ] + ); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] - ); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] + ); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] - ); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] + ); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], - ctx, - ) + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], ctx) .unwrap(); - }); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - ] - ); + }); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + ] + ); - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4), - ] - ); + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4), + ] + ); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - ] - ); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + ] + ); - view.update(app, |view, ctx| { - view.select_display_ranges( - &[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], - ctx, - ) + view.update(app, |view, ctx| { + view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], ctx) .unwrap(); - }); - view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), - ] - ); - - view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); - assert_eq!( - view.read(app).selection_ranges(app.as_ref()), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), - ] - ); }); + view.update(app, |view, ctx| view.add_selection_above(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), + ] + ); + + view.update(app, |view, ctx| view.add_selection_below(&(), ctx)); + assert_eq!( + view.read(app).selection_ranges(app.as_ref()), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), + ] + ); } impl BufferView { diff --git a/zed/src/editor/display_map/fold_map.rs b/zed/src/editor/display_map/fold_map.rs index e7f538c686e2a60bc8c9f06e4f9f111609ed9171..e33013565f66fc1a2d0a650850a1ad7e46e751e0 100644 --- a/zed/src/editor/display_map/fold_map.rs +++ b/zed/src/editor/display_map/fold_map.rs @@ -671,176 +671,163 @@ mod tests { use super::*; use crate::test::sample_text; use buffer::ToPoint; - use gpui::App; - #[test] - fn test_basic_folds() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(2, 4)..Point::new(4, 1), - ], - app.as_ref(), - ) - .unwrap(); - assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee"); - - buffer.update(app, |buffer, ctx| { - buffer - .edit( - vec![ - Point::new(0, 0)..Point::new(0, 1), - Point::new(2, 3)..Point::new(2, 3), - ], - "123", - Some(ctx), - ) - .unwrap(); - }); - assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee"); - - buffer.update(app, |buffer, ctx| { - let start_version = buffer.version.clone(); - buffer - .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx)) - .unwrap(); - buffer.edits_since(start_version).collect::>() - }); - assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); + #[gpui::test] + fn test_basic_folds(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(2, 4)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee"); + + buffer.update(app, |buffer, ctx| { + buffer + .edit( + vec![ + Point::new(0, 0)..Point::new(0, 1), + Point::new(2, 3)..Point::new(2, 3), + ], + "123", + Some(ctx), + ) + .unwrap(); + }); + assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee"); - map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref()) + buffer.update(app, |buffer, ctx| { + let start_version = buffer.version.clone(); + buffer + .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx)) .unwrap(); - assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee"); + buffer.edits_since(start_version).collect::>() }); + assert_eq!(map.text(app.as_ref()), "123a…c123456eee"); + + map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref()) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee"); } - #[test] - fn test_adjacent_folds() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx)); + #[gpui::test] + fn test_adjacent_folds(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx)); - { - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + { + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - map.fold(vec![5..8], app.as_ref()).unwrap(); - map.check_invariants(app.as_ref()); - assert_eq!(map.text(app.as_ref()), "abcde…ijkl"); + map.fold(vec![5..8], app.as_ref()).unwrap(); + map.check_invariants(app.as_ref()); + assert_eq!(map.text(app.as_ref()), "abcde…ijkl"); - // Create an fold adjacent to the start of the first fold. - map.fold(vec![0..1, 2..5], app.as_ref()).unwrap(); - map.check_invariants(app.as_ref()); - assert_eq!(map.text(app.as_ref()), "…b…ijkl"); + // Create an fold adjacent to the start of the first fold. + map.fold(vec![0..1, 2..5], app.as_ref()).unwrap(); + map.check_invariants(app.as_ref()); + assert_eq!(map.text(app.as_ref()), "…b…ijkl"); - // Create an fold adjacent to the end of the first fold. - map.fold(vec![11..11, 8..10], app.as_ref()).unwrap(); - map.check_invariants(app.as_ref()); - assert_eq!(map.text(app.as_ref()), "…b…kl"); - } + // Create an fold adjacent to the end of the first fold. + map.fold(vec![11..11, 8..10], app.as_ref()).unwrap(); + map.check_invariants(app.as_ref()); + assert_eq!(map.text(app.as_ref()), "…b…kl"); + } - { - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + { + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - // Create two adjacent folds. - map.fold(vec![0..2, 2..5], app.as_ref()).unwrap(); - map.check_invariants(app.as_ref()); - assert_eq!(map.text(app.as_ref()), "…fghijkl"); + // Create two adjacent folds. + map.fold(vec![0..2, 2..5], app.as_ref()).unwrap(); + map.check_invariants(app.as_ref()); + assert_eq!(map.text(app.as_ref()), "…fghijkl"); - // Edit within one of the folds. - buffer.update(app, |buffer, ctx| { - let version = buffer.version(); - buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap(); - buffer.edits_since(version).collect::>() - }); - map.check_invariants(app.as_ref()); - assert_eq!(map.text(app.as_ref()), "12345…fghijkl"); - } - }); + // Edit within one of the folds. + buffer.update(app, |buffer, ctx| { + let version = buffer.version(); + buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap(); + buffer.edits_since(version).collect::>() + }); + map.check_invariants(app.as_ref()); + assert_eq!(map.text(app.as_ref()), "12345…fghijkl"); + } } - #[test] - fn test_overlapping_folds() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(0, 4)..Point::new(1, 0), - Point::new(1, 2)..Point::new(3, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app.as_ref(), - ) - .unwrap(); - assert_eq!(map.text(app.as_ref()), "aa…eeeee"); - }) + #[gpui::test] + fn test_overlapping_folds(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(0, 4)..Point::new(1, 0), + Point::new(1, 2)..Point::new(3, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…eeeee"); } - #[test] - fn test_merging_folds_via_edit() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app.as_ref(), - ) - .unwrap(); - assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee"); - - buffer.update(app, |buffer, ctx| { - buffer - .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx)) - .unwrap(); - }); - assert_eq!(map.text(app.as_ref()), "aa…eeeee"); + #[gpui::test] + fn test_merging_folds_via_edit(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee"); + + buffer.update(app, |buffer, ctx| { + buffer + .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx)) + .unwrap(); }); + assert_eq!(map.text(app.as_ref()), "aa…eeeee"); } - #[test] - fn test_folds_in_range() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - let buffer = buffer.read(app); - - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(0, 4)..Point::new(1, 0), - Point::new(1, 2)..Point::new(3, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app.as_ref(), - ) - .unwrap(); - let fold_ranges = map - .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref()) - .unwrap() - .map(|fold| { - fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap() - }) - .collect::>(); - assert_eq!( - fold_ranges, - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(1, 2)..Point::new(3, 2) - ] - ); - }); + #[gpui::test] + fn test_folds_in_range(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx)); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + let buffer = buffer.read(app); + + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(0, 4)..Point::new(1, 0), + Point::new(1, 2)..Point::new(3, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + let fold_ranges = map + .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref()) + .unwrap() + .map(|fold| fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap()) + .collect::>(); + assert_eq!( + fold_ranges, + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(1, 2)..Point::new(3, 2) + ] + ); } - #[test] - fn test_random_folds() { + #[gpui::test] + fn test_random_folds(app: &mut gpui::MutableAppContext) { use crate::editor::ToPoint; use crate::util::RandomCharIter; use rand::prelude::*; @@ -863,203 +850,197 @@ mod tests { dbg!(seed); let mut rng = StdRng::seed_from_u64(seed); - App::test((), |app| { - let buffer = app.add_model(|ctx| { - let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); - Buffer::new(0, text, ctx) - }); - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); - - for _ in 0..operations { - log::info!("text: {:?}", buffer.read(app).text()); - match rng.gen_range(0..=100) { - 0..=34 => { - let buffer = buffer.read(app); - let mut to_fold = Vec::new(); - for _ in 0..rng.gen_range(1..=5) { - let end = rng.gen_range(0..=buffer.len()); - let start = rng.gen_range(0..=end); - to_fold.push(start..end); - } - log::info!("folding {:?}", to_fold); - map.fold(to_fold, app.as_ref()).unwrap(); - } - 35..=59 if !map.folds.is_empty() => { - let buffer = buffer.read(app); - let mut to_unfold = Vec::new(); - for _ in 0..rng.gen_range(1..=3) { - let end = rng.gen_range(0..=buffer.len()); - let start = rng.gen_range(0..=end); - to_unfold.push(start..end); - } - log::info!("unfolding {:?}", to_unfold); - map.unfold(to_unfold, app.as_ref()).unwrap(); + let buffer = app.add_model(|ctx| { + let len = rng.gen_range(0..10); + let text = RandomCharIter::new(&mut rng).take(len).collect::(); + Buffer::new(0, text, ctx) + }); + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + + for _ in 0..operations { + log::info!("text: {:?}", buffer.read(app).text()); + match rng.gen_range(0..=100) { + 0..=34 => { + let buffer = buffer.read(app); + let mut to_fold = Vec::new(); + for _ in 0..rng.gen_range(1..=5) { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + to_fold.push(start..end); } - _ => { - let edits = buffer.update(app, |buffer, ctx| { - let start_version = buffer.version.clone(); - let edit_count = rng.gen_range(1..=5); - buffer.randomly_edit(&mut rng, edit_count, Some(ctx)); - buffer.edits_since(start_version).collect::>() - }); - log::info!("editing {:?}", edits); + log::info!("folding {:?}", to_fold); + map.fold(to_fold, app.as_ref()).unwrap(); + } + 35..=59 if !map.folds.is_empty() => { + let buffer = buffer.read(app); + let mut to_unfold = Vec::new(); + for _ in 0..rng.gen_range(1..=3) { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + to_unfold.push(start..end); } + log::info!("unfolding {:?}", to_unfold); + map.unfold(to_unfold, app.as_ref()).unwrap(); } - map.check_invariants(app.as_ref()); - - let buffer = map.buffer.read(app); - let mut expected_text = buffer.text(); - let mut expected_buffer_rows = Vec::new(); - let mut next_row = buffer.max_point().row; - for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() { - let fold_start = buffer.point_for_offset(fold_range.start).unwrap(); - let fold_end = buffer.point_for_offset(fold_range.end).unwrap(); - expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); - next_row = fold_start.row; - - expected_text.replace_range(fold_range.start..fold_range.end, "…"); + _ => { + let edits = buffer.update(app, |buffer, ctx| { + let start_version = buffer.version.clone(); + let edit_count = rng.gen_range(1..=5); + buffer.randomly_edit(&mut rng, edit_count, Some(ctx)); + buffer.edits_since(start_version).collect::>() + }); + log::info!("editing {:?}", edits); } - expected_buffer_rows.extend((0..=next_row).rev()); - expected_buffer_rows.reverse(); - - assert_eq!(map.text(app.as_ref()), expected_text); + } + map.check_invariants(app.as_ref()); - for (display_row, line) in expected_text.lines().enumerate() { - let line_len = map.line_len(display_row as u32, app.as_ref()).unwrap(); - assert_eq!(line_len, line.chars().count() as u32); - } + let buffer = map.buffer.read(app); + let mut expected_text = buffer.text(); + let mut expected_buffer_rows = Vec::new(); + let mut next_row = buffer.max_point().row; + for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() { + let fold_start = buffer.point_for_offset(fold_range.start).unwrap(); + let fold_end = buffer.point_for_offset(fold_range.end).unwrap(); + expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev()); + next_row = fold_start.row; + + expected_text.replace_range(fold_range.start..fold_range.end, "…"); + } + expected_buffer_rows.extend((0..=next_row).rev()); + expected_buffer_rows.reverse(); - let mut display_point = DisplayPoint::new(0, 0); - let mut display_offset = DisplayOffset(0); - for c in expected_text.chars() { - let buffer_point = map.to_buffer_point(display_point, app.as_ref()); - let buffer_offset = buffer_point.to_offset(buffer).unwrap(); - assert_eq!( - map.to_display_point(buffer_point, app.as_ref()), - display_point - ); - assert_eq!( - map.to_buffer_offset(display_point, app.as_ref()).unwrap(), - buffer_offset - ); - assert_eq!( - map.to_display_offset(display_point, app.as_ref()).unwrap(), - display_offset - ); - - if c == '\n' { - *display_point.row_mut() += 1; - *display_point.column_mut() = 0; - } else { - *display_point.column_mut() += 1; - } - display_offset.0 += 1; - } + assert_eq!(map.text(app.as_ref()), expected_text); - for _ in 0..5 { - let row = rng.gen_range(0..=map.max_point(app.as_ref()).row()); - let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap()); - let point = DisplayPoint::new(row, column); - let offset = map.to_display_offset(point, app.as_ref()).unwrap().0; - let len = rng.gen_range(0..=map.len(app.as_ref()) - offset); - assert_eq!( - map.snapshot(app.as_ref()) - .chars_at(point, app.as_ref()) - .unwrap() - .take(len) - .collect::(), - expected_text - .chars() - .skip(offset) - .take(len) - .collect::() - ); - } + for (display_row, line) in expected_text.lines().enumerate() { + let line_len = map.line_len(display_row as u32, app.as_ref()).unwrap(); + assert_eq!(line_len, line.chars().count() as u32); + } - for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { - let display_row = map - .to_display_point(Point::new(*buffer_row, 0), app.as_ref()) - .row(); - assert_eq!( - map.snapshot(app.as_ref()) - .buffer_rows(display_row) - .unwrap() - .collect::>(), - expected_buffer_rows[idx..], - ); - } + let mut display_point = DisplayPoint::new(0, 0); + let mut display_offset = DisplayOffset(0); + for c in expected_text.chars() { + let buffer_point = map.to_buffer_point(display_point, app.as_ref()); + let buffer_offset = buffer_point.to_offset(buffer).unwrap(); + assert_eq!( + map.to_display_point(buffer_point, app.as_ref()), + display_point + ); + assert_eq!( + map.to_buffer_offset(display_point, app.as_ref()).unwrap(), + buffer_offset + ); + assert_eq!( + map.to_display_offset(display_point, app.as_ref()).unwrap(), + display_offset + ); - for fold_range in map.merged_fold_ranges(app.as_ref()) { - let display_point = map.to_display_point( - fold_range.start.to_point(buffer).unwrap(), - app.as_ref(), - ); - assert!(map.is_line_folded(display_point.row(), app.as_ref())); + if c == '\n' { + *display_point.row_mut() += 1; + *display_point.column_mut() = 0; + } else { + *display_point.column_mut() += 1; } + display_offset.0 += 1; + } - for _ in 0..5 { - let end = rng.gen_range(0..=buffer.len()); - let start = rng.gen_range(0..=end); - let expected_folds = map - .folds - .items() - .into_iter() - .filter(|fold| { - let start = buffer.anchor_before(start).unwrap(); - let end = buffer.anchor_after(end).unwrap(); - start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less - && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater - }) - .map(|fold| fold.0) - .collect::>(); - - assert_eq!( - map.folds_in_range(start..end, app.as_ref()) - .unwrap() - .cloned() - .collect::>(), - expected_folds - ); - } + for _ in 0..5 { + let row = rng.gen_range(0..=map.max_point(app.as_ref()).row()); + let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap()); + let point = DisplayPoint::new(row, column); + let offset = map.to_display_offset(point, app.as_ref()).unwrap().0; + let len = rng.gen_range(0..=map.len(app.as_ref()) - offset); + assert_eq!( + map.snapshot(app.as_ref()) + .chars_at(point, app.as_ref()) + .unwrap() + .take(len) + .collect::(), + expected_text + .chars() + .skip(offset) + .take(len) + .collect::() + ); } - }); - } - } - #[test] - fn test_buffer_rows() { - App::test((), |app| { - let text = sample_text(6, 6) + "\n"; - let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); + for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() { + let display_row = map + .to_display_point(Point::new(*buffer_row, 0), app.as_ref()) + .row(); + assert_eq!( + map.snapshot(app.as_ref()) + .buffer_rows(display_row) + .unwrap() + .collect::>(), + expected_buffer_rows[idx..], + ); + } - let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + for fold_range in map.merged_fold_ranges(app.as_ref()) { + let display_point = map + .to_display_point(fold_range.start.to_point(buffer).unwrap(), app.as_ref()); + assert!(map.is_line_folded(display_point.row(), app.as_ref())); + } - map.fold( - vec![ - Point::new(0, 2)..Point::new(2, 2), - Point::new(3, 1)..Point::new(4, 1), - ], - app.as_ref(), - ) - .unwrap(); + for _ in 0..5 { + let end = rng.gen_range(0..=buffer.len()); + let start = rng.gen_range(0..=end); + let expected_folds = map + .folds + .items() + .into_iter() + .filter(|fold| { + let start = buffer.anchor_before(start).unwrap(); + let end = buffer.anchor_after(end).unwrap(); + start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less + && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater + }) + .map(|fold| fold.0) + .collect::>(); + + assert_eq!( + map.folds_in_range(start..end, app.as_ref()) + .unwrap() + .cloned() + .collect::>(), + expected_folds + ); + } + } + } + } - assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n"); - assert_eq!( - map.snapshot(app.as_ref()) - .buffer_rows(0) - .unwrap() - .collect::>(), - vec![0, 3, 5, 6] - ); - assert_eq!( - map.snapshot(app.as_ref()) - .buffer_rows(3) - .unwrap() - .collect::>(), - vec![6] - ); - }); + #[gpui::test] + fn test_buffer_rows(app: &mut gpui::MutableAppContext) { + let text = sample_text(6, 6) + "\n"; + let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); + + let mut map = FoldMap::new(buffer.clone(), app.as_ref()); + + map.fold( + vec![ + Point::new(0, 2)..Point::new(2, 2), + Point::new(3, 1)..Point::new(4, 1), + ], + app.as_ref(), + ) + .unwrap(); + + assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n"); + assert_eq!( + map.snapshot(app.as_ref()) + .buffer_rows(0) + .unwrap() + .collect::>(), + vec![0, 3, 5, 6] + ); + assert_eq!( + map.snapshot(app.as_ref()) + .buffer_rows(3) + .unwrap() + .collect::>(), + vec![6] + ); } impl FoldMap { diff --git a/zed/src/editor/display_map/mod.rs b/zed/src/editor/display_map/mod.rs index 97c1c0891b0ee1fa74001c9fd3b8bffed3185298..608d39dac85161a28b4d9d4756cdea87d5c3d114 100644 --- a/zed/src/editor/display_map/mod.rs +++ b/zed/src/editor/display_map/mod.rs @@ -339,53 +339,50 @@ pub fn collapse_tabs( mod tests { use super::*; use crate::test::*; - use gpui::App; - #[test] - fn test_chars_at() { - App::test((), |app| { - let text = sample_text(6, 6); - let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); - let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); - buffer - .update(app, |buffer, ctx| { - buffer.edit( - vec![ - Point::new(1, 0)..Point::new(1, 0), - Point::new(1, 1)..Point::new(1, 1), - Point::new(2, 1)..Point::new(2, 1), - ], - "\t", - Some(ctx), - ) - }) - .unwrap(); - - assert_eq!( - map.snapshot(app.as_ref()) - .chars_at(DisplayPoint::new(1, 0), app.as_ref()) - .unwrap() - .take(10) - .collect::(), - " b bb" - ); - assert_eq!( - map.snapshot(app.as_ref()) - .chars_at(DisplayPoint::new(1, 2), app.as_ref()) - .unwrap() - .take(10) - .collect::(), - " b bbbb" - ); - assert_eq!( - map.snapshot(app.as_ref()) - .chars_at(DisplayPoint::new(1, 6), app.as_ref()) - .unwrap() - .take(13) - .collect::(), - " bbbbb\nc c" - ); - }); + #[gpui::test] + fn test_chars_at(app: &mut gpui::MutableAppContext) { + let text = sample_text(6, 6); + let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx)); + let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); + buffer + .update(app, |buffer, ctx| { + buffer.edit( + vec![ + Point::new(1, 0)..Point::new(1, 0), + Point::new(1, 1)..Point::new(1, 1), + Point::new(2, 1)..Point::new(2, 1), + ], + "\t", + Some(ctx), + ) + }) + .unwrap(); + + assert_eq!( + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 0), app.as_ref()) + .unwrap() + .take(10) + .collect::(), + " b bb" + ); + assert_eq!( + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 2), app.as_ref()) + .unwrap() + .take(10) + .collect::(), + " b bbbb" + ); + assert_eq!( + map.snapshot(app.as_ref()) + .chars_at(DisplayPoint::new(1, 6), app.as_ref()) + .unwrap() + .take(13) + .collect::(), + " bbbbb\nc c" + ); } #[test] @@ -411,12 +408,10 @@ mod tests { assert_eq!(collapse_tabs("\ta".chars(), 5, Bias::Right, 4), (2, 0)); } - #[test] - fn test_max_point() { - App::test((), |app| { - let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx)); - let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); - assert_eq!(map.max_point(app.as_ref()), DisplayPoint::new(1, 11)) - }); + #[gpui::test] + fn test_max_point(app: &mut gpui::MutableAppContext) { + let buffer = app.add_model(|ctx| Buffer::new(0, "aaa\n\t\tbbb", ctx)); + let map = DisplayMap::new(buffer.clone(), 4, app.as_ref()); + assert_eq!(map.max_point(app.as_ref()), DisplayPoint::new(1, 11)) } } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 7253e0a5af9777bf50e3cd0d1fc56ff9159b2745..1b254731733fbdcaf22076f540760fec5652d1ea 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -734,65 +734,63 @@ mod tests { use std::collections::HashSet; use tempdir::TempDir; - #[test] - fn test_open_paths_action() { - App::test((), |app| { - let settings = settings::channel(&app.font_cache()).unwrap().1; - - init(app); + #[gpui::test] + fn test_open_paths_action(app: &mut gpui::MutableAppContext) { + let settings = settings::channel(&app.font_cache()).unwrap().1; - let dir = temp_tree(json!({ - "a": { - "aa": null, - "ab": null, - }, - "b": { - "ba": null, - "bb": null, - }, - "c": { - "ca": null, - "cb": null, - }, - })); + init(app); - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![ - dir.path().join("a").to_path_buf(), - dir.path().join("b").to_path_buf(), - ], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 1); - - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![dir.path().join("a").to_path_buf()], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 1); - let workspace_view_1 = app - .root_view::(app.window_ids().next().unwrap()) - .unwrap(); - assert_eq!(workspace_view_1.read(app).worktrees().len(), 2); - - app.dispatch_global_action( - "workspace:open_paths", - OpenParams { - paths: vec![ - dir.path().join("b").to_path_buf(), - dir.path().join("c").to_path_buf(), - ], - settings: settings.clone(), - }, - ); - assert_eq!(app.window_ids().count(), 2); - }); + let dir = temp_tree(json!({ + "a": { + "aa": null, + "ab": null, + }, + "b": { + "ba": null, + "bb": null, + }, + "c": { + "ca": null, + "cb": null, + }, + })); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![ + dir.path().join("a").to_path_buf(), + dir.path().join("b").to_path_buf(), + ], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 1); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![dir.path().join("a").to_path_buf()], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 1); + let workspace_view_1 = app + .root_view::(app.window_ids().next().unwrap()) + .unwrap(); + assert_eq!(workspace_view_1.read(app).worktrees().len(), 2); + + app.dispatch_global_action( + "workspace:open_paths", + OpenParams { + paths: vec![ + dir.path().join("b").to_path_buf(), + dir.path().join("c").to_path_buf(), + ], + settings: settings.clone(), + }, + ); + assert_eq!(app.window_ids().count(), 2); } #[test]