inlay_map.rs

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