multi_buffer.rs

   1mod anchor;
   2
   3pub use anchor::{Anchor, AnchorRangeExt};
   4use anyhow::Result;
   5use clock::ReplicaId;
   6use collections::{HashMap, HashSet};
   7use gpui::{AppContext, ElementBox, 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, fmt, io,
  15    iter::{self, FromIterator},
  16    ops::{Range, Sub},
  17    str,
  18    sync::Arc,
  19    time::{Duration, Instant, 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;
  29use util::post_inc;
  30
  31const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
  32
  33pub type ExcerptId = Locator;
  34
  35pub struct MultiBuffer {
  36    snapshot: RefCell<MultiBufferSnapshot>,
  37    buffers: HashMap<usize, BufferState>,
  38    subscriptions: Topic,
  39    singleton: bool,
  40    replica_id: ReplicaId,
  41    history: History,
  42}
  43
  44struct History {
  45    next_transaction_id: usize,
  46    undo_stack: Vec<Transaction>,
  47    redo_stack: Vec<Transaction>,
  48    transaction_depth: usize,
  49    group_interval: Duration,
  50}
  51
  52struct Transaction {
  53    id: usize,
  54    buffer_transactions: HashSet<(usize, text::TransactionId)>,
  55    first_edit_at: Instant,
  56    last_edit_at: Instant,
  57}
  58
  59pub trait ToOffset: 'static + fmt::Debug {
  60    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
  61}
  62
  63pub trait ToPoint: 'static + fmt::Debug {
  64    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
  65}
  66
  67pub trait FromAnchor: 'static {
  68    fn from_anchor(anchor: &Anchor, snapshot: &MultiBufferSnapshot) -> Self;
  69}
  70
  71#[derive(Debug)]
  72struct BufferState {
  73    buffer: ModelHandle<Buffer>,
  74    last_version: clock::Global,
  75    last_parse_count: usize,
  76    last_diagnostics_update_count: usize,
  77    excerpts: Vec<ExcerptId>,
  78}
  79
  80#[derive(Clone, Default)]
  81pub struct MultiBufferSnapshot {
  82    excerpts: SumTree<Excerpt>,
  83    parse_count: usize,
  84    diagnostics_update_count: usize,
  85}
  86
  87pub type RenderHeaderFn = Arc<dyn 'static + Send + Sync + Fn(&AppContext) -> ElementBox>;
  88
  89pub struct ExcerptProperties<'a, T> {
  90    pub buffer: &'a ModelHandle<Buffer>,
  91    pub range: Range<T>,
  92    pub header_height: u8,
  93    pub render_header: Option<RenderHeaderFn>,
  94}
  95
  96#[derive(Clone)]
  97struct Excerpt {
  98    id: ExcerptId,
  99    buffer_id: usize,
 100    buffer: BufferSnapshot,
 101    range: Range<text::Anchor>,
 102    render_header: Option<RenderHeaderFn>,
 103    text_summary: TextSummary,
 104    header_height: u8,
 105    has_trailing_newline: bool,
 106}
 107
 108#[derive(Clone, Debug, Default)]
 109struct ExcerptSummary {
 110    excerpt_id: ExcerptId,
 111    text: TextSummary,
 112}
 113
 114pub struct MultiBufferChunks<'a> {
 115    range: Range<usize>,
 116    excerpts: Cursor<'a, Excerpt, usize>,
 117    excerpt_chunks: Option<ExcerptChunks<'a>>,
 118    theme: Option<&'a SyntaxTheme>,
 119}
 120
 121pub struct MultiBufferBytes<'a> {
 122    range: Range<usize>,
 123    excerpts: Cursor<'a, Excerpt, usize>,
 124    excerpt_bytes: Option<ExcerptBytes<'a>>,
 125    chunk: &'a [u8],
 126}
 127
 128struct ExcerptChunks<'a> {
 129    header_height: usize,
 130    content_chunks: BufferChunks<'a>,
 131    footer_height: usize,
 132}
 133
 134struct ExcerptBytes<'a> {
 135    header_height: usize,
 136    content_bytes: language::rope::Bytes<'a>,
 137    footer_height: usize,
 138}
 139
 140impl MultiBuffer {
 141    pub fn new(replica_id: ReplicaId) -> Self {
 142        Self {
 143            snapshot: Default::default(),
 144            buffers: Default::default(),
 145            subscriptions: Default::default(),
 146            singleton: false,
 147            replica_id,
 148            history: History {
 149                next_transaction_id: Default::default(),
 150                undo_stack: Default::default(),
 151                redo_stack: Default::default(),
 152                transaction_depth: 0,
 153                group_interval: Duration::from_millis(300),
 154            },
 155        }
 156    }
 157
 158    pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
 159        let mut this = Self::new(buffer.read(cx).replica_id());
 160        this.singleton = true;
 161        this.push_excerpt(
 162            ExcerptProperties {
 163                buffer: &buffer,
 164                range: text::Anchor::min()..text::Anchor::max(),
 165                header_height: 0,
 166                render_header: None,
 167            },
 168            cx,
 169        );
 170        this
 171    }
 172
 173    #[cfg(any(test, feature = "test-support"))]
 174    pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle<Self> {
 175        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 176        cx.add_model(|cx| Self::singleton(buffer, cx))
 177    }
 178
 179    #[cfg(any(test, feature = "test-support"))]
 180    pub fn build_random(
 181        excerpts: usize,
 182        mut rng: &mut impl rand::Rng,
 183        cx: &mut MutableAppContext,
 184    ) -> ModelHandle<Self> {
 185        use rand::prelude::*;
 186        use text::RandomCharIter;
 187
 188        cx.add_model(|cx| {
 189            let mut multibuffer = MultiBuffer::new(0);
 190            let mut buffers = Vec::new();
 191            for _ in 0..excerpts {
 192                let buffer_handle = if rng.gen() || buffers.is_empty() {
 193                    let text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
 194                    buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx)));
 195                    let buffer = buffers.last().unwrap();
 196                    log::info!(
 197                        "Creating new buffer {} with text: {:?}",
 198                        buffer.id(),
 199                        buffer.read(cx).text()
 200                    );
 201                    buffers.last().unwrap()
 202                } else {
 203                    buffers.choose(rng).unwrap()
 204                };
 205
 206                let buffer = buffer_handle.read(cx);
 207                let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
 208                let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
 209                let header_height = rng.gen_range(0..=5);
 210                log::info!(
 211                    "Inserting excerpt from buffer {} with header height {} and range {:?}: {:?}",
 212                    buffer_handle.id(),
 213                    header_height,
 214                    start_ix..end_ix,
 215                    &buffer.text()[start_ix..end_ix]
 216                );
 217
 218                multibuffer.push_excerpt(
 219                    ExcerptProperties {
 220                        buffer: buffer_handle,
 221                        range: start_ix..end_ix,
 222                        header_height,
 223                        render_header: None,
 224                    },
 225                    cx,
 226                );
 227            }
 228            multibuffer
 229        })
 230    }
 231
 232    pub fn replica_id(&self) -> ReplicaId {
 233        self.replica_id
 234    }
 235
 236    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
 237        self.sync(cx);
 238        self.snapshot.borrow().clone()
 239    }
 240
 241    pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
 242        self.sync(cx);
 243        self.snapshot.borrow()
 244    }
 245
 246    pub fn as_singleton(&self) -> Option<&ModelHandle<Buffer>> {
 247        if self.singleton {
 248            return Some(&self.buffers.values().next().unwrap().buffer);
 249        } else {
 250            None
 251        }
 252    }
 253
 254    pub fn subscribe(&mut self) -> Subscription {
 255        self.subscriptions.subscribe()
 256    }
 257
 258    pub fn edit<I, S, T>(&mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>)
 259    where
 260        I: IntoIterator<Item = Range<S>>,
 261        S: ToOffset,
 262        T: Into<String>,
 263    {
 264        self.edit_internal(ranges, new_text, false, cx)
 265    }
 266
 267    pub fn edit_with_autoindent<I, S, T>(
 268        &mut self,
 269        ranges: I,
 270        new_text: T,
 271        cx: &mut ModelContext<Self>,
 272    ) where
 273        I: IntoIterator<Item = Range<S>>,
 274        S: ToOffset,
 275        T: Into<String>,
 276    {
 277        self.edit_internal(ranges, new_text, true, cx)
 278    }
 279
 280    pub fn edit_internal<I, S, T>(
 281        &mut self,
 282        ranges_iter: I,
 283        new_text: T,
 284        autoindent: bool,
 285        cx: &mut ModelContext<Self>,
 286    ) where
 287        I: IntoIterator<Item = Range<S>>,
 288        S: ToOffset,
 289        T: Into<String>,
 290    {
 291        if let Some(buffer) = self.as_singleton() {
 292            let snapshot = self.read(cx);
 293            let ranges = ranges_iter
 294                .into_iter()
 295                .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
 296            return buffer.update(cx, |buffer, cx| {
 297                if autoindent {
 298                    buffer.edit_with_autoindent(ranges, new_text, cx)
 299                } else {
 300                    buffer.edit(ranges, new_text, cx)
 301                }
 302            });
 303        }
 304
 305        let snapshot = self.read(cx);
 306        let mut buffer_edits: HashMap<usize, Vec<(Range<usize>, bool)>> = Default::default();
 307        let mut cursor = snapshot.excerpts.cursor::<usize>();
 308        for range in ranges_iter {
 309            let start = range.start.to_offset(&snapshot);
 310            let end = range.end.to_offset(&snapshot);
 311            cursor.seek(&start, Bias::Right, &());
 312            if cursor.item().is_none() && start == *cursor.start() {
 313                cursor.prev(&());
 314            }
 315            let start_excerpt = cursor.item().expect("start offset out of bounds");
 316            let start_overshoot =
 317                (start - cursor.start()).saturating_sub(start_excerpt.header_height as usize);
 318            let buffer_start =
 319                start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot;
 320
 321            cursor.seek(&end, Bias::Right, &());
 322            if cursor.item().is_none() && end == *cursor.start() {
 323                cursor.prev(&());
 324            }
 325            let end_excerpt = cursor.item().expect("end offset out of bounds");
 326            let end_overshoot =
 327                (end - cursor.start()).saturating_sub(end_excerpt.header_height as usize);
 328            let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot;
 329
 330            if start_excerpt.id == end_excerpt.id {
 331                buffer_edits
 332                    .entry(start_excerpt.buffer_id)
 333                    .or_insert(Vec::new())
 334                    .push((buffer_start..buffer_end, true));
 335            } else {
 336                let start_excerpt_range =
 337                    buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer);
 338                let end_excerpt_range =
 339                    end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end;
 340                buffer_edits
 341                    .entry(start_excerpt.buffer_id)
 342                    .or_insert(Vec::new())
 343                    .push((start_excerpt_range, true));
 344                buffer_edits
 345                    .entry(end_excerpt.buffer_id)
 346                    .or_insert(Vec::new())
 347                    .push((end_excerpt_range, false));
 348
 349                cursor.seek(&start, Bias::Right, &());
 350                cursor.next(&());
 351                while let Some(excerpt) = cursor.item() {
 352                    if excerpt.id == end_excerpt.id {
 353                        break;
 354                    }
 355                    buffer_edits
 356                        .entry(excerpt.buffer_id)
 357                        .or_insert(Vec::new())
 358                        .push((excerpt.range.to_offset(&excerpt.buffer), false));
 359                    cursor.next(&());
 360                }
 361            }
 362        }
 363
 364        let new_text = new_text.into();
 365        for (buffer_id, mut edits) in buffer_edits {
 366            edits.sort_unstable_by_key(|(range, _)| range.start);
 367            self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
 368                let mut edits = edits.into_iter().peekable();
 369                let mut insertions = Vec::new();
 370                let mut deletions = Vec::new();
 371                while let Some((mut range, mut is_insertion)) = edits.next() {
 372                    while let Some((next_range, next_is_insertion)) = edits.peek() {
 373                        if range.end >= next_range.start {
 374                            range.end = cmp::max(next_range.end, range.end);
 375                            is_insertion |= *next_is_insertion;
 376                            edits.next();
 377                        } else {
 378                            break;
 379                        }
 380                    }
 381
 382                    if is_insertion {
 383                        insertions.push(
 384                            buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
 385                        );
 386                    } else if !range.is_empty() {
 387                        deletions.push(
 388                            buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
 389                        );
 390                    }
 391                }
 392
 393                if autoindent {
 394                    buffer.edit_with_autoindent(deletions, "", cx);
 395                    buffer.edit_with_autoindent(insertions, new_text.clone(), cx);
 396                } else {
 397                    buffer.edit(deletions, "", cx);
 398                    buffer.edit(insertions, new_text.clone(), cx);
 399                }
 400            })
 401        }
 402    }
 403
 404    pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
 405        self.start_transaction_at(Instant::now(), cx)
 406    }
 407
 408    pub(crate) fn start_transaction_at(
 409        &mut self,
 410        now: Instant,
 411        cx: &mut ModelContext<Self>,
 412    ) -> Option<TransactionId> {
 413        if let Some(buffer) = self.as_singleton() {
 414            return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
 415        }
 416
 417        for BufferState { buffer, .. } in self.buffers.values() {
 418            buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
 419        }
 420        self.history.start_transaction(now)
 421    }
 422
 423    pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
 424        self.end_transaction_at(Instant::now(), cx)
 425    }
 426
 427    pub(crate) fn end_transaction_at(
 428        &mut self,
 429        now: Instant,
 430        cx: &mut ModelContext<Self>,
 431    ) -> Option<TransactionId> {
 432        if let Some(buffer) = self.as_singleton() {
 433            return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
 434        }
 435
 436        let mut buffer_transactions = HashSet::default();
 437        for BufferState { buffer, .. } in self.buffers.values() {
 438            if let Some(transaction_id) =
 439                buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
 440            {
 441                buffer_transactions.insert((buffer.id(), transaction_id));
 442            }
 443        }
 444
 445        if self.history.end_transaction(now, buffer_transactions) {
 446            let transaction_id = self.history.group().unwrap();
 447            Some(transaction_id)
 448        } else {
 449            None
 450        }
 451    }
 452
 453    pub fn set_active_selections(
 454        &mut self,
 455        selections: &[Selection<Anchor>],
 456        cx: &mut ModelContext<Self>,
 457    ) {
 458        let mut selections_by_buffer: HashMap<usize, Vec<Selection<text::Anchor>>> =
 459            Default::default();
 460        let snapshot = self.read(cx);
 461        let mut cursor = snapshot.excerpts.cursor::<Option<&ExcerptId>>();
 462        for selection in selections {
 463            cursor.seek(&Some(&selection.start.excerpt_id), Bias::Left, &());
 464            while let Some(excerpt) = cursor.item() {
 465                if excerpt.id > selection.end.excerpt_id {
 466                    break;
 467                }
 468
 469                let mut start = excerpt.range.start.clone();
 470                let mut end = excerpt.range.end.clone();
 471                if excerpt.id == selection.start.excerpt_id {
 472                    start = selection.start.text_anchor.clone();
 473                }
 474                if excerpt.id == selection.end.excerpt_id {
 475                    end = selection.end.text_anchor.clone();
 476                }
 477                selections_by_buffer
 478                    .entry(excerpt.buffer_id)
 479                    .or_default()
 480                    .push(Selection {
 481                        id: selection.id,
 482                        start,
 483                        end,
 484                        reversed: selection.reversed,
 485                        goal: selection.goal,
 486                    });
 487
 488                cursor.next(&());
 489            }
 490        }
 491
 492        for (buffer_id, mut selections) in selections_by_buffer {
 493            self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
 494                selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer).unwrap());
 495                let mut selections = selections.into_iter().peekable();
 496                let merged_selections = Arc::from_iter(iter::from_fn(|| {
 497                    let mut selection = selections.next()?;
 498                    while let Some(next_selection) = selections.peek() {
 499                        if selection
 500                            .end
 501                            .cmp(&next_selection.start, buffer)
 502                            .unwrap()
 503                            .is_ge()
 504                        {
 505                            let next_selection = selections.next().unwrap();
 506                            if next_selection
 507                                .end
 508                                .cmp(&selection.end, buffer)
 509                                .unwrap()
 510                                .is_ge()
 511                            {
 512                                selection.end = next_selection.end;
 513                            }
 514                        } else {
 515                            break;
 516                        }
 517                    }
 518                    Some(selection)
 519                }));
 520                buffer.set_active_selections(merged_selections, cx);
 521            });
 522        }
 523    }
 524
 525    pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
 526        for buffer in self.buffers.values() {
 527            buffer
 528                .buffer
 529                .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
 530        }
 531    }
 532
 533    pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
 534        if let Some(buffer) = self.as_singleton() {
 535            return buffer.update(cx, |buffer, cx| buffer.undo(cx));
 536        }
 537
 538        while let Some(transaction) = self.history.pop_undo() {
 539            let mut undone = false;
 540            for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
 541                if let Some(BufferState { buffer, .. }) = self.buffers.get(&buffer_id) {
 542                    undone |= buffer.update(cx, |buf, cx| {
 543                        buf.undo_transaction(*buffer_transaction_id, cx)
 544                    });
 545                }
 546            }
 547
 548            if undone {
 549                return Some(transaction.id);
 550            }
 551        }
 552
 553        None
 554    }
 555
 556    pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
 557        if let Some(buffer) = self.as_singleton() {
 558            return buffer.update(cx, |buffer, cx| buffer.redo(cx));
 559        }
 560
 561        while let Some(transaction) = self.history.pop_redo() {
 562            let mut redone = false;
 563            for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
 564                if let Some(BufferState { buffer, .. }) = self.buffers.get(&buffer_id) {
 565                    redone |= buffer.update(cx, |buf, cx| {
 566                        buf.redo_transaction(*buffer_transaction_id, cx)
 567                    });
 568                }
 569            }
 570
 571            if redone {
 572                return Some(transaction.id);
 573            }
 574        }
 575
 576        None
 577    }
 578
 579    pub fn push_excerpt<O>(
 580        &mut self,
 581        props: ExcerptProperties<O>,
 582        cx: &mut ModelContext<Self>,
 583    ) -> ExcerptId
 584    where
 585        O: text::ToOffset,
 586    {
 587        assert_eq!(self.history.transaction_depth, 0);
 588        self.sync(cx);
 589
 590        let buffer = props.buffer.clone();
 591        cx.observe(&buffer, |_, _, cx| cx.notify()).detach();
 592        cx.subscribe(&buffer, Self::on_buffer_event).detach();
 593
 594        let buffer_snapshot = buffer.read(cx).snapshot();
 595        let range = buffer_snapshot.anchor_before(&props.range.start)
 596            ..buffer_snapshot.anchor_after(&props.range.end);
 597        let last_version = buffer_snapshot.version().clone();
 598        let last_parse_count = buffer_snapshot.parse_count();
 599        let last_diagnostics_update_count = buffer_snapshot.diagnostics_update_count();
 600
 601        let mut snapshot = self.snapshot.borrow_mut();
 602        let mut prev_id = None;
 603        let edit_start = snapshot.excerpts.summary().text.bytes;
 604        snapshot.excerpts.update_last(
 605            |excerpt| {
 606                excerpt.has_trailing_newline = true;
 607                excerpt.text_summary += TextSummary::from("\n");
 608                prev_id = Some(excerpt.id.clone());
 609            },
 610            &(),
 611        );
 612
 613        let id = ExcerptId::between(&prev_id.unwrap_or(ExcerptId::min()), &ExcerptId::max());
 614        let excerpt = Excerpt::new(
 615            id.clone(),
 616            buffer.id(),
 617            buffer_snapshot,
 618            range,
 619            props.header_height,
 620            props.render_header,
 621            false,
 622        );
 623        snapshot.excerpts.push(excerpt, &());
 624        self.buffers
 625            .entry(props.buffer.id())
 626            .or_insert_with(|| BufferState {
 627                buffer,
 628                last_version,
 629                last_parse_count,
 630                last_diagnostics_update_count,
 631                excerpts: Default::default(),
 632            })
 633            .excerpts
 634            .push(id.clone());
 635        self.subscriptions.publish_mut([Edit {
 636            old: edit_start..edit_start,
 637            new: edit_start..snapshot.excerpts.summary().text.bytes,
 638        }]);
 639
 640        cx.notify();
 641
 642        id
 643    }
 644
 645    fn on_buffer_event(
 646        &mut self,
 647        _: ModelHandle<Buffer>,
 648        event: &Event,
 649        cx: &mut ModelContext<Self>,
 650    ) {
 651        cx.emit(event.clone());
 652    }
 653
 654    pub fn save(
 655        &mut self,
 656        cx: &mut ModelContext<Self>,
 657    ) -> Result<Task<Result<(clock::Global, SystemTime)>>> {
 658        self.as_singleton()
 659            .unwrap()
 660            .update(cx, |buffer, cx| buffer.save(cx))
 661    }
 662
 663    pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
 664        self.buffers
 665            .values()
 666            .next()
 667            .and_then(|state| state.buffer.read(cx).language())
 668    }
 669
 670    pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
 671        self.as_singleton().unwrap().read(cx).file()
 672    }
 673
 674    pub fn is_dirty(&self, cx: &AppContext) -> bool {
 675        self.as_singleton().unwrap().read(cx).is_dirty()
 676    }
 677
 678    pub fn has_conflict(&self, cx: &AppContext) -> bool {
 679        self.as_singleton().unwrap().read(cx).has_conflict()
 680    }
 681
 682    pub fn is_parsing(&self, cx: &AppContext) -> bool {
 683        self.as_singleton().unwrap().read(cx).is_parsing()
 684    }
 685
 686    fn sync(&self, cx: &AppContext) {
 687        let mut snapshot = self.snapshot.borrow_mut();
 688        let mut excerpts_to_edit = Vec::new();
 689        let mut reparsed = false;
 690        let mut diagnostics_updated = false;
 691        for buffer_state in self.buffers.values() {
 692            let buffer = buffer_state.buffer.read(cx);
 693            let buffer_edited = buffer.version().gt(&buffer_state.last_version);
 694            let buffer_reparsed = buffer.parse_count() > buffer_state.last_parse_count;
 695            let buffer_diagnostics_updated =
 696                buffer.diagnostics_update_count() > buffer_state.last_diagnostics_update_count;
 697            if buffer_edited || buffer_reparsed || buffer_diagnostics_updated {
 698                excerpts_to_edit.extend(
 699                    buffer_state
 700                        .excerpts
 701                        .iter()
 702                        .map(|excerpt_id| (excerpt_id, buffer_state, buffer_edited)),
 703                );
 704            }
 705
 706            reparsed |= buffer_reparsed;
 707            diagnostics_updated |= buffer_diagnostics_updated;
 708        }
 709        if reparsed {
 710            snapshot.parse_count += 1;
 711        }
 712        if diagnostics_updated {
 713            snapshot.diagnostics_update_count += 1;
 714        }
 715        excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _, _)| *excerpt_id);
 716
 717        let mut edits = Vec::new();
 718        let mut new_excerpts = SumTree::new();
 719        let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
 720
 721        for (id, buffer_state, buffer_edited) in excerpts_to_edit {
 722            new_excerpts.push_tree(cursor.slice(&Some(id), Bias::Left, &()), &());
 723            let old_excerpt = cursor.item().unwrap();
 724            let buffer = buffer_state.buffer.read(cx);
 725
 726            let mut new_excerpt;
 727            if buffer_edited {
 728                edits.extend(
 729                    buffer
 730                        .edits_since_in_range::<usize>(
 731                            old_excerpt.buffer.version(),
 732                            old_excerpt.range.clone(),
 733                        )
 734                        .map(|mut edit| {
 735                            let excerpt_old_start =
 736                                cursor.start().1 + old_excerpt.header_height as usize;
 737                            let excerpt_new_start = new_excerpts.summary().text.bytes
 738                                + old_excerpt.header_height as usize;
 739                            edit.old.start += excerpt_old_start;
 740                            edit.old.end += excerpt_old_start;
 741                            edit.new.start += excerpt_new_start;
 742                            edit.new.end += excerpt_new_start;
 743                            edit
 744                        }),
 745                );
 746
 747                new_excerpt = Excerpt::new(
 748                    id.clone(),
 749                    buffer_state.buffer.id(),
 750                    buffer.snapshot(),
 751                    old_excerpt.range.clone(),
 752                    old_excerpt.header_height,
 753                    old_excerpt.render_header.clone(),
 754                    old_excerpt.has_trailing_newline,
 755                );
 756            } else {
 757                new_excerpt = old_excerpt.clone();
 758                new_excerpt.buffer = buffer.snapshot();
 759            }
 760
 761            new_excerpts.push(new_excerpt, &());
 762            cursor.next(&());
 763        }
 764        new_excerpts.push_tree(cursor.suffix(&()), &());
 765
 766        drop(cursor);
 767        snapshot.excerpts = new_excerpts;
 768
 769        self.subscriptions.publish(edits);
 770    }
 771}
 772
 773#[cfg(any(test, feature = "test-support"))]
 774impl MultiBuffer {
 775    pub fn randomly_edit(
 776        &mut self,
 777        rng: &mut impl rand::Rng,
 778        count: usize,
 779        cx: &mut ModelContext<Self>,
 780    ) {
 781        use text::RandomCharIter;
 782
 783        let snapshot = self.read(cx);
 784        let mut old_ranges: Vec<Range<usize>> = Vec::new();
 785        for _ in 0..count {
 786            let last_end = old_ranges.last().map_or(0, |last_range| last_range.end + 1);
 787            if last_end > snapshot.len() {
 788                break;
 789            }
 790            let end_ix = snapshot.clip_offset(rng.gen_range(0..=last_end), Bias::Right);
 791            let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
 792            old_ranges.push(start_ix..end_ix);
 793        }
 794        let new_text_len = rng.gen_range(0..10);
 795        let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
 796        log::info!("mutating multi-buffer at {:?}: {:?}", old_ranges, new_text);
 797        drop(snapshot);
 798
 799        self.edit(old_ranges.iter().cloned(), new_text.as_str(), cx);
 800    }
 801}
 802
 803impl Entity for MultiBuffer {
 804    type Event = language::Event;
 805}
 806
 807impl MultiBufferSnapshot {
 808    pub fn text(&self) -> String {
 809        self.chunks(0..self.len(), None)
 810            .map(|chunk| chunk.text)
 811            .collect()
 812    }
 813
 814    pub fn excerpt_headers_in_range<'a>(
 815        &'a self,
 816        range: Range<u32>,
 817    ) -> impl 'a + Iterator<Item = (Range<u32>, RenderHeaderFn)> {
 818        let mut cursor = self.excerpts.cursor::<Point>();
 819        cursor.seek(&Point::new(range.start, 0), Bias::Right, &());
 820
 821        if let Some(excerpt) = cursor.item() {
 822            if range.start >= cursor.start().row + excerpt.header_height as u32 {
 823                cursor.next(&());
 824            }
 825        }
 826
 827        iter::from_fn(move || {
 828            while let Some(excerpt) = cursor.item() {
 829                if cursor.start().row >= range.end {
 830                    break;
 831                }
 832
 833                if let Some(render) = excerpt.render_header.clone() {
 834                    let start = cursor.start().row;
 835                    let end = start + excerpt.header_height as u32;
 836                    cursor.next(&());
 837                    return Some((start..end, render));
 838                } else {
 839                    cursor.next(&());
 840                }
 841            }
 842            None
 843        })
 844    }
 845
 846    pub fn reversed_chars_at<'a, T: ToOffset>(
 847        &'a self,
 848        position: T,
 849    ) -> impl Iterator<Item = char> + 'a {
 850        let mut offset = position.to_offset(self);
 851        let mut cursor = self.excerpts.cursor::<usize>();
 852        cursor.seek(&offset, Bias::Left, &());
 853        let mut excerpt_chunks = cursor.item().map(|excerpt| {
 854            let start_after_header = cursor.start() + excerpt.header_height as usize;
 855            let mut end_before_footer = cursor.start() + excerpt.text_summary.bytes;
 856            if excerpt.has_trailing_newline {
 857                end_before_footer -= 1;
 858            }
 859
 860            let start = excerpt.range.start.to_offset(&excerpt.buffer);
 861            let end =
 862                start + (cmp::min(offset, end_before_footer).saturating_sub(start_after_header));
 863            excerpt.buffer.reversed_chunks_in_range(start..end)
 864        });
 865        iter::from_fn(move || {
 866            if offset == *cursor.start() {
 867                cursor.prev(&());
 868                let excerpt = cursor.item()?;
 869                excerpt_chunks = Some(
 870                    excerpt
 871                        .buffer
 872                        .reversed_chunks_in_range(excerpt.range.clone()),
 873                );
 874            }
 875
 876            let excerpt = cursor.item().unwrap();
 877            if offset <= cursor.start() + excerpt.header_height as usize {
 878                let header_height = offset - cursor.start();
 879                offset -= header_height;
 880                Some(unsafe { str::from_utf8_unchecked(&NEWLINES[..header_height]) })
 881            } else if offset == cursor.end(&()) && excerpt.has_trailing_newline {
 882                offset -= 1;
 883                Some("\n")
 884            } else {
 885                let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap();
 886                offset -= chunk.len();
 887                Some(chunk)
 888            }
 889        })
 890        .flat_map(|c| c.chars().rev())
 891    }
 892
 893    pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
 894        let offset = position.to_offset(self);
 895        self.text_for_range(offset..self.len())
 896            .flat_map(|chunk| chunk.chars())
 897    }
 898
 899    pub fn text_for_range<'a, T: ToOffset>(
 900        &'a self,
 901        range: Range<T>,
 902    ) -> impl Iterator<Item = &'a str> {
 903        self.chunks(range, None).map(|chunk| chunk.text)
 904    }
 905
 906    pub fn is_line_blank(&self, row: u32) -> bool {
 907        self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
 908            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
 909    }
 910
 911    pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
 912    where
 913        T: ToOffset,
 914    {
 915        let position = position.to_offset(self);
 916        position == self.clip_offset(position, Bias::Left)
 917            && self
 918                .bytes_in_range(position..self.len())
 919                .flatten()
 920                .copied()
 921                .take(needle.len())
 922                .eq(needle.bytes())
 923    }
 924
 925    fn as_singleton(&self) -> Option<&BufferSnapshot> {
 926        let mut excerpts = self.excerpts.iter();
 927        let buffer = excerpts.next().map(|excerpt| &excerpt.buffer);
 928        if excerpts.next().is_none() {
 929            buffer
 930        } else {
 931            None
 932        }
 933    }
 934
 935    pub fn len(&self) -> usize {
 936        self.excerpts.summary().text.bytes
 937    }
 938
 939    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 940        let mut cursor = self.excerpts.cursor::<usize>();
 941        cursor.seek(&offset, Bias::Right, &());
 942        if let Some(excerpt) = cursor.item() {
 943            let header_end = *cursor.start() + excerpt.header_height as usize;
 944            if offset < header_end {
 945                header_end
 946            } else {
 947                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 948                let buffer_offset = excerpt
 949                    .buffer
 950                    .clip_offset(excerpt_start + (offset - header_end), bias);
 951                let offset_in_excerpt = if buffer_offset > excerpt_start {
 952                    buffer_offset - excerpt_start
 953                } else {
 954                    0
 955                };
 956                header_end + offset_in_excerpt
 957            }
 958        } else {
 959            self.excerpts.summary().text.bytes
 960        }
 961    }
 962
 963    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 964        let mut cursor = self.excerpts.cursor::<Point>();
 965        cursor.seek(&point, Bias::Right, &());
 966        if let Some(excerpt) = cursor.item() {
 967            let header_end = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
 968            if point < header_end {
 969                header_end
 970            } else {
 971                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 972                let buffer_point = excerpt
 973                    .buffer
 974                    .clip_point(excerpt_start + (point - header_end), bias);
 975                let point_in_excerpt = if buffer_point > excerpt_start {
 976                    buffer_point - excerpt_start
 977                } else {
 978                    Point::zero()
 979                };
 980                header_end + point_in_excerpt
 981            }
 982        } else {
 983            self.excerpts.summary().text.lines
 984        }
 985    }
 986
 987    pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
 988        let mut cursor = self.excerpts.cursor::<PointUtf16>();
 989        cursor.seek(&point, Bias::Right, &());
 990        if let Some(excerpt) = cursor.item() {
 991            let header_end = *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
 992            if point < header_end {
 993                header_end
 994            } else {
 995                let excerpt_start = excerpt
 996                    .buffer
 997                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 998                let buffer_point = excerpt
 999                    .buffer
1000                    .clip_point_utf16(excerpt_start + (point - header_end), bias);
1001                let point_in_excerpt = if buffer_point > excerpt_start {
1002                    buffer_point - excerpt_start
1003                } else {
1004                    PointUtf16::new(0, 0)
1005                };
1006                header_end + point_in_excerpt
1007            }
1008        } else {
1009            self.excerpts.summary().text.lines_utf16
1010        }
1011    }
1012
1013    pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
1014        let range = range.start.to_offset(self)..range.end.to_offset(self);
1015        let mut excerpts = self.excerpts.cursor::<usize>();
1016        excerpts.seek(&range.start, Bias::Right, &());
1017
1018        let mut chunk = &[][..];
1019        let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
1020            let mut excerpt_bytes = excerpt.bytes_in_range(
1021                range.start - excerpts.start()
1022                    ..cmp::min(range.end - excerpts.start(), excerpt.text_summary.bytes),
1023            );
1024            chunk = excerpt_bytes.next().unwrap_or(&[][..]);
1025            Some(excerpt_bytes)
1026        } else {
1027            None
1028        };
1029
1030        MultiBufferBytes {
1031            range,
1032            excerpts,
1033            excerpt_bytes,
1034            chunk,
1035        }
1036    }
1037
1038    pub fn chunks<'a, T: ToOffset>(
1039        &'a self,
1040        range: Range<T>,
1041        theme: Option<&'a SyntaxTheme>,
1042    ) -> MultiBufferChunks<'a> {
1043        let range = range.start.to_offset(self)..range.end.to_offset(self);
1044        let mut chunks = MultiBufferChunks {
1045            range: range.clone(),
1046            excerpts: self.excerpts.cursor(),
1047            excerpt_chunks: None,
1048            theme,
1049        };
1050        chunks.seek(range.start);
1051        chunks
1052    }
1053
1054    pub fn offset_to_point(&self, offset: usize) -> Point {
1055        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
1056        cursor.seek(&offset, Bias::Right, &());
1057        if let Some(excerpt) = cursor.item() {
1058            let (start_offset, start_point) = cursor.start();
1059            let overshoot = offset - start_offset;
1060            let header_height = excerpt.header_height as usize;
1061            if overshoot < header_height {
1062                *start_point + Point::new(overshoot as u32, 0)
1063            } else {
1064                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1065                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
1066                let buffer_point = excerpt
1067                    .buffer
1068                    .offset_to_point(excerpt_start_offset + (overshoot - header_height));
1069                *start_point
1070                    + Point::new(header_height as u32, 0)
1071                    + (buffer_point - excerpt_start_point)
1072            }
1073        } else {
1074            self.excerpts.summary().text.lines
1075        }
1076    }
1077
1078    pub fn point_to_offset(&self, point: Point) -> usize {
1079        let mut cursor = self.excerpts.cursor::<(Point, usize)>();
1080        cursor.seek(&point, Bias::Right, &());
1081        if let Some(excerpt) = cursor.item() {
1082            let (start_point, start_offset) = cursor.start();
1083            let overshoot = point - start_point;
1084            let header_height = Point::new(excerpt.header_height as u32, 0);
1085            if overshoot < header_height {
1086                start_offset + overshoot.row as usize
1087            } else {
1088                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1089                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
1090                let buffer_offset = excerpt
1091                    .buffer
1092                    .point_to_offset(excerpt_start_point + (overshoot - header_height));
1093                *start_offset + excerpt.header_height as usize + buffer_offset
1094                    - excerpt_start_offset
1095            }
1096        } else {
1097            self.excerpts.summary().text.bytes
1098        }
1099    }
1100
1101    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
1102        let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
1103        cursor.seek(&point, Bias::Right, &());
1104        if let Some(excerpt) = cursor.item() {
1105            let (start_point, start_offset) = cursor.start();
1106            let overshoot = point - start_point;
1107            let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
1108            if overshoot < header_height {
1109                start_offset + overshoot.row as usize
1110            } else {
1111                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1112                let excerpt_start_point = excerpt
1113                    .buffer
1114                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
1115                let buffer_offset = excerpt
1116                    .buffer
1117                    .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
1118                *start_offset
1119                    + excerpt.header_height as usize
1120                    + (buffer_offset - excerpt_start_offset)
1121            }
1122        } else {
1123            self.excerpts.summary().text.bytes
1124        }
1125    }
1126
1127    pub fn indent_column_for_line(&self, row: u32) -> u32 {
1128        if let Some((buffer, range)) = self.buffer_line_for_row(row) {
1129            buffer
1130                .indent_column_for_line(range.start.row)
1131                .min(range.end.column)
1132                .saturating_sub(range.start.column)
1133        } else {
1134            0
1135        }
1136    }
1137
1138    pub fn line_len(&self, row: u32) -> u32 {
1139        if let Some((_, range)) = self.buffer_line_for_row(row) {
1140            range.end.column - range.start.column
1141        } else {
1142            0
1143        }
1144    }
1145
1146    fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
1147        let mut cursor = self.excerpts.cursor::<Point>();
1148        cursor.seek(&Point::new(row, 0), Bias::Right, &());
1149        if let Some(excerpt) = cursor.item() {
1150            let overshoot = row - cursor.start().row;
1151            let header_height = excerpt.header_height as u32;
1152            if overshoot >= header_height {
1153                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
1154                let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
1155                let buffer_row = excerpt_start.row + overshoot - header_height;
1156                let line_start = Point::new(buffer_row, 0);
1157                let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
1158                return Some((
1159                    &excerpt.buffer,
1160                    line_start.max(excerpt_start)..line_end.min(excerpt_end),
1161                ));
1162            }
1163        }
1164        None
1165    }
1166
1167    pub fn max_point(&self) -> Point {
1168        self.text_summary().lines
1169    }
1170
1171    pub fn text_summary(&self) -> TextSummary {
1172        self.excerpts.summary().text
1173    }
1174
1175    pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
1176    where
1177        D: TextDimension,
1178        O: ToOffset,
1179    {
1180        let mut summary = D::default();
1181        let mut range = range.start.to_offset(self)..range.end.to_offset(self);
1182        let mut cursor = self.excerpts.cursor::<usize>();
1183        cursor.seek(&range.start, Bias::Right, &());
1184        if let Some(excerpt) = cursor.item() {
1185            let start_after_header = cursor.start() + excerpt.header_height as usize;
1186            if range.start < start_after_header {
1187                let header_len = cmp::min(range.end, start_after_header) - range.start;
1188                summary.add_assign(&D::from_text_summary(&TextSummary {
1189                    bytes: header_len,
1190                    lines: Point::new(header_len as u32, 0),
1191                    lines_utf16: PointUtf16::new(header_len as u32, 0),
1192                    first_line_chars: 0,
1193                    last_line_chars: 0,
1194                    longest_row: 0,
1195                    longest_row_chars: 0,
1196                }));
1197                range.start = start_after_header;
1198                range.end = cmp::max(range.start, range.end);
1199            }
1200
1201            let mut end_before_newline = cursor.end(&());
1202            if excerpt.has_trailing_newline {
1203                end_before_newline -= 1;
1204            }
1205
1206            let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1207            let start_in_excerpt = excerpt_start + (range.start - start_after_header);
1208            let end_in_excerpt =
1209                excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
1210            summary.add_assign(
1211                &excerpt
1212                    .buffer
1213                    .text_summary_for_range(start_in_excerpt..end_in_excerpt),
1214            );
1215
1216            if range.end > end_before_newline {
1217                summary.add_assign(&D::from_text_summary(&TextSummary {
1218                    bytes: 1,
1219                    lines: Point::new(1 as u32, 0),
1220                    lines_utf16: PointUtf16::new(1 as u32, 0),
1221                    first_line_chars: 0,
1222                    last_line_chars: 0,
1223                    longest_row: 0,
1224                    longest_row_chars: 0,
1225                }));
1226            }
1227
1228            cursor.next(&());
1229        }
1230
1231        if range.end > *cursor.start() {
1232            summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
1233                &range.end,
1234                Bias::Right,
1235                &(),
1236            )));
1237            if let Some(excerpt) = cursor.item() {
1238                let start_after_header = cursor.start() + excerpt.header_height as usize;
1239                let header_len =
1240                    cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
1241                summary.add_assign(&D::from_text_summary(&TextSummary {
1242                    bytes: header_len,
1243                    lines: Point::new(header_len as u32, 0),
1244                    lines_utf16: PointUtf16::new(header_len as u32, 0),
1245                    first_line_chars: 0,
1246                    last_line_chars: 0,
1247                    longest_row: 0,
1248                    longest_row_chars: 0,
1249                }));
1250                range.end = cmp::max(start_after_header, range.end);
1251
1252                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1253                let end_in_excerpt = excerpt_start + (range.end - start_after_header);
1254                summary.add_assign(
1255                    &excerpt
1256                        .buffer
1257                        .text_summary_for_range(excerpt_start..end_in_excerpt),
1258                );
1259                cursor.next(&());
1260            }
1261        }
1262
1263        summary
1264    }
1265
1266    pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
1267    where
1268        D: TextDimension + Ord + Sub<D, Output = D>,
1269    {
1270        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1271        cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
1272        if let Some(excerpt) = cursor.item() {
1273            if excerpt.id == anchor.excerpt_id {
1274                let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1275                excerpt_start.add_summary(&excerpt.header_summary(), &());
1276                let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1277                let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
1278                if buffer_point > excerpt_buffer_start {
1279                    excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
1280                }
1281                return excerpt_start;
1282            }
1283        }
1284        D::from_text_summary(&cursor.start().text)
1285    }
1286
1287    pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
1288    where
1289        D: TextDimension + Ord + Sub<D, Output = D>,
1290        I: 'a + IntoIterator<Item = &'a Anchor>,
1291    {
1292        let mut anchors = anchors.into_iter().peekable();
1293        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1294        let mut summaries = Vec::new();
1295        while let Some(anchor) = anchors.peek() {
1296            let excerpt_id = &anchor.excerpt_id;
1297            let excerpt_anchors = iter::from_fn(|| {
1298                let anchor = anchors.peek()?;
1299                if anchor.excerpt_id == *excerpt_id {
1300                    Some(&anchors.next().unwrap().text_anchor)
1301                } else {
1302                    None
1303                }
1304            });
1305
1306            cursor.seek_forward(&Some(excerpt_id), Bias::Left, &());
1307            if let Some(excerpt) = cursor.item() {
1308                if excerpt.id == *excerpt_id {
1309                    let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1310                    excerpt_start.add_summary(&excerpt.header_summary(), &());
1311                    let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1312                    summaries.extend(
1313                        excerpt
1314                            .buffer
1315                            .summaries_for_anchors::<D, _>(excerpt_anchors)
1316                            .map(move |summary| {
1317                                let mut excerpt_start = excerpt_start.clone();
1318                                let excerpt_buffer_start = excerpt_buffer_start.clone();
1319                                if summary > excerpt_buffer_start {
1320                                    excerpt_start.add_assign(&(summary - excerpt_buffer_start));
1321                                }
1322                                excerpt_start
1323                            }),
1324                    );
1325                    continue;
1326                }
1327            }
1328
1329            let summary = D::from_text_summary(&cursor.start().text);
1330            summaries.extend(excerpt_anchors.map(|_| summary.clone()));
1331        }
1332
1333        summaries
1334    }
1335
1336    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
1337        self.anchor_at(position, Bias::Left)
1338    }
1339
1340    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
1341        self.anchor_at(position, Bias::Right)
1342    }
1343
1344    pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
1345        let offset = position.to_offset(self);
1346        let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
1347        cursor.seek(&offset, Bias::Right, &());
1348        if cursor.item().is_none() && offset == cursor.start().0 && bias == Bias::Left {
1349            cursor.prev(&());
1350        }
1351        if let Some(excerpt) = cursor.item() {
1352            let start_after_header = cursor.start().0 + excerpt.header_height as usize;
1353            let mut overshoot = offset.saturating_sub(start_after_header);
1354            if excerpt.has_trailing_newline && offset == cursor.end(&()).0 {
1355                overshoot -= 1;
1356                bias = Bias::Right;
1357            }
1358
1359            let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1360            let text_anchor =
1361                excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
1362            Anchor {
1363                excerpt_id: excerpt.id.clone(),
1364                text_anchor,
1365            }
1366        } else if offset == 0 && bias == Bias::Left {
1367            Anchor::min()
1368        } else {
1369            Anchor::max()
1370        }
1371    }
1372
1373    pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor {
1374        let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1375        cursor.seek(&Some(&excerpt_id), Bias::Left, &());
1376        if let Some(excerpt) = cursor.item() {
1377            if excerpt.id == excerpt_id {
1378                let text_anchor = excerpt.clip_anchor(text_anchor);
1379                drop(cursor);
1380                return Anchor {
1381                    excerpt_id,
1382                    text_anchor,
1383                };
1384            }
1385        }
1386        panic!("excerpt not found");
1387    }
1388
1389    pub fn parse_count(&self) -> usize {
1390        self.parse_count
1391    }
1392
1393    pub fn enclosing_bracket_ranges<T: ToOffset>(
1394        &self,
1395        range: Range<T>,
1396    ) -> Option<(Range<usize>, Range<usize>)> {
1397        let range = range.start.to_offset(self)..range.end.to_offset(self);
1398        self.as_singleton().unwrap().enclosing_bracket_ranges(range)
1399    }
1400
1401    pub fn diagnostics_update_count(&self) -> usize {
1402        self.diagnostics_update_count
1403    }
1404
1405    pub fn language(&self) -> Option<&Arc<Language>> {
1406        self.excerpts
1407            .iter()
1408            .next()
1409            .and_then(|excerpt| excerpt.buffer.language())
1410    }
1411
1412    pub fn diagnostic_group<'a, O>(
1413        &'a self,
1414        group_id: usize,
1415    ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1416    where
1417        O: text::FromAnchor + 'a,
1418    {
1419        self.as_singleton().unwrap().diagnostic_group(group_id)
1420    }
1421
1422    pub fn diagnostics_in_range<'a, T, O>(
1423        &'a self,
1424        range: Range<T>,
1425    ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1426    where
1427        T: 'a + ToOffset,
1428        O: 'a + text::FromAnchor,
1429    {
1430        let range = range.start.to_offset(self)..range.end.to_offset(self);
1431        self.as_singleton().unwrap().diagnostics_in_range(range)
1432    }
1433
1434    pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1435        let range = range.start.to_offset(self)..range.end.to_offset(self);
1436        self.as_singleton()
1437            .unwrap()
1438            .range_for_syntax_ancestor(range)
1439    }
1440
1441    fn buffer_snapshot_for_excerpt<'a>(
1442        &'a self,
1443        excerpt_id: &'a ExcerptId,
1444    ) -> Option<&'a BufferSnapshot> {
1445        let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1446        cursor.seek(&Some(excerpt_id), Bias::Left, &());
1447        if let Some(excerpt) = cursor.item() {
1448            if excerpt.id == *excerpt_id {
1449                return Some(&excerpt.buffer);
1450            }
1451        }
1452        None
1453    }
1454
1455    pub fn remote_selections_in_range<'a>(
1456        &'a self,
1457        range: &'a Range<Anchor>,
1458    ) -> impl 'a + Iterator<Item = (ReplicaId, Selection<Anchor>)> {
1459        let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1460        cursor.seek(&Some(&range.start.excerpt_id), Bias::Left, &());
1461        cursor
1462            .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id)
1463            .flat_map(move |excerpt| {
1464                let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone();
1465                if excerpt.id == range.start.excerpt_id {
1466                    query_range.start = range.start.text_anchor.clone();
1467                }
1468                if excerpt.id == range.end.excerpt_id {
1469                    query_range.end = range.end.text_anchor.clone();
1470                }
1471
1472                excerpt
1473                    .buffer
1474                    .remote_selections_in_range(query_range)
1475                    .flat_map(move |(replica_id, selections)| {
1476                        selections.map(move |selection| {
1477                            let mut start = Anchor {
1478                                excerpt_id: excerpt.id.clone(),
1479                                text_anchor: selection.start.clone(),
1480                            };
1481                            let mut end = Anchor {
1482                                excerpt_id: excerpt.id.clone(),
1483                                text_anchor: selection.end.clone(),
1484                            };
1485                            if range.start.cmp(&start, self).unwrap().is_gt() {
1486                                start = range.start.clone();
1487                            }
1488                            if range.end.cmp(&end, self).unwrap().is_lt() {
1489                                end = range.end.clone();
1490                            }
1491
1492                            (
1493                                replica_id,
1494                                Selection {
1495                                    id: selection.id,
1496                                    start,
1497                                    end,
1498                                    reversed: selection.reversed,
1499                                    goal: selection.goal,
1500                                },
1501                            )
1502                        })
1503                    })
1504            })
1505    }
1506}
1507
1508impl History {
1509    fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
1510        self.transaction_depth += 1;
1511        if self.transaction_depth == 1 {
1512            let id = post_inc(&mut self.next_transaction_id);
1513            self.undo_stack.push(Transaction {
1514                id,
1515                buffer_transactions: Default::default(),
1516                first_edit_at: now,
1517                last_edit_at: now,
1518            });
1519            Some(id)
1520        } else {
1521            None
1522        }
1523    }
1524
1525    fn end_transaction(
1526        &mut self,
1527        now: Instant,
1528        buffer_transactions: HashSet<(usize, TransactionId)>,
1529    ) -> bool {
1530        assert_ne!(self.transaction_depth, 0);
1531        self.transaction_depth -= 1;
1532        if self.transaction_depth == 0 {
1533            if buffer_transactions.is_empty() {
1534                self.undo_stack.pop();
1535                false
1536            } else {
1537                let transaction = self.undo_stack.last_mut().unwrap();
1538                transaction.last_edit_at = now;
1539                transaction.buffer_transactions.extend(buffer_transactions);
1540                true
1541            }
1542        } else {
1543            false
1544        }
1545    }
1546
1547    fn pop_undo(&mut self) -> Option<&Transaction> {
1548        assert_eq!(self.transaction_depth, 0);
1549        if let Some(transaction) = self.undo_stack.pop() {
1550            self.redo_stack.push(transaction);
1551            self.redo_stack.last()
1552        } else {
1553            None
1554        }
1555    }
1556
1557    fn pop_redo(&mut self) -> Option<&Transaction> {
1558        assert_eq!(self.transaction_depth, 0);
1559        if let Some(transaction) = self.redo_stack.pop() {
1560            self.undo_stack.push(transaction);
1561            self.undo_stack.last()
1562        } else {
1563            None
1564        }
1565    }
1566
1567    fn group(&mut self) -> Option<TransactionId> {
1568        let mut new_len = self.undo_stack.len();
1569        let mut transactions = self.undo_stack.iter_mut();
1570
1571        if let Some(mut transaction) = transactions.next_back() {
1572            while let Some(prev_transaction) = transactions.next_back() {
1573                if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval
1574                {
1575                    transaction = prev_transaction;
1576                    new_len -= 1;
1577                } else {
1578                    break;
1579                }
1580            }
1581        }
1582
1583        let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
1584        if let Some(last_transaction) = transactions_to_keep.last_mut() {
1585            if let Some(transaction) = transactions_to_merge.last() {
1586                last_transaction.last_edit_at = transaction.last_edit_at;
1587            }
1588        }
1589
1590        self.undo_stack.truncate(new_len);
1591        self.undo_stack.last().map(|t| t.id)
1592    }
1593}
1594
1595impl Excerpt {
1596    fn new(
1597        id: ExcerptId,
1598        buffer_id: usize,
1599        buffer: BufferSnapshot,
1600        range: Range<text::Anchor>,
1601        header_height: u8,
1602        render_header: Option<RenderHeaderFn>,
1603        has_trailing_newline: bool,
1604    ) -> Self {
1605        let mut text_summary =
1606            buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1607        if header_height > 0 {
1608            text_summary.first_line_chars = 0;
1609            text_summary.lines.row += header_height as u32;
1610            text_summary.lines_utf16.row += header_height as u32;
1611            text_summary.bytes += header_height as usize;
1612            text_summary.longest_row += header_height as u32;
1613        }
1614        if has_trailing_newline {
1615            text_summary.last_line_chars = 0;
1616            text_summary.lines.row += 1;
1617            text_summary.lines.column = 0;
1618            text_summary.lines_utf16.row += 1;
1619            text_summary.lines_utf16.column = 0;
1620            text_summary.bytes += 1;
1621        }
1622
1623        Excerpt {
1624            id,
1625            buffer_id,
1626            buffer,
1627            range,
1628            text_summary,
1629            header_height,
1630            render_header,
1631            has_trailing_newline,
1632        }
1633    }
1634
1635    fn header_summary(&self) -> TextSummary {
1636        TextSummary {
1637            bytes: self.header_height as usize,
1638            lines: Point::new(self.header_height as u32, 0),
1639            lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1640            first_line_chars: 0,
1641            last_line_chars: 0,
1642            longest_row: 0,
1643            longest_row_chars: 0,
1644        }
1645    }
1646
1647    fn chunks_in_range<'a>(
1648        &'a self,
1649        range: Range<usize>,
1650        theme: Option<&'a SyntaxTheme>,
1651    ) -> ExcerptChunks<'a> {
1652        let content_start = self.range.start.to_offset(&self.buffer);
1653        let chunks_start = content_start + range.start.saturating_sub(self.header_height as usize);
1654        let mut chunks_end = content_start
1655            + cmp::min(range.end, self.text_summary.bytes)
1656                .saturating_sub(self.header_height as usize);
1657
1658        let header_height = cmp::min(
1659            (self.header_height as usize).saturating_sub(range.start),
1660            range.len(),
1661        );
1662        let mut footer_height = 0;
1663        if self.has_trailing_newline && range.end == self.text_summary.bytes {
1664            chunks_end -= 1;
1665            if !range.is_empty() {
1666                footer_height = 1;
1667            }
1668        }
1669
1670        let content_chunks = self.buffer.chunks(chunks_start..chunks_end, theme);
1671
1672        ExcerptChunks {
1673            header_height,
1674            content_chunks,
1675            footer_height,
1676        }
1677    }
1678
1679    fn bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
1680        let content_start = self.range.start.to_offset(&self.buffer);
1681        let bytes_start = content_start + range.start.saturating_sub(self.header_height as usize);
1682        let mut bytes_end = content_start
1683            + cmp::min(range.end, self.text_summary.bytes)
1684                .saturating_sub(self.header_height as usize);
1685
1686        let header_height = cmp::min(
1687            (self.header_height as usize).saturating_sub(range.start),
1688            range.len(),
1689        );
1690        let mut footer_height = 0;
1691        if self.has_trailing_newline && range.end == self.text_summary.bytes {
1692            bytes_end -= 1;
1693            if !range.is_empty() {
1694                footer_height = 1;
1695            }
1696        }
1697
1698        let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end);
1699
1700        ExcerptBytes {
1701            header_height,
1702            content_bytes,
1703            footer_height,
1704        }
1705    }
1706
1707    fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
1708        if text_anchor
1709            .cmp(&self.range.start, &self.buffer)
1710            .unwrap()
1711            .is_lt()
1712        {
1713            self.range.start.clone()
1714        } else if text_anchor
1715            .cmp(&self.range.end, &self.buffer)
1716            .unwrap()
1717            .is_gt()
1718        {
1719            self.range.end.clone()
1720        } else {
1721            text_anchor
1722        }
1723    }
1724}
1725
1726impl fmt::Debug for Excerpt {
1727    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1728        f.debug_struct("Excerpt")
1729            .field("id", &self.id)
1730            .field("buffer_id", &self.buffer_id)
1731            .field("range", &self.range)
1732            .field("text_summary", &self.text_summary)
1733            .field("header_height", &self.header_height)
1734            .field("has_trailing_newline", &self.has_trailing_newline)
1735            .finish()
1736    }
1737}
1738
1739impl sum_tree::Item for Excerpt {
1740    type Summary = ExcerptSummary;
1741
1742    fn summary(&self) -> Self::Summary {
1743        ExcerptSummary {
1744            excerpt_id: self.id.clone(),
1745            text: self.text_summary.clone(),
1746        }
1747    }
1748}
1749
1750impl sum_tree::Summary for ExcerptSummary {
1751    type Context = ();
1752
1753    fn add_summary(&mut self, summary: &Self, _: &()) {
1754        debug_assert!(summary.excerpt_id > self.excerpt_id);
1755        self.excerpt_id = summary.excerpt_id.clone();
1756        self.text.add_summary(&summary.text, &());
1757    }
1758}
1759
1760impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1761    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1762        *self += &summary.text;
1763    }
1764}
1765
1766impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1767    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1768        *self += summary.text.bytes;
1769    }
1770}
1771
1772impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1773    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1774        Ord::cmp(self, &cursor_location.text.bytes)
1775    }
1776}
1777
1778impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1779    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1780        Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1781    }
1782}
1783
1784impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1785    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1786        *self += summary.text.lines;
1787    }
1788}
1789
1790impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1791    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1792        *self += summary.text.lines_utf16
1793    }
1794}
1795
1796impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1797    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1798        *self = Some(&summary.excerpt_id);
1799    }
1800}
1801
1802impl<'a> MultiBufferChunks<'a> {
1803    pub fn offset(&self) -> usize {
1804        self.range.start
1805    }
1806
1807    pub fn seek(&mut self, offset: usize) {
1808        self.range.start = offset;
1809        self.excerpts.seek(&offset, Bias::Right, &());
1810        if let Some(excerpt) = self.excerpts.item() {
1811            self.excerpt_chunks = Some(excerpt.chunks_in_range(
1812                self.range.start - self.excerpts.start()
1813                    ..cmp::min(
1814                        self.range.end - self.excerpts.start(),
1815                        excerpt.text_summary.bytes,
1816                    ),
1817                self.theme,
1818            ));
1819        } else {
1820            self.excerpt_chunks = None;
1821        }
1822    }
1823}
1824
1825impl<'a> Iterator for MultiBufferChunks<'a> {
1826    type Item = Chunk<'a>;
1827
1828    fn next(&mut self) -> Option<Self::Item> {
1829        if self.range.is_empty() {
1830            None
1831        } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
1832            self.range.start += chunk.text.len();
1833            Some(chunk)
1834        } else {
1835            self.excerpts.next(&());
1836            let excerpt = self.excerpts.item()?;
1837            self.excerpt_chunks = Some(excerpt.chunks_in_range(
1838                0..cmp::min(
1839                    self.range.end - self.excerpts.start(),
1840                    excerpt.text_summary.bytes,
1841                ),
1842                self.theme,
1843            ));
1844            self.next()
1845        }
1846    }
1847}
1848
1849impl<'a> MultiBufferBytes<'a> {
1850    fn consume(&mut self, len: usize) {
1851        self.range.start += len;
1852        self.chunk = &self.chunk[len..];
1853
1854        if !self.range.is_empty() && self.chunk.is_empty() {
1855            if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
1856                self.chunk = chunk;
1857            } else {
1858                self.excerpts.next(&());
1859                if let Some(excerpt) = self.excerpts.item() {
1860                    let mut excerpt_bytes = excerpt.bytes_in_range(
1861                        0..cmp::min(
1862                            self.range.end - self.excerpts.start(),
1863                            excerpt.text_summary.bytes,
1864                        ),
1865                    );
1866                    self.chunk = excerpt_bytes.next().unwrap();
1867                    self.excerpt_bytes = Some(excerpt_bytes);
1868                }
1869            }
1870        }
1871    }
1872}
1873
1874impl<'a> Iterator for MultiBufferBytes<'a> {
1875    type Item = &'a [u8];
1876
1877    fn next(&mut self) -> Option<Self::Item> {
1878        let chunk = self.chunk;
1879        if chunk.is_empty() {
1880            None
1881        } else {
1882            self.consume(chunk.len());
1883            Some(chunk)
1884        }
1885    }
1886}
1887
1888impl<'a> io::Read for MultiBufferBytes<'a> {
1889    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1890        let len = cmp::min(buf.len(), self.chunk.len());
1891        buf[..len].copy_from_slice(&self.chunk[..len]);
1892        if len > 0 {
1893            self.consume(len);
1894        }
1895        Ok(len)
1896    }
1897}
1898
1899impl<'a> Iterator for ExcerptBytes<'a> {
1900    type Item = &'a [u8];
1901
1902    fn next(&mut self) -> Option<Self::Item> {
1903        if self.header_height > 0 {
1904            let result = &NEWLINES[..self.header_height];
1905            self.header_height = 0;
1906            return Some(result);
1907        }
1908
1909        if let Some(chunk) = self.content_bytes.next() {
1910            if !chunk.is_empty() {
1911                return Some(chunk);
1912            }
1913        }
1914
1915        if self.footer_height > 0 {
1916            let result = &NEWLINES[..self.footer_height];
1917            self.footer_height = 0;
1918            return Some(result);
1919        }
1920
1921        None
1922    }
1923}
1924
1925impl<'a> Iterator for ExcerptChunks<'a> {
1926    type Item = Chunk<'a>;
1927
1928    fn next(&mut self) -> Option<Self::Item> {
1929        if self.header_height > 0 {
1930            let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.header_height]) };
1931            self.header_height = 0;
1932            return Some(Chunk {
1933                text,
1934                ..Default::default()
1935            });
1936        }
1937
1938        if let Some(chunk) = self.content_chunks.next() {
1939            if !chunk.text.is_empty() {
1940                return Some(chunk);
1941            }
1942        }
1943
1944        if self.footer_height > 0 {
1945            let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) };
1946            self.footer_height = 0;
1947            return Some(Chunk {
1948                text,
1949                ..Default::default()
1950            });
1951        }
1952
1953        None
1954    }
1955}
1956
1957impl ToOffset for Point {
1958    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1959        snapshot.point_to_offset(*self)
1960    }
1961}
1962
1963impl ToOffset for PointUtf16 {
1964    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1965        snapshot.point_utf16_to_offset(*self)
1966    }
1967}
1968
1969impl ToOffset for usize {
1970    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1971        assert!(*self <= snapshot.len(), "offset is out of range");
1972        *self
1973    }
1974}
1975
1976impl ToPoint for usize {
1977    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1978        snapshot.offset_to_point(*self)
1979    }
1980}
1981
1982impl ToPoint for Point {
1983    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1984        *self
1985    }
1986}
1987
1988#[cfg(test)]
1989mod tests {
1990    use super::*;
1991    use gpui::{elements::Empty, Element, MutableAppContext};
1992    use language::{Buffer, Rope};
1993    use rand::prelude::*;
1994    use std::env;
1995    use text::{Point, RandomCharIter};
1996    use util::test::sample_text;
1997
1998    #[gpui::test]
1999    fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
2000        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
2001        let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
2002        assert_eq!(
2003            multibuffer.read(cx).snapshot(cx).text(),
2004            buffer.read(cx).text()
2005        );
2006
2007        buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX", cx));
2008        assert_eq!(
2009            multibuffer.read(cx).snapshot(cx).text(),
2010            buffer.read(cx).text()
2011        );
2012    }
2013
2014    #[gpui::test]
2015    fn test_excerpt_buffer(cx: &mut MutableAppContext) {
2016        let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
2017        let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
2018        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2019
2020        let subscription = multibuffer.update(cx, |multibuffer, cx| {
2021            let subscription = multibuffer.subscribe();
2022            multibuffer.push_excerpt(
2023                ExcerptProperties {
2024                    buffer: &buffer_1,
2025                    range: Point::new(1, 2)..Point::new(2, 5),
2026                    header_height: 2,
2027                    render_header: Some(Arc::new(|_| Empty::new().named("header 1"))),
2028                },
2029                cx,
2030            );
2031            assert_eq!(
2032                subscription.consume().into_inner(),
2033                [Edit {
2034                    old: 0..0,
2035                    new: 0..12
2036                }]
2037            );
2038
2039            multibuffer.push_excerpt(
2040                ExcerptProperties {
2041                    buffer: &buffer_1,
2042                    range: Point::new(3, 3)..Point::new(4, 4),
2043                    header_height: 1,
2044                    render_header: Some(Arc::new(|_| Empty::new().named("header 2"))),
2045                },
2046                cx,
2047            );
2048            multibuffer.push_excerpt(
2049                ExcerptProperties {
2050                    buffer: &buffer_2,
2051                    range: Point::new(3, 1)..Point::new(3, 3),
2052                    header_height: 3,
2053                    render_header: Some(Arc::new(|_| Empty::new().named("header 3"))),
2054                },
2055                cx,
2056            );
2057            assert_eq!(
2058                subscription.consume().into_inner(),
2059                [Edit {
2060                    old: 12..12,
2061                    new: 12..28
2062                }]
2063            );
2064
2065            subscription
2066        });
2067
2068        assert_eq!(
2069            multibuffer.read(cx).snapshot(cx).text(),
2070            concat!(
2071                "\n",      // Preserve newlines
2072                "\n",      //
2073                "bbbb\n",  //
2074                "ccccc\n", //
2075                "\n",      //
2076                "ddd\n",   //
2077                "eeee\n",  //
2078                "\n",      //
2079                "\n",      //
2080                "\n",      //
2081                "jj"       //
2082            )
2083        );
2084
2085        {
2086            let snapshot = multibuffer.read(cx).read(cx);
2087            assert_eq!(
2088                snapshot
2089                    .excerpt_headers_in_range(0..snapshot.max_point().row + 1)
2090                    .map(|(rows, render)| (rows, render(cx).name().unwrap().to_string()))
2091                    .collect::<Vec<_>>(),
2092                &[
2093                    (0..2, "header 1".into()),
2094                    (4..5, "header 2".into()),
2095                    (7..10, "header 3".into())
2096                ]
2097            );
2098
2099            assert_eq!(
2100                snapshot
2101                    .excerpt_headers_in_range(1..5)
2102                    .map(|(rows, render)| (rows, render(cx).name().unwrap().to_string()))
2103                    .collect::<Vec<_>>(),
2104                &[(0..2, "header 1".into()), (4..5, "header 2".into())]
2105            );
2106
2107            assert_eq!(
2108                snapshot
2109                    .excerpt_headers_in_range(2..8)
2110                    .map(|(rows, render)| (rows, render(cx).name().unwrap().to_string()))
2111                    .collect::<Vec<_>>(),
2112                &[(4..5, "header 2".into()), (7..10, "header 3".into())]
2113            );
2114        }
2115
2116        buffer_1.update(cx, |buffer, cx| {
2117            buffer.edit(
2118                [
2119                    Point::new(0, 0)..Point::new(0, 0),
2120                    Point::new(2, 1)..Point::new(2, 3),
2121                ],
2122                "\n",
2123                cx,
2124            );
2125        });
2126
2127        assert_eq!(
2128            multibuffer.read(cx).snapshot(cx).text(),
2129            concat!(
2130                "\n",     // Preserve newlines
2131                "\n",     //
2132                "bbbb\n", //
2133                "c\n",    //
2134                "cc\n",   //
2135                "\n",     //
2136                "ddd\n",  //
2137                "eeee\n", //
2138                "\n",     //
2139                "\n",     //
2140                "\n",     //
2141                "jj"      //
2142            )
2143        );
2144
2145        assert_eq!(
2146            subscription.consume().into_inner(),
2147            [Edit {
2148                old: 8..10,
2149                new: 8..9
2150            }]
2151        );
2152    }
2153
2154    #[gpui::test]
2155    fn test_singleton_multibuffer_anchors(cx: &mut MutableAppContext) {
2156        let buffer = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
2157        let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
2158        let old_snapshot = multibuffer.read(cx).snapshot(cx);
2159        buffer.update(cx, |buffer, cx| {
2160            buffer.edit([0..0], "X", cx);
2161            buffer.edit([5..5], "Y", cx);
2162        });
2163        let new_snapshot = multibuffer.read(cx).snapshot(cx);
2164
2165        assert_eq!(old_snapshot.text(), "abcd");
2166        assert_eq!(new_snapshot.text(), "XabcdY");
2167
2168        assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
2169        assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
2170        assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
2171        assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
2172    }
2173
2174    #[gpui::test]
2175    fn test_multibuffer_anchors(cx: &mut MutableAppContext) {
2176        let buffer_1 = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
2177        let buffer_2 = cx.add_model(|cx| Buffer::new(0, "efghi", cx));
2178        let multibuffer = cx.add_model(|cx| {
2179            let mut multibuffer = MultiBuffer::new(0);
2180            multibuffer.push_excerpt(
2181                ExcerptProperties {
2182                    buffer: &buffer_1,
2183                    range: 0..4,
2184                    header_height: 1,
2185                    render_header: None,
2186                },
2187                cx,
2188            );
2189            multibuffer.push_excerpt(
2190                ExcerptProperties {
2191                    buffer: &buffer_2,
2192                    range: 0..5,
2193                    header_height: 1,
2194                    render_header: None,
2195                },
2196                cx,
2197            );
2198            multibuffer
2199        });
2200        let old_snapshot = multibuffer.read(cx).snapshot(cx);
2201
2202        buffer_1.update(cx, |buffer, cx| {
2203            buffer.edit([0..0], "W", cx);
2204            buffer.edit([5..5], "X", cx);
2205        });
2206        buffer_2.update(cx, |buffer, cx| {
2207            buffer.edit([0..0], "Y", cx);
2208            buffer.edit([6..0], "Z", cx);
2209        });
2210        let new_snapshot = multibuffer.read(cx).snapshot(cx);
2211
2212        assert_eq!(old_snapshot.text(), "\nabcd\n\nefghi");
2213        assert_eq!(new_snapshot.text(), "\nWabcdX\n\nYefghiZ");
2214
2215        assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 1);
2216        assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 2);
2217        assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 1);
2218        assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
2219        assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
2220        assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
2221        assert_eq!(old_snapshot.anchor_before(7).to_offset(&new_snapshot), 9);
2222        assert_eq!(old_snapshot.anchor_after(7).to_offset(&new_snapshot), 10);
2223        assert_eq!(old_snapshot.anchor_before(12).to_offset(&new_snapshot), 15);
2224        assert_eq!(old_snapshot.anchor_after(12).to_offset(&new_snapshot), 16);
2225    }
2226
2227    #[gpui::test(iterations = 100)]
2228    fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
2229        let operations = env::var("OPERATIONS")
2230            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2231            .unwrap_or(10);
2232
2233        let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
2234        let list = cx.add_model(|_| MultiBuffer::new(0));
2235        let mut excerpt_ids = Vec::new();
2236        let mut expected_excerpts = Vec::new();
2237        let mut old_versions = Vec::new();
2238
2239        for _ in 0..operations {
2240            match rng.gen_range(0..100) {
2241                0..=19 if !buffers.is_empty() => {
2242                    let buffer = buffers.choose(&mut rng).unwrap();
2243                    buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
2244                }
2245                _ => {
2246                    let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
2247                        let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
2248                        buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
2249                        buffers.last().unwrap()
2250                    } else {
2251                        buffers.choose(&mut rng).unwrap()
2252                    };
2253
2254                    let buffer = buffer_handle.read(cx);
2255                    let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
2256                    let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
2257                    let header_height = rng.gen_range(0..=5);
2258                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
2259                    log::info!(
2260                        "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
2261                        header_height,
2262                        buffer_handle.id(),
2263                        buffer.text(),
2264                        start_ix..end_ix,
2265                        &buffer.text()[start_ix..end_ix]
2266                    );
2267
2268                    let excerpt_id = list.update(cx, |list, cx| {
2269                        list.push_excerpt(
2270                            ExcerptProperties {
2271                                buffer: &buffer_handle,
2272                                range: start_ix..end_ix,
2273                                header_height,
2274                                render_header: None,
2275                            },
2276                            cx,
2277                        )
2278                    });
2279                    excerpt_ids.push(excerpt_id);
2280                    expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
2281                }
2282            }
2283
2284            if rng.gen_bool(0.3) {
2285                list.update(cx, |list, cx| {
2286                    old_versions.push((list.snapshot(cx), list.subscribe()));
2287                })
2288            }
2289
2290            let snapshot = list.read(cx).snapshot(cx);
2291
2292            let mut excerpt_starts = Vec::new();
2293            let mut expected_text = String::new();
2294            for (buffer, range, header_height) in &expected_excerpts {
2295                let buffer = buffer.read(cx);
2296                let buffer_range = range.to_offset(buffer);
2297
2298                for _ in 0..*header_height {
2299                    expected_text.push('\n');
2300                }
2301
2302                excerpt_starts.push(TextSummary::from(expected_text.as_str()));
2303                expected_text.extend(buffer.text_for_range(buffer_range.clone()));
2304                expected_text.push('\n');
2305            }
2306            // Remove final trailing newline.
2307            if !expected_excerpts.is_empty() {
2308                expected_text.pop();
2309            }
2310
2311            assert_eq!(snapshot.text(), expected_text);
2312            log::info!("MultiBuffer text: {:?}", expected_text);
2313
2314            let mut excerpt_starts = excerpt_starts.into_iter();
2315            for (buffer, range, _) in &expected_excerpts {
2316                let buffer_id = buffer.id();
2317                let buffer = buffer.read(cx);
2318                let buffer_range = range.to_offset(buffer);
2319                let buffer_start_point = buffer.offset_to_point(buffer_range.start);
2320                let buffer_start_point_utf16 =
2321                    buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
2322
2323                let excerpt_start = excerpt_starts.next().unwrap();
2324                let mut offset = excerpt_start.bytes;
2325                let mut buffer_offset = buffer_range.start;
2326                let mut point = excerpt_start.lines;
2327                let mut buffer_point = buffer_start_point;
2328                let mut point_utf16 = excerpt_start.lines_utf16;
2329                let mut buffer_point_utf16 = buffer_start_point_utf16;
2330                for ch in buffer
2331                    .snapshot()
2332                    .chunks(buffer_range.clone(), None)
2333                    .flat_map(|c| c.text.chars())
2334                {
2335                    for _ in 0..ch.len_utf8() {
2336                        let left_offset = snapshot.clip_offset(offset, Bias::Left);
2337                        let right_offset = snapshot.clip_offset(offset, Bias::Right);
2338                        let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
2339                        let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
2340                        assert_eq!(
2341                            left_offset,
2342                            excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
2343                            "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
2344                            offset,
2345                            buffer_id,
2346                            buffer_offset,
2347                        );
2348                        assert_eq!(
2349                            right_offset,
2350                            excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
2351                            "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
2352                            offset,
2353                            buffer_id,
2354                            buffer_offset,
2355                        );
2356
2357                        let left_point = snapshot.clip_point(point, Bias::Left);
2358                        let right_point = snapshot.clip_point(point, Bias::Right);
2359                        let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
2360                        let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
2361                        assert_eq!(
2362                            left_point,
2363                            excerpt_start.lines + (buffer_left_point - buffer_start_point),
2364                            "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
2365                            point,
2366                            buffer_id,
2367                            buffer_point,
2368                        );
2369                        assert_eq!(
2370                            right_point,
2371                            excerpt_start.lines + (buffer_right_point - buffer_start_point),
2372                            "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
2373                            point,
2374                            buffer_id,
2375                            buffer_point,
2376                        );
2377
2378                        assert_eq!(
2379                            snapshot.point_to_offset(left_point),
2380                            left_offset,
2381                            "point_to_offset({:?})",
2382                            left_point,
2383                        );
2384                        assert_eq!(
2385                            snapshot.offset_to_point(left_offset),
2386                            left_point,
2387                            "offset_to_point({:?})",
2388                            left_offset,
2389                        );
2390
2391                        offset += 1;
2392                        buffer_offset += 1;
2393                        if ch == '\n' {
2394                            point += Point::new(1, 0);
2395                            buffer_point += Point::new(1, 0);
2396                        } else {
2397                            point += Point::new(0, 1);
2398                            buffer_point += Point::new(0, 1);
2399                        }
2400                    }
2401
2402                    for _ in 0..ch.len_utf16() {
2403                        let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
2404                        let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
2405                        let buffer_left_point_utf16 =
2406                            buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
2407                        let buffer_right_point_utf16 =
2408                            buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
2409                        assert_eq!(
2410                            left_point_utf16,
2411                            excerpt_start.lines_utf16
2412                                + (buffer_left_point_utf16 - buffer_start_point_utf16),
2413                            "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
2414                            point_utf16,
2415                            buffer_id,
2416                            buffer_point_utf16,
2417                        );
2418                        assert_eq!(
2419                            right_point_utf16,
2420                            excerpt_start.lines_utf16
2421                                + (buffer_right_point_utf16 - buffer_start_point_utf16),
2422                            "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
2423                            point_utf16,
2424                            buffer_id,
2425                            buffer_point_utf16,
2426                        );
2427
2428                        if ch == '\n' {
2429                            point_utf16 += PointUtf16::new(1, 0);
2430                            buffer_point_utf16 += PointUtf16::new(1, 0);
2431                        } else {
2432                            point_utf16 += PointUtf16::new(0, 1);
2433                            buffer_point_utf16 += PointUtf16::new(0, 1);
2434                        }
2435                    }
2436                }
2437            }
2438
2439            for (row, line) in expected_text.split('\n').enumerate() {
2440                assert_eq!(
2441                    snapshot.line_len(row as u32),
2442                    line.len() as u32,
2443                    "line_len({}).",
2444                    row
2445                );
2446            }
2447
2448            let text_rope = Rope::from(expected_text.as_str());
2449            for _ in 0..10 {
2450                let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
2451                let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
2452
2453                assert_eq!(
2454                    snapshot
2455                        .text_for_range(start_ix..end_ix)
2456                        .collect::<String>(),
2457                    &expected_text[start_ix..end_ix],
2458                    "incorrect text for range {:?}",
2459                    start_ix..end_ix
2460                );
2461
2462                let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
2463                assert_eq!(
2464                    snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
2465                    expected_summary,
2466                    "incorrect summary for range {:?}",
2467                    start_ix..end_ix
2468                );
2469            }
2470
2471            for _ in 0..10 {
2472                let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
2473                assert_eq!(
2474                    snapshot.reversed_chars_at(end_ix).collect::<String>(),
2475                    expected_text[..end_ix].chars().rev().collect::<String>(),
2476                );
2477            }
2478
2479            for _ in 0..10 {
2480                let end_ix = rng.gen_range(0..=text_rope.len());
2481                let start_ix = rng.gen_range(0..=end_ix);
2482                assert_eq!(
2483                    snapshot
2484                        .bytes_in_range(start_ix..end_ix)
2485                        .flatten()
2486                        .copied()
2487                        .collect::<Vec<_>>(),
2488                    expected_text.as_bytes()[start_ix..end_ix].to_vec(),
2489                    "bytes_in_range({:?})",
2490                    start_ix..end_ix,
2491                );
2492            }
2493        }
2494
2495        let snapshot = list.read(cx).snapshot(cx);
2496        for (old_snapshot, subscription) in old_versions {
2497            let edits = subscription.consume().into_inner();
2498
2499            log::info!(
2500                "applying subscription edits to old text: {:?}: {:?}",
2501                old_snapshot.text(),
2502                edits,
2503            );
2504
2505            let mut text = old_snapshot.text();
2506            for edit in edits {
2507                let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2508                text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2509            }
2510            assert_eq!(text.to_string(), snapshot.text());
2511        }
2512    }
2513
2514    #[gpui::test]
2515    fn test_history(cx: &mut MutableAppContext) {
2516        let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
2517        let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
2518        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2519        let group_interval = multibuffer.read(cx).history.group_interval;
2520        multibuffer.update(cx, |multibuffer, cx| {
2521            multibuffer.push_excerpt(
2522                ExcerptProperties {
2523                    buffer: &buffer_1,
2524                    range: 0..buffer_1.read(cx).len(),
2525                    header_height: 0,
2526                    render_header: None,
2527                },
2528                cx,
2529            );
2530            multibuffer.push_excerpt(
2531                ExcerptProperties {
2532                    buffer: &buffer_2,
2533                    range: 0..buffer_2.read(cx).len(),
2534                    header_height: 0,
2535                    render_header: None,
2536                },
2537                cx,
2538            );
2539        });
2540
2541        let mut now = Instant::now();
2542
2543        multibuffer.update(cx, |multibuffer, cx| {
2544            multibuffer.start_transaction_at(now, cx);
2545            multibuffer.edit(
2546                [
2547                    Point::new(0, 0)..Point::new(0, 0),
2548                    Point::new(1, 0)..Point::new(1, 0),
2549                ],
2550                "A",
2551                cx,
2552            );
2553            multibuffer.edit(
2554                [
2555                    Point::new(0, 1)..Point::new(0, 1),
2556                    Point::new(1, 1)..Point::new(1, 1),
2557                ],
2558                "B",
2559                cx,
2560            );
2561            multibuffer.end_transaction_at(now, cx);
2562            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2563
2564            now += 2 * group_interval;
2565            multibuffer.start_transaction_at(now, cx);
2566            multibuffer.edit([2..2], "C", cx);
2567            multibuffer.end_transaction_at(now, cx);
2568            assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2569
2570            multibuffer.undo(cx);
2571            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2572
2573            multibuffer.undo(cx);
2574            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
2575
2576            multibuffer.redo(cx);
2577            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2578
2579            multibuffer.redo(cx);
2580            assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2581
2582            buffer_1.update(cx, |buffer_1, cx| buffer_1.undo(cx));
2583            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2584
2585            multibuffer.undo(cx);
2586            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
2587
2588            multibuffer.redo(cx);
2589            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2590
2591            multibuffer.redo(cx);
2592            assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2593
2594            multibuffer.undo(cx);
2595            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2596
2597            buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
2598            assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2599
2600            multibuffer.undo(cx);
2601            assert_eq!(multibuffer.read(cx).text(), "C1234\n5678");
2602        });
2603    }
2604}