multi_buffer.rs

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