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