multi_buffer.rs

   1mod anchor;
   2mod selection;
   3
   4use crate::{
   5    buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
   6    BufferSnapshot, Diagnostic, DiagnosticEntry, 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 contains_str_at<T>(&self, _: T, _: &str) -> bool
 387    where
 388        T: ToOffset,
 389    {
 390        todo!()
 391    }
 392
 393    pub fn max_point(&self) -> Point {
 394        self.snapshot.lock().max_point()
 395    }
 396
 397    pub fn len(&self) -> usize {
 398        self.snapshot.lock().len()
 399    }
 400
 401    pub fn line_len(&self, row: u32) -> u32 {
 402        self.snapshot.lock().line_len(row)
 403    }
 404
 405    pub fn is_line_blank(&self, row: u32) -> bool {
 406        self.snapshot.lock().is_line_blank(row)
 407    }
 408
 409    pub fn indent_column_for_line(&self, row: u32) -> u32 {
 410        self.snapshot.lock().indent_column_for_line(row)
 411    }
 412
 413    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
 414        self.snapshot.lock().anchor_before(position)
 415    }
 416
 417    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
 418        self.snapshot.lock().anchor_after(position)
 419    }
 420
 421    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
 422        self.snapshot.lock().anchor_at(position, bias)
 423    }
 424
 425    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 426        self.snapshot.lock().clip_offset(offset, bias)
 427    }
 428
 429    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 430        self.snapshot.lock().clip_point(point, bias)
 431    }
 432
 433    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
 434        todo!()
 435    }
 436
 437    pub fn parse_count(&self) -> usize {
 438        self.snapshot.lock().parse_count()
 439    }
 440
 441    pub fn diagnostics_update_count(&self) -> usize {
 442        self.snapshot.lock().diagnostics_update_count()
 443    }
 444
 445    pub fn diagnostics_in_range<'a, T, O>(
 446        &'a self,
 447        search_range: Range<T>,
 448    ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
 449    where
 450        T: 'a + ToOffset,
 451        O: 'a,
 452    {
 453        todo!();
 454        None.into_iter()
 455    }
 456}
 457
 458#[cfg(any(test, feature = "test-support"))]
 459impl MultiBuffer {
 460    pub fn randomly_edit<R: rand::Rng>(&mut self, _: &mut R, _: usize, _: &mut ModelContext<Self>) {
 461        todo!()
 462    }
 463
 464    pub fn randomly_mutate<R: rand::Rng>(&mut self, rng: &mut R, cx: &mut ModelContext<Self>) {
 465        todo!()
 466    }
 467}
 468
 469impl Entity for MultiBuffer {
 470    type Event = super::Event;
 471}
 472
 473impl MultiBufferSnapshot {
 474    pub fn replica_id(&self) -> ReplicaId {
 475        todo!()
 476    }
 477
 478    pub fn text(&self) -> String {
 479        self.chunks(0..self.len(), None)
 480            .map(|chunk| chunk.text)
 481            .collect()
 482    }
 483
 484    pub fn reversed_chars_at<'a, T: ToOffset>(
 485        &'a self,
 486        position: T,
 487    ) -> impl Iterator<Item = char> + 'a {
 488        todo!();
 489        None.into_iter()
 490    }
 491
 492    pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
 493        let offset = position.to_offset(self);
 494        self.text_for_range(offset..self.len())
 495            .flat_map(|chunk| chunk.chars())
 496    }
 497
 498    pub fn text_for_range<'a, T: ToOffset>(
 499        &'a self,
 500        range: Range<T>,
 501    ) -> impl Iterator<Item = &'a str> {
 502        self.chunks(range, None).map(|chunk| chunk.text)
 503    }
 504
 505    pub fn is_line_blank(&self, row: u32) -> bool {
 506        self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
 507            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
 508    }
 509
 510    pub fn contains_str_at<T>(&self, _: T, _: &str) -> bool
 511    where
 512        T: ToOffset,
 513    {
 514        todo!()
 515    }
 516
 517    pub fn len(&self) -> usize {
 518        self.excerpts.summary().text.bytes
 519    }
 520
 521    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 522        let mut cursor = self.excerpts.cursor::<usize>();
 523        cursor.seek(&offset, Bias::Right, &());
 524        if let Some(excerpt) = cursor.item() {
 525            let start_after_header = *cursor.start() + excerpt.header_height as usize;
 526            if offset < start_after_header {
 527                *cursor.start()
 528            } else {
 529                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 530                let buffer_offset = excerpt
 531                    .buffer
 532                    .clip_offset(excerpt_start + (offset - start_after_header), bias);
 533                let offset_in_excerpt = if buffer_offset > excerpt_start {
 534                    buffer_offset - excerpt_start
 535                } else {
 536                    0
 537                };
 538                start_after_header + offset_in_excerpt
 539            }
 540        } else {
 541            self.excerpts.summary().text.bytes
 542        }
 543    }
 544
 545    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 546        let mut cursor = self.excerpts.cursor::<Point>();
 547        cursor.seek(&point, Bias::Right, &());
 548        if let Some(excerpt) = cursor.item() {
 549            let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
 550            if point < start_after_header {
 551                *cursor.start()
 552            } else {
 553                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 554                let buffer_point = excerpt
 555                    .buffer
 556                    .clip_point(excerpt_start + (point - start_after_header), bias);
 557                let point_in_excerpt = if buffer_point > excerpt_start {
 558                    buffer_point - excerpt_start
 559                } else {
 560                    Point::zero()
 561                };
 562                start_after_header + point_in_excerpt
 563            }
 564        } else {
 565            self.excerpts.summary().text.lines
 566        }
 567    }
 568
 569    pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
 570        let mut cursor = self.excerpts.cursor::<PointUtf16>();
 571        cursor.seek(&point, Bias::Right, &());
 572        if let Some(excerpt) = cursor.item() {
 573            let start_after_header =
 574                *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
 575            if point < start_after_header {
 576                *cursor.start()
 577            } else {
 578                let excerpt_start = excerpt
 579                    .buffer
 580                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 581                let buffer_point = excerpt
 582                    .buffer
 583                    .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
 584                let point_in_excerpt = if buffer_point > excerpt_start {
 585                    buffer_point - excerpt_start
 586                } else {
 587                    PointUtf16::new(0, 0)
 588                };
 589                start_after_header + point_in_excerpt
 590            }
 591        } else {
 592            self.excerpts.summary().text.lines_utf16
 593        }
 594    }
 595
 596    pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
 597        todo!()
 598    }
 599
 600    pub fn chunks<'a, T: ToOffset>(
 601        &'a self,
 602        range: Range<T>,
 603        theme: Option<&'a SyntaxTheme>,
 604    ) -> MultiBufferChunks<'a> {
 605        let range = range.start.to_offset(self)..range.end.to_offset(self);
 606        let mut cursor = self.excerpts.cursor::<usize>();
 607        cursor.seek(&range.start, Bias::Right, &());
 608
 609        let mut header_height: u8 = 0;
 610        let excerpt_chunks = cursor.item().map(|excerpt| {
 611            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
 612            header_height = excerpt.header_height;
 613
 614            let buffer_start;
 615            let start_overshoot = range.start - cursor.start();
 616            if start_overshoot < excerpt.header_height as usize {
 617                header_height -= start_overshoot as u8;
 618                buffer_start = buffer_range.start;
 619            } else {
 620                buffer_start =
 621                    buffer_range.start + start_overshoot - excerpt.header_height as usize;
 622                header_height = 0;
 623            }
 624
 625            let buffer_end;
 626            let end_overshoot = range.end - cursor.start();
 627            if end_overshoot < excerpt.header_height as usize {
 628                header_height -= excerpt.header_height - end_overshoot as u8;
 629                buffer_end = buffer_start;
 630            } else {
 631                buffer_end = cmp::min(
 632                    buffer_range.end,
 633                    buffer_range.start + end_overshoot - excerpt.header_height as usize,
 634                );
 635            }
 636
 637            excerpt.buffer.chunks(buffer_start..buffer_end, theme)
 638        });
 639
 640        MultiBufferChunks {
 641            range,
 642            cursor,
 643            header_height,
 644            excerpt_chunks,
 645            theme,
 646        }
 647    }
 648
 649    pub fn offset_to_point(&self, offset: usize) -> Point {
 650        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
 651        cursor.seek(&offset, Bias::Right, &());
 652        if let Some(excerpt) = cursor.item() {
 653            let (start_offset, start_point) = cursor.start();
 654            let overshoot = offset - start_offset;
 655            let header_height = excerpt.header_height as usize;
 656            if overshoot < header_height {
 657                *start_point
 658            } else {
 659                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 660                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 661                let buffer_point = excerpt
 662                    .buffer
 663                    .offset_to_point(excerpt_start_offset + (overshoot - header_height));
 664                *start_point
 665                    + Point::new(header_height as u32, 0)
 666                    + (buffer_point - excerpt_start_point)
 667            }
 668        } else {
 669            self.excerpts.summary().text.lines
 670        }
 671    }
 672
 673    pub fn point_to_offset(&self, point: Point) -> usize {
 674        let mut cursor = self.excerpts.cursor::<(Point, usize)>();
 675        cursor.seek(&point, Bias::Right, &());
 676        if let Some(excerpt) = cursor.item() {
 677            let (start_point, start_offset) = cursor.start();
 678            let overshoot = point - start_point;
 679            let header_height = Point::new(excerpt.header_height as u32, 0);
 680            if overshoot < header_height {
 681                *start_offset
 682            } else {
 683                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 684                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 685                let buffer_offset = excerpt
 686                    .buffer
 687                    .point_to_offset(excerpt_start_point + (overshoot - header_height));
 688                *start_offset + excerpt.header_height as usize + buffer_offset
 689                    - excerpt_start_offset
 690            }
 691        } else {
 692            self.excerpts.summary().text.bytes
 693        }
 694    }
 695
 696    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
 697        let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
 698        cursor.seek(&point, Bias::Right, &());
 699        if let Some(excerpt) = cursor.item() {
 700            let (start_point, start_offset) = cursor.start();
 701            let overshoot = point - start_point;
 702            let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
 703            if overshoot < header_height {
 704                *start_offset
 705            } else {
 706                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 707                let excerpt_start_point = excerpt
 708                    .buffer
 709                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 710                let buffer_offset = excerpt
 711                    .buffer
 712                    .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
 713                *start_offset
 714                    + excerpt.header_height as usize
 715                    + (buffer_offset - excerpt_start_offset)
 716            }
 717        } else {
 718            self.excerpts.summary().text.bytes
 719        }
 720    }
 721
 722    pub fn indent_column_for_line(&self, row: u32) -> u32 {
 723        todo!()
 724    }
 725
 726    pub fn line_len(&self, row: u32) -> u32 {
 727        let mut cursor = self.excerpts.cursor::<Point>();
 728        cursor.seek(&Point::new(row, 0), Bias::Right, &());
 729        if let Some(excerpt) = cursor.item() {
 730            let overshoot = row - cursor.start().row;
 731            let header_height = excerpt.header_height as u32;
 732            if overshoot < header_height {
 733                0
 734            } else {
 735                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 736                let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
 737                let buffer_row = excerpt_start.row + overshoot - header_height;
 738                let mut len = excerpt.buffer.line_len(buffer_row);
 739                if buffer_row == excerpt_end.row {
 740                    len = excerpt_end.column;
 741                }
 742                if buffer_row == excerpt_start.row {
 743                    len -= excerpt_start.column
 744                }
 745                len
 746            }
 747        } else {
 748            0
 749        }
 750    }
 751
 752    pub fn max_point(&self) -> Point {
 753        self.text_summary().lines
 754    }
 755
 756    pub fn text_summary(&self) -> TextSummary {
 757        self.excerpts.summary().text
 758    }
 759
 760    pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
 761    where
 762        D: TextDimension,
 763        O: ToOffset,
 764    {
 765        let mut summary = D::default();
 766        let mut range = range.start.to_offset(self)..range.end.to_offset(self);
 767        let mut cursor = self.excerpts.cursor::<usize>();
 768        cursor.seek(&range.start, Bias::Right, &());
 769        if let Some(excerpt) = cursor.item() {
 770            let start_after_header = cursor.start() + excerpt.header_height as usize;
 771            if range.start < start_after_header {
 772                let header_len = cmp::min(range.end, start_after_header) - range.start;
 773                summary.add_assign(&D::from_text_summary(&TextSummary {
 774                    bytes: header_len,
 775                    lines: Point::new(header_len as u32, 0),
 776                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 777                    first_line_chars: 0,
 778                    last_line_chars: 0,
 779                    longest_row: 0,
 780                    longest_row_chars: 0,
 781                }));
 782                range.start = start_after_header;
 783                range.end = cmp::max(range.start, range.end);
 784            }
 785
 786            let end_before_newline = cursor.end(&()) - 1;
 787            let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 788            let start_in_excerpt = excerpt_start + (range.start - start_after_header);
 789            let end_in_excerpt =
 790                excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
 791            summary.add_assign(
 792                &excerpt
 793                    .buffer
 794                    .text_summary_for_range(start_in_excerpt..end_in_excerpt),
 795            );
 796
 797            if range.end > end_before_newline {
 798                summary.add_assign(&D::from_text_summary(&TextSummary {
 799                    bytes: 1,
 800                    lines: Point::new(1 as u32, 0),
 801                    lines_utf16: PointUtf16::new(1 as u32, 0),
 802                    first_line_chars: 0,
 803                    last_line_chars: 0,
 804                    longest_row: 0,
 805                    longest_row_chars: 0,
 806                }));
 807            }
 808
 809            cursor.next(&());
 810        }
 811
 812        if range.end > *cursor.start() {
 813            summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
 814                &range.end,
 815                Bias::Right,
 816                &(),
 817            )));
 818            if let Some(excerpt) = cursor.item() {
 819                let start_after_header = cursor.start() + excerpt.header_height as usize;
 820                let header_len =
 821                    cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
 822                summary.add_assign(&D::from_text_summary(&TextSummary {
 823                    bytes: header_len,
 824                    lines: Point::new(header_len as u32, 0),
 825                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 826                    first_line_chars: 0,
 827                    last_line_chars: 0,
 828                    longest_row: 0,
 829                    longest_row_chars: 0,
 830                }));
 831                range.end = cmp::max(start_after_header, range.end);
 832
 833                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 834                let end_in_excerpt = excerpt_start + (range.end - start_after_header);
 835                summary.add_assign(
 836                    &excerpt
 837                        .buffer
 838                        .text_summary_for_range(excerpt_start..end_in_excerpt),
 839                );
 840                cursor.next(&());
 841            }
 842        }
 843
 844        summary
 845    }
 846
 847    fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
 848    where
 849        D: TextDimension + Ord + Sub<D, Output = D>,
 850    {
 851        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
 852        cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
 853        if let Some(excerpt) = cursor.item() {
 854            if excerpt.id == anchor.excerpt_id {
 855                let mut excerpt_start = D::from_text_summary(&cursor.start().text);
 856                excerpt_start.add_summary(&excerpt.header_summary(), &());
 857                let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
 858                let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
 859                if buffer_point > excerpt_buffer_start {
 860                    excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
 861                }
 862                return excerpt_start;
 863            }
 864        }
 865        D::from_text_summary(&cursor.start().text)
 866    }
 867
 868    fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
 869    where
 870        D: TextDimension + Ord + Sub<D, Output = D>,
 871        I: 'a + IntoIterator<Item = &'a Anchor>,
 872    {
 873        let mut anchors = anchors.into_iter().peekable();
 874        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
 875        let mut summaries = Vec::new();
 876        while let Some(anchor) = anchors.peek() {
 877            let excerpt_id = &anchor.excerpt_id;
 878            cursor.seek(&Some(excerpt_id), Bias::Left, &());
 879            if let Some(excerpt) = cursor.item() {
 880                let excerpt_exists = excerpt.id == *excerpt_id;
 881                let excerpt_anchors = std::iter::from_fn(|| {
 882                    let anchor = anchors.peek()?;
 883                    if anchor.excerpt_id == *excerpt_id {
 884                        Some(&anchors.next().unwrap().text_anchor)
 885                    } else {
 886                        None
 887                    }
 888                });
 889
 890                if excerpt_exists {
 891                    let mut excerpt_start = D::from_text_summary(&cursor.start().text);
 892                    excerpt_start.add_summary(&excerpt.header_summary(), &());
 893                    let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
 894                    summaries.extend(
 895                        excerpt
 896                            .buffer
 897                            .summaries_for_anchors::<D, _>(excerpt_anchors)
 898                            .map(move |summary| {
 899                                let mut excerpt_start = excerpt_start.clone();
 900                                let excerpt_buffer_start = excerpt_buffer_start.clone();
 901                                if summary > excerpt_buffer_start {
 902                                    excerpt_start.add_assign(&(summary - excerpt_buffer_start));
 903                                }
 904                                excerpt_start
 905                            }),
 906                    );
 907                } else {
 908                    excerpt_anchors.for_each(drop);
 909                }
 910            } else {
 911                break;
 912            }
 913        }
 914
 915        summaries
 916    }
 917
 918    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
 919        self.anchor_at(position, Bias::Left)
 920    }
 921
 922    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
 923        self.anchor_at(position, Bias::Right)
 924    }
 925
 926    pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
 927        todo!()
 928    }
 929
 930    pub fn parse_count(&self) -> usize {
 931        todo!()
 932    }
 933
 934    pub fn enclosing_bracket_ranges<T: ToOffset>(
 935        &self,
 936        range: Range<T>,
 937    ) -> Option<(Range<usize>, Range<usize>)> {
 938        todo!()
 939    }
 940
 941    pub fn diagnostics_update_count(&self) -> usize {
 942        todo!()
 943    }
 944
 945    pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
 946        todo!()
 947    }
 948
 949    pub fn diagnostic_group<'a, O>(
 950        &'a self,
 951        group_id: usize,
 952    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
 953    where
 954        O: 'a,
 955    {
 956        todo!();
 957        None.into_iter()
 958    }
 959
 960    pub fn diagnostics_in_range<'a, T, O>(
 961        &'a self,
 962        search_range: Range<T>,
 963    ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
 964    where
 965        T: 'a + ToOffset,
 966        O: 'a,
 967    {
 968        todo!();
 969        None.into_iter()
 970    }
 971
 972    pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
 973        todo!()
 974    }
 975
 976    fn buffer_snapshot_for_excerpt<'a>(
 977        &'a self,
 978        excerpt_id: &'a ExcerptId,
 979    ) -> Option<&'a BufferSnapshot> {
 980        let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
 981        cursor.seek(&Some(excerpt_id), Bias::Left, &());
 982        if let Some(excerpt) = cursor.item() {
 983            if *cursor.start() == Some(excerpt_id) {
 984                return Some(&excerpt.buffer);
 985            }
 986        }
 987        None
 988    }
 989}
 990
 991impl Excerpt {
 992    fn new(
 993        id: ExcerptId,
 994        buffer: buffer::BufferSnapshot,
 995        range: Range<text::Anchor>,
 996        header_height: u8,
 997    ) -> Self {
 998        let mut text_summary =
 999            buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1000        if header_height > 0 {
1001            text_summary.first_line_chars = 0;
1002            text_summary.lines.row += header_height as u32;
1003            text_summary.lines_utf16.row += header_height as u32;
1004            text_summary.bytes += header_height as usize;
1005            text_summary.longest_row += header_height as u32;
1006        }
1007        text_summary.last_line_chars = 0;
1008        text_summary.lines.row += 1;
1009        text_summary.lines.column = 0;
1010        text_summary.lines_utf16.row += 1;
1011        text_summary.lines_utf16.column = 0;
1012        text_summary.bytes += 1;
1013
1014        Excerpt {
1015            id,
1016            buffer,
1017            range,
1018            text_summary,
1019            header_height,
1020        }
1021    }
1022
1023    fn header_summary(&self) -> TextSummary {
1024        TextSummary {
1025            bytes: self.header_height as usize,
1026            lines: Point::new(self.header_height as u32, 0),
1027            lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1028            first_line_chars: 0,
1029            last_line_chars: 0,
1030            longest_row: 0,
1031            longest_row_chars: 0,
1032        }
1033    }
1034}
1035
1036impl sum_tree::Item for Excerpt {
1037    type Summary = ExcerptSummary;
1038
1039    fn summary(&self) -> Self::Summary {
1040        ExcerptSummary {
1041            excerpt_id: self.id.clone(),
1042            text: self.text_summary.clone(),
1043        }
1044    }
1045}
1046
1047impl sum_tree::Summary for ExcerptSummary {
1048    type Context = ();
1049
1050    fn add_summary(&mut self, summary: &Self, _: &()) {
1051        debug_assert!(summary.excerpt_id > self.excerpt_id);
1052        self.excerpt_id = summary.excerpt_id.clone();
1053        self.text.add_summary(&summary.text, &());
1054    }
1055}
1056
1057impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1058    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1059        *self += &summary.text;
1060    }
1061}
1062
1063impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1064    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1065        *self += summary.text.bytes;
1066    }
1067}
1068
1069impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1070    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1071        Ord::cmp(self, &cursor_location.text.bytes)
1072    }
1073}
1074
1075impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1076    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1077        Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1078    }
1079}
1080
1081impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1082    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1083        *self += summary.text.lines;
1084    }
1085}
1086
1087impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1088    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1089        *self += summary.text.lines_utf16
1090    }
1091}
1092
1093impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1094    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1095        *self = Some(&summary.excerpt_id);
1096    }
1097}
1098
1099impl<'a> MultiBufferChunks<'a> {
1100    pub fn offset(&self) -> usize {
1101        todo!()
1102    }
1103
1104    pub fn seek(&mut self, offset: usize) {
1105        todo!()
1106    }
1107}
1108
1109impl<'a> Iterator for MultiBufferChunks<'a> {
1110    type Item = Chunk<'a>;
1111
1112    fn next(&mut self) -> Option<Self::Item> {
1113        loop {
1114            if self.header_height > 0 {
1115                let chunk = Chunk {
1116                    text: unsafe {
1117                        std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1118                    },
1119                    ..Default::default()
1120                };
1121                self.header_height = 0;
1122                return Some(chunk);
1123            }
1124
1125            if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1126                if let Some(chunk) = excerpt_chunks.next() {
1127                    return Some(chunk);
1128                }
1129                self.excerpt_chunks.take();
1130                if self.cursor.end(&()) <= self.range.end {
1131                    return Some(Chunk {
1132                        text: "\n",
1133                        ..Default::default()
1134                    });
1135                }
1136            }
1137
1138            self.cursor.next(&());
1139            if *self.cursor.start() >= self.range.end {
1140                return None;
1141            }
1142
1143            let excerpt = self.cursor.item()?;
1144            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1145
1146            let buffer_end = cmp::min(
1147                buffer_range.end,
1148                buffer_range.start + self.range.end
1149                    - excerpt.header_height as usize
1150                    - self.cursor.start(),
1151            );
1152
1153            self.header_height = excerpt.header_height;
1154            self.excerpt_chunks = Some(
1155                excerpt
1156                    .buffer
1157                    .chunks(buffer_range.start..buffer_end, self.theme),
1158            );
1159        }
1160    }
1161}
1162
1163impl<'a> Iterator for MultiBufferBytes<'a> {
1164    type Item = &'a [u8];
1165
1166    fn next(&mut self) -> Option<Self::Item> {
1167        todo!()
1168    }
1169}
1170
1171impl<'a> io::Read for MultiBufferBytes<'a> {
1172    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1173        todo!()
1174    }
1175}
1176
1177impl ToOffset for Point {
1178    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1179        snapshot.point_to_offset(*self)
1180    }
1181}
1182
1183impl ToOffset for PointUtf16 {
1184    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1185        snapshot.point_utf16_to_offset(*self)
1186    }
1187}
1188
1189impl ToOffset for usize {
1190    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1191        assert!(*self <= snapshot.len(), "offset is out of range");
1192        *self
1193    }
1194}
1195
1196impl ToPoint for usize {
1197    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1198        snapshot.offset_to_point(*self)
1199    }
1200}
1201
1202impl ToPoint for Point {
1203    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1204        *self
1205    }
1206}
1207
1208#[cfg(test)]
1209mod tests {
1210    use super::*;
1211    use crate::buffer::Buffer;
1212    use gpui::MutableAppContext;
1213    use rand::prelude::*;
1214    use std::env;
1215    use text::{Point, RandomCharIter};
1216    use util::test::sample_text;
1217
1218    #[gpui::test]
1219    fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1220        let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1221        let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1222
1223        let list = cx.add_model(|_| MultiBuffer::new());
1224
1225        let subscription = list.update(cx, |list, cx| {
1226            let subscription = list.subscribe();
1227            list.push(
1228                ExcerptProperties {
1229                    buffer: &buffer_1,
1230                    range: Point::new(1, 2)..Point::new(2, 5),
1231                    header_height: 2,
1232                },
1233                cx,
1234            );
1235            assert_eq!(
1236                subscription.consume().into_inner(),
1237                [Edit {
1238                    old: 0..0,
1239                    new: 0..13
1240                }]
1241            );
1242
1243            list.push(
1244                ExcerptProperties {
1245                    buffer: &buffer_1,
1246                    range: Point::new(3, 3)..Point::new(4, 4),
1247                    header_height: 1,
1248                },
1249                cx,
1250            );
1251            list.push(
1252                ExcerptProperties {
1253                    buffer: &buffer_2,
1254                    range: Point::new(3, 1)..Point::new(3, 3),
1255                    header_height: 3,
1256                },
1257                cx,
1258            );
1259            assert_eq!(
1260                subscription.consume().into_inner(),
1261                [Edit {
1262                    old: 13..13,
1263                    new: 13..29
1264                }]
1265            );
1266
1267            subscription
1268        });
1269
1270        assert_eq!(
1271            list.read(cx).snapshot(cx).text(),
1272            concat!(
1273                "\n",      // Preserve newlines
1274                "\n",      //
1275                "bbbb\n",  //
1276                "ccccc\n", //
1277                "\n",      //
1278                "ddd\n",   //
1279                "eeee\n",  //
1280                "\n",      //
1281                "\n",      //
1282                "\n",      //
1283                "jj\n"     //
1284            )
1285        );
1286
1287        buffer_1.update(cx, |buffer, cx| {
1288            buffer.edit(
1289                [
1290                    Point::new(0, 0)..Point::new(0, 0),
1291                    Point::new(2, 1)..Point::new(2, 3),
1292                ],
1293                "\n",
1294                cx,
1295            );
1296        });
1297
1298        assert_eq!(
1299            list.read(cx).snapshot(cx).text(),
1300            concat!(
1301                "\n",     // Preserve newlines
1302                "\n",     //
1303                "bbbb\n", //
1304                "c\n",    //
1305                "cc\n",   //
1306                "\n",     //
1307                "ddd\n",  //
1308                "eeee\n", //
1309                "\n",     //
1310                "\n",     //
1311                "\n",     //
1312                "jj\n"    //
1313            )
1314        );
1315
1316        assert_eq!(
1317            subscription.consume().into_inner(),
1318            [Edit {
1319                old: 8..10,
1320                new: 8..9
1321            }]
1322        );
1323    }
1324
1325    #[gpui::test(iterations = 100)]
1326    fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1327        let operations = env::var("OPERATIONS")
1328            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1329            .unwrap_or(10);
1330
1331        let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1332        let list = cx.add_model(|_| MultiBuffer::new());
1333        let mut excerpt_ids = Vec::new();
1334        let mut expected_excerpts = Vec::new();
1335        let mut old_versions = Vec::new();
1336
1337        for _ in 0..operations {
1338            match rng.gen_range(0..100) {
1339                0..=19 if !buffers.is_empty() => {
1340                    let buffer = buffers.choose(&mut rng).unwrap();
1341                    buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1342                }
1343                _ => {
1344                    let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1345                        let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1346                        buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1347                        buffers.last().unwrap()
1348                    } else {
1349                        buffers.choose(&mut rng).unwrap()
1350                    };
1351
1352                    let buffer = buffer_handle.read(cx);
1353                    let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1354                    let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1355                    let header_height = rng.gen_range(0..=5);
1356                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1357                    log::info!(
1358                        "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1359                        header_height,
1360                        buffer_handle.id(),
1361                        buffer.text(),
1362                        start_ix..end_ix,
1363                        &buffer.text()[start_ix..end_ix]
1364                    );
1365
1366                    let excerpt_id = list.update(cx, |list, cx| {
1367                        list.push(
1368                            ExcerptProperties {
1369                                buffer: &buffer_handle,
1370                                range: start_ix..end_ix,
1371                                header_height,
1372                            },
1373                            cx,
1374                        )
1375                    });
1376                    excerpt_ids.push(excerpt_id);
1377                    expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1378                }
1379            }
1380
1381            if rng.gen_bool(0.3) {
1382                list.update(cx, |list, cx| {
1383                    old_versions.push((list.snapshot(cx), list.subscribe()));
1384                })
1385            }
1386
1387            let snapshot = list.read(cx).snapshot(cx);
1388
1389            let mut excerpt_starts = Vec::new();
1390            let mut expected_text = String::new();
1391            for (buffer, range, header_height) in &expected_excerpts {
1392                let buffer = buffer.read(cx);
1393                let buffer_range = range.to_offset(buffer);
1394
1395                for _ in 0..*header_height {
1396                    expected_text.push('\n');
1397                }
1398
1399                excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1400                expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1401                expected_text.push('\n');
1402            }
1403
1404            assert_eq!(snapshot.text(), expected_text);
1405
1406            let mut excerpt_starts = excerpt_starts.into_iter();
1407            for (buffer, range, _) in &expected_excerpts {
1408                let buffer_id = buffer.id();
1409                let buffer = buffer.read(cx);
1410                let buffer_range = range.to_offset(buffer);
1411                let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1412                let buffer_start_point_utf16 =
1413                    buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1414
1415                let excerpt_start = excerpt_starts.next().unwrap();
1416                let mut offset = excerpt_start.bytes;
1417                let mut buffer_offset = buffer_range.start;
1418                let mut point = excerpt_start.lines;
1419                let mut buffer_point = buffer_start_point;
1420                let mut point_utf16 = excerpt_start.lines_utf16;
1421                let mut buffer_point_utf16 = buffer_start_point_utf16;
1422                for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1423                    let left_offset = snapshot.clip_offset(offset, Bias::Left);
1424                    let right_offset = snapshot.clip_offset(offset, Bias::Right);
1425                    let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1426                    let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1427                    assert_eq!(
1428                        left_offset,
1429                        excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1430                        "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1431                        offset,
1432                        buffer_id,
1433                        buffer_offset,
1434                    );
1435                    assert_eq!(
1436                        right_offset,
1437                        excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1438                        "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1439                        offset,
1440                        buffer_id,
1441                        buffer_offset,
1442                    );
1443
1444                    let left_point = snapshot.clip_point(point, Bias::Left);
1445                    let right_point = snapshot.clip_point(point, Bias::Right);
1446                    let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1447                    let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1448                    assert_eq!(
1449                        left_point,
1450                        excerpt_start.lines + (buffer_left_point - buffer_start_point),
1451                        "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1452                        point,
1453                        buffer_id,
1454                        buffer_point,
1455                    );
1456                    assert_eq!(
1457                        right_point,
1458                        excerpt_start.lines + (buffer_right_point - buffer_start_point),
1459                        "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1460                        point,
1461                        buffer_id,
1462                        buffer_point,
1463                    );
1464
1465                    let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1466                    let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1467                    let buffer_left_point_utf16 =
1468                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1469                    let buffer_right_point_utf16 =
1470                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1471                    assert_eq!(
1472                        left_point_utf16,
1473                        excerpt_start.lines_utf16
1474                            + (buffer_left_point_utf16 - buffer_start_point_utf16),
1475                        "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1476                        point_utf16,
1477                        buffer_id,
1478                        buffer_point_utf16,
1479                    );
1480                    assert_eq!(
1481                        right_point_utf16,
1482                        excerpt_start.lines_utf16
1483                            + (buffer_right_point_utf16 - buffer_start_point_utf16),
1484                        "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1485                        point_utf16,
1486                        buffer_id,
1487                        buffer_point_utf16,
1488                    );
1489
1490                    assert_eq!(
1491                        snapshot.point_to_offset(left_point),
1492                        left_offset,
1493                        "point_to_offset({:?})",
1494                        left_point,
1495                    );
1496                    assert_eq!(
1497                        snapshot.offset_to_point(left_offset),
1498                        left_point,
1499                        "offset_to_point({:?})",
1500                        left_offset,
1501                    );
1502
1503                    offset += 1;
1504                    buffer_offset += 1;
1505                    if *byte == b'\n' {
1506                        point += Point::new(1, 0);
1507                        point_utf16 += PointUtf16::new(1, 0);
1508                        buffer_point += Point::new(1, 0);
1509                        buffer_point_utf16 += PointUtf16::new(1, 0);
1510                    } else {
1511                        point += Point::new(0, 1);
1512                        point_utf16 += PointUtf16::new(0, 1);
1513                        buffer_point += Point::new(0, 1);
1514                        buffer_point_utf16 += PointUtf16::new(0, 1);
1515                    }
1516                }
1517            }
1518
1519            for (row, line) in expected_text.split('\n').enumerate() {
1520                assert_eq!(
1521                    snapshot.line_len(row as u32),
1522                    line.len() as u32,
1523                    "line_len({}).",
1524                    row
1525                );
1526            }
1527
1528            for _ in 0..10 {
1529                let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1530                let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1531
1532                assert_eq!(
1533                    snapshot
1534                        .text_for_range(start_ix..end_ix)
1535                        .collect::<String>(),
1536                    &expected_text[start_ix..end_ix],
1537                    "incorrect text for range {:?}",
1538                    start_ix..end_ix
1539                );
1540
1541                let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1542                assert_eq!(
1543                    snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1544                    expected_summary,
1545                    "incorrect summary for range {:?}",
1546                    start_ix..end_ix
1547                );
1548            }
1549        }
1550
1551        let snapshot = list.read(cx).snapshot(cx);
1552        for (old_snapshot, subscription) in old_versions {
1553            let edits = subscription.consume().into_inner();
1554
1555            log::info!(
1556                "applying edits since old text: {:?}: {:?}",
1557                old_snapshot.text(),
1558                edits,
1559            );
1560
1561            let mut text = old_snapshot.text();
1562            for edit in edits {
1563                let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1564                text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1565            }
1566            assert_eq!(text.to_string(), snapshot.text());
1567        }
1568    }
1569}