inlay_map.rs

   1use crate::{
   2    inlay_cache::{Inlay, InlayId, InlayProperties},
   3    multi_buffer::{MultiBufferChunks, MultiBufferRows},
   4    MultiBufferSnapshot, ToOffset,
   5};
   6use collections::{BTreeSet, HashMap};
   7use gpui::fonts::HighlightStyle;
   8use language::{Chunk, Edit, Point, Rope, TextSummary};
   9use parking_lot::Mutex;
  10use std::{
  11    cmp,
  12    ops::{Add, AddAssign, Range, Sub, SubAssign},
  13};
  14use sum_tree::{Bias, Cursor, SumTree};
  15use text::Patch;
  16use util::post_inc;
  17
  18pub struct InlayMap {
  19    snapshot: Mutex<InlaySnapshot>,
  20    inlays_by_id: HashMap<InlayId, Inlay>,
  21    inlays: Vec<Inlay>,
  22}
  23
  24#[derive(Clone)]
  25pub struct InlaySnapshot {
  26    pub buffer: MultiBufferSnapshot,
  27    transforms: SumTree<Transform>,
  28    pub version: usize,
  29}
  30
  31#[derive(Clone, Debug)]
  32enum Transform {
  33    Isomorphic(TextSummary),
  34    Inlay(Inlay),
  35}
  36
  37impl sum_tree::Item for Transform {
  38    type Summary = TransformSummary;
  39
  40    fn summary(&self) -> Self::Summary {
  41        match self {
  42            Transform::Isomorphic(summary) => TransformSummary {
  43                input: summary.clone(),
  44                output: summary.clone(),
  45            },
  46            Transform::Inlay(inlay) => TransformSummary {
  47                input: TextSummary::default(),
  48                output: inlay.text.summary(),
  49            },
  50        }
  51    }
  52}
  53
  54#[derive(Clone, Debug, Default)]
  55struct TransformSummary {
  56    input: TextSummary,
  57    output: TextSummary,
  58}
  59
  60impl sum_tree::Summary for TransformSummary {
  61    type Context = ();
  62
  63    fn add_summary(&mut self, other: &Self, _: &()) {
  64        self.input += &other.input;
  65        self.output += &other.output;
  66    }
  67}
  68
  69pub type InlayEdit = Edit<InlayOffset>;
  70
  71#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  72pub struct InlayOffset(pub usize);
  73
  74impl Add for InlayOffset {
  75    type Output = Self;
  76
  77    fn add(self, rhs: Self) -> Self::Output {
  78        Self(self.0 + rhs.0)
  79    }
  80}
  81
  82impl Sub for InlayOffset {
  83    type Output = Self;
  84
  85    fn sub(self, rhs: Self) -> Self::Output {
  86        Self(self.0 - rhs.0)
  87    }
  88}
  89
  90impl AddAssign for InlayOffset {
  91    fn add_assign(&mut self, rhs: Self) {
  92        self.0 += rhs.0;
  93    }
  94}
  95
  96impl SubAssign for InlayOffset {
  97    fn sub_assign(&mut self, rhs: Self) {
  98        self.0 -= rhs.0;
  99    }
 100}
 101
 102impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
 103    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 104        self.0 += &summary.output.len;
 105    }
 106}
 107
 108#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 109pub struct InlayPoint(pub Point);
 110
 111impl Add for InlayPoint {
 112    type Output = Self;
 113
 114    fn add(self, rhs: Self) -> Self::Output {
 115        Self(self.0 + rhs.0)
 116    }
 117}
 118
 119impl Sub for InlayPoint {
 120    type Output = Self;
 121
 122    fn sub(self, rhs: Self) -> Self::Output {
 123        Self(self.0 - rhs.0)
 124    }
 125}
 126
 127impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
 128    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 129        self.0 += &summary.output.lines;
 130    }
 131}
 132
 133impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
 134    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 135        *self += &summary.input.len;
 136    }
 137}
 138
 139impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
 140    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 141        *self += &summary.input.lines;
 142    }
 143}
 144
 145#[derive(Clone)]
 146pub struct InlayBufferRows<'a> {
 147    transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
 148    buffer_rows: MultiBufferRows<'a>,
 149    inlay_row: u32,
 150    max_buffer_row: u32,
 151}
 152
 153pub struct InlayChunks<'a> {
 154    transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
 155    buffer_chunks: MultiBufferChunks<'a>,
 156    buffer_chunk: Option<Chunk<'a>>,
 157    inlay_chunks: Option<text::Chunks<'a>>,
 158    output_offset: InlayOffset,
 159    max_output_offset: InlayOffset,
 160    highlight_style: Option<HighlightStyle>,
 161    snapshot: &'a InlaySnapshot,
 162}
 163
 164impl<'a> InlayChunks<'a> {
 165    pub fn seek(&mut self, offset: InlayOffset) {
 166        self.transforms.seek(&offset, Bias::Right, &());
 167
 168        let buffer_offset = self.snapshot.to_buffer_offset(offset);
 169        self.buffer_chunks.seek(buffer_offset);
 170        self.inlay_chunks = None;
 171        self.buffer_chunk = None;
 172        self.output_offset = offset;
 173    }
 174
 175    pub fn offset(&self) -> InlayOffset {
 176        self.output_offset
 177    }
 178}
 179
 180impl<'a> Iterator for InlayChunks<'a> {
 181    type Item = Chunk<'a>;
 182
 183    fn next(&mut self) -> Option<Self::Item> {
 184        if self.output_offset == self.max_output_offset {
 185            return None;
 186        }
 187
 188        let chunk = match self.transforms.item()? {
 189            Transform::Isomorphic(_) => {
 190                let chunk = self
 191                    .buffer_chunk
 192                    .get_or_insert_with(|| self.buffer_chunks.next().unwrap());
 193                if chunk.text.is_empty() {
 194                    *chunk = self.buffer_chunks.next().unwrap();
 195                }
 196
 197                let (prefix, suffix) = chunk.text.split_at(cmp::min(
 198                    self.transforms.end(&()).0 .0 - self.output_offset.0,
 199                    chunk.text.len(),
 200                ));
 201
 202                chunk.text = suffix;
 203                self.output_offset.0 += prefix.len();
 204                Chunk {
 205                    text: prefix,
 206                    ..chunk.clone()
 207                }
 208            }
 209            Transform::Inlay(inlay) => {
 210                let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| {
 211                    let start = self.output_offset - self.transforms.start().0;
 212                    let end = cmp::min(self.max_output_offset, self.transforms.end(&()).0)
 213                        - self.transforms.start().0;
 214                    inlay.text.chunks_in_range(start.0..end.0)
 215                });
 216
 217                let chunk = inlay_chunks.next().unwrap();
 218                self.output_offset.0 += chunk.len();
 219                Chunk {
 220                    text: chunk,
 221                    highlight_style: self.highlight_style,
 222                    ..Default::default()
 223                }
 224            }
 225        };
 226
 227        if self.output_offset == self.transforms.end(&()).0 {
 228            self.inlay_chunks = None;
 229            self.transforms.next(&());
 230        }
 231
 232        Some(chunk)
 233    }
 234}
 235
 236impl<'a> InlayBufferRows<'a> {
 237    pub fn seek(&mut self, row: u32) {
 238        let inlay_point = InlayPoint::new(row, 0);
 239        self.transforms.seek(&inlay_point, Bias::Left, &());
 240
 241        let mut buffer_point = self.transforms.start().1;
 242        let buffer_row = if row == 0 {
 243            0
 244        } else {
 245            match self.transforms.item() {
 246                Some(Transform::Isomorphic(_)) => {
 247                    buffer_point += inlay_point.0 - self.transforms.start().0 .0;
 248                    buffer_point.row
 249                }
 250                _ => cmp::min(buffer_point.row + 1, self.max_buffer_row),
 251            }
 252        };
 253        self.inlay_row = inlay_point.row();
 254        self.buffer_rows.seek(buffer_row);
 255    }
 256}
 257
 258impl<'a> Iterator for InlayBufferRows<'a> {
 259    type Item = Option<u32>;
 260
 261    fn next(&mut self) -> Option<Self::Item> {
 262        let buffer_row = if self.inlay_row == 0 {
 263            self.buffer_rows.next().unwrap()
 264        } else {
 265            match self.transforms.item()? {
 266                Transform::Inlay(_) => None,
 267                Transform::Isomorphic(_) => self.buffer_rows.next().unwrap(),
 268            }
 269        };
 270
 271        self.inlay_row += 1;
 272        self.transforms
 273            .seek_forward(&InlayPoint::new(self.inlay_row, 0), Bias::Left, &());
 274
 275        Some(buffer_row)
 276    }
 277}
 278
 279impl InlayPoint {
 280    pub fn new(row: u32, column: u32) -> Self {
 281        Self(Point::new(row, column))
 282    }
 283
 284    pub fn row(self) -> u32 {
 285        self.0.row
 286    }
 287
 288    pub fn column(self) -> u32 {
 289        self.0.column
 290    }
 291}
 292
 293impl InlayMap {
 294    pub fn new(buffer: MultiBufferSnapshot) -> (Self, InlaySnapshot) {
 295        let version = 0;
 296        let snapshot = InlaySnapshot {
 297            buffer: buffer.clone(),
 298            transforms: SumTree::from_item(Transform::Isomorphic(buffer.text_summary()), &()),
 299            version,
 300        };
 301
 302        (
 303            Self {
 304                snapshot: Mutex::new(snapshot.clone()),
 305                inlays_by_id: Default::default(),
 306                inlays: Default::default(),
 307            },
 308            snapshot,
 309        )
 310    }
 311
 312    pub fn sync(
 313        &mut self,
 314        buffer_snapshot: MultiBufferSnapshot,
 315        mut buffer_edits: Vec<text::Edit<usize>>,
 316    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 317        let mut snapshot = self.snapshot.lock();
 318
 319        if buffer_edits.is_empty() {
 320            if snapshot.buffer.trailing_excerpt_update_count()
 321                != buffer_snapshot.trailing_excerpt_update_count()
 322            {
 323                buffer_edits.push(Edit {
 324                    old: snapshot.buffer.len()..snapshot.buffer.len(),
 325                    new: buffer_snapshot.len()..buffer_snapshot.len(),
 326                });
 327            }
 328        }
 329
 330        if buffer_edits.is_empty() {
 331            if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
 332                || snapshot.buffer.parse_count() != buffer_snapshot.parse_count()
 333                || snapshot.buffer.diagnostics_update_count()
 334                    != buffer_snapshot.diagnostics_update_count()
 335                || snapshot.buffer.git_diff_update_count()
 336                    != buffer_snapshot.git_diff_update_count()
 337                || snapshot.buffer.trailing_excerpt_update_count()
 338                    != buffer_snapshot.trailing_excerpt_update_count()
 339            {
 340                snapshot.version += 1;
 341            }
 342
 343            snapshot.buffer = buffer_snapshot;
 344            (snapshot.clone(), Vec::new())
 345        } else {
 346            let mut inlay_edits = Patch::default();
 347            let mut new_transforms = SumTree::new();
 348            let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>();
 349            let mut buffer_edits_iter = buffer_edits.iter().peekable();
 350            while let Some(buffer_edit) = buffer_edits_iter.next() {
 351                new_transforms
 352                    .push_tree(cursor.slice(&buffer_edit.old.start, Bias::Left, &()), &());
 353                if let Some(Transform::Isomorphic(transform)) = cursor.item() {
 354                    if cursor.end(&()).0 == buffer_edit.old.start {
 355                        new_transforms.push(Transform::Isomorphic(transform.clone()), &());
 356                        cursor.next(&());
 357                    }
 358                }
 359
 360                // Remove all the inlays and transforms contained by the edit.
 361                let old_start =
 362                    cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0);
 363                cursor.seek(&buffer_edit.old.end, Bias::Right, &());
 364                let old_end =
 365                    cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0);
 366
 367                // Push the unchanged prefix.
 368                let prefix_start = new_transforms.summary().input.len;
 369                let prefix_end = buffer_edit.new.start;
 370                push_isomorphic(
 371                    &mut new_transforms,
 372                    buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
 373                );
 374                let new_start = InlayOffset(new_transforms.summary().output.len);
 375
 376                let start_ix = match self.inlays.binary_search_by(|probe| {
 377                    probe
 378                        .position
 379                        .to_offset(&buffer_snapshot)
 380                        .cmp(&buffer_edit.new.start)
 381                        .then(std::cmp::Ordering::Greater)
 382                }) {
 383                    Ok(ix) | Err(ix) => ix,
 384                };
 385
 386                for inlay in &self.inlays[start_ix..] {
 387                    let buffer_offset = inlay.position.to_offset(&buffer_snapshot);
 388                    if buffer_offset > buffer_edit.new.end {
 389                        break;
 390                    }
 391
 392                    let prefix_start = new_transforms.summary().input.len;
 393                    let prefix_end = buffer_offset;
 394                    push_isomorphic(
 395                        &mut new_transforms,
 396                        buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
 397                    );
 398
 399                    if inlay.position.is_valid(&buffer_snapshot) {
 400                        new_transforms.push(Transform::Inlay(inlay.clone()), &());
 401                    }
 402                }
 403
 404                // Apply the rest of the edit.
 405                let transform_start = new_transforms.summary().input.len;
 406                push_isomorphic(
 407                    &mut new_transforms,
 408                    buffer_snapshot.text_summary_for_range(transform_start..buffer_edit.new.end),
 409                );
 410                let new_end = InlayOffset(new_transforms.summary().output.len);
 411                inlay_edits.push(Edit {
 412                    old: old_start..old_end,
 413                    new: new_start..new_end,
 414                });
 415
 416                // If the next edit doesn't intersect the current isomorphic transform, then
 417                // we can push its remainder.
 418                if buffer_edits_iter
 419                    .peek()
 420                    .map_or(true, |edit| edit.old.start >= cursor.end(&()).0)
 421                {
 422                    let transform_start = new_transforms.summary().input.len;
 423                    let transform_end =
 424                        buffer_edit.new.end + (cursor.end(&()).0 - buffer_edit.old.end);
 425                    push_isomorphic(
 426                        &mut new_transforms,
 427                        buffer_snapshot.text_summary_for_range(transform_start..transform_end),
 428                    );
 429                    cursor.next(&());
 430                }
 431            }
 432
 433            new_transforms.push_tree(cursor.suffix(&()), &());
 434            if new_transforms.first().is_none() {
 435                new_transforms.push(Transform::Isomorphic(Default::default()), &());
 436            }
 437
 438            drop(cursor);
 439            snapshot.transforms = new_transforms;
 440            snapshot.version += 1;
 441            snapshot.buffer = buffer_snapshot;
 442            snapshot.check_invariants();
 443
 444            (snapshot.clone(), inlay_edits.into_inner())
 445        }
 446    }
 447
 448    pub fn splice<T: Into<Rope>>(
 449        &mut self,
 450        to_remove: Vec<InlayId>,
 451        to_insert: Vec<(InlayId, InlayProperties<T>)>,
 452    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 453        let snapshot = self.snapshot.lock();
 454        let mut edits = BTreeSet::new();
 455
 456        self.inlays.retain(|inlay| !to_remove.contains(&inlay.id));
 457        for inlay_id in to_remove {
 458            if let Some(inlay) = self.inlays_by_id.remove(&inlay_id) {
 459                let offset = inlay.position.to_offset(&snapshot.buffer);
 460                edits.insert(offset);
 461            }
 462        }
 463
 464        for (id, properties) in to_insert {
 465            let inlay = Inlay {
 466                id,
 467                position: properties.position,
 468                text: properties.text.into(),
 469            };
 470            self.inlays_by_id.insert(inlay.id, inlay.clone());
 471            match self
 472                .inlays
 473                .binary_search_by(|probe| probe.position.cmp(&inlay.position, &snapshot.buffer))
 474            {
 475                Ok(ix) | Err(ix) => {
 476                    self.inlays.insert(ix, inlay.clone());
 477                }
 478            }
 479
 480            let offset = inlay.position.to_offset(&snapshot.buffer);
 481            edits.insert(offset);
 482        }
 483
 484        let buffer_edits = edits
 485            .into_iter()
 486            .map(|offset| Edit {
 487                old: offset..offset,
 488                new: offset..offset,
 489            })
 490            .collect();
 491        let buffer_snapshot = snapshot.buffer.clone();
 492        drop(snapshot);
 493        self.sync(buffer_snapshot, buffer_edits)
 494    }
 495
 496    #[cfg(any(test, feature = "test-support"))]
 497    pub(crate) fn randomly_mutate(
 498        &mut self,
 499        next_inlay_id: &mut usize,
 500        rng: &mut rand::rngs::StdRng,
 501    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 502        use rand::prelude::*;
 503
 504        let mut to_remove = Vec::new();
 505        let mut to_insert = Vec::new();
 506        let snapshot = self.snapshot.lock();
 507        for _ in 0..rng.gen_range(1..=5) {
 508            if self.inlays.is_empty() || rng.gen() {
 509                let position = snapshot.buffer.random_byte_range(0, rng).start;
 510                let bias = if rng.gen() { Bias::Left } else { Bias::Right };
 511                let len = rng.gen_range(1..=5);
 512                let text = util::RandomCharIter::new(&mut *rng)
 513                    .filter(|ch| *ch != '\r')
 514                    .take(len)
 515                    .collect::<String>();
 516                log::info!(
 517                    "creating inlay at buffer offset {} with bias {:?} and text {:?}",
 518                    position,
 519                    bias,
 520                    text
 521                );
 522                to_insert.push((
 523                    InlayId(post_inc(next_inlay_id)),
 524                    InlayProperties {
 525                        position: snapshot.buffer.anchor_at(position, bias),
 526                        text,
 527                    },
 528                ));
 529            } else {
 530                to_remove.push(*self.inlays_by_id.keys().choose(rng).unwrap());
 531            }
 532        }
 533        log::info!("removing inlays: {:?}", to_remove);
 534
 535        drop(snapshot);
 536        self.splice(to_remove, to_insert)
 537    }
 538}
 539
 540impl InlaySnapshot {
 541    pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
 542        let mut cursor = self
 543            .transforms
 544            .cursor::<(InlayOffset, (InlayPoint, usize))>();
 545        cursor.seek(&offset, Bias::Right, &());
 546        let overshoot = offset.0 - cursor.start().0 .0;
 547        match cursor.item() {
 548            Some(Transform::Isomorphic(_)) => {
 549                let buffer_offset_start = cursor.start().1 .1;
 550                let buffer_offset_end = buffer_offset_start + overshoot;
 551                let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
 552                let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
 553                InlayPoint(cursor.start().1 .0 .0 + (buffer_end - buffer_start))
 554            }
 555            Some(Transform::Inlay(inlay)) => {
 556                let overshoot = inlay.text.offset_to_point(overshoot);
 557                InlayPoint(cursor.start().1 .0 .0 + overshoot)
 558            }
 559            None => self.max_point(),
 560        }
 561    }
 562
 563    pub fn len(&self) -> InlayOffset {
 564        InlayOffset(self.transforms.summary().output.len)
 565    }
 566
 567    pub fn max_point(&self) -> InlayPoint {
 568        InlayPoint(self.transforms.summary().output.lines)
 569    }
 570
 571    pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
 572        let mut cursor = self
 573            .transforms
 574            .cursor::<(InlayPoint, (InlayOffset, Point))>();
 575        cursor.seek(&point, Bias::Right, &());
 576        let overshoot = point.0 - cursor.start().0 .0;
 577        match cursor.item() {
 578            Some(Transform::Isomorphic(_)) => {
 579                let buffer_point_start = cursor.start().1 .1;
 580                let buffer_point_end = buffer_point_start + overshoot;
 581                let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
 582                let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
 583                InlayOffset(cursor.start().1 .0 .0 + (buffer_offset_end - buffer_offset_start))
 584            }
 585            Some(Transform::Inlay(inlay)) => {
 586                let overshoot = inlay.text.point_to_offset(overshoot);
 587                InlayOffset(cursor.start().1 .0 .0 + overshoot)
 588            }
 589            None => self.len(),
 590        }
 591    }
 592
 593    pub fn chars_at(&self, start: InlayPoint) -> impl '_ + Iterator<Item = char> {
 594        self.chunks(self.to_offset(start)..self.len(), false, None)
 595            .flat_map(|chunk| chunk.text.chars())
 596    }
 597
 598    pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
 599        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
 600        cursor.seek(&point, Bias::Right, &());
 601        match cursor.item() {
 602            Some(Transform::Isomorphic(_)) => {
 603                let overshoot = point.0 - cursor.start().0 .0;
 604                cursor.start().1 + overshoot
 605            }
 606            Some(Transform::Inlay(_)) => cursor.start().1,
 607            None => self.buffer.max_point(),
 608        }
 609    }
 610
 611    pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
 612        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
 613        cursor.seek(&offset, Bias::Right, &());
 614        match cursor.item() {
 615            Some(Transform::Isomorphic(_)) => {
 616                let overshoot = offset - cursor.start().0;
 617                cursor.start().1 + overshoot.0
 618            }
 619            Some(Transform::Inlay(_)) => cursor.start().1,
 620            None => self.buffer.len(),
 621        }
 622    }
 623
 624    pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
 625        let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>();
 626        cursor.seek(&offset, Bias::Left, &());
 627        match cursor.item() {
 628            Some(Transform::Isomorphic(_)) => {
 629                let overshoot = offset - cursor.start().0;
 630                InlayOffset(cursor.start().1 .0 + overshoot)
 631            }
 632            Some(Transform::Inlay(_)) => cursor.start().1,
 633            None => self.len(),
 634        }
 635    }
 636
 637    pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
 638        let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>();
 639        cursor.seek(&point, Bias::Left, &());
 640        match cursor.item() {
 641            Some(Transform::Isomorphic(_)) => {
 642                let overshoot = point - cursor.start().0;
 643                InlayPoint(cursor.start().1 .0 + overshoot)
 644            }
 645            Some(Transform::Inlay(_)) => cursor.start().1,
 646            None => self.max_point(),
 647        }
 648    }
 649
 650    pub fn clip_point(&self, point: InlayPoint, bias: Bias) -> InlayPoint {
 651        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
 652        cursor.seek(&point, Bias::Left, &());
 653
 654        let mut bias = bias;
 655        let mut skipped_inlay = false;
 656        loop {
 657            match cursor.item() {
 658                Some(Transform::Isomorphic(transform)) => {
 659                    let overshoot = if skipped_inlay {
 660                        match bias {
 661                            Bias::Left => transform.lines,
 662                            Bias::Right => {
 663                                if transform.first_line_chars == 0 {
 664                                    Point::new(1, 0)
 665                                } else {
 666                                    Point::new(0, 1)
 667                                }
 668                            }
 669                        }
 670                    } else {
 671                        point.0 - cursor.start().0 .0
 672                    };
 673                    let buffer_point = cursor.start().1 + overshoot;
 674                    let clipped_buffer_point = self.buffer.clip_point(buffer_point, bias);
 675                    let clipped_overshoot = clipped_buffer_point - cursor.start().1;
 676                    return InlayPoint(cursor.start().0 .0 + clipped_overshoot);
 677                }
 678                Some(Transform::Inlay(_)) => skipped_inlay = true,
 679                None => match bias {
 680                    Bias::Left => return Default::default(),
 681                    Bias::Right => bias = Bias::Left,
 682                },
 683            }
 684
 685            if bias == Bias::Left {
 686                cursor.prev(&());
 687            } else {
 688                cursor.next(&());
 689            }
 690        }
 691    }
 692
 693    pub fn text_summary(&self) -> TextSummary {
 694        self.transforms.summary().output.clone()
 695    }
 696
 697    pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
 698        let mut summary = TextSummary::default();
 699
 700        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
 701        cursor.seek(&range.start, Bias::Right, &());
 702
 703        let overshoot = range.start.0 - cursor.start().0 .0;
 704        match cursor.item() {
 705            Some(Transform::Isomorphic(_)) => {
 706                let buffer_start = cursor.start().1;
 707                let suffix_start = buffer_start + overshoot;
 708                let suffix_end =
 709                    buffer_start + (cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0);
 710                summary = self.buffer.text_summary_for_range(suffix_start..suffix_end);
 711                cursor.next(&());
 712            }
 713            Some(Transform::Inlay(inlay)) => {
 714                let suffix_start = overshoot;
 715                let suffix_end = cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0;
 716                summary = inlay.text.cursor(suffix_start).summary(suffix_end);
 717                cursor.next(&());
 718            }
 719            None => {}
 720        }
 721
 722        if range.end > cursor.start().0 {
 723            summary += cursor
 724                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
 725                .output;
 726
 727            let overshoot = range.end.0 - cursor.start().0 .0;
 728            match cursor.item() {
 729                Some(Transform::Isomorphic(_)) => {
 730                    let prefix_start = cursor.start().1;
 731                    let prefix_end = prefix_start + overshoot;
 732                    summary += self
 733                        .buffer
 734                        .text_summary_for_range::<TextSummary, _>(prefix_start..prefix_end);
 735                }
 736                Some(Transform::Inlay(inlay)) => {
 737                    let prefix_end = overshoot;
 738                    summary += inlay.text.cursor(0).summary::<TextSummary>(prefix_end);
 739                }
 740                None => {}
 741            }
 742        }
 743
 744        summary
 745    }
 746
 747    pub fn buffer_rows<'a>(&'a self, row: u32) -> InlayBufferRows<'a> {
 748        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>();
 749        let inlay_point = InlayPoint::new(row, 0);
 750        cursor.seek(&inlay_point, Bias::Left, &());
 751
 752        let max_buffer_row = self.buffer.max_point().row;
 753        let mut buffer_point = cursor.start().1;
 754        let buffer_row = if row == 0 {
 755            0
 756        } else {
 757            match cursor.item() {
 758                Some(Transform::Isomorphic(_)) => {
 759                    buffer_point += inlay_point.0 - cursor.start().0 .0;
 760                    buffer_point.row
 761                }
 762                _ => cmp::min(buffer_point.row + 1, max_buffer_row),
 763            }
 764        };
 765
 766        InlayBufferRows {
 767            transforms: cursor,
 768            inlay_row: inlay_point.row(),
 769            buffer_rows: self.buffer.buffer_rows(buffer_row),
 770            max_buffer_row,
 771        }
 772    }
 773
 774    pub fn line_len(&self, row: u32) -> u32 {
 775        let line_start = self.to_offset(InlayPoint::new(row, 0)).0;
 776        let line_end = if row >= self.max_point().row() {
 777            self.len().0
 778        } else {
 779            self.to_offset(InlayPoint::new(row + 1, 0)).0 - 1
 780        };
 781        (line_end - line_start) as u32
 782    }
 783
 784    pub fn chunks<'a>(
 785        &'a self,
 786        range: Range<InlayOffset>,
 787        language_aware: bool,
 788        inlay_highlight_style: Option<HighlightStyle>,
 789    ) -> InlayChunks<'a> {
 790        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>();
 791        cursor.seek(&range.start, Bias::Right, &());
 792
 793        let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
 794        let buffer_chunks = self.buffer.chunks(buffer_range, language_aware);
 795
 796        InlayChunks {
 797            transforms: cursor,
 798            buffer_chunks,
 799            inlay_chunks: None,
 800            buffer_chunk: None,
 801            output_offset: range.start,
 802            max_output_offset: range.end,
 803            highlight_style: inlay_highlight_style,
 804            snapshot: self,
 805        }
 806    }
 807
 808    #[cfg(test)]
 809    pub fn text(&self) -> String {
 810        self.chunks(Default::default()..self.len(), false, None)
 811            .map(|chunk| chunk.text)
 812            .collect()
 813    }
 814
 815    fn check_invariants(&self) {
 816        #[cfg(any(debug_assertions, feature = "test-support"))]
 817        {
 818            assert_eq!(self.transforms.summary().input, self.buffer.text_summary());
 819        }
 820    }
 821}
 822
 823fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
 824    if summary.len == 0 {
 825        return;
 826    }
 827
 828    let mut summary = Some(summary);
 829    sum_tree.update_last(
 830        |transform| {
 831            if let Transform::Isomorphic(transform) = transform {
 832                *transform += summary.take().unwrap();
 833            }
 834        },
 835        &(),
 836    );
 837
 838    if let Some(summary) = summary {
 839        sum_tree.push(Transform::Isomorphic(summary), &());
 840    }
 841}
 842
 843#[cfg(test)]
 844mod tests {
 845    use super::*;
 846    use crate::MultiBuffer;
 847    use gpui::AppContext;
 848    use rand::prelude::*;
 849    use settings::SettingsStore;
 850    use std::env;
 851    use text::Patch;
 852    use util::post_inc;
 853
 854    #[gpui::test]
 855    fn test_basic_inlays(cx: &mut AppContext) {
 856        let buffer = MultiBuffer::build_simple("abcdefghi", cx);
 857        let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
 858        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
 859        assert_eq!(inlay_snapshot.text(), "abcdefghi");
 860        let mut next_inlay_id = 0;
 861
 862        let (inlay_snapshot, _) = inlay_map.splice(
 863            Vec::new(),
 864            vec![(
 865                InlayId(post_inc(&mut next_inlay_id)),
 866                InlayProperties {
 867                    position: buffer.read(cx).snapshot(cx).anchor_after(3),
 868                    text: "|123|",
 869                },
 870            )],
 871        );
 872        assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
 873        assert_eq!(
 874            inlay_snapshot.to_inlay_point(Point::new(0, 0)),
 875            InlayPoint::new(0, 0)
 876        );
 877        assert_eq!(
 878            inlay_snapshot.to_inlay_point(Point::new(0, 1)),
 879            InlayPoint::new(0, 1)
 880        );
 881        assert_eq!(
 882            inlay_snapshot.to_inlay_point(Point::new(0, 2)),
 883            InlayPoint::new(0, 2)
 884        );
 885        assert_eq!(
 886            inlay_snapshot.to_inlay_point(Point::new(0, 3)),
 887            InlayPoint::new(0, 3)
 888        );
 889        assert_eq!(
 890            inlay_snapshot.to_inlay_point(Point::new(0, 4)),
 891            InlayPoint::new(0, 9)
 892        );
 893        assert_eq!(
 894            inlay_snapshot.to_inlay_point(Point::new(0, 5)),
 895            InlayPoint::new(0, 10)
 896        );
 897        assert_eq!(
 898            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
 899            InlayPoint::new(0, 0)
 900        );
 901        assert_eq!(
 902            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
 903            InlayPoint::new(0, 0)
 904        );
 905        assert_eq!(
 906            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
 907            InlayPoint::new(0, 3)
 908        );
 909        assert_eq!(
 910            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
 911            InlayPoint::new(0, 3)
 912        );
 913        assert_eq!(
 914            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
 915            InlayPoint::new(0, 3)
 916        );
 917        assert_eq!(
 918            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
 919            InlayPoint::new(0, 9)
 920        );
 921
 922        // Edits before or after the inlay should not affect it.
 923        buffer.update(cx, |buffer, cx| {
 924            buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
 925        });
 926        let (inlay_snapshot, _) = inlay_map.sync(
 927            buffer.read(cx).snapshot(cx),
 928            buffer_edits.consume().into_inner(),
 929        );
 930        assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
 931
 932        // An edit surrounding the inlay should invalidate it.
 933        buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
 934        let (inlay_snapshot, _) = inlay_map.sync(
 935            buffer.read(cx).snapshot(cx),
 936            buffer_edits.consume().into_inner(),
 937        );
 938        assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
 939
 940        let (inlay_snapshot, _) = inlay_map.splice(
 941            Vec::new(),
 942            vec![
 943                (
 944                    InlayId(post_inc(&mut next_inlay_id)),
 945                    InlayProperties {
 946                        position: buffer.read(cx).snapshot(cx).anchor_before(3),
 947                        text: "|123|",
 948                    },
 949                ),
 950                (
 951                    InlayId(post_inc(&mut next_inlay_id)),
 952                    InlayProperties {
 953                        position: buffer.read(cx).snapshot(cx).anchor_after(3),
 954                        text: "|456|",
 955                    },
 956                ),
 957            ],
 958        );
 959        assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi");
 960
 961        // Edits ending where the inlay starts should not move it if it has a left bias.
 962        buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
 963        let (inlay_snapshot, _) = inlay_map.sync(
 964            buffer.read(cx).snapshot(cx),
 965            buffer_edits.consume().into_inner(),
 966        );
 967        assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
 968
 969        // The inlays can be manually removed.
 970        let (inlay_snapshot, _) = inlay_map
 971            .splice::<String>(inlay_map.inlays_by_id.keys().copied().collect(), Vec::new());
 972        assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi");
 973    }
 974
 975    #[gpui::test]
 976    fn test_inlay_buffer_rows(cx: &mut AppContext) {
 977        let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
 978        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
 979        assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
 980
 981        let (inlay_snapshot, _) = inlay_map.splice(
 982            Vec::new(),
 983            vec![
 984                (
 985                    InlayId(0),
 986                    InlayProperties {
 987                        position: buffer.read(cx).snapshot(cx).anchor_before(0),
 988                        text: "|123|\n",
 989                    },
 990                ),
 991                (
 992                    InlayId(1),
 993                    InlayProperties {
 994                        position: buffer.read(cx).snapshot(cx).anchor_before(4),
 995                        text: "|456|",
 996                    },
 997                ),
 998                (
 999                    InlayId(1),
1000                    InlayProperties {
1001                        position: buffer.read(cx).snapshot(cx).anchor_before(7),
1002                        text: "\n|567|\n",
1003                    },
1004                ),
1005            ],
1006        );
1007        assert_eq!(inlay_snapshot.text(), "|123|\nabc\n|456|def\n|567|\n\nghi");
1008        assert_eq!(
1009            inlay_snapshot.buffer_rows(0).collect::<Vec<_>>(),
1010            vec![Some(0), None, Some(1), None, None, Some(2)]
1011        );
1012    }
1013
1014    #[gpui::test(iterations = 100)]
1015    fn test_random_inlays(cx: &mut AppContext, mut rng: StdRng) {
1016        init_test(cx);
1017
1018        let operations = env::var("OPERATIONS")
1019            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1020            .unwrap_or(10);
1021
1022        let len = rng.gen_range(0..30);
1023        let buffer = if rng.gen() {
1024            let text = util::RandomCharIter::new(&mut rng)
1025                .take(len)
1026                .collect::<String>();
1027            MultiBuffer::build_simple(&text, cx)
1028        } else {
1029            MultiBuffer::build_random(&mut rng, cx)
1030        };
1031        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
1032        log::info!("buffer text: {:?}", buffer_snapshot.text());
1033
1034        let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1035        let mut next_inlay_id = 0;
1036
1037        for _ in 0..operations {
1038            let mut inlay_edits = Patch::default();
1039
1040            let mut prev_inlay_text = inlay_snapshot.text();
1041            let mut buffer_edits = Vec::new();
1042            match rng.gen_range(0..=100) {
1043                0..=50 => {
1044                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1045                    log::info!("mutated text: {:?}", snapshot.text());
1046                    inlay_edits = Patch::new(edits);
1047                }
1048                _ => buffer.update(cx, |buffer, cx| {
1049                    let subscription = buffer.subscribe();
1050                    let edit_count = rng.gen_range(1..=5);
1051                    buffer.randomly_mutate(&mut rng, edit_count, cx);
1052                    buffer_snapshot = buffer.snapshot(cx);
1053                    let edits = subscription.consume().into_inner();
1054                    log::info!("editing {:?}", edits);
1055                    buffer_edits.extend(edits);
1056                }),
1057            };
1058
1059            let (new_inlay_snapshot, new_inlay_edits) =
1060                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1061            inlay_snapshot = new_inlay_snapshot;
1062            inlay_edits = inlay_edits.compose(new_inlay_edits);
1063
1064            log::info!("buffer text: {:?}", buffer_snapshot.text());
1065            log::info!("inlay text: {:?}", inlay_snapshot.text());
1066
1067            let inlays = inlay_map
1068                .inlays
1069                .iter()
1070                .filter(|inlay| inlay.position.is_valid(&buffer_snapshot))
1071                .map(|inlay| {
1072                    let offset = inlay.position.to_offset(&buffer_snapshot);
1073                    (offset, inlay.clone())
1074                })
1075                .collect::<Vec<_>>();
1076            let mut expected_text = Rope::from(buffer_snapshot.text().as_str());
1077            for (offset, inlay) in inlays.into_iter().rev() {
1078                expected_text.replace(offset..offset, &inlay.text.to_string());
1079            }
1080            assert_eq!(inlay_snapshot.text(), expected_text.to_string());
1081
1082            let expected_buffer_rows = inlay_snapshot.buffer_rows(0).collect::<Vec<_>>();
1083            assert_eq!(
1084                expected_buffer_rows.len() as u32,
1085                expected_text.max_point().row + 1
1086            );
1087            for row_start in 0..expected_buffer_rows.len() {
1088                assert_eq!(
1089                    inlay_snapshot
1090                        .buffer_rows(row_start as u32)
1091                        .collect::<Vec<_>>(),
1092                    &expected_buffer_rows[row_start..],
1093                    "incorrect buffer rows starting at {}",
1094                    row_start
1095                );
1096            }
1097
1098            for _ in 0..5 {
1099                let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
1100                end = expected_text.clip_offset(end, Bias::Right);
1101                let mut start = rng.gen_range(0..=end);
1102                start = expected_text.clip_offset(start, Bias::Right);
1103
1104                let actual_text = inlay_snapshot
1105                    .chunks(InlayOffset(start)..InlayOffset(end), false, None)
1106                    .map(|chunk| chunk.text)
1107                    .collect::<String>();
1108                assert_eq!(
1109                    actual_text,
1110                    expected_text.slice(start..end).to_string(),
1111                    "incorrect text in range {:?}",
1112                    start..end
1113                );
1114
1115                assert_eq!(
1116                    inlay_snapshot.text_summary_for_range(InlayOffset(start)..InlayOffset(end)),
1117                    expected_text.slice(start..end).summary()
1118                );
1119            }
1120
1121            for edit in inlay_edits {
1122                prev_inlay_text.replace_range(
1123                    edit.new.start.0..edit.new.start.0 + edit.old_len().0,
1124                    &inlay_snapshot.text()[edit.new.start.0..edit.new.end.0],
1125                );
1126            }
1127            assert_eq!(prev_inlay_text, inlay_snapshot.text());
1128
1129            assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0);
1130            assert_eq!(expected_text.len(), inlay_snapshot.len().0);
1131
1132            let mut inlay_point = InlayPoint::default();
1133            let mut inlay_offset = InlayOffset::default();
1134            for ch in expected_text.chars() {
1135                assert_eq!(
1136                    inlay_snapshot.to_offset(inlay_point),
1137                    inlay_offset,
1138                    "invalid to_offset({:?})",
1139                    inlay_point
1140                );
1141                assert_eq!(
1142                    inlay_snapshot.to_point(inlay_offset),
1143                    inlay_point,
1144                    "invalid to_point({:?})",
1145                    inlay_offset
1146                );
1147                assert_eq!(
1148                    inlay_snapshot.to_inlay_point(inlay_snapshot.to_buffer_point(inlay_point)),
1149                    inlay_snapshot.clip_point(inlay_point, Bias::Left),
1150                    "to_buffer_point({:?}) = {:?}",
1151                    inlay_point,
1152                    inlay_snapshot.to_buffer_point(inlay_point),
1153                );
1154
1155                let mut bytes = [0; 4];
1156                for byte in ch.encode_utf8(&mut bytes).as_bytes() {
1157                    inlay_offset.0 += 1;
1158                    if *byte == b'\n' {
1159                        inlay_point.0 += Point::new(1, 0);
1160                    } else {
1161                        inlay_point.0 += Point::new(0, 1);
1162                    }
1163
1164                    let clipped_left_point = inlay_snapshot.clip_point(inlay_point, Bias::Left);
1165                    let clipped_right_point = inlay_snapshot.clip_point(inlay_point, Bias::Right);
1166                    assert!(
1167                        clipped_left_point <= clipped_right_point,
1168                        "clipped left point {:?} is greater than clipped right point {:?}",
1169                        clipped_left_point,
1170                        clipped_right_point
1171                    );
1172
1173                    // Ensure the clipped points are at valid text locations.
1174                    assert_eq!(
1175                        clipped_left_point.0,
1176                        expected_text.clip_point(clipped_left_point.0, Bias::Left)
1177                    );
1178                    assert_eq!(
1179                        clipped_right_point.0,
1180                        expected_text.clip_point(clipped_right_point.0, Bias::Right)
1181                    );
1182
1183                    // Ensure the clipped points never overshoot the end of the map.
1184                    assert!(clipped_left_point <= inlay_snapshot.max_point());
1185                    assert!(clipped_right_point <= inlay_snapshot.max_point());
1186                }
1187            }
1188        }
1189    }
1190
1191    fn init_test(cx: &mut AppContext) {
1192        cx.set_global(SettingsStore::test(cx));
1193        theme::init((), cx);
1194    }
1195}