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