multi_buffer.rs

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