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