Get Editor compiling with MultiBuffer as its buffer

Nathan Sobo and Max Brunsfeld created

There's a bunch of unimplemented methods in MultiBuffer, but everything compiles.

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

crates/editor/src/display_map.rs           |  49 +-
crates/editor/src/display_map/block_map.rs |  72 ++-
crates/editor/src/display_map/fold_map.rs  | 120 +++---
crates/editor/src/display_map/tab_map.rs   |  12 
crates/editor/src/display_map/wrap_map.rs  |  14 
crates/editor/src/editor.rs                | 316 +++++++++--------
crates/editor/src/element.rs               |  19 
crates/editor/src/items.rs                 |  38 +
crates/editor/src/movement.rs              |   9 
crates/go_to_line/src/go_to_line.rs        |   2 
crates/language/src/language.rs            |   3 
crates/language/src/multi_buffer.rs        | 422 ++++++++++++++++++++++-
crates/language/src/multi_buffer/anchor.rs |  85 ++++
crates/language/src/tests.rs               |   2 
crates/server/src/rpc.rs                   |   3 
crates/text/src/patch.rs                   |   9 
crates/text/src/selection.rs               |  24 -
crates/text/src/text.rs                    |  14 
18 files changed, 836 insertions(+), 377 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -6,7 +6,10 @@ mod wrap_map;
 use block_map::{BlockMap, BlockPoint};
 use fold_map::{FoldMap, ToFoldPoint as _};
 use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle};
-use language::{Anchor, Buffer, Point, Subscription as BufferSubscription, ToOffset, ToPoint};
+use language::{
+    multi_buffer::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint},
+    Point, Subscription as BufferSubscription,
+};
 use std::{
     collections::{HashMap, HashSet},
     ops::Range,
@@ -26,7 +29,7 @@ pub trait ToDisplayPoint {
 }
 
 pub struct DisplayMap {
-    buffer: ModelHandle<Buffer>,
+    buffer: ModelHandle<MultiBuffer>,
     buffer_subscription: BufferSubscription,
     fold_map: FoldMap,
     tab_map: TabMap,
@@ -40,7 +43,7 @@ impl Entity for DisplayMap {
 
 impl DisplayMap {
     pub fn new(
-        buffer: ModelHandle<Buffer>,
+        buffer: ModelHandle<MultiBuffer>,
         tab_size: usize,
         font_id: FontId,
         font_size: f32,
@@ -48,7 +51,7 @@ impl DisplayMap {
         cx: &mut ModelContext<Self>,
     ) -> Self {
         let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
-        let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot());
+        let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
         let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
         let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
         let block_map = BlockMap::new(buffer.clone(), snapshot);
@@ -64,7 +67,7 @@ impl DisplayMap {
     }
 
     pub fn snapshot(&self, cx: &mut ModelContext<Self>) -> DisplaySnapshot {
-        let buffer_snapshot = self.buffer.read(cx).snapshot();
+        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (folds_snapshot, edits) = self.fold_map.read(buffer_snapshot, edits);
         let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
@@ -74,7 +77,7 @@ impl DisplayMap {
         let blocks_snapshot = self.block_map.read(wraps_snapshot.clone(), edits, cx);
 
         DisplaySnapshot {
-            buffer_snapshot: self.buffer.read(cx).snapshot(),
+            buffer_snapshot: self.buffer.read(cx).snapshot(cx),
             folds_snapshot,
             tabs_snapshot,
             wraps_snapshot,
@@ -87,7 +90,7 @@ impl DisplayMap {
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &mut ModelContext<Self>,
     ) {
-        let snapshot = self.buffer.read(cx).snapshot();
+        let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -108,7 +111,7 @@ impl DisplayMap {
         ranges: impl IntoIterator<Item = Range<T>>,
         cx: &mut ModelContext<Self>,
     ) {
-        let snapshot = self.buffer.read(cx).snapshot();
+        let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -132,7 +135,7 @@ impl DisplayMap {
     where
         P: ToOffset + Clone,
     {
-        let snapshot = self.buffer.read(cx).snapshot();
+        let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -151,7 +154,7 @@ impl DisplayMap {
     }
 
     pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
-        let snapshot = self.buffer.read(cx).snapshot();
+        let snapshot = self.buffer.read(cx).snapshot(cx);
         let edits = self.buffer_subscription.consume().into_inner();
         let (snapshot, edits) = self.fold_map.read(snapshot, edits);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -179,7 +182,7 @@ impl DisplayMap {
 }
 
 pub struct DisplaySnapshot {
-    pub buffer_snapshot: language::BufferSnapshot,
+    pub buffer_snapshot: MultiBufferSnapshot,
     folds_snapshot: fold_map::FoldSnapshot,
     tabs_snapshot: tab_map::TabSnapshot,
     wraps_snapshot: wrap_map::WrapSnapshot,
@@ -457,7 +460,7 @@ mod tests {
     use super::*;
     use crate::{movement, test::*};
     use gpui::{color::Color, MutableAppContext};
-    use language::{Language, LanguageConfig, RandomCharIter, SelectionGoal};
+    use language::{Buffer, Language, LanguageConfig, RandomCharIter, SelectionGoal};
     use rand::{prelude::StdRng, Rng};
     use std::{env, sync::Arc};
     use theme::SyntaxTheme;
@@ -489,10 +492,10 @@ mod tests {
         log::info!("tab size: {}", tab_size);
         log::info!("wrap width: {:?}", wrap_width);
 
-        let buffer = cx.add_model(|cx| {
+        let buffer = cx.update(|cx| {
             let len = rng.gen_range(0..10);
             let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            Buffer::new(0, text, cx)
+            MultiBuffer::build_simple(&text, cx)
         });
 
         let map = cx.add_model(|cx| {
@@ -563,8 +566,10 @@ mod tests {
                 assert_eq!(prev_display_bound.column(), 0);
                 if next_display_bound < snapshot.max_point() {
                     assert_eq!(
-                        buffer
-                            .read_with(&cx, |buffer, _| buffer.chars_at(next_buffer_bound).next()),
+                        buffer.read_with(&cx, |buffer, _| buffer
+                            .as_snapshot()
+                            .chars_at(next_buffer_bound)
+                            .next()),
                         Some('\n')
                     )
                 }
@@ -651,7 +656,7 @@ mod tests {
         let wrap_width = Some(64.);
 
         let text = "one two three four five\nsix seven eight";
-        let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx));
+        let buffer = MultiBuffer::build_simple(text, cx);
         let map = cx.add_model(|cx| {
             DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx)
         });
@@ -724,7 +729,7 @@ mod tests {
     #[gpui::test]
     fn test_text_chunks(cx: &mut gpui::MutableAppContext) {
         let text = sample_text(6, 6, 'a');
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
+        let buffer = MultiBuffer::build_simple(&text, cx);
         let tab_size = 4;
         let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
         let font_id = cx
@@ -803,6 +808,7 @@ mod tests {
         let buffer =
             cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Some(lang), None, cx));
         buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 
         let tab_size = 2;
         let font_cache = cx.font_cache();
@@ -890,6 +896,7 @@ mod tests {
         let buffer =
             cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Some(lang), None, cx));
         buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
 
         let font_cache = cx.font_cache();
 
@@ -935,7 +942,7 @@ mod tests {
 
         let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n";
         let display_text = "\n'a', 'α',   '✋',    '❎', '🍐'\n";
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
+        let buffer = MultiBuffer::build_simple(text, cx);
 
         let tab_size = 4;
         let font_cache = cx.font_cache();
@@ -979,7 +986,7 @@ mod tests {
     #[gpui::test]
     fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
         let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
+        let buffer = MultiBuffer::build_simple(text, cx);
         let tab_size = 4;
         let font_cache = cx.font_cache();
         let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
@@ -1038,7 +1045,7 @@ mod tests {
 
     #[gpui::test]
     fn test_max_point(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, "aaa\n\t\tbbb", cx));
+        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
         let tab_size = 4;
         let font_cache = cx.font_cache();
         let family_id = font_cache.load_family(&["Helvetica"]).unwrap();

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

@@ -1,6 +1,9 @@
 use super::wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot};
 use gpui::{AppContext, ElementBox, ModelHandle};
-use language::{Buffer, Chunk};
+use language::{
+    multi_buffer::{Anchor, MultiBuffer, ToOffset, ToPoint as _},
+    Chunk,
+};
 use parking_lot::Mutex;
 use std::{
     cmp::{self, Ordering},
@@ -12,14 +15,14 @@ use std::{
         Arc,
     },
 };
-use sum_tree::SumTree;
-use text::{Anchor, Bias, Edit, Point, ToOffset, ToPoint as _};
+use sum_tree::{Bias, SumTree};
+use text::{Edit, Point};
 use theme::SyntaxTheme;
 
 const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
 
 pub struct BlockMap {
-    buffer: ModelHandle<Buffer>,
+    buffer: ModelHandle<MultiBuffer>,
     next_block_id: AtomicUsize,
     wrap_snapshot: Mutex<WrapSnapshot>,
     blocks: Vec<Arc<Block>>,
@@ -109,7 +112,7 @@ pub struct BlockBufferRows<'a> {
 }
 
 impl BlockMap {
-    pub fn new(buffer: ModelHandle<Buffer>, wrap_snapshot: WrapSnapshot) -> Self {
+    pub fn new(buffer: ModelHandle<MultiBuffer>, wrap_snapshot: WrapSnapshot) -> Self {
         Self {
             buffer,
             next_block_id: AtomicUsize::new(0),
@@ -153,6 +156,7 @@ impl BlockMap {
         }
 
         let buffer = self.buffer.read(cx);
+        let buffer = buffer.as_snapshot();
         let mut transforms = self.transforms.lock();
         let mut new_transforms = SumTree::new();
         let old_row_count = transforms.summary().input_rows;
@@ -241,7 +245,7 @@ impl BlockMap {
             let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
                 probe
                     .position
-                    .cmp(&start_anchor, buffer)
+                    .cmp(&start_anchor, &buffer)
                     .unwrap()
                     .then(Ordering::Greater)
             }) {
@@ -255,7 +259,7 @@ impl BlockMap {
                 match self.blocks[start_block_ix..].binary_search_by(|probe| {
                     probe
                         .position
-                        .cmp(&end_anchor, buffer)
+                        .cmp(&end_anchor, &buffer)
                         .unwrap()
                         .then(Ordering::Greater)
                 }) {
@@ -268,7 +272,7 @@ impl BlockMap {
                 self.blocks[start_block_ix..end_block_ix]
                     .iter()
                     .map(|block| {
-                        let mut position = block.position.to_point(buffer);
+                        let mut position = block.position.to_point(&buffer);
                         let column = wrap_snapshot.from_point(position, Bias::Left).column();
                         match block.disposition {
                             BlockDisposition::Above => position.column = 0,
@@ -380,6 +384,7 @@ impl<'a> BlockMapWriter<'a> {
         P: ToOffset + Clone,
     {
         let buffer = self.0.buffer.read(cx);
+        let buffer = buffer.as_snapshot();
         let mut ids = Vec::new();
         let mut edits = Vec::<Edit<u32>>::new();
         let wrap_snapshot = &*self.0.wrap_snapshot.lock();
@@ -389,7 +394,7 @@ impl<'a> BlockMapWriter<'a> {
             ids.push(id);
 
             let position = buffer.anchor_after(block.position);
-            let point = position.to_point(buffer);
+            let point = position.to_point(&buffer);
             let start_row = wrap_snapshot
                 .from_point(Point::new(point.row, 0), Bias::Left)
                 .row();
@@ -404,7 +409,7 @@ impl<'a> BlockMapWriter<'a> {
             let block_ix = match self
                 .0
                 .blocks
-                .binary_search_by(|probe| probe.position.cmp(&position, buffer).unwrap())
+                .binary_search_by(|probe| probe.position.cmp(&position, &buffer).unwrap())
             {
                 Ok(ix) | Err(ix) => ix,
             };
@@ -436,12 +441,13 @@ impl<'a> BlockMapWriter<'a> {
 
     pub fn remove(&mut self, block_ids: HashSet<BlockId>, cx: &AppContext) {
         let buffer = self.0.buffer.read(cx);
+        let buffer = buffer.as_snapshot();
         let wrap_snapshot = &*self.0.wrap_snapshot.lock();
         let mut edits = Vec::new();
         let mut last_block_buffer_row = None;
         self.0.blocks.retain(|block| {
             if block_ids.contains(&block.id) {
-                let buffer_row = block.position.to_point(buffer).row;
+                let buffer_row = block.position.to_point(&buffer).row;
                 if last_block_buffer_row != Some(buffer_row) {
                     last_block_buffer_row = Some(buffer_row);
                     let start_row = wrap_snapshot
@@ -877,7 +883,6 @@ mod tests {
     use super::*;
     use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
     use gpui::{elements::Empty, Element};
-    use language::Buffer;
     use rand::prelude::*;
     use std::env;
     use text::RandomCharIter;
@@ -906,8 +911,9 @@ mod tests {
 
         let text = "aaa\nbbb\nccc\nddd";
 
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
-        let (fold_map, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot());
+        let buffer = MultiBuffer::build_simple(text, cx);
+        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
+        let (fold_map, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
         let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
         let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
         let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
@@ -1050,15 +1056,14 @@ mod tests {
             ]
         );
 
-        // Insert a line break, separating two block decorations into separate
-        // lines.
-        let (buffer_snapshot, buffer_edits) = buffer.update(cx, |buffer, cx| {
-            let v0 = buffer.version();
+        // Insert a line break, separating two block decorations into separate lines.
+        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
             buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx);
-            (buffer.snapshot(), buffer.edits_since(&v0).collect())
+            buffer.snapshot(cx)
         });
 
-        let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot, buffer_edits);
+        let (folds_snapshot, fold_edits) =
+            fold_map.read(buffer_snapshot, subscription.consume().into_inner());
         let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
         let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
             wrap_map.sync(tabs_snapshot, tab_edits, cx)
@@ -1077,8 +1082,8 @@ mod tests {
 
         let text = "one two three\nfour five six\nseven eight";
 
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
-        let (_, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot());
+        let buffer = MultiBuffer::build_simple(text, cx);
+        let (_, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
         let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
         let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
         let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
@@ -1132,13 +1137,12 @@ mod tests {
 
         log::info!("Wrap width: {:?}", wrap_width);
 
-        let buffer = cx.add_model(|cx| {
-            let len = rng.gen_range(0..10);
-            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            log::info!("initial buffer text: {:?}", text);
-            Buffer::new(0, text, cx)
-        });
-        let mut buffer_snapshot = buffer.read(cx).snapshot();
+        let len = rng.gen_range(0..10);
+        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
+        log::info!("initial buffer text: {:?}", text);
+        let buffer = MultiBuffer::build_simple(&text, cx);
+
+        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
         let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
         let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
         let (wrap_map, wraps_snapshot) =
@@ -1176,7 +1180,7 @@ mod tests {
                             log::info!(
                                 "inserting block {:?} {:?} with height {}",
                                 disposition,
-                                position.to_point(buffer),
+                                position.to_point(&buffer.as_snapshot()),
                                 height
                             );
                             BlockProperties {
@@ -1221,12 +1225,12 @@ mod tests {
                 }
                 _ => {
                     buffer.update(cx, |buffer, cx| {
-                        let v0 = buffer.version();
                         let edit_count = rng.gen_range(1..=5);
+                        let subscription = buffer.subscribe();
                         buffer.randomly_edit(&mut rng, edit_count, cx);
                         log::info!("buffer text: {:?}", buffer.text());
-                        buffer_edits.extend(buffer.edits_since(&v0));
-                        buffer_snapshot = buffer.snapshot();
+                        buffer_edits.extend(subscription.consume());
+                        buffer_snapshot = buffer.snapshot(cx);
                     });
                 }
             }
@@ -1248,7 +1252,7 @@ mod tests {
                 .iter()
                 .cloned()
                 .map(|(id, block)| {
-                    let mut position = block.position.to_point(buffer);
+                    let mut position = block.position.to_point(&buffer.as_snapshot());
                     let column = wraps_snapshot.from_point(position, Bias::Left).column();
                     match block.disposition {
                         BlockDisposition::Above => {

crates/editor/src/display_map/fold_map.rs 🔗

@@ -1,5 +1,6 @@
 use language::{
-    Anchor, AnchorRangeExt, BufferSnapshot, Chunk, Edit, Point, PointUtf16, TextSummary, ToOffset,
+    multi_buffer::{Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot, ToOffset},
+    Chunk, Edit, Point, PointUtf16, TextSummary,
 };
 use parking_lot::Mutex;
 use std::{
@@ -189,14 +190,14 @@ impl<'a> FoldMapWriter<'a> {
 }
 
 pub struct FoldMap {
-    buffer: Mutex<BufferSnapshot>,
+    buffer: Mutex<MultiBufferSnapshot>,
     transforms: Mutex<SumTree<Transform>>,
     folds: SumTree<Fold>,
     version: AtomicUsize,
 }
 
 impl FoldMap {
-    pub fn new(buffer: BufferSnapshot) -> (Self, FoldSnapshot) {
+    pub fn new(buffer: MultiBufferSnapshot) -> (Self, FoldSnapshot) {
         let this = Self {
             buffer: Mutex::new(buffer.clone()),
             folds: Default::default(),
@@ -224,7 +225,7 @@ impl FoldMap {
 
     pub fn read(
         &self,
-        buffer: BufferSnapshot,
+        buffer: MultiBufferSnapshot,
         edits: Vec<Edit<usize>>,
     ) -> (FoldSnapshot, Vec<FoldEdit>) {
         let edits = self.sync(buffer, edits);
@@ -240,7 +241,7 @@ impl FoldMap {
 
     pub fn write(
         &mut self,
-        buffer: BufferSnapshot,
+        buffer: MultiBufferSnapshot,
         edits: Vec<Edit<usize>>,
     ) -> (FoldMapWriter, FoldSnapshot, Vec<FoldEdit>) {
         let (snapshot, edits) = self.read(buffer, edits);
@@ -259,7 +260,7 @@ impl FoldMap {
 
     fn sync(
         &self,
-        new_buffer: BufferSnapshot,
+        new_buffer: MultiBufferSnapshot,
         buffer_edits: Vec<text::Edit<usize>>,
     ) -> Vec<FoldEdit> {
         if buffer_edits.is_empty() {
@@ -476,7 +477,7 @@ impl FoldMap {
 pub struct FoldSnapshot {
     transforms: SumTree<Transform>,
     folds: SumTree<Fold>,
-    buffer_snapshot: language::BufferSnapshot,
+    buffer_snapshot: MultiBufferSnapshot,
     pub version: usize,
 }
 
@@ -699,7 +700,7 @@ impl FoldSnapshot {
 }
 
 fn intersecting_folds<'a, T>(
-    buffer: &'a text::BufferSnapshot,
+    buffer: &'a MultiBufferSnapshot,
     folds: &'a SumTree<Fold>,
     range: Range<T>,
     inclusive: bool,
@@ -850,9 +851,9 @@ impl Default for FoldSummary {
 }
 
 impl sum_tree::Summary for FoldSummary {
-    type Context = text::BufferSnapshot;
+    type Context = MultiBufferSnapshot;
 
-    fn add_summary(&mut self, other: &Self, buffer: &text::BufferSnapshot) {
+    fn add_summary(&mut self, other: &Self, buffer: &MultiBufferSnapshot) {
         if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
             self.min_start = other.min_start.clone();
         }
@@ -876,20 +877,20 @@ impl sum_tree::Summary for FoldSummary {
 }
 
 impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
-    fn add_summary(&mut self, summary: &'a FoldSummary, _: &text::BufferSnapshot) {
+    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
         self.0.start = summary.start.clone();
         self.0.end = summary.end.clone();
     }
 }
 
 impl<'a> sum_tree::SeekTarget<'a, FoldSummary, Fold> for Fold {
-    fn cmp(&self, other: &Self, buffer: &text::BufferSnapshot) -> Ordering {
+    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
         self.0.cmp(&other.0, buffer).unwrap()
     }
 }
 
 impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
-    fn add_summary(&mut self, summary: &'a FoldSummary, _: &text::BufferSnapshot) {
+    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
         *self += summary.count;
     }
 }
@@ -924,7 +925,7 @@ impl<'a> Iterator for FoldBufferRows<'a> {
 
 pub struct FoldChunks<'a> {
     transform_cursor: Cursor<'a, Transform, (FoldOffset, usize)>,
-    buffer_chunks: language::BufferChunks<'a>,
+    buffer_chunks: MultiBufferChunks<'a>,
     buffer_chunk: Option<(usize, Chunk<'a>)>,
     buffer_offset: usize,
     output_offset: usize,
@@ -1053,7 +1054,7 @@ pub type FoldEdit = Edit<FoldOffset>;
 mod tests {
     use super::*;
     use crate::ToPoint;
-    use language::Buffer;
+    use language::multi_buffer::MultiBuffer;
     use rand::prelude::*;
     use std::{env, mem};
     use text::RandomCharIter;
@@ -1062,8 +1063,9 @@ mod tests {
 
     #[gpui::test]
     fn test_basic_folds(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6, 'a'), cx));
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
+        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
 
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
@@ -1086,8 +1088,7 @@ mod tests {
             ]
         );
 
-        let (buffer_snapshot, edits) = buffer.update(cx, |buffer, cx| {
-            let v0 = buffer.version();
+        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
             buffer.edit(
                 vec![
                     Point::new(0, 0)..Point::new(0, 1),
@@ -1096,9 +1097,10 @@ mod tests {
                 "123",
                 cx,
             );
-            (buffer.snapshot(), buffer.edits_since(&v0).collect())
+            buffer.snapshot(cx)
         });
-        let (snapshot3, edits) = map.read(buffer_snapshot.clone(), edits);
+        let (snapshot3, edits) =
+            map.read(buffer_snapshot.clone(), subscription.consume().into_inner());
         assert_eq!(snapshot3.text(), "123a…c123c…eeeee");
         assert_eq!(
             edits,
@@ -1114,12 +1116,11 @@ mod tests {
             ]
         );
 
-        let (buffer_snapshot, edits) = buffer.update(cx, |buffer, cx| {
-            let v0 = buffer.version();
+        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
             buffer.edit(vec![Point::new(2, 6)..Point::new(4, 3)], "456", cx);
-            (buffer.snapshot(), buffer.edits_since(&v0).collect())
+            buffer.snapshot(cx)
         });
-        let (snapshot4, _) = map.read(buffer_snapshot.clone(), edits);
+        let (snapshot4, _) = map.read(buffer_snapshot.clone(), subscription.consume().into_inner());
         assert_eq!(snapshot4.text(), "123a…c123456eee");
 
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
@@ -1130,8 +1131,9 @@ mod tests {
 
     #[gpui::test]
     fn test_adjacent_folds(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, "abcdefghijkl", cx));
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
+        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
 
         {
             let mut map = FoldMap::new(buffer_snapshot.clone()).0;
@@ -1164,20 +1166,20 @@ mod tests {
             assert_eq!(snapshot.text(), "…fghijkl");
 
             // Edit within one of the folds.
-            let (buffer_snapshot, edits) = buffer.update(cx, |buffer, cx| {
-                let v0 = buffer.version();
+            let buffer_snapshot = buffer.update(cx, |buffer, cx| {
                 buffer.edit(vec![0..1], "12345", cx);
-                (buffer.snapshot(), buffer.edits_since(&v0).collect())
+                buffer.snapshot(cx)
             });
-            let (snapshot, _) = map.read(buffer_snapshot.clone(), edits);
+            let (snapshot, _) =
+                map.read(buffer_snapshot.clone(), subscription.consume().into_inner());
             assert_eq!(snapshot.text(), "12345…fghijkl");
         }
     }
 
     #[gpui::test]
     fn test_overlapping_folds(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6, 'a'), cx));
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
         writer.fold(vec![
@@ -1192,8 +1194,9 @@ mod tests {
 
     #[gpui::test]
     fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6, 'a'), cx));
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
+        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
 
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);
@@ -1204,19 +1207,18 @@ mod tests {
         let (snapshot, _) = map.read(buffer_snapshot.clone(), vec![]);
         assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee");
 
-        let (buffer_snapshot, edits) = buffer.update(cx, |buffer, cx| {
-            let v0 = buffer.version();
+        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
             buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", cx);
-            (buffer.snapshot(), buffer.edits_since(&v0).collect())
+            buffer.snapshot(cx)
         });
-        let (snapshot, _) = map.read(buffer_snapshot.clone(), edits);
+        let (snapshot, _) = map.read(buffer_snapshot.clone(), subscription.consume().into_inner());
         assert_eq!(snapshot.text(), "aa…eeeee");
     }
 
     #[gpui::test]
     fn test_folds_in_range(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6, 'a'), cx));
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
         let buffer = buffer.read(cx);
 
@@ -1230,7 +1232,9 @@ mod tests {
         let (snapshot, _) = map.read(buffer_snapshot.clone(), vec![]);
         let fold_ranges = snapshot
             .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
-            .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer))
+            .map(|fold| {
+                fold.start.to_point(&buffer.as_snapshot())..fold.end.to_point(&buffer.as_snapshot())
+            })
             .collect::<Vec<_>>();
         assert_eq!(
             fold_ranges,
@@ -1247,12 +1251,10 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let buffer = cx.add_model(|cx| {
-            let len = rng.gen_range(0..10);
-            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            Buffer::new(0, text, cx)
-        });
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let len = rng.gen_range(0..10);
+        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
+        let buffer = MultiBuffer::build_simple(&text, cx);
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
 
         let (mut initial_snapshot, _) = map.read(buffer_snapshot.clone(), vec![]);
@@ -1260,23 +1262,21 @@ mod tests {
 
         for _ in 0..operations {
             log::info!("text: {:?}", buffer.read(cx).text());
-            let buffer_edits = match rng.gen_range(0..=100) {
+            let mut buffer_edits = Vec::new();
+            match rng.gen_range(0..=100) {
                 0..=59 => {
                     snapshot_edits.extend(map.randomly_mutate(&mut rng));
-                    vec![]
                 }
                 _ => buffer.update(cx, |buffer, cx| {
-                    let start_version = buffer.version.clone();
+                    let subscription = buffer.subscribe();
                     let edit_count = rng.gen_range(1..=5);
                     buffer.randomly_edit(&mut rng, edit_count, cx);
-                    let edits = buffer
-                        .edits_since::<Point>(&start_version)
-                        .collect::<Vec<_>>();
+                    let edits = subscription.consume().into_inner();
                     log::info!("editing {:?}", edits);
-                    buffer.edits_since::<usize>(&start_version).collect()
+                    buffer_edits.extend(edits);
                 }),
             };
-            let buffer_snapshot = buffer.read(cx).snapshot();
+            let buffer_snapshot = buffer.read(cx).snapshot(cx);
 
             let (snapshot, edits) = map.read(buffer_snapshot.clone(), buffer_edits);
             snapshot_edits.push((snapshot.clone(), edits));
@@ -1285,8 +1285,8 @@ mod tests {
             let mut expected_buffer_rows = Vec::new();
             let mut next_row = buffer_snapshot.max_point().row;
             for fold_range in map.merged_fold_ranges().into_iter().rev() {
-                let fold_start = buffer_snapshot.point_for_offset(fold_range.start).unwrap();
-                let fold_end = buffer_snapshot.point_for_offset(fold_range.end).unwrap();
+                let fold_start = buffer_snapshot.offset_to_point(fold_range.start);
+                let fold_end = buffer_snapshot.offset_to_point(fold_range.end);
                 expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
                 next_row = fold_start.row;
 
@@ -1458,9 +1458,9 @@ mod tests {
     #[gpui::test]
     fn test_buffer_rows(cx: &mut gpui::MutableAppContext) {
         let text = sample_text(6, 6, 'a') + "\n";
-        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
+        let buffer = MultiBuffer::build_simple(&text, cx);
 
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         let mut map = FoldMap::new(buffer_snapshot.clone()).0;
 
         let (mut writer, _, _) = map.write(buffer_snapshot.clone(), vec![]);

crates/editor/src/display_map/tab_map.rs 🔗

@@ -435,7 +435,7 @@ impl<'a> Iterator for TabChunks<'a> {
 mod tests {
     use super::*;
     use crate::display_map::fold_map::FoldMap;
-    use language::Buffer;
+    use language::multi_buffer::MultiBuffer;
     use rand::{prelude::StdRng, Rng};
     use text::{RandomCharIter, Rope};
 
@@ -449,12 +449,10 @@ mod tests {
     #[gpui::test(iterations = 100)]
     fn test_random(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
         let tab_size = rng.gen_range(1..=4);
-        let buffer = cx.add_model(|cx| {
-            let len = rng.gen_range(0..30);
-            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            Buffer::new(0, text, cx)
-        });
-        let buffer_snapshot = buffer.read(cx).snapshot();
+        let len = rng.gen_range(0..30);
+        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
+        let buffer = MultiBuffer::build_simple(&text, cx);
+        let buffer_snapshot = buffer.read(cx).snapshot(cx);
         log::info!("Buffer text: {:?}", buffer.read(cx).text());
 
         let (mut fold_map, _) = FoldMap::new(buffer_snapshot.clone());

crates/editor/src/display_map/wrap_map.rs 🔗

@@ -974,7 +974,7 @@ mod tests {
         display_map::{fold_map::FoldMap, tab_map::TabMap},
         test::Observer,
     };
-    use language::{Buffer, RandomCharIter};
+    use language::{multi_buffer::MultiBuffer, RandomCharIter};
     use rand::prelude::*;
     use std::{cmp, env};
     use text::Rope;
@@ -1004,12 +1004,12 @@ mod tests {
         log::info!("Tab size: {}", tab_size);
         log::info!("Wrap width: {:?}", wrap_width);
 
-        let buffer = cx.add_model(|cx| {
+        let buffer = cx.update(|cx| {
             let len = rng.gen_range(0..10);
             let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
-            Buffer::new(0, text, cx)
+            MultiBuffer::build_simple(&text, cx)
         });
-        let buffer_snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
+        let buffer_snapshot = buffer.read_with(&cx, |buffer, cx| buffer.snapshot(cx));
         let (mut fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
         let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
         log::info!(
@@ -1074,15 +1074,15 @@ mod tests {
                 }
                 _ => {
                     buffer.update(&mut cx, |buffer, cx| {
-                        let v0 = buffer.version();
+                        let subscription = buffer.subscribe();
                         let edit_count = rng.gen_range(1..=5);
                         buffer.randomly_edit(&mut rng, edit_count, cx);
-                        buffer_edits.extend(buffer.edits_since(&v0));
+                        buffer_edits.extend(subscription.consume());
                     });
                 }
             }
 
-            let buffer_snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
+            let buffer_snapshot = buffer.read_with(&cx, |buffer, cx| buffer.snapshot(cx));
             log::info!("Unwrapped text (no folds): {:?}", buffer_snapshot.text());
             let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot, buffer_edits);
             log::info!(

crates/editor/src/editor.rs 🔗

@@ -20,7 +20,14 @@ use gpui::{
     MutableAppContext, RenderContext, View, ViewContext, WeakViewHandle,
 };
 use items::BufferItemHandle;
-use language::*;
+use language::{
+    multi_buffer::{
+        Anchor, AnchorRangeExt, AnchorRangeSet, MultiBuffer, MultiBufferSnapshot, SelectionSet,
+        ToOffset, ToPoint,
+    },
+    BracketPair, Buffer, Diagnostic, DiagnosticSeverity, Language, Point, Selection, SelectionGoal,
+    SelectionSetId,
+};
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use smol::Timer;
@@ -29,7 +36,7 @@ use std::{
     cmp,
     collections::HashMap,
     iter, mem,
-    ops::{Deref, Range, RangeInclusive},
+    ops::{Deref, Range, RangeInclusive, Sub},
     rc::Rc,
     sync::Arc,
     time::Duration,
@@ -273,6 +280,8 @@ pub fn init(cx: &mut MutableAppContext, entry_openers: &mut Vec<Box<dyn EntryOpe
 }
 
 trait SelectionExt {
+    fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
+    fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
     fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
     fn spanned_rows(
         &self,
@@ -347,7 +356,7 @@ pub enum SoftWrap {
 
 pub struct Editor {
     handle: WeakViewHandle<Self>,
-    buffer: ModelHandle<Buffer>,
+    buffer: ModelHandle<MultiBuffer>,
     display_map: ModelHandle<DisplayMap>,
     selection_set_id: SelectionSetId,
     pending_selection: Option<PendingSelection>,
@@ -422,6 +431,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) -> Self {
         let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
         let mut view = Self::for_buffer(buffer, build_settings, cx);
         view.mode = EditorMode::SingleLine;
         view
@@ -433,13 +443,14 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) -> Self {
         let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
         let mut view = Self::for_buffer(buffer, build_settings, cx);
         view.mode = EditorMode::AutoHeight { max_lines };
         view
     }
 
     pub fn for_buffer(
-        buffer: ModelHandle<Buffer>,
+        buffer: ModelHandle<MultiBuffer>,
         build_settings: impl 'static + Fn(&AppContext) -> EditorSettings,
         cx: &mut ViewContext<Self>,
     ) -> Self {
@@ -454,7 +465,7 @@ impl Editor {
     }
 
     pub fn new(
-        buffer: ModelHandle<Buffer>,
+        buffer: ModelHandle<MultiBuffer>,
         build_settings: Rc<RefCell<dyn Fn(&AppContext) -> EditorSettings>>,
         cx: &mut ViewContext<Self>,
     ) -> Self {
@@ -522,6 +533,7 @@ impl Editor {
         let buffer = cx.add_model(|cx| {
             Buffer::new(0, "", cx).with_language(Some(language::PLAIN_TEXT.clone()), None, cx)
         });
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
         workspace.add_item(BufferItemHandle(buffer), cx);
     }
 
@@ -529,7 +541,7 @@ impl Editor {
         self.buffer.read(cx).replica_id()
     }
 
-    pub fn buffer(&self) -> &ModelHandle<Buffer> {
+    pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
         &self.buffer
     }
 
@@ -628,9 +640,9 @@ impl Editor {
             first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
             last_cursor_bottom = first_cursor_top + 1.;
         } else {
-            let mut selections = self.selections::<Point>(cx).peekable();
+            let selections = self.selections::<Point>(cx);
             first_cursor_top = selections
-                .peek()
+                .first()
                 .unwrap()
                 .head()
                 .to_display_point(&display_map)
@@ -756,11 +768,10 @@ impl Editor {
         click_count: usize,
         cx: &mut ViewContext<Self>,
     ) {
+        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let tail = self.newest_selection::<usize>(cx).tail();
-
         self.begin_selection(position, false, click_count, cx);
 
-        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = self.buffer.read(cx);
         let position = position.to_offset(&display_map, Bias::Left);
         let tail_anchor = buffer.anchor_before(tail);
@@ -842,13 +853,9 @@ impl Editor {
         } else if click_count > 1 {
             // Remove the newest selection since it was only added as part of this multi-click.
             let newest_selection = self.newest_selection::<usize>(cx);
-            self.update_selections::<usize>(
-                self.selections(cx)
-                    .filter(|selection| selection.id != newest_selection.id)
-                    .collect(),
-                None,
-                cx,
-            )
+            let mut selections = self.selections(cx);
+            selections.retain(|selection| selection.id != newest_selection.id);
+            self.update_selections::<usize>(selections, None, cx)
         }
 
         self.pending_selection = Some(PendingSelection { selection, mode });
@@ -895,13 +902,13 @@ impl Editor {
             let tail = tail.to_display_point(&display_map);
             self.select_columns(tail, position, overshoot, &display_map, cx);
         } else if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() {
-            let buffer = self.buffer.read(cx);
+            let buffer = self.buffer.read(cx).snapshot(cx);
             let head;
             let tail;
             match mode {
                 SelectMode::Character => {
                     head = position.to_point(&display_map);
-                    tail = selection.tail().to_point(buffer);
+                    tail = selection.tail().to_point(&buffer);
                 }
                 SelectMode::Word(original_range) => {
                     let original_display_range = original_range.start.to_display_point(&display_map)
@@ -976,7 +983,7 @@ impl Editor {
     fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
         self.columnar_selection_tail.take();
         if self.pending_selection.is_some() {
-            let selections = self.selections::<usize>(cx).collect::<Vec<_>>();
+            let selections = self.selections::<usize>(cx);
             self.update_selections(selections, None, cx);
         }
     }
@@ -1029,19 +1036,20 @@ impl Editor {
         if self.active_diagnostics.is_some() {
             self.dismiss_diagnostics(cx);
         } else if let Some(PendingSelection { selection, .. }) = self.pending_selection.take() {
-            let buffer = self.buffer.read(cx);
+            let buffer = self.buffer.read(cx).snapshot(cx);
             let selection = Selection {
                 id: selection.id,
-                start: selection.start.to_point(buffer),
-                end: selection.end.to_point(buffer),
+                start: selection.start.to_point(&buffer),
+                end: selection.end.to_point(&buffer),
                 reversed: selection.reversed,
                 goal: selection.goal,
             };
-            if self.selections::<Point>(cx).next().is_none() {
+            if self.selections::<Point>(cx).is_empty() {
                 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
             }
         } else {
-            let mut oldest_selection = self.oldest_selection::<usize>(cx);
+            let buffer = self.buffer.read(cx).snapshot(cx);
+            let mut oldest_selection = self.oldest_selection::<usize>(&buffer, cx);
             if self.selection_count(cx) == 1 {
                 oldest_selection.start = oldest_selection.head().clone();
                 oldest_selection.end = oldest_selection.head().clone();
@@ -1059,12 +1067,12 @@ impl Editor {
         I: IntoIterator<Item = Range<T>>,
         T: ToOffset,
     {
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
         let selections = ranges
             .into_iter()
             .map(|range| {
-                let mut start = range.start.to_offset(buffer);
-                let mut end = range.end.to_offset(buffer);
+                let mut start = range.start.to_offset(&buffer);
+                let mut end = range.end.to_offset(&buffer);
                 let reversed = if start > end {
                     mem::swap(&mut start, &mut end);
                     true
@@ -1131,15 +1139,15 @@ impl Editor {
         self.start_transaction(cx);
         let mut old_selections = SmallVec::<[_; 32]>::new();
         {
-            let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
-            let buffer = self.buffer.read(cx);
+            let selections = self.selections::<Point>(cx);
+            let buffer = self.buffer.read(cx).snapshot(cx);
             for selection in selections.iter() {
                 let start_point = selection.start;
                 let indent = buffer
                     .indent_column_for_line(start_point.row)
                     .min(start_point.column);
-                let start = selection.start.to_offset(buffer);
-                let end = selection.end.to_offset(buffer);
+                let start = selection.start.to_offset(&buffer);
+                let end = selection.end.to_offset(&buffer);
 
                 let mut insert_extra_newline = false;
                 if let Some(language) = buffer.language() {
@@ -1253,7 +1261,7 @@ impl Editor {
 
     fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
-        let old_selections = self.selections::<usize>(cx).collect::<SmallVec<[_; 32]>>();
+        let old_selections = self.selections::<usize>(cx);
         let mut new_selections = Vec::new();
         self.buffer.update(cx, |buffer, cx| {
             let edit_ranges = old_selections.iter().map(|s| s.start..s.end);
@@ -1284,19 +1292,20 @@ impl Editor {
     }
 
     fn autoclose_pairs(&mut self, cx: &mut ViewContext<Self>) {
-        let selections = self.selections::<usize>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<usize>(cx);
         let new_autoclose_pair_state = self.buffer.update(cx, |buffer, cx| {
-            let autoclose_pair = buffer.language().and_then(|language| {
+            let buffer_snapshot = buffer.snapshot(cx);
+            let autoclose_pair = buffer_snapshot.language().and_then(|language| {
                 let first_selection_start = selections.first().unwrap().start;
                 let pair = language.brackets().iter().find(|pair| {
-                    buffer.contains_str_at(
+                    buffer_snapshot.contains_str_at(
                         first_selection_start.saturating_sub(pair.start.len()),
                         &pair.start,
                     )
                 });
                 pair.and_then(|pair| {
                     let should_autoclose = selections[1..].iter().all(|selection| {
-                        buffer.contains_str_at(
+                        buffer_snapshot.contains_str_at(
                             selection.start.saturating_sub(pair.start.len()),
                             &pair.start,
                         )
@@ -1314,7 +1323,7 @@ impl Editor {
                 let selection_ranges = selections
                     .iter()
                     .map(|selection| {
-                        let start = selection.start.to_offset(&*buffer);
+                        let start = selection.start.to_offset(&buffer_snapshot);
                         start..start
                     })
                     .collect::<SmallVec<[_; 32]>>();
@@ -1344,7 +1353,7 @@ impl Editor {
     }
 
     fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
-        let old_selections = self.selections::<usize>(cx).collect::<Vec<_>>();
+        let old_selections = self.selections::<usize>(cx);
         let autoclose_pair_state = if let Some(autoclose_pair_state) = self.autoclose_stack.last() {
             autoclose_pair_state
         } else {
@@ -1356,12 +1365,12 @@ impl Editor {
 
         debug_assert_eq!(old_selections.len(), autoclose_pair_state.ranges.len());
 
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
         if old_selections
             .iter()
-            .zip(autoclose_pair_state.ranges.ranges::<usize>(buffer))
+            .zip(autoclose_pair_state.ranges.ranges::<usize>(&buffer))
             .all(|(selection, autoclose_range)| {
-                let autoclose_range_end = autoclose_range.end.to_offset(buffer);
+                let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
                 selection.is_empty() && selection.start == autoclose_range_end
             })
         {
@@ -1395,7 +1404,7 @@ impl Editor {
 
     pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         for selection in &mut selections {
             if selection.is_empty() {
@@ -1415,7 +1424,7 @@ impl Editor {
     pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             if selection.is_empty() {
                 let head = selection.head().to_display_point(&display_map);
@@ -1434,13 +1443,15 @@ impl Editor {
     pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
         let tab_size = self.build_settings.borrow()(cx).tab_size;
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let mut last_indent = None;
         self.buffer.update(cx, |buffer, cx| {
             for selection in &mut selections {
                 if selection.is_empty() {
                     let char_column = buffer
-                        .chars_for_range(Point::new(selection.start.row, 0)..selection.start)
+                        .as_snapshot()
+                        .text_for_range(Point::new(selection.start.row, 0)..selection.start)
+                        .flat_map(str::chars)
                         .count();
                     let chars_to_next_tab_stop = tab_size - (char_column % tab_size);
                     buffer.edit(
@@ -1504,7 +1515,7 @@ impl Editor {
     pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
         let tab_size = self.build_settings.borrow()(cx).tab_size;
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let mut deletion_ranges = Vec::new();
         let mut last_outdent = None;
         self.buffer.update(cx, |buffer, cx| {
@@ -1541,20 +1552,16 @@ impl Editor {
             buffer.edit(deletion_ranges, "", cx);
         });
 
-        self.update_selections(
-            self.selections::<usize>(cx).collect(),
-            Some(Autoscroll::Fit),
-            cx,
-        );
+        self.update_selections(self.selections::<usize>(cx), Some(Autoscroll::Fit), cx);
         self.end_transaction(cx);
     }
 
     pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
 
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
 
         let mut row_delta = 0;
         let mut new_cursors = Vec::new();
@@ -1575,13 +1582,13 @@ impl Editor {
                 }
             }
 
-            let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
+            let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
             let edit_end;
             let cursor_buffer_row;
             if buffer.max_point().row >= rows.end {
                 // If there's a line after the range, delete the \n from the end of the row range
                 // and position the cursor on the next line.
-                edit_end = Point::new(rows.end, 0).to_offset(buffer);
+                edit_end = Point::new(rows.end, 0).to_offset(&buffer);
                 cursor_buffer_row = rows.start;
             } else {
                 // If there isn't a line after the range, delete the \n from the line before the
@@ -1621,7 +1628,7 @@ impl Editor {
     pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
 
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = self.buffer.read(cx);
 
@@ -1679,9 +1686,9 @@ impl Editor {
     pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
 
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
@@ -1692,7 +1699,7 @@ impl Editor {
         let mut contiguous_selections = Vec::new();
         while let Some(selection) = selections.next() {
             // Accumulate contiguous regions of rows that we want to move.
-            contiguous_selections.push(selection.point_range(buffer));
+            contiguous_selections.push(selection.point_range(&buffer));
             let SpannedRows {
                 mut buffer_rows,
                 mut display_rows,
@@ -1706,7 +1713,7 @@ impl Editor {
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
-                    contiguous_selections.push(next_selection.point_range(buffer));
+                    contiguous_selections.push(next_selection.point_range(&buffer));
                     selections.next().unwrap();
                 } else {
                     break;
@@ -1715,13 +1722,13 @@ impl Editor {
 
             // Cut the text from the selected rows and paste it at the start of the previous line.
             if display_rows.start != 0 {
-                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
+                let start = Point::new(buffer_rows.start, 0).to_offset(&buffer);
                 let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
-                    .to_offset(buffer);
+                    .to_offset(&buffer);
 
                 let prev_row_display_start = DisplayPoint::new(display_rows.start - 1, 0);
                 let prev_row_buffer_start = display_map.prev_row_boundary(prev_row_display_start).1;
-                let prev_row_buffer_start_offset = prev_row_buffer_start.to_offset(buffer);
+                let prev_row_buffer_start_offset = prev_row_buffer_start.to_offset(&buffer);
 
                 let mut text = String::new();
                 text.extend(buffer.text_for_range(start..end));
@@ -1743,8 +1750,8 @@ impl Editor {
                 // Move folds up.
                 old_folds.push(start..end);
                 for fold in display_map.folds_in_range(start..end) {
-                    let mut start = fold.start.to_point(buffer);
-                    let mut end = fold.end.to_point(buffer);
+                    let mut start = fold.start.to_point(&buffer);
+                    let mut end = fold.end.to_point(&buffer);
                     start.row -= row_delta;
                     end.row -= row_delta;
                     new_folds.push(start..end);
@@ -1769,9 +1776,9 @@ impl Editor {
     pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
 
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
 
         let mut edits = Vec::new();
         let mut new_selection_ranges = Vec::new();
@@ -1782,7 +1789,7 @@ impl Editor {
         let mut contiguous_selections = Vec::new();
         while let Some(selection) = selections.next() {
             // Accumulate contiguous regions of rows that we want to move.
-            contiguous_selections.push(selection.point_range(buffer));
+            contiguous_selections.push(selection.point_range(&buffer));
             let SpannedRows {
                 mut buffer_rows,
                 mut display_rows,
@@ -1795,7 +1802,7 @@ impl Editor {
                 if next_buffer_rows.start <= buffer_rows.end {
                     buffer_rows.end = next_buffer_rows.end;
                     display_rows.end = next_display_rows.end;
-                    contiguous_selections.push(next_selection.point_range(buffer));
+                    contiguous_selections.push(next_selection.point_range(&buffer));
                     selections.next().unwrap();
                 } else {
                     break;
@@ -1804,14 +1811,14 @@ impl Editor {
 
             // Cut the text from the selected rows and paste it at the end of the next line.
             if display_rows.end <= display_map.max_point().row() {
-                let start = Point::new(buffer_rows.start, 0).to_offset(buffer);
+                let start = Point::new(buffer_rows.start, 0).to_offset(&buffer);
                 let end = Point::new(buffer_rows.end - 1, buffer.line_len(buffer_rows.end - 1))
-                    .to_offset(buffer);
+                    .to_offset(&buffer);
 
                 let next_row_display_end =
                     DisplayPoint::new(display_rows.end, display_map.line_len(display_rows.end));
                 let next_row_buffer_end = display_map.next_row_boundary(next_row_display_end).1;
-                let next_row_buffer_end_offset = next_row_buffer_end.to_offset(buffer);
+                let next_row_buffer_end_offset = next_row_buffer_end.to_offset(&buffer);
 
                 let mut text = String::new();
                 text.push('\n');
@@ -1830,8 +1837,8 @@ impl Editor {
                 // Move folds down.
                 old_folds.push(start..end);
                 for fold in display_map.folds_in_range(start..end) {
-                    let mut start = fold.start.to_point(buffer);
-                    let mut end = fold.end.to_point(buffer);
+                    let mut start = fold.start.to_point(&buffer);
+                    let mut end = fold.end.to_point(&buffer);
                     start.row += row_delta;
                     end.row += row_delta;
                     new_folds.push(start..end);
@@ -1856,7 +1863,7 @@ impl Editor {
     pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
         self.start_transaction(cx);
         let mut text = String::new();
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let mut clipboard_selections = Vec::with_capacity(selections.len());
         {
             let buffer = self.buffer.read(cx);
@@ -1887,7 +1894,7 @@ impl Editor {
     }
 
     pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let buffer = self.buffer.read(cx);
         let max_point = buffer.max_point();
         let mut text = String::new();
@@ -1919,7 +1926,7 @@ impl Editor {
         if let Some(item) = cx.as_mut().read_from_clipboard() {
             let clipboard_text = item.text();
             if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
-                let mut selections = self.selections::<usize>(cx).collect::<Vec<_>>();
+                let mut selections = self.selections::<usize>(cx);
                 let all_selections_were_entire_line =
                     clipboard_selections.iter().all(|s| s.is_entire_line);
                 if clipboard_selections.len() != selections.len() {
@@ -1950,7 +1957,8 @@ impl Editor {
                         // selection was copied. If this selection is also currently empty,
                         // then paste the line before the current line of the buffer.
                         let range = if selection.is_empty() && entire_line {
-                            let column = selection.start.to_point(&*buffer).column as usize;
+                            let column =
+                                selection.start.to_point(&*buffer.as_snapshot()).column as usize;
                             let line_start = selection.start - column;
                             line_start..line_start
                         } else {
@@ -1982,7 +1990,7 @@ impl Editor {
 
     pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let start = selection.start.to_display_point(&display_map);
             let end = selection.end.to_display_point(&display_map);
@@ -2004,7 +2012,7 @@ impl Editor {
 
     pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::left(&display_map, head)
@@ -2018,7 +2026,7 @@ impl Editor {
 
     pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let start = selection.start.to_display_point(&display_map);
             let end = selection.end.to_display_point(&display_map);
@@ -2040,7 +2048,7 @@ impl Editor {
 
     pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::right(&display_map, head)
@@ -2059,7 +2067,7 @@ impl Editor {
         }
 
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let start = selection.start.to_display_point(&display_map);
             let end = selection.end.to_display_point(&display_map);
@@ -2079,7 +2087,7 @@ impl Editor {
 
     pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let (head, goal) = movement::up(&display_map, head, selection.goal).unwrap();
@@ -2097,7 +2105,7 @@ impl Editor {
         }
 
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let start = selection.start.to_display_point(&display_map);
             let end = selection.end.to_display_point(&display_map);
@@ -2117,7 +2125,7 @@ impl Editor {
 
     pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let (head, goal) = movement::down(&display_map, head, selection.goal).unwrap();
@@ -2134,7 +2142,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map);
@@ -2152,7 +2160,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map);
@@ -2169,7 +2177,7 @@ impl Editor {
     ) {
         self.start_transaction(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             if selection.is_empty() {
                 let head = selection.head().to_display_point(&display_map);
@@ -2190,7 +2198,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map);
@@ -2208,7 +2216,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map);
@@ -2225,7 +2233,7 @@ impl Editor {
     ) {
         self.start_transaction(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             if selection.is_empty() {
                 let head = selection.head().to_display_point(&display_map);
@@ -2246,7 +2254,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let new_head = movement::line_beginning(&display_map, head, true);
@@ -2265,7 +2273,7 @@ impl Editor {
         cx: &mut ViewContext<Self>,
     ) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let new_head = movement::line_beginning(&display_map, head, *toggle_indent);
@@ -2288,7 +2296,7 @@ impl Editor {
 
     pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         {
             for selection in &mut selections {
                 let head = selection.head().to_display_point(&display_map);
@@ -2305,7 +2313,7 @@ impl Editor {
 
     pub fn select_to_end_of_line(&mut self, _: &SelectToEndOfLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         for selection in &mut selections {
             let head = selection.head().to_display_point(&display_map);
             let new_head = movement::line_end(&display_map, head);
@@ -2378,7 +2386,7 @@ impl Editor {
 
     pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let buffer = self.buffer.read(cx);
         let max_point = buffer.max_point();
         for selection in &mut selections {
@@ -2395,7 +2403,7 @@ impl Editor {
         _: &SplitSelectionIntoLines,
         cx: &mut ViewContext<Self>,
     ) {
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let buffer = self.buffer.read(cx);
 
         let mut to_unfold = Vec::new();
@@ -2434,7 +2442,7 @@ impl Editor {
 
     fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let mut state = self.add_selections_state.take().unwrap_or_else(|| {
             let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
             let range = oldest_selection.display_range(&display_map).sorted();
@@ -2529,7 +2537,7 @@ impl Editor {
         let replace_newest = action.0;
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = &display_map.buffer_snapshot;
-        let mut selections = self.selections::<usize>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<usize>(cx);
         if let Some(mut select_next_state) = self.select_next_state.take() {
             let query = &select_next_state.query;
             if !select_next_state.done {
@@ -2636,11 +2644,12 @@ impl Editor {
         let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
 
         self.start_transaction(cx);
-        let mut selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let mut selections = self.selections::<Point>(cx);
         let mut all_selection_lines_are_comments = true;
         let mut edit_ranges = Vec::new();
         let mut last_toggled_row = None;
         self.buffer.update(cx, |buffer, cx| {
+            let buffer_snapshot = buffer.snapshot(cx);
             for selection in &mut selections {
                 edit_ranges.clear();
 
@@ -2660,12 +2669,12 @@ impl Editor {
                         last_toggled_row = Some(row);
                     }
 
-                    if buffer.is_line_blank(row) {
+                    if buffer_snapshot.is_line_blank(row) {
                         continue;
                     }
 
-                    let start = Point::new(row, buffer.indent_column_for_line(row));
-                    let mut line_bytes = buffer
+                    let start = Point::new(row, buffer_snapshot.indent_column_for_line(row));
+                    let mut line_bytes = buffer_snapshot
                         .bytes_in_range(start..buffer.max_point())
                         .flatten()
                         .copied();
@@ -2712,11 +2721,7 @@ impl Editor {
             }
         });
 
-        self.update_selections(
-            self.selections::<usize>(cx).collect(),
-            Some(Autoscroll::Fit),
-            cx,
-        );
+        self.update_selections(self.selections::<usize>(cx), Some(Autoscroll::Fit), cx);
         self.end_transaction(cx);
     }
 
@@ -2725,9 +2730,9 @@ impl Editor {
         _: &SelectLargerSyntaxNode,
         cx: &mut ViewContext<Self>,
     ) {
-        let old_selections = self.selections::<usize>(cx).collect::<Box<_>>();
+        let old_selections = self.selections::<usize>(cx).into_boxed_slice();
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
 
         let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
         let mut selected_larger_node = false;
@@ -2783,8 +2788,8 @@ impl Editor {
         _: &MoveToEnclosingBracket,
         cx: &mut ViewContext<Self>,
     ) {
-        let mut selections = self.selections::<usize>(cx).collect::<Vec<_>>();
-        let buffer = self.buffer.read(cx.as_ref());
+        let mut selections = self.selections::<usize>(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
         for selection in &mut selections {
             if let Some((open_range, close_range)) =
                 buffer.enclosing_bracket_ranges(selection.start..selection.end)
@@ -2806,12 +2811,12 @@ impl Editor {
     }
 
     pub fn show_next_diagnostic(&mut self, _: &ShowNextDiagnostic, cx: &mut ViewContext<Self>) {
+        let buffer = self.buffer.read(cx).snapshot(cx);
         let selection = self.newest_selection::<usize>(cx);
-        let buffer = self.buffer.read(cx.as_ref());
         let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
             active_diagnostics
                 .primary_range
-                .to_offset(buffer)
+                .to_offset(&buffer)
                 .to_inclusive()
         });
         let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
@@ -2863,8 +2868,8 @@ impl Editor {
 
     fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
         if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
-            let buffer = self.buffer.read(cx);
-            let primary_range_start = active_diagnostics.primary_range.start.to_offset(buffer);
+            let buffer = self.buffer.read(cx).snapshot(cx);
+            let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
             let is_valid = buffer
                 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
                 .any(|(range, diagnostic)| {
@@ -2895,7 +2900,7 @@ impl Editor {
     fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
         self.dismiss_diagnostics(cx);
         self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
-            let buffer = self.buffer.read(cx);
+            let buffer = self.buffer.read(cx).snapshot(cx);
 
             let mut primary_range = None;
             let mut primary_message = None;
@@ -3010,7 +3015,7 @@ impl Editor {
         set_id: SelectionSetId,
         range: Range<DisplayPoint>,
         cx: &'a mut MutableAppContext,
-    ) -> impl 'a + Iterator<Item = Selection<DisplayPoint>> {
+    ) -> Vec<Selection<DisplayPoint>> {
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = self.buffer.read(cx);
 
@@ -3036,14 +3041,10 @@ impl Editor {
 
         let range = (range.start.to_offset(&display_map, Bias::Left), Bias::Left)
             ..(range.end.to_offset(&display_map, Bias::Left), Bias::Right);
-        let selections = self
-            .buffer
-            .read(cx)
+        buffer
             .selection_set(set_id)
             .unwrap()
-            .intersecting_selections::<Point, _>(range, buffer);
-
-        selections
+            .intersecting_selections::<Point, _>(range, &buffer.as_snapshot())
             .map(move |s| Selection {
                 id: s.id,
                 start: s.start.to_display_point(&display_map),
@@ -3052,15 +3053,17 @@ impl Editor {
                 goal: s.goal,
             })
             .chain(pending_selection)
+            .collect()
     }
 
-    pub fn selections<'a, D>(&self, cx: &'a AppContext) -> impl 'a + Iterator<Item = Selection<D>>
+    pub fn selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
     where
-        D: 'a + TextDimension + Ord,
+        D: 'a + TextDimension + Ord + Sub<D, Output = D>,
     {
-        let buffer = self.buffer.read(cx);
-        let mut selections = self.selection_set(cx).selections::<D>(buffer).peekable();
+        let buffer = self.buffer.read(cx).snapshot(cx);
+        let mut selections = self.selection_set(cx).selections::<D>(&buffer).peekable();
         let mut pending_selection = self.pending_selection(cx);
+
         iter::from_fn(move || {
             if let Some(pending) = pending_selection.as_mut() {
                 while let Some(next_selection) = selections.peek() {
@@ -3084,14 +3087,18 @@ impl Editor {
                 selections.next()
             }
         })
+        .collect()
     }
 
-    fn pending_selection<D: TextDimension>(&self, cx: &AppContext) -> Option<Selection<D>> {
-        let buffer = self.buffer.read(cx);
+    fn pending_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
+        &self,
+        cx: &AppContext,
+    ) -> Option<Selection<D>> {
+        let buffer = self.buffer.read(cx).as_snapshot();
         self.pending_selection.as_ref().map(|pending| Selection {
             id: pending.selection.id,
-            start: pending.selection.start.summary::<D>(buffer),
-            end: pending.selection.end.summary::<D>(buffer),
+            start: pending.selection.start.summary::<D>(&buffer),
+            end: pending.selection.end.summary::<D>(&buffer),
             reversed: pending.selection.reversed,
             goal: pending.selection.goal,
         })
@@ -3105,18 +3112,26 @@ impl Editor {
         selection_count
     }
 
-    pub fn oldest_selection<T: TextDimension>(&self, cx: &AppContext) -> Selection<T> {
-        let buffer = self.buffer.read(cx);
+    pub fn oldest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
+        &self,
+        snapshot: &MultiBufferSnapshot,
+        cx: &AppContext,
+    ) -> Selection<D> {
         self.selection_set(cx)
-            .oldest_selection(buffer)
+            .oldest_selection(snapshot)
             .or_else(|| self.pending_selection(cx))
             .unwrap()
     }
 
-    pub fn newest_selection<T: TextDimension>(&self, cx: &AppContext) -> Selection<T> {
-        let buffer = self.buffer.read(cx);
+    pub fn newest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
+        &self,
+        cx: &AppContext,
+    ) -> Selection<D> {
         self.pending_selection(cx)
-            .or_else(|| self.selection_set(cx).newest_selection(buffer))
+            .or_else(|| {
+                self.selection_set(cx)
+                    .newest_selection(&self.buffer.read(cx).as_snapshot())
+            })
             .unwrap()
     }
 
@@ -3136,7 +3151,7 @@ impl Editor {
         T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
     {
         // Merge overlapping selections.
-        let buffer = self.buffer.read(cx);
+        let buffer = self.buffer.read(cx).snapshot(cx);
         let mut i = 1;
         while i < selections.len() {
             if selections[i - 1].end >= selections[i].start {
@@ -3161,9 +3176,9 @@ impl Editor {
                 if selections.len() == autoclose_pair_state.ranges.len() {
                     selections
                         .iter()
-                        .zip(autoclose_pair_state.ranges.ranges::<Point>(buffer))
+                        .zip(autoclose_pair_state.ranges.ranges::<Point>(&buffer))
                         .all(|(selection, autoclose_range)| {
-                            let head = selection.head().to_point(&*buffer);
+                            let head = selection.head().to_point(&buffer);
                             autoclose_range.start <= head && autoclose_range.end >= head
                         })
                 } else {
@@ -3222,7 +3237,7 @@ impl Editor {
     pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
         let mut fold_ranges = Vec::new();
 
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         for selection in selections {
             let range = selection.display_range(&display_map).sorted();
@@ -3245,7 +3260,7 @@ impl Editor {
     }
 
     pub fn unfold(&mut self, _: &Unfold, cx: &mut ViewContext<Self>) {
-        let selections = self.selections::<Point>(cx).collect::<Vec<_>>();
+        let selections = self.selections::<Point>(cx);
         let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
         let buffer = self.buffer.read(cx);
         let ranges = selections
@@ -3306,12 +3321,17 @@ impl Editor {
 
     pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
         let selections = self.selections::<Point>(cx);
-        let ranges = selections.map(|s| s.start..s.end).collect();
+        let ranges = selections.into_iter().map(|s| s.start..s.end);
         self.fold_ranges(ranges, cx);
     }
 
-    fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, cx: &mut ViewContext<Self>) {
-        if !ranges.is_empty() {
+    fn fold_ranges<T: ToOffset>(
+        &mut self,
+        ranges: impl IntoIterator<Item = Range<T>>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        let mut ranges = ranges.into_iter().peekable();
+        if ranges.peek().is_some() {
             self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
             self.request_autoscroll(Autoscroll::Fit, cx);
             cx.notify();

crates/editor/src/element.rs 🔗

@@ -19,7 +19,7 @@ use gpui::{
     MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
 };
 use json::json;
-use language::{Chunk, ToPoint};
+use language::{multi_buffer::ToPoint, Chunk};
 use smallvec::SmallVec;
 use std::{
     cmp::{self, Ordering},
@@ -738,13 +738,11 @@ impl Element for EditorElement {
         self.update_view(cx.app, |view, cx| {
             highlighted_row = view.highlighted_row();
             for selection_set_id in view.active_selection_sets(cx).collect::<Vec<_>>() {
-                let replica_selections = view
-                    .intersecting_selections(
-                        selection_set_id,
-                        DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0),
-                        cx,
-                    )
-                    .collect::<Vec<_>>();
+                let replica_selections = view.intersecting_selections(
+                    selection_set_id,
+                    DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0),
+                    cx,
+                );
                 for selection in &replica_selections {
                     if selection_set_id == view.selection_set_id {
                         let is_empty = selection.start == selection.end;
@@ -1165,14 +1163,13 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
 mod tests {
     use super::*;
     use crate::{Editor, EditorSettings};
-    use language::Buffer;
+    use language::{MultiBuffer};
     use util::test::sample_text;
 
     #[gpui::test]
     fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
         let settings = EditorSettings::test(cx);
-
-        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
+        let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
         let (window_id, editor) = cx.add_window(Default::default(), |cx| {
             Editor::for_buffer(
                 buffer,

crates/editor/src/items.rs 🔗

@@ -5,12 +5,15 @@ use gpui::{
     MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
     WeakModelHandle,
 };
-use language::{Buffer, Diagnostic, File as _};
+use language::{
+    multi_buffer::{MultiBuffer, ToPoint as _},
+    Diagnostic, File as _,
+};
 use postage::watch;
 use project::{ProjectPath, Worktree};
 use std::fmt::Write;
 use std::path::Path;
-use text::{Point, Selection, ToPoint};
+use text::{Point, Selection};
 use workspace::{
     settings, EntryOpener, ItemHandle, ItemView, ItemViewHandle, Settings, StatusItemView,
     WeakItemHandle,
@@ -19,10 +22,10 @@ use workspace::{
 pub struct BufferOpener;
 
 #[derive(Clone)]
-pub struct BufferItemHandle(pub ModelHandle<Buffer>);
+pub struct BufferItemHandle(pub ModelHandle<MultiBuffer>);
 
 #[derive(Clone)]
-struct WeakBufferItemHandle(WeakModelHandle<Buffer>);
+struct WeakBufferItemHandle(WeakModelHandle<MultiBuffer>);
 
 impl EntryOpener for BufferOpener {
     fn open(
@@ -32,10 +35,10 @@ impl EntryOpener for BufferOpener {
         cx: &mut ModelContext<Worktree>,
     ) -> Option<Task<Result<Box<dyn ItemHandle>>>> {
         let buffer = worktree.open_buffer(project_path.path, cx);
-        let task = cx.spawn(|_, _| async move {
-            buffer
-                .await
-                .map(|buffer| Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
+        let task = cx.spawn(|_, mut cx| async move {
+            let buffer = buffer.await?;
+            let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+            Ok(Box::new(BufferItemHandle(buffer)) as Box<dyn ItemHandle>)
         });
         Some(task)
     }
@@ -102,7 +105,7 @@ impl ItemHandle for BufferItemHandle {
     }
 
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        self.0.read(cx).file().map(|f| ProjectPath {
+        self.0.read(cx).file(cx).map(|f| ProjectPath {
             worktree_id: f.worktree_id(),
             path: f.path().clone(),
         })
@@ -137,7 +140,7 @@ impl ItemView for Editor {
         let filename = self
             .buffer()
             .read(cx)
-            .file()
+            .file(cx)
             .and_then(|file| file.file_name());
         if let Some(name) = filename {
             name.to_string_lossy().into()
@@ -147,7 +150,7 @@ impl ItemView for Editor {
     }
 
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        self.buffer().read(cx).file().map(|file| ProjectPath {
+        self.buffer().read(cx).file(cx).map(|file| ProjectPath {
             worktree_id: file.worktree_id(),
             path: file.path().clone(),
         })
@@ -174,7 +177,14 @@ impl ItemView for Editor {
         path: &Path,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
-        self.buffer().update(cx, |buffer, cx| {
+        let buffer = self
+            .buffer()
+            .read(cx)
+            .as_singleton()
+            .expect("cannot call save_as on an excerpt list")
+            .clone();
+
+        buffer.update(cx, |buffer, cx| {
             let handle = cx.handle();
             let text = buffer.as_rope().clone();
             let version = buffer.version();
@@ -237,7 +247,7 @@ impl CursorPosition {
 
     fn update_position(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
         let editor = editor.read(cx);
-        let buffer = editor.buffer().read(cx);
+        let buffer = editor.buffer().read(cx).snapshot(cx);
 
         self.selected_count = 0;
         let mut last_selection: Option<Selection<usize>> = None;
@@ -250,7 +260,7 @@ impl CursorPosition {
                 last_selection = Some(selection);
             }
         }
-        self.position = last_selection.map(|s| s.head().to_point(buffer));
+        self.position = last_selection.map(|s| s.head().to_point(&buffer));
 
         cx.notify();
     }

crates/editor/src/movement.rs 🔗

@@ -1,7 +1,7 @@
 use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
 use anyhow::Result;
+use language::multi_buffer::ToPoint;
 use std::{cmp, ops::Range};
-use text::ToPoint;
 
 pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> Result<DisplayPoint> {
     if point.column() > 0 {
@@ -244,7 +244,8 @@ fn char_kind(c: char) -> CharKind {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{display_map::DisplayMap, Buffer};
+    use crate::display_map::DisplayMap;
+    use language::MultiBuffer;
 
     #[gpui::test]
     fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
@@ -256,7 +257,7 @@ mod tests {
             .unwrap();
         let font_size = 14.0;
 
-        let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ hi—jk", cx));
+        let buffer = MultiBuffer::build_simple("a bcΔ defγ hi—jk", cx);
         let display_map =
             cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
         let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -312,7 +313,7 @@ mod tests {
             .select_font(family_id, &Default::default())
             .unwrap();
         let font_size = 14.0;
-        let buffer = cx.add_model(|cx| Buffer::new(0, "lorem ipsum   dolor\n    sit", cx));
+        let buffer = MultiBuffer::build_simple("lorem ipsum   dolor\n    sit", cx);
         let display_map =
             cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
         let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));

crates/go_to_line/src/go_to_line.rs 🔗

@@ -67,7 +67,7 @@ impl GoToLine {
         let (restore_state, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
             let restore_state = Some(RestoreState {
                 scroll_position: editor.scroll_position(cx),
-                selections: editor.selections::<usize>(cx).collect(),
+                selections: editor.selections::<usize>(cx),
             });
 
             (

crates/language/src/language.rs 🔗

@@ -1,6 +1,6 @@
 mod buffer;
 mod highlight_map;
-mod multi_buffer;
+pub mod multi_buffer;
 pub mod proto;
 #[cfg(test)]
 mod tests;
@@ -12,6 +12,7 @@ use gpui::{executor::Background, AppContext};
 use highlight_map::HighlightMap;
 use lazy_static::lazy_static;
 use lsp::LanguageServer;
+pub use multi_buffer::MultiBuffer;
 use parking_lot::Mutex;
 use serde::Deserialize;
 use std::{collections::HashSet, path::Path, str, sync::Arc};

crates/language/src/multi_buffer.rs 🔗

@@ -5,20 +5,25 @@ mod selection;
 use self::location::*;
 use crate::{
     buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
-    BufferSnapshot,
+    BufferSnapshot, Diagnostic, File, Language,
 };
+use anyhow::Result;
+use clock::ReplicaId;
 use collections::HashMap;
-use gpui::{AppContext, Entity, ModelContext, ModelHandle};
-use parking_lot::Mutex;
-use std::{cmp, ops::Range};
+use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
+use parking_lot::{Mutex, MutexGuard};
+use std::{cmp, io, ops::Range, sync::Arc, time::SystemTime};
 use sum_tree::{Bias, Cursor, SumTree};
 use text::{
     rope::TextDimension,
     subscription::{Subscription, Topic},
-    AnchorRangeExt, Edit, Point, PointUtf16, TextSummary,
+    AnchorRangeExt as _, Edit, Point, PointUtf16, Selection, SelectionSetId, TextSummary,
 };
 use theme::SyntaxTheme;
 
+pub use anchor::{Anchor, AnchorRangeExt, AnchorRangeMap, AnchorRangeSet};
+pub use selection::SelectionSet;
+
 const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
 
 #[derive(Default)]
@@ -28,11 +33,11 @@ pub struct MultiBuffer {
     subscriptions: Topic,
 }
 
-pub trait ToOffset {
+pub trait ToOffset: 'static {
     fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize;
 }
 
-pub trait ToPoint {
+pub trait ToPoint: 'static {
     fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point;
 }
 
@@ -69,7 +74,7 @@ struct ExcerptSummary {
     text: TextSummary,
 }
 
-pub struct Chunks<'a> {
+pub struct MultiBufferChunks<'a> {
     range: Range<usize>,
     cursor: Cursor<'a, Excerpt, usize>,
     header_height: u8,
@@ -77,20 +82,155 @@ pub struct Chunks<'a> {
     theme: Option<&'a SyntaxTheme>,
 }
 
+pub struct MultiBufferBytes<'a> {
+    chunks: MultiBufferChunks<'a>,
+}
+
 impl MultiBuffer {
     pub fn new() -> Self {
         Self::default()
     }
 
+    pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
+        let mut this = Self::new();
+        this.push(
+            ExcerptProperties {
+                buffer: &buffer,
+                range: text::Anchor::min()..text::Anchor::max(),
+                header_height: 0,
+            },
+            cx,
+        );
+        this
+    }
+
+    pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle<Self> {
+        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
+        cx.add_model(|cx| Self::singleton(buffer, cx))
+    }
+
     pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
         self.sync(cx);
         self.snapshot.lock().clone()
     }
 
+    pub fn as_snapshot(&self) -> MutexGuard<MultiBufferSnapshot> {
+        self.snapshot.lock()
+    }
+
+    pub fn as_singleton(&self) -> Option<&ModelHandle<Buffer>> {
+        if self.buffers.len() == 1 {
+            return Some(&self.buffers.values().next().unwrap().buffer);
+        } else {
+            None
+        }
+    }
+
     pub fn subscribe(&mut self) -> Subscription {
         self.subscriptions.subscribe()
     }
 
+    pub fn edit<I, S, T>(&mut self, ranges_iter: I, new_text: T, cx: &mut ModelContext<Self>)
+    where
+        I: IntoIterator<Item = Range<S>>,
+        S: ToOffset,
+        T: Into<String>,
+    {
+        self.edit_internal(ranges_iter, new_text, false, cx)
+    }
+
+    pub fn edit_with_autoindent<I, S, T>(
+        &mut self,
+        ranges_iter: I,
+        new_text: T,
+        cx: &mut ModelContext<Self>,
+    ) where
+        I: IntoIterator<Item = Range<S>>,
+        S: ToOffset,
+        T: Into<String>,
+    {
+        self.edit_internal(ranges_iter, new_text, true, cx)
+    }
+
+    pub fn edit_internal<I, S, T>(
+        &mut self,
+        ranges_iter: I,
+        new_text: T,
+        autoindent: bool,
+        cx: &mut ModelContext<Self>,
+    ) where
+        I: IntoIterator<Item = Range<S>>,
+        S: ToOffset,
+        T: Into<String>,
+    {
+        todo!()
+    }
+
+    pub fn start_transaction(
+        &mut self,
+        selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
+    ) -> Result<()> {
+        todo!()
+    }
+
+    pub fn end_transaction(
+        &mut self,
+        selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
+        cx: &mut ModelContext<Self>,
+    ) -> Result<()> {
+        todo!()
+    }
+
+    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
+        todo!()
+    }
+
+    pub fn redo(&mut self, cx: &mut ModelContext<Self>) {
+        todo!()
+    }
+
+    pub fn selection_set(&self, set_id: SelectionSetId) -> Result<&SelectionSet> {
+        todo!()
+    }
+
+    pub fn add_selection_set<T: ToOffset>(
+        &mut self,
+        selections: &[Selection<T>],
+        cx: &mut ModelContext<Self>,
+    ) -> SelectionSetId {
+        todo!()
+    }
+
+    pub fn remove_selection_set(
+        &mut self,
+        set_id: SelectionSetId,
+        cx: &mut ModelContext<Self>,
+    ) -> Result<()> {
+        todo!()
+    }
+
+    pub fn update_selection_set<T: ToOffset>(
+        &mut self,
+        set_id: SelectionSetId,
+        selections: &[Selection<T>],
+        cx: &mut ModelContext<Self>,
+    ) -> Result<()> {
+        todo!()
+    }
+
+    pub fn set_active_selection_set(
+        &mut self,
+        set_id: Option<SelectionSetId>,
+        cx: &mut ModelContext<Self>,
+    ) -> Result<()> {
+        todo!()
+    }
+
+    pub fn selection_sets(&self) -> impl Iterator<Item = (&SelectionSetId, &SelectionSet)> {
+        todo!();
+        None.into_iter()
+    }
+
     pub fn push<O>(&mut self, props: ExcerptProperties<O>, cx: &mut ModelContext<Self>) -> ExcerptId
     where
         O: text::ToOffset,
@@ -125,6 +265,30 @@ impl MultiBuffer {
         id
     }
 
+    pub fn save(
+        &mut self,
+        cx: &mut ModelContext<Self>,
+    ) -> Result<Task<Result<(clock::Global, SystemTime)>>> {
+        todo!()
+    }
+
+    pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
+        self.as_singleton()
+            .and_then(|buffer| buffer.read(cx).file())
+    }
+
+    pub fn is_dirty(&self) -> bool {
+        todo!()
+    }
+
+    pub fn has_conflict(&self) -> bool {
+        todo!()
+    }
+
+    pub fn is_parsing(&self, _: &AppContext) -> bool {
+        todo!()
+    }
+
     fn sync(&self, cx: &AppContext) {
         let mut snapshot = self.snapshot.lock();
         let mut excerpts_to_edit = Vec::new();
@@ -194,17 +358,141 @@ impl MultiBuffer {
     }
 }
 
+// Methods delegating to the snapshot
+impl MultiBuffer {
+    pub fn replica_id(&self) -> ReplicaId {
+        self.snapshot.lock().replica_id()
+    }
+
+    pub fn text(&self) -> String {
+        self.snapshot.lock().text()
+    }
+
+    pub fn text_for_range<'a, T: ToOffset>(
+        &'a self,
+        range: Range<T>,
+    ) -> impl Iterator<Item = &'a str> {
+        todo!();
+        [].into_iter()
+    }
+
+    pub fn max_point(&self) -> Point {
+        self.snapshot.lock().max_point()
+    }
+
+    pub fn len(&self) -> usize {
+        self.snapshot.lock().len()
+    }
+
+    pub fn line_len(&self, row: u32) -> u32 {
+        self.snapshot.lock().line_len(row)
+    }
+
+    pub fn is_line_blank(&self, row: u32) -> bool {
+        self.snapshot.lock().is_line_blank(row)
+    }
+
+    pub fn indent_column_for_line(&self, row: u32) -> u32 {
+        self.snapshot.lock().indent_column_for_line(row)
+    }
+
+    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
+        self.snapshot.lock().anchor_before(position)
+    }
+
+    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
+        self.snapshot.lock().anchor_after(position)
+    }
+
+    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
+        self.snapshot.lock().anchor_at(position, bias)
+    }
+
+    pub fn anchor_range_set<E>(
+        &self,
+        start_bias: Bias,
+        end_bias: Bias,
+        entries: E,
+    ) -> AnchorRangeSet
+    where
+        E: IntoIterator<Item = Range<usize>>,
+    {
+        todo!()
+    }
+
+    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
+        self.snapshot.lock().clip_offset(offset, bias)
+    }
+
+    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
+        self.snapshot.lock().clip_point(point, bias)
+    }
+
+    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
+        todo!()
+    }
+
+    pub fn parse_count(&self) -> usize {
+        self.snapshot.lock().parse_count()
+    }
+
+    pub fn diagnostics_update_count(&self) -> usize {
+        self.snapshot.lock().diagnostics_update_count()
+    }
+
+    pub fn diagnostics_in_range<'a, T, O>(
+        &'a self,
+        search_range: Range<T>,
+    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
+    where
+        T: 'a + ToOffset,
+        O: 'a,
+    {
+        todo!();
+        None.into_iter()
+    }
+}
+
+#[cfg(any(test, feature = "test-support"))]
+impl MultiBuffer {
+    pub fn randomly_edit<R: rand::Rng>(&mut self, _: &mut R, _: usize, _: &mut ModelContext<Self>) {
+        todo!()
+    }
+
+    pub fn randomly_mutate<R: rand::Rng>(&mut self, rng: &mut R, cx: &mut ModelContext<Self>) {
+        todo!()
+    }
+}
+
 impl Entity for MultiBuffer {
-    type Event = ();
+    type Event = super::Event;
 }
 
 impl MultiBufferSnapshot {
+    pub fn replica_id(&self) -> ReplicaId {
+        todo!()
+    }
+
     pub fn text(&self) -> String {
         self.chunks(0..self.len(), None)
             .map(|chunk| chunk.text)
             .collect()
     }
 
+    pub fn reversed_chars_at<'a, T: ToOffset>(
+        &'a self,
+        position: T,
+    ) -> impl Iterator<Item = char> + 'a {
+        todo!();
+        None.into_iter()
+    }
+
+    pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
+        let offset = position.to_offset(self);
+        self.text_for_range(offset..self.len())
+            .flat_map(|chunk| chunk.chars())
+    }
+
     pub fn text_for_range<'a, T: ToOffset>(
         &'a self,
         range: Range<T>,
@@ -212,6 +500,18 @@ impl MultiBufferSnapshot {
         self.chunks(range, None).map(|chunk| chunk.text)
     }
 
+    pub fn is_line_blank(&self, row: u32) -> bool {
+        self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
+            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
+    }
+
+    pub fn contains_str_at<T>(&self, _: T, _: &str) -> bool
+    where
+        T: ToOffset,
+    {
+        todo!()
+    }
+
     pub fn len(&self) -> usize {
         self.excerpts.summary().text.bytes
     }
@@ -291,11 +591,15 @@ impl MultiBufferSnapshot {
         }
     }
 
+    pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
+        todo!()
+    }
+
     pub fn chunks<'a, T: ToOffset>(
         &'a self,
         range: Range<T>,
         theme: Option<&'a SyntaxTheme>,
-    ) -> Chunks<'a> {
+    ) -> MultiBufferChunks<'a> {
         let range = range.start.to_offset(self)..range.end.to_offset(self);
         let mut cursor = self.excerpts.cursor::<usize>();
         cursor.seek(&range.start, Bias::Right, &());
@@ -331,7 +635,7 @@ impl MultiBufferSnapshot {
             excerpt.buffer.chunks(buffer_start..buffer_end, theme)
         });
 
-        Chunks {
+        MultiBufferChunks {
             range,
             cursor,
             header_height,
@@ -413,6 +717,10 @@ impl MultiBufferSnapshot {
         }
     }
 
+    pub fn indent_column_for_line(&self, row: u32) -> u32 {
+        todo!()
+    }
+
     pub fn line_len(&self, row: u32) -> u32 {
         let mut cursor = self.excerpts.cursor::<Point>();
         cursor.seek(&Point::new(row, 0), Bias::Right, &());
@@ -534,18 +842,62 @@ impl MultiBufferSnapshot {
         summary
     }
 
-    fn resolve_excerpt<'a, D: TextDimension>(
+    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
+        self.anchor_at(position, Bias::Left)
+    }
+
+    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
+        self.anchor_at(position, Bias::Right)
+    }
+
+    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
+        todo!()
+    }
+
+    pub fn parse_count(&self) -> usize {
+        todo!()
+    }
+
+    pub fn enclosing_bracket_ranges<T: ToOffset>(
+        &self,
+        range: Range<T>,
+    ) -> Option<(Range<usize>, Range<usize>)> {
+        todo!()
+    }
+
+    pub fn diagnostics_update_count(&self) -> usize {
+        todo!()
+    }
+
+    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
+        todo!()
+    }
+
+    pub fn diagnostic_group<'a, O>(
         &'a self,
-        excerpt_id: &ExcerptId,
-    ) -> Option<(D, &'a BufferSnapshot)> {
-        let mut cursor = self.excerpts.cursor::<(ExcerptId, TextSummary)>();
-        cursor.seek(excerpt_id, Bias::Left, &());
-        if let Some(excerpt) = cursor.item() {
-            if cursor.start().0 == *excerpt_id {
-                return Some((D::from_text_summary(&cursor.start().1), &excerpt.buffer));
-            }
-        }
-        None
+        group_id: usize,
+    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
+    where
+        O: 'a,
+    {
+        todo!();
+        None.into_iter()
+    }
+
+    pub fn diagnostics_in_range<'a, T, O>(
+        &'a self,
+        search_range: Range<T>,
+    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
+    where
+        T: 'a + ToOffset,
+        O: 'a,
+    {
+        todo!();
+        None.into_iter()
+    }
+
+    pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
+        todo!()
     }
 
     fn buffer_snapshot_for_excerpt<'a>(
@@ -672,7 +1024,17 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Location {
     }
 }
 
-impl<'a> Iterator for Chunks<'a> {
+impl<'a> MultiBufferChunks<'a> {
+    pub fn offset(&self) -> usize {
+        todo!()
+    }
+
+    pub fn seek(&mut self, offset: usize) {
+        todo!()
+    }
+}
+
+impl<'a> Iterator for MultiBufferChunks<'a> {
     type Item = Chunk<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -726,6 +1088,20 @@ impl<'a> Iterator for Chunks<'a> {
     }
 }
 
+impl<'a> Iterator for MultiBufferBytes<'a> {
+    type Item = &'a [u8];
+
+    fn next(&mut self) -> Option<Self::Item> {
+        todo!()
+    }
+}
+
+impl<'a> io::Read for MultiBufferBytes<'a> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        todo!()
+    }
+}
+
 impl ToOffset for Point {
     fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
         snapshot.point_to_offset(*self)

crates/language/src/multi_buffer/anchor.rs 🔗

@@ -1,9 +1,12 @@
-use super::{location::*, ExcerptSummary, MultiBufferSnapshot, ToOffset};
+use super::{location::*, ExcerptSummary, MultiBufferSnapshot, ToOffset, ToPoint};
 use anyhow::{anyhow, Result};
 use smallvec::SmallVec;
-use std::{cmp::Ordering, ops::Range};
+use std::{
+    cmp::Ordering,
+    ops::{Range, Sub},
+};
 use sum_tree::Bias;
-use text::{rope::TextDimension, AnchorRangeExt, ToOffset as _};
+use text::{rope::TextDimension, AnchorRangeExt as _, Point};
 
 #[derive(Clone, Eq, PartialEq, Debug, Hash)]
 pub struct Anchor {
@@ -16,6 +19,9 @@ pub struct AnchorRangeMap<T> {
     entries: SmallVec<[(ExcerptId, text::AnchorRangeMap<T>); 1]>,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct AnchorRangeSet(AnchorRangeMap<()>);
+
 impl Anchor {
     pub fn min() -> Self {
         Self {
@@ -68,6 +74,27 @@ impl Anchor {
         }
         self.clone()
     }
+
+    pub fn summary<'a, D>(&self, snapshot: &'a MultiBufferSnapshot) -> D
+    where
+        D: TextDimension + Ord + Sub<D, Output = D>,
+    {
+        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
+        cursor.seek(&self.excerpt_id, Bias::Left, &());
+        if let Some(excerpt) = cursor.item() {
+            if excerpt.id == self.excerpt_id {
+                let mut excerpt_start = D::from_text_summary(&cursor.start().text);
+                excerpt_start.add_summary(&excerpt.header_summary(), &());
+                let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
+                let buffer_point = self.text_anchor.summary::<D>(&excerpt.buffer);
+                if buffer_point > excerpt_buffer_start {
+                    excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
+                }
+                return excerpt_start;
+            }
+        }
+        D::from_text_summary(&cursor.start().text)
+    }
 }
 
 impl<T> AnchorRangeMap<T> {
@@ -263,18 +290,48 @@ impl<T> AnchorRangeMap<T> {
     }
 }
 
+impl AnchorRangeSet {
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn ranges<'a, D>(
+        &'a self,
+        content: &'a MultiBufferSnapshot,
+    ) -> impl 'a + Iterator<Item = Range<Point>>
+    where
+        D: TextDimension,
+    {
+        self.0.ranges(content).map(|(range, _)| range)
+    }
+}
+
 impl ToOffset for Anchor {
     fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
-        cursor.seek(&self.excerpt_id, Bias::Left, &());
-        if let Some(excerpt) = cursor.item() {
-            if excerpt.id == self.excerpt_id {
-                let buffer_offset = self.text_anchor.to_offset(&excerpt.buffer);
-                return cursor.start().text.bytes
-                    + excerpt.header_height as usize
-                    + buffer_offset.saturating_sub(excerpt.range.start.to_offset(&excerpt.buffer));
-            }
-        }
-        cursor.start().text.bytes
+        self.summary(snapshot)
+    }
+}
+
+impl ToPoint for Anchor {
+    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
+        self.summary(snapshot)
+    }
+}
+
+pub trait AnchorRangeExt {
+    fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering>;
+    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
+}
+
+impl AnchorRangeExt for Range<Anchor> {
+    fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Result<Ordering> {
+        Ok(match self.start.cmp(&other.start, buffer)? {
+            Ordering::Equal => other.end.cmp(&self.end, buffer)?,
+            ord @ _ => ord,
+        })
+    }
+
+    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize> {
+        self.start.to_offset(&content)..self.end.to_offset(&content)
     }
 }

crates/language/src/tests.rs 🔗

@@ -376,7 +376,7 @@ fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
             .selection_set(selection_set_id)
             .unwrap()
             .selections::<Point>(&buffer)
-            .map(|selection| selection.point_range(&buffer))
+            .map(|selection| selection.start.to_point(&buffer)..selection.end.to_point(&buffer))
             .collect::<Vec<_>>();
 
         assert_eq!(selection_ranges[0], empty(Point::new(1, 4)));

crates/server/src/rpc.rs 🔗

@@ -948,7 +948,7 @@ mod tests {
         fs::{FakeFs, Fs as _},
         language::{
             tree_sitter_rust, Diagnostic, Language, LanguageConfig, LanguageRegistry,
-            LanguageServerConfig, Point,
+            LanguageServerConfig, MultiBuffer, Point,
         },
         lsp,
         project::{ProjectPath, Worktree},
@@ -1035,6 +1035,7 @@ mod tests {
             .update(&mut cx_b, |worktree, cx| worktree.open_buffer("b.txt", cx))
             .await
             .unwrap();
+        let buffer_b = cx_b.add_model(|cx| MultiBuffer::singleton(buffer_b, cx));
         buffer_b.read_with(&cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
         worktree_a.read_with(&cx_a, |tree, cx| assert!(tree.has_open_buffer("b.txt", cx)));
         let buffer_a = worktree_a

crates/text/src/patch.rs 🔗

@@ -201,6 +201,15 @@ where
     }
 }
 
+impl<T: Clone> IntoIterator for Patch<T> {
+    type Item = Edit<T>;
+    type IntoIter = std::vec::IntoIter<Edit<T>>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.into_iter()
+    }
+}
+
 impl<'a, T: Clone> IntoIterator for &'a Patch<T> {
     type Item = Edit<T>;
     type IntoIter = std::iter::Cloned<std::slice::Iter<'a, Edit<T>>>;

crates/text/src/selection.rs 🔗

@@ -1,6 +1,4 @@
-use crate::{
-    rope::TextDimension, AnchorRangeMap, Buffer, BufferSnapshot, Point, ToOffset, ToPoint,
-};
+use crate::{rope::TextDimension, AnchorRangeMap, BufferSnapshot, ToOffset, ToPoint};
 use std::{cmp::Ordering, ops::Range, sync::Arc};
 use sum_tree::Bias;
 
@@ -75,26 +73,6 @@ impl<T: ToOffset + ToPoint + Copy + Ord> Selection<T> {
             self.end = head;
         }
     }
-
-    pub fn point_range(&self, buffer: &Buffer) -> Range<Point> {
-        let start = self.start.to_point(buffer);
-        let end = self.end.to_point(buffer);
-        if self.reversed {
-            end..start
-        } else {
-            start..end
-        }
-    }
-
-    pub fn offset_range(&self, buffer: &Buffer) -> Range<usize> {
-        let start = self.start.to_offset(buffer);
-        let end = self.end.to_offset(buffer);
-        if self.reversed {
-            end..start
-        } else {
-            start..end
-        }
-    }
 }
 
 impl SelectionSet {

crates/text/src/text.rs 🔗

@@ -1850,13 +1850,13 @@ impl BufferSnapshot {
         self.visible_text.clip_point_utf16(point, bias)
     }
 
-    pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
-        if offset <= self.len() {
-            Ok(self.text_summary_for_range(0..offset))
-        } else {
-            Err(anyhow!("offset out of bounds"))
-        }
-    }
+    // pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
+    //     if offset <= self.len() {
+    //         Ok(self.text_summary_for_range(0..offset))
+    //     } else {
+    //         Err(anyhow!("offset out of bounds"))
+    //     }
+    // }
 
     pub fn edits_since<'a, D>(
         &'a self,