inlay_map.rs

   1use crate::{HighlightStyles, InlayId};
   2use collections::BTreeSet;
   3use language::{Chunk, Edit, Point, TextSummary};
   4use multi_buffer::{Anchor, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset};
   5use std::{
   6    cmp,
   7    ops::{Add, AddAssign, Range, Sub, SubAssign},
   8};
   9use sum_tree::{Bias, Cursor, SumTree};
  10use text::{Patch, Rope};
  11
  12use super::{custom_highlights::CustomHighlightsChunks, Highlights};
  13
  14/// Decides where the [`Inlay`]s should be displayed.
  15///
  16/// See the [`display_map` module documentation](crate::display_map) for more information.
  17pub struct InlayMap {
  18    snapshot: InlaySnapshot,
  19    inlays: Vec<Inlay>,
  20}
  21
  22#[derive(Clone)]
  23pub struct InlaySnapshot {
  24    pub buffer: MultiBufferSnapshot,
  25    transforms: SumTree<Transform>,
  26    pub version: usize,
  27}
  28
  29#[derive(Clone, Debug)]
  30enum Transform {
  31    Isomorphic(TextSummary),
  32    Inlay(Inlay),
  33}
  34
  35#[derive(Debug, Clone)]
  36pub struct Inlay {
  37    pub(crate) id: InlayId,
  38    pub position: Anchor,
  39    pub text: text::Rope,
  40}
  41
  42impl Inlay {
  43    pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self {
  44        let mut text = hint.text();
  45        if hint.padding_right && !text.ends_with(' ') {
  46            text.push(' ');
  47        }
  48        if hint.padding_left && !text.starts_with(' ') {
  49            text.insert(0, ' ');
  50        }
  51        Self {
  52            id: InlayId::Hint(id),
  53            position,
  54            text: text.into(),
  55        }
  56    }
  57
  58    pub fn inline_completion<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
  59        Self {
  60            id: InlayId::InlineCompletion(id),
  61            position,
  62            text: text.into(),
  63        }
  64    }
  65}
  66
  67impl sum_tree::Item for Transform {
  68    type Summary = TransformSummary;
  69
  70    fn summary(&self, _cx: &()) -> Self::Summary {
  71        match self {
  72            Transform::Isomorphic(summary) => TransformSummary {
  73                input: summary.clone(),
  74                output: summary.clone(),
  75            },
  76            Transform::Inlay(inlay) => TransformSummary {
  77                input: TextSummary::default(),
  78                output: inlay.text.summary(),
  79            },
  80        }
  81    }
  82}
  83
  84#[derive(Clone, Debug, Default)]
  85struct TransformSummary {
  86    input: TextSummary,
  87    output: TextSummary,
  88}
  89
  90impl sum_tree::Summary for TransformSummary {
  91    type Context = ();
  92
  93    fn zero(_cx: &()) -> Self {
  94        Default::default()
  95    }
  96
  97    fn add_summary(&mut self, other: &Self, _: &()) {
  98        self.input += &other.input;
  99        self.output += &other.output;
 100    }
 101}
 102
 103pub type InlayEdit = Edit<InlayOffset>;
 104
 105#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 106pub struct InlayOffset(pub usize);
 107
 108impl Add for InlayOffset {
 109    type Output = Self;
 110
 111    fn add(self, rhs: Self) -> Self::Output {
 112        Self(self.0 + rhs.0)
 113    }
 114}
 115
 116impl Sub for InlayOffset {
 117    type Output = Self;
 118
 119    fn sub(self, rhs: Self) -> Self::Output {
 120        Self(self.0 - rhs.0)
 121    }
 122}
 123
 124impl AddAssign for InlayOffset {
 125    fn add_assign(&mut self, rhs: Self) {
 126        self.0 += rhs.0;
 127    }
 128}
 129
 130impl SubAssign for InlayOffset {
 131    fn sub_assign(&mut self, rhs: Self) {
 132        self.0 -= rhs.0;
 133    }
 134}
 135
 136impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
 137    fn zero(_cx: &()) -> Self {
 138        Default::default()
 139    }
 140
 141    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 142        self.0 += &summary.output.len;
 143    }
 144}
 145
 146#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 147pub struct InlayPoint(pub Point);
 148
 149impl Add for InlayPoint {
 150    type Output = Self;
 151
 152    fn add(self, rhs: Self) -> Self::Output {
 153        Self(self.0 + rhs.0)
 154    }
 155}
 156
 157impl Sub for InlayPoint {
 158    type Output = Self;
 159
 160    fn sub(self, rhs: Self) -> Self::Output {
 161        Self(self.0 - rhs.0)
 162    }
 163}
 164
 165impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
 166    fn zero(_cx: &()) -> Self {
 167        Default::default()
 168    }
 169
 170    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 171        self.0 += &summary.output.lines;
 172    }
 173}
 174
 175impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
 176    fn zero(_cx: &()) -> Self {
 177        Default::default()
 178    }
 179
 180    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 181        *self += &summary.input.len;
 182    }
 183}
 184
 185impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
 186    fn zero(_cx: &()) -> Self {
 187        Default::default()
 188    }
 189
 190    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 191        *self += &summary.input.lines;
 192    }
 193}
 194
 195#[derive(Clone)]
 196pub struct InlayBufferRows<'a> {
 197    transforms: Cursor<'a, Transform, (InlayPoint, Point)>,
 198    buffer_rows: MultiBufferRows<'a>,
 199    inlay_row: u32,
 200    max_buffer_row: MultiBufferRow,
 201}
 202
 203pub struct InlayChunks<'a> {
 204    transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
 205    buffer_chunks: CustomHighlightsChunks<'a>,
 206    buffer_chunk: Option<Chunk<'a>>,
 207    inlay_chunks: Option<text::Chunks<'a>>,
 208    inlay_chunk: Option<&'a str>,
 209    output_offset: InlayOffset,
 210    max_output_offset: InlayOffset,
 211    highlight_styles: HighlightStyles,
 212    highlights: Highlights<'a>,
 213    snapshot: &'a InlaySnapshot,
 214}
 215
 216impl<'a> InlayChunks<'a> {
 217    pub fn seek(&mut self, new_range: Range<InlayOffset>) {
 218        self.transforms.seek(&new_range.start, Bias::Right, &());
 219
 220        let buffer_range = self.snapshot.to_buffer_offset(new_range.start)
 221            ..self.snapshot.to_buffer_offset(new_range.end);
 222        self.buffer_chunks.seek(buffer_range);
 223        self.inlay_chunks = None;
 224        self.buffer_chunk = None;
 225        self.output_offset = new_range.start;
 226        self.max_output_offset = new_range.end;
 227    }
 228
 229    pub fn offset(&self) -> InlayOffset {
 230        self.output_offset
 231    }
 232}
 233
 234impl<'a> Iterator for InlayChunks<'a> {
 235    type Item = Chunk<'a>;
 236
 237    fn next(&mut self) -> Option<Self::Item> {
 238        if self.output_offset == self.max_output_offset {
 239            return None;
 240        }
 241
 242        let chunk = match self.transforms.item()? {
 243            Transform::Isomorphic(_) => {
 244                let chunk = self
 245                    .buffer_chunk
 246                    .get_or_insert_with(|| self.buffer_chunks.next().unwrap());
 247                if chunk.text.is_empty() {
 248                    *chunk = self.buffer_chunks.next().unwrap();
 249                }
 250
 251                let (prefix, suffix) = chunk.text.split_at(
 252                    chunk
 253                        .text
 254                        .len()
 255                        .min(self.transforms.end(&()).0 .0 - self.output_offset.0),
 256                );
 257
 258                chunk.text = suffix;
 259                self.output_offset.0 += prefix.len();
 260                Chunk {
 261                    text: prefix,
 262                    ..chunk.clone()
 263                }
 264            }
 265            Transform::Inlay(inlay) => {
 266                let mut inlay_style_and_highlight = None;
 267                if let Some(inlay_highlights) = self.highlights.inlay_highlights {
 268                    for (_, inlay_id_to_data) in inlay_highlights.iter() {
 269                        let style_and_highlight = inlay_id_to_data.get(&inlay.id);
 270                        if style_and_highlight.is_some() {
 271                            inlay_style_and_highlight = style_and_highlight;
 272                            break;
 273                        }
 274                    }
 275                }
 276
 277                let mut highlight_style = match inlay.id {
 278                    InlayId::InlineCompletion(_) => {
 279                        self.highlight_styles.inline_completion.map(|s| {
 280                            if inlay.text.chars().all(|c| c.is_whitespace()) {
 281                                s.whitespace
 282                            } else {
 283                                s.insertion
 284                            }
 285                        })
 286                    }
 287                    InlayId::Hint(_) => self.highlight_styles.inlay_hint,
 288                };
 289                let next_inlay_highlight_endpoint;
 290                let offset_in_inlay = self.output_offset - self.transforms.start().0;
 291                if let Some((style, highlight)) = inlay_style_and_highlight {
 292                    let range = &highlight.range;
 293                    if offset_in_inlay.0 < range.start {
 294                        next_inlay_highlight_endpoint = range.start - offset_in_inlay.0;
 295                    } else if offset_in_inlay.0 >= range.end {
 296                        next_inlay_highlight_endpoint = usize::MAX;
 297                    } else {
 298                        next_inlay_highlight_endpoint = range.end - offset_in_inlay.0;
 299                        highlight_style
 300                            .get_or_insert_with(Default::default)
 301                            .highlight(*style);
 302                    }
 303                } else {
 304                    next_inlay_highlight_endpoint = usize::MAX;
 305                }
 306
 307                let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| {
 308                    let start = offset_in_inlay;
 309                    let end = cmp::min(self.max_output_offset, self.transforms.end(&()).0)
 310                        - self.transforms.start().0;
 311                    inlay.text.chunks_in_range(start.0..end.0)
 312                });
 313                let inlay_chunk = self
 314                    .inlay_chunk
 315                    .get_or_insert_with(|| inlay_chunks.next().unwrap());
 316                let (chunk, remainder) =
 317                    inlay_chunk.split_at(inlay_chunk.len().min(next_inlay_highlight_endpoint));
 318                *inlay_chunk = remainder;
 319                if inlay_chunk.is_empty() {
 320                    self.inlay_chunk = None;
 321                }
 322
 323                self.output_offset.0 += chunk.len();
 324
 325                Chunk {
 326                    text: chunk,
 327                    highlight_style,
 328                    ..Default::default()
 329                }
 330            }
 331        };
 332
 333        if self.output_offset == self.transforms.end(&()).0 {
 334            self.inlay_chunks = None;
 335            self.transforms.next(&());
 336        }
 337
 338        Some(chunk)
 339    }
 340}
 341
 342impl<'a> InlayBufferRows<'a> {
 343    pub fn seek(&mut self, row: u32) {
 344        let inlay_point = InlayPoint::new(row, 0);
 345        self.transforms.seek(&inlay_point, Bias::Left, &());
 346
 347        let mut buffer_point = self.transforms.start().1;
 348        let buffer_row = MultiBufferRow(if row == 0 {
 349            0
 350        } else {
 351            match self.transforms.item() {
 352                Some(Transform::Isomorphic(_)) => {
 353                    buffer_point += inlay_point.0 - self.transforms.start().0 .0;
 354                    buffer_point.row
 355                }
 356                _ => cmp::min(buffer_point.row + 1, self.max_buffer_row.0),
 357            }
 358        });
 359        self.inlay_row = inlay_point.row();
 360        self.buffer_rows.seek(buffer_row);
 361    }
 362}
 363
 364impl<'a> Iterator for InlayBufferRows<'a> {
 365    type Item = Option<u32>;
 366
 367    fn next(&mut self) -> Option<Self::Item> {
 368        let buffer_row = if self.inlay_row == 0 {
 369            self.buffer_rows.next().unwrap()
 370        } else {
 371            match self.transforms.item()? {
 372                Transform::Inlay(_) => None,
 373                Transform::Isomorphic(_) => self.buffer_rows.next().unwrap(),
 374            }
 375        };
 376
 377        self.inlay_row += 1;
 378        self.transforms
 379            .seek_forward(&InlayPoint::new(self.inlay_row, 0), Bias::Left, &());
 380
 381        Some(buffer_row)
 382    }
 383}
 384
 385impl InlayPoint {
 386    pub fn new(row: u32, column: u32) -> Self {
 387        Self(Point::new(row, column))
 388    }
 389
 390    pub fn row(self) -> u32 {
 391        self.0.row
 392    }
 393}
 394
 395impl InlayMap {
 396    pub fn new(buffer: MultiBufferSnapshot) -> (Self, InlaySnapshot) {
 397        let version = 0;
 398        let snapshot = InlaySnapshot {
 399            buffer: buffer.clone(),
 400            transforms: SumTree::from_iter(Some(Transform::Isomorphic(buffer.text_summary())), &()),
 401            version,
 402        };
 403
 404        (
 405            Self {
 406                snapshot: snapshot.clone(),
 407                inlays: Vec::new(),
 408            },
 409            snapshot,
 410        )
 411    }
 412
 413    pub fn sync(
 414        &mut self,
 415        buffer_snapshot: MultiBufferSnapshot,
 416        mut buffer_edits: Vec<text::Edit<usize>>,
 417    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 418        let snapshot = &mut self.snapshot;
 419
 420        if buffer_edits.is_empty()
 421            && snapshot.buffer.trailing_excerpt_update_count()
 422                != buffer_snapshot.trailing_excerpt_update_count()
 423        {
 424            buffer_edits.push(Edit {
 425                old: snapshot.buffer.len()..snapshot.buffer.len(),
 426                new: buffer_snapshot.len()..buffer_snapshot.len(),
 427            });
 428        }
 429
 430        if buffer_edits.is_empty() {
 431            if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
 432                || snapshot.buffer.non_text_state_update_count()
 433                    != buffer_snapshot.non_text_state_update_count()
 434                || snapshot.buffer.trailing_excerpt_update_count()
 435                    != buffer_snapshot.trailing_excerpt_update_count()
 436            {
 437                snapshot.version += 1;
 438            }
 439
 440            snapshot.buffer = buffer_snapshot;
 441            (snapshot.clone(), Vec::new())
 442        } else {
 443            let mut inlay_edits = Patch::default();
 444            let mut new_transforms = SumTree::default();
 445            let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>(&());
 446            let mut buffer_edits_iter = buffer_edits.iter().peekable();
 447            while let Some(buffer_edit) = buffer_edits_iter.next() {
 448                new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left, &()), &());
 449                if let Some(Transform::Isomorphic(transform)) = cursor.item() {
 450                    if cursor.end(&()).0 == buffer_edit.old.start {
 451                        push_isomorphic(&mut new_transforms, transform.clone());
 452                        cursor.next(&());
 453                    }
 454                }
 455
 456                // Remove all the inlays and transforms contained by the edit.
 457                let old_start =
 458                    cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0);
 459                cursor.seek(&buffer_edit.old.end, Bias::Right, &());
 460                let old_end =
 461                    cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0);
 462
 463                // Push the unchanged prefix.
 464                let prefix_start = new_transforms.summary().input.len;
 465                let prefix_end = buffer_edit.new.start;
 466                push_isomorphic(
 467                    &mut new_transforms,
 468                    buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
 469                );
 470                let new_start = InlayOffset(new_transforms.summary().output.len);
 471
 472                let start_ix = match self.inlays.binary_search_by(|probe| {
 473                    probe
 474                        .position
 475                        .to_offset(&buffer_snapshot)
 476                        .cmp(&buffer_edit.new.start)
 477                        .then(std::cmp::Ordering::Greater)
 478                }) {
 479                    Ok(ix) | Err(ix) => ix,
 480                };
 481
 482                for inlay in &self.inlays[start_ix..] {
 483                    let buffer_offset = inlay.position.to_offset(&buffer_snapshot);
 484                    if buffer_offset > buffer_edit.new.end {
 485                        break;
 486                    }
 487
 488                    let prefix_start = new_transforms.summary().input.len;
 489                    let prefix_end = buffer_offset;
 490                    push_isomorphic(
 491                        &mut new_transforms,
 492                        buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
 493                    );
 494
 495                    if inlay.position.is_valid(&buffer_snapshot) {
 496                        new_transforms.push(Transform::Inlay(inlay.clone()), &());
 497                    }
 498                }
 499
 500                // Apply the rest of the edit.
 501                let transform_start = new_transforms.summary().input.len;
 502                push_isomorphic(
 503                    &mut new_transforms,
 504                    buffer_snapshot.text_summary_for_range(transform_start..buffer_edit.new.end),
 505                );
 506                let new_end = InlayOffset(new_transforms.summary().output.len);
 507                inlay_edits.push(Edit {
 508                    old: old_start..old_end,
 509                    new: new_start..new_end,
 510                });
 511
 512                // If the next edit doesn't intersect the current isomorphic transform, then
 513                // we can push its remainder.
 514                if buffer_edits_iter
 515                    .peek()
 516                    .map_or(true, |edit| edit.old.start >= cursor.end(&()).0)
 517                {
 518                    let transform_start = new_transforms.summary().input.len;
 519                    let transform_end =
 520                        buffer_edit.new.end + (cursor.end(&()).0 - buffer_edit.old.end);
 521                    push_isomorphic(
 522                        &mut new_transforms,
 523                        buffer_snapshot.text_summary_for_range(transform_start..transform_end),
 524                    );
 525                    cursor.next(&());
 526                }
 527            }
 528
 529            new_transforms.append(cursor.suffix(&()), &());
 530            if new_transforms.is_empty() {
 531                new_transforms.push(Transform::Isomorphic(Default::default()), &());
 532            }
 533
 534            drop(cursor);
 535            snapshot.transforms = new_transforms;
 536            snapshot.version += 1;
 537            snapshot.buffer = buffer_snapshot;
 538            snapshot.check_invariants();
 539
 540            (snapshot.clone(), inlay_edits.into_inner())
 541        }
 542    }
 543
 544    pub fn splice(
 545        &mut self,
 546        to_remove: Vec<InlayId>,
 547        to_insert: Vec<Inlay>,
 548    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 549        let snapshot = &mut self.snapshot;
 550        let mut edits = BTreeSet::new();
 551
 552        self.inlays.retain(|inlay| {
 553            let retain = !to_remove.contains(&inlay.id);
 554            if !retain {
 555                let offset = inlay.position.to_offset(&snapshot.buffer);
 556                edits.insert(offset);
 557            }
 558            retain
 559        });
 560
 561        for inlay_to_insert in to_insert {
 562            // Avoid inserting empty inlays.
 563            if inlay_to_insert.text.is_empty() {
 564                continue;
 565            }
 566
 567            let offset = inlay_to_insert.position.to_offset(&snapshot.buffer);
 568            match self.inlays.binary_search_by(|probe| {
 569                probe
 570                    .position
 571                    .cmp(&inlay_to_insert.position, &snapshot.buffer)
 572                    .then(std::cmp::Ordering::Less)
 573            }) {
 574                Ok(ix) | Err(ix) => {
 575                    self.inlays.insert(ix, inlay_to_insert);
 576                }
 577            }
 578
 579            edits.insert(offset);
 580        }
 581
 582        let buffer_edits = edits
 583            .into_iter()
 584            .map(|offset| Edit {
 585                old: offset..offset,
 586                new: offset..offset,
 587            })
 588            .collect();
 589        let buffer_snapshot = snapshot.buffer.clone();
 590        let (snapshot, edits) = self.sync(buffer_snapshot, buffer_edits);
 591        (snapshot, edits)
 592    }
 593
 594    pub fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
 595        self.inlays.iter()
 596    }
 597
 598    #[cfg(test)]
 599    pub(crate) fn randomly_mutate(
 600        &mut self,
 601        next_inlay_id: &mut usize,
 602        rng: &mut rand::rngs::StdRng,
 603    ) -> (InlaySnapshot, Vec<InlayEdit>) {
 604        use rand::prelude::*;
 605        use util::post_inc;
 606
 607        let mut to_remove = Vec::new();
 608        let mut to_insert = Vec::new();
 609        let snapshot = &mut self.snapshot;
 610        for i in 0..rng.gen_range(1..=5) {
 611            if self.inlays.is_empty() || rng.gen() {
 612                let position = snapshot.buffer.random_byte_range(0, rng).start;
 613                let bias = if rng.gen() { Bias::Left } else { Bias::Right };
 614                let len = if rng.gen_bool(0.01) {
 615                    0
 616                } else {
 617                    rng.gen_range(1..=5)
 618                };
 619                let text = util::RandomCharIter::new(&mut *rng)
 620                    .filter(|ch| *ch != '\r')
 621                    .take(len)
 622                    .collect::<String>();
 623
 624                let inlay_id = if i % 2 == 0 {
 625                    InlayId::Hint(post_inc(next_inlay_id))
 626                } else {
 627                    InlayId::InlineCompletion(post_inc(next_inlay_id))
 628                };
 629                log::info!(
 630                    "creating inlay {:?} at buffer offset {} with bias {:?} and text {:?}",
 631                    inlay_id,
 632                    position,
 633                    bias,
 634                    text
 635                );
 636
 637                to_insert.push(Inlay {
 638                    id: inlay_id,
 639                    position: snapshot.buffer.anchor_at(position, bias),
 640                    text: text.into(),
 641                });
 642            } else {
 643                to_remove.push(
 644                    self.inlays
 645                        .iter()
 646                        .choose(rng)
 647                        .map(|inlay| inlay.id)
 648                        .unwrap(),
 649                );
 650            }
 651        }
 652        log::info!("removing inlays: {:?}", to_remove);
 653
 654        let (snapshot, edits) = self.splice(to_remove, to_insert);
 655        (snapshot, edits)
 656    }
 657}
 658
 659impl InlaySnapshot {
 660    pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
 661        let mut cursor = self
 662            .transforms
 663            .cursor::<(InlayOffset, (InlayPoint, usize))>(&());
 664        cursor.seek(&offset, Bias::Right, &());
 665        let overshoot = offset.0 - cursor.start().0 .0;
 666        match cursor.item() {
 667            Some(Transform::Isomorphic(_)) => {
 668                let buffer_offset_start = cursor.start().1 .1;
 669                let buffer_offset_end = buffer_offset_start + overshoot;
 670                let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
 671                let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
 672                InlayPoint(cursor.start().1 .0 .0 + (buffer_end - buffer_start))
 673            }
 674            Some(Transform::Inlay(inlay)) => {
 675                let overshoot = inlay.text.offset_to_point(overshoot);
 676                InlayPoint(cursor.start().1 .0 .0 + overshoot)
 677            }
 678            None => self.max_point(),
 679        }
 680    }
 681
 682    pub fn len(&self) -> InlayOffset {
 683        InlayOffset(self.transforms.summary().output.len)
 684    }
 685
 686    pub fn max_point(&self) -> InlayPoint {
 687        InlayPoint(self.transforms.summary().output.lines)
 688    }
 689
 690    pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
 691        let mut cursor = self
 692            .transforms
 693            .cursor::<(InlayPoint, (InlayOffset, Point))>(&());
 694        cursor.seek(&point, Bias::Right, &());
 695        let overshoot = point.0 - cursor.start().0 .0;
 696        match cursor.item() {
 697            Some(Transform::Isomorphic(_)) => {
 698                let buffer_point_start = cursor.start().1 .1;
 699                let buffer_point_end = buffer_point_start + overshoot;
 700                let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
 701                let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
 702                InlayOffset(cursor.start().1 .0 .0 + (buffer_offset_end - buffer_offset_start))
 703            }
 704            Some(Transform::Inlay(inlay)) => {
 705                let overshoot = inlay.text.point_to_offset(overshoot);
 706                InlayOffset(cursor.start().1 .0 .0 + overshoot)
 707            }
 708            None => self.len(),
 709        }
 710    }
 711    pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
 712        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
 713        cursor.seek(&point, Bias::Right, &());
 714        match cursor.item() {
 715            Some(Transform::Isomorphic(_)) => {
 716                let overshoot = point.0 - cursor.start().0 .0;
 717                cursor.start().1 + overshoot
 718            }
 719            Some(Transform::Inlay(_)) => cursor.start().1,
 720            None => self.buffer.max_point(),
 721        }
 722    }
 723    pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
 724        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
 725        cursor.seek(&offset, Bias::Right, &());
 726        match cursor.item() {
 727            Some(Transform::Isomorphic(_)) => {
 728                let overshoot = offset - cursor.start().0;
 729                cursor.start().1 + overshoot.0
 730            }
 731            Some(Transform::Inlay(_)) => cursor.start().1,
 732            None => self.buffer.len(),
 733        }
 734    }
 735
 736    pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
 737        let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>(&());
 738        cursor.seek(&offset, Bias::Left, &());
 739        loop {
 740            match cursor.item() {
 741                Some(Transform::Isomorphic(_)) => {
 742                    if offset == cursor.end(&()).0 {
 743                        while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
 744                            if inlay.position.bias() == Bias::Right {
 745                                break;
 746                            } else {
 747                                cursor.next(&());
 748                            }
 749                        }
 750                        return cursor.end(&()).1;
 751                    } else {
 752                        let overshoot = offset - cursor.start().0;
 753                        return InlayOffset(cursor.start().1 .0 + overshoot);
 754                    }
 755                }
 756                Some(Transform::Inlay(inlay)) => {
 757                    if inlay.position.bias() == Bias::Left {
 758                        cursor.next(&());
 759                    } else {
 760                        return cursor.start().1;
 761                    }
 762                }
 763                None => {
 764                    return self.len();
 765                }
 766            }
 767        }
 768    }
 769    pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
 770        let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>(&());
 771        cursor.seek(&point, Bias::Left, &());
 772        loop {
 773            match cursor.item() {
 774                Some(Transform::Isomorphic(_)) => {
 775                    if point == cursor.end(&()).0 {
 776                        while let Some(Transform::Inlay(inlay)) = cursor.next_item() {
 777                            if inlay.position.bias() == Bias::Right {
 778                                break;
 779                            } else {
 780                                cursor.next(&());
 781                            }
 782                        }
 783                        return cursor.end(&()).1;
 784                    } else {
 785                        let overshoot = point - cursor.start().0;
 786                        return InlayPoint(cursor.start().1 .0 + overshoot);
 787                    }
 788                }
 789                Some(Transform::Inlay(inlay)) => {
 790                    if inlay.position.bias() == Bias::Left {
 791                        cursor.next(&());
 792                    } else {
 793                        return cursor.start().1;
 794                    }
 795                }
 796                None => {
 797                    return self.max_point();
 798                }
 799            }
 800        }
 801    }
 802
 803    pub fn clip_point(&self, mut point: InlayPoint, mut bias: Bias) -> InlayPoint {
 804        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
 805        cursor.seek(&point, Bias::Left, &());
 806        loop {
 807            match cursor.item() {
 808                Some(Transform::Isomorphic(transform)) => {
 809                    if cursor.start().0 == point {
 810                        if let Some(Transform::Inlay(inlay)) = cursor.prev_item() {
 811                            if inlay.position.bias() == Bias::Left {
 812                                return point;
 813                            } else if bias == Bias::Left {
 814                                cursor.prev(&());
 815                            } else if transform.first_line_chars == 0 {
 816                                point.0 += Point::new(1, 0);
 817                            } else {
 818                                point.0 += Point::new(0, 1);
 819                            }
 820                        } else {
 821                            return point;
 822                        }
 823                    } else if cursor.end(&()).0 == point {
 824                        if let Some(Transform::Inlay(inlay)) = cursor.next_item() {
 825                            if inlay.position.bias() == Bias::Right {
 826                                return point;
 827                            } else if bias == Bias::Right {
 828                                cursor.next(&());
 829                            } else if point.0.column == 0 {
 830                                point.0.row -= 1;
 831                                point.0.column = self.line_len(point.0.row);
 832                            } else {
 833                                point.0.column -= 1;
 834                            }
 835                        } else {
 836                            return point;
 837                        }
 838                    } else {
 839                        let overshoot = point.0 - cursor.start().0 .0;
 840                        let buffer_point = cursor.start().1 + overshoot;
 841                        let clipped_buffer_point = self.buffer.clip_point(buffer_point, bias);
 842                        let clipped_overshoot = clipped_buffer_point - cursor.start().1;
 843                        let clipped_point = InlayPoint(cursor.start().0 .0 + clipped_overshoot);
 844                        if clipped_point == point {
 845                            return clipped_point;
 846                        } else {
 847                            point = clipped_point;
 848                        }
 849                    }
 850                }
 851                Some(Transform::Inlay(inlay)) => {
 852                    if point == cursor.start().0 && inlay.position.bias() == Bias::Right {
 853                        match cursor.prev_item() {
 854                            Some(Transform::Inlay(inlay)) => {
 855                                if inlay.position.bias() == Bias::Left {
 856                                    return point;
 857                                }
 858                            }
 859                            _ => return point,
 860                        }
 861                    } else if point == cursor.end(&()).0 && inlay.position.bias() == Bias::Left {
 862                        match cursor.next_item() {
 863                            Some(Transform::Inlay(inlay)) => {
 864                                if inlay.position.bias() == Bias::Right {
 865                                    return point;
 866                                }
 867                            }
 868                            _ => return point,
 869                        }
 870                    }
 871
 872                    if bias == Bias::Left {
 873                        point = cursor.start().0;
 874                        cursor.prev(&());
 875                    } else {
 876                        cursor.next(&());
 877                        point = cursor.start().0;
 878                    }
 879                }
 880                None => {
 881                    bias = bias.invert();
 882                    if bias == Bias::Left {
 883                        point = cursor.start().0;
 884                        cursor.prev(&());
 885                    } else {
 886                        cursor.next(&());
 887                        point = cursor.start().0;
 888                    }
 889                }
 890            }
 891        }
 892    }
 893
 894    pub fn text_summary(&self) -> TextSummary {
 895        self.transforms.summary().output.clone()
 896    }
 897
 898    pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
 899        let mut summary = TextSummary::default();
 900
 901        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
 902        cursor.seek(&range.start, Bias::Right, &());
 903
 904        let overshoot = range.start.0 - cursor.start().0 .0;
 905        match cursor.item() {
 906            Some(Transform::Isomorphic(_)) => {
 907                let buffer_start = cursor.start().1;
 908                let suffix_start = buffer_start + overshoot;
 909                let suffix_end =
 910                    buffer_start + (cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0);
 911                summary = self.buffer.text_summary_for_range(suffix_start..suffix_end);
 912                cursor.next(&());
 913            }
 914            Some(Transform::Inlay(inlay)) => {
 915                let suffix_start = overshoot;
 916                let suffix_end = cmp::min(cursor.end(&()).0, range.end).0 - cursor.start().0 .0;
 917                summary = inlay.text.cursor(suffix_start).summary(suffix_end);
 918                cursor.next(&());
 919            }
 920            None => {}
 921        }
 922
 923        if range.end > cursor.start().0 {
 924            summary += cursor
 925                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
 926                .output;
 927
 928            let overshoot = range.end.0 - cursor.start().0 .0;
 929            match cursor.item() {
 930                Some(Transform::Isomorphic(_)) => {
 931                    let prefix_start = cursor.start().1;
 932                    let prefix_end = prefix_start + overshoot;
 933                    summary += self
 934                        .buffer
 935                        .text_summary_for_range::<TextSummary, _>(prefix_start..prefix_end);
 936                }
 937                Some(Transform::Inlay(inlay)) => {
 938                    let prefix_end = overshoot;
 939                    summary += inlay.text.cursor(0).summary::<TextSummary>(prefix_end);
 940                }
 941                None => {}
 942            }
 943        }
 944
 945        summary
 946    }
 947
 948    pub fn buffer_rows(&self, row: u32) -> InlayBufferRows<'_> {
 949        let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
 950        let inlay_point = InlayPoint::new(row, 0);
 951        cursor.seek(&inlay_point, Bias::Left, &());
 952
 953        let max_buffer_row = self.buffer.max_row();
 954        let mut buffer_point = cursor.start().1;
 955        let buffer_row = if row == 0 {
 956            MultiBufferRow(0)
 957        } else {
 958            match cursor.item() {
 959                Some(Transform::Isomorphic(_)) => {
 960                    buffer_point += inlay_point.0 - cursor.start().0 .0;
 961                    MultiBufferRow(buffer_point.row)
 962                }
 963                _ => cmp::min(MultiBufferRow(buffer_point.row + 1), max_buffer_row),
 964            }
 965        };
 966
 967        InlayBufferRows {
 968            transforms: cursor,
 969            inlay_row: inlay_point.row(),
 970            buffer_rows: self.buffer.buffer_rows(buffer_row),
 971            max_buffer_row,
 972        }
 973    }
 974
 975    pub fn line_len(&self, row: u32) -> u32 {
 976        let line_start = self.to_offset(InlayPoint::new(row, 0)).0;
 977        let line_end = if row >= self.max_point().row() {
 978            self.len().0
 979        } else {
 980            self.to_offset(InlayPoint::new(row + 1, 0)).0 - 1
 981        };
 982        (line_end - line_start) as u32
 983    }
 984
 985    pub(crate) fn chunks<'a>(
 986        &'a self,
 987        range: Range<InlayOffset>,
 988        language_aware: bool,
 989        highlights: Highlights<'a>,
 990    ) -> InlayChunks<'a> {
 991        let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
 992        cursor.seek(&range.start, Bias::Right, &());
 993
 994        let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
 995        let buffer_chunks = CustomHighlightsChunks::new(
 996            buffer_range,
 997            language_aware,
 998            highlights.text_highlights,
 999            &self.buffer,
1000        );
1001
1002        InlayChunks {
1003            transforms: cursor,
1004            buffer_chunks,
1005            inlay_chunks: None,
1006            inlay_chunk: None,
1007            buffer_chunk: None,
1008            output_offset: range.start,
1009            max_output_offset: range.end,
1010            highlight_styles: highlights.styles,
1011            highlights,
1012            snapshot: self,
1013        }
1014    }
1015
1016    #[cfg(test)]
1017    pub fn text(&self) -> String {
1018        self.chunks(Default::default()..self.len(), false, Highlights::default())
1019            .map(|chunk| chunk.text)
1020            .collect()
1021    }
1022
1023    fn check_invariants(&self) {
1024        #[cfg(any(debug_assertions, feature = "test-support"))]
1025        {
1026            assert_eq!(self.transforms.summary().input, self.buffer.text_summary());
1027            let mut transforms = self.transforms.iter().peekable();
1028            while let Some(transform) = transforms.next() {
1029                let transform_is_isomorphic = matches!(transform, Transform::Isomorphic(_));
1030                if let Some(next_transform) = transforms.peek() {
1031                    let next_transform_is_isomorphic =
1032                        matches!(next_transform, Transform::Isomorphic(_));
1033                    assert!(
1034                        !transform_is_isomorphic || !next_transform_is_isomorphic,
1035                        "two adjacent isomorphic transforms"
1036                    );
1037                }
1038            }
1039        }
1040    }
1041}
1042
1043fn push_isomorphic(sum_tree: &mut SumTree<Transform>, summary: TextSummary) {
1044    if summary.len == 0 {
1045        return;
1046    }
1047
1048    let mut summary = Some(summary);
1049    sum_tree.update_last(
1050        |transform| {
1051            if let Transform::Isomorphic(transform) = transform {
1052                *transform += summary.take().unwrap();
1053            }
1054        },
1055        &(),
1056    );
1057
1058    if let Some(summary) = summary {
1059        sum_tree.push(Transform::Isomorphic(summary), &());
1060    }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065    use super::*;
1066    use crate::{
1067        display_map::{InlayHighlights, TextHighlights},
1068        hover_links::InlayHighlight,
1069        InlayId, MultiBuffer,
1070    };
1071    use gpui::{AppContext, HighlightStyle};
1072    use project::{InlayHint, InlayHintLabel, ResolveState};
1073    use rand::prelude::*;
1074    use settings::SettingsStore;
1075    use std::{any::TypeId, cmp::Reverse, env, sync::Arc};
1076    use sum_tree::TreeMap;
1077    use text::Patch;
1078    use util::post_inc;
1079
1080    #[test]
1081    fn test_inlay_properties_label_padding() {
1082        assert_eq!(
1083            Inlay::hint(
1084                0,
1085                Anchor::min(),
1086                &InlayHint {
1087                    label: InlayHintLabel::String("a".to_string()),
1088                    position: text::Anchor::default(),
1089                    padding_left: false,
1090                    padding_right: false,
1091                    tooltip: None,
1092                    kind: None,
1093                    resolve_state: ResolveState::Resolved,
1094                },
1095            )
1096            .text
1097            .to_string(),
1098            "a",
1099            "Should not pad label if not requested"
1100        );
1101
1102        assert_eq!(
1103            Inlay::hint(
1104                0,
1105                Anchor::min(),
1106                &InlayHint {
1107                    label: InlayHintLabel::String("a".to_string()),
1108                    position: text::Anchor::default(),
1109                    padding_left: true,
1110                    padding_right: true,
1111                    tooltip: None,
1112                    kind: None,
1113                    resolve_state: ResolveState::Resolved,
1114                },
1115            )
1116            .text
1117            .to_string(),
1118            " a ",
1119            "Should pad label for every side requested"
1120        );
1121
1122        assert_eq!(
1123            Inlay::hint(
1124                0,
1125                Anchor::min(),
1126                &InlayHint {
1127                    label: InlayHintLabel::String(" a ".to_string()),
1128                    position: text::Anchor::default(),
1129                    padding_left: false,
1130                    padding_right: false,
1131                    tooltip: None,
1132                    kind: None,
1133                    resolve_state: ResolveState::Resolved,
1134                },
1135            )
1136            .text
1137            .to_string(),
1138            " a ",
1139            "Should not change already padded label"
1140        );
1141
1142        assert_eq!(
1143            Inlay::hint(
1144                0,
1145                Anchor::min(),
1146                &InlayHint {
1147                    label: InlayHintLabel::String(" a ".to_string()),
1148                    position: text::Anchor::default(),
1149                    padding_left: true,
1150                    padding_right: true,
1151                    tooltip: None,
1152                    kind: None,
1153                    resolve_state: ResolveState::Resolved,
1154                },
1155            )
1156            .text
1157            .to_string(),
1158            " a ",
1159            "Should not change already padded label"
1160        );
1161    }
1162
1163    #[gpui::test]
1164    fn test_basic_inlays(cx: &mut AppContext) {
1165        let buffer = MultiBuffer::build_simple("abcdefghi", cx);
1166        let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
1167        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
1168        assert_eq!(inlay_snapshot.text(), "abcdefghi");
1169        let mut next_inlay_id = 0;
1170
1171        let (inlay_snapshot, _) = inlay_map.splice(
1172            Vec::new(),
1173            vec![Inlay {
1174                id: InlayId::Hint(post_inc(&mut next_inlay_id)),
1175                position: buffer.read(cx).snapshot(cx).anchor_after(3),
1176                text: "|123|".into(),
1177            }],
1178        );
1179        assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
1180        assert_eq!(
1181            inlay_snapshot.to_inlay_point(Point::new(0, 0)),
1182            InlayPoint::new(0, 0)
1183        );
1184        assert_eq!(
1185            inlay_snapshot.to_inlay_point(Point::new(0, 1)),
1186            InlayPoint::new(0, 1)
1187        );
1188        assert_eq!(
1189            inlay_snapshot.to_inlay_point(Point::new(0, 2)),
1190            InlayPoint::new(0, 2)
1191        );
1192        assert_eq!(
1193            inlay_snapshot.to_inlay_point(Point::new(0, 3)),
1194            InlayPoint::new(0, 3)
1195        );
1196        assert_eq!(
1197            inlay_snapshot.to_inlay_point(Point::new(0, 4)),
1198            InlayPoint::new(0, 9)
1199        );
1200        assert_eq!(
1201            inlay_snapshot.to_inlay_point(Point::new(0, 5)),
1202            InlayPoint::new(0, 10)
1203        );
1204        assert_eq!(
1205            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
1206            InlayPoint::new(0, 0)
1207        );
1208        assert_eq!(
1209            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
1210            InlayPoint::new(0, 0)
1211        );
1212        assert_eq!(
1213            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
1214            InlayPoint::new(0, 3)
1215        );
1216        assert_eq!(
1217            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
1218            InlayPoint::new(0, 3)
1219        );
1220        assert_eq!(
1221            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
1222            InlayPoint::new(0, 3)
1223        );
1224        assert_eq!(
1225            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
1226            InlayPoint::new(0, 9)
1227        );
1228
1229        // Edits before or after the inlay should not affect it.
1230        buffer.update(cx, |buffer, cx| {
1231            buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
1232        });
1233        let (inlay_snapshot, _) = inlay_map.sync(
1234            buffer.read(cx).snapshot(cx),
1235            buffer_edits.consume().into_inner(),
1236        );
1237        assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
1238
1239        // An edit surrounding the inlay should invalidate it.
1240        buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
1241        let (inlay_snapshot, _) = inlay_map.sync(
1242            buffer.read(cx).snapshot(cx),
1243            buffer_edits.consume().into_inner(),
1244        );
1245        assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
1246
1247        let (inlay_snapshot, _) = inlay_map.splice(
1248            Vec::new(),
1249            vec![
1250                Inlay {
1251                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
1252                    position: buffer.read(cx).snapshot(cx).anchor_before(3),
1253                    text: "|123|".into(),
1254                },
1255                Inlay {
1256                    id: InlayId::InlineCompletion(post_inc(&mut next_inlay_id)),
1257                    position: buffer.read(cx).snapshot(cx).anchor_after(3),
1258                    text: "|456|".into(),
1259                },
1260            ],
1261        );
1262        assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi");
1263
1264        // Edits ending where the inlay starts should not move it if it has a left bias.
1265        buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
1266        let (inlay_snapshot, _) = inlay_map.sync(
1267            buffer.read(cx).snapshot(cx),
1268            buffer_edits.consume().into_inner(),
1269        );
1270        assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
1271
1272        assert_eq!(
1273            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Left),
1274            InlayPoint::new(0, 0)
1275        );
1276        assert_eq!(
1277            inlay_snapshot.clip_point(InlayPoint::new(0, 0), Bias::Right),
1278            InlayPoint::new(0, 0)
1279        );
1280
1281        assert_eq!(
1282            inlay_snapshot.clip_point(InlayPoint::new(0, 1), Bias::Left),
1283            InlayPoint::new(0, 1)
1284        );
1285        assert_eq!(
1286            inlay_snapshot.clip_point(InlayPoint::new(0, 1), Bias::Right),
1287            InlayPoint::new(0, 1)
1288        );
1289
1290        assert_eq!(
1291            inlay_snapshot.clip_point(InlayPoint::new(0, 2), Bias::Left),
1292            InlayPoint::new(0, 2)
1293        );
1294        assert_eq!(
1295            inlay_snapshot.clip_point(InlayPoint::new(0, 2), Bias::Right),
1296            InlayPoint::new(0, 2)
1297        );
1298
1299        assert_eq!(
1300            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Left),
1301            InlayPoint::new(0, 2)
1302        );
1303        assert_eq!(
1304            inlay_snapshot.clip_point(InlayPoint::new(0, 3), Bias::Right),
1305            InlayPoint::new(0, 8)
1306        );
1307
1308        assert_eq!(
1309            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Left),
1310            InlayPoint::new(0, 2)
1311        );
1312        assert_eq!(
1313            inlay_snapshot.clip_point(InlayPoint::new(0, 4), Bias::Right),
1314            InlayPoint::new(0, 8)
1315        );
1316
1317        assert_eq!(
1318            inlay_snapshot.clip_point(InlayPoint::new(0, 5), Bias::Left),
1319            InlayPoint::new(0, 2)
1320        );
1321        assert_eq!(
1322            inlay_snapshot.clip_point(InlayPoint::new(0, 5), Bias::Right),
1323            InlayPoint::new(0, 8)
1324        );
1325
1326        assert_eq!(
1327            inlay_snapshot.clip_point(InlayPoint::new(0, 6), Bias::Left),
1328            InlayPoint::new(0, 2)
1329        );
1330        assert_eq!(
1331            inlay_snapshot.clip_point(InlayPoint::new(0, 6), Bias::Right),
1332            InlayPoint::new(0, 8)
1333        );
1334
1335        assert_eq!(
1336            inlay_snapshot.clip_point(InlayPoint::new(0, 7), Bias::Left),
1337            InlayPoint::new(0, 2)
1338        );
1339        assert_eq!(
1340            inlay_snapshot.clip_point(InlayPoint::new(0, 7), Bias::Right),
1341            InlayPoint::new(0, 8)
1342        );
1343
1344        assert_eq!(
1345            inlay_snapshot.clip_point(InlayPoint::new(0, 8), Bias::Left),
1346            InlayPoint::new(0, 8)
1347        );
1348        assert_eq!(
1349            inlay_snapshot.clip_point(InlayPoint::new(0, 8), Bias::Right),
1350            InlayPoint::new(0, 8)
1351        );
1352
1353        assert_eq!(
1354            inlay_snapshot.clip_point(InlayPoint::new(0, 9), Bias::Left),
1355            InlayPoint::new(0, 9)
1356        );
1357        assert_eq!(
1358            inlay_snapshot.clip_point(InlayPoint::new(0, 9), Bias::Right),
1359            InlayPoint::new(0, 9)
1360        );
1361
1362        assert_eq!(
1363            inlay_snapshot.clip_point(InlayPoint::new(0, 10), Bias::Left),
1364            InlayPoint::new(0, 10)
1365        );
1366        assert_eq!(
1367            inlay_snapshot.clip_point(InlayPoint::new(0, 10), Bias::Right),
1368            InlayPoint::new(0, 10)
1369        );
1370
1371        assert_eq!(
1372            inlay_snapshot.clip_point(InlayPoint::new(0, 11), Bias::Left),
1373            InlayPoint::new(0, 11)
1374        );
1375        assert_eq!(
1376            inlay_snapshot.clip_point(InlayPoint::new(0, 11), Bias::Right),
1377            InlayPoint::new(0, 11)
1378        );
1379
1380        assert_eq!(
1381            inlay_snapshot.clip_point(InlayPoint::new(0, 12), Bias::Left),
1382            InlayPoint::new(0, 11)
1383        );
1384        assert_eq!(
1385            inlay_snapshot.clip_point(InlayPoint::new(0, 12), Bias::Right),
1386            InlayPoint::new(0, 17)
1387        );
1388
1389        assert_eq!(
1390            inlay_snapshot.clip_point(InlayPoint::new(0, 13), Bias::Left),
1391            InlayPoint::new(0, 11)
1392        );
1393        assert_eq!(
1394            inlay_snapshot.clip_point(InlayPoint::new(0, 13), Bias::Right),
1395            InlayPoint::new(0, 17)
1396        );
1397
1398        assert_eq!(
1399            inlay_snapshot.clip_point(InlayPoint::new(0, 14), Bias::Left),
1400            InlayPoint::new(0, 11)
1401        );
1402        assert_eq!(
1403            inlay_snapshot.clip_point(InlayPoint::new(0, 14), Bias::Right),
1404            InlayPoint::new(0, 17)
1405        );
1406
1407        assert_eq!(
1408            inlay_snapshot.clip_point(InlayPoint::new(0, 15), Bias::Left),
1409            InlayPoint::new(0, 11)
1410        );
1411        assert_eq!(
1412            inlay_snapshot.clip_point(InlayPoint::new(0, 15), Bias::Right),
1413            InlayPoint::new(0, 17)
1414        );
1415
1416        assert_eq!(
1417            inlay_snapshot.clip_point(InlayPoint::new(0, 16), Bias::Left),
1418            InlayPoint::new(0, 11)
1419        );
1420        assert_eq!(
1421            inlay_snapshot.clip_point(InlayPoint::new(0, 16), Bias::Right),
1422            InlayPoint::new(0, 17)
1423        );
1424
1425        assert_eq!(
1426            inlay_snapshot.clip_point(InlayPoint::new(0, 17), Bias::Left),
1427            InlayPoint::new(0, 17)
1428        );
1429        assert_eq!(
1430            inlay_snapshot.clip_point(InlayPoint::new(0, 17), Bias::Right),
1431            InlayPoint::new(0, 17)
1432        );
1433
1434        assert_eq!(
1435            inlay_snapshot.clip_point(InlayPoint::new(0, 18), Bias::Left),
1436            InlayPoint::new(0, 18)
1437        );
1438        assert_eq!(
1439            inlay_snapshot.clip_point(InlayPoint::new(0, 18), Bias::Right),
1440            InlayPoint::new(0, 18)
1441        );
1442
1443        // The inlays can be manually removed.
1444        let (inlay_snapshot, _) = inlay_map.splice(
1445            inlay_map.inlays.iter().map(|inlay| inlay.id).collect(),
1446            Vec::new(),
1447        );
1448        assert_eq!(inlay_snapshot.text(), "abxJKLyDzefghi");
1449    }
1450
1451    #[gpui::test]
1452    fn test_inlay_buffer_rows(cx: &mut AppContext) {
1453        let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
1454        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
1455        assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
1456        let mut next_inlay_id = 0;
1457
1458        let (inlay_snapshot, _) = inlay_map.splice(
1459            Vec::new(),
1460            vec![
1461                Inlay {
1462                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
1463                    position: buffer.read(cx).snapshot(cx).anchor_before(0),
1464                    text: "|123|\n".into(),
1465                },
1466                Inlay {
1467                    id: InlayId::Hint(post_inc(&mut next_inlay_id)),
1468                    position: buffer.read(cx).snapshot(cx).anchor_before(4),
1469                    text: "|456|".into(),
1470                },
1471                Inlay {
1472                    id: InlayId::InlineCompletion(post_inc(&mut next_inlay_id)),
1473                    position: buffer.read(cx).snapshot(cx).anchor_before(7),
1474                    text: "\n|567|\n".into(),
1475                },
1476            ],
1477        );
1478        assert_eq!(inlay_snapshot.text(), "|123|\nabc\n|456|def\n|567|\n\nghi");
1479        assert_eq!(
1480            inlay_snapshot.buffer_rows(0).collect::<Vec<_>>(),
1481            vec![Some(0), None, Some(1), None, None, Some(2)]
1482        );
1483    }
1484
1485    #[gpui::test(iterations = 100)]
1486    fn test_random_inlays(cx: &mut AppContext, mut rng: StdRng) {
1487        init_test(cx);
1488
1489        let operations = env::var("OPERATIONS")
1490            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1491            .unwrap_or(10);
1492
1493        let len = rng.gen_range(0..30);
1494        let buffer = if rng.gen() {
1495            let text = util::RandomCharIter::new(&mut rng)
1496                .take(len)
1497                .collect::<String>();
1498            MultiBuffer::build_simple(&text, cx)
1499        } else {
1500            MultiBuffer::build_random(&mut rng, cx)
1501        };
1502        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
1503        let mut next_inlay_id = 0;
1504        log::info!("buffer text: {:?}", buffer_snapshot.text());
1505        let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1506        for _ in 0..operations {
1507            let mut inlay_edits = Patch::default();
1508
1509            let mut prev_inlay_text = inlay_snapshot.text();
1510            let mut buffer_edits = Vec::new();
1511            match rng.gen_range(0..=100) {
1512                0..=50 => {
1513                    let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1514                    log::info!("mutated text: {:?}", snapshot.text());
1515                    inlay_edits = Patch::new(edits);
1516                }
1517                _ => buffer.update(cx, |buffer, cx| {
1518                    let subscription = buffer.subscribe();
1519                    let edit_count = rng.gen_range(1..=5);
1520                    buffer.randomly_mutate(&mut rng, edit_count, cx);
1521                    buffer_snapshot = buffer.snapshot(cx);
1522                    let edits = subscription.consume().into_inner();
1523                    log::info!("editing {:?}", edits);
1524                    buffer_edits.extend(edits);
1525                }),
1526            };
1527
1528            let (new_inlay_snapshot, new_inlay_edits) =
1529                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1530            inlay_snapshot = new_inlay_snapshot;
1531            inlay_edits = inlay_edits.compose(new_inlay_edits);
1532
1533            log::info!("buffer text: {:?}", buffer_snapshot.text());
1534            log::info!("inlay text: {:?}", inlay_snapshot.text());
1535
1536            let inlays = inlay_map
1537                .inlays
1538                .iter()
1539                .filter(|inlay| inlay.position.is_valid(&buffer_snapshot))
1540                .map(|inlay| {
1541                    let offset = inlay.position.to_offset(&buffer_snapshot);
1542                    (offset, inlay.clone())
1543                })
1544                .collect::<Vec<_>>();
1545            let mut expected_text = Rope::from(buffer_snapshot.text());
1546            for (offset, inlay) in inlays.iter().rev() {
1547                expected_text.replace(*offset..*offset, &inlay.text.to_string());
1548            }
1549            assert_eq!(inlay_snapshot.text(), expected_text.to_string());
1550
1551            let expected_buffer_rows = inlay_snapshot.buffer_rows(0).collect::<Vec<_>>();
1552            assert_eq!(
1553                expected_buffer_rows.len() as u32,
1554                expected_text.max_point().row + 1
1555            );
1556            for row_start in 0..expected_buffer_rows.len() {
1557                assert_eq!(
1558                    inlay_snapshot
1559                        .buffer_rows(row_start as u32)
1560                        .collect::<Vec<_>>(),
1561                    &expected_buffer_rows[row_start..],
1562                    "incorrect buffer rows starting at {}",
1563                    row_start
1564                );
1565            }
1566
1567            let mut text_highlights = TextHighlights::default();
1568            let text_highlight_count = rng.gen_range(0_usize..10);
1569            let mut text_highlight_ranges = (0..text_highlight_count)
1570                .map(|_| buffer_snapshot.random_byte_range(0, &mut rng))
1571                .collect::<Vec<_>>();
1572            text_highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
1573            log::info!("highlighting text ranges {text_highlight_ranges:?}");
1574            text_highlights.insert(
1575                TypeId::of::<()>(),
1576                Arc::new((
1577                    HighlightStyle::default(),
1578                    text_highlight_ranges
1579                        .into_iter()
1580                        .map(|range| {
1581                            buffer_snapshot.anchor_before(range.start)
1582                                ..buffer_snapshot.anchor_after(range.end)
1583                        })
1584                        .collect(),
1585                )),
1586            );
1587
1588            let mut inlay_highlights = InlayHighlights::default();
1589            if !inlays.is_empty() {
1590                let inlay_highlight_count = rng.gen_range(0..inlays.len());
1591                let mut inlay_indices = BTreeSet::default();
1592                while inlay_indices.len() < inlay_highlight_count {
1593                    inlay_indices.insert(rng.gen_range(0..inlays.len()));
1594                }
1595                let new_highlights = TreeMap::from_ordered_entries(
1596                    inlay_indices
1597                        .into_iter()
1598                        .filter_map(|i| {
1599                            let (_, inlay) = &inlays[i];
1600                            let inlay_text_len = inlay.text.len();
1601                            match inlay_text_len {
1602                                0 => None,
1603                                1 => Some(InlayHighlight {
1604                                    inlay: inlay.id,
1605                                    inlay_position: inlay.position,
1606                                    range: 0..1,
1607                                }),
1608                                n => {
1609                                    let inlay_text = inlay.text.to_string();
1610                                    let mut highlight_end = rng.gen_range(1..n);
1611                                    let mut highlight_start = rng.gen_range(0..highlight_end);
1612                                    while !inlay_text.is_char_boundary(highlight_end) {
1613                                        highlight_end += 1;
1614                                    }
1615                                    while !inlay_text.is_char_boundary(highlight_start) {
1616                                        highlight_start -= 1;
1617                                    }
1618                                    Some(InlayHighlight {
1619                                        inlay: inlay.id,
1620                                        inlay_position: inlay.position,
1621                                        range: highlight_start..highlight_end,
1622                                    })
1623                                }
1624                            }
1625                        })
1626                        .map(|highlight| (highlight.inlay, (HighlightStyle::default(), highlight))),
1627                );
1628                log::info!("highlighting inlay ranges {new_highlights:?}");
1629                inlay_highlights.insert(TypeId::of::<()>(), new_highlights);
1630            }
1631
1632            for _ in 0..5 {
1633                let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
1634                end = expected_text.clip_offset(end, Bias::Right);
1635                let mut start = rng.gen_range(0..=end);
1636                start = expected_text.clip_offset(start, Bias::Right);
1637
1638                let range = InlayOffset(start)..InlayOffset(end);
1639                log::info!("calling inlay_snapshot.chunks({range:?})");
1640                let actual_text = inlay_snapshot
1641                    .chunks(
1642                        range,
1643                        false,
1644                        Highlights {
1645                            text_highlights: Some(&text_highlights),
1646                            inlay_highlights: Some(&inlay_highlights),
1647                            ..Highlights::default()
1648                        },
1649                    )
1650                    .map(|chunk| chunk.text)
1651                    .collect::<String>();
1652                assert_eq!(
1653                    actual_text,
1654                    expected_text.slice(start..end).to_string(),
1655                    "incorrect text in range {:?}",
1656                    start..end
1657                );
1658
1659                assert_eq!(
1660                    inlay_snapshot.text_summary_for_range(InlayOffset(start)..InlayOffset(end)),
1661                    expected_text.slice(start..end).summary()
1662                );
1663            }
1664
1665            for edit in inlay_edits {
1666                prev_inlay_text.replace_range(
1667                    edit.new.start.0..edit.new.start.0 + edit.old_len().0,
1668                    &inlay_snapshot.text()[edit.new.start.0..edit.new.end.0],
1669                );
1670            }
1671            assert_eq!(prev_inlay_text, inlay_snapshot.text());
1672
1673            assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0);
1674            assert_eq!(expected_text.len(), inlay_snapshot.len().0);
1675
1676            let mut buffer_point = Point::default();
1677            let mut inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
1678            let mut buffer_chars = buffer_snapshot.chars_at(0);
1679            loop {
1680                // Ensure conversion from buffer coordinates to inlay coordinates
1681                // is consistent.
1682                let buffer_offset = buffer_snapshot.point_to_offset(buffer_point);
1683                assert_eq!(
1684                    inlay_snapshot.to_point(inlay_snapshot.to_inlay_offset(buffer_offset)),
1685                    inlay_point
1686                );
1687
1688                // No matter which bias we clip an inlay point with, it doesn't move
1689                // because it was constructed from a buffer point.
1690                assert_eq!(
1691                    inlay_snapshot.clip_point(inlay_point, Bias::Left),
1692                    inlay_point,
1693                    "invalid inlay point for buffer point {:?} when clipped left",
1694                    buffer_point
1695                );
1696                assert_eq!(
1697                    inlay_snapshot.clip_point(inlay_point, Bias::Right),
1698                    inlay_point,
1699                    "invalid inlay point for buffer point {:?} when clipped right",
1700                    buffer_point
1701                );
1702
1703                if let Some(ch) = buffer_chars.next() {
1704                    if ch == '\n' {
1705                        buffer_point += Point::new(1, 0);
1706                    } else {
1707                        buffer_point += Point::new(0, ch.len_utf8() as u32);
1708                    }
1709
1710                    // Ensure that moving forward in the buffer always moves the inlay point forward as well.
1711                    let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
1712                    assert!(new_inlay_point > inlay_point);
1713                    inlay_point = new_inlay_point;
1714                } else {
1715                    break;
1716                }
1717            }
1718
1719            let mut inlay_point = InlayPoint::default();
1720            let mut inlay_offset = InlayOffset::default();
1721            for ch in expected_text.chars() {
1722                assert_eq!(
1723                    inlay_snapshot.to_offset(inlay_point),
1724                    inlay_offset,
1725                    "invalid to_offset({:?})",
1726                    inlay_point
1727                );
1728                assert_eq!(
1729                    inlay_snapshot.to_point(inlay_offset),
1730                    inlay_point,
1731                    "invalid to_point({:?})",
1732                    inlay_offset
1733                );
1734
1735                let mut bytes = [0; 4];
1736                for byte in ch.encode_utf8(&mut bytes).as_bytes() {
1737                    inlay_offset.0 += 1;
1738                    if *byte == b'\n' {
1739                        inlay_point.0 += Point::new(1, 0);
1740                    } else {
1741                        inlay_point.0 += Point::new(0, 1);
1742                    }
1743
1744                    let clipped_left_point = inlay_snapshot.clip_point(inlay_point, Bias::Left);
1745                    let clipped_right_point = inlay_snapshot.clip_point(inlay_point, Bias::Right);
1746                    assert!(
1747                        clipped_left_point <= clipped_right_point,
1748                        "inlay point {:?} when clipped left is greater than when clipped right ({:?} > {:?})",
1749                        inlay_point,
1750                        clipped_left_point,
1751                        clipped_right_point
1752                    );
1753
1754                    // Ensure the clipped points are at valid text locations.
1755                    assert_eq!(
1756                        clipped_left_point.0,
1757                        expected_text.clip_point(clipped_left_point.0, Bias::Left)
1758                    );
1759                    assert_eq!(
1760                        clipped_right_point.0,
1761                        expected_text.clip_point(clipped_right_point.0, Bias::Right)
1762                    );
1763
1764                    // Ensure the clipped points never overshoot the end of the map.
1765                    assert!(clipped_left_point <= inlay_snapshot.max_point());
1766                    assert!(clipped_right_point <= inlay_snapshot.max_point());
1767
1768                    // Ensure the clipped points are at valid buffer locations.
1769                    assert_eq!(
1770                        inlay_snapshot
1771                            .to_inlay_point(inlay_snapshot.to_buffer_point(clipped_left_point)),
1772                        clipped_left_point,
1773                        "to_buffer_point({:?}) = {:?}",
1774                        clipped_left_point,
1775                        inlay_snapshot.to_buffer_point(clipped_left_point),
1776                    );
1777                    assert_eq!(
1778                        inlay_snapshot
1779                            .to_inlay_point(inlay_snapshot.to_buffer_point(clipped_right_point)),
1780                        clipped_right_point,
1781                        "to_buffer_point({:?}) = {:?}",
1782                        clipped_right_point,
1783                        inlay_snapshot.to_buffer_point(clipped_right_point),
1784                    );
1785                }
1786            }
1787        }
1788    }
1789
1790    fn init_test(cx: &mut AppContext) {
1791        let store = SettingsStore::test(cx);
1792        cx.set_global(store);
1793        theme::init(theme::LoadThemes::JustBase, cx);
1794    }
1795}