multi_buffer.rs

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