multi_buffer.rs

   1mod anchor;
   2mod location;
   3mod selection;
   4
   5use self::location::*;
   6use crate::{
   7    buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
   8    BufferSnapshot, Diagnostic, File, Language,
   9};
  10use anyhow::Result;
  11use clock::ReplicaId;
  12use collections::HashMap;
  13use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
  14use parking_lot::{Mutex, MutexGuard};
  15use std::{cmp, io, ops::Range, sync::Arc, time::SystemTime};
  16use sum_tree::{Bias, Cursor, SumTree};
  17use text::{
  18    rope::TextDimension,
  19    subscription::{Subscription, Topic},
  20    AnchorRangeExt as _, Edit, Point, PointUtf16, Selection, SelectionSetId, TextSummary,
  21};
  22use theme::SyntaxTheme;
  23
  24pub use anchor::{Anchor, AnchorRangeExt, AnchorRangeMap, AnchorRangeSet};
  25pub use selection::SelectionSet;
  26
  27const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
  28
  29#[derive(Default)]
  30pub struct MultiBuffer {
  31    snapshot: Mutex<MultiBufferSnapshot>,
  32    buffers: HashMap<usize, BufferState>,
  33    subscriptions: Topic,
  34}
  35
  36pub trait ToOffset: 'static {
  37    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize;
  38}
  39
  40pub trait ToPoint: 'static {
  41    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point;
  42}
  43
  44#[derive(Debug)]
  45struct BufferState {
  46    buffer: ModelHandle<Buffer>,
  47    last_sync: clock::Global,
  48    excerpts: Vec<ExcerptId>,
  49}
  50
  51#[derive(Clone, Default)]
  52pub struct MultiBufferSnapshot {
  53    excerpts: SumTree<Excerpt>,
  54}
  55
  56pub struct ExcerptProperties<'a, T> {
  57    buffer: &'a ModelHandle<Buffer>,
  58    range: Range<T>,
  59    header_height: u8,
  60}
  61
  62#[derive(Clone)]
  63struct Excerpt {
  64    id: ExcerptId,
  65    buffer: buffer::BufferSnapshot,
  66    range: Range<text::Anchor>,
  67    text_summary: TextSummary,
  68    header_height: u8,
  69}
  70
  71#[derive(Clone, Debug, Default)]
  72struct ExcerptSummary {
  73    excerpt_id: ExcerptId,
  74    text: TextSummary,
  75}
  76
  77pub struct MultiBufferChunks<'a> {
  78    range: Range<usize>,
  79    cursor: Cursor<'a, Excerpt, usize>,
  80    header_height: u8,
  81    excerpt_chunks: Option<buffer::BufferChunks<'a>>,
  82    theme: Option<&'a SyntaxTheme>,
  83}
  84
  85pub struct MultiBufferBytes<'a> {
  86    chunks: MultiBufferChunks<'a>,
  87}
  88
  89impl MultiBuffer {
  90    pub fn new() -> Self {
  91        Self::default()
  92    }
  93
  94    pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
  95        let mut this = Self::new();
  96        this.push(
  97            ExcerptProperties {
  98                buffer: &buffer,
  99                range: text::Anchor::min()..text::Anchor::max(),
 100                header_height: 0,
 101            },
 102            cx,
 103        );
 104        this
 105    }
 106
 107    pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle<Self> {
 108        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 109        cx.add_model(|cx| Self::singleton(buffer, cx))
 110    }
 111
 112    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
 113        self.sync(cx);
 114        self.snapshot.lock().clone()
 115    }
 116
 117    pub fn as_snapshot(&self) -> MutexGuard<MultiBufferSnapshot> {
 118        self.snapshot.lock()
 119    }
 120
 121    pub fn as_singleton(&self) -> Option<&ModelHandle<Buffer>> {
 122        if self.buffers.len() == 1 {
 123            return Some(&self.buffers.values().next().unwrap().buffer);
 124        } else {
 125            None
 126        }
 127    }
 128
 129    pub fn subscribe(&mut self) -> Subscription {
 130        self.subscriptions.subscribe()
 131    }
 132
 133    pub fn edit<I, S, T>(&mut self, ranges_iter: I, new_text: T, cx: &mut ModelContext<Self>)
 134    where
 135        I: IntoIterator<Item = Range<S>>,
 136        S: ToOffset,
 137        T: Into<String>,
 138    {
 139        self.edit_internal(ranges_iter, new_text, false, cx)
 140    }
 141
 142    pub fn edit_with_autoindent<I, S, T>(
 143        &mut self,
 144        ranges_iter: I,
 145        new_text: T,
 146        cx: &mut ModelContext<Self>,
 147    ) where
 148        I: IntoIterator<Item = Range<S>>,
 149        S: ToOffset,
 150        T: Into<String>,
 151    {
 152        self.edit_internal(ranges_iter, new_text, true, cx)
 153    }
 154
 155    pub fn edit_internal<I, S, T>(
 156        &mut self,
 157        ranges_iter: I,
 158        new_text: T,
 159        autoindent: bool,
 160        cx: &mut ModelContext<Self>,
 161    ) where
 162        I: IntoIterator<Item = Range<S>>,
 163        S: ToOffset,
 164        T: Into<String>,
 165    {
 166        todo!()
 167    }
 168
 169    pub fn start_transaction(
 170        &mut self,
 171        selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
 172        cx: &mut ModelContext<Self>,
 173    ) -> Result<()> {
 174        todo!()
 175    }
 176
 177    pub fn end_transaction(
 178        &mut self,
 179        selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
 180        cx: &mut ModelContext<Self>,
 181    ) -> Result<()> {
 182        todo!()
 183    }
 184
 185    pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
 186        todo!()
 187    }
 188
 189    pub fn redo(&mut self, cx: &mut ModelContext<Self>) {
 190        todo!()
 191    }
 192
 193    pub fn selection_set(&self, set_id: SelectionSetId) -> Result<&SelectionSet> {
 194        todo!()
 195    }
 196
 197    pub fn add_selection_set<T: ToOffset>(
 198        &mut self,
 199        selections: &[Selection<T>],
 200        cx: &mut ModelContext<Self>,
 201    ) -> SelectionSetId {
 202        todo!()
 203    }
 204
 205    pub fn remove_selection_set(
 206        &mut self,
 207        set_id: SelectionSetId,
 208        cx: &mut ModelContext<Self>,
 209    ) -> Result<()> {
 210        todo!()
 211    }
 212
 213    pub fn update_selection_set<T: ToOffset>(
 214        &mut self,
 215        set_id: SelectionSetId,
 216        selections: &[Selection<T>],
 217        cx: &mut ModelContext<Self>,
 218    ) -> Result<()> {
 219        todo!()
 220    }
 221
 222    pub fn set_active_selection_set(
 223        &mut self,
 224        set_id: Option<SelectionSetId>,
 225        cx: &mut ModelContext<Self>,
 226    ) -> Result<()> {
 227        todo!()
 228    }
 229
 230    pub fn selection_sets(&self) -> impl Iterator<Item = (&SelectionSetId, &SelectionSet)> {
 231        todo!();
 232        None.into_iter()
 233    }
 234
 235    pub fn push<O>(&mut self, props: ExcerptProperties<O>, cx: &mut ModelContext<Self>) -> ExcerptId
 236    where
 237        O: text::ToOffset,
 238    {
 239        self.sync(cx);
 240
 241        let buffer = props.buffer.read(cx);
 242        let range = buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end);
 243        let mut snapshot = self.snapshot.lock();
 244        let prev_id = snapshot.excerpts.last().map(|e| &e.id);
 245        let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
 246
 247        let edit_start = snapshot.excerpts.summary().text.bytes;
 248        let excerpt = Excerpt::new(id.clone(), buffer.snapshot(), range, props.header_height);
 249        let edit = Edit {
 250            old: edit_start..edit_start,
 251            new: edit_start..edit_start + excerpt.text_summary.bytes,
 252        };
 253        snapshot.excerpts.push(excerpt, &());
 254        self.buffers
 255            .entry(props.buffer.id())
 256            .or_insert_with(|| BufferState {
 257                buffer: props.buffer.clone(),
 258                last_sync: buffer.version(),
 259                excerpts: Default::default(),
 260            })
 261            .excerpts
 262            .push(id.clone());
 263
 264        self.subscriptions.publish_mut([edit]);
 265
 266        id
 267    }
 268
 269    pub fn save(
 270        &mut self,
 271        cx: &mut ModelContext<Self>,
 272    ) -> Result<Task<Result<(clock::Global, SystemTime)>>> {
 273        todo!()
 274    }
 275
 276    pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
 277        self.as_singleton()
 278            .and_then(|buffer| buffer.read(cx).file())
 279    }
 280
 281    pub fn is_dirty(&self) -> bool {
 282        todo!()
 283    }
 284
 285    pub fn has_conflict(&self) -> bool {
 286        todo!()
 287    }
 288
 289    pub fn is_parsing(&self, _: &AppContext) -> bool {
 290        todo!()
 291    }
 292
 293    fn sync(&self, cx: &AppContext) {
 294        let mut snapshot = self.snapshot.lock();
 295        let mut excerpts_to_edit = Vec::new();
 296        for buffer_state in self.buffers.values() {
 297            if buffer_state
 298                .buffer
 299                .read(cx)
 300                .version()
 301                .gt(&buffer_state.last_sync)
 302            {
 303                excerpts_to_edit.extend(
 304                    buffer_state
 305                        .excerpts
 306                        .iter()
 307                        .map(|excerpt_id| (excerpt_id, buffer_state)),
 308                );
 309            }
 310        }
 311        excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _)| *excerpt_id);
 312
 313        let mut edits = Vec::new();
 314        let mut new_excerpts = SumTree::new();
 315        let mut cursor = snapshot.excerpts.cursor::<(ExcerptId, usize)>();
 316
 317        for (id, buffer_state) in excerpts_to_edit {
 318            new_excerpts.push_tree(cursor.slice(id, Bias::Left, &()), &());
 319            let old_excerpt = cursor.item().unwrap();
 320            let buffer = buffer_state.buffer.read(cx);
 321
 322            edits.extend(
 323                buffer
 324                    .edits_since_in_range::<usize>(
 325                        old_excerpt.buffer.version(),
 326                        old_excerpt.range.clone(),
 327                    )
 328                    .map(|mut edit| {
 329                        let excerpt_old_start =
 330                            cursor.start().1 + old_excerpt.header_height as usize;
 331                        let excerpt_new_start =
 332                            new_excerpts.summary().text.bytes + old_excerpt.header_height as usize;
 333                        edit.old.start += excerpt_old_start;
 334                        edit.old.end += excerpt_old_start;
 335                        edit.new.start += excerpt_new_start;
 336                        edit.new.end += excerpt_new_start;
 337                        edit
 338                    }),
 339            );
 340
 341            new_excerpts.push(
 342                Excerpt::new(
 343                    id.clone(),
 344                    buffer.snapshot(),
 345                    old_excerpt.range.clone(),
 346                    old_excerpt.header_height,
 347                ),
 348                &(),
 349            );
 350
 351            cursor.next(&());
 352        }
 353        new_excerpts.push_tree(cursor.suffix(&()), &());
 354
 355        drop(cursor);
 356        snapshot.excerpts = new_excerpts;
 357
 358        self.subscriptions.publish(edits);
 359    }
 360}
 361
 362// Methods delegating to the snapshot
 363impl MultiBuffer {
 364    pub fn replica_id(&self) -> ReplicaId {
 365        self.snapshot.lock().replica_id()
 366    }
 367
 368    pub fn text(&self) -> String {
 369        self.snapshot.lock().text()
 370    }
 371
 372    pub fn text_for_range<'a, T: ToOffset>(
 373        &'a self,
 374        range: Range<T>,
 375    ) -> impl Iterator<Item = &'a str> {
 376        todo!();
 377        [].into_iter()
 378    }
 379
 380    pub fn max_point(&self) -> Point {
 381        self.snapshot.lock().max_point()
 382    }
 383
 384    pub fn len(&self) -> usize {
 385        self.snapshot.lock().len()
 386    }
 387
 388    pub fn line_len(&self, row: u32) -> u32 {
 389        self.snapshot.lock().line_len(row)
 390    }
 391
 392    pub fn is_line_blank(&self, row: u32) -> bool {
 393        self.snapshot.lock().is_line_blank(row)
 394    }
 395
 396    pub fn indent_column_for_line(&self, row: u32) -> u32 {
 397        self.snapshot.lock().indent_column_for_line(row)
 398    }
 399
 400    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
 401        self.snapshot.lock().anchor_before(position)
 402    }
 403
 404    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
 405        self.snapshot.lock().anchor_after(position)
 406    }
 407
 408    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
 409        self.snapshot.lock().anchor_at(position, bias)
 410    }
 411
 412    pub fn anchor_range_set<E>(
 413        &self,
 414        start_bias: Bias,
 415        end_bias: Bias,
 416        entries: E,
 417    ) -> AnchorRangeSet
 418    where
 419        E: IntoIterator<Item = Range<usize>>,
 420    {
 421        todo!()
 422    }
 423
 424    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 425        self.snapshot.lock().clip_offset(offset, bias)
 426    }
 427
 428    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 429        self.snapshot.lock().clip_point(point, bias)
 430    }
 431
 432    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
 433        todo!()
 434    }
 435
 436    pub fn parse_count(&self) -> usize {
 437        self.snapshot.lock().parse_count()
 438    }
 439
 440    pub fn diagnostics_update_count(&self) -> usize {
 441        self.snapshot.lock().diagnostics_update_count()
 442    }
 443
 444    pub fn diagnostics_in_range<'a, T, O>(
 445        &'a self,
 446        search_range: Range<T>,
 447    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
 448    where
 449        T: 'a + ToOffset,
 450        O: 'a,
 451    {
 452        todo!();
 453        None.into_iter()
 454    }
 455}
 456
 457#[cfg(any(test, feature = "test-support"))]
 458impl MultiBuffer {
 459    pub fn randomly_edit<R: rand::Rng>(&mut self, _: &mut R, _: usize, _: &mut ModelContext<Self>) {
 460        todo!()
 461    }
 462
 463    pub fn randomly_mutate<R: rand::Rng>(&mut self, rng: &mut R, cx: &mut ModelContext<Self>) {
 464        todo!()
 465    }
 466}
 467
 468impl Entity for MultiBuffer {
 469    type Event = super::Event;
 470}
 471
 472impl MultiBufferSnapshot {
 473    pub fn replica_id(&self) -> ReplicaId {
 474        todo!()
 475    }
 476
 477    pub fn text(&self) -> String {
 478        self.chunks(0..self.len(), None)
 479            .map(|chunk| chunk.text)
 480            .collect()
 481    }
 482
 483    pub fn reversed_chars_at<'a, T: ToOffset>(
 484        &'a self,
 485        position: T,
 486    ) -> impl Iterator<Item = char> + 'a {
 487        todo!();
 488        None.into_iter()
 489    }
 490
 491    pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
 492        let offset = position.to_offset(self);
 493        self.text_for_range(offset..self.len())
 494            .flat_map(|chunk| chunk.chars())
 495    }
 496
 497    pub fn text_for_range<'a, T: ToOffset>(
 498        &'a self,
 499        range: Range<T>,
 500    ) -> impl Iterator<Item = &'a str> {
 501        self.chunks(range, None).map(|chunk| chunk.text)
 502    }
 503
 504    pub fn is_line_blank(&self, row: u32) -> bool {
 505        self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
 506            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
 507    }
 508
 509    pub fn contains_str_at<T>(&self, _: T, _: &str) -> bool
 510    where
 511        T: ToOffset,
 512    {
 513        todo!()
 514    }
 515
 516    pub fn len(&self) -> usize {
 517        self.excerpts.summary().text.bytes
 518    }
 519
 520    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 521        let mut cursor = self.excerpts.cursor::<usize>();
 522        cursor.seek(&offset, Bias::Right, &());
 523        if let Some(excerpt) = cursor.item() {
 524            let start_after_header = *cursor.start() + excerpt.header_height as usize;
 525            if offset < start_after_header {
 526                *cursor.start()
 527            } else {
 528                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 529                let buffer_offset = excerpt
 530                    .buffer
 531                    .clip_offset(excerpt_start + (offset - start_after_header), bias);
 532                let offset_in_excerpt = if buffer_offset > excerpt_start {
 533                    buffer_offset - excerpt_start
 534                } else {
 535                    0
 536                };
 537                start_after_header + offset_in_excerpt
 538            }
 539        } else {
 540            self.excerpts.summary().text.bytes
 541        }
 542    }
 543
 544    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 545        let mut cursor = self.excerpts.cursor::<Point>();
 546        cursor.seek(&point, Bias::Right, &());
 547        if let Some(excerpt) = cursor.item() {
 548            let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
 549            if point < start_after_header {
 550                *cursor.start()
 551            } else {
 552                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 553                let buffer_point = excerpt
 554                    .buffer
 555                    .clip_point(excerpt_start + (point - start_after_header), bias);
 556                let point_in_excerpt = if buffer_point > excerpt_start {
 557                    buffer_point - excerpt_start
 558                } else {
 559                    Point::zero()
 560                };
 561                start_after_header + point_in_excerpt
 562            }
 563        } else {
 564            self.excerpts.summary().text.lines
 565        }
 566    }
 567
 568    pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
 569        let mut cursor = self.excerpts.cursor::<PointUtf16>();
 570        cursor.seek(&point, Bias::Right, &());
 571        if let Some(excerpt) = cursor.item() {
 572            let start_after_header =
 573                *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
 574            if point < start_after_header {
 575                *cursor.start()
 576            } else {
 577                let excerpt_start = excerpt
 578                    .buffer
 579                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 580                let buffer_point = excerpt
 581                    .buffer
 582                    .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
 583                let point_in_excerpt = if buffer_point > excerpt_start {
 584                    buffer_point - excerpt_start
 585                } else {
 586                    PointUtf16::new(0, 0)
 587                };
 588                start_after_header + point_in_excerpt
 589            }
 590        } else {
 591            self.excerpts.summary().text.lines_utf16
 592        }
 593    }
 594
 595    pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
 596        todo!()
 597    }
 598
 599    pub fn chunks<'a, T: ToOffset>(
 600        &'a self,
 601        range: Range<T>,
 602        theme: Option<&'a SyntaxTheme>,
 603    ) -> MultiBufferChunks<'a> {
 604        let range = range.start.to_offset(self)..range.end.to_offset(self);
 605        let mut cursor = self.excerpts.cursor::<usize>();
 606        cursor.seek(&range.start, Bias::Right, &());
 607
 608        let mut header_height: u8 = 0;
 609        let excerpt_chunks = cursor.item().map(|excerpt| {
 610            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
 611            header_height = excerpt.header_height;
 612
 613            let buffer_start;
 614            let start_overshoot = range.start - cursor.start();
 615            if start_overshoot < excerpt.header_height as usize {
 616                header_height -= start_overshoot as u8;
 617                buffer_start = buffer_range.start;
 618            } else {
 619                buffer_start =
 620                    buffer_range.start + start_overshoot - excerpt.header_height as usize;
 621                header_height = 0;
 622            }
 623
 624            let buffer_end;
 625            let end_overshoot = range.end - cursor.start();
 626            if end_overshoot < excerpt.header_height as usize {
 627                header_height -= excerpt.header_height - end_overshoot as u8;
 628                buffer_end = buffer_start;
 629            } else {
 630                buffer_end = cmp::min(
 631                    buffer_range.end,
 632                    buffer_range.start + end_overshoot - excerpt.header_height as usize,
 633                );
 634            }
 635
 636            excerpt.buffer.chunks(buffer_start..buffer_end, theme)
 637        });
 638
 639        MultiBufferChunks {
 640            range,
 641            cursor,
 642            header_height,
 643            excerpt_chunks,
 644            theme,
 645        }
 646    }
 647
 648    pub fn offset_to_point(&self, offset: usize) -> Point {
 649        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
 650        cursor.seek(&offset, Bias::Right, &());
 651        if let Some(excerpt) = cursor.item() {
 652            let (start_offset, start_point) = cursor.start();
 653            let overshoot = offset - start_offset;
 654            let header_height = excerpt.header_height as usize;
 655            if overshoot < header_height {
 656                *start_point
 657            } else {
 658                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 659                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 660                let buffer_point = excerpt
 661                    .buffer
 662                    .offset_to_point(excerpt_start_offset + (overshoot - header_height));
 663                *start_point
 664                    + Point::new(header_height as u32, 0)
 665                    + (buffer_point - excerpt_start_point)
 666            }
 667        } else {
 668            self.excerpts.summary().text.lines
 669        }
 670    }
 671
 672    pub fn point_to_offset(&self, point: Point) -> usize {
 673        let mut cursor = self.excerpts.cursor::<(Point, usize)>();
 674        cursor.seek(&point, Bias::Right, &());
 675        if let Some(excerpt) = cursor.item() {
 676            let (start_point, start_offset) = cursor.start();
 677            let overshoot = point - start_point;
 678            let header_height = Point::new(excerpt.header_height as u32, 0);
 679            if overshoot < header_height {
 680                *start_offset
 681            } else {
 682                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 683                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 684                let buffer_offset = excerpt
 685                    .buffer
 686                    .point_to_offset(excerpt_start_point + (overshoot - header_height));
 687                *start_offset + excerpt.header_height as usize + buffer_offset
 688                    - excerpt_start_offset
 689            }
 690        } else {
 691            self.excerpts.summary().text.bytes
 692        }
 693    }
 694
 695    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
 696        let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
 697        cursor.seek(&point, Bias::Right, &());
 698        if let Some(excerpt) = cursor.item() {
 699            let (start_point, start_offset) = cursor.start();
 700            let overshoot = point - start_point;
 701            let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
 702            if overshoot < header_height {
 703                *start_offset
 704            } else {
 705                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 706                let excerpt_start_point = excerpt
 707                    .buffer
 708                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 709                let buffer_offset = excerpt
 710                    .buffer
 711                    .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
 712                *start_offset
 713                    + excerpt.header_height as usize
 714                    + (buffer_offset - excerpt_start_offset)
 715            }
 716        } else {
 717            self.excerpts.summary().text.bytes
 718        }
 719    }
 720
 721    pub fn indent_column_for_line(&self, row: u32) -> u32 {
 722        todo!()
 723    }
 724
 725    pub fn line_len(&self, row: u32) -> u32 {
 726        let mut cursor = self.excerpts.cursor::<Point>();
 727        cursor.seek(&Point::new(row, 0), Bias::Right, &());
 728        if let Some(excerpt) = cursor.item() {
 729            let overshoot = row - cursor.start().row;
 730            let header_height = excerpt.header_height as u32;
 731            if overshoot < header_height {
 732                0
 733            } else {
 734                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 735                let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
 736                let buffer_row = excerpt_start.row + overshoot - header_height;
 737                let mut len = excerpt.buffer.line_len(buffer_row);
 738                if buffer_row == excerpt_end.row {
 739                    len = excerpt_end.column;
 740                }
 741                if buffer_row == excerpt_start.row {
 742                    len -= excerpt_start.column
 743                }
 744                len
 745            }
 746        } else {
 747            0
 748        }
 749    }
 750
 751    pub fn max_point(&self) -> Point {
 752        self.text_summary().lines
 753    }
 754
 755    pub fn text_summary(&self) -> TextSummary {
 756        self.excerpts.summary().text
 757    }
 758
 759    pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
 760    where
 761        D: TextDimension,
 762        O: ToOffset,
 763    {
 764        let mut summary = D::default();
 765        let mut range = range.start.to_offset(self)..range.end.to_offset(self);
 766        let mut cursor = self.excerpts.cursor::<usize>();
 767        cursor.seek(&range.start, Bias::Right, &());
 768        if let Some(excerpt) = cursor.item() {
 769            let start_after_header = cursor.start() + excerpt.header_height as usize;
 770            if range.start < start_after_header {
 771                let header_len = cmp::min(range.end, start_after_header) - range.start;
 772                summary.add_assign(&D::from_text_summary(&TextSummary {
 773                    bytes: header_len,
 774                    lines: Point::new(header_len as u32, 0),
 775                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 776                    first_line_chars: 0,
 777                    last_line_chars: 0,
 778                    longest_row: 0,
 779                    longest_row_chars: 0,
 780                }));
 781                range.start = start_after_header;
 782                range.end = cmp::max(range.start, range.end);
 783            }
 784
 785            let end_before_newline = cursor.end(&()) - 1;
 786            let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 787            let start_in_excerpt = excerpt_start + (range.start - start_after_header);
 788            let end_in_excerpt =
 789                excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
 790            summary.add_assign(
 791                &excerpt
 792                    .buffer
 793                    .text_summary_for_range(start_in_excerpt..end_in_excerpt),
 794            );
 795
 796            if range.end > end_before_newline {
 797                summary.add_assign(&D::from_text_summary(&TextSummary {
 798                    bytes: 1,
 799                    lines: Point::new(1 as u32, 0),
 800                    lines_utf16: PointUtf16::new(1 as u32, 0),
 801                    first_line_chars: 0,
 802                    last_line_chars: 0,
 803                    longest_row: 0,
 804                    longest_row_chars: 0,
 805                }));
 806            }
 807
 808            cursor.next(&());
 809        }
 810
 811        if range.end > *cursor.start() {
 812            summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
 813                &range.end,
 814                Bias::Right,
 815                &(),
 816            )));
 817            if let Some(excerpt) = cursor.item() {
 818                let start_after_header = cursor.start() + excerpt.header_height as usize;
 819                let header_len =
 820                    cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
 821                summary.add_assign(&D::from_text_summary(&TextSummary {
 822                    bytes: header_len,
 823                    lines: Point::new(header_len as u32, 0),
 824                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 825                    first_line_chars: 0,
 826                    last_line_chars: 0,
 827                    longest_row: 0,
 828                    longest_row_chars: 0,
 829                }));
 830                range.end = cmp::max(start_after_header, range.end);
 831
 832                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 833                let end_in_excerpt = excerpt_start + (range.end - start_after_header);
 834                summary.add_assign(
 835                    &excerpt
 836                        .buffer
 837                        .text_summary_for_range(excerpt_start..end_in_excerpt),
 838                );
 839                cursor.next(&());
 840            }
 841        }
 842
 843        summary
 844    }
 845
 846    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
 847        self.anchor_at(position, Bias::Left)
 848    }
 849
 850    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
 851        self.anchor_at(position, Bias::Right)
 852    }
 853
 854    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
 855        todo!()
 856    }
 857
 858    pub fn parse_count(&self) -> usize {
 859        todo!()
 860    }
 861
 862    pub fn enclosing_bracket_ranges<T: ToOffset>(
 863        &self,
 864        range: Range<T>,
 865    ) -> Option<(Range<usize>, Range<usize>)> {
 866        todo!()
 867    }
 868
 869    pub fn diagnostics_update_count(&self) -> usize {
 870        todo!()
 871    }
 872
 873    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
 874        todo!()
 875    }
 876
 877    pub fn diagnostic_group<'a, O>(
 878        &'a self,
 879        group_id: usize,
 880    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
 881    where
 882        O: 'a,
 883    {
 884        todo!();
 885        None.into_iter()
 886    }
 887
 888    pub fn diagnostics_in_range<'a, T, O>(
 889        &'a self,
 890        search_range: Range<T>,
 891    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
 892    where
 893        T: 'a + ToOffset,
 894        O: 'a,
 895    {
 896        todo!();
 897        None.into_iter()
 898    }
 899
 900    pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
 901        todo!()
 902    }
 903
 904    fn buffer_snapshot_for_excerpt<'a>(
 905        &'a self,
 906        excerpt_id: &ExcerptId,
 907    ) -> Option<&'a BufferSnapshot> {
 908        let mut cursor = self.excerpts.cursor::<ExcerptId>();
 909        cursor.seek(excerpt_id, Bias::Left, &());
 910        if let Some(excerpt) = cursor.item() {
 911            if cursor.start() == excerpt_id {
 912                return Some(&excerpt.buffer);
 913            }
 914        }
 915        None
 916    }
 917}
 918
 919impl Excerpt {
 920    fn new(
 921        id: ExcerptId,
 922        buffer: buffer::BufferSnapshot,
 923        range: Range<text::Anchor>,
 924        header_height: u8,
 925    ) -> Self {
 926        let mut text_summary =
 927            buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
 928        if header_height > 0 {
 929            text_summary.first_line_chars = 0;
 930            text_summary.lines.row += header_height as u32;
 931            text_summary.lines_utf16.row += header_height as u32;
 932            text_summary.bytes += header_height as usize;
 933            text_summary.longest_row += header_height as u32;
 934        }
 935        text_summary.last_line_chars = 0;
 936        text_summary.lines.row += 1;
 937        text_summary.lines.column = 0;
 938        text_summary.lines_utf16.row += 1;
 939        text_summary.lines_utf16.column = 0;
 940        text_summary.bytes += 1;
 941
 942        Excerpt {
 943            id,
 944            buffer,
 945            range,
 946            text_summary,
 947            header_height,
 948        }
 949    }
 950
 951    fn header_summary(&self) -> TextSummary {
 952        TextSummary {
 953            bytes: self.header_height as usize,
 954            lines: Point::new(self.header_height as u32, 0),
 955            lines_utf16: PointUtf16::new(self.header_height as u32, 0),
 956            first_line_chars: 0,
 957            last_line_chars: 0,
 958            longest_row: 0,
 959            longest_row_chars: 0,
 960        }
 961    }
 962}
 963
 964impl sum_tree::Item for Excerpt {
 965    type Summary = ExcerptSummary;
 966
 967    fn summary(&self) -> Self::Summary {
 968        ExcerptSummary {
 969            excerpt_id: self.id.clone(),
 970            text: self.text_summary.clone(),
 971        }
 972    }
 973}
 974
 975impl sum_tree::Summary for ExcerptSummary {
 976    type Context = ();
 977
 978    fn add_summary(&mut self, summary: &Self, _: &()) {
 979        debug_assert!(summary.excerpt_id > self.excerpt_id);
 980        self.excerpt_id = summary.excerpt_id.clone();
 981        self.text.add_summary(&summary.text, &());
 982    }
 983}
 984
 985impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
 986    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 987        *self += &summary.text;
 988    }
 989}
 990
 991impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
 992    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 993        *self += summary.text.bytes;
 994    }
 995}
 996
 997impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
 998    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
 999        Ord::cmp(self, &cursor_location.text.bytes)
1000    }
1001}
1002
1003impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Location {
1004    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1005        Ord::cmp(self, &cursor_location.excerpt_id)
1006    }
1007}
1008
1009impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1010    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1011        *self += summary.text.lines;
1012    }
1013}
1014
1015impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1016    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1017        *self += summary.text.lines_utf16
1018    }
1019}
1020
1021impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Location {
1022    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1023        debug_assert!(summary.excerpt_id > *self);
1024        *self = summary.excerpt_id.clone();
1025    }
1026}
1027
1028impl<'a> MultiBufferChunks<'a> {
1029    pub fn offset(&self) -> usize {
1030        todo!()
1031    }
1032
1033    pub fn seek(&mut self, offset: usize) {
1034        todo!()
1035    }
1036}
1037
1038impl<'a> Iterator for MultiBufferChunks<'a> {
1039    type Item = Chunk<'a>;
1040
1041    fn next(&mut self) -> Option<Self::Item> {
1042        loop {
1043            if self.header_height > 0 {
1044                let chunk = Chunk {
1045                    text: unsafe {
1046                        std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1047                    },
1048                    ..Default::default()
1049                };
1050                self.header_height = 0;
1051                return Some(chunk);
1052            }
1053
1054            if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1055                if let Some(chunk) = excerpt_chunks.next() {
1056                    return Some(chunk);
1057                }
1058                self.excerpt_chunks.take();
1059                if self.cursor.end(&()) <= self.range.end {
1060                    return Some(Chunk {
1061                        text: "\n",
1062                        ..Default::default()
1063                    });
1064                }
1065            }
1066
1067            self.cursor.next(&());
1068            if *self.cursor.start() >= self.range.end {
1069                return None;
1070            }
1071
1072            let excerpt = self.cursor.item()?;
1073            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1074
1075            let buffer_end = cmp::min(
1076                buffer_range.end,
1077                buffer_range.start + self.range.end
1078                    - excerpt.header_height as usize
1079                    - self.cursor.start(),
1080            );
1081
1082            self.header_height = excerpt.header_height;
1083            self.excerpt_chunks = Some(
1084                excerpt
1085                    .buffer
1086                    .chunks(buffer_range.start..buffer_end, self.theme),
1087            );
1088        }
1089    }
1090}
1091
1092impl<'a> Iterator for MultiBufferBytes<'a> {
1093    type Item = &'a [u8];
1094
1095    fn next(&mut self) -> Option<Self::Item> {
1096        todo!()
1097    }
1098}
1099
1100impl<'a> io::Read for MultiBufferBytes<'a> {
1101    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1102        todo!()
1103    }
1104}
1105
1106impl ToOffset for Point {
1107    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1108        snapshot.point_to_offset(*self)
1109    }
1110}
1111
1112impl ToOffset for PointUtf16 {
1113    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1114        snapshot.point_utf16_to_offset(*self)
1115    }
1116}
1117
1118impl ToOffset for usize {
1119    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1120        assert!(*self <= snapshot.len(), "offset is out of range");
1121        *self
1122    }
1123}
1124
1125impl ToPoint for usize {
1126    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1127        snapshot.offset_to_point(*self)
1128    }
1129}
1130
1131impl ToPoint for Point {
1132    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1133        *self
1134    }
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139    use super::*;
1140    use crate::buffer::Buffer;
1141    use gpui::MutableAppContext;
1142    use rand::prelude::*;
1143    use std::env;
1144    use text::{Point, RandomCharIter};
1145    use util::test::sample_text;
1146
1147    #[gpui::test]
1148    fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1149        let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1150        let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1151
1152        let list = cx.add_model(|_| MultiBuffer::new());
1153
1154        let subscription = list.update(cx, |list, cx| {
1155            let subscription = list.subscribe();
1156            list.push(
1157                ExcerptProperties {
1158                    buffer: &buffer_1,
1159                    range: Point::new(1, 2)..Point::new(2, 5),
1160                    header_height: 2,
1161                },
1162                cx,
1163            );
1164            assert_eq!(
1165                subscription.consume().into_inner(),
1166                [Edit {
1167                    old: 0..0,
1168                    new: 0..13
1169                }]
1170            );
1171
1172            list.push(
1173                ExcerptProperties {
1174                    buffer: &buffer_1,
1175                    range: Point::new(3, 3)..Point::new(4, 4),
1176                    header_height: 1,
1177                },
1178                cx,
1179            );
1180            list.push(
1181                ExcerptProperties {
1182                    buffer: &buffer_2,
1183                    range: Point::new(3, 1)..Point::new(3, 3),
1184                    header_height: 3,
1185                },
1186                cx,
1187            );
1188            assert_eq!(
1189                subscription.consume().into_inner(),
1190                [Edit {
1191                    old: 13..13,
1192                    new: 13..29
1193                }]
1194            );
1195
1196            subscription
1197        });
1198
1199        assert_eq!(
1200            list.read(cx).snapshot(cx).text(),
1201            concat!(
1202                "\n",      // Preserve newlines
1203                "\n",      //
1204                "bbbb\n",  //
1205                "ccccc\n", //
1206                "\n",      //
1207                "ddd\n",   //
1208                "eeee\n",  //
1209                "\n",      //
1210                "\n",      //
1211                "\n",      //
1212                "jj\n"     //
1213            )
1214        );
1215
1216        buffer_1.update(cx, |buffer, cx| {
1217            buffer.edit(
1218                [
1219                    Point::new(0, 0)..Point::new(0, 0),
1220                    Point::new(2, 1)..Point::new(2, 3),
1221                ],
1222                "\n",
1223                cx,
1224            );
1225        });
1226
1227        assert_eq!(
1228            list.read(cx).snapshot(cx).text(),
1229            concat!(
1230                "\n",     // Preserve newlines
1231                "\n",     //
1232                "bbbb\n", //
1233                "c\n",    //
1234                "cc\n",   //
1235                "\n",     //
1236                "ddd\n",  //
1237                "eeee\n", //
1238                "\n",     //
1239                "\n",     //
1240                "\n",     //
1241                "jj\n"    //
1242            )
1243        );
1244
1245        assert_eq!(
1246            subscription.consume().into_inner(),
1247            [Edit {
1248                old: 8..10,
1249                new: 8..9
1250            }]
1251        );
1252    }
1253
1254    #[gpui::test(iterations = 100)]
1255    fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1256        let operations = env::var("OPERATIONS")
1257            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1258            .unwrap_or(10);
1259
1260        let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1261        let list = cx.add_model(|_| MultiBuffer::new());
1262        let mut excerpt_ids = Vec::new();
1263        let mut expected_excerpts = Vec::new();
1264        let mut old_versions = Vec::new();
1265
1266        for _ in 0..operations {
1267            match rng.gen_range(0..100) {
1268                0..=19 if !buffers.is_empty() => {
1269                    let buffer = buffers.choose(&mut rng).unwrap();
1270                    buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1271                }
1272                _ => {
1273                    let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1274                        let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1275                        buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1276                        buffers.last().unwrap()
1277                    } else {
1278                        buffers.choose(&mut rng).unwrap()
1279                    };
1280
1281                    let buffer = buffer_handle.read(cx);
1282                    let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1283                    let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1284                    let header_height = rng.gen_range(0..=5);
1285                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1286                    log::info!(
1287                        "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1288                        header_height,
1289                        buffer_handle.id(),
1290                        buffer.text(),
1291                        start_ix..end_ix,
1292                        &buffer.text()[start_ix..end_ix]
1293                    );
1294
1295                    let excerpt_id = list.update(cx, |list, cx| {
1296                        list.push(
1297                            ExcerptProperties {
1298                                buffer: &buffer_handle,
1299                                range: start_ix..end_ix,
1300                                header_height,
1301                            },
1302                            cx,
1303                        )
1304                    });
1305                    excerpt_ids.push(excerpt_id);
1306                    expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1307                }
1308            }
1309
1310            if rng.gen_bool(0.3) {
1311                list.update(cx, |list, cx| {
1312                    old_versions.push((list.snapshot(cx), list.subscribe()));
1313                })
1314            }
1315
1316            let snapshot = list.read(cx).snapshot(cx);
1317
1318            let mut excerpt_starts = Vec::new();
1319            let mut expected_text = String::new();
1320            for (buffer, range, header_height) in &expected_excerpts {
1321                let buffer = buffer.read(cx);
1322                let buffer_range = range.to_offset(buffer);
1323
1324                for _ in 0..*header_height {
1325                    expected_text.push('\n');
1326                }
1327
1328                excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1329                expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1330                expected_text.push('\n');
1331            }
1332
1333            assert_eq!(snapshot.text(), expected_text);
1334
1335            let mut excerpt_starts = excerpt_starts.into_iter();
1336            for (buffer, range, _) in &expected_excerpts {
1337                let buffer_id = buffer.id();
1338                let buffer = buffer.read(cx);
1339                let buffer_range = range.to_offset(buffer);
1340                let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1341                let buffer_start_point_utf16 =
1342                    buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1343
1344                let excerpt_start = excerpt_starts.next().unwrap();
1345                let mut offset = excerpt_start.bytes;
1346                let mut buffer_offset = buffer_range.start;
1347                let mut point = excerpt_start.lines;
1348                let mut buffer_point = buffer_start_point;
1349                let mut point_utf16 = excerpt_start.lines_utf16;
1350                let mut buffer_point_utf16 = buffer_start_point_utf16;
1351                for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1352                    let left_offset = snapshot.clip_offset(offset, Bias::Left);
1353                    let right_offset = snapshot.clip_offset(offset, Bias::Right);
1354                    let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1355                    let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1356                    assert_eq!(
1357                        left_offset,
1358                        excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1359                        "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1360                        offset,
1361                        buffer_id,
1362                        buffer_offset,
1363                    );
1364                    assert_eq!(
1365                        right_offset,
1366                        excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1367                        "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1368                        offset,
1369                        buffer_id,
1370                        buffer_offset,
1371                    );
1372
1373                    let left_point = snapshot.clip_point(point, Bias::Left);
1374                    let right_point = snapshot.clip_point(point, Bias::Right);
1375                    let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1376                    let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1377                    assert_eq!(
1378                        left_point,
1379                        excerpt_start.lines + (buffer_left_point - buffer_start_point),
1380                        "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1381                        point,
1382                        buffer_id,
1383                        buffer_point,
1384                    );
1385                    assert_eq!(
1386                        right_point,
1387                        excerpt_start.lines + (buffer_right_point - buffer_start_point),
1388                        "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1389                        point,
1390                        buffer_id,
1391                        buffer_point,
1392                    );
1393
1394                    let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1395                    let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1396                    let buffer_left_point_utf16 =
1397                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1398                    let buffer_right_point_utf16 =
1399                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1400                    assert_eq!(
1401                        left_point_utf16,
1402                        excerpt_start.lines_utf16
1403                            + (buffer_left_point_utf16 - buffer_start_point_utf16),
1404                        "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1405                        point_utf16,
1406                        buffer_id,
1407                        buffer_point_utf16,
1408                    );
1409                    assert_eq!(
1410                        right_point_utf16,
1411                        excerpt_start.lines_utf16
1412                            + (buffer_right_point_utf16 - buffer_start_point_utf16),
1413                        "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1414                        point_utf16,
1415                        buffer_id,
1416                        buffer_point_utf16,
1417                    );
1418
1419                    assert_eq!(
1420                        snapshot.point_to_offset(left_point),
1421                        left_offset,
1422                        "point_to_offset({:?})",
1423                        left_point,
1424                    );
1425                    assert_eq!(
1426                        snapshot.offset_to_point(left_offset),
1427                        left_point,
1428                        "offset_to_point({:?})",
1429                        left_offset,
1430                    );
1431
1432                    offset += 1;
1433                    buffer_offset += 1;
1434                    if *byte == b'\n' {
1435                        point += Point::new(1, 0);
1436                        point_utf16 += PointUtf16::new(1, 0);
1437                        buffer_point += Point::new(1, 0);
1438                        buffer_point_utf16 += PointUtf16::new(1, 0);
1439                    } else {
1440                        point += Point::new(0, 1);
1441                        point_utf16 += PointUtf16::new(0, 1);
1442                        buffer_point += Point::new(0, 1);
1443                        buffer_point_utf16 += PointUtf16::new(0, 1);
1444                    }
1445                }
1446            }
1447
1448            for (row, line) in expected_text.split('\n').enumerate() {
1449                assert_eq!(
1450                    snapshot.line_len(row as u32),
1451                    line.len() as u32,
1452                    "line_len({}).",
1453                    row
1454                );
1455            }
1456
1457            for _ in 0..10 {
1458                let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1459                let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1460
1461                assert_eq!(
1462                    snapshot
1463                        .text_for_range(start_ix..end_ix)
1464                        .collect::<String>(),
1465                    &expected_text[start_ix..end_ix],
1466                    "incorrect text for range {:?}",
1467                    start_ix..end_ix
1468                );
1469
1470                let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1471                assert_eq!(
1472                    snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1473                    expected_summary,
1474                    "incorrect summary for range {:?}",
1475                    start_ix..end_ix
1476                );
1477            }
1478        }
1479
1480        let snapshot = list.read(cx).snapshot(cx);
1481        for (old_snapshot, subscription) in old_versions {
1482            let edits = subscription.consume().into_inner();
1483
1484            log::info!(
1485                "applying edits since old text: {:?}: {:?}",
1486                old_snapshot.text(),
1487                edits,
1488            );
1489
1490            let mut text = old_snapshot.text();
1491            for edit in edits {
1492                let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1493                text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1494            }
1495            assert_eq!(text.to_string(), snapshot.text());
1496        }
1497    }
1498}