multi_buffer.rs

   1mod anchor;
   2mod location;
   3mod selection;
   4
   5use self::location::*;
   6use crate::{
   7    buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
   8    BufferSnapshot,
   9};
  10use collections::HashMap;
  11use gpui::{AppContext, Entity, ModelContext, ModelHandle};
  12use parking_lot::Mutex;
  13use std::{cmp, ops::Range};
  14use sum_tree::{Bias, Cursor, SumTree};
  15use text::{
  16    rope::TextDimension,
  17    subscription::{Subscription, Topic},
  18    AnchorRangeExt, Edit, Point, PointUtf16, TextSummary,
  19};
  20use theme::SyntaxTheme;
  21
  22const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
  23
  24#[derive(Default)]
  25pub struct MultiBuffer {
  26    snapshot: Mutex<MultiBufferSnapshot>,
  27    buffers: HashMap<usize, BufferState>,
  28    subscriptions: Topic,
  29}
  30
  31pub trait ToOffset {
  32    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize;
  33}
  34
  35pub trait ToPoint {
  36    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point;
  37}
  38
  39#[derive(Debug)]
  40struct BufferState {
  41    buffer: ModelHandle<Buffer>,
  42    last_sync: clock::Global,
  43    excerpts: Vec<ExcerptId>,
  44}
  45
  46#[derive(Clone, Default)]
  47pub struct MultiBufferSnapshot {
  48    excerpts: SumTree<Excerpt>,
  49}
  50
  51pub struct ExcerptProperties<'a, T> {
  52    buffer: &'a ModelHandle<Buffer>,
  53    range: Range<T>,
  54    header_height: u8,
  55}
  56
  57#[derive(Clone)]
  58struct Excerpt {
  59    id: ExcerptId,
  60    buffer: buffer::BufferSnapshot,
  61    range: Range<text::Anchor>,
  62    text_summary: TextSummary,
  63    header_height: u8,
  64}
  65
  66#[derive(Clone, Debug, Default)]
  67struct ExcerptSummary {
  68    excerpt_id: ExcerptId,
  69    text: TextSummary,
  70}
  71
  72pub struct Chunks<'a> {
  73    range: Range<usize>,
  74    cursor: Cursor<'a, Excerpt, usize>,
  75    header_height: u8,
  76    excerpt_chunks: Option<buffer::BufferChunks<'a>>,
  77    theme: Option<&'a SyntaxTheme>,
  78}
  79
  80impl MultiBuffer {
  81    pub fn new() -> Self {
  82        Self::default()
  83    }
  84
  85    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
  86        self.sync(cx);
  87        self.snapshot.lock().clone()
  88    }
  89
  90    pub fn subscribe(&mut self) -> Subscription {
  91        self.subscriptions.subscribe()
  92    }
  93
  94    pub fn push<O>(&mut self, props: ExcerptProperties<O>, cx: &mut ModelContext<Self>) -> ExcerptId
  95    where
  96        O: text::ToOffset,
  97    {
  98        self.sync(cx);
  99
 100        let buffer = props.buffer.read(cx);
 101        let range = buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end);
 102        let mut snapshot = self.snapshot.lock();
 103        let prev_id = snapshot.excerpts.last().map(|e| &e.id);
 104        let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
 105
 106        let edit_start = snapshot.excerpts.summary().text.bytes;
 107        let excerpt = Excerpt::new(id.clone(), buffer.snapshot(), range, props.header_height);
 108        let edit = Edit {
 109            old: edit_start..edit_start,
 110            new: edit_start..edit_start + excerpt.text_summary.bytes,
 111        };
 112        snapshot.excerpts.push(excerpt, &());
 113        self.buffers
 114            .entry(props.buffer.id())
 115            .or_insert_with(|| BufferState {
 116                buffer: props.buffer.clone(),
 117                last_sync: buffer.version(),
 118                excerpts: Default::default(),
 119            })
 120            .excerpts
 121            .push(id.clone());
 122
 123        self.subscriptions.publish_mut([edit]);
 124
 125        id
 126    }
 127
 128    fn sync(&self, cx: &AppContext) {
 129        let mut snapshot = self.snapshot.lock();
 130        let mut excerpts_to_edit = Vec::new();
 131        for buffer_state in self.buffers.values() {
 132            if buffer_state
 133                .buffer
 134                .read(cx)
 135                .version()
 136                .gt(&buffer_state.last_sync)
 137            {
 138                excerpts_to_edit.extend(
 139                    buffer_state
 140                        .excerpts
 141                        .iter()
 142                        .map(|excerpt_id| (excerpt_id, buffer_state)),
 143                );
 144            }
 145        }
 146        excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _)| *excerpt_id);
 147
 148        let mut edits = Vec::new();
 149        let mut new_excerpts = SumTree::new();
 150        let mut cursor = snapshot.excerpts.cursor::<(ExcerptId, usize)>();
 151
 152        for (id, buffer_state) in excerpts_to_edit {
 153            new_excerpts.push_tree(cursor.slice(id, Bias::Left, &()), &());
 154            let old_excerpt = cursor.item().unwrap();
 155            let buffer = buffer_state.buffer.read(cx);
 156
 157            edits.extend(
 158                buffer
 159                    .edits_since_in_range::<usize>(
 160                        old_excerpt.buffer.version(),
 161                        old_excerpt.range.clone(),
 162                    )
 163                    .map(|mut edit| {
 164                        let excerpt_old_start =
 165                            cursor.start().1 + old_excerpt.header_height as usize;
 166                        let excerpt_new_start =
 167                            new_excerpts.summary().text.bytes + old_excerpt.header_height as usize;
 168                        edit.old.start += excerpt_old_start;
 169                        edit.old.end += excerpt_old_start;
 170                        edit.new.start += excerpt_new_start;
 171                        edit.new.end += excerpt_new_start;
 172                        edit
 173                    }),
 174            );
 175
 176            new_excerpts.push(
 177                Excerpt::new(
 178                    id.clone(),
 179                    buffer.snapshot(),
 180                    old_excerpt.range.clone(),
 181                    old_excerpt.header_height,
 182                ),
 183                &(),
 184            );
 185
 186            cursor.next(&());
 187        }
 188        new_excerpts.push_tree(cursor.suffix(&()), &());
 189
 190        drop(cursor);
 191        snapshot.excerpts = new_excerpts;
 192
 193        self.subscriptions.publish(edits);
 194    }
 195}
 196
 197impl Entity for MultiBuffer {
 198    type Event = ();
 199}
 200
 201impl MultiBufferSnapshot {
 202    pub fn text(&self) -> String {
 203        self.chunks(0..self.len(), None)
 204            .map(|chunk| chunk.text)
 205            .collect()
 206    }
 207
 208    pub fn text_for_range<'a, T: ToOffset>(
 209        &'a self,
 210        range: Range<T>,
 211    ) -> impl Iterator<Item = &'a str> {
 212        self.chunks(range, None).map(|chunk| chunk.text)
 213    }
 214
 215    pub fn len(&self) -> usize {
 216        self.excerpts.summary().text.bytes
 217    }
 218
 219    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
 220        let mut cursor = self.excerpts.cursor::<usize>();
 221        cursor.seek(&offset, Bias::Right, &());
 222        if let Some(excerpt) = cursor.item() {
 223            let start_after_header = *cursor.start() + excerpt.header_height as usize;
 224            if offset < start_after_header {
 225                *cursor.start()
 226            } else {
 227                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 228                let buffer_offset = excerpt
 229                    .buffer
 230                    .clip_offset(excerpt_start + (offset - start_after_header), bias);
 231                let offset_in_excerpt = if buffer_offset > excerpt_start {
 232                    buffer_offset - excerpt_start
 233                } else {
 234                    0
 235                };
 236                start_after_header + offset_in_excerpt
 237            }
 238        } else {
 239            self.excerpts.summary().text.bytes
 240        }
 241    }
 242
 243    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
 244        let mut cursor = self.excerpts.cursor::<Point>();
 245        cursor.seek(&point, Bias::Right, &());
 246        if let Some(excerpt) = cursor.item() {
 247            let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
 248            if point < start_after_header {
 249                *cursor.start()
 250            } else {
 251                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 252                let buffer_point = excerpt
 253                    .buffer
 254                    .clip_point(excerpt_start + (point - start_after_header), bias);
 255                let point_in_excerpt = if buffer_point > excerpt_start {
 256                    buffer_point - excerpt_start
 257                } else {
 258                    Point::zero()
 259                };
 260                start_after_header + point_in_excerpt
 261            }
 262        } else {
 263            self.excerpts.summary().text.lines
 264        }
 265    }
 266
 267    pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
 268        let mut cursor = self.excerpts.cursor::<PointUtf16>();
 269        cursor.seek(&point, Bias::Right, &());
 270        if let Some(excerpt) = cursor.item() {
 271            let start_after_header =
 272                *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
 273            if point < start_after_header {
 274                *cursor.start()
 275            } else {
 276                let excerpt_start = excerpt
 277                    .buffer
 278                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 279                let buffer_point = excerpt
 280                    .buffer
 281                    .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
 282                let point_in_excerpt = if buffer_point > excerpt_start {
 283                    buffer_point - excerpt_start
 284                } else {
 285                    PointUtf16::new(0, 0)
 286                };
 287                start_after_header + point_in_excerpt
 288            }
 289        } else {
 290            self.excerpts.summary().text.lines_utf16
 291        }
 292    }
 293
 294    pub fn chunks<'a, T: ToOffset>(
 295        &'a self,
 296        range: Range<T>,
 297        theme: Option<&'a SyntaxTheme>,
 298    ) -> Chunks<'a> {
 299        let range = range.start.to_offset(self)..range.end.to_offset(self);
 300        let mut cursor = self.excerpts.cursor::<usize>();
 301        cursor.seek(&range.start, Bias::Right, &());
 302
 303        let mut header_height: u8 = 0;
 304        let excerpt_chunks = cursor.item().map(|excerpt| {
 305            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
 306            header_height = excerpt.header_height;
 307
 308            let buffer_start;
 309            let start_overshoot = range.start - cursor.start();
 310            if start_overshoot < excerpt.header_height as usize {
 311                header_height -= start_overshoot as u8;
 312                buffer_start = buffer_range.start;
 313            } else {
 314                buffer_start =
 315                    buffer_range.start + start_overshoot - excerpt.header_height as usize;
 316                header_height = 0;
 317            }
 318
 319            let buffer_end;
 320            let end_overshoot = range.end - cursor.start();
 321            if end_overshoot < excerpt.header_height as usize {
 322                header_height -= excerpt.header_height - end_overshoot as u8;
 323                buffer_end = buffer_start;
 324            } else {
 325                buffer_end = cmp::min(
 326                    buffer_range.end,
 327                    buffer_range.start + end_overshoot - excerpt.header_height as usize,
 328                );
 329            }
 330
 331            excerpt.buffer.chunks(buffer_start..buffer_end, theme)
 332        });
 333
 334        Chunks {
 335            range,
 336            cursor,
 337            header_height,
 338            excerpt_chunks,
 339            theme,
 340        }
 341    }
 342
 343    pub fn offset_to_point(&self, offset: usize) -> Point {
 344        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
 345        cursor.seek(&offset, Bias::Right, &());
 346        if let Some(excerpt) = cursor.item() {
 347            let (start_offset, start_point) = cursor.start();
 348            let overshoot = offset - start_offset;
 349            let header_height = excerpt.header_height as usize;
 350            if overshoot < header_height {
 351                *start_point
 352            } else {
 353                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 354                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 355                let buffer_point = excerpt
 356                    .buffer
 357                    .offset_to_point(excerpt_start_offset + (overshoot - header_height));
 358                *start_point
 359                    + Point::new(header_height as u32, 0)
 360                    + (buffer_point - excerpt_start_point)
 361            }
 362        } else {
 363            self.excerpts.summary().text.lines
 364        }
 365    }
 366
 367    pub fn point_to_offset(&self, point: Point) -> usize {
 368        let mut cursor = self.excerpts.cursor::<(Point, usize)>();
 369        cursor.seek(&point, Bias::Right, &());
 370        if let Some(excerpt) = cursor.item() {
 371            let (start_point, start_offset) = cursor.start();
 372            let overshoot = point - start_point;
 373            let header_height = Point::new(excerpt.header_height as u32, 0);
 374            if overshoot < header_height {
 375                *start_offset
 376            } else {
 377                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 378                let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
 379                let buffer_offset = excerpt
 380                    .buffer
 381                    .point_to_offset(excerpt_start_point + (overshoot - header_height));
 382                *start_offset + excerpt.header_height as usize + buffer_offset
 383                    - excerpt_start_offset
 384            }
 385        } else {
 386            self.excerpts.summary().text.bytes
 387        }
 388    }
 389
 390    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
 391        let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
 392        cursor.seek(&point, Bias::Right, &());
 393        if let Some(excerpt) = cursor.item() {
 394            let (start_point, start_offset) = cursor.start();
 395            let overshoot = point - start_point;
 396            let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
 397            if overshoot < header_height {
 398                *start_offset
 399            } else {
 400                let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
 401                let excerpt_start_point = excerpt
 402                    .buffer
 403                    .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
 404                let buffer_offset = excerpt
 405                    .buffer
 406                    .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
 407                *start_offset
 408                    + excerpt.header_height as usize
 409                    + (buffer_offset - excerpt_start_offset)
 410            }
 411        } else {
 412            self.excerpts.summary().text.bytes
 413        }
 414    }
 415
 416    pub fn line_len(&self, row: u32) -> u32 {
 417        let mut cursor = self.excerpts.cursor::<Point>();
 418        cursor.seek(&Point::new(row, 0), Bias::Right, &());
 419        if let Some(excerpt) = cursor.item() {
 420            let overshoot = row - cursor.start().row;
 421            let header_height = excerpt.header_height as u32;
 422            if overshoot < header_height {
 423                0
 424            } else {
 425                let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
 426                let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
 427                let buffer_row = excerpt_start.row + overshoot - header_height;
 428                let mut len = excerpt.buffer.line_len(buffer_row);
 429                if buffer_row == excerpt_end.row {
 430                    len = excerpt_end.column;
 431                }
 432                if buffer_row == excerpt_start.row {
 433                    len -= excerpt_start.column
 434                }
 435                len
 436            }
 437        } else {
 438            0
 439        }
 440    }
 441
 442    pub fn max_point(&self) -> Point {
 443        self.text_summary().lines
 444    }
 445
 446    pub fn text_summary(&self) -> TextSummary {
 447        self.excerpts.summary().text
 448    }
 449
 450    pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
 451    where
 452        D: TextDimension,
 453        O: ToOffset,
 454    {
 455        let mut summary = D::default();
 456        let mut range = range.start.to_offset(self)..range.end.to_offset(self);
 457        let mut cursor = self.excerpts.cursor::<usize>();
 458        cursor.seek(&range.start, Bias::Right, &());
 459        if let Some(excerpt) = cursor.item() {
 460            let start_after_header = cursor.start() + excerpt.header_height as usize;
 461            if range.start < start_after_header {
 462                let header_len = cmp::min(range.end, start_after_header) - range.start;
 463                summary.add_assign(&D::from_text_summary(&TextSummary {
 464                    bytes: header_len,
 465                    lines: Point::new(header_len as u32, 0),
 466                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 467                    first_line_chars: 0,
 468                    last_line_chars: 0,
 469                    longest_row: 0,
 470                    longest_row_chars: 0,
 471                }));
 472                range.start = start_after_header;
 473                range.end = cmp::max(range.start, range.end);
 474            }
 475
 476            let end_before_newline = cursor.end(&()) - 1;
 477            let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 478            let start_in_excerpt = excerpt_start + (range.start - start_after_header);
 479            let end_in_excerpt =
 480                excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
 481            summary.add_assign(
 482                &excerpt
 483                    .buffer
 484                    .text_summary_for_range(start_in_excerpt..end_in_excerpt),
 485            );
 486
 487            if range.end > end_before_newline {
 488                summary.add_assign(&D::from_text_summary(&TextSummary {
 489                    bytes: 1,
 490                    lines: Point::new(1 as u32, 0),
 491                    lines_utf16: PointUtf16::new(1 as u32, 0),
 492                    first_line_chars: 0,
 493                    last_line_chars: 0,
 494                    longest_row: 0,
 495                    longest_row_chars: 0,
 496                }));
 497            }
 498
 499            cursor.next(&());
 500        }
 501
 502        if range.end > *cursor.start() {
 503            summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
 504                &range.end,
 505                Bias::Right,
 506                &(),
 507            )));
 508            if let Some(excerpt) = cursor.item() {
 509                let start_after_header = cursor.start() + excerpt.header_height as usize;
 510                let header_len =
 511                    cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
 512                summary.add_assign(&D::from_text_summary(&TextSummary {
 513                    bytes: header_len,
 514                    lines: Point::new(header_len as u32, 0),
 515                    lines_utf16: PointUtf16::new(header_len as u32, 0),
 516                    first_line_chars: 0,
 517                    last_line_chars: 0,
 518                    longest_row: 0,
 519                    longest_row_chars: 0,
 520                }));
 521                range.end = cmp::max(start_after_header, range.end);
 522
 523                let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
 524                let end_in_excerpt = excerpt_start + (range.end - start_after_header);
 525                summary.add_assign(
 526                    &excerpt
 527                        .buffer
 528                        .text_summary_for_range(excerpt_start..end_in_excerpt),
 529                );
 530                cursor.next(&());
 531            }
 532        }
 533
 534        summary
 535    }
 536
 537    fn resolve_excerpt<'a, D: TextDimension>(
 538        &'a self,
 539        excerpt_id: &ExcerptId,
 540    ) -> Option<(D, &'a BufferSnapshot)> {
 541        let mut cursor = self.excerpts.cursor::<(ExcerptId, TextSummary)>();
 542        cursor.seek(excerpt_id, Bias::Left, &());
 543        if let Some(excerpt) = cursor.item() {
 544            if cursor.start().0 == *excerpt_id {
 545                return Some((D::from_text_summary(&cursor.start().1), &excerpt.buffer));
 546            }
 547        }
 548        None
 549    }
 550
 551    fn buffer_snapshot_for_excerpt<'a>(
 552        &'a self,
 553        excerpt_id: &ExcerptId,
 554    ) -> Option<&'a BufferSnapshot> {
 555        let mut cursor = self.excerpts.cursor::<ExcerptId>();
 556        cursor.seek(excerpt_id, Bias::Left, &());
 557        if let Some(excerpt) = cursor.item() {
 558            if cursor.start() == excerpt_id {
 559                return Some(&excerpt.buffer);
 560            }
 561        }
 562        None
 563    }
 564}
 565
 566impl Excerpt {
 567    fn new(
 568        id: ExcerptId,
 569        buffer: buffer::BufferSnapshot,
 570        range: Range<text::Anchor>,
 571        header_height: u8,
 572    ) -> Self {
 573        let mut text_summary =
 574            buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
 575        if header_height > 0 {
 576            text_summary.first_line_chars = 0;
 577            text_summary.lines.row += header_height as u32;
 578            text_summary.lines_utf16.row += header_height as u32;
 579            text_summary.bytes += header_height as usize;
 580            text_summary.longest_row += header_height as u32;
 581        }
 582        text_summary.last_line_chars = 0;
 583        text_summary.lines.row += 1;
 584        text_summary.lines.column = 0;
 585        text_summary.lines_utf16.row += 1;
 586        text_summary.lines_utf16.column = 0;
 587        text_summary.bytes += 1;
 588
 589        Excerpt {
 590            id,
 591            buffer,
 592            range,
 593            text_summary,
 594            header_height,
 595        }
 596    }
 597
 598    fn header_summary(&self) -> TextSummary {
 599        TextSummary {
 600            bytes: self.header_height as usize,
 601            lines: Point::new(self.header_height as u32, 0),
 602            lines_utf16: PointUtf16::new(self.header_height as u32, 0),
 603            first_line_chars: 0,
 604            last_line_chars: 0,
 605            longest_row: 0,
 606            longest_row_chars: 0,
 607        }
 608    }
 609}
 610
 611impl sum_tree::Item for Excerpt {
 612    type Summary = ExcerptSummary;
 613
 614    fn summary(&self) -> Self::Summary {
 615        ExcerptSummary {
 616            excerpt_id: self.id.clone(),
 617            text: self.text_summary.clone(),
 618        }
 619    }
 620}
 621
 622impl sum_tree::Summary for ExcerptSummary {
 623    type Context = ();
 624
 625    fn add_summary(&mut self, summary: &Self, _: &()) {
 626        debug_assert!(summary.excerpt_id > self.excerpt_id);
 627        self.excerpt_id = summary.excerpt_id.clone();
 628        self.text.add_summary(&summary.text, &());
 629    }
 630}
 631
 632impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
 633    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 634        *self += &summary.text;
 635    }
 636}
 637
 638impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
 639    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 640        *self += summary.text.bytes;
 641    }
 642}
 643
 644impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
 645    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
 646        Ord::cmp(self, &cursor_location.text.bytes)
 647    }
 648}
 649
 650impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Location {
 651    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
 652        Ord::cmp(self, &cursor_location.excerpt_id)
 653    }
 654}
 655
 656impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
 657    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 658        *self += summary.text.lines;
 659    }
 660}
 661
 662impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
 663    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 664        *self += summary.text.lines_utf16
 665    }
 666}
 667
 668impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Location {
 669    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
 670        debug_assert!(summary.excerpt_id > *self);
 671        *self = summary.excerpt_id.clone();
 672    }
 673}
 674
 675impl<'a> Iterator for Chunks<'a> {
 676    type Item = Chunk<'a>;
 677
 678    fn next(&mut self) -> Option<Self::Item> {
 679        loop {
 680            if self.header_height > 0 {
 681                let chunk = Chunk {
 682                    text: unsafe {
 683                        std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
 684                    },
 685                    ..Default::default()
 686                };
 687                self.header_height = 0;
 688                return Some(chunk);
 689            }
 690
 691            if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
 692                if let Some(chunk) = excerpt_chunks.next() {
 693                    return Some(chunk);
 694                }
 695                self.excerpt_chunks.take();
 696                if self.cursor.end(&()) <= self.range.end {
 697                    return Some(Chunk {
 698                        text: "\n",
 699                        ..Default::default()
 700                    });
 701                }
 702            }
 703
 704            self.cursor.next(&());
 705            if *self.cursor.start() >= self.range.end {
 706                return None;
 707            }
 708
 709            let excerpt = self.cursor.item()?;
 710            let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
 711
 712            let buffer_end = cmp::min(
 713                buffer_range.end,
 714                buffer_range.start + self.range.end
 715                    - excerpt.header_height as usize
 716                    - self.cursor.start(),
 717            );
 718
 719            self.header_height = excerpt.header_height;
 720            self.excerpt_chunks = Some(
 721                excerpt
 722                    .buffer
 723                    .chunks(buffer_range.start..buffer_end, self.theme),
 724            );
 725        }
 726    }
 727}
 728
 729impl ToOffset for Point {
 730    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
 731        snapshot.point_to_offset(*self)
 732    }
 733}
 734
 735impl ToOffset for PointUtf16 {
 736    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
 737        snapshot.point_utf16_to_offset(*self)
 738    }
 739}
 740
 741impl ToOffset for usize {
 742    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
 743        assert!(*self <= snapshot.len(), "offset is out of range");
 744        *self
 745    }
 746}
 747
 748impl ToPoint for usize {
 749    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
 750        snapshot.offset_to_point(*self)
 751    }
 752}
 753
 754impl ToPoint for Point {
 755    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
 756        *self
 757    }
 758}
 759
 760#[cfg(test)]
 761mod tests {
 762    use super::*;
 763    use crate::buffer::Buffer;
 764    use gpui::MutableAppContext;
 765    use rand::prelude::*;
 766    use std::env;
 767    use text::{Point, RandomCharIter};
 768    use util::test::sample_text;
 769
 770    #[gpui::test]
 771    fn test_excerpt_buffer(cx: &mut MutableAppContext) {
 772        let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
 773        let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
 774
 775        let list = cx.add_model(|_| MultiBuffer::new());
 776
 777        let subscription = list.update(cx, |list, cx| {
 778            let subscription = list.subscribe();
 779            list.push(
 780                ExcerptProperties {
 781                    buffer: &buffer_1,
 782                    range: Point::new(1, 2)..Point::new(2, 5),
 783                    header_height: 2,
 784                },
 785                cx,
 786            );
 787            assert_eq!(
 788                subscription.consume().into_inner(),
 789                [Edit {
 790                    old: 0..0,
 791                    new: 0..13
 792                }]
 793            );
 794
 795            list.push(
 796                ExcerptProperties {
 797                    buffer: &buffer_1,
 798                    range: Point::new(3, 3)..Point::new(4, 4),
 799                    header_height: 1,
 800                },
 801                cx,
 802            );
 803            list.push(
 804                ExcerptProperties {
 805                    buffer: &buffer_2,
 806                    range: Point::new(3, 1)..Point::new(3, 3),
 807                    header_height: 3,
 808                },
 809                cx,
 810            );
 811            assert_eq!(
 812                subscription.consume().into_inner(),
 813                [Edit {
 814                    old: 13..13,
 815                    new: 13..29
 816                }]
 817            );
 818
 819            subscription
 820        });
 821
 822        assert_eq!(
 823            list.read(cx).snapshot(cx).text(),
 824            concat!(
 825                "\n",      // Preserve newlines
 826                "\n",      //
 827                "bbbb\n",  //
 828                "ccccc\n", //
 829                "\n",      //
 830                "ddd\n",   //
 831                "eeee\n",  //
 832                "\n",      //
 833                "\n",      //
 834                "\n",      //
 835                "jj\n"     //
 836            )
 837        );
 838
 839        buffer_1.update(cx, |buffer, cx| {
 840            buffer.edit(
 841                [
 842                    Point::new(0, 0)..Point::new(0, 0),
 843                    Point::new(2, 1)..Point::new(2, 3),
 844                ],
 845                "\n",
 846                cx,
 847            );
 848        });
 849
 850        assert_eq!(
 851            list.read(cx).snapshot(cx).text(),
 852            concat!(
 853                "\n",     // Preserve newlines
 854                "\n",     //
 855                "bbbb\n", //
 856                "c\n",    //
 857                "cc\n",   //
 858                "\n",     //
 859                "ddd\n",  //
 860                "eeee\n", //
 861                "\n",     //
 862                "\n",     //
 863                "\n",     //
 864                "jj\n"    //
 865            )
 866        );
 867
 868        assert_eq!(
 869            subscription.consume().into_inner(),
 870            [Edit {
 871                old: 8..10,
 872                new: 8..9
 873            }]
 874        );
 875    }
 876
 877    #[gpui::test(iterations = 100)]
 878    fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
 879        let operations = env::var("OPERATIONS")
 880            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 881            .unwrap_or(10);
 882
 883        let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
 884        let list = cx.add_model(|_| MultiBuffer::new());
 885        let mut excerpt_ids = Vec::new();
 886        let mut expected_excerpts = Vec::new();
 887        let mut old_versions = Vec::new();
 888
 889        for _ in 0..operations {
 890            match rng.gen_range(0..100) {
 891                0..=19 if !buffers.is_empty() => {
 892                    let buffer = buffers.choose(&mut rng).unwrap();
 893                    buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
 894                }
 895                _ => {
 896                    let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
 897                        let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
 898                        buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
 899                        buffers.last().unwrap()
 900                    } else {
 901                        buffers.choose(&mut rng).unwrap()
 902                    };
 903
 904                    let buffer = buffer_handle.read(cx);
 905                    let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
 906                    let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
 907                    let header_height = rng.gen_range(0..=5);
 908                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
 909                    log::info!(
 910                        "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
 911                        header_height,
 912                        buffer_handle.id(),
 913                        buffer.text(),
 914                        start_ix..end_ix,
 915                        &buffer.text()[start_ix..end_ix]
 916                    );
 917
 918                    let excerpt_id = list.update(cx, |list, cx| {
 919                        list.push(
 920                            ExcerptProperties {
 921                                buffer: &buffer_handle,
 922                                range: start_ix..end_ix,
 923                                header_height,
 924                            },
 925                            cx,
 926                        )
 927                    });
 928                    excerpt_ids.push(excerpt_id);
 929                    expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
 930                }
 931            }
 932
 933            if rng.gen_bool(0.3) {
 934                list.update(cx, |list, cx| {
 935                    old_versions.push((list.snapshot(cx), list.subscribe()));
 936                })
 937            }
 938
 939            let snapshot = list.read(cx).snapshot(cx);
 940
 941            let mut excerpt_starts = Vec::new();
 942            let mut expected_text = String::new();
 943            for (buffer, range, header_height) in &expected_excerpts {
 944                let buffer = buffer.read(cx);
 945                let buffer_range = range.to_offset(buffer);
 946
 947                for _ in 0..*header_height {
 948                    expected_text.push('\n');
 949                }
 950
 951                excerpt_starts.push(TextSummary::from(expected_text.as_str()));
 952                expected_text.extend(buffer.text_for_range(buffer_range.clone()));
 953                expected_text.push('\n');
 954            }
 955
 956            assert_eq!(snapshot.text(), expected_text);
 957
 958            let mut excerpt_starts = excerpt_starts.into_iter();
 959            for (buffer, range, _) in &expected_excerpts {
 960                let buffer_id = buffer.id();
 961                let buffer = buffer.read(cx);
 962                let buffer_range = range.to_offset(buffer);
 963                let buffer_start_point = buffer.offset_to_point(buffer_range.start);
 964                let buffer_start_point_utf16 =
 965                    buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
 966
 967                let excerpt_start = excerpt_starts.next().unwrap();
 968                let mut offset = excerpt_start.bytes;
 969                let mut buffer_offset = buffer_range.start;
 970                let mut point = excerpt_start.lines;
 971                let mut buffer_point = buffer_start_point;
 972                let mut point_utf16 = excerpt_start.lines_utf16;
 973                let mut buffer_point_utf16 = buffer_start_point_utf16;
 974                for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
 975                    let left_offset = snapshot.clip_offset(offset, Bias::Left);
 976                    let right_offset = snapshot.clip_offset(offset, Bias::Right);
 977                    let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
 978                    let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
 979                    assert_eq!(
 980                        left_offset,
 981                        excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
 982                        "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
 983                        offset,
 984                        buffer_id,
 985                        buffer_offset,
 986                    );
 987                    assert_eq!(
 988                        right_offset,
 989                        excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
 990                        "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
 991                        offset,
 992                        buffer_id,
 993                        buffer_offset,
 994                    );
 995
 996                    let left_point = snapshot.clip_point(point, Bias::Left);
 997                    let right_point = snapshot.clip_point(point, Bias::Right);
 998                    let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
 999                    let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1000                    assert_eq!(
1001                        left_point,
1002                        excerpt_start.lines + (buffer_left_point - buffer_start_point),
1003                        "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1004                        point,
1005                        buffer_id,
1006                        buffer_point,
1007                    );
1008                    assert_eq!(
1009                        right_point,
1010                        excerpt_start.lines + (buffer_right_point - buffer_start_point),
1011                        "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1012                        point,
1013                        buffer_id,
1014                        buffer_point,
1015                    );
1016
1017                    let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1018                    let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1019                    let buffer_left_point_utf16 =
1020                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1021                    let buffer_right_point_utf16 =
1022                        buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1023                    assert_eq!(
1024                        left_point_utf16,
1025                        excerpt_start.lines_utf16
1026                            + (buffer_left_point_utf16 - buffer_start_point_utf16),
1027                        "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1028                        point_utf16,
1029                        buffer_id,
1030                        buffer_point_utf16,
1031                    );
1032                    assert_eq!(
1033                        right_point_utf16,
1034                        excerpt_start.lines_utf16
1035                            + (buffer_right_point_utf16 - buffer_start_point_utf16),
1036                        "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1037                        point_utf16,
1038                        buffer_id,
1039                        buffer_point_utf16,
1040                    );
1041
1042                    assert_eq!(
1043                        snapshot.point_to_offset(left_point),
1044                        left_offset,
1045                        "point_to_offset({:?})",
1046                        left_point,
1047                    );
1048                    assert_eq!(
1049                        snapshot.offset_to_point(left_offset),
1050                        left_point,
1051                        "offset_to_point({:?})",
1052                        left_offset,
1053                    );
1054
1055                    offset += 1;
1056                    buffer_offset += 1;
1057                    if *byte == b'\n' {
1058                        point += Point::new(1, 0);
1059                        point_utf16 += PointUtf16::new(1, 0);
1060                        buffer_point += Point::new(1, 0);
1061                        buffer_point_utf16 += PointUtf16::new(1, 0);
1062                    } else {
1063                        point += Point::new(0, 1);
1064                        point_utf16 += PointUtf16::new(0, 1);
1065                        buffer_point += Point::new(0, 1);
1066                        buffer_point_utf16 += PointUtf16::new(0, 1);
1067                    }
1068                }
1069            }
1070
1071            for (row, line) in expected_text.split('\n').enumerate() {
1072                assert_eq!(
1073                    snapshot.line_len(row as u32),
1074                    line.len() as u32,
1075                    "line_len({}).",
1076                    row
1077                );
1078            }
1079
1080            for _ in 0..10 {
1081                let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1082                let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1083
1084                assert_eq!(
1085                    snapshot
1086                        .text_for_range(start_ix..end_ix)
1087                        .collect::<String>(),
1088                    &expected_text[start_ix..end_ix],
1089                    "incorrect text for range {:?}",
1090                    start_ix..end_ix
1091                );
1092
1093                let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1094                assert_eq!(
1095                    snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1096                    expected_summary,
1097                    "incorrect summary for range {:?}",
1098                    start_ix..end_ix
1099                );
1100            }
1101        }
1102
1103        let snapshot = list.read(cx).snapshot(cx);
1104        for (old_snapshot, subscription) in old_versions {
1105            let edits = subscription.consume().into_inner();
1106
1107            log::info!(
1108                "applying edits since old text: {:?}: {:?}",
1109                old_snapshot.text(),
1110                edits,
1111            );
1112
1113            let mut text = old_snapshot.text();
1114            for edit in edits {
1115                let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1116                text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1117            }
1118            assert_eq!(text.to_string(), snapshot.text());
1119        }
1120    }
1121}