multi_buffer.rs

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