use super::*;use gpui::{AppContext, Context, TestAppContext};use language::{Buffer, Rope};use parking_lot::RwLock;use rand::prelude::*;use settings::SettingsStore;use std::env;use util::test::sample_text; #[ctor::ctor]fn init_logger() { if std::env::var("RUST_LOG").is_ok() { env_logger::init(); }} #[gpui::test]fn test_singleton(cx: &mut AppContext) { let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), buffer.read(cx).text()); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), (0..buffer.read(cx).row_count()) .map(Some) .collect::<Vec<_>>() ); buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), buffer.read(cx).text()); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), (0..buffer.read(cx).row_count()) .map(Some) .collect::<Vec<_>>() );} #[gpui::test]fn test_remote(cx: &mut AppContext) { let host_buffer = cx.new_model(|cx| Buffer::local("a", cx)); let guest_buffer = cx.new_model(|cx| { let state = host_buffer.read(cx).to_proto(cx); let ops = cx .background_executor() .block(host_buffer.read(cx).serialize_ops(None, cx)); let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap(); buffer.apply_ops( ops.into_iter() .map(|op| language::proto::deserialize_operation(op).unwrap()), cx, ); buffer }); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "a"); guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "ab"); guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "abc");} #[gpui::test]fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let events = Arc::new(RwLock::new(Vec::<Event>::new())); multibuffer.update(cx, |_, cx| { let events = events.clone(); cx.subscribe(&multibuffer, move |_, _, event, _| { if let Event::Edited { .. } = event { events.write().push(event.clone()) } }) .detach(); }); let subscription = multibuffer.update(cx, |multibuffer, cx| { let subscription = multibuffer.subscribe(); multibuffer.push_excerpts( buffer_1.clone(), [ExcerptRange { context: Point::new(1, 2)..Point::new(2, 5), primary: None, }], cx, ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 0..0, new: 0..10 }] ); multibuffer.push_excerpts( buffer_1.clone(), [ExcerptRange { context: Point::new(3, 3)..Point::new(4, 4), primary: None, }], cx, ); multibuffer.push_excerpts( buffer_2.clone(), [ExcerptRange { context: Point::new(3, 1)..Point::new(3, 3), primary: None, }], cx, ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 10..10, new: 10..22 }] ); subscription }); // Adding excerpts emits an edited event. assert_eq!( events.read().as_slice(), &[ Event::Edited { singleton_buffer_edited: false, edited_buffer: None, }, Event::Edited { singleton_buffer_edited: false, edited_buffer: None, }, Event::Edited { singleton_buffer_edited: false, edited_buffer: None, } ] ); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "ccccc\n", // "ddd\n", // "eeee\n", // "jj" // ) ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), [Some(1), Some(2), Some(3), Some(4), Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(2)).collect::<Vec<_>>(), [Some(3), Some(4), Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(4)).collect::<Vec<_>>(), [Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(5)).collect::<Vec<_>>(), [] ); assert_eq!( boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot), &[ (MultiBufferRow(0), "bbbb\nccccc".to_string(), true), (MultiBufferRow(2), "ddd\neeee".to_string(), false), (MultiBufferRow(4), "jj".to_string(), true), ] ); assert_eq!( boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot), &[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot), &[] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot), &[] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot), &[(MultiBufferRow(4), "jj".to_string(), true)] ); assert_eq!( boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot), &[] ); buffer_1.update(cx, |buffer, cx| { let text = "\n"; buffer.edit( [ (Point::new(0, 0)..Point::new(0, 0), text), (Point::new(2, 1)..Point::new(2, 3), text), ], None, cx, ); }); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "c\n", // "cc\n", // "ddd\n", // "eeee\n", // "jj" // ) ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 6..8, new: 6..7 }] ); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.clip_point(Point::new(0, 5), Bias::Left), Point::new(0, 4) ); assert_eq!( snapshot.clip_point(Point::new(0, 5), Bias::Right), Point::new(0, 4) ); assert_eq!( snapshot.clip_point(Point::new(5, 1), Bias::Right), Point::new(5, 1) ); assert_eq!( snapshot.clip_point(Point::new(5, 2), Bias::Right), Point::new(5, 2) ); assert_eq!( snapshot.clip_point(Point::new(5, 3), Bias::Right), Point::new(5, 2) ); let snapshot = multibuffer.update(cx, |multibuffer, cx| { let (buffer_2_excerpt_id, _) = multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone(); multibuffer.remove_excerpts([buffer_2_excerpt_id], cx); multibuffer.snapshot(cx) }); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "c\n", // "cc\n", // "ddd\n", // "eeee", // ) ); fn boundaries_in_range( range: Range<Point>, snapshot: &MultiBufferSnapshot, ) -> Vec<(MultiBufferRow, String, bool)> { snapshot .excerpt_boundaries_in_range(range) .filter_map(|boundary| { let starts_new_buffer = boundary.starts_new_buffer(); boundary.next.map(|next| { ( boundary.row, next.buffer .text_for_range(next.range.context) .collect::<String>(), starts_new_buffer, ) }) }) .collect::<Vec<_>>() }} #[gpui::test]fn test_excerpt_events(cx: &mut AppContext) { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx)); let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx)); let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let follower_edit_event_count = Arc::new(RwLock::new(0)); follower_multibuffer.update(cx, |_, cx| { let follower_edit_event_count = follower_edit_event_count.clone(); cx.subscribe( &leader_multibuffer, move |follower, _, event, cx| match event.clone() { Event::ExcerptsAdded { buffer, predecessor, excerpts, } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx), Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx), Event::Edited { .. } => { *follower_edit_event_count.write() += 1; } _ => {} }, ) .detach(); }); leader_multibuffer.update(cx, |leader, cx| { leader.push_excerpts( buffer_1.clone(), [ ExcerptRange { context: 0..8, primary: None, }, ExcerptRange { context: 12..16, primary: None, }, ], cx, ); leader.insert_excerpts_after( leader.excerpt_ids()[0], buffer_2.clone(), [ ExcerptRange { context: 0..5, primary: None, }, ExcerptRange { context: 10..15, primary: None, }, ], cx, ) }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 2); leader_multibuffer.update(cx, |leader, cx| { let excerpt_ids = leader.excerpt_ids(); leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); // Removing an empty set of excerpts is a noop. leader_multibuffer.update(cx, |leader, cx| { leader.remove_excerpts([], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); // Adding an empty set of excerpts is a noop. leader_multibuffer.update(cx, |leader, cx| { leader.push_excerpts::<usize>(buffer_2.clone(), [], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); leader_multibuffer.update(cx, |leader, cx| { leader.clear(cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 4);} #[gpui::test]fn test_expand_excerpts(cx: &mut AppContext) { let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); multibuffer.update(cx, |multibuffer, cx| { multibuffer.push_excerpts_with_context_lines( buffer.clone(), vec![ // Note that in this test, this first excerpt // does not contain a new line Point::new(3, 2)..Point::new(3, 3), Point::new(7, 1)..Point::new(7, 3), Point::new(15, 0)..Point::new(15, 0), ], 1, cx, ) }); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "ccc\n", // "ddd\n", // "eee", // "\n", // End of excerpt "ggg\n", // "hhh\n", // "iii", // "\n", // End of excerpt "ooo\n", // "ppp\n", // "qqq", // End of excerpt ) ); drop(snapshot); multibuffer.update(cx, |multibuffer, cx| { multibuffer.expand_excerpts( multibuffer.excerpt_ids(), 1, ExpandExcerptDirection::UpAndDown, cx, ) }); let snapshot = multibuffer.read(cx).snapshot(cx); // Expanding context lines causes the line containing 'fff' to appear in two different excerpts. // We don't attempt to merge them, because removing the excerpt could create inconsistency with other layers // that are tracking excerpt ids. assert_eq!( snapshot.text(), concat!( "bbb\n", // "ccc\n", // "ddd\n", // "eee\n", // "fff\n", // End of excerpt "fff\n", // "ggg\n", // "hhh\n", // "iii\n", // "jjj\n", // End of excerpt "nnn\n", // "ooo\n", // "ppp\n", // "qqq\n", // "rrr", // End of excerpt ) );} #[gpui::test]fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {Lines 1–500 of 1989. Load more
use super::*; use gpui::{AppContext, Context, TestAppContext}; use language::{Buffer, Rope}; use parking_lot::RwLock; use rand::prelude::*; use settings::SettingsStore; use std::env; use util::test::sample_text; #[ctor::ctor] fn init_logger() { if std::env::var("RUST_LOG").is_ok() { env_logger::init(); } } #[gpui::test] fn test_singleton(cx: &mut AppContext) { let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), buffer.read(cx).text()); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), (0..buffer.read(cx).row_count()) .map(Some) .collect::<Vec<_>>() ); buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), buffer.read(cx).text()); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), (0..buffer.read(cx).row_count()) .map(Some) .collect::<Vec<_>>() ); } #[gpui::test] fn test_remote(cx: &mut AppContext) { let host_buffer = cx.new_model(|cx| Buffer::local("a", cx)); let guest_buffer = cx.new_model(|cx| { let state = host_buffer.read(cx).to_proto(cx); let ops = cx .background_executor() .block(host_buffer.read(cx).serialize_ops(None, cx)); let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap(); buffer.apply_ops( ops.into_iter() .map(|op| language::proto::deserialize_operation(op).unwrap()), cx, ); buffer }); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "a"); guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "ab"); guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "abc"); } #[gpui::test] fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx)); let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let events = Arc::new(RwLock::new(Vec::<Event>::new())); multibuffer.update(cx, |_, cx| { let events = events.clone(); cx.subscribe(&multibuffer, move |_, _, event, _| { if let Event::Edited { .. } = event { events.write().push(event.clone()) } }) .detach(); }); let subscription = multibuffer.update(cx, |multibuffer, cx| { let subscription = multibuffer.subscribe(); multibuffer.push_excerpts( buffer_1.clone(), [ExcerptRange { context: Point::new(1, 2)..Point::new(2, 5), primary: None, }], cx, ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 0..0, new: 0..10 }] ); multibuffer.push_excerpts( buffer_1.clone(), [ExcerptRange { context: Point::new(3, 3)..Point::new(4, 4), primary: None, }], cx, ); multibuffer.push_excerpts( buffer_2.clone(), [ExcerptRange { context: Point::new(3, 1)..Point::new(3, 3), primary: None, }], cx, ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 10..10, new: 10..22 }] ); subscription }); // Adding excerpts emits an edited event. assert_eq!( events.read().as_slice(), &[ Event::Edited { singleton_buffer_edited: false, edited_buffer: None, }, Event::Edited { singleton_buffer_edited: false, edited_buffer: None, }, Event::Edited { singleton_buffer_edited: false, edited_buffer: None, } ] ); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "ccccc\n", // "ddd\n", // "eeee\n", // "jj" // ) ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(), [Some(1), Some(2), Some(3), Some(4), Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(2)).collect::<Vec<_>>(), [Some(3), Some(4), Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(4)).collect::<Vec<_>>(), [Some(3)] ); assert_eq!( snapshot.buffer_rows(MultiBufferRow(5)).collect::<Vec<_>>(), [] ); assert_eq!( boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot), &[ (MultiBufferRow(0), "bbbb\nccccc".to_string(), true), (MultiBufferRow(2), "ddd\neeee".to_string(), false), (MultiBufferRow(4), "jj".to_string(), true), ] ); assert_eq!( boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot), &[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot), &[] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot), &[] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot), &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)] ); assert_eq!( boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot), &[(MultiBufferRow(4), "jj".to_string(), true)] ); assert_eq!( boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot), &[] ); buffer_1.update(cx, |buffer, cx| { let text = "\n"; buffer.edit( [ (Point::new(0, 0)..Point::new(0, 0), text), (Point::new(2, 1)..Point::new(2, 3), text), ], None, cx, ); }); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "c\n", // "cc\n", // "ddd\n", // "eeee\n", // "jj" // ) ); assert_eq!( subscription.consume().into_inner(), [Edit { old: 6..8, new: 6..7 }] ); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.clip_point(Point::new(0, 5), Bias::Left), Point::new(0, 4) ); assert_eq!( snapshot.clip_point(Point::new(0, 5), Bias::Right), Point::new(0, 4) ); assert_eq!( snapshot.clip_point(Point::new(5, 1), Bias::Right), Point::new(5, 1) ); assert_eq!( snapshot.clip_point(Point::new(5, 2), Bias::Right), Point::new(5, 2) ); assert_eq!( snapshot.clip_point(Point::new(5, 3), Bias::Right), Point::new(5, 2) ); let snapshot = multibuffer.update(cx, |multibuffer, cx| { let (buffer_2_excerpt_id, _) = multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone(); multibuffer.remove_excerpts([buffer_2_excerpt_id], cx); multibuffer.snapshot(cx) }); assert_eq!( snapshot.text(), concat!( "bbbb\n", // Preserve newlines "c\n", // "cc\n", // "ddd\n", // "eeee", // ) ); fn boundaries_in_range( range: Range<Point>, snapshot: &MultiBufferSnapshot, ) -> Vec<(MultiBufferRow, String, bool)> { snapshot .excerpt_boundaries_in_range(range) .filter_map(|boundary| { let starts_new_buffer = boundary.starts_new_buffer(); boundary.next.map(|next| { ( boundary.row, next.buffer .text_for_range(next.range.context) .collect::<String>(), starts_new_buffer, ) }) }) .collect::<Vec<_>>() } } #[gpui::test] fn test_excerpt_events(cx: &mut AppContext) { let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx)); let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx)); let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); let follower_edit_event_count = Arc::new(RwLock::new(0)); follower_multibuffer.update(cx, |_, cx| { let follower_edit_event_count = follower_edit_event_count.clone(); cx.subscribe( &leader_multibuffer, move |follower, _, event, cx| match event.clone() { Event::ExcerptsAdded { buffer, predecessor, excerpts, } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx), Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx), Event::Edited { .. } => { *follower_edit_event_count.write() += 1; } _ => {} }, ) .detach(); }); leader_multibuffer.update(cx, |leader, cx| { leader.push_excerpts( buffer_1.clone(), [ ExcerptRange { context: 0..8, primary: None, }, ExcerptRange { context: 12..16, primary: None, }, ], cx, ); leader.insert_excerpts_after( leader.excerpt_ids()[0], buffer_2.clone(), [ ExcerptRange { context: 0..5, primary: None, }, ExcerptRange { context: 10..15, primary: None, }, ], cx, ) }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 2); leader_multibuffer.update(cx, |leader, cx| { let excerpt_ids = leader.excerpt_ids(); leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); // Removing an empty set of excerpts is a noop. leader_multibuffer.update(cx, |leader, cx| { leader.remove_excerpts([], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); // Adding an empty set of excerpts is a noop. leader_multibuffer.update(cx, |leader, cx| { leader.push_excerpts::<usize>(buffer_2.clone(), [], cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 3); leader_multibuffer.update(cx, |leader, cx| { leader.clear(cx); }); assert_eq!( leader_multibuffer.read(cx).snapshot(cx).text(), follower_multibuffer.read(cx).snapshot(cx).text(), ); assert_eq!(*follower_edit_event_count.read(), 4); } #[gpui::test] fn test_expand_excerpts(cx: &mut AppContext) { let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx)); let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite)); multibuffer.update(cx, |multibuffer, cx| { multibuffer.push_excerpts_with_context_lines( buffer.clone(), vec![ // Note that in this test, this first excerpt // does not contain a new line Point::new(3, 2)..Point::new(3, 3), Point::new(7, 1)..Point::new(7, 3), Point::new(15, 0)..Point::new(15, 0), ], 1, cx, ) }); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!( snapshot.text(), concat!( "ccc\n", // "ddd\n", // "eee", // "\n", // End of excerpt "ggg\n", // "hhh\n", // "iii", // "\n", // End of excerpt "ooo\n", // "ppp\n", // "qqq", // End of excerpt ) ); drop(snapshot); multibuffer.update(cx, |multibuffer, cx| { multibuffer.expand_excerpts( multibuffer.excerpt_ids(), 1, ExpandExcerptDirection::UpAndDown, cx, ) }); let snapshot = multibuffer.read(cx).snapshot(cx); // Expanding context lines causes the line containing 'fff' to appear in two different excerpts. // We don't attempt to merge them, because removing the excerpt could create inconsistency with other layers // that are tracking excerpt ids. assert_eq!( snapshot.text(), concat!( "bbb\n", // "ccc\n", // "ddd\n", // "eee\n", // "fff\n", // End of excerpt "fff\n", // "ggg\n", // "hhh\n", // "iii\n", // "jjj\n", // End of excerpt "nnn\n", // "ooo\n", // "ppp\n", // "qqq\n", // "rrr", // End of excerpt ) ); } #[gpui::test] fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {