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