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