inlay_map.rs

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