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