multi_buffer.rs

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