inlay_map.rs

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